source: trunk/SL/CANVAS/canvas.cxx

Last change on this file was 19611, checked in by westram, 11 days ago
  • fix references to old names.
  • fix names of include wrappers.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.6 KB
Line 
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)
26inline 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
35inline void TRACE_UPDATE(const char *) {}
36#endif
37
38using namespace std;
39using namespace AW;
40
41void AWT_graphic_exports::init() {
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
58inline void AWT_canvas::push_transaction() const { if (gb_main) GB_push_transaction(gb_main); }
59inline void AWT_canvas::pop_transaction() const { if (gb_main) GB_pop_transaction(gb_main); }
60
61void AWT_canvas::set_horizontal_scrollbar_position(AW_window *, int pos) {
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
68void AWT_canvas::set_vertical_scrollbar_position(AW_window *, int pos) {
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
75void AWT_canvas::set_scrollbars() {
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
105void AWT_canvas::init_device(AW_device *device) {
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
111void AWT_canvas::instant_resize(bool adjust_scrollbars) {
112    TRACE_UPDATE("resize");
113
114    assert_no_auto_refresh_for(this);
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
155void AWT_canvas::instant_zoom_reset() {
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
204void 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
325inline 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}
328inline 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
335static 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
372void AWT_canvas::instant_refresh() {
373    TRACE_UPDATE("refresh");
374    assert_no_auto_refresh_for(this);
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
384void AWT_expose_cb(UNFIXED, AWT_canvas *scr) {
385    AWT_auto_refresh allowed_on(scr);
386    scr->request_refresh();
387}
388static void canvas_resize_cb(UNFIXED, AWT_canvas *scr) {
389    AWT_auto_refresh allowed_on(scr);
390    scr->request_zoom_reset();
391}
392
393void 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:
399        case GC_COLOR_GROUP_USE_CHANGED:
400            scr->request_refresh();
401            break;
402        case GC_FONT_CHANGED:
403            scr->request_resize();
404            break;
405    }
406}
407
408static 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
418const int ZOOM_SPEED_CLICK = 10;
419const int ZOOM_SPEED_WHEEL = 4;
420
421static 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
453bool 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
487void 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
514void 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
534static 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
588void 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
647static 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
702void 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
780static 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
788static 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
797AWT_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{
811    gfx->drag_gc   = AW_get_drag_gc(gc_manager);
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
828GB_ERROR AWT_nonDB_graphic::load_from_DB(GBDATA *, const char *) {
829    return "AWT_nonDB_graphic cannot be loaded";
830}
831
832GB_ERROR AWT_nonDB_graphic::save_to_DB(GBDATA *, const char *) {
833    return "AWT_nonDB_graphic cannot be saved";
834}
835
836void 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}
841void AWT_nonDB_graphic::notify_synchronized(GBDATA *) {
842#if defined(DEBUG)
843    printf("AWT_nonDB_graphic can't notify_synchronized\n");
844#endif // DEBUG
845}
846
847
848
Note: See TracBrowser for help on using the repository browser.