logger.conf.sample: add missing comment mark
[asterisk/asterisk.git] / menuselect / menuselect_gtk.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007, Russell Bryant
5  *
6  * Russell Bryant <russell@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*
20  * \file
21  *
22  * \author Russell Bryant <russell@digium.com>
23  *
24  * \brief GTK 2.x frontend for selection maintenance
25  */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <gtk/gtk.h>
31
32 #include "menuselect.h"
33
34 enum {
35         /*! The row name */
36         COLUMN_NAME,
37         /*! Whether this row is enabled */
38         COLUMN_SELECTED,
39         /*! Dependencies */
40         COLUMN_DEPS,
41         /*! Optional dependencies */
42         COLUMN_USES,
43         /*! Conflicts */
44         COLUMN_CNFS,
45         /*! Description */
46         COLUMN_DESC,
47         /*! Number of columns, must be the last element in the enum */
48         NUM_COLUMNS,
49 };
50
51 static void handle_save(GtkWidget *w, gpointer data);
52 static void handle_about(GtkWidget *w, gpointer data);
53 static void handle_quit(GtkWidget *w, gpointer data);
54
55 static GtkItemFactoryEntry menu_items[] = {
56   { "/_File",               NULL,         NULL,           0, "<Branch>" },
57   { "/File/_Save And Quit", "<control>S", handle_save, 0, "<StockItem>", GTK_STOCK_SAVE },
58   { "/File/sep1",           NULL,         NULL,           0, "<Separator>" },
59   { "/File/_Quit",          "<CTRL>Q",    handle_quit,  0, "<StockItem>", GTK_STOCK_QUIT },
60   { "/_Help",               NULL,         NULL,           0, "<LastBranch>" },
61   { "/_Help/About",         NULL,         handle_about,   0, "<Item>" },
62 };
63
64 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
65
66 static GtkTreeView *tree;
67 static GtkWidget *window;
68
69 /* 0, save ... non-zero, don't save */
70 static int main_res = 1;
71 static int change_made = 0;
72
73 static void handle_save(GtkWidget *w, gpointer data)
74 {
75         main_res = 0;
76         gtk_main_quit();
77 }
78
79 static void handle_about(GtkWidget *w, gpointer data)
80 {
81         GtkWidget *dialog;
82
83         dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
84                                 GTK_MESSAGE_INFO, GTK_BUTTONS_OK,
85                                 "GMenuselect - http://www.asterisk.org/\n"
86                                 "Russell Bryant <russell@digium.com>\n"
87                                 "Copyright (C) 2007\n");
88
89         gtk_dialog_run(GTK_DIALOG(dialog));
90         gtk_widget_destroy(dialog);
91 }
92
93 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
94 {
95         return FALSE;
96 }
97
98 static void handle_quit(GtkWidget *widget, gpointer data)
99 {
100         gtk_main_quit();
101 }
102
103 static void destroy(GtkWidget *widget, gpointer data)
104 {
105         GtkWidget *dialog;
106         gint response;
107
108         if (!main_res || !change_made) {
109                 gtk_main_quit();
110                 return;
111         }
112
113         dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
114                         GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "Save before quit?");
115         response = gtk_dialog_run(GTK_DIALOG(dialog));
116         gtk_widget_destroy(dialog);
117
118         if (response == GTK_RESPONSE_YES)
119                 main_res = 0;
120
121         gtk_main_quit();
122 }
123
124 static void toggled_handler(GtkCellRendererToggle *renderer, gchar *path, gpointer data)
125 {
126         gchar *cat_num_str, *mem_num_str;
127         int cat_num, mem_num;
128         int i = 0;
129         struct category *cat;
130         struct member *mem;
131         GtkTreeStore *store = data;
132         GtkTreeModel *model;
133         GtkTreeIter cat_iter, mem_iter;
134
135         mem_num_str = alloca(strlen(path)) + 1;
136         strcpy(mem_num_str, path);
137         cat_num_str = strsep(&mem_num_str, ":");
138
139         if (!mem_num_str || !*mem_num_str)
140                 return;
141
142         cat_num = atoi(cat_num_str);
143         mem_num = atoi(mem_num_str);
144
145         AST_LIST_TRAVERSE(&categories, cat, list) {
146                 if (i == cat_num)
147                         break;
148                 i++;
149         }
150         if (!cat)
151                 return;
152
153         i = 0;
154         AST_LIST_TRAVERSE(&cat->members, mem, list) {
155                 if (i == mem_num)
156                         break;
157                 i++;
158         }
159         if (!mem)
160                 return;
161
162         toggle_enabled(mem);
163
164         model = gtk_tree_view_get_model(tree);
165
166         gtk_tree_model_get_iter_first(model, &cat_iter);
167         for (i = 0; i < cat_num; i++) {
168                 if (!gtk_tree_model_iter_next(model, &cat_iter))
169                         break;
170         }
171         if (i != cat_num)
172                 return;
173
174         if (!gtk_tree_model_iter_children(model, &mem_iter, &cat_iter))
175                 return;
176
177         for (i = 0; i < mem_num; i++) {
178                 if (!gtk_tree_model_iter_next(model, &mem_iter))
179                         break;
180         }
181         if (i != mem_num)
182                 return;
183
184         gtk_tree_store_set(store, &mem_iter, COLUMN_SELECTED, mem->enabled, -1);
185
186         change_made = 1;
187 }
188
189 static void row_activated_handler(GtkTreeView *treeview, GtkTreePath *path,
190         GtkTreeViewColumn *col, gpointer data)
191 {
192         GtkTreeIter iter;
193         GtkTreeModel *model;
194         GtkTreeStore *store = data;
195         gchar *name;
196         struct category *cat;
197         struct member *mem;
198
199         model = gtk_tree_view_get_model(treeview);
200
201         if (!gtk_tree_model_get_iter(model, &iter, path))
202                 return;
203
204         gtk_tree_model_get(model, &iter, COLUMN_NAME, &name, -1);
205
206         AST_LIST_TRAVERSE(&categories, cat, list) {
207                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
208                         if (strcmp(name, mem->name))
209                                 continue;
210
211                         toggle_enabled(mem);
212                         gtk_tree_store_set(store, &iter, COLUMN_SELECTED, mem->enabled, -1);
213                         change_made = 1;
214                         break;
215                 }
216                 if (mem)
217                         break;
218         }
219
220         g_free(name);
221 }
222
223 static GtkWidget *get_menubar_menu(GtkWidget *window)
224 {
225         GtkItemFactory *item_factory;
226         GtkAccelGroup *accel_group;
227
228         /* Make an accelerator group (shortcut keys) */
229         accel_group = gtk_accel_group_new();
230
231         /* Make an ItemFactory (that makes a menubar) */
232         item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>",
233                                         accel_group);
234
235         /* This function generates the menu items. Pass the item factory,
236            the number of items in the array, the array itself, and any
237            callback data for the menu items. */
238         gtk_item_factory_create_items(item_factory, nmenu_items, menu_items, NULL);
239
240         /* Attach the new accelerator group to the window. */
241         gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
242
243         /* Finally, return the actual menu bar created by the item factory. */
244         return gtk_item_factory_get_widget(item_factory, "<main>");
245 }
246
247 int run_menu(void)
248 {
249         int argc = 0;
250         char **argv = NULL;
251         GtkWidget *s_window;
252         GtkCellRenderer *renderer;
253         GtkTreeViewColumn *column;
254         GtkTreeStore *store;
255         struct category *cat;
256         struct member *mem;
257         GtkWidget *main_vbox;
258         GtkWidget *menubar;
259
260         gtk_init(&argc, &argv);
261
262         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
263         gtk_widget_set_size_request(window, 640, 480);
264         gtk_window_set_title(GTK_WINDOW(window), "GMenuselect");
265
266         main_vbox = gtk_vbox_new(FALSE, 1);
267         gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 1);
268         gtk_container_add(GTK_CONTAINER(window), main_vbox);
269
270         menubar = get_menubar_menu(window);
271         gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, FALSE, 0);
272
273         s_window = gtk_scrolled_window_new(NULL, NULL);
274
275         g_signal_connect(G_OBJECT(window), "delete_event",
276                          G_CALLBACK(delete_event), NULL);
277         g_signal_connect(G_OBJECT(window), "destroy",
278                          G_CALLBACK(destroy), NULL);
279
280         store = gtk_tree_store_new(NUM_COLUMNS,
281                 G_TYPE_STRING,   /* COLUMN_NAME */
282                 G_TYPE_BOOLEAN,  /* COLUMN_SELECTED */
283                 G_TYPE_STRING,   /* COLUMN_DEPS */
284                 G_TYPE_STRING,   /* COLUMN_USES */
285                 G_TYPE_STRING,  /* COLUMN_CNFS */
286                 G_TYPE_STRING);  /* COLUMN_DESC */
287
288         AST_LIST_TRAVERSE(&categories, cat, list) {
289                 GtkTreeIter iter, iter2;
290                 gtk_tree_store_append(store, &iter, NULL);
291                 gtk_tree_store_set(store, &iter,
292                         COLUMN_NAME, cat->displayname,
293                         COLUMN_SELECTED, TRUE,
294                         -1);
295                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
296                         char name_buf[64];
297                         char dep_buf[64] = "";
298                         char use_buf[64] = "";
299                         char cnf_buf[64] = "";
300                         struct reference *dep;
301                         struct reference *use;
302                         struct reference *cnf;
303
304                         AST_LIST_TRAVERSE(&mem->deps, dep, list) {
305                                 strncat(dep_buf, dep->displayname, sizeof(dep_buf) - strlen(dep_buf) - 1);
306                                 strncat(dep_buf, dep->member ? "(M)" : "(E)", sizeof(dep_buf) - strlen(dep_buf) - 1);
307                                 if (AST_LIST_NEXT(dep, list))
308                                         strncat(dep_buf, ", ", sizeof(dep_buf) - strlen(dep_buf) - 1);
309                         }
310                         AST_LIST_TRAVERSE(&mem->uses, use, list) {
311                                 strncat(use_buf, use->displayname, sizeof(use_buf) - strlen(use_buf) - 1);
312                                 if (AST_LIST_NEXT(use, list))
313                                         strncat(use_buf, ", ", sizeof(use_buf) - strlen(use_buf) - 1);
314                         }
315                         AST_LIST_TRAVERSE(&mem->conflicts, cnf, list) {
316                                 strncat(cnf_buf, cnf->displayname, sizeof(cnf_buf) - strlen(cnf_buf) - 1);
317                                 strncat(cnf_buf, cnf->member ? "(M)" : "(E)", sizeof(cnf_buf) - strlen(cnf_buf) - 1);
318                                 if (AST_LIST_NEXT(cnf, list))
319                                         strncat(cnf_buf, ", ", sizeof(cnf_buf) - strlen(cnf_buf) - 1);
320                         }
321
322                         if (mem->is_separator) {
323                                 snprintf(name_buf, sizeof(name_buf), "--- %s ---", mem->name);
324                         } else {
325                                 snprintf(name_buf, sizeof(name_buf), "%s", mem->name);
326                         }
327                         if (mem->depsfailed == HARD_FAILURE)
328                                 strncat(name_buf, " (Failed Deps.)", sizeof(name_buf) - strlen(name_buf) - 1);
329                         if (mem->conflictsfailed == HARD_FAILURE)
330                                 strncat(name_buf, " (In Conflict)", sizeof(name_buf) - strlen(name_buf) - 1);
331
332                         gtk_tree_store_append(store, &iter2, &iter);
333                         gtk_tree_store_set(store, &iter2,
334                                 COLUMN_NAME, name_buf,
335                                 COLUMN_SELECTED, mem->enabled,
336                                 COLUMN_DEPS, dep_buf,
337                                 COLUMN_USES, use_buf,
338                                 COLUMN_CNFS, cnf_buf,
339                                 COLUMN_DESC, mem->displayname,
340                                 -1);
341                 }
342         }
343
344         tree = (GtkTreeView *) gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
345
346 #if GTK_CHECK_VERSION(2,10,0)
347         gtk_tree_view_set_enable_tree_lines(tree, TRUE);
348         gtk_tree_view_set_grid_lines(tree, GTK_TREE_VIEW_GRID_LINES_BOTH);
349 #endif
350
351         renderer = gtk_cell_renderer_text_new();
352         column = gtk_tree_view_column_new_with_attributes("Name",
353                                 renderer, "text", COLUMN_NAME, NULL);
354         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
355
356         renderer = gtk_cell_renderer_toggle_new();
357         column = gtk_tree_view_column_new_with_attributes("Selected",
358                                 renderer, "active", COLUMN_SELECTED, NULL);
359         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
360         g_signal_connect(renderer, "toggled", (GCallback) toggled_handler, store);
361
362         renderer = gtk_cell_renderer_text_new();
363         column = gtk_tree_view_column_new_with_attributes("Depends On",
364                                 renderer, "text", COLUMN_DEPS, NULL);
365         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
366
367         renderer = gtk_cell_renderer_text_new();
368         column = gtk_tree_view_column_new_with_attributes("Can Use",
369                                 renderer, "text", COLUMN_USES, NULL);
370         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
371
372         renderer = gtk_cell_renderer_text_new();
373         column = gtk_tree_view_column_new_with_attributes("Conflicts With",
374                                 renderer, "text", COLUMN_CNFS, NULL);
375         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
376
377         renderer = gtk_cell_renderer_text_new();
378         column = gtk_tree_view_column_new_with_attributes("Description",
379                                 renderer, "text", COLUMN_DESC, NULL);
380         gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
381
382         g_signal_connect(tree, "row-activated", (GCallback) row_activated_handler, store);
383
384         gtk_container_add(GTK_CONTAINER(s_window), GTK_WIDGET(tree));
385
386         gtk_box_pack_end(GTK_BOX(main_vbox), s_window, TRUE, TRUE, 0);
387
388         gtk_widget_show_all(window);
389
390         gtk_main();
391
392         return main_res;
393 }