2211736dd479ea974e4d7f01812be5451e0a8e75
[simple-launcher] / simple-launcher.cc
1 // This file is a part of Simple Launcher
2 //
3 // Copyright (C) 2006, 2007, Mikhail Sobolev
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 <hildon-home-plugin/hildon-home-plugin-interface.h>
27 #include <libosso.h>
28
29 #include "launcher-item.h"
30 #include "sla-list.h"
31 #include "launchable-item.h"
32 #include "settings-dialog.h"
33
34 #define SL_APPLET_DBUS_NAME  "simple-launcher"
35 #define SL_APPLET_VERSION    "0.0"
36 #define SL_APPLET_ICON_SIZE  48
37
38 #define SL_APPLET_GCONF_PATH  "/apps/simple-launcher"
39
40 class SimpleLauncherApplet {
41 public:
42   SimpleLauncherApplet();
43  ~SimpleLauncherApplet();
44
45   bool doInit(void *state_data, int *state_size);
46
47   void background() {}
48   void foreground() {}
49   int saveState(void **state_data, int *state_size);
50   GtkWidget *settings(GtkWindow *parent);
51
52   GtkWidget *getWidget() { return myWidget; }
53
54 private:
55   static void addItem(LauncherItems&, const std::string&, bool);
56
57   void loadConfig();
58   void saveConfig();
59
60   static void updateItems(LauncherItems&);
61   static void processDirectory(LauncherItems&, const std::string&);
62
63   bool initWidget();
64   void updateWidget();
65
66   void buttonPressed(GtkWidget *button, GdkEventButton *event);
67   void runDialog();
68
69   static void _button_pressed(GtkWidget *button, GdkEventButton *event, void *self);
70   static void _run_dialog(GtkMenuItem *, void *);
71
72 private:
73   // GConfClientWrapper myClient;
74   // GConfKey myMainSettings;
75
76   osso_context_t *myContext;
77
78   GtkWidget *myWidget;
79   GtkWindow *myParent;
80
81   LauncherItems myItems;
82
83   bool myTransparent;
84   // bool myShowInfobanner; // FIXME: to implement
85
86   static char *ourDirs[];
87 };
88
89 // Hildon home applet interface functions
90
91 void *hildon_home_applet_lib_initialize(void *state_data, int *state_size, GtkWidget **widget) {
92   SimpleLauncherApplet *applet = new SimpleLauncherApplet();
93
94   if (applet != NULL) {
95     if (applet->doInit(state_data, state_size)) {
96       *widget = applet->getWidget();
97     } else {
98       delete applet;
99       applet = NULL;
100     }
101   }
102
103   return (void*)applet;
104 }
105
106 void hildon_home_applet_lib_deinitialize(void *applet_data) {
107   SimpleLauncherApplet *applet = (SimpleLauncherApplet *)applet_data;
108
109   delete applet;
110 }
111
112 void hildon_home_applet_lib_background(void *applet_data) {
113   ((SimpleLauncherApplet *)applet_data)->background();
114 }
115
116 void hildon_home_applet_lib_foreground (void *applet_data) {
117   ((SimpleLauncherApplet *)applet_data)->foreground();
118 }
119
120 GtkWidget *hildon_home_applet_lib_settings(void *applet_data, GtkWindow *parent) {
121   return ((SimpleLauncherApplet *)applet_data)->settings(parent);
122 }
123
124 int hildon_home_applet_lib_save_state (void *applet_data, void **state_data, int *state_size) {
125   return ((SimpleLauncherApplet *)applet_data)->saveState(state_data, state_size);
126 }
127
128 // SimpleLauncherApplet implementation
129
130 char *SimpleLauncherApplet::ourDirs[] = {
131   "/usr/share/applications/hildon",
132   NULL
133 };
134
135 // SimpleLauncherApplet::SimpleLauncherApplet() : myMainSettings(myClient.getKey(SL_APPLET_GCONF_PATH)), myContext(NULL), myWidget(NULL), myParent(NULL) {
136 SimpleLauncherApplet::SimpleLauncherApplet() : myContext(NULL), myWidget(NULL), myParent(NULL), myTransparent(false) {
137 }
138
139 bool SimpleLauncherApplet::doInit(void *state_data, int *state_size) {
140   if ((myContext = osso_initialize(SL_APPLET_DBUS_NAME, SL_APPLET_VERSION, FALSE, NULL)) == NULL) {
141     g_debug("sla-applet: failed to initialize the osso layer");
142     return false;
143   }
144
145   loadConfig();
146
147   if (!initWidget()) {
148     return false;
149   }
150
151   return true;
152 }
153
154 SimpleLauncherApplet::~SimpleLauncherApplet() {
155   myItems.clear();
156
157   if (myWidget != NULL) {
158     gtk_widget_destroy(myWidget);
159     myWidget = NULL;
160   }
161
162   if (myContext != NULL) {
163     osso_deinitialize(myContext);
164     myContext = NULL;
165   }
166 }
167
168 void SimpleLauncherApplet::addItem(LauncherItems& items, const std::string& name, bool enabled) {
169   if (!items.exists(name)) {
170     LaunchableItem *item = new LaunchableItem();
171
172     item->load(name);
173
174     if (enabled) {
175       item->enable();
176     } else {
177       item->disable();
178     }
179
180     items.add(name, item);
181   }
182 }
183
184 // FIXME: this probably should be done somehow differently
185 static char *configFileName="/home/user/.slarc";
186
187 void SimpleLauncherApplet::loadConfig() {
188   std::ifstream config(configFileName);
189
190   if (config) {
191     char *buffer = new char [1024];
192
193     while (config.getline(buffer, 1024)) {
194       char *p = strchr(buffer, ',');
195
196       if (p != NULL) {
197         *p++ = '\0';
198       }
199
200       addItem(myItems, buffer, (p != NULL && (*p == '1' || *p == 'y' || *p == 'Y')));
201     }
202
203     delete buffer;
204   }
205 }
206
207 void SimpleLauncherApplet::saveConfig() {
208   // TODO: make saving config an atomic operation
209   std::ofstream config(configFileName);
210
211   if (config) {
212     for (size_t i = 0 ; i < myItems.size() ; ++i) {
213       config << myItems.name(i) << ',' << myItems[i]->isEnabled() << std::endl;
214     }
215   }
216 }
217
218 void SimpleLauncherApplet::updateItems(LauncherItems& items) {
219   for (int i = 0 ; ourDirs[i] != NULL ; ++i) {
220     processDirectory(items, ourDirs[i]);
221   }
222 }
223
224 void SimpleLauncherApplet::processDirectory(LauncherItems& items, const std::string& dirname) {
225   DIR *dir = opendir(dirname.c_str());
226
227   if (dir != NULL) {
228     const std::string namePrefix = dirname + "/";
229     std::string shortName;
230     std::string desktopExtension = ".desktop";
231     const dirent *file;
232
233     while ((file = readdir(dir)) != 0) {
234       shortName = file->d_name;
235       if ((shortName == ".") || (shortName == "..")) {
236         continue;
237       }
238
239       if ((shortName.length() >= desktopExtension.length()) && (shortName.compare(shortName.length() - desktopExtension.length(), desktopExtension.length(), desktopExtension) == 0)) {
240         addItem(items, namePrefix+shortName, false);
241       }
242     }
243
244     closedir(dir);
245   }
246 }
247
248 bool SimpleLauncherApplet::initWidget() {
249   myWidget = gtk_hbox_new(false, 0);
250
251   if (myWidget != NULL) {
252     updateWidget();
253   }
254
255   return myWidget != NULL;
256 }
257
258 void SimpleLauncherApplet::updateWidget() {
259   gtk_container_foreach(GTK_CONTAINER(myWidget), (GtkCallback)gtk_widget_destroy, NULL);
260
261   GtkSizeGroup *group = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
262
263   int button_no = 0;
264
265   for (size_t i = 0 ; i < myItems.size() ; ++i) {
266     LauncherItem *item = myItems[i];
267
268     if (item != NULL && item->isEnabled()) {
269       GtkWidget *button = gtk_event_box_new();
270
271       gtk_widget_set_events(button, GDK_BUTTON_PRESS_MASK);
272       g_signal_connect(button, "button-press-event", G_CALLBACK(_button_pressed), this);
273
274       gtk_event_box_set_visible_window(GTK_EVENT_BOX(button), !myTransparent);
275
276       gtk_container_add(GTK_CONTAINER(button), gtk_image_new_from_pixbuf(item->getIcon(SL_APPLET_ICON_SIZE)));
277
278       gtk_object_set_user_data(GTK_OBJECT(button), item);
279
280       gtk_size_group_add_widget(group, button);
281
282       gtk_box_pack_start(GTK_BOX(myWidget), GTK_WIDGET(button), false, false, 0);
283
284       ++button_no;
285     }
286   }
287
288   g_object_unref(G_OBJECT(group));
289
290   if (button_no == 0) {
291     gtk_widget_set_size_request(myWidget, SL_APPLET_ICON_SIZE, SL_APPLET_ICON_SIZE);
292   } else {
293     gtk_widget_set_size_request(myWidget, button_no*SL_APPLET_ICON_SIZE, SL_APPLET_ICON_SIZE);
294   }
295
296   gtk_widget_show_all(myWidget);
297 }
298
299 void SimpleLauncherApplet::_button_pressed(GtkWidget *button, GdkEventButton *event, void *self) {
300   ((SimpleLauncherApplet *)self)->buttonPressed(button, event);
301 }
302
303 void SimpleLauncherApplet::buttonPressed(GtkWidget *button, GdkEventButton *event) {
304   if (button != NULL && event->button == 1) {
305     LaunchableItem *item = (LaunchableItem *)gtk_object_get_user_data(GTK_OBJECT(button));
306
307     if (item != NULL) {
308       item->activate(myContext);
309     }
310   }
311 }
312
313 int SimpleLauncherApplet::saveState(void **state_data, int *state_size) {
314   if (state_data != NULL) {
315     *state_data = NULL;
316   }
317
318   if (state_size != NULL) {
319     *state_size = 0;
320   }
321
322   return 1;
323 }
324
325 GtkWidget *SimpleLauncherApplet::settings(GtkWindow *parent) {
326   myParent = parent;  // FIXME: Ugly piece of code :(
327
328   GtkWidget *menuItem = gtk_menu_item_new_with_label("Launcher settings...");
329
330   g_signal_connect(menuItem, "activate", G_CALLBACK(_run_dialog), this);
331
332   return menuItem;
333 }
334
335 void SimpleLauncherApplet::_run_dialog(GtkMenuItem *, void *self) {
336   ((SimpleLauncherApplet *)self)->runDialog();
337 }
338
339 void SimpleLauncherApplet::runDialog() {
340   // We update the items before using them to avoid a small memory leak
341   // FIXME: deal with the situation in a better way (figure it out first :))
342   updateItems(myItems);       // User requested 'settings', let's give her the latest stuff :)
343
344   LauncherItems newItems = myItems;
345
346   SettingsDialog dialog(myParent, SL_APPLET_ICON_SIZE, newItems);
347
348   switch (dialog.run()) {
349     case GTK_RESPONSE_OK:
350       myItems = newItems;
351       saveConfig();   // save it immediately!
352       updateWidget();
353       break;
354
355     case GTK_RESPONSE_CANCEL:
356       break;
357
358     default:
359       ;     // FIXME: do I want to do anything in here?
360   }
361 }
362
363 // vim:ts=2:sw=2:et