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