enable folding
[simple-launcher] / simple-launcher.cc
1 // This file is a part of Simple Launcher
2 //
3 // Copyright (C) 2006, 2007, 2008 Mikhail Sobolev <mss@mawhrin.net>
4 //
5 // Simple Launcher is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License version 2 as published by
7 // the Free Software Foundation.
8 //
9 // This program is distributed in the hope that it will be useful, but WITHOUT
10 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12 // more details.
13 //
14 // You should have received a copy of the GNU General Public License along with
15 // this program; if not, write to the Free Software Foundation, Inc., 51
16 // Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 #include <string>
19 #include <vector>
20 #include <fstream>
21
22 #include <dirent.h>
23
24 #include <gtk/gtk.h>
25
26 #include "simple-launcher.h"
27
28 #define SL_APPLET_DBUS_NAME  "simple-launcher"
29 #define SL_APPLET_VERSION    "0.0"
30
31 // SimpleLauncherApplet implementation
32
33 char *SimpleLauncherApplet::ourDirs[] = {
34   "/usr/share/applications/hildon",
35   NULL
36 };
37
38 SimpleLauncherApplet::SimpleLauncherApplet(const std::string& base) : myContext(NULL), myWidget(NULL), myParent(NULL), myValidItems(0), myTransparent(GConfKey(base), "transparent", false), myIconSize(GConfKey(base), "icon_size", 48) {
39 }
40
41 bool SimpleLauncherApplet::doInit() {
42   if ((myContext = osso_initialize(SL_APPLET_DBUS_NAME, SL_APPLET_VERSION, FALSE, NULL)) == NULL) {
43     g_debug("sla-applet: failed to initialize the osso layer");
44     return false;
45   }
46
47   loadConfig();
48
49   if (!initWidget()) {
50     return false;
51   }
52
53   return true;
54 }
55
56 SimpleLauncherApplet::~SimpleLauncherApplet() {
57   myItems.clear();
58 #if 0
59   // This does not seem to be necessary
60   if (myWidget != NULL) {
61     gtk_widget_destroy(myWidget);
62     myWidget = NULL;
63   }
64 #endif
65   if (myContext != NULL) {
66     osso_deinitialize(myContext);
67     myContext = NULL;
68   }
69 }
70
71 void SimpleLauncherApplet::addItem(LauncherItems& items, const std::string& name, bool enabled) {
72   if (!items.exists(name)) {
73     LaunchableItem *item = new LaunchableItem();
74
75     item->load(name);
76
77     if (enabled) {
78       item->enable();
79     } else {
80       item->disable();
81     }
82
83     items.add(name, item);
84   }
85 }
86
87 // {{{ Configuration file managment
88 static const gchar *getConfigFileName() {
89   static gchar *configFileName = NULL;
90
91   if (configFileName == NULL) {
92     configFileName = g_build_filename(g_get_home_dir(), ".slarc", NULL);
93   }
94
95   return configFileName;
96 }
97
98 void SimpleLauncherApplet::loadConfig() {
99   std::ifstream config(getConfigFileName());
100
101   if (config) {
102     char *buffer = new char [1024];
103
104     while (config.getline(buffer, 1024)) {
105       char *p = strchr(buffer, ',');
106
107       if (p != NULL) {
108         *p++ = '\0';
109       }
110
111       addItem(myItems, buffer, (p != NULL && (*p == '1' || *p == 'y' || *p == 'Y')));
112     }
113
114     delete [] buffer;
115   }
116 }
117
118 void SimpleLauncherApplet::saveConfig() {
119   // TODO: make saving config an atomic operation
120   std::ofstream config(getConfigFileName());
121
122   if (config) {
123     for (size_t i = 0 ; i < myItems.size() ; ++i) {
124       config << myItems.name(i) << ',' << myItems[i]->isEnabled() << std::endl;
125     }
126   }
127 }
128
129 // }}}
130
131 void SimpleLauncherApplet::updateItems(LauncherItems& items) {
132   for (int i = 0 ; ourDirs[i] != NULL ; ++i) {
133     processDirectory(items, ourDirs[i]);
134   }
135 }
136
137 void SimpleLauncherApplet::processDirectory(LauncherItems& items, const std::string& dirname) {
138   DIR *dir = opendir(dirname.c_str());
139
140   if (dir != NULL) {
141     const std::string namePrefix = dirname + "/";
142     std::string shortName;
143     std::string desktopExtension = ".desktop";
144     const dirent *file;
145
146     while ((file = readdir(dir)) != 0) {
147       shortName = file->d_name;
148       if ((shortName == ".") || (shortName == "..")) {
149         continue;
150       }
151
152       if ((shortName.length() >= desktopExtension.length()) && (shortName.compare(shortName.length() - desktopExtension.length(), desktopExtension.length(), desktopExtension) == 0)) {
153         addItem(items, namePrefix+shortName, false);
154       }
155     }
156
157     closedir(dir);
158   }
159 }
160
161 bool SimpleLauncherApplet::initWidget() {
162   myWidget = gtk_hbox_new(false, 0);
163
164   if (myWidget != NULL) {
165     updateWidget();
166   }
167
168   return myWidget != NULL;
169 }
170
171 void SimpleLauncherApplet::updateWidget() {
172   gtk_container_foreach(GTK_CONTAINER(myWidget), (GtkCallback)gtk_widget_destroy, NULL);
173
174   GtkSizeGroup *group = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
175
176   myValidItems = 0;
177
178   for (size_t i = 0 ; i < myItems.size() ; ++i) {
179     LauncherItem *item = myItems[i];
180
181     if (item != NULL && item->isEnabled()) {
182       GtkWidget *button = gtk_event_box_new();
183
184       gtk_widget_set_events(button, GDK_BUTTON_PRESS_MASK);
185       g_signal_connect(button, "button-press-event", G_CALLBACK(_button_pressed), this);
186
187       gtk_event_box_set_visible_window(GTK_EVENT_BOX(button), !myTransparent.value());
188
189       {
190         GdkPixbuf *pixbuf = item->getIcon(myIconSize.value());
191         gtk_container_add(GTK_CONTAINER(button), gtk_image_new_from_pixbuf(pixbuf));
192         g_object_unref(G_OBJECT(pixbuf));
193       }
194
195       gtk_object_set_user_data(GTK_OBJECT(button), item);
196
197       gtk_size_group_add_widget(group, button);
198
199       gtk_box_pack_start(GTK_BOX(myWidget), GTK_WIDGET(button), false, false, 0);
200
201       ++myValidItems;
202     }
203   }
204
205   g_object_unref(G_OBJECT(group));
206
207   gtk_widget_set_size_request(myWidget, getWidth(), getHeight());
208
209   gtk_widget_show_all(myWidget);
210 }
211
212 void SimpleLauncherApplet::_button_pressed(GtkWidget *button, GdkEventButton *event, void *self) {
213   ((SimpleLauncherApplet *)self)->buttonPressed(button, event);
214 }
215
216 void SimpleLauncherApplet::buttonPressed(GtkWidget *button, GdkEventButton *event) {
217   if (button != NULL && event->button == 1) {
218     LaunchableItem *item = (LaunchableItem *)gtk_object_get_user_data(GTK_OBJECT(button));
219
220     if (item != NULL) {
221       item->activate(myContext);
222     }
223   }
224 }
225
226 GtkWidget *SimpleLauncherApplet::settings(GtkWindow *parent) {
227   myParent = parent;  // FIXME: Ugly piece of code :(
228
229   GtkWidget *menuItem = gtk_menu_item_new_with_label("Launcher settings...");
230
231   g_signal_connect(menuItem, "activate", G_CALLBACK(_run_dialog), this);
232
233   return menuItem;
234 }
235
236 void SimpleLauncherApplet::_run_dialog(GtkMenuItem *, void *self) {
237   ((SimpleLauncherApplet *)self)->runDialog();
238 }
239
240 void SimpleLauncherApplet::runDialog() {
241   // We update the items before using them to avoid a small memory leak
242   // FIXME: deal with the situation in a better way (figure it out first :))
243   updateItems(myItems);       // User requested 'settings', let's give her the latest stuff :)
244
245   LauncherItems newItems = myItems;
246
247   // TODO: make it nicer... this code is ugly :(
248   SettingsDialog dialog(myParent, newItems, myTransparent, myIconSize);
249
250   switch (dialog.run()) {
251     case GTK_RESPONSE_OK:
252       myItems = newItems;
253       dialog.updateValues();  // FIXME: hackish :( make it better
254
255       saveConfig();   // save it immediately!
256       updateWidget();
257       break;
258
259     case GTK_RESPONSE_CANCEL:
260       break;
261
262     default:
263       ;     // FIXME: do I want to do anything in here?
264   }
265
266   // newItems.clear(); // TODO: do I really need it?
267 }
268
269 int SimpleLauncherApplet::getWidth() const {
270   if (myValidItems) {
271     return myIconSize.value() * myValidItems;
272   } else {
273     return myIconSize.value();
274   }
275 }
276
277 int SimpleLauncherApplet::getHeight() const {
278   return myIconSize.value();
279 }
280
281 void SimpleLauncherApplet::getBackgroundColour(double& red, double& green, double& blue, double alpha) const {
282   // white :)
283   red = 1.0; green = 1.0; blue = 1.0;
284
285   alpha = myTransparent.value() ? 0.0 : 1.0;
286 }
287
288 // vim:ts=2:sw=2:et:foldmethod=marker