Merge branches/akos to trunk.
3 * This file is a part of LEMON, a generic C++ optimization library
5 * Copyright (C) 2003-2006
6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7 * (Egervary Research Group on Combinatorial Optimization, EGRES).
9 * Permission to use, modify and distribute this software is granted
10 * provided that this copyright notice appears in all copies. For
11 * precise terms see the accompanying LICENSE file.
13 * This software is provided "AS IS" with no warranty of any kind,
14 * express or implied, and with no claim as to its suitability for any
19 #include <graph_displayer_canvas.h>
20 #include <mapstorage.h>
25 bool GraphDisplayerCanvas::on_expose_event(GdkEventExpose *event)
27 Gnome::Canvas::CanvasAA::on_expose_event(event);
33 void GraphDisplayerCanvas::changeEditorialTool(int newtool)
35 if(actual_tool!=newtool)
38 actual_handler.disconnect();
44 GdkEvent * generated=new GdkEvent();
45 generated->type=GDK_BUTTON_RELEASE;
46 generated->button.button=3;
47 createEdgeEventHandler(generated);
69 actual_handler=signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::moveEventHandler), false);
73 actual_handler=signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::createNodeEventHandler), false);
77 actual_handler=signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::createEdgeEventHandler), false);
81 actual_handler=signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::eraserEventHandler), false);
86 actual_handler=signal_event().connect(sigc::mem_fun(*this, &GraphDisplayerCanvas::mapEditEventHandler), false);
95 int GraphDisplayerCanvas::getActualTool()
100 bool GraphDisplayerCanvas::scrollEventHandler(GdkEvent* e)
103 if(e->type==GDK_SCROLL)
106 //pointer shows this win point before zoom
107 XY win_coord(((GdkEventScroll*)e)->x, ((GdkEventScroll*)e)->y);
109 //the original scroll settings
110 int scroll_offset_x, scroll_offset_y;
111 get_scroll_offsets(scroll_offset_x, scroll_offset_y);
113 //pointer shows this canvas point before zoom
115 window_to_world(win_coord.x, win_coord.y, canvas_coord.x, canvas_coord.y);
117 if(((GdkEventScroll*)e)->direction) //IN
126 //pointer shows this window point after zoom
128 world_to_window(canvas_coord.x, canvas_coord.y, post_win_coord.x, post_win_coord.y);
130 //we have to add the difference between new and old window point to original scroll offset
131 scroll_to(scroll_offset_x+(int)(post_win_coord.x-win_coord.x),scroll_offset_y+(int)(post_win_coord.y-win_coord.y));
133 //no other eventhandler is needed
139 bool GraphDisplayerCanvas::moveEventHandler(GdkEvent* e)
141 MapStorage& ms = *mytab.mapstorage;
143 static Gnome::Canvas::Text *coord_text = 0;
146 case GDK_BUTTON_PRESS:
147 //we mark the location of the event to be able to calculate parameters of dragging
148 window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
150 active_item=(get_item_at(clicked_x, clicked_y));
152 for (NodeIt i(ms.graph); i!=INVALID; ++i)
154 if(nodesmap[i]==active_item)
159 isbutton=e->button.button;
161 case GDK_BUTTON_RELEASE:
171 case GDK_MOTION_NOTIFY:
172 //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
173 if(active_node!=INVALID)
177 //new coordinates will be the old values,
178 //because the item will be moved to the
179 //new coordinate therefore the new movement
180 //has to be calculated from here
184 window_to_world (e->motion.x, e->motion.y, new_x, new_y);
186 double dx=new_x-clicked_x;
187 double dy=new_y-clicked_y;
194 // reposition the coordinates text
195 std::ostringstream ostr;
197 ms.getNodeCoords(active_node).x << ", " <<
198 ms.getNodeCoords(active_node).y << ")";
200 (nodesmap[active_node]->property_x2().get_value() -
201 nodesmap[active_node]->property_x1().get_value()) / 2.0;
204 coord_text->property_text().set_value(ostr.str());
205 coord_text->property_x().set_value(
206 ms.getNodeCoords(active_node).x + radius);
207 coord_text->property_y().set_value(
208 ms.getNodeCoords(active_node).y - radius);
212 coord_text = new Gnome::Canvas::Text(
214 ms.getNodeCoords(active_node).x + radius,
215 ms.getNodeCoords(active_node).y - radius,
217 coord_text->property_fill_color().set_value("black");
218 coord_text->property_anchor().set_value(Gtk::ANCHOR_SOUTH_WEST);
229 XY GraphDisplayerCanvas::calcArrowPos(XY moved_node_1, XY moved_node_2, XY fix_node, XY old_arrow_pos, int move_code)
234 return XY((moved_node_2.x + fix_node.x) / 2.0, (moved_node_2.y + fix_node.y) / 2.0);
237 return old_arrow_pos;
241 //////////////////////////////////////////////////////////////////////////////////////////////////////
242 /////////// keeps shape-with scalar multiplication - version 2.
243 //////////////////////////////////////////////////////////////////////////////////////////////////////
245 //old vector from one to the other node - a
246 XY a_v(moved_node_1.x-fix_node.x,moved_node_1.y-fix_node.y);
247 //new vector from one to the other node - b
248 XY b_v(moved_node_2.x-fix_node.x,moved_node_2.y-fix_node.y);
250 double absa=sqrt(a_v.normSquare());
251 double absb=sqrt(b_v.normSquare());
253 if ((absa == 0.0) || (absb == 0.0))
255 return old_arrow_pos;
259 //old vector from one node to the breakpoint - c
260 XY c_v(old_arrow_pos.x-fix_node.x,old_arrow_pos.y-fix_node.y);
262 //unit vector with the same direction to a_v
263 XY a_v_u(a_v.x/absa,a_v.y/absa);
265 //normal vector of unit vector with the same direction to a_v
266 XY a_v_u_n(((-1)*a_v_u.y),a_v_u.x);
268 //unit vector with the same direction to b_v
269 XY b_v_u(b_v.x/absb,b_v.y/absb);
271 //normal vector of unit vector with the same direction to b_v
272 XY b_v_u_n(((-1)*b_v_u.y),b_v_u.x);
274 //vector c in a_v_u and a_v_u_n co-ordinate system
275 XY c_a(c_v*a_v_u,c_v*a_v_u_n);
277 //new vector from one node to the breakpoint - d - we have to calculate this one
278 XY d_v=absb/absa*(c_a.x*b_v_u+c_a.y*b_v_u_n);
280 return XY(d_v.x+fix_node.x,d_v.y+fix_node.y);
290 bool GraphDisplayerCanvas::createNodeEventHandler(GdkEvent* e)
292 MapStorage& ms = *mytab.mapstorage;
297 case GDK_MOTION_NOTIFY:
299 GdkEvent * generated=new GdkEvent();
300 generated->motion.x=e->motion.x;
301 generated->motion.y=e->motion.y;
302 generated->type=GDK_MOTION_NOTIFY;
303 moveEventHandler(generated);
307 case GDK_BUTTON_RELEASE:
314 window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
316 active_node = ms.addNode(XY(clicked_x, clicked_y));
318 nodesmap[active_node]=new Gnome::Canvas::Ellipse(displayed_graph,
319 clicked_x-20, clicked_y-20, clicked_x+20, clicked_y+20);
320 active_item=(Gnome::Canvas::Item *)(nodesmap[active_node]);
321 *(nodesmap[active_node]) <<
322 Gnome::Canvas::Properties::fill_color("blue");
323 *(nodesmap[active_node]) <<
324 Gnome::Canvas::Properties::outline_color("black");
325 active_item->raise_to_top();
327 (nodesmap[active_node])->show();
329 nodetextmap[active_node]=new Gnome::Canvas::Text(displayed_graph,
330 clicked_x+node_property_defaults[N_RADIUS]+5,
331 clicked_y+node_property_defaults[N_RADIUS]+5, "");
332 nodetextmap[active_node]->property_fill_color().set_value("darkblue");
333 nodetextmap[active_node]->raise_to_top();
335 // mapwin.updateNode(active_node);
336 propertyUpdate(active_node);
349 bool GraphDisplayerCanvas::createEdgeEventHandler(GdkEvent* e)
351 MapStorage& ms = *mytab.mapstorage;
355 case GDK_BUTTON_PRESS:
356 //in edge creation right button has special meaning
357 if(e->button.button!=3)
359 //there is not yet selected node
360 if(active_node==INVALID)
362 //we mark the location of the event to be able to calculate parameters of dragging
364 window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
366 active_item=(get_item_at(clicked_x, clicked_y));
368 for (NodeIt i(ms.graph); i!=INVALID; ++i)
370 if(nodesmap[i]==active_item)
375 //the clicked item is really a node
376 if(active_node!=INVALID)
378 *(nodesmap[active_node]) << Gnome::Canvas::Properties::fill_color("red");
381 //clicked item was not a node. It could be e.g. edge.
387 //we only have to do sg. if the mouse button
388 // is pressed already once AND the click was
389 // on a node that was found in the set of
390 //nodes, and now we only search for the second
394 window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
395 target_item=(get_item_at(clicked_x, clicked_y));
396 Node target_node=INVALID;
397 for (NodeIt i(ms.graph); i!=INVALID; ++i)
399 if(nodesmap[i]==target_item)
404 //the clicked item is a node, the edge can be drawn
405 if(target_node!=INVALID)
409 *(nodesmap[target_node]) <<
410 Gnome::Canvas::Properties::fill_color("red");
412 active_edge = ms.addEdge(active_node, target_node);
414 if(target_node!=active_node)
416 edgesmap[active_edge]=new BrokenEdge(displayed_graph, active_edge, *this);
420 edgesmap[active_edge]=new LoopEdge(displayed_graph, active_edge, *this);
423 //initializing edge-text as well, to empty string
424 XY text_pos=ms.getArrowCoords(active_edge);
425 text_pos+=(XY(10,10));
427 edgetextmap[active_edge]=new Gnome::Canvas::Text(displayed_graph,
428 text_pos.x, text_pos.y, "");
429 edgetextmap[active_edge]->property_fill_color().set_value(
431 edgetextmap[active_edge]->raise_to_top();
433 propertyUpdate(active_edge);
435 //clicked item was not a node. it could be an e.g. edge. we do not
436 //deal with it furthermore.
444 case GDK_BUTTON_RELEASE:
446 //we clear settings in two cases
447 //1: the edge is ready (target_item has valid value)
448 //2: the edge creation is cancelled with right button
449 if((target_item)||(e->button.button==3))
453 propertyUpdate(active_node,N_COLOR);
458 propertyUpdate(ms.graph.target(active_edge),N_COLOR);
471 bool GraphDisplayerCanvas::eraserEventHandler(GdkEvent* e)
473 MapStorage& ms = *mytab.mapstorage;
477 case GDK_BUTTON_PRESS:
478 //finding the clicked items
479 window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
480 active_item=(get_item_at(clicked_x, clicked_y));
484 for (NodeIt i(ms.graph); i!=INVALID; ++i)
486 if(nodesmap[i]==active_item)
492 if(active_node==INVALID)
494 for (EdgeIt i(ms.graph); i!=INVALID; ++i)
496 if(edgesmap[i]->getLine()==active_item)
503 // return if the clicked object is neither an edge nor a node
504 if (active_edge == INVALID) return false;
506 //recolor activated item
509 *active_item << Gnome::Canvas::Properties::fill_color("red");
513 case GDK_BUTTON_RELEASE:
514 window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
517 //the cursor was not moved since pressing it
518 if( active_item == ( get_item_at (clicked_x, clicked_y) ) )
521 if(active_node!=INVALID)
525 std::set<Graph::Edge> edges_to_delete;
527 for(OutEdgeIt e(ms.graph,active_node);e!=INVALID;++e)
529 edges_to_delete.insert(e);
532 for(InEdgeIt e(ms.graph,active_node);e!=INVALID;++e)
534 edges_to_delete.insert(e);
537 //deleting collected edges
538 for(std::set<Graph::Edge>::iterator
539 edge_set_it=edges_to_delete.begin();
540 edge_set_it!=edges_to_delete.end();
543 deleteItem(*edge_set_it);
545 deleteItem(active_node);
547 //a simple edge was chosen
548 else if (active_edge != INVALID)
550 deleteItem(active_edge);
553 //pointer was moved, deletion is cancelled
556 if(active_node!=INVALID)
558 *active_item << Gnome::Canvas::Properties::fill_color("blue");
560 else if (active_edge != INVALID)
562 *active_item << Gnome::Canvas::Properties::fill_color("green");
572 case GDK_MOTION_NOTIFY:
581 bool GraphDisplayerCanvas::mapEditEventHandler(GdkEvent* e)
583 MapStorage& ms = *mytab.mapstorage;
585 if(actual_tool==MAP_EDIT)
589 case GDK_BUTTON_PRESS:
591 //for determine, whether it was an edge
592 Edge clicked_edge=INVALID;
593 //for determine, whether it was a node
594 Node clicked_node=INVALID;
596 window_to_world (e->button.x, e->button.y, clicked_x, clicked_y);
597 active_item=(get_item_at(clicked_x, clicked_y));
599 //find the activated item between text of nodes
600 for (NodeIt i(ms.graph); i!=INVALID; ++i)
602 //at the same time only one can be active
603 if(nodetextmap[i]==active_item)
609 //if there was not, search for it between nodes
610 if(clicked_node==INVALID)
612 for (NodeIt i(ms.graph); i!=INVALID; ++i)
614 //at the same time only one can be active
615 if(nodesmap[i]==active_item)
622 if(clicked_node==INVALID)
624 //find the activated item between texts
625 for (EdgeIt i(ms.graph); i!=INVALID; ++i)
627 //at the same time only one can be active
628 if(edgetextmap[i]==active_item)
634 //if it was not between texts, search for it between edges
635 if(clicked_edge==INVALID)
637 for (EdgeIt i(ms.graph); i!=INVALID; ++i)
639 //at the same time only one can be active
640 if((edgesmap[i]->getLine())==active_item)
648 //if it was really a node...
649 if(clicked_node!=INVALID)
651 // the id map is not editable
652 if (nodemap_to_edit == "label") return 0;
654 //and there is activated map
655 if(nodetextmap[clicked_node]->property_text().get_value()!="")
657 //activate the general variable for it
658 active_node=clicked_node;
661 Gtk::Dialog dialog("Edit value", true);
662 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
663 dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
664 Gtk::VBox* vbox = dialog.get_vbox();
667 Gtk::SpinButton spin(0.0, 4);
668 spin.set_increments(1.0, 10.0);
669 spin.set_range(-1000000.0, 1000000.0);
670 spin.set_numeric(true);
671 spin.set_value(atof(nodetextmap[active_node]->property_text().get_value().c_str()));
676 entry.set_text(nodetextmap[active_node]->property_text().get_value());
680 switch (dialog.run())
682 case Gtk::RESPONSE_NONE:
683 case Gtk::RESPONSE_CANCEL:
685 case Gtk::RESPONSE_ACCEPT:
686 switch (ms.getNodeMapElementType(nodemap_to_edit))
688 case MapValue::NUMERIC:
689 ms.set(nodemap_to_edit, active_node,
690 atof(entry.get_text().c_str()));
692 case MapValue::STRING:
693 ms.set(nodemap_to_edit, active_node,
694 static_cast<std::string>(entry.get_text()));
697 nodetextmap[active_node]->property_text().set_value(
698 static_cast<std::string>(ms.get(nodemap_to_edit, active_node)));
700 //mapwin.updateNode(active_node);
701 //mapwin.updateNode(Node(INVALID));
702 propertyUpdate(Node(INVALID));
707 //if it was really an edge...
708 if(clicked_edge!=INVALID)
710 // the id map is not editable
711 if (edgemap_to_edit == "label") return 0;
713 //and there is activated map
714 if(edgetextmap[clicked_edge]->property_text().get_value()!="")
716 //activate the general variable for it
717 active_edge=clicked_edge;
720 Gtk::Dialog dialog("Edit value", true);
721 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
722 dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
723 Gtk::VBox* vbox = dialog.get_vbox();
726 Gtk::SpinButton spin(0.0, 4);
727 spin.set_increments(1.0, 10.0);
728 spin.set_range(-1000000.0, 1000000.0);
729 spin.set_numeric(true);
730 spin.set_value(atof(edgetextmap[active_edge]->property_text().get_value().c_str()));
735 entry.set_text(edgetextmap[active_edge]->property_text().get_value());
739 std::cout << edgemap_to_edit << std::endl;
740 switch (dialog.run())
742 case Gtk::RESPONSE_NONE:
743 case Gtk::RESPONSE_CANCEL:
745 case Gtk::RESPONSE_ACCEPT:
746 switch (ms.getEdgeMapElementType(edgemap_to_edit))
748 case MapValue::NUMERIC:
749 ms.set(edgemap_to_edit, active_edge,
750 atof(entry.get_text().c_str()));
752 case MapValue::STRING:
753 ms.set(edgemap_to_edit, active_edge,
754 static_cast<std::string>(entry.get_text()));
757 edgetextmap[active_edge]->property_text().set_value(
758 static_cast<std::string>(ms.get(edgemap_to_edit, active_edge)));
760 //mapwin.updateEdge(active_edge);
761 // mapwin.updateEdge(Edge(INVALID));
762 propertyUpdate(Edge(INVALID));
775 void GraphDisplayerCanvas::deleteItem(Node node_to_delete)
777 delete(nodetextmap[node_to_delete]);
778 delete(nodesmap[node_to_delete]);
779 mytab.mapstorage->graph.erase(node_to_delete);
782 void GraphDisplayerCanvas::deleteItem(Edge edge_to_delete)
784 delete(edgetextmap[edge_to_delete]);
785 delete(edgesmap[edge_to_delete]);
786 mytab.mapstorage->graph.erase(edge_to_delete);
789 void GraphDisplayerCanvas::textReposition(XY new_place)
791 new_place+=(XY(10,10));
792 edgetextmap[forming_edge]->property_x().set_value(new_place.x);
793 edgetextmap[forming_edge]->property_y().set_value(new_place.y);
796 void GraphDisplayerCanvas::toggleEdgeActivity(EdgeBase* active_bre, bool on)
800 if(forming_edge!=INVALID)
802 std::cerr << "ERROR!!!! Valid edge found!" << std::endl;
806 for (EdgeIt i(mytab.mapstorage->graph); i!=INVALID; ++i)
808 if(edgesmap[i]==active_bre)
817 if(forming_edge!=INVALID)
819 forming_edge=INVALID;
823 std::cerr << "ERROR!!!! Invalid edge found!" << std::endl;
828 void GraphDisplayerCanvas::moveNode(double dx, double dy, Gnome::Canvas::Item * item, Node node)
830 MapStorage& ms = *mytab.mapstorage;
832 Gnome::Canvas::Item * moved_item=item;
833 Node moved_node=node;
835 if(item==NULL && node==INVALID)
837 moved_item=active_item;
838 moved_node=active_node;
845 //repositioning node and its text
846 moved_item->move(dx, dy);
847 nodetextmap[moved_node]->move(dx, dy);
849 // the new coordinates of the centre of the node
850 double coord_x = dx + ms.getNodeCoords(moved_node).x;
851 double coord_y = dy + ms.getNodeCoords(moved_node).y;
853 // write back the new coordinates to the coords map
854 ms.setNodeCoords(moved_node, XY(coord_x, coord_y));
856 //all the edges connected to the moved point has to be redrawn
857 for(OutEdgeIt ei(ms.graph,moved_node);ei!=INVALID;++ei)
861 if (ms.graph.source(ei) == ms.graph.target(ei))
863 arrow_pos = ms.getArrowCoords(ei) + XY(dx, dy);
867 XY moved_node_1(coord_x - dx, coord_y - dy);
868 XY moved_node_2(coord_x, coord_y);
869 Node target = ms.graph.target(ei);
870 XY fix_node = ms.getNodeCoords(target);
871 XY old_arrow_pos(ms.getArrowCoords(ei));
873 arrow_pos = calcArrowPos(moved_node_1, moved_node_2, fix_node, old_arrow_pos, isbutton);
876 ms.setArrowCoords(ei, arrow_pos);
877 edgesmap[ei]->draw();
879 //reposition of edgetext
880 XY text_pos=ms.getArrowCoords(ei);
881 text_pos+=(XY(10,10));
882 edgetextmap[ei]->property_x().set_value(text_pos.x);
883 edgetextmap[ei]->property_y().set_value(text_pos.y);
886 for(InEdgeIt ei(ms.graph,moved_node);ei!=INVALID;++ei)
888 if (ms.graph.source(ei) != ms.graph.target(ei))
890 XY moved_node_1(coord_x - dx, coord_y - dy);
891 XY moved_node_2(coord_x, coord_y);
892 Node source = ms.graph.source(ei);
893 XY fix_node = ms.getNodeCoords(source);
894 XY old_arrow_pos(ms.getArrowCoords(ei));
897 arrow_pos = calcArrowPos(moved_node_1, moved_node_2, fix_node, old_arrow_pos, isbutton);
899 ms.setArrowCoords(ei, arrow_pos);
900 edgesmap[ei]->draw();
902 //reposition of edgetext
903 XY text_pos=ms.getArrowCoords(ei);
904 text_pos+=(XY(10,10));
905 edgetextmap[ei]->property_x().set_value(text_pos.x);
906 edgetextmap[ei]->property_y().set_value(text_pos.y);
911 Gdk::Color GraphDisplayerCanvas::rainbowColorCounter(double min, double max, double w)
915 double pos=(w-min)/(max-min);
918 //rainbow transitions contain 6 phase
919 //in each phase only one color is changed
920 //first we determine the phase, in which
921 //the actual value belongs to
922 for (int i=0;i<=5;i++)
924 if(((double)i/6<pos)&&(pos<=(double(i+1)/6)))
931 //within its 1/6 long phase the relativ position
932 //determines the power of the color changed in
934 //we normalize that to one, to be able to give percentage
935 //value for the function
936 double rel_pos=(pos-(phase/6.0))*6.0;
941 color.set_rgb_p (1, 0, 1-rel_pos);
944 color.set_rgb_p (1, rel_pos, 0);
947 color.set_rgb_p (1-rel_pos, 1, 0);
950 color.set_rgb_p (0, 1, rel_pos);
953 color.set_rgb_p (0, 1-rel_pos, 1);
956 color.set_rgb_p ((rel_pos/3.0), 0, 1);
959 std::cout << "Wrong phase: " << phase << " " << pos << std::endl;
964 std::cout << "Wrong phase: " << phase << " " << pos << std::endl;