frmobjectui.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  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 <stdbool.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include "gui.h"
  14. #include <glade/glade.h>
  15. #include <gdk/gdkkeysyms.h>
  16. #include <gdk/gdk.h>
  17. #include "viewport.h"
  18. #include "frmobjectui.h"
  19. #include <assert.h>
  20. #include "gvprpipe.h"
  21. #include <cgraph/strview.h>
  22. #include <stdint.h>
  23. #include <string.h>
  24. #include <util/agxbuf.h>
  25. #include <util/alloc.h>
  26. #include <util/strcasecmp.h>
  27. #include <util/startswith.h>
  28. #include <util/unreachable.h>
  29. static int sel_node;
  30. static int sel_edge;
  31. static int sel_graph;
  32. static char *safestrdup(const char *src) {
  33. if (!src)
  34. return NULL;
  35. else
  36. return gv_strdup(src);
  37. }
  38. static int get_object_type(void)
  39. {
  40. if (gtk_toggle_button_get_active
  41. ((GtkToggleButton *) glade_xml_get_widget(xml, "attrRB0")))
  42. return AGRAPH;
  43. if (gtk_toggle_button_get_active
  44. ((GtkToggleButton *) glade_xml_get_widget(xml, "attrRB1")))
  45. return AGNODE;
  46. if (gtk_toggle_button_get_active
  47. ((GtkToggleButton *) glade_xml_get_widget(xml, "attrRB2")))
  48. return AGEDGE;
  49. return -1;
  50. }
  51. static attr_t *new_attr(void) {
  52. return gv_alloc(sizeof(attr_t));
  53. }
  54. static attr_t *new_attr_with_ref(Agsym_t * sym)
  55. {
  56. attr_t *a = new_attr();
  57. a->name = gv_strdup(sym->name);
  58. switch (sym->kind) {
  59. case AGRAPH:
  60. a->objType[0] = 1;
  61. a->defValG = safestrdup(sym->defval);
  62. break;
  63. case AGNODE:
  64. a->objType[1] = 1;
  65. a->defValN = safestrdup(sym->defval);
  66. break;
  67. case AGEDGE:
  68. a->objType[2] = 1;
  69. a->defValE = safestrdup(sym->defval);
  70. break;
  71. default:
  72. UNREACHABLE();
  73. }
  74. return a;
  75. }
  76. static attr_t *new_attr_ref(attr_t * refAttr)
  77. {
  78. attr_t *a = gv_alloc(sizeof(attr_t));
  79. *a = *refAttr;
  80. a->defValG = safestrdup(refAttr->defValG);
  81. a->defValN = safestrdup(refAttr->defValN);
  82. a->defValE = safestrdup(refAttr->defValE);
  83. a->name = gv_strdup(refAttr->name);
  84. a->value = safestrdup(refAttr->value);
  85. return a;
  86. }
  87. static void reset_attr_list_widgets(attr_list * l)
  88. {
  89. int id;
  90. for (id = 0; id < MAX_FILTERED_ATTR_COUNT; id++) {
  91. gtk_label_set_text(l->fLabels[id], "");
  92. }
  93. }
  94. // creates a new attr_list
  95. // attr_list is a basic stack implementation
  96. // with alphanumeric sorting functions
  97. // that uses quicksort
  98. static attr_list *attr_list_new(bool with_widgets) {
  99. int id;
  100. attr_list *l = gv_alloc(sizeof(attr_list));
  101. /*create filter widgets */
  102. if (with_widgets) {
  103. for (id = 0; id < MAX_FILTERED_ATTR_COUNT; id++) {
  104. l->fLabels[id] = (GtkLabel *) gtk_label_new("");
  105. gtk_widget_add_events((GtkWidget *) l->fLabels[id],
  106. GDK_BUTTON_MOTION_MASK |
  107. GDK_POINTER_MOTION_MASK |
  108. GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS |
  109. GDK_BUTTON_RELEASE_MASK |
  110. GDK_SCROLL | GDK_VISIBILITY_NOTIFY_MASK);
  111. gtk_widget_show((GtkWidget *) l->fLabels[id]);
  112. Color_Widget_bg("blue", (GtkWidget *) l->fLabels[id]);
  113. gtk_fixed_put((GtkFixed *) glade_xml_get_widget(xml, "fixed6"),
  114. (GtkWidget *) l->fLabels[id], 10, 110 + id * 13);
  115. }
  116. }
  117. return l;
  118. }
  119. static int attr_compare_core(const void *key, const void *candidate) {
  120. const char *k = key;
  121. const attr_t *const *c = candidate;
  122. return strcasecmp(k, (*c)->name);
  123. }
  124. static int attr_compare(const attr_t **a, const attr_t **b) {
  125. return attr_compare_core((*a)->name, b);
  126. }
  127. static void attr_list_add(attr_list *l, attr_t *a) {
  128. if (!l || !a)
  129. return;
  130. attrs_append(&l->attributes, a);
  131. attrs_sort(&l->attributes, attr_compare);
  132. /*update indices */
  133. for (size_t id = 0; id < attrs_size(&l->attributes); ++id)
  134. attrs_get(&l->attributes, id)->index = id;
  135. }
  136. static attr_data_type get_attr_data_type(char c)
  137. {
  138. switch (c) {
  139. case 'A':
  140. return attr_alpha;
  141. break;
  142. case 'F':
  143. return attr_float;
  144. break;
  145. case 'B':
  146. return attr_bool;
  147. break;
  148. case 'I':
  149. return attr_int;
  150. break;
  151. default:
  152. break;
  153. }
  154. return attr_alpha;
  155. }
  156. static void object_type_helper(strview_t a, int *t) {
  157. if (strview_str_eq(a, "GRAPH"))
  158. t[0] = 1;
  159. if (strview_str_eq(a, "CLUSTER"))
  160. t[0] = 1;
  161. if (strview_str_eq(a, "NODE"))
  162. t[1] = 1;
  163. if (strview_str_eq(a, "EDGE"))
  164. t[2] = 1;
  165. if (strview_str_eq(a, "ANY_ELEMENT")) {
  166. t[0] = 1;
  167. t[1] = 1;
  168. t[2] = 1;
  169. }
  170. }
  171. static void set_attr_object_type(const char *str, int *t) {
  172. strview_t a = strview(str, ' ');
  173. object_type_helper(a, t);
  174. while (true) {
  175. const char *start = a.data + a.size;
  176. if (!startswith(start, " or ")) {
  177. break;
  178. }
  179. start += strlen(" or ");
  180. const char *end = strstr(start, " or ");
  181. if (end == NULL) {
  182. a = strview(start, '\0');
  183. } else {
  184. a = (strview_t){.data = start, .size = (size_t)(end - start)};
  185. }
  186. object_type_helper(a, t);
  187. }
  188. }
  189. static attr_t *binarySearch(attr_list *l, const char *searchKey) {
  190. attrs_sync(&l->attributes);
  191. attr_t **attrp = bsearch(searchKey, attrs_front(&l->attributes),
  192. attrs_size(&l->attributes), sizeof(attr_t*),
  193. attr_compare_core);
  194. if (attrp != NULL) {
  195. return *attrp;
  196. }
  197. return NULL;
  198. }
  199. static int cmp(const void *key, const void *candidate) {
  200. const attr_t *const a = candidate;
  201. return strncasecmp(key, a->name, strlen(key));
  202. }
  203. static void create_filtered_list(const char *prefix, attr_list *sl,
  204. attr_list *tl) {
  205. int objKind = get_object_type();
  206. if (strlen(prefix) == 0)
  207. return;
  208. /*locate first occurrence */
  209. attrs_sync(&sl->attributes);
  210. attr_t *at = bsearch(prefix, attrs_front(&sl->attributes),
  211. attrs_size(&sl->attributes), sizeof(attr_t), cmp);
  212. if (!at)
  213. return;
  214. /*go backward to get the first */
  215. for (int res = 0; at->index > 0 && res == 0; ) {
  216. at = attrs_get(&sl->attributes, at->index - 1);
  217. res = strncasecmp(prefix, at->name, strlen(prefix));
  218. }
  219. for (int res = 0; at->index < attrs_size(&sl->attributes) && res == 0; ) {
  220. at = attrs_get(&sl->attributes, at->index + 1);
  221. res = strncasecmp(prefix, at->name, strlen(prefix));
  222. if (res == 0 && at->objType[objKind] == 1)
  223. attr_list_add(tl, new_attr_ref(at));
  224. }
  225. }
  226. static void filter_attributes(const char *prefix, topview *t) {
  227. int tmp;
  228. attr_list *l = t->attributes;
  229. int objKind = get_object_type();
  230. attr_list *fl = attr_list_new(false);
  231. reset_attr_list_widgets(l);
  232. create_filtered_list(prefix, l, fl);
  233. for (size_t ind = 0; ind < attrs_size(&fl->attributes); ++ind) {
  234. gtk_label_set_text(l->fLabels[ind], attrs_get(&fl->attributes, ind)->name);
  235. }
  236. Color_Widget_bg("white", glade_xml_get_widget(xml, "txtAttr"));
  237. if (attrs_is_empty(&fl->attributes))
  238. Color_Widget_bg("red", glade_xml_get_widget(xml, "txtAttr"));
  239. /*a new attribute can be entered */
  240. gtk_widget_show(glade_xml_get_widget(xml, "txtValue"));
  241. gtk_widget_show(glade_xml_get_widget(xml, "txtDefValue"));
  242. gtk_entry_set_text((GtkEntry *)
  243. glade_xml_get_widget(xml, "txtDefValue"), "");
  244. gtk_entry_set_text((GtkEntry *) glade_xml_get_widget(xml, "txtValue"),
  245. "");
  246. gtk_widget_set_sensitive(glade_xml_get_widget(xml, "txtDefValue"), 1);
  247. gtk_widget_show(glade_xml_get_widget(xml, "attrAddBtn"));
  248. gtk_widget_hide(glade_xml_get_widget(xml, "attrApplyBtn"));
  249. gtk_widget_hide(glade_xml_get_widget(xml, "attrApplyAllBtn"));
  250. gtk_widget_hide(glade_xml_get_widget(xml, "attrSearchBtn"));
  251. gtk_toggle_button_set_active((GtkToggleButton *)
  252. glade_xml_get_widget(xml, "attrProg"), 0);
  253. if (strlen(prefix) == 0) {
  254. gtk_widget_hide(glade_xml_get_widget(xml, "attrAddBtn"));
  255. gtk_widget_hide(glade_xml_get_widget(xml, "attrApplyBtn"));
  256. gtk_widget_hide(glade_xml_get_widget(xml, "attrApplyAllBtn"));
  257. gtk_widget_hide(glade_xml_get_widget(xml, "attrSearchBtn"));
  258. gtk_widget_hide(glade_xml_get_widget(xml, "attrAddBtn"));
  259. gtk_widget_hide(glade_xml_get_widget(xml, "txtValue"));
  260. gtk_widget_hide(glade_xml_get_widget(xml, "txtDefValue"));
  261. Color_Widget_bg("white", glade_xml_get_widget(xml, "txtAttr"));
  262. }
  263. for (size_t ind = 0; ind < attrs_size(&fl->attributes); ++ind) {
  264. if (strcasecmp(prefix, attrs_get(&fl->attributes, ind)->name) == 0) { // an existing attribute
  265. Color_Widget_bg("green", glade_xml_get_widget(xml, "txtAttr"));
  266. if (get_object_type() == AGRAPH)
  267. gtk_entry_set_text((GtkEntry *)
  268. glade_xml_get_widget(xml,
  269. "txtDefValue"),
  270. attrs_get(&fl->attributes, 0)->defValG);
  271. if (get_object_type() == AGNODE)
  272. gtk_entry_set_text((GtkEntry *)
  273. glade_xml_get_widget(xml,
  274. "txtDefValue"),
  275. attrs_get(&fl->attributes, 0)->defValN);
  276. if (get_object_type() == AGEDGE)
  277. gtk_entry_set_text((GtkEntry *)
  278. glade_xml_get_widget(xml,
  279. "txtDefValue"),
  280. attrs_get(&fl->attributes, 0)->defValE);
  281. gtk_widget_set_sensitive(glade_xml_get_widget
  282. (xml, "txtDefValue"), 0);
  283. gtk_widget_hide(glade_xml_get_widget(xml, "attrAddBtn"));
  284. gtk_widget_show(glade_xml_get_widget(xml, "attrApplyBtn"));
  285. gtk_widget_show(glade_xml_get_widget(xml, "attrApplyAllBtn"));
  286. gtk_widget_show(glade_xml_get_widget(xml, "attrSearchBtn"));
  287. gtk_toggle_button_set_active((GtkToggleButton *)
  288. glade_xml_get_widget(xml,
  289. "attrProg"),
  290. attrs_get(&fl->attributes, 0)->propagate);
  291. break;
  292. }
  293. }
  294. tmp = (objKind == AGNODE && sel_node)
  295. || (objKind == AGEDGE && sel_edge)
  296. || (objKind == AGRAPH && sel_graph);
  297. gtk_widget_set_sensitive(glade_xml_get_widget(xml, "attrApplyBtn"),
  298. tmp);
  299. }
  300. // attribute text changed call back
  301. _BB void on_txtAttr_changed(GtkWidget *widget, void *user_data) {
  302. (void)user_data;
  303. filter_attributes(gtk_entry_get_text((GtkEntry*)widget), view->Topview);
  304. }
  305. static void doApply(void)
  306. {
  307. char *attr_name;
  308. int prog;
  309. Agnode_t *v;
  310. Agedge_t *e;
  311. Agraph_t *g;
  312. int objKind;
  313. Agsym_t *sym;
  314. attr_t *attr;
  315. /*values to be applied to selected objects */
  316. attr_name =
  317. (char *) gtk_entry_get_text((GtkEntry *)
  318. glade_xml_get_widget(xml, "txtAttr"));
  319. const char *def_val = gtk_entry_get_text((GtkEntry *)
  320. glade_xml_get_widget(xml,
  321. "txtDefValue"));
  322. const char *value = gtk_entry_get_text((GtkEntry *)
  323. glade_xml_get_widget(xml, "txtValue"));
  324. prog =
  325. gtk_toggle_button_get_active((GtkToggleButton *)
  326. glade_xml_get_widget(xml,
  327. "attrProg"));
  328. g = view->g[view->activeGraph];
  329. objKind = get_object_type();
  330. attr = binarySearch(view->Topview->attributes, attr_name);
  331. assert(attr);
  332. attr->propagate = prog;
  333. sym = agattr(g, objKind, attr_name, NULL);
  334. if (!sym) /*it shouldnt be null, just in case it is null */
  335. sym = agattr(g, objKind, attr_name, def_val);
  336. /*graph */
  337. if (objKind == AGRAPH)
  338. agset(g, attr_name, value);
  339. /*nodes */
  340. else if (objKind == AGNODE) {
  341. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  342. if (ND_selected(v))
  343. agxset(v, sym, value);
  344. }
  345. }
  346. /*edges */
  347. else if (objKind == AGEDGE) {
  348. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  349. for (e = agfstout(g, v); e; e = agnxtout(g, e)) {
  350. if (ED_selected(e))
  351. agxset(e, sym, value);
  352. }
  353. }
  354. } else
  355. fprintf(stderr,
  356. "on_attrApplyBtn_clicked: unknown object kind %d\n",
  357. objKind);
  358. }
  359. _BB void on_attrApplyBtn_clicked(GtkWidget *widget, void *user_data) {
  360. (void)widget;
  361. (void)user_data;
  362. doApply();
  363. }
  364. _BB void on_attrRB0_clicked(GtkWidget *widget, void *user_data) {
  365. (void)widget;
  366. (void)user_data;
  367. filter_attributes(gtk_entry_get_text((GtkEntry*)glade_xml_get_widget(xml,
  368. "txtAttr")), view->Topview);
  369. }
  370. /* This is the action attached to the publish button on the attributes
  371. * window. What should happen?
  372. */
  373. _BB void on_attrProg_toggled(GtkWidget *widget, void *user_data) {
  374. (void)widget;
  375. (void)user_data;
  376. /* FIX */
  377. }
  378. _BB void on_attrAddBtn_clicked(GtkWidget *widget, void *user_data) {
  379. (void)widget;
  380. (void)user_data;
  381. char *attr_name;
  382. int objKind;
  383. topview *t;
  384. Agraph_t *g;
  385. attr_t *attr;
  386. objKind = get_object_type();
  387. /*values to be applied to selected objects */
  388. attr_name =
  389. (char *) gtk_entry_get_text((GtkEntry *)
  390. glade_xml_get_widget(xml, "txtAttr"));
  391. const char *defValue = gtk_entry_get_text((GtkEntry *)
  392. glade_xml_get_widget(xml,
  393. "txtDefValue"));
  394. g = view->g[view->activeGraph];
  395. t = view->Topview;
  396. /*try to find first */
  397. attr = binarySearch(t->attributes, attr_name);
  398. if (!attr) {
  399. attr = new_attr();
  400. attr->index = 0;
  401. attr->name = safestrdup(attr_name);
  402. attr->type = attr_alpha;
  403. attr->value = gv_strdup("");
  404. attr->widget = NULL;
  405. attr_list_add(t->attributes, attr);
  406. }
  407. attr->propagate = 0;
  408. if (objKind == AGRAPH) {
  409. agattr(g, AGRAPH, attr_name, defValue);
  410. attr->defValG = safestrdup(defValue);
  411. attr->objType[0] = 1;
  412. }
  413. /*nodes */
  414. else if (objKind == AGNODE) {
  415. agattr(g, AGNODE, attr_name, defValue);
  416. attr->defValN = safestrdup(defValue);
  417. attr->objType[1] = 1;
  418. } else if (objKind == AGEDGE) {
  419. agattr(g, AGEDGE, attr_name, defValue);
  420. attr->defValE = safestrdup(defValue);
  421. attr->objType[2] = 1;
  422. } else
  423. fprintf(stderr, "on_attrAddBtn_clicked: unknown object kind %d\n",
  424. objKind);
  425. filter_attributes(attr_name, view->Topview);
  426. }
  427. attr_list *load_attr_list(Agraph_t * g)
  428. {
  429. attr_t *attr;
  430. attr_list *l;
  431. FILE *file;
  432. char buffer[BUFSIZ];
  433. static char *smyrna_attrs;
  434. char *a;
  435. if (!smyrna_attrs)
  436. smyrna_attrs = smyrnaPath("attrs.txt");
  437. g = view->g[view->activeGraph];
  438. l = attr_list_new(true);
  439. file = fopen(smyrna_attrs, "r");
  440. if (file != NULL) {
  441. for (size_t i = 0; fgets(buffer, sizeof(buffer), file) != NULL; ++i) {
  442. attr = new_attr();
  443. a = strtok(buffer, ",");
  444. attr->index = i;
  445. attr->type = get_attr_data_type(a[0]);
  446. for (int idx = 0; (a = strtok(NULL, ",")); ++idx) {
  447. /*C,(0)color, (1)black, (2)EDGE Or NODE Or CLUSTER, (3)ALL_ENGINES */
  448. switch (idx) {
  449. case 0:
  450. attr->name = gv_strdup(a);
  451. break;
  452. case 1:
  453. attr->defValG = gv_strdup(a);
  454. attr->defValN = gv_strdup(a);
  455. attr->defValE = gv_strdup(a);
  456. break;
  457. case 2:
  458. set_attr_object_type(a, attr->objType);
  459. break;
  460. default:
  461. break;
  462. }
  463. }
  464. attr_list_add(l, attr);
  465. }
  466. fclose(file);
  467. }
  468. for (Agsym_t *sym = NULL; (sym = agnxtattr(g, AGRAPH, sym)); ) {
  469. attr = binarySearch(l, sym->name);
  470. if (attr)
  471. attr->objType[0] = 1;
  472. else {
  473. attr = new_attr_with_ref(sym);
  474. attr_list_add(l, attr);
  475. }
  476. }
  477. for (Agsym_t *sym = NULL; (sym = agnxtattr(g, AGNODE, sym)); ) {
  478. attr = binarySearch(l, sym->name);
  479. if (attr) {
  480. attr->objType[1] = 1;
  481. } else {
  482. attr = new_attr_with_ref(sym);
  483. attr_list_add(l, attr);
  484. }
  485. }
  486. for (Agsym_t *sym = NULL; (sym = agnxtattr(g, AGEDGE, sym)); ) {
  487. attr = binarySearch(l, sym->name);
  488. if (attr)
  489. attr->objType[2] = 1;
  490. else {
  491. attr = new_attr_with_ref(sym);
  492. attr_list_add(l, attr);
  493. }
  494. }
  495. return l;
  496. }
  497. static void set_header_text(void)
  498. {
  499. int nodeCnt = 0;
  500. int edgeCnt = 0;
  501. char buf[512];
  502. Agedge_t* ep;
  503. Agnode_t* v;
  504. Agraph_t* g;
  505. g = view->g[view->activeGraph];
  506. for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
  507. if (ND_selected(v))
  508. nodeCnt++;
  509. for (ep = agfstout(g, v); ep; ep = agnxtout(g, ep)) {
  510. if (ND_selected(v))
  511. edgeCnt++;
  512. }
  513. }
  514. sel_node = nodeCnt;
  515. sel_edge = edgeCnt;
  516. sel_graph = 1;
  517. snprintf(buf, sizeof(buf), "%d Nodes and %d edges selected", nodeCnt,
  518. edgeCnt);
  519. gtk_label_set_text((GtkLabel *) glade_xml_get_widget(xml, "label124"),
  520. buf);
  521. gtk_entry_set_text((GtkEntry *) glade_xml_get_widget(xml, "txtAttr"),
  522. "");
  523. Color_Widget_bg("white", glade_xml_get_widget(xml, "fixed6"));
  524. }
  525. void showAttrsWidget(void) {
  526. gtk_widget_hide(glade_xml_get_widget(xml, "dlgSettings"));
  527. gtk_widget_show(glade_xml_get_widget(xml, "dlgSettings"));
  528. gtk_notebook_set_current_page((GtkNotebook *)
  529. glade_xml_get_widget(xml, "notebook3"),
  530. ATTR_NOTEBOOK_IDX);
  531. set_header_text();
  532. filter_attributes("", view->Topview);
  533. }
  534. static void gvpr_select(const char *attrname, const char *regex_str,
  535. int objType) {
  536. char *bf2;
  537. int i, argc;
  538. agxbuf sf = {0};
  539. if (objType == AGNODE)
  540. agxbprint(&sf, "N[%s==\"%s\"]{selected = \"1\"}", attrname, regex_str);
  541. else if (objType == AGEDGE)
  542. agxbprint(&sf, "E[%s==\"%s\"]{selected = \"1\"}", attrname, regex_str);
  543. bf2 = agxbdisown(&sf);
  544. argc = 1;
  545. if (*bf2 != '\0')
  546. argc++;
  547. char *argv[3] = {0};
  548. size_t j = 0;
  549. argv[j++] = "smyrna";
  550. argv[j++] = bf2;
  551. run_gvpr(view->g[view->activeGraph], j, argv);
  552. for (i = 1; i < argc; i++)
  553. free(argv[i]);
  554. set_header_text();
  555. }
  556. _BB void on_attrSearchBtn_clicked(GtkWidget *widget, void *user_data) {
  557. (void)widget;
  558. (void)user_data;
  559. const char *attrname = gtk_entry_get_text((GtkEntry *)
  560. glade_xml_get_widget(xml, "txtAttr"));
  561. const char *regex_str = gtk_entry_get_text((GtkEntry *)
  562. glade_xml_get_widget(xml, "txtValue"));
  563. gvpr_select(attrname, regex_str, get_object_type());
  564. }