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