menuselect_gtk.c 11 KB

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