source: trunk/SL/CANVAS/canvas.hxx

Last change on this file was 19603, checked in by westram, 13 days ago
  • move canvas code from AWTSL/CANVAS
    • correct include wrapper + remove duplicate includes.
    • update/insert file headers.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.6 KB
Line 
1// ========================================================= //
2//                                                           //
3//   File      : canvas.hxx                                  //
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#ifndef CANVAS_HXX
12#define CANVAS_HXX
13
14#ifndef AW_WINDOW_HXX
15#include <aw_window.hxx>
16#endif
17#ifndef AW_DEVICE_HXX
18#include <aw_device.hxx>
19#endif
20#ifndef AW_DEVICE_CLICK_HXX
21#include <aw_device_click.hxx>
22#endif
23#ifndef ATTRIBUTES_H
24#include <attributes.h>
25#endif
26#ifndef ARB_ASSERT_H
27#include <arb_assert.h>
28#endif
29
30#define awt_assert(cond) arb_assert(cond)
31
32class AWT_canvas;
33class AW_device;
34
35enum AWT_COMMAND_MODE {
36    AWT_MODE_NONE,
37    AWT_MODE_EMPTY, // placeholder (currently used in PARSIMONY)
38
39    // NTREE, PARSIMONY, GENEMAP and SECEDIT:
40    AWT_MODE_ZOOM,
41
42    // NTREE, PARSIMONY and GENEMAP:
43    AWT_MODE_SELECT,
44    AWT_MODE_INFO, // (=ED4_SM_INFO in EDIT4)
45
46    // NTREE, PARSIMONY and SECEDIT:
47    AWT_MODE_SETROOT,
48
49    // NTREE and PARSIMONY:
50    AWT_MODE_MOVE,
51    AWT_MODE_MARK,
52    AWT_MODE_GROUP,
53    AWT_MODE_LZOOM,
54    AWT_MODE_SWAP,
55
56    // NTREE and SECEDIT:
57    AWT_MODE_ROTATE,
58
59    // NTREE only:
60    AWT_MODE_LINE,
61    AWT_MODE_WWW,
62    AWT_MODE_SPREAD,
63    AWT_MODE_LENGTH,
64    AWT_MODE_MULTIFURC,
65
66    // PARSIMONY only:
67    AWT_MODE_KERNINGHAN,
68    AWT_MODE_NNI,
69    AWT_MODE_OPTIMIZE,
70
71    // SECEDIT only:
72    AWT_MODE_FOLD,
73    AWT_MODE_CURSOR,
74    AWT_MODE_EDIT,
75    AWT_MODE_PINFO,
76    AWT_MODE_STRETCH,
77    AWT_MODE_SET_CURSOR
78};
79
80#define STANDARD_PADDING 10
81
82// --------------------------------------------------------------------------------
83// AWT_zoom_mode + AWT_fit_mode are correlated, but not strictly coupled
84
85enum AWT_zoom_mode { // bit values!
86    AWT_ZOOM_NEVER = 0,
87    AWT_ZOOM_X     = 1,
88    AWT_ZOOM_Y     = 2,
89    AWT_ZOOM_BOTH  = 3,
90};
91
92enum AWT_fit_mode {
93    AWT_FIT_NEVER,
94    AWT_FIT_LARGER,
95    AWT_FIT_SMALLER,
96    AWT_FIT_X,
97    AWT_FIT_Y,
98};
99
100// used combinations are:
101// AWT_ZOOM_NEVER + AWT_FIT_NEVER (NDS list, others)
102// AWT_ZOOM_X + AWT_FIT_X (dendrogram tree)
103// AWT_ZOOM_Y + AWT_FIT_Y
104// AWT_ZOOM_BOTH + AWT_FIT_LARGER (radial tree/gene-map; secedit)
105// AWT_ZOOM_BOTH + AWT_FIT_SMALLER (book-style gene-map)
106//
107// other combinations may work as well. some combinations make no sense.
108// --------------------------------------------------------------------------------
109
110
111class AWT_graphic_exports {
112    AW_borders default_padding;
113    AW_borders padding;
114
115    // sync-flags to update between
116    // - internal structure of AWT_graphic (e.g. AP_tree)
117    // - stored representation (normally in DB)
118    // - display/userinput
119    unsigned int refresh : 1;          // 1 -> do a refresh
120    unsigned int resize : 1;           // 1 -> size of graphic might have changed (implies 'refresh')
121    unsigned int zoom_reset : 1;       // 1 -> do a zoom-reset (implies 'resize')
122    unsigned int supdate : 1;          // 1 -> internal structure needs update; calls update_structure() (implies 'resize')
123    unsigned int save : 1;             // 1 -> save structure to DB (implies 'supdate')
124
125    int modifying; // number of AWT_auto_refresh instances.
126                   // !=0 -> flag modification allowed
127                   // >=0 -> AWT_auto_refresh instanciation allowed
128                   // -1 is used while performing updates
129
130    friend class AWT_auto_refresh;
131#if defined(UNIT_TESTS)
132    friend class fake_AWT_graphic_tree;
133#endif
134
135public:
136
137    AWT_zoom_mode zoom_mode;
138    AWT_fit_mode  fit_mode;
139
140    unsigned int dont_scroll : 1; // normally 0 (1 for IRS tree)
141
142    void init();     // like clear, but resets fit, scroll state and padding
143
144    bool inside_auto_refresh() const { return modifying>0; } // returns true if AWT_auto_refresh instance exists
145    bool inside_update() const { return modifying<0; }       // returns true during update (=destruction of initial AWT_auto_refresh instance)
146
147    bool flags_clearable() const { return inside_update(); }
148    bool flags_writeable() const {
149        // returns true if sync-flags may be modified.
150        // In that case, no explicit refresh etc. shall happen.
151        return inside_auto_refresh() || inside_update();
152    }
153    int& get_modifying_flag_ref() { return modifying; } // do not use!
154
155    // clear sync request (should happen outside of AWT_auto_refresh)
156    void clear_refresh_request()          { awt_assert(flags_clearable()); refresh    = false; }
157    void clear_resize_request()           { awt_assert(flags_clearable()); resize     = false; }
158    void clear_zoom_reset_request()       { awt_assert(flags_clearable()); zoom_reset = false; }
159    void clear_structure_update_request() { awt_assert(flags_clearable()); supdate    = false; }
160    void clear_save_request()             { awt_assert(flags_clearable()); save       = false; }
161
162    // request sync:
163    void request_refresh()             { awt_assert(flags_writeable()); refresh    = true; }
164    void request_resize()              { awt_assert(flags_writeable()); resize     = true; }
165    void request_zoom_reset()          { awt_assert(flags_writeable()); zoom_reset = true; }
166    void request_structure_update()    { awt_assert(flags_writeable()); supdate    = true; }
167    void request_save()                { awt_assert(flags_writeable()); save       = true; }
168    // common combinations:
169    void request_save_and_zoom_reset() { awt_assert(flags_writeable()); save = true; zoom_reset = true; }
170
171    // sync requested?:
172    bool needs_structure_update() const { return supdate; }
173    bool needs_save() const { return save; }
174
175    inline void update_display_as_requested(AWT_canvas *scr, bool perform_refresh); // handles zoom_reset + resize + refresh
176
177    void set_default_padding(int t, int b, int l, int r) {
178        default_padding.t = t;
179        default_padding.b = b;
180        default_padding.l = l;
181        default_padding.r = r;
182
183        padding = default_padding;
184    }
185
186    void set_equilateral_default_padding(int pad) { set_default_padding(pad, pad, pad, pad); }
187    void set_standard_default_padding() { set_equilateral_default_padding(STANDARD_PADDING); }
188
189    void set_extra_text_padding(const AW_borders& text_padding) {
190        padding.t = default_padding.t + text_padding.t;
191        padding.b = default_padding.b + text_padding.b;
192        padding.l = default_padding.l + text_padding.l;
193        padding.r = default_padding.r + text_padding.r;
194    }
195
196    int get_x_padding() const { return padding.l+padding.r; }
197    int get_y_padding() const { return padding.t+padding.b; }
198    int get_top_padding() const { return padding.t; }
199    int get_left_padding() const { return padding.l; }
200
201    AW::Vector zoomVector(double transToFit) const {
202        return AW::Vector(zoom_mode&AWT_ZOOM_X ? transToFit : 1.0,
203                          zoom_mode&AWT_ZOOM_Y ? transToFit : 1.0);
204    }
205};
206
207class AWT_graphic_event : virtual Noncopyable {
208    AWT_COMMAND_MODE M_cmd;  // currently active mode
209
210    AW_MouseButton M_button;
211    AW_key_mod     M_key_modifier;
212    AW_key_code    M_key_code;
213    char           M_key_char;
214    AW_event_type  M_type;
215
216    AW::Position mousepos;
217
218    AW_device_click *click_dev;
219
220public:
221    AWT_graphic_event(AWT_COMMAND_MODE cmd_, const AW_event& event, bool is_drag, AW_device_click *click_dev_)
222        : M_cmd(cmd_),
223          M_button(event.button),
224          M_key_modifier(event.keymodifier),
225          M_key_code(event.keycode),
226          M_key_char(event.character),
227          M_type(is_drag ? AW_Mouse_Drag : event.type),
228          mousepos(event.x, event.y),
229          click_dev(click_dev_)
230    {}
231
232    AWT_COMMAND_MODE cmd() const { return M_cmd; }
233    AW_MouseButton button() const { return M_button; }
234
235    AW_key_mod key_modifier() const { return M_key_modifier; }
236    AW_key_code key_code() const { return M_key_code; }
237    char key_char() const { return M_key_char; }
238
239    AW_event_type type() const { return M_type; }
240
241    const AW::Position& position() const { return mousepos; } // screen-coordinates
242
243    const AW_clicked_element *best_click(AW_device_click::ClickPreference prefer = AW_device_click::PREFER_NEARER) {
244        return click_dev ? click_dev->best_click(prefer) : NULp;
245    }
246};
247
248class AWT_graphic {
249    friend class AWT_canvas;
250
251    void update_DB_and_model_as_requested(GBDATA *gb_main);
252
253    bool detect_drag_target;
254
255protected:
256    int drag_gc;
257
258public:
259    AWT_graphic_exports exports;
260
261    AWT_graphic() { exports.init(); }
262    virtual ~AWT_graphic() {}
263
264    // pure virtual interface (methods implemented by AWT_nonDB_graphic)
265
266    virtual GB_ERROR load_from_DB(GBDATA *gb_main, const char *name) = 0;
267    virtual GB_ERROR save_to_DB(GBDATA *gb_main, const char *name)   = 0;
268    virtual void check_for_DB_update(GBDATA *gb_main)                = 0; // check whether anything changed in DB (and reload internal structure if needed)
269    virtual void notify_synchronized(GBDATA *gb_main)                = 0; // mark the database content and internal structure of AWT_graphic as synchronized
270
271    // pure virtual interface (rest)
272
273    virtual void show(AW_device *device) = 0;
274
275    virtual AW_gc_manager *init_devices(AW_window *, AW_device *, AWT_canvas *scr) = 0; /* init gcs, if any gc is changed AWT_GC_changed_cb() is called */
276
277    virtual void handle_command(AW_device *device, AWT_graphic_event& event) = 0;
278    virtual void update_structure()                                          = 0; // called when exports.needs_structure_update()
279
280    bool wants_drag_target() const { return detect_drag_target; }
281    void drag_target_detection(bool detect) { detect_drag_target = detect; }
282
283    int get_drag_gc() const { return drag_gc; }
284};
285
286class AWT_nonDB_graphic : public AWT_graphic { // @@@ check AWT_nonDB_graphic
287    void update_structure() OVERRIDE {}
288    // a partly implementation of AWT_graphic
289public:
290    AWT_nonDB_graphic() {}
291    ~AWT_nonDB_graphic() OVERRIDE {}
292
293    // dummy functions, only spittings out warnings:
294    GB_ERROR load_from_DB(GBDATA *gb_main, const char *name) OVERRIDE __ATTR__USERESULT;
295    GB_ERROR save_to_DB(GBDATA *gb_main, const char *name) OVERRIDE __ATTR__USERESULT;
296    void check_for_DB_update(GBDATA *gb_main) OVERRIDE;
297    void notify_synchronized(GBDATA *gb_main) OVERRIDE;
298};
299
300
301#define EPS               0.0001 // div zero check
302#define CLIP_OVERLAP      15
303#define AWT_ZOOM_OUT_STEP 40    // (pixel) rand um screen
304#define AWT_MIN_WIDTH     100   // Minimum center screen (= screen-offset)
305
306typedef void (*screen_update_callback)(AWT_canvas*, AW_CL cd);
307
308class AWT_canvas : virtual Noncopyable {
309    bool  consider_text_for_size;
310    char *gc_base_name;
311
312protected:
313    // callback called after each screen-update (set by derived class; currently only by TREE_canvas)
314    screen_update_callback announce_update_cb;
315    AW_CL                  user_data;
316
317public:
318    // @@@ make members private!
319
320    AW_pos trans_to_fit;
321    AW_pos shift_x_to_fit;
322    AW_pos shift_y_to_fit;
323
324    int old_hor_scroll_pos;
325    int old_vert_scroll_pos;
326    AW_screen_area rect;  // screen coordinates
327    AW_world worldinfo; // real coordinates without transform.
328    AW_world worldsize;
329    int zoom_drag_sx;
330    int zoom_drag_sy;
331    int zoom_drag_ex;
332    int zoom_drag_ey;
333    int drag;
334
335    void init_device(AW_device *device);
336
337    void set_scrollbars();
338    void set_dragEndpoint(int x, int y);
339
340    void set_horizontal_scrollbar_position(AW_window *aww, int pos);
341    void set_vertical_scrollbar_position(AW_window *aww, int pos);
342
343    // public (read only)
344    // @@@ make members private!
345
346    GBDATA      *gb_main;
347    AW_window   *aww;
348    AW_root     *awr;
349    AWT_graphic *gfx;
350
351    AW_gc_manager *gc_manager;
352
353    AWT_COMMAND_MODE mode;
354
355    // real public
356
357    AWT_canvas(GBDATA *gb_main_, AW_window *aww_, const char *gc_base_name_, AWT_graphic *gfx_);
358    virtual ~AWT_canvas() {}
359
360    inline void push_transaction() const;
361    inline void pop_transaction() const;
362
363#if defined(ASSERTION_USED)
364    bool inside_auto_refresh() const {
365        // returns true if AWT_auto_refresh instance exists for this canvas
366        return gfx->exports.inside_auto_refresh();
367    }
368#endif
369#define assert_no_auto_refresh_for(CANVAS) awt_assert(!(CANVAS)->inside_auto_refresh())
370
371    // request updates from underlaying AWT_graphic
372    void request_refresh()          { if (gfx) gfx->exports.request_refresh(); }
373    void request_resize()           { if (gfx) gfx->exports.request_resize(); }
374    void request_zoom_reset()       { if (gfx) gfx->exports.request_zoom_reset(); }
375    void request_structure_update() { if (gfx) gfx->exports.request_structure_update(); }
376    void request_save()             { if (gfx) gfx->exports.request_save(); }
377    // common combinations:
378    void request_save_and_zoom_reset() { if (gfx) gfx->exports.request_save_and_zoom_reset(); }
379
380    // instant refresh functions (unrecommended, should only be used internally)
381    // -> instead use AWT_auto_refresh + request_XXX-functions above!
382    void instant_refresh();
383    void instant_resize(bool adjust_scrollbars); // [Note: should normally be called with 'true']
384    void instant_zoom_reset();
385
386    // --------------------
387
388    void set_consider_text_for_zoom_reset(bool consider) { consider_text_for_size = consider; }
389
390    void zoom(AW_device *device, bool zoomIn, const AW::Rectangle& wanted_part, const AW::Rectangle& current_part, int percent);
391
392    void set_mode(AWT_COMMAND_MODE mo) { mode = mo; }
393
394    void scroll(int delta_x, int delta_y, bool dont_update_scrollbars = false);
395    void scroll(const AW::Vector& delta, bool dont_update_scrollbars = false) {
396        scroll(int(delta.x()), int(delta.y()), dont_update_scrollbars);
397    }
398
399    bool handleWheelEvent(AW_device *device, const AW_event& event);
400
401    const char *get_gc_base_name() const { return gc_base_name; }
402
403    void sync_DB_model_and_view(bool perform_refresh);
404
405    void announce_screen_update() { if (announce_update_cb) announce_update_cb(this, user_data); }
406
407    bool is_shown() const { return aww->is_shown(); }
408};
409
410class AWT_auto_refresh {
411    // While instance exists -> sync flags of AWT_graphic_exports may be modified
412    // Creating additional instances just incs/decs a counter.
413    // When initial instance gets destroyed
414    // => AWT_canvas::sync_DB_model_and_view() handles all requests (save, update, resize, refresh)
415
416    RefPtr<AWT_canvas> scr;
417
418    // @@@ delay non-instant refresh into idle callback?
419    // bool instant_refresh; // true -> do instant refresh (only has effect on first instance!) @@@ unused atm
420
421public:
422    AWT_auto_refresh(AWT_canvas *scr_) :
423        scr(scr_)
424    {
425        AWT_graphic_exports& exports = scr->gfx->exports;
426        awt_assert(exports.modifying >= 0); // otherwise you try to instanciate from inside sync_DB_model_and_view()
427        if (exports.modifying++ == 0) {
428            // test for already set export-flags here? might indicate wrong logic
429            scr->gfx->check_for_DB_update(scr->gb_main);
430        }
431    }
432    ~AWT_auto_refresh() {
433        AWT_graphic_exports& exports = scr->gfx->exports;
434        if (--exports.modifying <= 0) {
435            awt_assert(exports.modifying == 0);
436            scr->sync_DB_model_and_view(true);
437        }
438    }
439
440    void suppress_update_and_refresh() {
441        // use at end of scope of initial AWT_auto_refresh to suppress any updates of model + view
442        // Note: use carefully, may cause model inconsistencies!!!
443
444        AWT_graphic_exports& exports = scr->gfx->exports;
445        LocallyModify<int>   permit_suppression(exports.get_modifying_flag_ref(), -1);
446
447        exports.clear_structure_update_request();
448        exports.clear_zoom_reset_request();
449        exports.clear_resize_request();
450        exports.clear_refresh_request();
451    }
452};
453
454inline void AWT_graphic_exports::update_display_as_requested(AWT_canvas *scr, bool perform_refresh) {
455    assert_no_auto_refresh_for(scr);
456
457    if (zoom_reset) {
458        scr->instant_zoom_reset(); // also does resize
459        awt_assert(!zoom_reset && !resize && refresh);
460    }
461    else if (resize) {
462        scr->instant_resize(true);
463        awt_assert(!resize && refresh);
464    }
465
466    if (refresh && perform_refresh) {
467        scr->instant_refresh();
468        awt_assert(!refresh);
469    }
470}
471
472void AWT_expose_cb(UNFIXED, AWT_canvas *scr);
473void AWT_resize_cb(UNFIXED, AWT_canvas *scr);
474void AWT_GC_changed_cb(GcChange whatChanged, AWT_canvas *scr);
475
476void AWT_popup_tree_export_window(AW_window *parent_win, AWT_canvas *scr);
477void AWT_popup_sec_export_window (AW_window *parent_win, AWT_canvas *scr);
478void AWT_popup_print_window      (AW_window *parent_win, AWT_canvas *scr);
479
480#else
481#error canvas.hxx included twice
482#endif // CANVAS_HXX
Note: See TracBrowser for help on using the repository browser.