ARB
canvas.cxx
Go to the documentation of this file.
1 // ================================================================ //
2 // //
3 // File : canvas.cxx //
4 // Purpose : provide canvas to draw tree, seqdata, ... //
5 // //
6 // Institute of Microbiology (Technical University Munich) //
7 // http://www.arb-home.de/ //
8 // //
9 // ================================================================ //
10 
11 #include "canvas.hxx"
12 
13 #include <aw_root.hxx>
14 #include <aw_msg.hxx>
15 #include <aw_preset.hxx>
16 
17 #include <arbdbt.h>
18 
19 #include <algorithm>
20 
21 #if defined(DEVEL_RALF)
22 // # define TRACE_MODEL_VIEW_UPDATES
23 #endif
24 
25 #if defined(TRACE_MODEL_VIEW_UPDATES)
26 inline void TRACE_UPDATE(const char *action) {
27  char atime[256];
28  time_t t = time(0);
29  tm *tms = localtime(&t);
30 
31  strftime(atime, 255, "%k:%M:%S", tms);
32  fprintf(stderr, "[MVU] %s: %s\n", atime, action);
33 }
34 #else // !TRACE_MODEL_VIEW_UPDATES
35 inline void TRACE_UPDATE(const char *) {}
36 #endif
37 
38 using namespace std;
39 using namespace AW;
40 
42  modifying = 0;
43 
44  refresh = 0;
45  resize = 0;
46  zoom_reset = 0;
47  supdate = 0;
48  save = 0;
49 
50  padding.clear();
51 
52  zoom_mode = AWT_ZOOM_BOTH;
53  fit_mode = AWT_FIT_LARGER;
54 
55  dont_scroll = 0;
56 }
57 
60 
62  int maxpos = int(worldsize.r-rect.r)-1;
63  if (pos>maxpos) pos = maxpos;
64  if (pos<0) pos = 0;
65  aww->set_horizontal_scrollbar_position(pos);
66 }
67 
69  int maxpos = int(worldsize.b-rect.b)-1;
70  if (pos>maxpos) pos = maxpos;
71  if (pos<0) pos = 0;
72  aww->set_vertical_scrollbar_position(pos);
73 }
74 
76  AW_pos width = this->worldinfo.r - this->worldinfo.l;
77  AW_pos height = this->worldinfo.b - this->worldinfo.t;
78 
79  worldsize.l = 0;
80  worldsize.t = 0;
81 
82  AW::Vector zv = gfx->exports.zoomVector(trans_to_fit);
83 
84  worldsize.r = width *zv.x() + gfx->exports.get_x_padding();
85  worldsize.b = height*zv.y() + gfx->exports.get_y_padding();
86 
87  aww->tell_scrolled_picture_size(worldsize);
88 
89  aww->calculate_scrollbars();
90 
91  this->old_hor_scroll_pos = (int)((-this->worldinfo.l -
92  this->shift_x_to_fit)*
93  this->trans_to_fit +
94  gfx->exports.get_left_padding());
95  this->set_horizontal_scrollbar_position(this->aww, old_hor_scroll_pos);
96 
97  this->old_vert_scroll_pos = (int)((-this->worldinfo.t -
98  this->shift_y_to_fit)*
99  this->trans_to_fit+
100  gfx->exports.get_top_padding());
101 
102  this->set_vertical_scrollbar_position(this->aww, old_vert_scroll_pos);
103 }
104 
106  device->reset();
107  device->shift(AW::Vector(shift_x_to_fit, shift_y_to_fit));
108  device->zoom(this->trans_to_fit);
109 }
110 
111 void AWT_canvas::instant_resize(bool adjust_scrollbars) {
112  TRACE_UPDATE("resize");
113 
115 
116  AW_pos old_width = worldinfo.r - worldinfo.l;
117  AW_pos old_height = worldinfo.b - worldinfo.t;
118 
119  GB_transaction ta(this->gb_main);
120  AW_device_size *size_device = aww->get_size_device(AW_MIDDLE_AREA);
121 
122  size_device->set_filter(AW_SIZE|(consider_text_for_size ? AW_SIZE_UNSCALED : 0));
123  size_device->reset();
124 
125  gfx->show(size_device);
126 
127  if (consider_text_for_size) {
128  gfx->exports.set_extra_text_padding(size_device->get_unscaleable_overlap());
129  }
130 
131  size_device->get_size_information(&(this->worldinfo));
132  rect = size_device->get_area_size(); // real world size (no offset)
133 
134  if (adjust_scrollbars) {
135  if (old_width>0) {
136  if (shift_x_to_fit<0) {
137  AW_pos new_width = worldinfo.r - worldinfo.l;
138  shift_x_to_fit *= new_width/old_width;
139  }
140  }
141  if (old_height>0) {
142  if (shift_y_to_fit<0) {
143  AW_pos new_height = worldinfo.b - worldinfo.t;
144  shift_y_to_fit *= new_height/old_height;
145  }
146  }
147 
148  set_scrollbars();
149  }
150 
151  gfx->exports.clear_resize_request();
152  gfx->exports.request_refresh();
153 }
154 
156  instant_resize(false);
157 
158  TRACE_UPDATE("zoom_reset");
159  AW_pos width = this->worldinfo.r - this->worldinfo.l;
160  AW_pos height = this->worldinfo.b - this->worldinfo.t;
161 
162  AW_pos net_window_width = rect.r - rect.l - gfx->exports.get_x_padding();
163  AW_pos net_window_height = rect.b - rect.t - gfx->exports.get_y_padding();
164 
165  if (net_window_width<AWT_MIN_WIDTH) net_window_width = AWT_MIN_WIDTH;
166  if (net_window_height<AWT_MIN_WIDTH) net_window_height = AWT_MIN_WIDTH;
167 
168  if (width <EPS) width = EPS;
169  if (height <EPS) height = EPS;
170 
171  AW_pos x_scale = net_window_width/width;
172  AW_pos y_scale = net_window_height/height;
173 
174  trans_to_fit = -1;
175  switch (gfx->exports.fit_mode) {
176  case AWT_FIT_NEVER: trans_to_fit = 1.0; break;
177  case AWT_FIT_X: trans_to_fit = x_scale; break;
178  case AWT_FIT_Y: trans_to_fit = y_scale; break;
179  case AWT_FIT_LARGER: trans_to_fit = std::min(x_scale, y_scale); break;
180  case AWT_FIT_SMALLER: trans_to_fit = std::max(x_scale, y_scale); break;
181  }
182  aw_assert(trans_to_fit > 0);
183 
184  AW_pos center_shift_x = 0;
185  AW_pos center_shift_y = 0;
186 
187  if (gfx->exports.zoom_mode&AWT_ZOOM_X) center_shift_x = (net_window_width /trans_to_fit - width)/2;
188  if (gfx->exports.zoom_mode&AWT_ZOOM_Y) center_shift_y = (net_window_height/trans_to_fit - height)/2;
189 
190  // complete, upper left corner
191  this->shift_x_to_fit = - this->worldinfo.l + gfx->exports.get_left_padding()/trans_to_fit + center_shift_x;
192  this->shift_y_to_fit = - this->worldinfo.t + gfx->exports.get_top_padding()/trans_to_fit + center_shift_y;
193 
194  this->old_hor_scroll_pos = 0;
195  this->old_vert_scroll_pos = 0;
196 
197  // scale
198 
199  this->set_scrollbars();
200 
201  gfx->exports.clear_zoom_reset_request();
202 }
203 
204 void AWT_canvas::zoom(AW_device *device, bool zoomIn, const Rectangle& wanted_part, const Rectangle& current_part, int percent) {
205  // zooms the device.
206  //
207  // zoomIn == true -> wanted_part is zoomed to current_part
208  // zoomIn == false -> current_part is zoomed to wanted_part
209  //
210  // If wanted_part is very small -> assume mistake (act like single click)
211  // Single click zooms by 'percent' % centering on click position
212 
213  init_device(device);
214 
215  if (!gfx) {
216  awt_assert(0); // we have no display - does this occur?
217  // if yes, pls inform devel@arb-home.de about circumstances
218  return;
219  }
220 
221  AW_pos width = worldinfo.r-worldinfo.l;
222  AW_pos height = worldinfo.b-worldinfo.t;
223 
224  if (width<EPS) width = EPS;
225  if (height<EPS) height = EPS;
226 
227  AWT_zoom_mode zoom_mode = gfx->exports.zoom_mode;
228  if (zoom_mode == AWT_ZOOM_NEVER) {
229  aw_message("Zoom does not work in this mode");
230  return;
231  }
232 
233  Rectangle current(device->rtransform(current_part));
234  Rectangle wanted;
235 
236  bool isClick = false;
237  switch (zoom_mode) {
238  case AWT_ZOOM_BOTH: isClick = wanted_part.line_vector().length()<40.0; break;
239  case AWT_ZOOM_X: isClick = wanted_part.width()<30.0; break;
240  case AWT_ZOOM_Y: isClick = wanted_part.height()<30.0; break;
241 
242  case AWT_ZOOM_NEVER: awt_assert(0); break;
243  }
244 
245  if (isClick) { // very small part or single click
246  // -> zoom by 'percent' % on click position
247  Position clickPos = device->rtransform(wanted_part.centroid());
248 
249  Vector click2UpperLeft = current.upper_left_corner()-clickPos;
250  Vector click2LowerRight = current.lower_right_corner()-clickPos;
251 
252  double scale = (100-percent)/100.0;
253 
254  wanted = Rectangle(clickPos+scale*click2UpperLeft, clickPos+scale*click2LowerRight);
255  }
256  else {
257  wanted = Rectangle(device->rtransform(wanted_part));
258  }
259 
260  if (!zoomIn) {
261  // calculate big rectangle (outside of viewport), which is zoomed into viewport
262 
263  if (zoom_mode == AWT_ZOOM_BOTH) {
264  double factor = current.diagonal().length()/wanted.diagonal().length();
265  Vector curr2wanted(current.upper_left_corner(), wanted.upper_left_corner());
266  Rectangle big(current.upper_left_corner()+(curr2wanted*-factor), current.diagonal()*factor);
267 
268  wanted = big;
269  }
270  else {
271  double factor;
272  if (zoom_mode == AWT_ZOOM_X) {
273  factor = current.width()/wanted.width();
274  }
275  else {
276  awt_assert(zoom_mode == AWT_ZOOM_Y);
277  factor = current.height()/wanted.height();
278  }
279  Vector curr2wanted_start(current.upper_left_corner(), wanted.upper_left_corner());
280  Vector curr2wanted_end(current.lower_right_corner(), wanted.lower_right_corner());
281  Rectangle big(current.upper_left_corner()+(curr2wanted_start*-factor),
282  current.lower_right_corner()+(curr2wanted_end*-factor));
283 
284  wanted = big;
285  }
286  }
287 
288  // scroll
289  shift_x_to_fit = (zoom_mode&AWT_ZOOM_X) ? -wanted.start().xpos() : (shift_x_to_fit+worldinfo.l)*trans_to_fit;
290  shift_y_to_fit = (zoom_mode&AWT_ZOOM_Y) ? -wanted.start().ypos() : (shift_y_to_fit+worldinfo.t)*trans_to_fit;
291 
292  // scale
293  if ((rect.r-rect.l)<EPS) rect.r = rect.l+1;
294  if ((rect.b-rect.t)<EPS) rect.b = rect.t+1;
295 
296  AW_pos max_trans_to_fit = 0;
297 
298  switch (zoom_mode) {
299  case AWT_ZOOM_BOTH:
300  trans_to_fit = max((rect.r-rect.l)/wanted.width(), (rect.b-rect.t)/wanted.height());
301  max_trans_to_fit = 32000.0/max(width, height);
302  break;
303 
304  case AWT_ZOOM_X:
305  trans_to_fit = (rect.r-rect.l)/wanted.width();
306  max_trans_to_fit = 32000.0/width;
307  break;
308 
309  case AWT_ZOOM_Y:
310  trans_to_fit = (rect.b-rect.t)/wanted.height();
311  max_trans_to_fit = 32000.0/height;
312  break;
313 
314  case AWT_ZOOM_NEVER: awt_assert(0); break;
315  }
316  trans_to_fit = std::min(trans_to_fit, max_trans_to_fit);
317 
318  // correct scrolling for "dont_fit"-direction
319  if (zoom_mode == AWT_ZOOM_Y) shift_x_to_fit = (shift_x_to_fit/trans_to_fit)-worldinfo.l;
320  if (zoom_mode == AWT_ZOOM_X) shift_y_to_fit = (shift_y_to_fit/trans_to_fit)-worldinfo.t;
321 
322  set_scrollbars();
323 }
324 
325 inline void nt_draw_zoom_box(AW_device *device, int gc, AW_pos x1, AW_pos y1, AW_pos x2, AW_pos y2) {
326  device->box(gc, AW::FillStyle::EMPTY, x1, y1, x2-x1, y2-y1);
327 }
328 inline void nt_draw_zoom_box(AW_device *device, AWT_canvas *scr) {
329  nt_draw_zoom_box(device,
330  scr->gfx->get_drag_gc(),
331  scr->zoom_drag_sx, scr->zoom_drag_sy,
332  scr->zoom_drag_ex, scr->zoom_drag_ey);
333 }
334 
335 static void clip_expose(AW_window *aww, AWT_canvas *scr,
336  int left_border, int right_border,
337  int top_border, int bottom_border,
338  int hor_overlap, int ver_overlap)
339 {
340  AW_device *device = aww->get_device(AW_MIDDLE_AREA);
341  device->set_filter(AW_SCREEN);
342  device->reset();
343 
344  device->set_top_clip_border(top_border);
345  device->set_bottom_clip_border(bottom_border);
346  device->set_left_clip_border(left_border);
347  device->set_right_clip_border(right_border);
348 
349  device->clear_part(left_border, top_border, right_border-left_border,
350  bottom_border-top_border, -1);
351 
352  GB_transaction ta(scr->gb_main);
353 
354  scr->init_device(device);
355 
356  if (hor_overlap> 0.0) {
357  device->set_right_clip_border(right_border + hor_overlap);
358  }
359  if (hor_overlap< 0.0) {
360  device->set_left_clip_border(left_border + hor_overlap);
361  }
362  if (ver_overlap> 0.0) {
363  device->set_bottom_clip_border(bottom_border + ver_overlap);
364  }
365  if (ver_overlap< 0.0) {
366  device->set_top_clip_border(top_border + ver_overlap);
367  }
368  scr->gfx->show(device);
369  scr->announce_screen_update();
370 }
371 
373  TRACE_UPDATE("refresh");
375 
376  AW_device *device = this->aww->get_device (AW_MIDDLE_AREA);
377  device->clear(-1);
378  clip_expose(this->aww, this, this->rect.l, this->rect.r,
379  this->rect.t, this->rect.b, 0, 0);
380 
381  gfx->exports.clear_refresh_request();
382 }
383 
385  AWT_auto_refresh allowed_on(scr);
386  scr->request_refresh();
387 }
388 static void canvas_resize_cb(UNFIXED, AWT_canvas *scr) {
389  AWT_auto_refresh allowed_on(scr);
390  scr->request_zoom_reset();
391 }
392 
393 void AWT_GC_changed_cb(GcChange whatChanged, AWT_canvas *scr) {
394  // standard callback which may be passed to AW_manage_GC
395  AWT_auto_refresh allowed_on(scr);
396 
397  switch (whatChanged) {
398  case GC_COLOR_CHANGED:
400  scr->request_refresh();
401  break;
402  case GC_FONT_CHANGED:
403  scr->request_resize();
404  break;
405  }
406 }
407 
408 static void canvas_focus_cb(AW_window *, AWT_canvas *scr) {
409  if (scr->gb_main) {
410  scr->push_transaction();
411 
412  { AWT_auto_refresh check_db_update(scr); }
413 
414  scr->pop_transaction();
415  }
416 }
417 
418 const int ZOOM_SPEED_CLICK = 10;
419 const int ZOOM_SPEED_WHEEL = 4;
420 
421 static bool handleZoomEvent(AWT_canvas *scr, AW_device *device, const AW_event& event, int percent) {
422  bool handled = false;
423  bool zoomIn = true;
424 
425  if (event.button == AW_BUTTON_LEFT) { handled = true; }
426  else if (event.button == AW_BUTTON_RIGHT) { handled = true; zoomIn = false; }
427 
428  if (handled) {
429  if (event.type == AW_Mouse_Press) {
430  scr->drag = 1;
431  scr->zoom_drag_sx = scr->zoom_drag_ex = event.x;
432  scr->zoom_drag_sy = scr->zoom_drag_ey = event.y;
433  }
434  else {
435  // delete last box
436  nt_draw_zoom_box(device, scr);
437  scr->drag = 0;
438 
439  Rectangle screen(scr->rect, INCLUSIVE_OUTLINE);
440  Rectangle drag(scr->zoom_drag_sx, scr->zoom_drag_sy, scr->zoom_drag_ex, scr->zoom_drag_ey);
441 
442  scr->zoom(device, zoomIn, drag, screen, percent);
443  scr->request_refresh();
444  }
445  }
446  else if (event.keycode == AW_KEY_ASCII && event.character == '0') { // reset zoom (as promised by MODE_TEXT_STANDARD_ZOOMMODE)
447  scr->request_zoom_reset();
448  handled = true;
449  }
450  return handled;
451 }
452 
453 bool AWT_canvas::handleWheelEvent(AW_device *device, const AW_event& event) {
454  if (event.button != AW_WHEEL_UP && event.button != AW_WHEEL_DOWN) {
455  return false; // not handled
456  }
457  if (event.type == AW_Mouse_Press) {
458  AWT_auto_refresh allowed_on(this);
459 
460  if (event.keymodifier & AW_KEYMODE_CONTROL) {
461  AW_event faked = event;
462 
463  faked.button = (event.button == AW_WHEEL_UP) ? AW_BUTTON_LEFT : AW_BUTTON_RIGHT;
464  handleZoomEvent(this, device, faked, ZOOM_SPEED_WHEEL);
465  faked.type = AW_Mouse_Release;
466  handleZoomEvent(this, device, faked, ZOOM_SPEED_WHEEL);
467  }
468  else {
469  bool horizontal = event.keymodifier & AW_KEYMODE_ALT;
470 
471  int viewport_size = horizontal ? (rect.r-rect.l+1) : (rect.b-rect.t+1);
472  int gfx_size = horizontal ? (worldsize.r-worldsize.l) : (worldsize.b-worldsize.t);
473 
474  // scroll 10% of screen or 10% of graphic size (whichever is smaller):
475  int dist = std::min(viewport_size / 20, gfx_size / 30);
476  int direction = event.button == AW_WHEEL_UP ? -dist : dist;
477 
478  int dx = horizontal ? direction : 0;
479  int dy = horizontal ? 0 : direction;
480 
481  scroll(dx, dy);
482  }
483  }
484  return true;
485 }
486 
487 void AWT_graphic::update_DB_and_model_as_requested(GBDATA *gb_main) {
488  // updates DB from model and recalculates structure
489  // (does only what is requested via exports-flags)
490 
491  if (exports.needs_save()) {
492  TRACE_UPDATE("save_to_DB");
493  GB_ERROR error = save_to_DB(gb_main, NULp);
494  if (error) {
495  aw_message(error);
496  load_from_DB(gb_main, NULp);
497  }
498  exports.clear_save_request(); // move into save and load?
499 
500  exports.request_structure_update(); // @@@ shall be done by load/save
501  awt_assert(!exports.needs_save());
502  }
503  if (exports.needs_structure_update()) {
504  TRACE_UPDATE("update_structure");
505  update_structure();
506  exports.clear_structure_update_request(); // @@@ move into update_structure?
507 
508  exports.request_resize(); // @@@ shall be done by update_structure itself!
509  awt_assert(!exports.needs_structure_update());
510  }
511  if (gb_main) notify_synchronized(gb_main);
512 }
513 
514 void AWT_canvas::sync_DB_model_and_view(bool perform_refresh) {
515  // synchronizes DB from model, updates the model and the view
516  // (does only what is requested via exports-flags)
517  //
518  // if 'perform_refresh' is true -> updates display
519  // otherwise refresh is skipped
520 
521  push_transaction(); // w/o sync may perform an invalid recursion (e.g. if AWAR_TREE_REFRESH gets triggered by save)
522  {
523  LocallyModify<int> allow_flag_modify(gfx->exports.get_modifying_flag_ref(), -1); // allow flag updates
524 
525  gfx->update_DB_and_model_as_requested(gb_main);
526 
527  // @@@ display shall not be updated unconditionally, as done atm
528  // (instead force after some commands, delay until idle callback for other commands)
529  gfx->exports.update_display_as_requested(this, perform_refresh);
530  }
531  pop_transaction();
532 }
533 
534 static void input_event(AW_window *aww, AWT_canvas *scr) {
535  awt_assert(aww = scr->aww);
536 
537  AW_event event;
538  aww->get_event(&event);
539 
540  AW_device *device = aww->get_device(AW_MIDDLE_AREA);
541  device->set_filter(AW_SCREEN);
542  device->reset();
543 
544  scr->push_transaction();
545  {
546  AWT_auto_refresh allowed_on(scr);
547 
548  bool event_handled = false;
549 
550  if (event.button == AW_BUTTON_MIDDLE) {
551  event_handled = true; // only set zoom_drag_e.. below
552  }
553  else if (scr->mode == AWT_MODE_ZOOM) { // zoom mode is identical for all applications, so handle it here
554  event_handled = handleZoomEvent(scr, device, event, ZOOM_SPEED_CLICK);
555  }
556 
557  if (!event_handled) {
558  event_handled = scr->handleWheelEvent(device, event);
559  }
560 
561  if (!event_handled) {
562  AW_device_click *click_device = aww->get_click_device(AW_MIDDLE_AREA, event.x, event.y, AWT_CATCH);
563  click_device->set_filter(AW_CLICK);
564  device->set_filter(AW_SCREEN);
565 
566  scr->init_device(click_device);
567  scr->init_device(device);
568 
569  scr->gfx->show(click_device);
570  if (event.type == AW_Mouse_Press) {
571  scr->gfx->drag_target_detection(false);
572  // drag_target_detection is off by default.
573  // it should be activated in handle_command (by all modes that need it)
574  }
575 
576  AWT_graphic_event gevent(scr->mode, event, false, click_device);
577  scr->gfx->handle_command(device, gevent);
578  }
579  }
580 
581  scr->zoom_drag_ex = event.x;
582  scr->zoom_drag_ey = event.y;
583 
584  scr->pop_transaction();
585 }
586 
587 
588 void AWT_canvas::set_dragEndpoint(int dragx, int dragy) {
589  switch (gfx->exports.zoom_mode) {
590  case AWT_ZOOM_NEVER: {
591  awt_assert(0);
592  break;
593  }
594  case AWT_ZOOM_X: {
595  zoom_drag_sy = rect.t;
596  zoom_drag_ey = rect.b-1;
597  zoom_drag_ex = dragx;
598  break;
599  }
600  case AWT_ZOOM_Y: {
601  zoom_drag_sx = rect.l;
602  zoom_drag_ex = rect.r-1;
603  zoom_drag_ey = dragy;
604  break;
605  }
606  case AWT_ZOOM_BOTH: {
607  zoom_drag_ex = dragx;
608  zoom_drag_ey = dragy;
609 
610  int drag_sx = zoom_drag_ex-zoom_drag_sx;
611  int drag_sy = zoom_drag_ey-zoom_drag_sy;
612 
613  bool correct_x = false;
614  bool correct_y = false;
615  double factor;
616 
617  int scr_sx = rect.r-rect.l;
618  int scr_sy = rect.b-rect.t;
619 
620  if (drag_sx == 0) {
621  if (drag_sy != 0) { factor = double(drag_sy)/scr_sy; correct_x = true; }
622  }
623  else {
624  if (drag_sy == 0) { factor = double(drag_sx)/scr_sx; correct_y = true; }
625  else {
626  double facx = double(drag_sx)/scr_sx;
627  double facy = double(drag_sy)/scr_sy;
628 
629  if (fabs(facx)>fabs(facy)) { factor = facx; correct_y = true; }
630  else { factor = facy; correct_x = true; }
631  }
632  }
633 
634  if (correct_x) {
635  int width = int(scr_sx*factor) * ((drag_sx*drag_sy) < 0 ? -1 : 1);
636  zoom_drag_ex = zoom_drag_sx+width;
637  }
638  else if (correct_y) {
639  int height = int(scr_sy*factor) * ((drag_sx*drag_sy) < 0 ? -1 : 1);
640  zoom_drag_ey = zoom_drag_sy+height;
641  }
642  break;
643  }
644  }
645 }
646 
647 static void motion_event(AW_window *aww, AWT_canvas *scr) {
648  AW_device *device = aww->get_device(AW_MIDDLE_AREA);
649  device->reset();
650  device->set_filter(AW_SCREEN);
651 
652  scr->push_transaction();
653 
654  AW_event event;
655  aww->get_event(&event);
656 
657  {
658  AWT_auto_refresh allowed_on(scr);
659 
660  if (event.button == AW_BUTTON_MIDDLE) {
661  // shift display in ALL modes
662  int dx = event.x - scr->zoom_drag_ex;
663  int dy = event.y - scr->zoom_drag_ey;
664 
665  scr->zoom_drag_ex = event.x;
666  scr->zoom_drag_ey = event.y;
667 
668  // display
669  scr->scroll(-dx*3, -dy*3);
670  }
671  else {
672  if (event.button == AW_BUTTON_LEFT || event.button == AW_BUTTON_RIGHT) {
673  if (scr->mode == AWT_MODE_ZOOM) {
674  if (scr->gfx->exports.zoom_mode != AWT_ZOOM_NEVER) {
675  nt_draw_zoom_box(device, scr);
676  scr->set_dragEndpoint(event.x, event.y);
677  nt_draw_zoom_box(device, scr);
678  }
679  }
680  else {
681  AW_device_click *click_device = NULp;
682 
683  if (scr->gfx->wants_drag_target()) {
684  // drag/drop-target is only updated if requested via AWT_graphic::drag_target_detection
685  click_device = aww->get_click_device(AW_MIDDLE_AREA, event.x, event.y, AWT_CATCH);
686  click_device->set_filter(AW_CLICK_DROP);
687 
688  scr->init_device(click_device);
689  scr->gfx->show(click_device);
690  }
691 
692  scr->init_device(device);
693  AWT_graphic_event gevent(scr->mode, event, true, click_device);
694  scr->gfx->handle_command(device, gevent);
695  }
696  }
697  }
698  }
699  scr->pop_transaction();
700 }
701 
702 void AWT_canvas::scroll(int dx, int dy, bool dont_update_scrollbars) {
703  int csx, cdx, cwidth, csy, cdy, cheight;
704  AW_device *device;
705  if (!dont_update_scrollbars) {
706  this->old_hor_scroll_pos += dx;
707  this->set_horizontal_scrollbar_position(aww, this->old_hor_scroll_pos);
708  this->old_vert_scroll_pos += dy;
709  this->set_vertical_scrollbar_position(aww, this->old_vert_scroll_pos);
710  }
711  device = aww->get_device (AW_MIDDLE_AREA);
712  device->set_filter(AW_SCREEN);
713  device->reset();
714  int screenwidth = this->rect.r-this->rect.l;
715  int screenheight = this->rect.b-this->rect.t;
716 
717  // compute move area params
718 
719  if (dx>0) {
720  csx = dx;
721  cdx = 0;
722  cwidth = screenwidth-dx;
723  }
724  else {
725  csx = 0;
726  cdx = -dx;
727  cwidth = screenwidth+dx;
728  }
729  if (dy>0) {
730  csy = dy;
731  cdy = 0;
732  cheight = screenheight-dy;
733  }
734  else {
735  csy = 0;
736  cdy = -dy;
737  cheight = screenheight+dy;
738  }
739 
740  // move area
741  if (!gfx->exports.dont_scroll) {
742  device->move_region(csx, csy, cwidth, cheight, cdx, cdy);
743  // redraw stripes
744  this->shift_x_to_fit -= dx/this->trans_to_fit;
745  this->shift_y_to_fit -= dy/this->trans_to_fit;
746 
747  // x-stripe
748  if ((int)dx>0) {
749  clip_expose(aww, this,
750  screenwidth-dx, screenwidth, 0, screenheight,
751  -CLIP_OVERLAP, 0);
752  }
753  if ((int)dx<0) {
754  clip_expose(aww, this,
755  0, -dx, 0, screenheight,
756  CLIP_OVERLAP, 0);
757  }
758 
759  // y-stripe
760  if ((int)dy>0) {
761  clip_expose(aww, this,
762  0, screenwidth, screenheight-dy, screenheight,
763  0, -CLIP_OVERLAP);
764  }
765  if ((int)dy<0) {
766  clip_expose(aww, this,
767  0, screenwidth, 0, -dy,
768  0, CLIP_OVERLAP);
769  }
770  }
771  else { // redraw everything
772  // redraw stripes
773  this->shift_x_to_fit -= dx/this->trans_to_fit;
774  this->shift_y_to_fit -= dy/this->trans_to_fit;
775  }
776  AWT_auto_refresh allowed_on(this);
777  this->request_refresh();
778 }
779 
780 static void scroll_vert_cb(AW_window *aww, AWT_canvas* scr) {
781  int new_vert = aww->slider_pos_vertical;
782  int delta_screen_y = (new_vert - scr->old_vert_scroll_pos);
783 
784  scr->scroll(0, delta_screen_y, true);
785  scr->old_vert_scroll_pos = new_vert;
786 }
787 
788 static void scroll_hor_cb(AW_window *aww, AWT_canvas* scr) {
789  int new_hor = aww->slider_pos_horizontal;
790  int delta_screen_x = (new_hor - scr->old_hor_scroll_pos);
791 
792  scr->scroll(delta_screen_x, 0, true);
793  scr->old_hor_scroll_pos = new_hor;
794 }
795 
796 
797 AWT_canvas::AWT_canvas(GBDATA *gb_main_, AW_window *aww_, const char *gc_base_name_, AWT_graphic *gfx_) :
798  consider_text_for_size(true),
799  gc_base_name(ARB_strdup(gc_base_name_)),
800  announce_update_cb(NULp),
801  user_data(0),
802  shift_x_to_fit(0),
803  shift_y_to_fit(0),
804  gb_main(gb_main_),
805  aww(aww_),
806  awr(aww->get_root()),
807  gfx(gfx_),
808  gc_manager(gfx->init_devices(aww, aww->get_device(AW_MIDDLE_AREA), this)),
809  mode(AWT_MODE_NONE)
810 {
812 
813  canvas_resize_cb(NULp, this);
814 
815  aww->set_expose_callback(AW_MIDDLE_AREA, makeWindowCallback(AWT_expose_cb, this));
816  aww->set_resize_callback(AW_MIDDLE_AREA, makeWindowCallback(canvas_resize_cb, this));
817  aww->set_input_callback(AW_MIDDLE_AREA, makeWindowCallback(input_event, this));
818  aww->set_focus_callback(makeWindowCallback(canvas_focus_cb, this));
819 
820  aww->set_motion_callback(AW_MIDDLE_AREA, makeWindowCallback(motion_event, this));
821  aww->set_horizontal_change_callback(makeWindowCallback(scroll_hor_cb, this));
822  aww->set_vertical_change_callback(makeWindowCallback(scroll_vert_cb, this));
823 }
824 
825 // --------------------------
826 // AWT_nonDB_graphic
827 
829  return "AWT_nonDB_graphic cannot be loaded";
830 }
831 
833  return "AWT_nonDB_graphic cannot be saved";
834 }
835 
836 void AWT_nonDB_graphic::check_for_DB_update(GBDATA *) { // @@@ should not be called!
837 #if defined(DEBUG)
838  printf("AWT_nonDB_graphic can't check_for_DB_update\n");
839 #endif // DEBUG
840 }
842 #if defined(DEBUG)
843  printf("AWT_nonDB_graphic can't notify_synchronized\n");
844 #endif // DEBUG
845 }
846 
847 
848 
void zoom(AW_device *device, bool zoomIn, const AW::Rectangle &wanted_part, const AW::Rectangle &current_part, int percent)
Definition: canvas.cxx:204
#define awt_assert(cond)
Definition: TreeAwars.cxx:26
virtual void clear(AW_bitset filteri)
Definition: AW_device.cxx:313
const char * GB_ERROR
Definition: arb_core.h:25
void push_transaction() const
Definition: canvas.cxx:58
static void motion_event(AW_window *aww, AWT_canvas *scr)
Definition: canvas.cxx:647
int zoom_drag_ey
Definition: canvas.hxx:332
void set_resize_callback(AW_area area, const WindowCallback &wcb)
Definition: AW_window.cxx:816
const AW_bitset AW_SIZE
Definition: aw_device.hxx:37
const AW_bitset AW_SCREEN
Definition: aw_device.hxx:34
void request_zoom_reset()
Definition: canvas.hxx:374
void set_horizontal_scrollbar_position(AW_window *aww, int pos)
Definition: canvas.cxx:61
AW_device * get_device(AW_area area)
Definition: AW_window.cxx:537
const AW_screen_area & get_area_size() const
Definition: AW_device.cxx:57
Position centroid() const
void set_left_clip_border(int left, bool allow_oversize=false)
double width() const
void shift(const AW::Vector &doff)
Definition: aw_device.hxx:131
void request_refresh()
Definition: canvas.hxx:372
const int ZOOM_SPEED_WHEEL
Definition: canvas.cxx:419
virtual void clear_part(const AW::Rectangle &rect, AW_bitset filteri)
Definition: AW_device.cxx:317
int drag_gc
Definition: canvas.hxx:256
char * ARB_strdup(const char *str)
Definition: arb_string.h:27
void set_top_clip_border(int top, bool allow_oversize=false)
void sync_DB_model_and_view(bool perform_refresh)
Definition: canvas.cxx:514
STL namespace.
void announce_screen_update()
Definition: canvas.hxx:405
int get_drag_gc() const
Definition: canvas.hxx:283
void instant_zoom_reset()
Definition: canvas.cxx:155
int slider_pos_horizontal
current position of the vertical slider
Definition: aw_window.hxx:350
AWT_canvas(GBDATA *gb_main_, AW_window *aww_, const char *gc_base_name_, AWT_graphic *gfx_)
Definition: canvas.cxx:797
void zoom(AW_pos scale)
Definition: AW_device.cxx:336
AWT_zoom_mode
Definition: canvas.hxx:85
AW_screen_area rect
Definition: canvas.hxx:326
void nt_draw_zoom_box(AW_device *device, int gc, AW_pos x1, AW_pos y1, AW_pos x2, AW_pos y2)
Definition: canvas.cxx:325
int old_vert_scroll_pos
Definition: canvas.hxx:325
GB_ERROR GB_push_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2494
char character
Definition: aw_window.hxx:91
void set_motion_callback(AW_area area, const WindowCallback &wcb)
Definition: AW_window.cxx:811
struct Unfixed_cb_parameter * UNFIXED
Definition: cb_base.h:15
#define EPS
Definition: trnsprob.cxx:19
void AWT_GC_changed_cb(GcChange whatChanged, AWT_canvas *scr)
Definition: canvas.cxx:393
GcChange
Definition: aw_base.hxx:94
double AW_pos
Definition: aw_base.hxx:29
int AW_get_drag_gc(AW_gc_manager *gcman)
Definition: AW_preset.cxx:1377
void drag_target_detection(bool detect)
Definition: canvas.hxx:281
#define assert_no_auto_refresh_for(CANVAS)
Definition: canvas.hxx:369
#define AWT_CATCH
void get_event(AW_event *eventi) const
Definition: AW_window.cxx:543
GB_ERROR save_to_DB(GBDATA *gb_main, const char *name) OVERRIDE __ATTR__USERESULT
Definition: canvas.cxx:832
const int ZOOM_SPEED_CLICK
Definition: canvas.cxx:418
void set_bottom_clip_border(int bottom, bool allow_oversize=false)
#define true
Definition: ureadseq.h:14
#define aw_assert(bed)
Definition: aw_position.hxx:29
AW_device_click * get_click_device(AW_area area, int mousex, int mousey, int max_distance)
Definition: AW_window.cxx:526
double height() const
void set_dragEndpoint(int x, int y)
Definition: canvas.cxx:588
void set_expose_callback(AW_area area, const WindowCallback &wcb)
Definition: AW_window.cxx:765
AW::Vector rtransform(const AW::Vector &vec) const
Definition: aw_device.hxx:145
static void error(const char *msg)
Definition: mkptypes.cxx:96
const AW_bitset AW_CLICK_DROP
Definition: aw_device.hxx:36
void set_vertical_scrollbar_position(AW_window *aww, int pos)
Definition: canvas.cxx:68
GB_write_int const char GB_write_autoconv_string WRITE_SKELETON(write_pointer, GBDATA *,"%p", GB_write_pointer) char *AW_awa if)(!gb_var) return strdup("")
Definition: AW_awar.cxx:163
void set_scrollbars()
Definition: canvas.cxx:75
const double & y() const
static void clip_expose(AW_window *aww, AWT_canvas *scr, int left_border, int right_border, int top_border, int bottom_border, int hor_overlap, int ver_overlap)
Definition: canvas.cxx:335
void AWT_expose_cb(UNFIXED, AWT_canvas *scr)
Definition: canvas.cxx:384
void check_for_DB_update(GBDATA *gb_main) OVERRIDE
Definition: canvas.cxx:836
const AW_bitset AW_CLICK
Definition: aw_device.hxx:35
GB_ERROR GB_pop_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2524
GB_ERROR load_from_DB(GBDATA *gb_main, const char *name) OVERRIDE __ATTR__USERESULT
Definition: canvas.cxx:828
AW_borders get_unscaleable_overlap() const
AW_key_code keycode
Definition: aw_window.hxx:90
AW_key_mod keymodifier
Definition: aw_window.hxx:83
int zoom_drag_sy
Definition: canvas.hxx:330
const Vector & diagonal() const
int old_hor_scroll_pos
Definition: canvas.hxx:324
const Vector & line_vector() const
virtual void handle_command(AW_device *device, AWT_graphic_event &event)=0
void set_right_clip_border(int right, bool allow_oversize=false)
void instant_refresh()
Definition: canvas.cxx:372
AWT_graphic * gfx
Definition: canvas.hxx:349
int zoom_drag_sx
Definition: canvas.hxx:329
void TRACE_UPDATE(const char *)
Definition: canvas.cxx:35
AW_event_type type
Definition: aw_window.hxx:81
bool wants_drag_target() const
Definition: canvas.hxx:280
const double & length() const
static void canvas_resize_cb(UNFIXED, AWT_canvas *scr)
Definition: canvas.cxx:388
void scroll(int delta_x, int delta_y, bool dont_update_scrollbars=false)
Definition: canvas.cxx:702
static void scroll_hor_cb(AW_window *aww, AWT_canvas *scr)
Definition: canvas.cxx:788
#define AWT_MIN_WIDTH
Definition: canvas.hxx:304
static void canvas_focus_cb(AW_window *, AWT_canvas *scr)
Definition: canvas.cxx:408
void set_focus_callback(const WindowCallback &wcb)
Definition: AW_window.cxx:778
static void input_event(AW_window *aww, AWT_canvas *scr)
Definition: canvas.cxx:534
void get_size_information(AW_world *ptr) const __ATTR__DEPRECATED_TODO("whole AW_world is deprecated")
Definition: aw_device.hxx:657
AW_gc_manager * gc_manager
Definition: canvas.hxx:351
AW_MouseButton button
Definition: aw_window.hxx:86
bool handleWheelEvent(AW_device *device, const AW_event &event)
Definition: canvas.cxx:453
static void scroll_vert_cb(AW_window *aww, AWT_canvas *scr)
Definition: canvas.cxx:780
#define CLIP_OVERLAP
Definition: canvas.hxx:302
AWT_graphic_exports exports
Definition: canvas.hxx:259
void init_device(AW_device *device)
Definition: canvas.cxx:105
void reset()
Definition: AW_device.cxx:280
const AW_bitset AW_SIZE_UNSCALED
Definition: aw_device.hxx:38
void aw_message(const char *msg)
Definition: AW_status.cxx:1142
int zoom_drag_ex
Definition: canvas.hxx:331
#define NULp
Definition: cxxforward.h:116
void set_filter(AW_bitset filteri)
Definition: AW_device.cxx:321
virtual void move_region(AW_pos src_x, AW_pos src_y, AW_pos width, AW_pos height, AW_pos dest_x, AW_pos dest_y)
Definition: AW_device.cxx:263
void pop_transaction() const
Definition: canvas.cxx:59
void instant_resize(bool adjust_scrollbars)
Definition: canvas.cxx:111
GB_transaction ta(gb_var)
void set_horizontal_change_callback(const WindowCallback &wcb)
Definition: AW_window.cxx:1049
bool box(int gc, AW::FillStyle filled, const AW::Rectangle &rect, AW_bitset filteri=AW_ALL_DEVICES_SCALED)
Definition: aw_device.hxx:471
void set_vertical_change_callback(const WindowCallback &wcb)
Definition: AW_window.cxx:1033
AWT_COMMAND_MODE mode
Definition: canvas.hxx:353
AWT_zoom_mode zoom_mode
Definition: canvas.hxx:137
GBDATA * gb_main
Definition: adname.cxx:32
AW_window * aww
Definition: canvas.hxx:347
static bool handleZoomEvent(AWT_canvas *scr, AW_device *device, const AW_event &event, int percent)
Definition: canvas.cxx:421
#define min(a, b)
Definition: f2c.h:153
int slider_pos_vertical
window id
Definition: aw_window.hxx:349
int drag
Definition: canvas.hxx:333
void set_input_callback(AW_area area, const WindowCallback &wcb)
Definition: AW_window.cxx:806
const double & x() const
void notify_synchronized(GBDATA *gb_main) OVERRIDE
Definition: canvas.cxx:841
GBDATA * gb_main
Definition: canvas.hxx:346
void request_resize()
Definition: canvas.hxx:373
virtual void show(AW_device *device)=0
#define max(a, b)
Definition: f2c.h:154