First of all: revision 1981 is mine, what is important me because I was born in 1981. But what is new in my revision? If you drag nodes with left button, edge-breakpoints do not change location. If you drag nodes by right button, they do, they take up their base situation at the halfpoint of the edge.
1 #include <graph_displayer_canvas.h>
2 #include <broken_edge.h>
5 GraphDisplayerCanvas::GraphDisplayerCanvas(Graph & gr, CoordinatesMap & cm, MapStorage & ms):g(gr),nodesmap(g),edgesmap(g),edgetextmap(g),displayed_graph(*(root()), 0, 0),mapstorage(ms),isbutton(0),active_item(NULL),target_item(NULL)
8 actual_handler=displayed_graph.signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::create_edge_event_handler), false);
13 //set_center_scroll_region(true);
15 //first edges are drawn, to hide joining with nodes later
17 for (EdgeIt i(g); i!=INVALID; ++i)
20 //drawing green lines, coordinates are from cm
22 Gnome::Canvas::Points coos;
23 coos.push_back(Gnome::Art::Point(cm[g.source(i)].x,cm[g.source(i)].y));
24 coos.push_back(Gnome::Art::Point(cm[g.target(i)].x,cm[g.target(i)].y));
26 edgesmap[i]=new BrokenEdge(displayed_graph, coos);
27 *(edgesmap[i]) << Gnome::Canvas::Properties::fill_color("green");
28 edgesmap[i]->property_width_pixels().set_value(10);
30 //initializing edge-text as well, to empty string
32 double x1, x2, y1, y2;
33 edgesmap[i]->get_bounds(x1, y1, x2, y2);
35 edgetextmap[i]=new Gnome::Canvas::Text(displayed_graph,(x1+x2)/2, (y1+y2)/2, "");
36 edgetextmap[i]->property_fill_color().set_value("black");
39 //afterwards nodes come to be drawn
42 int maxx=0, maxy=0, minx=(int)cm[i].x, miny=(int)cm[i].y;
44 for (; i!=INVALID; ++i)
46 //minimum and maximum is gathered to be able to zoom to the graph correctly (whole figure should be seen)
48 if(cm[i].x>maxx)maxx=(int)cm[i].x;
49 if(cm[i].y>maxy)maxy=(int)cm[i].y;
50 if(cm[i].x<minx)minx=(int)cm[i].x;
51 if(cm[i].y<miny)miny=(int)cm[i].y;
53 //drawing bule nodes, with black line around them
55 nodesmap[i]=new Gnome::Canvas::Ellipse(displayed_graph, cm[i].x-20, cm[i].y-20, cm[i].x+20, cm[i].y+20);
56 *(nodesmap[i]) << Gnome::Canvas::Properties::fill_color("blue");
57 *(nodesmap[i]) << Gnome::Canvas::Properties::outline_color("black");
58 //!!!!!!! (nodesmap[i])->signal_event().connect(sigc::bind(sigc::mem_fun(*this, &GraphDisplayerCanvas::event_handler),i));
64 GraphDisplayerCanvas::~GraphDisplayerCanvas()
67 //writing out the end state of the graph
68 //\todo all the maps has to be write out!
70 Graph::NodeMap <int> id(g);
71 Graph::NodeMap <double> xc(g);
72 Graph::NodeMap <double> yc(g);
76 for (NodeIt i(g); i!=INVALID; ++i)
79 nodesmap[i]->get_bounds(x1, y1, x2, y2);
86 GraphWriter<Graph> writer(std::cout,g);
88 writer.writeNodeMap("id", id);
89 writer.writeNodeMap("coordinates_x", xc);
90 writer.writeNodeMap("coordinates_y", yc);
94 int GraphDisplayerCanvas::changeLineWidth (std::string mapname)
96 for (EdgeIt i(g); i!=INVALID; ++i)
98 int w=(int)(*(mapstorage.edgemap_storage)[mapname])[i];
99 edgesmap[i]->property_width_pixels().set_value(w);
104 int GraphDisplayerCanvas::changeColor (std::string mapname)
107 //function maps the range of the maximum and
108 //the minimum of the nodemap to the range of
111 for (EdgeIt i(g); i!=INVALID; ++i)
113 double w=(*(mapstorage.edgemap_storage)[mapname])[i];
114 double max=mapstorage.maxOfEdgeMap(mapname);
115 double min=mapstorage.minOfEdgeMap(mapname);
117 //std::cout<<w<<" "<<max<<" "<<min<<" "<<100*(w-min)/(max-min)<<std::endl;
121 color.set_rgb_p (0, 100*(w-min)/(max-min), 0);
125 color.set_rgb_p (0, 100, 0);
128 edgesmap[i]->property_fill_color_gdk().set_value(color);
133 int GraphDisplayerCanvas::changeText (std::string mapname)
136 //the number in the map will be written on the edge
137 //EXCEPT when the name of the map is Text, because
138 //in that case empty string will be written, because
139 //that is the deleter map
140 //\todo isn't it a bit woodcutter?
142 for (EdgeIt i(g); i!=INVALID; ++i)
146 double number=(*(mapstorage.edgemap_storage)[mapname])[i];
147 int length=(int)(floor(log(number)/log(10)))+1;
148 int maxpos=(int)(pow(10,length-1));
149 int strl=length+1+RANGE;
150 char * str=new char[strl];
154 for(int j=0;j<strl;j++)
158 int digit=(int)(number/maxpos);
160 number-=digit*maxpos;
165 edgetextmap[i]->property_text().set_value(str);
169 edgetextmap[i]->property_text().set_value("");
175 bool GraphDisplayerCanvas::event_handler(GdkEvent* e, Node n)
179 case GDK_BUTTON_PRESS:
180 //we mark the location of the event to be able to calculate parameters of dragging
181 clicked_x=e->button.x;
182 clicked_y=e->button.y;
183 active_item=(get_item_at(e->button.x, e->button.y));
186 case GDK_BUTTON_RELEASE:
189 updateScrollRegion();
191 case GDK_MOTION_NOTIFY:
192 //we only have to do sg. if the mouse button is pressed
195 //new coordinates will be the old values,
196 //because the item will be moved to the
197 //new coordinate therefore the new movement
198 //has to be calculated from here
200 double dx=e->motion.x-clicked_x;
201 double dy=e->motion.y-clicked_y;
202 active_item->move(dx, dy);
203 clicked_x=e->motion.x;
204 clicked_y=e->motion.y;
206 //all the edges connected to the moved point has to be redrawn
210 for(;e!=INVALID;g.nextOut(e))
212 Gnome::Canvas::Points coos;
213 double x1, x2, y1, y2;
215 nodesmap[g.source(e)]->get_bounds(x1, y1, x2, y2);
216 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
218 nodesmap[g.target(e)]->get_bounds(x1, y1, x2, y2);
219 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
221 edgesmap[e]->property_points().set_value(coos);
223 edgesmap[e]->get_bounds(x1, y1, x2, y2);
225 edgetextmap[e]->property_x().set_value((x1+x2)/2);
226 edgetextmap[e]->property_y().set_value((y1+y2)/2);
230 for(;e!=INVALID;g.nextIn(e))
232 Gnome::Canvas::Points coos;
233 double x1, x2, y1, y2;
235 nodesmap[g.source(e)]->get_bounds(x1, y1, x2, y2);
236 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
238 nodesmap[g.target(e)]->get_bounds(x1, y1, x2, y2);
239 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
241 edgesmap[e]->property_points().set_value(coos);
243 edgesmap[e]->get_bounds(x1, y1, x2, y2);
245 edgetextmap[e]->property_x().set_value((x1+x2)/2);
246 edgetextmap[e]->property_y().set_value((y1+y2)/2);
254 bool GraphDisplayerCanvas::on_expose_event(GdkEventExpose *event)
256 Gnome::Canvas::CanvasAA::on_expose_event(event);
262 void GraphDisplayerCanvas::zoomIn()
265 (1.0 + (double) zoom_step / 100.0) * get_pixels_per_unit());
268 void GraphDisplayerCanvas::zoomOut()
271 (1.0 - (double) zoom_step / 100.0) * get_pixels_per_unit());
274 void GraphDisplayerCanvas::zoomFit()
276 // get the height and width of the canvas
277 Gtk::Allocation a = get_allocation();
278 int aw = a.get_width();
279 int ah = a.get_height();
281 aw -= 5; if (aw < 0) aw = 0;
282 ah -= 5; if (ah < 0) ah = 0;
284 // get the bounding box of the graph
285 double wx1, wy1, wx2, wy2;
286 Gnome::Canvas::Item* pCanvasItem = root();
287 pCanvasItem->get_bounds(wx1, wy1, wx2, wy2);
289 // fit the graph to the window
290 double ppu1 = (double) aw / fabs(wx2 - wx1);
291 double ppu2 = (double) ah / fabs(wy2 - wy1);
292 set_pixels_per_unit((ppu1 < ppu2) ? ppu1 : ppu2);
295 void GraphDisplayerCanvas::zoom100()
297 set_pixels_per_unit(1.0);
300 void GraphDisplayerCanvas::updateScrollRegion()
302 double wx1, wy1, wx2, wy2;
303 Gnome::Canvas::Item* pCanvasItem = root();
304 pCanvasItem->get_bounds(wx1, wy1, wx2, wy2);
305 set_scroll_region(wx1, wy1, wx2, wy2);
308 void GraphDisplayerCanvas::changeEditorialTool(int newtool)
310 actual_handler.disconnect();
315 actual_handler=displayed_graph.signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::move_event_handler), false);
318 //it has to assigned to canvas, because all the canvas has to be monitored, not only the elements of the already drawn group
320 actual_handler=signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::create_node_event_handler), false);
324 actual_handler=displayed_graph.signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::create_edge_event_handler), false);
328 actual_handler=displayed_graph.signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::eraser_event_handler), false);
336 bool GraphDisplayerCanvas::move_event_handler(GdkEvent* e)
340 case GDK_BUTTON_PRESS:
341 //we mark the location of the event to be able to calculate parameters of dragging
342 clicked_x=e->button.x;
343 clicked_y=e->button.y;
344 active_item=(get_item_at(e->button.x, e->button.y));
346 for (NodeIt i(g); i!=INVALID; ++i)
348 if(nodesmap[i]==active_item)
353 switch(e->button.button)
363 case GDK_BUTTON_RELEASE:
367 updateScrollRegion();
369 case GDK_MOTION_NOTIFY:
370 //we only have to do sg. if the mouse button is pressed AND the click was on a node that was found in the set of nodes
371 if(active_node!=INVALID)
373 //new coordinates will be the old values,
374 //because the item will be moved to the
375 //new coordinate therefore the new movement
376 //has to be calculated from here
378 double dx=e->motion.x-clicked_x;
379 double dy=e->motion.y-clicked_y;
381 active_item->move(dx, dy);
383 clicked_x=e->motion.x;
384 clicked_y=e->motion.y;
386 //all the edges connected to the moved point has to be redrawn
389 g.firstOut(ei,active_node);
391 for(;ei!=INVALID;g.nextOut(ei))
393 Gnome::Canvas::Points coos;
394 double x1, x2, y1, y2;
396 nodesmap[g.source(ei)]->get_bounds(x1, y1, x2, y2);
397 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
399 nodesmap[g.target(ei)]->get_bounds(x1, y1, x2, y2);
400 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
404 edgesmap[ei]->set_points(coos);
408 edgesmap[ei]->set_points(coos,true);
411 edgesmap[ei]->get_bounds(x1, y1, x2, y2);
413 edgetextmap[ei]->property_x().set_value((x1+x2)/2);
414 edgetextmap[ei]->property_y().set_value((y1+y2)/2);
417 g.firstIn(ei,active_node);
418 for(;ei!=INVALID;g.nextIn(ei))
420 Gnome::Canvas::Points coos;
421 double x1, x2, y1, y2;
423 nodesmap[g.source(ei)]->get_bounds(x1, y1, x2, y2);
424 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
426 nodesmap[g.target(ei)]->get_bounds(x1, y1, x2, y2);
427 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
431 edgesmap[ei]->set_points(coos);
435 edgesmap[ei]->set_points(coos,true);
438 edgesmap[ei]->get_bounds(x1, y1, x2, y2);
440 edgetextmap[ei]->property_x().set_value((x1+x2)/2);
441 edgetextmap[ei]->property_y().set_value((y1+y2)/2);
450 bool GraphDisplayerCanvas::create_node_event_handler(GdkEvent* e)
455 //draw the new node in red at the clicked place
456 case GDK_BUTTON_PRESS:
459 active_node=NodeIt(g,g.addNode());
461 window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
463 nodesmap[active_node]=new Gnome::Canvas::Ellipse(displayed_graph, clicked_x-20, clicked_y-20, clicked_x+20, clicked_y+20);
464 active_item=(Gnome::Canvas::Item *)(nodesmap[active_node]);
465 *(nodesmap[active_node]) << Gnome::Canvas::Properties::fill_color("red");
466 *(nodesmap[active_node]) << Gnome::Canvas::Properties::outline_color("black");
467 (nodesmap[active_node])->show();
471 case GDK_MOTION_NOTIFY:
473 double world_motion_x, world_motion_y;
474 GdkEvent * generated=new GdkEvent();
475 window_to_world (e->motion.x, e->motion.y, world_motion_x, world_motion_y);
476 generated->motion.x=world_motion_x;
477 generated->motion.y=world_motion_y;
478 generated->type=GDK_MOTION_NOTIFY;
479 move_event_handler(generated);
483 //finalize the new node
484 case GDK_BUTTON_RELEASE:
486 *active_item << Gnome::Canvas::Properties::fill_color("blue");
489 updateScrollRegion();
497 bool GraphDisplayerCanvas::create_edge_event_handler(GdkEvent* e)
501 case GDK_BUTTON_PRESS:
502 //in edge creatino right button has special meaning
503 if(e->button.button!=3)
505 //there is not yet selected node
506 if(active_node==INVALID)
508 //we mark the location of the event to be able to calculate parameters of dragging
509 clicked_x=e->button.x;
510 clicked_y=e->button.y;
511 active_item=(get_item_at(e->button.x, e->button.y));
513 for (NodeIt i(g); i!=INVALID; ++i)
515 if(nodesmap[i]==active_item)
520 //the clicked item is really a node
521 if(active_node!=INVALID)
523 *(nodesmap[active_node]) << Gnome::Canvas::Properties::fill_color("red");
526 //clicked item was not a node. It could be e.g. edge.
532 //we only have to do sg. if the mouse button
533 // is pressed already once AND the click was
534 // on a node that was found in the set of
535 //nodes, and now we only search for the second
539 target_item=(get_item_at(e->button.x, e->button.y));
540 Graph::NodeIt target_node=INVALID;
541 for (NodeIt i(g); i!=INVALID; ++i)
543 if(nodesmap[i]==target_item)
548 //the clicked item is a node, the edge can be drawn
549 if(target_node!=INVALID)
551 *(nodesmap[target_node]) << Gnome::Canvas::Properties::fill_color("red");
554 active_edge=EdgeIt(g,g.addEdge(active_node, target_node));
556 //calculating coordinates of new edge
557 Gnome::Canvas::Points coos;
558 double x1, x2, y1, y2;
560 active_item->get_bounds(x1, y1, x2, y2);
561 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
563 target_item->get_bounds(x1, y1, x2, y2);
564 coos.push_back(Gnome::Art::Point((x1+x2)/2,(y1+y2)/2));
567 edgesmap[active_edge]=new BrokenEdge(displayed_graph, coos);
568 *(edgesmap[active_edge]) << Gnome::Canvas::Properties::fill_color("green");
569 edgesmap[active_edge]->property_width_pixels().set_value(10);
571 //redraw nodes to blank terminations of the new edge
572 target_item->raise_to_top();
573 active_item->raise_to_top();
575 //initializing edge-text as well, to empty string
576 edgesmap[active_edge]->get_bounds(x1, y1, x2, y2);
577 edgetextmap[active_edge]=new Gnome::Canvas::Text(displayed_graph,(x1+x2)/2, (y1+y2)/2, "");
578 edgetextmap[active_edge]->property_fill_color().set_value("black");
580 //clicked item was not a node. it could be an e.g. edge. we do not deal with it furthermore.
588 case GDK_BUTTON_RELEASE:
590 //we clear settings in two cases
591 //1: the edge is ready (target_item has valid value)
592 //2: the edge creation is cancelled with right button
593 if((target_item)||(e->button.button==3))
597 *active_item << Gnome::Canvas::Properties::fill_color("blue");
602 *target_item << Gnome::Canvas::Properties::fill_color("blue");
615 bool GraphDisplayerCanvas::eraser_event_handler(GdkEvent* e)
619 case GDK_BUTTON_PRESS:
620 active_item=(get_item_at(e->button.x, e->button.y));
623 for (NodeIt i(g); i!=INVALID; ++i)
625 if(nodesmap[i]==active_item)
630 if(active_node==INVALID)
632 for (EdgeIt i(g); i!=INVALID; ++i)
634 if(edgesmap[i]==active_item)
640 *active_item << Gnome::Canvas::Properties::fill_color("red");
643 case GDK_BUTTON_RELEASE:
644 if(active_item==(get_item_at(e->button.x, e->button.y)))
646 if(active_node!=INVALID)
649 //collecting edges to delete
651 std::set<Graph::Edge> edges_to_delete;
653 g.firstOut(e,active_node);
654 for(;e!=INVALID;g.nextOut(e))
656 edges_to_delete.insert(e);
659 g.firstIn(e,active_node);
660 for(;e!=INVALID;g.nextIn(e))
662 edges_to_delete.insert(e);
665 //deleting collected edges
666 for(std::set<Graph::Edge>::iterator edge_set_it=edges_to_delete.begin();edge_set_it!=edges_to_delete.end();edge_set_it++)
668 delete_item(*edge_set_it);
670 delete_item(active_node);
672 //a simple edge was chosen
675 delete_item(active_edge);
680 //pointer was moved, deletion is cancelled
683 if(active_node!=INVALID)
685 *active_item << Gnome::Canvas::Properties::fill_color("blue");
689 *active_item << Gnome::Canvas::Properties::fill_color("green");
698 case GDK_MOTION_NOTIFY:
707 void GraphDisplayerCanvas::delete_item(NodeIt node_to_delete)
709 delete(nodesmap[node_to_delete]);
710 g.erase(node_to_delete);
713 void GraphDisplayerCanvas::delete_item(EdgeIt edge_to_delete)
715 delete(edgesmap[edge_to_delete]);
716 g.erase(edge_to_delete);
719 void GraphDisplayerCanvas::delete_item(Graph::Edge edge_to_delete)
721 delete(edgesmap[edge_to_delete]);
722 g.erase(edge_to_delete);