tvnodes.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. #include <assert.h>
  11. #include "tvnodes.h"
  12. #include "viewport.h"
  13. #include "topviewfuncs.h"
  14. #include <cgraph/strview.h>
  15. #include <cgraph/tokenize.h>
  16. #include <stdbool.h>
  17. #include <string.h>
  18. #include <util/alloc.h>
  19. typedef struct {
  20. GType type;
  21. char *name;
  22. bool editable;
  23. } gridCol;
  24. typedef struct {
  25. int count;
  26. gridCol *columns;
  27. GtkTreeStore *store;
  28. char *flds;
  29. char *buf;
  30. } grid_t;
  31. static char* ID = "ID";
  32. static char* Name = "Name";
  33. static char* Visible = "Visible";
  34. /* induceEdges:
  35. * Add all edges of g which have both ends in sg to sg
  36. */
  37. static void induceEdges (Agraph_t * g, Agraph_t * sg)
  38. {
  39. Agnode_t *n;
  40. Agedge_t *e;
  41. for (n = agfstnode(sg); n; n = agnxtnode(sg, n)) {
  42. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  43. if (agsubnode(sg, aghead(e), 0)) {
  44. agsubedge(sg, e, 1);
  45. }
  46. }
  47. }
  48. }
  49. /*
  50. call this function to create a subgraph from filtered nodes and maybe edges
  51. */
  52. static int create_save_subgraph_from_filter(char *filename, int withEdges)
  53. {
  54. Agraph_t *subg = agsubg(view->g[view->activeGraph], "temp", 1);
  55. FILE *outputfile;
  56. Agnode_t *v;
  57. Agraph_t *g;
  58. int ret;
  59. g = view->g[view->activeGraph];
  60. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  61. if (ND_selected(v))
  62. agsubnode(subg, v, 1);
  63. }
  64. if (withEdges)
  65. induceEdges (view->g[view->activeGraph], subg);
  66. if ((outputfile = fopen(filename, "w"))) {
  67. ret = agwrite(subg, outputfile);
  68. fclose(outputfile);
  69. } else {
  70. fprintf (stderr, "Could not open %s for writing\n", filename);
  71. ret = 1;
  72. }
  73. agdelsubg(view->g[view->activeGraph], subg);
  74. return ret;
  75. }
  76. static void set_visibility(Agraph_t * g, int visibility)
  77. {
  78. Agnode_t *v;
  79. char *bf;
  80. Agsym_t *visible_attr = GN_visible(g);
  81. Agsym_t *selected_attr = GN_selected(g);
  82. if (!selected_attr)
  83. return;
  84. if (!visible_attr)
  85. visible_attr = GN_visible(g) = agattr(g, AGNODE, "visible", "1");
  86. if (visibility)
  87. bf = "1";
  88. else
  89. bf = "0";
  90. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  91. if (!ND_selected(v)) continue;
  92. agxset(v, visible_attr, bf);
  93. }
  94. }
  95. int tv_show_all(void)
  96. {
  97. set_visibility(view->g[view->activeGraph], 1);
  98. updateSmGraph(view->g[view->activeGraph], view->Topview);
  99. return 1;
  100. }
  101. int tv_hide_all(void)
  102. {
  103. set_visibility(view->g[view->activeGraph], 0);
  104. updateSmGraph(view->g[view->activeGraph], view->Topview);
  105. return 1;
  106. }
  107. int tv_save_as(int withEdges)
  108. {
  109. GtkWidget *dialog;
  110. dialog = gtk_file_chooser_dialog_new("Save File",
  111. NULL,
  112. GTK_FILE_CHOOSER_ACTION_SAVE,
  113. GTK_STOCK_CANCEL,
  114. GTK_RESPONSE_CANCEL,
  115. GTK_STOCK_SAVE,
  116. GTK_RESPONSE_ACCEPT, NULL);
  117. gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER
  118. (dialog), TRUE);
  119. if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
  120. char *filename;
  121. filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
  122. create_save_subgraph_from_filter(filename, withEdges);
  123. g_free(filename);
  124. gtk_widget_destroy(dialog);
  125. return 1;
  126. }
  127. gtk_widget_destroy(dialog);
  128. return 0;
  129. }
  130. static void create_text_column(char *Title, GtkTreeView * tree, int asso,
  131. bool editable)
  132. {
  133. PangoColor c;
  134. GtkTreeViewColumn *column;
  135. GtkCellRendererText *renderer;
  136. renderer = (GtkCellRendererText *) gtk_cell_renderer_text_new();
  137. ((GtkCellRenderer *) renderer)->mode = GTK_CELL_RENDERER_MODE_EDITABLE;
  138. renderer->editable = editable;
  139. c.blue = 0;
  140. c.green = 1;
  141. c.red = 0;
  142. renderer->foreground = c;
  143. column =
  144. gtk_tree_view_column_new_with_attributes(Title,
  145. (GtkCellRenderer *)
  146. renderer, "text", asso,
  147. NULL);
  148. gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
  149. gtk_tree_view_column_set_resizable(column, 1);
  150. }
  151. static void create_toggle_column(char *Title, GtkTreeView * tree, int asso,
  152. bool editable)
  153. {
  154. GtkTreeViewColumn *column;
  155. GtkCellRendererToggle *renderer;
  156. renderer = (GtkCellRendererToggle *) gtk_cell_renderer_toggle_new();
  157. renderer->activatable = editable;
  158. column =
  159. gtk_tree_view_column_new_with_attributes(Title,
  160. (GtkCellRenderer *)
  161. renderer, "active", asso,
  162. NULL);
  163. gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
  164. gtk_tree_view_column_set_resizable(column, 1);
  165. }
  166. static void populate_data(Agraph_t *g, grid_t *grid) {
  167. Agnode_t *v;
  168. int id = 0;
  169. GtkTreeIter iter;
  170. GValue value = {0};
  171. char* bf;
  172. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  173. if (!ND_selected(v))
  174. continue;
  175. gtk_tree_store_append(grid->store, &iter, NULL);
  176. for (id = 0; id < grid->count; id++) {
  177. gridCol *cp = &grid->columns[id];
  178. if (strcmp(cp->name, ID) == 0) continue;
  179. if (strcmp(cp->name, Name) == 0)
  180. bf = agnameof(v);
  181. else
  182. bf = agget(v, cp->name);
  183. if (!bf && strcmp(cp->name, Visible) != 0)
  184. continue;
  185. g_value_init(&value, cp->type);
  186. switch (grid->columns[id].type) {
  187. case G_TYPE_BOOLEAN:
  188. if (bf) {
  189. if ((strcmp(bf, "1") == 0) || (strcmp(bf, "true") == 0)
  190. || (strcmp(bf, "True") == 0))
  191. g_value_set_boolean(&value, 1);
  192. else
  193. g_value_set_boolean(&value, 0);
  194. } else {
  195. if (strcmp(cp->name, Visible) == 0)
  196. g_value_set_boolean(&value, 1);
  197. }
  198. break;
  199. default:
  200. g_value_set_static_string(&value, bf);
  201. }
  202. gtk_tree_store_set_value(grid->store, &iter, id, &value);
  203. g_value_unset(&value);
  204. }
  205. }
  206. }
  207. static GtkTreeStore *update_tree_store(GtkTreeStore * store, int ncolumns,
  208. GType * types)
  209. {
  210. if ((ncolumns == 0) || (types == NULL))
  211. return NULL;
  212. if (store) {
  213. gtk_tree_store_clear(store);
  214. g_object_unref(store);
  215. }
  216. store = gtk_tree_store_newv(ncolumns, types);
  217. return store;
  218. }
  219. static void create_column(gridCol * c, GtkTreeView * tree, int id)
  220. {
  221. assert(c != NULL);
  222. switch (c->type) {
  223. case G_TYPE_STRING:
  224. case G_TYPE_INT:
  225. create_text_column(c->name, tree, id, c->editable);
  226. break;
  227. case G_TYPE_BOOLEAN:
  228. create_toggle_column(c->name, tree, id, c->editable);
  229. break;
  230. default:
  231. create_text_column(c->name, tree, id, c->editable);
  232. }
  233. }
  234. static GtkTreeView *update_tree(GtkTreeView *tree, grid_t *g) {
  235. GtkTreeStore *store = NULL;
  236. GtkTreeViewColumn *column;
  237. GType *types;
  238. int id = 0;
  239. if (tree) {
  240. while ((column = gtk_tree_view_get_column(tree, 0))) /*clear all columns */
  241. gtk_tree_view_remove_column(tree, column);
  242. store = (GtkTreeStore *) gtk_tree_view_get_model(tree);
  243. } else {
  244. tree = (GtkTreeView *) gtk_tree_view_new();
  245. gtk_widget_show((GtkWidget *) tree);
  246. gtk_container_add((GtkContainer *)
  247. glade_xml_get_widget(xml, "scrolledwindow9"),
  248. (GtkWidget *) tree);
  249. }
  250. if (g->count > 0) {
  251. types = gv_calloc((size_t)g->count, sizeof(GType));
  252. for (id = 0; id < g->count; id++)
  253. types[id] = g->columns[id].type;
  254. store = update_tree_store(g->store, g->count, types);
  255. free (types);
  256. gtk_tree_view_set_model(tree, (GtkTreeModel *) store);
  257. /*insert columns */
  258. for (id = 0; id < g->count; id++)
  259. create_column(&g->columns[id], tree, id);
  260. }
  261. g->store = store;
  262. return tree;
  263. }
  264. static void add_column(grid_t *g, strview_t name, bool editable, GType g_type) {
  265. if (strview_str_eq(name, ""))
  266. return;
  267. assert(g->count >= 0);
  268. g->columns = gv_recalloc(g->columns, (size_t)g->count, (size_t)g->count + 1,
  269. sizeof(gridCol));
  270. g->columns[g->count].editable = editable;
  271. g->columns[g->count].name = strview_str(name);
  272. g->columns[g->count].type = g_type;
  273. g->count++;
  274. }
  275. static void clearGrid(grid_t * g) {
  276. int id;
  277. for (id = 0; id < g->count; id++) {
  278. free(g->columns[id].name);
  279. }
  280. free(g->columns);
  281. free(g->buf);
  282. g->count = 0;
  283. g->columns = 0;
  284. g->flds = 0;
  285. }
  286. static grid_t *initGrid(void) {
  287. grid_t *gr = gv_alloc(sizeof(grid_t));
  288. gr->columns = NULL;
  289. gr->count = 0;
  290. gr->buf = 0;
  291. return gr;
  292. }
  293. static grid_t *update_columns(grid_t *g, char *str) {
  294. if (g) {
  295. if (g->flds != str)
  296. clearGrid(g);
  297. else
  298. return g;
  299. } else
  300. g = initGrid();
  301. add_column(g, strview(Name, '\0'), false, G_TYPE_STRING);
  302. if (!str)
  303. return g;
  304. g->flds = str;
  305. for (tok_t t = tok(str, ","); !tok_end(&t); tok_next(&t)) {
  306. strview_t a = tok_get(&t);
  307. add_column(g, a, true, G_TYPE_STRING);
  308. }
  309. return g;
  310. }
  311. void setup_tree(Agraph_t * g)
  312. {
  313. /*
  314. G_TYPE_STRING:
  315. G_TYPE_INT:
  316. G_TYPE_BOOLEAN:
  317. */
  318. static GtkTreeView *tree;
  319. static grid_t *gr;
  320. char *buf = agget(g, "datacolumns");
  321. gr = update_columns(gr, buf);
  322. tree = update_tree(tree, gr);
  323. populate_data(g, gr);
  324. }