6c644a79878cc32696c7708904527950acf9f725
[dhcpcd-ui] / src / dhcpcd-gtk / menu.c
1 /*
2  * dhcpcd-gtk
3  * Copyright 2009-2014 Roy Marples <roy@marples.name>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "dhcpcd-gtk.h"
29
30 static const char *copyright = "Copyright (c) 2009-2014 Roy Marples";
31
32 static GtkStatusIcon *sicon;
33 static GtkWidget *menu;
34 static GtkAboutDialog *about;
35
36 static void
37 on_pref(_unused GObject *o, gpointer data)
38 {
39
40         prefs_show((DHCPCD_CONNECTION *)data);
41 }
42
43 static void
44 on_quit(void)
45 {
46
47         wpa_abort();
48         gtk_main_quit();
49 }
50
51 static void
52 ssid_hook(GtkMenuItem *item, _unused gpointer data)
53 {
54         DHCPCD_WI_SCAN *scan;
55         WI_SCAN *wi;
56
57         scan = g_object_get_data(G_OBJECT(item), "dhcpcd_wi_scan");
58         wi = wi_scan_find(scan);
59         if (wi) {
60                 DHCPCD_CONNECTION *con;
61
62                 con = dhcpcd_if_connection(wi->interface);
63                 if (con) {
64                         DHCPCD_WPA *wpa;
65
66                         wpa = dhcpcd_wpa_find(con, wi->interface->ifname);
67                         if (wpa)
68                                 wpa_configure(wpa, scan);
69                 }
70         }
71 }
72
73 static void
74 on_about(_unused GtkMenuItem *item)
75 {
76
77         if (about == NULL) {
78                 about = GTK_ABOUT_DIALOG(gtk_about_dialog_new());
79                 gtk_about_dialog_set_version(about, VERSION);
80                 gtk_about_dialog_set_copyright(about, copyright);
81                 gtk_about_dialog_set_website_label(about, "dhcpcd Website");
82                 gtk_about_dialog_set_website(about,
83                     "http://roy.marples.name/projects/dhcpcd");
84                 gtk_about_dialog_set_logo_icon_name(about,
85                     "network-transmit-receive");
86                 gtk_about_dialog_set_comments(about,
87                     "Part of the dhcpcd project");
88         }
89         gtk_window_set_position(GTK_WINDOW(about), GTK_WIN_POS_MOUSE);
90         gtk_window_present(GTK_WINDOW(about));
91         gtk_dialog_run(GTK_DIALOG(about));
92         gtk_widget_hide(GTK_WIDGET(about));
93 }
94
95 static const char *
96 get_strength_icon_name(int strength)
97 {
98
99         if (strength > 80)
100                 return "network-wireless-connected-100";
101         else if (strength > 55)
102                 return "network-wireless-connected-75";
103         else if (strength > 30)
104                 return "network-wireless-connected-50";
105         else if (strength > 5)
106                 return "network-wireless-connected-25";
107         else
108                 return "network-wireless-connected-00";
109 }
110
111 static void
112 update_item(WI_SCAN *wi, WI_MENU *m, DHCPCD_WI_SCAN *scan)
113 {
114         const char *icon;
115         GtkWidget *sel;
116
117         m->scan = scan;
118
119         g_object_set_data(G_OBJECT(m->menu), "dhcpcd_wi_scan", scan);
120
121         if (wi->interface->up &&
122             g_strcmp0(scan->ssid, wi->interface->ssid) == 0)
123                 sel = gtk_image_new_from_icon_name("dialog-ok-apply",
124                     GTK_ICON_SIZE_MENU);
125         else
126                 sel = NULL;
127         gtk_image_menu_item_set_image(
128             GTK_IMAGE_MENU_ITEM(m->menu), sel);
129
130         gtk_label_set_text(GTK_LABEL(m->ssid), scan->ssid);
131         if (scan->flags & WSF_SECURE)
132                 icon = "network-wireless-encrypted";
133         else
134                 icon = "network-wireless";
135         m->icon = gtk_image_new_from_icon_name(icon,
136             GTK_ICON_SIZE_MENU);
137
138         icon = get_strength_icon_name(scan->strength.value);
139         m->strength = gtk_image_new_from_icon_name(icon,
140                 GTK_ICON_SIZE_MENU);
141
142 #if 0
143         if (scan->wpa_flags[0] == '\0')
144                 gtk_widget_set_tooltip_text(m->menu, scan->bssid);
145         else {
146                 char *tip = g_strconcat(scan->bssid, " ", scan->wpa_flags,
147                     NULL);
148                 gtk_widget_set_tooltip_text(m->menu, tip);
149                 g_free(tip);
150         }
151 #endif
152
153         g_object_set_data(G_OBJECT(m->menu), "dhcpcd_wi_scan", scan);
154 }
155
156 static WI_MENU *
157 create_menu(WI_SCAN *wis, DHCPCD_WI_SCAN *scan)
158 {
159         WI_MENU *wim;
160         GtkWidget *box, *sel;
161         const char *icon;
162
163         wim = g_malloc(sizeof(*wim));
164         wim->scan = scan;
165         wim->menu = gtk_image_menu_item_new();
166         box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
167         gtk_container_add(GTK_CONTAINER(wim->menu), box);
168
169         if (wis->interface->up &&
170             g_strcmp0(scan->ssid, wis->interface->ssid) == 0)
171                 sel = gtk_image_new_from_icon_name("dialog-ok-apply",
172                     GTK_ICON_SIZE_MENU);
173         else
174                 sel = NULL;
175         gtk_image_menu_item_set_image(
176             GTK_IMAGE_MENU_ITEM(wim->menu), sel);
177
178         wim->ssid = gtk_label_new(scan->ssid);
179         gtk_misc_set_alignment(GTK_MISC(wim->ssid), 0.0, 0.5);
180         gtk_box_pack_start(GTK_BOX(box), wim->ssid, TRUE, TRUE, 0);
181
182         if (scan->flags & WSF_SECURE)
183                 icon = "network-wireless-encrypted";
184         else
185                 icon = "network-wireless";
186         wim->icon = gtk_image_new_from_icon_name(icon,
187             GTK_ICON_SIZE_MENU);
188         gtk_box_pack_start(GTK_BOX(box), wim->icon, FALSE, FALSE, 0);
189
190         icon = get_strength_icon_name(scan->strength.value);
191         wim->strength = gtk_image_new_from_icon_name(icon,
192                 GTK_ICON_SIZE_MENU);
193         gtk_box_pack_start(GTK_BOX(box), wim->strength, FALSE, FALSE, 0);
194
195 #if 0
196         if (scan->wpa_flags[0] == '\0')
197                 gtk_widget_set_tooltip_text(wim->menu, scan->bssid);
198         else {
199                 tip = g_strconcat(scan->bssid, " ", scan->wpa_flags, NULL);
200                 gtk_widget_set_tooltip_text(wim->menu, tip);
201                 g_free(tip);
202         }
203 #endif
204
205         g_signal_connect(G_OBJECT(wim->menu), "activate",
206             G_CALLBACK(ssid_hook), NULL);
207         g_object_set_data(G_OBJECT(wim->menu), "dhcpcd_wi_scan", scan);
208
209         return wim;
210 }
211
212 void
213 menu_update_scans(WI_SCAN *wi, DHCPCD_WI_SCAN *scans)
214 {
215         WI_MENU *wim, *win;
216         DHCPCD_WI_SCAN *s;
217         bool found;
218         int position;
219
220         if (wi->ifmenu == NULL) {
221                 dhcpcd_wi_scans_free(wi->scans);
222                 wi->scans = scans;
223                 return;
224         }
225
226         TAILQ_FOREACH_SAFE(wim, &wi->menus, next, win) {
227                 found = false;
228                 for (s = scans; s; s = s->next) {
229                         if (memcmp(wim->scan->bssid, s->bssid,
230                             sizeof(s->bssid)) == 0)
231                         {
232                                 found = true;
233                                 update_item(wi, wim, s);
234                         }
235                 }
236                 if (!found) {
237                         TAILQ_REMOVE(&wi->menus, wim, next);
238                         gtk_widget_destroy(wim->menu);
239                         g_free(wim);
240                 }
241         }
242
243         for (s = scans; s; s = s->next) {
244                 found = false;
245                 position = 0;
246                 TAILQ_FOREACH(wim, &wi->menus, next) {
247                         if (memcmp(wim->scan->bssid, s->bssid,
248                             sizeof(s->bssid)) == 0)
249                         {
250                                 found = true;
251                                 break;
252                         }
253                         if (dhcpcd_wi_scan_compare(wim->scan, s) < 0)
254                                 position++;
255                 }
256                 if (!found) {
257                         wim = create_menu(wi, s);
258                         printf ("inserting %s\n", s->ssid);
259                         TAILQ_INSERT_TAIL(&wi->menus, wim, next);
260                         gtk_menu_shell_insert(GTK_MENU_SHELL(wi->ifmenu),
261                             wim->menu, position);
262                         gtk_widget_show_all(wim->menu);
263                 }
264         }
265
266         dhcpcd_wi_scans_free(wi->scans);
267         wi->scans = scans;
268
269         if (gtk_widget_get_visible(wi->ifmenu))
270                 gtk_menu_reposition(GTK_MENU(wi->ifmenu));
271 }
272
273 void
274 menu_remove_if(WI_SCAN *wi)
275 {
276         WI_MENU *wim;
277
278         if (wi->ifmenu == NULL)
279                 return;
280
281         if (wi->ifmenu == menu)
282                 menu = NULL;
283
284         gtk_widget_destroy(wi->ifmenu);
285         wi->ifmenu = NULL;
286         while ((wim = TAILQ_FIRST(&wi->menus))) {
287                 TAILQ_REMOVE(&wi->menus, wim, next);
288                 g_free(wim);
289         }
290
291         if (menu && gtk_widget_get_visible(menu))
292                 gtk_menu_reposition(GTK_MENU(menu));
293 }
294
295 static GtkWidget *
296 add_scans(WI_SCAN *wi)
297 {
298         GtkWidget *m;
299         DHCPCD_WI_SCAN *wis;
300         WI_MENU *wim;
301
302         if (wi->scans == NULL)
303                 return NULL;
304
305         m = gtk_menu_new();
306         for (wis = wi->scans; wis; wis = wis->next) {
307                 wim = create_menu(wi, wis);
308                 TAILQ_INSERT_TAIL(&wi->menus, wim, next);
309                 gtk_menu_shell_append(GTK_MENU_SHELL(m), wim->menu);
310         }
311
312         return m;
313 }
314
315 void
316 menu_abort(void)
317 {
318         WI_SCAN *wis;
319         WI_MENU *wim;
320
321         TAILQ_FOREACH(wis, &wi_scans, next) {
322                 wis->ifmenu = NULL;
323                 while ((wim = TAILQ_FIRST(&wis->menus))) {
324                         TAILQ_REMOVE(&wis->menus, wim, next);
325                         g_free(wim);
326                 }
327         }
328
329         if (menu != NULL) {
330                 gtk_widget_destroy(menu);
331                 g_object_ref_sink(menu);
332                 g_object_unref(menu);
333                 menu = NULL;
334         }
335 }
336
337 static void
338 on_activate(GtkStatusIcon *icon)
339 {
340         WI_SCAN *w, *l;
341         GtkWidget *item, *image;
342
343         sicon = icon;
344         notify_close();
345         prefs_abort();
346         menu_abort();
347
348         if ((w = TAILQ_FIRST(&wi_scans)) == NULL)
349                 return;
350
351         if ((l = TAILQ_LAST(&wi_scans, wi_scan_head)) && l != w) {
352                 menu = gtk_menu_new();
353                 TAILQ_FOREACH(w, &wi_scans, next) {
354                         item = gtk_image_menu_item_new_with_label(
355                                 w->interface->ifname);
356                         image = gtk_image_new_from_icon_name(
357                                 "network-wireless", GTK_ICON_SIZE_MENU);
358                         gtk_image_menu_item_set_image(
359                                 GTK_IMAGE_MENU_ITEM(item), image);
360                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
361                         w->ifmenu = add_scans(w);
362                         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
363                             w->ifmenu);
364                 }
365         } else {
366                 w->ifmenu = menu = add_scans(w);
367         }
368
369         gtk_widget_show_all(GTK_WIDGET(menu));
370         gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
371             gtk_status_icon_position_menu, icon,
372             1, gtk_get_current_event_time());
373 }
374
375 static void
376 on_popup(GtkStatusIcon *icon, guint button, guint32 atime, gpointer data)
377 {
378         DHCPCD_CONNECTION *con;
379         GtkMenu *mnu;
380         GtkWidget *item, *image;
381
382         notify_close();
383
384         con = (DHCPCD_CONNECTION *)data;
385         mnu = (GtkMenu *)gtk_menu_new();
386
387         item = gtk_image_menu_item_new_with_mnemonic(_("_Preferences"));
388         image = gtk_image_new_from_icon_name("preferences-system-network",
389             GTK_ICON_SIZE_MENU);
390         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
391         if (g_strcmp0(dhcpcd_status(con), "down") == 0)
392                 gtk_widget_set_sensitive(item, false);
393         else
394                 g_signal_connect(G_OBJECT(item), "activate",
395                     G_CALLBACK(on_pref), data);
396         gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
397
398         item = gtk_separator_menu_item_new();
399         gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
400
401         item = gtk_image_menu_item_new_with_mnemonic(_("_About"));
402         image = gtk_image_new_from_icon_name("help-about",
403             GTK_ICON_SIZE_MENU);
404         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
405         g_signal_connect(G_OBJECT(item), "activate",
406             G_CALLBACK(on_about), icon);
407         gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
408
409         item = gtk_image_menu_item_new_with_mnemonic(_("_Quit"));
410         image = gtk_image_new_from_icon_name("application-exit",
411             GTK_ICON_SIZE_MENU);
412         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
413         g_signal_connect(G_OBJECT(item), "activate",
414             G_CALLBACK(on_quit), icon);
415         gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
416
417         gtk_widget_show_all(GTK_WIDGET(mnu));
418         gtk_menu_popup(GTK_MENU(mnu), NULL, NULL,
419             gtk_status_icon_position_menu, icon, button, atime);
420         if (button == 0)
421                 gtk_menu_shell_select_first(GTK_MENU_SHELL(mnu), FALSE);
422 }
423
424 void
425 menu_init(GtkStatusIcon *icon, DHCPCD_CONNECTION *con)
426 {
427
428         g_signal_connect(G_OBJECT(icon), "activate",
429             G_CALLBACK(on_activate), con);
430         g_signal_connect(G_OBJECT(icon), "popup_menu",
431             G_CALLBACK(on_popup), con);
432 }
433
434
435 #if GTK_MAJOR_VERSION == 2
436 GtkWidget *
437 gtk_box_new(GtkOrientation o, gint s)
438 {
439
440         if (o == GTK_ORIENTATION_HORIZONTAL)
441                 return gtk_hbox_new(false, s);
442         else
443                 return gtk_vbox_new(false, s);
444 }
445 #endif