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