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