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