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