1 /*
2 Copyright (C) 2002 Kai Sterker <kai.sterker@gmail.com>
3 Part of the Adonthell Project <http://adonthell.nongnu.org>
4
5 Dlgedit is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 Dlgedit is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Dlgedit. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * @file gui_graph.cc
21 *
22 * @author Kai Sterker
23 * @brief View for the dialogue graph
24 */
25
26 #include "cfg_data.h"
27 #include "dlg_mover.h"
28 #include "gui_dlgedit.h"
29 #include "gui_graph_events.h"
30 #include "gui_resources.h"
31 #include "gui_circle.h"
32 #include "gui_file.h"
33
34 // Constructor
GuiGraph(GtkWidget * paned)35 GuiGraph::GuiGraph (GtkWidget *paned) : Scrollable ()
36 {
37 // initialize members to sane values
38 mover = NULL;
39 module = NULL;
40 offset = NULL;
41 surface = NULL;
42 tooltip = NULL;
43
44 // create drawing area for the graph
45 graph = gtk_drawing_area_new ();
46 gtk_widget_set_size_request (graph, 200, 450);
47 gtk_paned_add2 (GTK_PANED (paned), graph);
48 gtk_widget_show (graph);
49 gtk_widget_grab_focus (graph);
50
51 // register our event callbacks
52 g_signal_connect (G_OBJECT (graph), "expose_event", G_CALLBACK(expose_event), this);
53 g_signal_connect (G_OBJECT (graph), "configure_event", G_CALLBACK(configure_event), this);
54 g_signal_connect (G_OBJECT (graph), "button_press_event", G_CALLBACK(button_press_event), this);
55 g_signal_connect (G_OBJECT (graph), "button_release_event", G_CALLBACK(button_release_event), this);
56 g_signal_connect (G_OBJECT (graph), "motion_notify_event", G_CALLBACK(motion_notify_event), this);
57 g_signal_connect (G_OBJECT (GuiDlgedit::window->getWindow ()), "key_press_event", G_CALLBACK(key_press_notify_event), this);
58
59 gtk_widget_set_events (graph, GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK |
60 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_KEY_PRESS_MASK);
61 }
62
63 // dtor
~GuiGraph()64 GuiGraph::~GuiGraph()
65 {
66 cairo_surface_destroy(surface);
67 }
68
69 // attach a module
attachModule(DlgModule * m,bool cntr)70 void GuiGraph::attachModule (DlgModule *m, bool cntr)
71 {
72 module = m;
73
74 // get reference of the module's offset
75 offset = &m->offset ();
76
77 // center the module in view
78 if (cntr) center ();
79
80 // if a node is selected, update the instant preview
81 GuiDlgedit::window->list ()->display (module->selected ());
82
83 // update the module structure
84 GuiDlgedit::window->tree ()->select (module);
85
86 // update the program state
87 GuiDlgedit::window->setMode (module->state ());
88
89 GtkAllocation allocation;
90 gtk_widget_get_allocation (graph, &allocation);
91
92 // set the size of the dialogue
93 drawing_area.resize (allocation.width, allocation.height);
94
95 // tell the module that it is in view
96 module->setDisplayed (true);
97
98 // display the module
99 draw ();
100 }
101
102 // detach a module
detachModule()103 void GuiGraph::detachModule ()
104 {
105 module = NULL;
106
107 // clear the instant preview
108 GuiDlgedit::window->list ()->clear ();
109
110 // update the program state
111 GuiDlgedit::window->setMode (IDLE);
112
113 // remove the tooltip if it is open
114 if (tooltip)
115 {
116 delete tooltip;
117 tooltip = NULL;
118 }
119 }
120
121 // display a different module
switchModule(DlgModule * m)122 void GuiGraph::switchModule (DlgModule *m)
123 {
124 // we're switching between sub-dialogues
125 if (module) module->setDisplayed (false);
126
127 detachModule ();
128 attachModule (m);
129 }
130
131 // create a new circle
newCircle(DlgPoint & point,node_type type)132 bool GuiGraph::newCircle (DlgPoint &point, node_type type)
133 {
134 // if there is no module assigned to the view, there is nothing to do
135 if (module == NULL) return false;
136
137 // get the serial number to use for this node
138 int &serial = module->serial ();
139
140 // create the new node ...
141 DlgCircle *circle = new DlgCircle (point, type, serial, module->node_id ());
142
143 // ... add it to the module ...
144 module->addNode (circle);
145
146 // ... and select it for editing
147 module->selectNode (circle);
148
149 // see whether creation was cancelled
150 if (!editNode ())
151 {
152 // cleanup
153 module->deleteNode ();
154
155 return false;
156 }
157
158 // update the program state
159 GuiDlgedit::window->setMode (NODE_SELECTED);
160
161 // node created -> increase serial number for next node
162 serial++;
163
164 return true;
165 }
166
167 // create a new arrow
newArrow(DlgPoint & point)168 bool GuiGraph::newArrow (DlgPoint &point)
169 {
170 // if there is no module assigned to the view, there is nothing to do
171 if (module == NULL) return false;
172
173 // calculate absolute position of the point
174 point.move (-offset->x (), -offset->y ());
175
176 // get start and end of the circle
177 DlgNode *start = module->selected ();
178 DlgNode *end = module->getNode (point);
179
180 // sanity checks
181 if (!start || start->type () == LINK) return false;
182 if (end && end->type () == LINK) return false;
183
184 // if no end selected, create a new circle first
185 if (end == NULL)
186 {
187 // chose a sensible type for the new circle
188 node_type type = (start->type () == NPC ? PLAYER : NPC);
189
190 // there musn't be a node selected to create a new circle
191 module->deselectNode ();
192
193 // try to create a new circle
194 if (newCircle (point, type))
195 {
196 // chose the newly created circle as end of the arrow
197 end = module->getNode (point);
198
199 // restore selection
200 deselectNode ();
201 selectNode (start);
202 }
203
204 // do we have a valid end now?
205 if (end == NULL) return false;
206 }
207
208 // no loops and no duplicate connections
209 if (start == end || ((DlgCircle *) start)->hasChild (end)) return false;
210
211 // no connection between start and end if both are PLAYER nodes
212 if (start->type () == PLAYER && end->type () == PLAYER) return false;
213
214 // now create the arrow between start and end, ...
215 DlgArrow *arrow = new DlgArrow (start, end);
216
217 // ... add it to the module ...
218 module->addNode (arrow);
219
220 // ... and update everything
221 GuiDlgedit::window->list ()->display (start);
222 arrow->draw (surface, *offset, graph);
223 module->setChanged ();
224
225 return true;
226 }
227
228 // add a new subdialogue
newModule(DlgPoint & point)229 bool GuiGraph::newModule (DlgPoint &point)
230 {
231 // if there is no module assigned to the view, there is nothing to do
232 if (module == NULL) return false;
233
234 // if a project root exists, use that in file selector
235 std::string dir = CfgData::data->getBasedir (module->entry ()->project ());
236
237 // otherwise revert to directory last opened
238 if (dir == "") dir = GuiDlgedit::window->directory ();
239
240 // allow the user to select a module
241 GtkWindow *parent = GTK_WINDOW (GuiDlgedit::window->getWindow());
242 GuiFile fs (parent, GTK_FILE_CHOOSER_ACTION_OPEN, "Select sub-dialogue to add", dir + "/");
243 fs.add_filter ("*" FILE_EXT, "Adonthell Dialogue Source");
244
245 // set shortcuts
246 const std::vector<std::string> & projects = CfgData::data->projectsFromDatadir ();
247 for (std::vector<std::string>::const_iterator i = projects.begin (); i != projects.end (); i++)
248 {
249 const std::string &dir = CfgData::data->getBasedir (*i);
250 fs.add_shortcut (dir);
251 }
252
253 if (fs.run ())
254 {
255 DlgModule *subdlg = GuiDlgedit::window->loadSubdialogue (fs.getSelection());
256
257 if (subdlg == NULL) return false;
258
259 // set parent of the sub-dialogue
260 subdlg->setParent (module);
261
262 // set id of the sub-dialogue
263 subdlg->setID ();
264
265 // draw the sub-dialogue
266 subdlg->initShape (point);
267 subdlg->draw (surface, *offset, graph);
268
269 // update the module
270 module->setChanged ();
271 module->addNode (subdlg);
272
273 // update the module tree
274 GuiDlgedit::window->tree ()->insert (module, subdlg);
275
276 return true;
277 }
278
279 return false;
280 }
281
282 // delete the selected node
deleteNode()283 bool GuiGraph::deleteNode ()
284 {
285 // if there is no module assigned to the view, there is nothing to select
286 if (module == NULL) return false;
287
288 // try to delete node
289 if (module->deleteNode ())
290 {
291 // update the program state
292 GuiDlgedit::window->setMode (IDLE);
293
294 // update the instant preview
295 GuiDlgedit::window->list ()->clear ();
296
297 // remove tooltip if it is open
298 if (tooltip)
299 {
300 delete tooltip;
301 tooltip = NULL;
302 }
303
304 // redraw the dialogue
305 draw ();
306
307 return true;
308 }
309
310 return false;
311 }
312
313 // select a node
selectNode(DlgNode * node)314 bool GuiGraph::selectNode (DlgNode *node)
315 {
316 // if there is no module assigned to the view, there is nothing to select
317 if (module == NULL) return false;
318
319 // a node has been selected
320 if (module->selectNode (node))
321 {
322 // update the program state
323 GuiDlgedit::window->setMode (NODE_SELECTED);
324
325 // update the instant preview
326 GuiDlgedit::window->list ()->display (node);
327
328 // redraw the node
329 node->draw (surface, *offset, graph);
330
331 return true;
332 }
333
334 return false;
335 }
336
337 // select the node at the given position
selectNode(DlgPoint & point)338 bool GuiGraph::selectNode (DlgPoint &point)
339 {
340 // if there is no module assigned to the view, there is nothing to select
341 if (module == NULL) return false;
342
343 // is point within module's boundaries?
344 if (!drawing_area.contains (point)) return false;
345
346 // calculate absolute position of the point
347 point.move (-offset->x (), -offset->y ());
348
349 // see if we're over a node
350 DlgNode *node = module->getNode (point);
351
352 // no node at that position
353 if (node == NULL) return false;
354
355 // otherwise select the node
356 if (selectNode (node))
357 {
358 module->traverse ()->select (node);
359 return true;
360 }
361
362 return false;
363 }
364
365 // select parent
selectParent()366 bool GuiGraph::selectParent ()
367 {
368 // if there is no module assigned to the view, there is nothing to select
369 if (module == NULL) return false;
370
371 // see if a node is currently selected
372 DlgNode *selected = module->selected ();
373
374 // if so ...
375 if (selected)
376 {
377 // ... try to retrieve it's parent
378 DlgNode *parent = module->traverse ()->up ();
379
380 // deselect current
381 deselectNode ();
382
383 // if we have it, then select it
384 if (parent) return selectNode (parent);
385 }
386
387 // if no node is selected, we simply select the first one
388 return selectRoot ();
389 }
390
391 // select the child of a node
selectChild()392 bool GuiGraph::selectChild ()
393 {
394 // if there is no module assigned to the view, there is nothing to select
395 if (module == NULL) return false;
396
397 // see if a node is currently selected
398 DlgNode *selected = module->selected ();
399
400 // if so ...
401 if (selected)
402 {
403 // ... try to retrieve it's child
404 DlgNode *child = module->traverse ()->down ();
405
406 // if we have it, then select it
407 if (child)
408 {
409 deselectNode ();
410 return selectNode (child);
411 }
412 }
413
414 // if no node is selected, we simply select the first one
415 else return selectRoot ();
416
417 return false;
418 }
419
420 // select a sibling of the currently selected node
selectSibling(query_type pos)421 bool GuiGraph::selectSibling (query_type pos)
422 {
423 // if there is no module assigned to the view, there is nothing to select
424 if (module == NULL) return false;
425
426 // see if a node is currently selected
427 DlgNode *selected = module->selected ();
428
429 // if so ...
430 if (selected)
431 {
432 DlgNode *sibling;
433
434 // see if selected is arrow
435 if (selected->type () == LINK)
436 selected = selected->next (FIRST);
437
438 // ... try to retrieve it's child
439 if (pos == PREV) sibling = module->traverse ()->left ();
440 else sibling = module->traverse ()->right ();
441
442 // if we have something now
443 if (sibling && sibling != selected)
444 {
445 deselectNode ();
446 return selectNode (sibling);
447 }
448 }
449
450 // if no node is selected, we simply select the first one
451 else return selectRoot ();
452
453 return false;
454 }
455
456 // select the first node in the dialogue
selectRoot()457 bool GuiGraph::selectRoot ()
458 {
459 return selectNode (module->traverse ()->selectRoot (&module->getNodes ()));
460 }
461
462 // deselect a node
deselectNode()463 void GuiGraph::deselectNode ()
464 {
465 // if there is no module assigned to the view, there is nothing to select
466 if (module == NULL) return;
467
468 DlgNode *deselected = module->deselectNode ();
469
470 if (deselected)
471 {
472 // update the program state
473 GuiDlgedit::window->setMode (IDLE);
474
475 // update the instant preview
476 GuiDlgedit::window->list ()->clear ();
477
478 // redraw the node
479 deselected->draw (surface, *offset, graph);
480 }
481
482 return;
483 }
484
485 // center the view on given node
centerNode(DlgNode * node)486 bool GuiGraph::centerNode (DlgNode *node)
487 {
488 if (module == NULL) return false;
489
490 if (node == NULL && module->selected () != NULL)
491 node = module->selected ();
492 else return false;
493
494 // calculate the correct offset for the given node
495 DlgPoint pos = node->center ().offset (*offset);
496 int x, y;
497
498 x = drawing_area.width () / 5;
499 y = drawing_area.height () / 5;
500
501 // is node outside the views inner 60% ?
502 if (!drawing_area.inflate (-x, -y).contains (pos))
503 {
504 // then move the view so it is centered on the given point
505 DlgPoint o (-(pos.x()-drawing_area.width()/2), -(pos.y()-drawing_area.height()/2));
506 offset->move (o);
507
508 draw ();
509 return true;
510 }
511
512 return false;
513 }
514
515 // center whole dialogue in view
center()516 void GuiGraph::center ()
517 {
518 if (module == NULL) return;
519
520 int min_x, max_x, y;
521
522 module->extension (min_x, max_x, y);
523
524 GtkAllocation allocation;
525 gtk_widget_get_allocation (graph, &allocation);
526
527 int x_off = (allocation.width - (max_x - min_x))/2 - min_x;
528 int y_off = -y + 20;
529
530 offset->move (x_off, y_off);
531 }
532
533 // edit selected node
editNode()534 bool GuiGraph::editNode ()
535 {
536 if (module == NULL) return false;
537
538 // see if a node is currently selected
539 DlgNode *selected = module->selected ();
540
541 // disable scrolling (just in case)
542 stopScrolling();
543
544 // if we have a sub-dialogue, descent for editing
545 if (selected->type () == MODULE)
546 {
547 switchModule ((DlgModule *) selected);
548 return true;
549 }
550
551 // if we have a circle, open edit dialog
552 if (selected && selected->type () != LINK)
553 {
554 GtkWindow *parent = GTK_WINDOW (GuiDlgedit::window->getWindow());
555 GuiCircle edit (parent, &selected->type (), ((DlgCircle *) selected)->entry (), module->entry ());
556
557 // editing aborted?
558 if (!edit.run ()) return false;
559
560 // otherwise update everything
561 GuiDlgedit::window->list ()->display (selected);
562 selected->draw (surface, *offset, graph);
563 module->setChanged ();
564
565 return true;
566 }
567
568 return false;
569 }
570
571 // set everything up for moving nodes around
prepareDragging(DlgPoint & point)572 bool GuiGraph::prepareDragging (DlgPoint &point)
573 {
574 // if there is no module assigned to the view, there is nothing to do
575 if (module == NULL) return false;
576
577 // calculate absolute position of the point
578 point.move (-offset->x (), -offset->y ());
579
580 // see if we're over a node
581 DlgNode *node = module->getNode (point);
582
583 // Not over a node
584 if (node == NULL) return false;
585
586 // if no node selected, select node for dragging
587 if (module->selected () == NULL) selectNode (node);
588
589 // else check whether dragged and selected node are the same
590 else if (node != module->selected ()) return false;
591
592 // Is dragged node circle or arrow?
593 if (node->type () != LINK)
594 {
595 // circles and modules can be dragged directly
596 mover = node;
597
598 // remove any tooltip, as it only gets in the way
599 if (tooltip)
600 {
601 delete tooltip;
602 tooltip = NULL;
603 }
604 }
605 else
606 {
607 // arrows have to be attached to a (invisible) mover
608 mover = new DlgMover (point);
609
610 // try to attach arrow to mover
611 if (!((DlgMover *) mover)->attach ((DlgArrow *) node))
612 {
613 // moving of arrow failed, so clean up
614 delete mover;
615 mover = NULL;
616 }
617 }
618
619 // if we have a mover, update program state
620 if (mover != NULL)
621 {
622 GuiDlgedit::window->setMode (NODE_DRAGGED);
623 module->setState (NODE_DRAGGED);
624
625 return true;
626 }
627
628 return false;
629 }
630
631 // drag a node around
drag(DlgPoint & point)632 void GuiGraph::drag (DlgPoint &point)
633 {
634 static int redraw = 0;
635
636 // if there is no module assigned to the view, there is nothing to do
637 if (module == NULL) return;
638
639 // calculate absolute position of the point
640 point.move (-offset->x (), -offset->y ());
641
642 // move node
643 mover->setPos (DlgPoint (point.x () - (point.x () % CIRCLE_DIAMETER),
644 point.y () - (point.y () % CIRCLE_DIAMETER)));
645
646 // update arrows
647 for (DlgNode *a = mover->prev (FIRST); a != NULL; a = mover->prev (NEXT))
648 ((DlgArrow *) a)->initShape ();
649
650 for (DlgNode *a = mover->next (FIRST); a != NULL; a = mover->next (NEXT))
651 ((DlgArrow *) a)->initShape ();
652
653 // update view
654 switch (redraw)
655 {
656 case 0:
657 {
658 draw ();
659 redraw++;
660 break;
661 }
662 case 7:
663 {
664 redraw = 0;
665 break;
666 }
667 default:
668 {
669 redraw++;
670 break;
671 }
672 }
673 }
674
675 // stop dragging node
stopDragging(DlgPoint & point)676 void GuiGraph::stopDragging (DlgPoint &point)
677 {
678 // if there is no module assigned to the view, there is nothing to do
679 if (module == NULL || mover == NULL) return;
680
681 // calculate absolute position of the point
682 point.move (-offset->x (), -offset->y ());
683
684 // see whether arrow was dragged
685 if (mover->type () == MOVER)
686 {
687 // see whether we are over a node
688 DlgNode *node = module->getNode (point);
689
690 // drop the mover onto the node
691 ((DlgMover *) mover)->drop (node);
692
693 // cleanup
694 delete mover;
695 }
696
697 // if circle moved, realign it to the grid
698 else
699 {
700 // make sure we drop on an empty location
701 DlgNode *node = module->getNode (point);
702 while (node != NULL && node != mover && node->type() != LINK)
703 {
704 point.move (0, 2*CIRCLE_DIAMETER);
705 node = module->getNode (point);
706 }
707
708 mover->setPos (DlgPoint (point.x () - (point.x () % CIRCLE_DIAMETER),
709 point.y () - (point.y () % CIRCLE_DIAMETER)));
710
711 // also need to update arrows and reorder children and parents
712 for (DlgNode *a = mover->prev (FIRST); a != NULL; a = mover->prev (NEXT))
713 {
714 a->prev (FIRST)->removeNext (a);
715 a->prev (FIRST)->addNext (a);
716 ((DlgArrow *) a)->initShape ();
717 }
718
719 for (DlgNode *a = mover->next (FIRST); a != NULL; a = mover->next (NEXT))
720 {
721 a->next (FIRST)->removePrev (a);
722 a->next (FIRST)->addPrev (a);
723 ((DlgArrow *) a)->initShape ();
724 }
725 }
726 // update everything
727 if (mover->type() == MODULE)
728 deselectNode ();
729 else
730 {
731 GuiDlgedit::window->list ()->display (module->selected ());
732 GuiDlgedit::window->setMode (NODE_SELECTED);
733 module->setState (NODE_SELECTED);
734 module->setChanged ();
735 }
736
737 // clear mover
738 mover = NULL;
739
740 draw ();
741 }
742
743 // resize the drawing area
resizeSurface(GtkWidget * widget)744 void GuiGraph::resizeSurface (GtkWidget *widget)
745 {
746 GtkAllocation allocation;
747 gtk_widget_get_allocation (widget, &allocation);
748
749 // delete the old surface
750 if (surface) cairo_surface_destroy (surface);
751
752 // create a new one with the proper size
753 surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget), CAIRO_CONTENT_COLOR, allocation.width, allocation.height);
754
755 // init the surface
756 if (GuiResources::getColor (GC_GREY)) clear ();
757
758 // set the size of the attached dialogues
759 drawing_area.resize (allocation.width, allocation.height);
760 }
761
762 // empty the drawing area
clear()763 void GuiGraph::clear ()
764 {
765 GdkRectangle t;
766
767 GtkAllocation allocation;
768 gtk_widget_get_allocation (graph, &allocation);
769
770 cairo_t *cr = cairo_create (surface);
771 gdk_cairo_set_source_color(cr, GuiResources::getColor (GC_GREY));
772 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
773 cairo_fill(cr);
774 cairo_destroy(cr);
775
776 t.x = 0;
777 t.y = 0;
778 t.width = allocation.width;
779 t.height = allocation.height;
780
781 gdk_window_invalidate_rect (gtk_widget_get_window (graph), &t, FALSE);
782 }
783
784 // draw the graph to the surface
draw()785 void GuiGraph::draw ()
786 {
787 // nothing to draw
788 if (module == NULL) return;
789
790 GdkRectangle t;
791 std::vector<DlgNode*>::reverse_iterator i;
792 std::vector<DlgNode*> nodes = module->getNodes ();
793
794 // get visible part of graph
795 t.x = -offset->x ();
796 t.y = -offset->y ();
797 t.width = drawing_area.width ();
798 t.height = drawing_area.height ();
799
800 DlgRect rect (t);
801
802 // Clear graph
803 cairo_t *cr = cairo_create (surface);
804 gdk_cairo_set_source_color(cr, GuiResources::getColor (GC_WHITE));
805 cairo_rectangle(cr, 0, 0, t.width, t.height);
806 cairo_fill(cr);
807 cairo_destroy(cr);
808
809 // normalize rect
810 t.x = 0;
811 t.y = 0;
812
813 // check for each node, wether it is visible
814 for (i = nodes.rbegin (); i != nodes.rend (); i++)
815 // draw nodes and arrows
816 if ((*i)->contains (rect))
817 (*i)->draw (surface, *offset, NULL);
818
819 // draw backing image to screen
820 gdk_window_invalidate_rect (gtk_widget_get_window (graph), &t, FALSE);
821 }
822
823 // the mouse has been moved
mouseMoved(DlgPoint & point)824 void GuiGraph::mouseMoved (DlgPoint &point)
825 {
826 // if there is no module assigned to the view, there is nothing to do
827 if (module == NULL) return;
828
829 // calculate absolute position of the point
830 point.move (-offset->x (), -offset->y ());
831
832 // see if we're over a node
833 DlgNode *node = module->getNode (point);
834
835 // get the node that was highlighted before (if any)
836 DlgNode *prev = module->highlightNode (node);
837
838 // there's no need for action unless old and new highlight differs
839 if (prev != node)
840 {
841 // clear old if necessary
842 if (prev != NULL)
843 {
844 prev->draw (surface, *offset, graph);
845 if (tooltip)
846 {
847 delete tooltip;
848 tooltip = NULL;
849 }
850 }
851
852 // then highlight the new one
853 if (node != NULL && gtk_window_is_active(GTK_WINDOW(gtk_widget_get_toplevel(graph))))
854 {
855 node->draw (surface, *offset, graph, NODE_HILIGHTED);
856 tooltip = new GuiTooltip (node);
857 tooltip->draw (graph, *offset);
858 }
859 }
860
861 return;
862 }
863
864 // is scrolling allowed?
scrollingAllowed() const865 bool GuiGraph::scrollingAllowed () const
866 {
867 // if there is no module assigned to the view or no nodes
868 // in the module yet, there is nothing to do
869 if (module == NULL || module->getNodes ().empty ()) return false;
870 return true;
871 }
872
873 // the actual scrolling
scroll()874 void GuiGraph::scroll ()
875 {
876 offset->move (scroll_offset.x, scroll_offset.y);
877 draw ();
878 }
879
880 // get the toplevel dialogue module
dialogue()881 DlgModule *GuiGraph::dialogue ()
882 {
883 // if there is no module assigned to the view, there is nothing to do
884 if (module == NULL) return NULL;
885
886 return module->toplevel ();
887 }
888