-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathplugin.cpp
2889 lines (2520 loc) · 68.4 KB
/
plugin.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
GraphSlick (c) Elias Bachaalany
-------------------------------------
Plugin module
This module is responsible for handling IDA plugin code
History
--------
10/15/2013 - eliasb - First version
10/21/2013 - eliasb - Working chooser / graph renderer
10/22/2013 - eliasb - Version with working selection of NodeDefLists and GroupDefs
- Now the graph-view closes when the panel closes
- Factored out functions from the grdata_t class
- Wrote initial combine nodes algorithm
10/23/2013 - eliasb - Polished and completed the combine algorithm
- Factored out many code into various modules
10/24/2013 - eliasb - Added proper coloring on selection (via colorgen class)
- Factored out many code into various modules
- Fixed crash on re-opening the plugin chooser
10/25/2013 - eliasb - Refactored the code further
- Renamed from grdata_t to a full graphview class (gsgraphview_t)
- Devised the graph context menu system
- Added clear/toggle selection mode
- Added graph view mode functionality
- Dock the gsgraphview next to IDA-View
- Factored out get_color_anyway() to the colorgen module
- Proper support for node selection/coloring in single and combined mode
- Change naming from chooser nodes to chooser line
10/28/2013 - eliasb - Separated highlight and selection
- Added support for 'user hint' on the node graph
- Added 'Load bbgroup' support
- Added quick selection support (no UI for it though)
- Added reload input file functionality
10/29/2013 - eliasb - Added 'Highlight similar nodes' placeholder code and UI
- Refactored chooser population code into 'populate_chooser_lines()'
- Speed optimization: generate flowchart once in the chooser and pass it to other functions
- Added find_and_highlight_nodes() to the graph
10/30/2013 - eliasb - Adapted to new class names
- Added to-do placeholder for parameters that could be passed as options
10/31/2013 - eliasb - Get rid of the 'additive' parameter in set_highlighted_nodes
- renamed set_highlighted_nodes() to highlight_nodes(). it also checks whether to highlight or not
(thus the caller does not have to do redundant highlight/lazy checks)
- Introduced the options class and parameterized all relevant function calls
- Added 'refresh view' and 'show options' context menus
- Introduced get_ng_id() helper function to resolve node ids automatically based on current view mode
- Fixed wrong node id resolution upon double click on chooser lines
11/01/2013 - eliasb - Introduced redo_layout() to actually refresh and make the layout
- refresh_view() is a placeholder for a hack that will cause a screen repaint
- renamed 'lazy_sh_mode' to 'manual_refresh_mode'
- added a second chooser column to show EA of first ND in the NG
- got rid of 'refresh view' context menu item -> redundant with IDA's layout graph
11/04/2013 - eliasb - added 'debug' option
- "Edit SG" is no more dynamic. It seems it is not possible to add menu item inside the grcallback / refresh cb
- Added "Combine nodes" functionality
- refactored graph mode view switch into functions
- Added get_ng_from_ngid(ngid) utility function to do reverse ng2nid lookup
- Added ngid_to_sg(ngid) utility function to do ngid to containing SG lookup
- Added gsgv_actions_t to allow better interfacing between GSGV and GSCH
- Added Jump to next highlighted/selected nodes
- Added save bbgroup functionality
- Added chooser on_show() event -> show copyright message on start
11/06/2013 - eliasb - Added select all
- Added graph layout selection
- Added focus_node after a group or split operation
- Added Promote nodes
- Added "Move nodes to their own NGs"
- Added "Merge highlight with selection"
- Added "Jump to next highlight" / "Selection"
- Added chooser menu -> show graph (in case the graph was closed)
- Added "Reset groupping"
11/07/2013 - eliasb - Removed the Orthogonal layout was not implemented in IDA <=6.4, causing a "not yet" messages
- Added initial Python adapter code
04/15/2014 - eliasb - Try to load screen function bbgroup on load
- Added PUBLIC define to compile-out a few experimental features
04/16/2014 - eliasb - Added NO_PYTHON compile define
09/24/2014 - eliasb - Integrated changes from Hex-Rays, thanks to Arnaud Diederen
TODO
-----------
[ ] Add chooser that shows only SGs so one can move an NG to the dest SG
[ ] Ungroup -> move NDs to own SGs
[ ] Add code to handle operations group/ungroup from chooser
[ ] Expose the py::analyze function to chooser context menu
[ ] Augment bbload to call py::load ; same for SAVE
[ ] py::load should return
[ ] bug: somehow ungroupping all nodes is not correct, when analyze is used -> i experienced a crash
--------------------------------------------------------------------------*/
//--------------------------------------------------------------------------
#ifdef __NT__
#pragma warning(disable: 4018 4800)
#endif
#include <ida.hpp>
#include <idp.hpp>
#include <graph.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include <diskio.hpp>
#include <prodir.h>
#include "groupman.h"
#include "util.h"
#include "algo.hpp"
#include "colorgen.h"
#include "pybbmatcher.h"
//--------------------------------------------------------------------------
// Some defines
#define PUBLIC
//#define NO_PYTHON
//--------------------------------------------------------------------------
#define MY_TABSTR " "
#define STR_GS_MSG "GS: "
#define BBGROUP_EXT "bbgroup"
//--------------------------------------------------------------------------
static const char STR_CANNOT_BUILD_F_FC[] = "Cannot build function flowchart!";
static const char STR_PLGNAME[] = "GraphSlick";
static const char TITLE_GS_PANEL[] = "Graph Slick - Panel";
static const char STR_GS_VIEW[] = "Graph Slick - View";
static const char STR_OUTWIN_TITLE[] = "Output window";
static const char STR_IDAVIEWA_TITLE[] = "IDA View-A";
static const char STR_SEARCH_PROMPT[] = "Please enter search string";
static const char STR_DUMMY_SG_NAME[] = "No name";
static const char STR_GS_PY_PLGFILE[] = "GraphSlick" SDIRCHAR "init.py";
//--------------------------------------------------------------------------
typedef std::map<int, bgcolor_t> ncolormap_t;
const bgcolor_t NODE_SEL_COLOR = 0x7C75AD;
//--------------------------------------------------------------------------
enum gvrefresh_modes_e
{
gvrfm_soft,
gvrfm_single_mode,
gvrfm_combined_mode,
};
//--------------------------------------------------------------------------
#define DECL_CG \
colorgen_t cg; \
cg.L_INT = -15
//--------------------------------------------------------------------------
/**
* @brief GraphSlick options handling class
*/
struct gsoptions_t
{
/**
* @brief Append node id to the node text
*/
bool append_node_id;
/**
* @brief Do not propose initial path information on Analyze()
*/
bool no_initial_path_info;
/**
* @brief Manual refresh view on selection/highlight
*/
bool manual_refresh_mode;
/**
* @brief Highlight synthetic nodes
*/
bool highlight_syntethic_nodes;
/**
* @brief Should the options dialog be shown the next time?
*/
bool show_options_dialog_next_time;
/**
* @brief If the group name is one line then add a few more lines
* to make it look bigger
*/
bool enlarge_group_name;
/**
* @brief Display debug messages
*/
bool debug;
/**
* @brief Graph layout
*/
layout_type_t graph_layout;
/**
* @brief GraphSlick start up view mode
*/
gvrefresh_modes_e start_view_mode;
/**
* @brief Constructor
*/
gsoptions_t()
{
manual_refresh_mode = true;
append_node_id = false;
highlight_syntethic_nodes = false;
show_options_dialog_next_time = true;
enlarge_group_name = true;
start_view_mode = gvrfm_combined_mode; // gvrfm_single_mode;
debug = true;
graph_layout = layout_digraph;
//;!
no_initial_path_info = false;
}
/**
* @brief Show the options dialog
*/
void show_dialog()
{
// TODO: askusingform
}
/**
* @brief Load options from the current database
*/
void load_options()
{
//TODO: load from netnode
}
/**
* @brief Save options to the current database
*/
void save_options()
{
// TODO: save to netnode
}
};
//--------------------------------------------------------------------------
/**
* @brief GSGraph actions. It allows parents to get notified on GSGV actions
*/
class gsgv_actions_t
{
public:
/**
* @brief The GSGV is closing
*/
virtual void notify_close() = 0;
/**
* @brief The GSGV content is refreshing
*/
virtual void notify_refresh(bool hard_refresh = false) = 0;
/**
* @brief Find nodes similar to the highlighted ones
*/
virtual pnodegroup_list_t find_similar(intvec_t &sel_nodes) = 0;
};
//--------------------------------------------------------------------------
/**
* @brief Graph data/context
*/
class gsgraphview_t
{
public:
/**
* @brief Currently selected node
*/
int cur_node;
/**
* @brief Node to focus on after a group/split
*/
int focus_node;
/**
* @brief Pointer to the graph viewer
*/
graph_viewer_t *gv;
/**
* @brief Pointer to the form hosting the graph viewer
*/
TForm *form;
/**
* @brief Pointer to the associated group manager class
*/
groupman_t *gm;
/**
* @brief GraphSlick options
*/
gsoptions_t *options;
private:
struct menucbctx_t
{
gsgraphview_t *gsgv;
qstring name;
};
typedef std::map<int, menucbctx_t> idmenucbtx_t;
static idmenucbtx_t menu_ids;
gnodemap_t node_map;
ng2nid_t ng2id;
qflow_chart_t *func_fc;
gvrefresh_modes_e refresh_mode, cur_view_mode;
gsgv_actions_t *actions;
/**
* @brief Menu item IDs
*/
int idm_single_view_mode, idm_combined_view_mode;
int idm_clear_sel, idm_clear_highlight, idm_select_all;
int idm_merge_highlight_with_selection;
int idm_jump_next_selected_node, idm_jump_next_highlighted_node;
int idm_set_sel_mode;
int idm_edit_sg_desc;
int idm_change_graph_layout;
int idm_remove_nodes_from_group;
int idm_promote_node_groups;
int idm_reset_groupping;
int idm_test;
int idm_highlight_similar, idm_find_highlight;
int idm_combine_ngs;
int idm_show_options;
bool in_sel_mode;
ncolormap_t highlighted_nodes;
ncolormap_t selected_nodes;
ncolormap_t::iterator it_selected_node, it_highlighted_node;
/**
* @brief Static menu item dispatcher
*/
static bool idaapi s_menu_item_callback(void *ud)
{
int id = (int)ud;
idmenucbtx_t::iterator it = menu_ids.find(id);
if (it == menu_ids.end())
return false;
it->second.gsgv->on_menu(id);
return true;
}
/**
* @brief Static graph callback
*/
static int idaapi _gr_callback(
void *ud,
int code, va_list va)
{
return ((gsgraphview_t *)ud)->gr_callback(code, va);
}
/**
* @brief Displays an error when a node is not found!
*/
void msg_err_node_not_found(int nid=0)
{
if (options->debug)
msg(STR_GS_MSG "Error, node(%d) not found!\n", nid);
}
void msg_unk_mode()
{
if (options->debug)
msg(STR_GS_MSG "Unknown mode\n");
}
/**
* @brief Menu items handler
*/
void on_menu(int menu_id)
{
//
// Clear selection
//
if (menu_id == idm_clear_sel)
{
clear_selection(options->manual_refresh_mode);
}
//
// Clear highlighted nodes
//
else if (menu_id == idm_clear_highlight)
{
clear_highlighting(options->manual_refresh_mode);
}
//
// Select all nodes
//
else if (menu_id == idm_select_all)
{
select_all_nodes();
}
//
// Selection mode change
//
else if (menu_id == idm_set_sel_mode)
{
// Toggle selection mode
set_sel_mode(!in_sel_mode);
}
//
// Switch to single view mode
//
else if (menu_id == idm_single_view_mode)
{
redo_layout(gvrfm_single_mode);
}
//
// Switch to combined view mode
//
else if (menu_id == idm_combined_view_mode)
{
redo_layout(gvrfm_combined_mode);
}
//
// Show the options dialog
//
else if (menu_id == idm_show_options)
{
options->show_dialog();
}
//
// Highlight similar node group
//
else if (menu_id == idm_highlight_similar)
{
highlight_similar_selection(options->manual_refresh_mode);
}
//
// Find and highlight supergroup
//
else if (menu_id == idm_find_highlight)
{
find_and_highlight_nodes(options->manual_refresh_mode);
}
//
// Change the current graph layout
//
else if (menu_id == idm_change_graph_layout)
{
int code = askbuttons_c(
"Circle", // YES
"Tree", // NO
"Digraph", // CANCEL
ASKBTN_YES,
"Please select layout type");
switch (code)
{
case ASKBTN_YES:
options->graph_layout = layout_circle;
break;
case ASKBTN_NO:
options->graph_layout = layout_tree;
break;
case ASKBTN_CANCEL:
options->graph_layout = layout_digraph;
break;
}
redo_layout(cur_view_mode);
}
//
// Edit supergroup description
//
else if (menu_id == idm_edit_sg_desc)
{
// Check the view mode and selection
if ( cur_view_mode != gvrfm_combined_mode
|| cur_node == -1)
{
msg(STR_GS_MSG "Incorrect view mode or no nodes are selected\n");
return;
}
psupergroup_t sg = get_sg_from_ngid(cur_node);
if (sg == NULL)
return;
if (edit_sg_description(sg))
{
// Notify that a refresh is taking place
actions->notify_refresh();
}
}
//
// Combine node groups
//
else if (menu_id == idm_combine_ngs)
{
if (selected_nodes.size() <= 1)
{
msg(STR_GS_MSG "Not enough selected nodes\n");
return;
}
combine_node_groups();
}
//
// Jump to next selected node
//
else if (menu_id == idm_jump_next_selected_node)
{
jump_to_next_node(it_selected_node, selected_nodes);
}
//
// Jump to next highlighted node
//
else if (menu_id == idm_jump_next_highlighted_node)
{
jump_to_next_node(it_highlighted_node, highlighted_nodes);
}
//
// Remove nodes from a node group into their own individual node groups
//
else if (menu_id == idm_remove_nodes_from_group)
{
move_nodes_to_own_ng();
}
//
// Merge highlight with selection
//
else if (menu_id == idm_merge_highlight_with_selection)
{
merge_highlight_with_selection();
}
//
// Merge highlight with selection
//
else if (menu_id == idm_promote_node_groups)
{
promote_node_groups_to_sgs();
}
//
// Reset groupping
//
else if (menu_id == idm_reset_groupping)
{
gm->reset_groupping();
// Refresh the chooser
actions->notify_refresh(true);
// Re-layout
redo_current_layout();
}
//
// Test: interactive groupping
//
else if (menu_id == idm_test)
{
selected_nodes.clear();
int sel[] = {1,3,4};
intvec_t sel_nodes;
for (int i=0;i<qnumber(sel);i++)
{
int nid = sel[i];
selected_nodes[nid] = NODE_SEL_COLOR;
sel_nodes.push_back(nid);
}
pnodegroup_list_t ngl = actions->find_similar(sel_nodes);
DECL_CG;
highlight_nodes(ngl, cg, options->manual_refresh_mode);
ngl->free_nodegroup(false);
delete ngl;
}
}
#ifdef MY_DEBUG
/**
* @brief
* @param
* @return
*/
void _DUMP_NG(const char *str, pnodegroup_t ng)
{
for (nodegroup_t::iterator it=ng->begin();
it != ng->end();
++it)
{
pnodedef_t nd = *it;
msg("%s: p=%p id=%d s=%a e=%a\n", str, nd, nd->nid, nd->start, nd->end);
}
}
#endif
/**
* @brief Graph callback
*/
int idaapi gr_callback(
int code,
va_list va)
{
int result = 0;
switch (code)
{
//
// graph is being clicked
//
case grcode_clicked:
{
va_arg(va, graph_viewer_t *);
selection_item_t *item1 = va_arg(va, selection_item_t *);
va_arg(va, graph_item_t *);
if (in_sel_mode && item1 != NULL && item1->is_node)
{
toggle_select_node(
item1->node,
options->manual_refresh_mode);
}
// don't ignore the click
result = 0;
break;
}
//
// a new graph node became the current node
//
case grcode_changed_current:
{
va_arg(va, graph_viewer_t *);
// Remember the current node
cur_node = va_argi(va, int);
break;
}
//
// A group is being created
//
case grcode_creating_group:
{
//mutable_graph_t *mg = va_arg(va, mutable_graph_t *);
//intset_t *nodes = va_arg(va, intset_t *);
//msg("grcode_creating_group(%p):", mg);
//for (intset_t::iterator it=nodes->begin();
// it != nodes->end();
// ++it)
//{
// msg("%d,", *it);
//}
//msg("\n");
//result = 1;
// out: 0-ok, 1-forbid group creation
break;
}
//
// A group is being deleted
//
case grcode_deleting_group:
{
// in: mutable_graph_t *g
// int old_group
// out: 0-ok, 1-forbid group deletion
break;
}
//
// New graph has been set
//
case grcode_changed_graph:
{
// in: mutable_graph_t *g
mutable_graph_t *mg = va_arg(va, mutable_graph_t *);
// out: must return 0
//msg("grcode_changed_graph: %p\n", mg);
break;
}
//
// Redraw the graph
//
case grcode_user_refresh:
{
mutable_graph_t *mg = va_arg(va, mutable_graph_t *);
if (node_map.empty() || refresh_mode != gvrfm_soft)
{
// Clear previous graph node data
mg->clear();
reset_states();
mg->current_layout = options->graph_layout;
mg->circle_center = point_t(200, 200);
mg->circle_radius = 100;
// Remember the current graph mode
// NOTE: we remember the state only if not 'soft'.
// Otherwise it will screw up all the logic that rely on its value
cur_view_mode = refresh_mode;
// Switch to the desired mode
if (refresh_mode == gvrfm_single_mode)
switch_to_single_view_mode(mg);
else if (refresh_mode == gvrfm_combined_mode)
switch_to_combined_view_mode(mg);
else
msg_unk_mode();
}
mg->redo_layout();
result = 1;
break;
}
//
// Retrieve text and background color for the user-defined graph node
//
case grcode_user_text:
{
va_arg(va, mutable_graph_t *);
int node = va_arg(va, int);
const char **text = va_arg(va, const char **);
bgcolor_t *bgcolor = va_arg(va, bgcolor_t *);
// Retrieve the node text
gnode_t *gnode = get_node(node);
if (gnode == NULL)
{
result = 0;
break;
}
*text = gnode->text.c_str();
// Caller requested a bgcolor?
if (bgcolor != NULL) do
{
// Selection has priority over highlight
ncolormap_t::iterator psel = selected_nodes.find(node);
if (psel == selected_nodes.end())
{
psel = highlighted_nodes.find(node);
if (psel == highlighted_nodes.end())
break;
}
// Pass the color
*bgcolor = psel->second;
} while (false);
result = 1;
break;
}
//
// retrieve hint for the user-defined graph
//
case grcode_user_hint:
{
va_arg(va, mutable_graph_t *);
int mousenode = va_arg(va, int);
va_arg(va, int); // mouseedge_src
va_arg(va, int); // mouseedge_dst
char **hint = va_arg(va, char **);
// Get node data, aim for 'hint' field then 'text'
gnode_t *node_data;
if ( mousenode != -1
&& (node_data = get_node(mousenode)) != NULL )
{
qstring *s = &node_data->hint;
if (s->empty())
s = &node_data->text;
// 'hint' must be allocated by qalloc() or qstrdup()
*hint = qstrdup(s->c_str());
// out: 0-use default hint, 1-use proposed hint
result = 1;
}
break;
}
//
// The graph is being destroyed
//
case grcode_destroyed:
{
gv = NULL;
form = NULL;
actions->notify_close();
delete this;
break;
}
}
return result;
}
/**
* @brief Resets state variables upon view mode change
*/
void reset_states()
{
// Clear node information
node_map.clear();
ng2id.clear();
// Clear highlight / selected
highlighted_nodes.clear();
selected_nodes.clear();
// Clear highlight / selection iterators
it_selected_node = selected_nodes.end();
it_highlighted_node = highlighted_nodes.end();
// No node is selected
cur_node = -1;
}
/**
* @brief Set the selection mode
*/
void set_sel_mode(bool sel_mode)
{
// Previous mode was set?
if (idm_set_sel_mode != -1)
{
// Delete previous menu item
del_menu(idm_set_sel_mode);
}
const char *labels[] =
{
"Start selection mode",
"End selection mode"
};
const char *label = labels[sel_mode ? 1 : 0];
idm_set_sel_mode = add_menu(label, "S");
in_sel_mode = sel_mode;
msg(STR_GS_MSG "Trigger again to '%s'\n", label);
}
public:
/**
* @brief Return graph view node id from an actual node id
*/
int get_gvnid_from_nid(int nid)
{
if (cur_view_mode == gvrfm_single_mode)
return nid;
nodeloc_t *loc = gm->find_nodeid_loc(nid);
// Get the other selected NG
if (loc == NULL)
return -1;
else
return ng2id.get_ng_id(loc->ng);
}
/**
* @brief Return supergroup for which a nodegroup_id belongs to
*/
psupergroup_t get_sg_from_ngid(int ngid)
{
// The current node is a node group id
// Convert ngid to a node id
pnodegroup_t ng = get_ng_from_ngid(cur_node);
if (ng == NULL)
return NULL;
else
return get_sg_from_ng(ng);
}
/**
* @brief Convert a node group id to a nodegroup instance
*/
pnodegroup_t get_ng_from_ngid(int ngid)
{
//TODO: PERFORMANCE: make a lookup table if needed
for (ng2nid_t::iterator it=ng2id.begin();
it != ng2id.end();
++it)
{
if (it->second == ngid)
return it->first;
}
return NULL;
}
/**
* @brief Return node data
*/
inline gnode_t *get_node(int nid)
{
return node_map.get(nid);
}
/**
* @brief Return a node id corresponding to the given node group.
* The current view mode is respected
*/
inline int get_ngid_from_ng(pnodegroup_t ng)
{
if (ng != NULL)
{
if (cur_view_mode == gvrfm_combined_mode)
{
// Get the nodegroup id from the map
return ng2id.get_ng_id(ng);
}
else if (cur_view_mode == gvrfm_single_mode)
{
// Just get the node id of the first node definition in the node group
pnodedef_t nd = ng->get_first_node();
return nd == NULL ? -1 : nd->nid;
}
}
if (options->debug)
msg(STR_GS_MSG "Could not find gr_nid for %p\n", ng);
return -1;
}
/**
* @brief Return the super group that hosts the NG
*/
psupergroup_t get_sg_from_ng(pnodegroup_t ng)
{
pnodedef_t nd = ng->get_first_node();
if (nd == NULL)
return NULL;
nodeloc_t *loc = gm->find_nodeid_loc(nd->nid);
if (loc == NULL)
return NULL;
else
return loc->sg;
}
/**
* @brief Clear the selection
*/
void clear_selection(bool delay_refresh)
{
selected_nodes.clear();
it_selected_node = selected_nodes.end();
if (!delay_refresh)
refresh_view();
}
/**
* @brief Clear the highlighted nodes
*/
void clear_highlighting(bool delay_refresh)
{
highlighted_nodes.clear();
it_highlighted_node = highlighted_nodes.end();
if (!delay_refresh)
refresh_view();
}
/**
* @brief Highlights a group
*/
bool highlight_nodes(
pnodegroup_t ng,
bgcolor_t clr,
bool delay_refresh)
{
intset_t newly_colored;
// Combined mode?
if (cur_view_mode == gvrfm_combined_mode)
{
int gr_nid = get_ngid_from_ng(ng);
if (gr_nid == -1)
return false;