1 //
2 // canvas.cc
3 //
4
5 #include "wx/brush.h"
6 #include "wx/colour.h"
7 #include "wx/dc.h"
8 #include "wx/dcclient.h"
9 #include "wx/font.h"
10 #include "wx/gdicmn.h"
11 #include "wx/pen.h"
12 #include "wx/scrolwin.h"
13 #include "wx/textdlg.h"
14
15 #include "canvas.h"
16 #include "edge.h"
17 #include "graph.h"
18 #include "gui.h"
19 #include "lang.h"
20 #include "paramdialog.h"
21 #include "vertex.h"
22
23
BEGIN_EVENT_TABLE(Canvas,wxScrolledWindow)24 BEGIN_EVENT_TABLE(Canvas, wxScrolledWindow)
25 EVT_PAINT (Canvas::OnPaint)
26 EVT_ERASE_BACKGROUND(Canvas::OnEraseBackground)
27 EVT_LEFT_DOWN (Canvas::OnClick)
28 EVT_LEFT_DCLICK (Canvas::OnClick)
29 EVT_RIGHT_DOWN (Canvas::OnClick)
30 EVT_MOTION (Canvas::OnMouseMove)
31 EVT_CHAR (Canvas::OnKeyPress)
32 END_EVENT_TABLE()
33
34
35 void Canvas::draw (wxDC &dc, Vertex *v)
36 {
37 int r = Canvas::vertex_radius;
38 wxColour col = black;
39
40 if (v->selected) {
41 unsigned int idx = v->selection_colour %
42 selection_colours.size ();
43 col = selection_colours[idx];
44 }
45
46 dc.SetBrush (wxBrush (col));
47 dc.SetPen (wxPen (col));
48
49 dc.DrawCircle (v->x, v->y, r);
50
51 if (do_labels) {
52 dc.SetFont (*bold_font);
53 dc.SetTextForeground (col);
54 dc.DrawText (v->label, v->x + r, v->y - 2 * r);
55 }
56 }
57
draw(wxDC & dc,Edge * e,bool curved)58 void Canvas::draw (wxDC &dc, Edge *e, bool curved)
59 {
60 double dx, dy, r = Canvas::vertex_radius;
61 double theta;
62 wxColour col = black;
63
64 if (e->selected) {
65 unsigned int idx = e->selection_colour %
66 selection_colours.size ();
67 col = selection_colours[idx];
68 }
69 wxPen pen (col, Canvas::edge_width);
70 pen.SetCap (wxCAP_BUTT);
71 pen.SetJoin (wxJOIN_MITER);
72 dc.SetPen (pen);
73 dc.SetBrush (wxBrush (col));
74
75 theta = atan2 (e->w->y - e->v->y, e->w->x - e->v->x);
76 dx = r * cos (theta);
77 dy = r * sin (theta);
78
79 if (e->v->y == e->w->y) // horizontal line (dy = 0)
80 dy = 0;
81 else if (e->v->x == e->w->x) // vertical line (dx = 0)
82 dx = 0;
83
84 if (!curved)
85 dc.DrawLine (int (e->v->x + dx), int (e->v->y + dy),
86 int (e->w->x - dx), int (e->w->y - dy));
87 else {
88 dc.SetBrush (wxBrush (white));
89 // TODO: get this right!
90 double cx, cy; // mid-spline control point
91 cx = (e->v->x + e->w->x) / 2 - 2 * dy;
92 cy = (e->v->y + e->w->y) / 2 - 2 * dx;
93 dc.DrawSpline (int (e->v->x + dx), int (e->v->y + dy),
94 int (cx), int (cy),
95 int (e->w->x - dx), int (e->w->y - dy));
96
97 dc.SetBrush (wxBrush (col));
98 }
99
100 if (e->directed) {
101 wxPoint tri[3];
102 double scale = 0.7;
103 tri[0] = wxPoint (0, 0);
104 tri[1] = wxPoint (int (scale * (dy - dx)),
105 int (-scale * (dx + dy)));
106 tri[2] = wxPoint (int (-scale * (dx + dy)),
107 int (scale * (dx - dy)));
108 dc.DrawPolygon (3, tri, int (e->w->x - dx), int (e->w->y - dy));
109 }
110
111 if (do_weights) {
112 wxString str;
113 if (!do_flows)
114 str = wxString::Format (wxT("%i"), e->weight);
115 else
116 str = wxString::Format (wxT("%i/%i"), e->flow, e->weight);
117 double rx, ry;
118 wxCoord w, h;
119
120 dc.SetFont (*normal_font);
121 dc.GetTextExtent (str, &w, &h);
122
123 rx = (e->v->x + e->w->x) / 2 + dy;
124 ry = (e->v->y + e->w->y) / 2 - dx;
125 if (curved) {
126 rx += dy;
127 ry -= dy;
128 }
129 if (theta < 0)
130 rx += w * sin (theta);
131 ry -= h / 2;
132
133 if (fabs (theta) > M_PI_2)
134 ry -= dx / 2;
135
136 dc.SetTextForeground (col);
137 dc.DrawText (str, int (rx), int (ry));
138 }
139 }
140
findVertex(int x,int y) const141 Vertex *Canvas::findVertex (int x, int y) const
142 {
143 Graph::v_const_iterator vit;
144 int r2 = Canvas::vertex_radius * Canvas::vertex_radius;
145 Graph *g = *gg;
146
147 for (vit = g->v_begin (); vit != g->v_end (); ++vit) {
148 int dx = x - (*vit)->x, dy = y - (*vit)->y;
149 if ((dx * dx + dy * dy) < r2)
150 return *vit;
151 }
152
153 return 0;
154 }
155
findEdge(int x,int y) const156 Edge *Canvas::findEdge (int x, int y) const
157 {
158 Graph::e_const_iterator eit;
159 double rad = Canvas::vertex_radius;
160 double r2 = rad * rad;
161 double best_dist = 0; // actually, best distance squared
162 Edge *best_edge = 0;
163 Graph *g = *gg;
164
165 // find straight-line edge closest to (x,y)
166 for (eit = g->e_begin (); eit != g->e_end (); ++eit) {
167 double x1, y1, x2, y2, s2, num, d2;
168 x1 = (*eit)->v->x;
169 y1 = (*eit)->v->y;
170 x2 = (*eit)->w->x;
171 y2 = (*eit)->w->y;
172 if (x1 < x2) {
173 if ((x < (x1 - rad)) || ((x2 + rad) < x))
174 continue;
175 } else if (x2 < x1) {
176 if ((x < (x2 - rad)) || ((x1 + rad) < x))
177 continue;
178 }
179 if (y1 < y2) {
180 if ((y < (y1 - rad)) || ((y2 + rad) < y))
181 continue;
182 } else if (y2 < y1) {
183 if ((y < (y2 - rad)) || ((y1 + rad) < y))
184 continue;
185 }
186 s2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
187 if (s2 < 0.01)
188 continue; // degenerate edge
189 num = (y2 - y1) * (x - x1) - (x2 - x1) * (y - y1);
190 d2 = num * num / s2;
191 if ((d2 < best_dist) || !best_edge) {
192 best_edge = *eit;
193 best_dist = d2;
194 }
195 }
196
197 // still only accept it if within a vertex of (x,y)
198 if (best_dist <= r2)
199 return best_edge;
200 return 0;
201 }
202
cb_Properties()203 void Canvas::cb_Properties ()
204 {
205 Edge *e;
206 Graph *g = *gg;
207 Vertex *v;
208
209 v = g->v_selected_head;
210 e = g->e_selected_head;
211
212 if (v && !e) {
213 // Vertex Properties
214
215 wxString str = wxGetTextFromUser (_("Label:"),
216 _("Vertex Properties"),
217 v->label, gui);
218 if (str.IsEmpty ())
219 return;
220
221 // Handle duplicate labels
222 while (1) {
223 Vertex *f = g->find (str);
224 if (!f || (f == v))
225 break;
226 str += wxT("-dup");
227 }
228
229 if (str != v->label) {
230 gui->undoableAction (_("Rename vertex"));
231 g->rename (v, str);
232 redraw ();
233 }
234 } else if (e) {
235 // Edge Properties
236 ParamDialogEdge dlg (gui, _("Edge Properties"), e);
237 if (dlg.ShowModal () == wxID_CANCEL)
238 return;
239
240 int curr_dir = e->directed ? 1 : 0;
241 int new_wt = dlg.GetWeight (), new_dir = dlg.GetDirection ();
242 int new_flow = dlg.GetFlow ();
243 if ((new_wt == e->weight) && (new_dir == curr_dir)) {
244 if (e->flow != new_flow) {
245 e->flow = new_flow;
246 redraw ();
247 }
248 return;
249 }
250 if (new_wt == e->weight)
251 gui->undoableAction (_("Change edge direction"));
252 else if (new_dir == curr_dir)
253 gui->undoableAction (_("Change edge weight"));
254 else
255 gui->undoableAction (_("Change edge weight and direction"));
256 e->weight = new_wt;
257 if (new_dir == 0)
258 e->directed = false;
259 else if (new_dir == 1)
260 e->directed = true;
261 else {
262 e->directed = true;
263 Vertex *tmp = e->v;
264 e->v = e->w;
265 e->w = tmp;
266 }
267 redraw ();
268 }
269 }
270
Canvas(GTFrame * gui,Graph ** g)271 Canvas::Canvas (GTFrame *gui, Graph **g)
272 : wxScrolledWindow (gui, -1, wxPoint (0, 0), wxDefaultSize,
273 // 0),
274 wxNO_FULL_REPAINT_ON_RESIZE | wxCLIP_CHILDREN),
275 gui (gui), gg (g)
276 {
277 vertex_mode = true;
278 do_labels = true;
279 do_weights = do_flows = false;
280
281 white = wxColour (255, 255, 255);
282 black = wxColour (0, 0, 0);
283
284 // Selection colours
285 const char *cols[] = {
286 "red", "blue", "magenta", "green",
287 "brown", "cyan", "black"
288 };
289 for (unsigned int i = 0; i < (sizeof (cols) / sizeof (cols[0])); ++i) {
290 wxColour col = wxTheColourDatabase->Find
291 (wxString (cols[i], wxConvUTF8));
292 selection_colours.push_back (col);
293 }
294 red = selection_colours[0];
295
296
297 int pointSize = Canvas::font_height;
298 normal_font = new wxFont (pointSize, wxFONTFAMILY_DEFAULT,
299 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
300 bold_font = new wxFont (pointSize, wxFONTFAMILY_DEFAULT,
301 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD);
302
303 SetScrollRate (10, 10);
304
305 }
306
307 #define USE_WXMEMORYDC
308
309 #ifdef USE_WXMEMORYDC
310 #include "wx/dcmemory.h"
311 #endif
312
OnPaint(wxPaintEvent & event)313 void Canvas::OnPaint (wxPaintEvent &event)
314 {
315 #ifndef USE_WXMEMORYDC
316 wxPaintDC dc (this);
317 #else
318 wxPaintDC dest_dc (this);
319 int blit_width, blit_height;
320 GetClientSize (&blit_width, &blit_height);
321 wxBitmap bmp_blit (blit_width, blit_height);
322 wxMemoryDC dc;
323 dc.SelectObject (bmp_blit);
324 #endif
325 Graph *g = *gg;
326
327 #ifndef USE_WXMEMORYDC
328 DoPrepareDC (dc);
329 #else
330 DoPrepareDC (dc);
331 DoPrepareDC (dest_dc);
332 #endif
333
334 dc.SetBackground (wxBrush (white));
335 dc.Clear ();
336
337 Graph::e_const_iterator eit;
338 for (eit = g->e_begin (); eit != g->e_end (); ++eit) {
339 Edge *e = *eit;
340 bool curved = false;
341 if (e->directed) {
342 if (g->find (e->w, e->v, true))
343 curved = true;
344 }
345 draw (dc, e, curved);
346 }
347
348 Graph::v_const_iterator vit;
349 int maxx = 0, maxy = 0;
350 for (vit = g->v_begin (); vit != g->v_end (); ++vit) {
351 Vertex *v = *vit;
352 draw (dc, v);
353 if (v->x > maxx)
354 maxx = v->x;
355 if (v->y > maxy)
356 maxy = v->y;
357 }
358 #ifdef USE_WXMEMORYDC
359 // Blit!
360 dest_dc.Blit (0, 0, blit_width, blit_height, &dc, 0, 0);
361 #endif
362
363 SetVirtualSize (maxx + 30, maxy + 30);
364 }
365
OnEraseBackground(wxEraseEvent & event)366 void Canvas::OnEraseBackground (wxEraseEvent &event)
367 {
368 wxDC *dc = event.GetDC ();
369
370 dc->SetBackground (wxBrush (white));
371 dc->Clear ();
372 }
373
OnClick(wxMouseEvent & event)374 void Canvas::OnClick (wxMouseEvent &event)
375 {
376 Graph *g = *gg;
377 Vertex *v;
378 int m_x, m_y;
379
380 bool left_click = event.LeftDown () || event.LeftDClick (),
381 shifted = event.ShiftDown (),
382 double_click = event.ButtonDClick ();
383
384 CalcUnscrolledPosition (int (event.m_x), int (event.m_y), &m_x, &m_y);
385 v = findVertex (m_x, m_y);
386
387 // Assumptions: [lr]-click (or double click)
388
389 if (vertex_mode && left_click) {
390 if (!shifted) {
391 // no shift key
392 if (!double_click) {
393 // Single click
394 g->unselect_all ();
395 if (v)
396 g->select (v);
397 redraw ();
398 return;
399 } else {
400 // Double click
401 if (v)
402 cb_Properties ();
403 else {
404 gui->undoableAction (_("Add vertex"));
405 g->add (new Vertex ("", m_x, m_y));
406 redraw ();
407 }
408 return;
409 }
410 } else {
411 // shift key
412 if (!double_click) {
413 if (v) {
414 v->selected ?
415 g->unselect (v) :
416 g->select (v);
417 redraw ();
418 }
419 }
420 return;
421 }
422 } else if (vertex_mode && !left_click)
423 return; // do nothing
424
425 // edge mode
426 if (!v) {
427 Edge *e = findEdge (m_x, m_y);
428 if (!e) {
429 if (left_click && !shifted) {
430 g->unselect_all ();
431 redraw ();
432 }
433 return;
434 }
435 // clicking on an edge
436 if (!double_click) {
437 // Single click
438 if (left_click) {
439 if (!shifted) {
440 g->unselect_all ();
441 g->select (e);
442 } else {
443 e->selected ?
444 g->unselect (e) :
445 g->select (e);
446 }
447 } else
448 e->cycle_orientations ();
449 } else {
450 // Double click
451 if (left_click)
452 cb_Properties ();
453 }
454 redraw ();
455 return;
456 }
457 if (!left_click)
458 return;
459 if (!v->selected) {
460 Vertex *valt = g->v_selected_head;
461 if (!valt) {
462 g->select (v);
463 redraw ();
464 return;
465 }
466 if (!g->are_adjacent (valt, v)) {
467 gui->undoableAction (_("Add edge"));
468 g->add (new Edge (valt, v));
469 }
470 g->unselect_all ();
471 g->select (v);
472 redraw ();
473 return;
474 }
475 g->unselect (v);
476 redraw ();
477 }
478
OnMouseMove(wxMouseEvent & event)479 void Canvas::OnMouseMove (wxMouseEvent &event)
480 {
481 Graph *g = *gg;
482 Vertex *v;
483 int m_x, m_y;
484
485 CalcUnscrolledPosition (int (event.m_x), int (event.m_y), &m_x, &m_y);
486
487 if (event.Moving ()) {
488 // Button not down => nothing to do
489 motion_last_x = -1;
490 motion_last_y = -1;
491 return;
492 }
493
494 if (motion_last_x == -1) {
495 // First motion event; record start only
496 motion_last_x = m_x;
497 motion_last_y = m_y;
498 return;
499 }
500
501 // Move stuff
502 for (v = g->v_selected_head; v; v = v->next) {
503 v->x += (m_x - motion_last_x);
504 v->y += (m_y - motion_last_y);
505 }
506
507 motion_last_x = m_x;
508 motion_last_y = m_y;
509
510 redraw ();
511 }
512
OnKeyPress(wxKeyEvent & event)513 void Canvas::OnKeyPress (wxKeyEvent &event)
514 {
515 Graph *g = *gg;
516
517 if (!event.HasModifiers () && (event.GetKeyCode () == WXK_DELETE)) {
518 // delete selected objects
519 // TODO: make this message more accurate
520 gui->undoableAction (_("Delete objects"));
521
522 Edge *prevE, *e = g->e_selected_head;
523 while (e) {
524 prevE = e;
525 e = e->next;
526 g->remove (prevE);
527 }
528
529 Vertex *prevV, *v = g->v_selected_head;
530 while (v) {
531 prevV = v;
532 v = v->next;
533 g->remove (prevV);
534 }
535
536 redraw ();
537 return;
538 }
539
540 if (event.GetKeyCode () == ' ') {
541 gui->toggleMode ();
542 return;
543 }
544
545 // We don't want it - pass it back up
546 event.Skip ();
547 }
548
redraw()549 void Canvas::redraw ()
550 {
551 Refresh ();
552 Update ();
553 }
554
setVertexMode(bool v_mode)555 void Canvas::setVertexMode (bool v_mode)
556 {
557 vertex_mode = v_mode;
558 }
559
setEdgeMode()560 void Canvas::setEdgeMode ()
561 {
562 vertex_mode = false;
563 }
564
setParam(bool labels,bool weights,bool flows)565 void Canvas::setParam (bool labels, bool weights, bool flows)
566 {
567 do_labels = labels;
568 do_weights = weights;
569 do_flows = flows;
570 }
571