implemented directory reading; next step is testing :)
[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
33 #define SL_APPLET_DBUS_NAME  "simple-launcher"
34 #define SL_APPLET_VERSION    "0.0"
35 #define SL_APPLET_ICON_SIZE  26
36 #define SL_APPLET_BORDER_SIZE  14
37 #define SL_APPLET_CANVAS_SIZE  (SL_APPLET_BORDER_SIZE+SL_APPLET_BORDER_SIZE)
38
39 class SimpleLauncherApplet {
40 public:
41   SimpleLauncherApplet();
42  ~SimpleLauncherApplet();
43
44   bool doInit(void *state_data, int *state_size);
45
46   void background() {}
47   void foreground() {}
48   int saveState(void **state_data, int *state_size);
49   GtkWidget *settings(GtkWindow *parent);
50
51   GtkWidget *getWidget() { return myWidget; }
52
53 private:
54   void addItem(const std::string&, bool);
55
56   void loadConfig();
57   void saveConfig();
58
59   void processDirectory(const std::string&);
60
61   bool initWidget();
62   void updateWidget();
63
64   void buttonClicked(GtkToolButton *);
65   void runDialog();
66
67   static void _button_clicked(GtkToolButton *, void *);
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   for (int i = 0 ; ourDirs[i] != NULL ; ++i) {
138     processDirectory(ourDirs[i]);
139   }
140
141   if (!initWidget()) {
142     return false;
143   }
144
145   gtk_widget_show_all(myWidget);
146
147   return true;
148 }
149
150 SimpleLauncherApplet::~SimpleLauncherApplet() {
151   myItems.clear();
152
153   if (myWidget != NULL) {
154     gtk_widget_destroy(myWidget);
155     myWidget = NULL;
156   }
157
158   if (myContext != NULL) {
159     osso_deinitialize(myContext);
160     myContext = NULL;
161   }
162 }
163
164 void SimpleLauncherApplet::addItem(const std::string& name, bool enabled) {
165   if (!myItems.exists(name)) {
166     LaunchableItem *item = new LaunchableItem();
167
168     item->load(name);
169
170     if (enabled) {
171       item->enable();
172     } else {
173       item->disable();
174     }
175
176     myItems.add(name, item);
177   }
178 }
179
180 static char *configFileName="/home/user/.slarc";
181
182 void SimpleLauncherApplet::loadConfig() {
183   std::ifstream config(configFileName);
184
185   if (config) {
186     char *buffer = new char [1024];
187
188     while (config.getline(buffer, 1024)) {
189       char *p = strchr(buffer, ',');
190
191       if (p != NULL) {
192         *p++ = '\0';
193       }
194
195       addItem(buffer, (p != NULL && (*p == '1' || *p == 'y' || *p == 'Y')));
196
197     }
198
199     delete buffer;
200   }
201 }
202
203 void SimpleLauncherApplet::saveConfig() {
204   // TODO: make saving config an atomic operation
205   std::ofstream config(configFileName);
206
207   if (config) {
208     for (size_t i = 0 ; i < myItems.size() ; ++i) {
209       config << myItems.name(i) << ',' << myItems[i]->isEnabled() << std::endl;
210     }
211   }
212 }
213
214 void SimpleLauncherApplet::processDirectory(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(namePrefix+shortName, false);
231       }
232     }
233
234     closedir(dir);
235   }
236 }
237
238 bool SimpleLauncherApplet::initWidget() {
239   myWidget = gtk_frame_new(NULL);
240
241   if (myWidget != NULL) {
242     gtk_frame_set_shadow_type(GTK_FRAME(myWidget), GTK_SHADOW_ETCHED_IN);
243
244     updateWidget();
245   }
246
247   return myWidget != NULL;
248 }
249
250 void SimpleLauncherApplet::updateWidget() {
251   GtkWidget *child = gtk_bin_get_child(GTK_BIN(myWidget));
252
253   if (child != NULL) {
254     gtk_container_remove(GTK_CONTAINER(myWidget), child);
255     gtk_widget_destroy(child);
256   }
257
258   int button_no = 0;
259   GtkToolbar *toolbar = GTK_TOOLBAR(gtk_toolbar_new());
260
261   for (size_t i = 0 ; i < myItems.size() ; ++i) {
262     LauncherItem *item = myItems[i];
263
264     if (item != NULL && item->isEnabled()) {
265       GtkToolItem *button = gtk_tool_button_new(gtk_image_new_from_pixbuf(item->getIcon(SL_APPLET_ICON_SIZE)), NULL);
266
267       gtk_object_set_user_data(GTK_OBJECT(button), item);
268       g_signal_connect(button, "clicked", G_CALLBACK(_button_clicked), this);
269
270       gtk_toolbar_insert(toolbar, button, -1);
271
272       ++button_no;
273     }
274   }
275
276   if (button_no) {
277     gtk_container_add(GTK_CONTAINER(myWidget), GTK_WIDGET(toolbar));
278     gtk_widget_set_size_request(myWidget, button_no*(SL_APPLET_ICON_SIZE+SL_APPLET_CANVAS_SIZE), SL_APPLET_ICON_SIZE+SL_APPLET_CANVAS_SIZE);
279   } else {
280     gtk_widget_destroy(GTK_WIDGET(toolbar));
281   }
282 }
283
284 void SimpleLauncherApplet::_button_clicked(GtkToolButton *button, void *self) {
285   ((SimpleLauncherApplet *)self)->buttonClicked(button);
286 }
287
288 void SimpleLauncherApplet::buttonClicked(GtkToolButton *button) {
289   if (button != NULL) {
290     LaunchableItem *item = (LaunchableItem *)gtk_object_get_user_data(GTK_OBJECT(button));
291
292     if (item != NULL) {
293       item->activate(myContext);
294     }
295   }
296 }
297
298 int SimpleLauncherApplet::saveState(void **state_data, int *state_size) {
299   if (state_data != NULL) {
300     *state_data = NULL;
301   }
302
303   if (state_size != NULL) {
304     *state_size = 0;
305   }
306
307   return 1;
308 }
309
310 GtkWidget *SimpleLauncherApplet::settings(GtkWindow *parent) {
311   // TODO: in case we want SimpleLauncherApplet to be configurable, this method
312   // should return a gtk_menu_item that would be included in home settings
313   // menu.  Method should make sure that when we activate that item, a
314   // corresponding dialog appears.
315   myParent = parent;  // FIXME: Ugly piece of code :(
316
317   GtkWidget *menuItem = gtk_menu_item_new_with_label("Launcher settings...");
318
319   g_signal_connect(menuItem, "activate", G_CALLBACK(_run_dialog), this);
320
321   return menuItem;
322 }
323
324 void SimpleLauncherApplet::_run_dialog(GtkMenuItem *, void *self) {
325   ((SimpleLauncherApplet *)self)->runDialog();
326 }
327
328 void SimpleLauncherApplet::runDialog() {
329   SLAList list(SL_APPLET_ICON_SIZE, myItems);
330
331   GtkDialog *dialog = GTK_DIALOG(gtk_dialog_new_with_buttons("Launcher Settings", myParent, (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), "OK", GTK_RESPONSE_OK, "Cancel", GTK_RESPONSE_CANCEL, NULL));
332
333   gtk_container_add(GTK_CONTAINER(dialog->vbox), list.getWidget());
334
335   gtk_widget_set_size_request(GTK_WIDGET(dialog), 540, 257);
336
337   int response = gtk_dialog_run(dialog);
338
339   gtk_widget_destroy(GTK_WIDGET(dialog));
340
341   switch (response) {
342     case GTK_RESPONSE_OK:
343       break;
344
345     case GTK_RESPONSE_CANCEL:
346       break;
347
348     default:
349       ;     // FIXME: do I want to do anything in here?
350   }
351 }
352
353 // vim:ts=2:sw=2:et