source: branches/help/NTREE/ScrollSynchronizer.h

Last change on this file was 16763, checked in by westram, 7 years ago
File size: 12.7 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : ScrollSynchronizer.h                              //
4//   Purpose   : synchronize TREE_canvas scrolling                 //
5//                                                                 //
6//   Coded by Ralf Westram (coder@reallysoft.de) in October 2016   //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#ifndef SCROLLSYNCHRONIZER_H
12#define SCROLLSYNCHRONIZER_H
13
14#ifndef NT_LOCAL_H
15#include "NT_local.h"
16#endif
17#ifndef SMARTPTR_H
18#include <smartptr.h>
19#endif
20#ifndef _GLIBCXX_SET
21#include <set>
22#endif
23
24#define NO_SCROLL_SYNC (-1)
25
26#if defined(DEBUG)
27// # define DUMP_SYNC
28// # define DUMP_SYNC_AUTO // auto-update and source-canvas-change
29#endif
30
31
32CONSTEXPR_INLINE bool valid_canvas_index(int idx) { return idx>=0 && idx<MAX_NT_WINDOWS; }
33
34class timestamp {
35    unsigned t;
36public:
37    explicit timestamp(unsigned i) : t(i) {}
38    operator unsigned() const { return t; }
39
40    static timestamp before(const timestamp& t) { return timestamp(t-1); }
41    static timestamp after(const timestamp& t) { return timestamp(t+1); }
42
43    void setNewerThan(const timestamp& other) { *this = after(other); }
44    void setOlderThan(const timestamp& other) { *this = before(other); }
45
46    bool newer_than(const timestamp& other) const { return t>other; }
47    bool older_than(const timestamp& other) const { return t<other; }
48
49};
50
51typedef std::set< RefPtr<GBDATA> > SpeciesSet;
52
53typedef SmartPtr<SpeciesSet> SpeciesSetPtr;
54
55inline bool operator == (const SpeciesSetPtr& spec1, const SpeciesSetPtr& spec2) {
56    if (spec1.isNull()) return spec2.isNull() ? true : false;
57    if (spec2.isNull()) return false;
58    if (spec1.sameObject(spec2)) return true;
59    return *spec1 == *spec2;
60}
61inline bool operator != (const SpeciesSetPtr& spec1, const SpeciesSetPtr& spec2) {
62    return !(spec1 == spec2);
63}
64
65class CanvasRef {
66    int                  index;
67    mutable TREE_canvas *canvas;
68public:
69    CanvasRef() :
70        index(-1),
71        canvas(NULp)
72    {}
73
74    void define_canvas_index(int i) {
75        nt_assert(index == -1);
76        nt_assert(valid_canvas_index(i));
77
78        index = i;
79    }
80
81    int get_index() const {
82        nt_assert(valid_canvas_index(index));
83        return index;
84    }
85    TREE_canvas *get_canvas() const {
86        if (!canvas) {
87            canvas = NT_get_canvas_by_index(get_index());
88            nt_assert(canvas);
89        }
90        return canvas;
91    }
92};
93
94class MasterCanvas : public CanvasRef {
95    timestamp last_Refresh;      // passive (last refresh of canvas)
96    timestamp last_DisplayTrack; // last tracking of displayed species (=last update of species-set; probably w/o change)
97    timestamp last_SetChange;    // last CHANGE of species-set
98
99    SpeciesSetPtr species;
100
101    SpeciesSetPtr track_displayed_species();
102
103    void update_SpeciesSet() {
104        if (last_Refresh.newer_than(last_SetChange)) {
105
106            if (last_Refresh.newer_than(last_DisplayTrack)) {
107                SpeciesSetPtr current_species = track_displayed_species();
108                last_DisplayTrack             = last_Refresh;
109#if defined(DUMP_SYNC)
110                fprintf(stderr, "DEBUG: MasterCanvas tracking species (idx=%i, last_DisplayTrack=%u, species count=%zu)\n", get_index(), unsigned(last_DisplayTrack), current_species->size());
111#endif
112
113                if (species != current_species) { // set of species changed?
114                    species        = current_species;
115                    last_SetChange = last_DisplayTrack;
116#if defined(DUMP_SYNC)
117                    fprintf(stderr, "       MasterCanvas::SpeciesSet changed/updated (last_SetChange=%u)\n", unsigned(last_SetChange));
118#endif
119                }
120#if defined(DUMP_SYNC)
121                else {
122                    fputs("       MasterCanvas::SpeciesSet did not change\n", stderr);
123                }
124#endif
125            }
126        }
127    }
128
129public:
130    MasterCanvas() :
131        last_Refresh(8),
132        last_DisplayTrack(7),
133        last_SetChange(6)
134    {}
135
136    void announce_update() { last_Refresh.setNewerThan(last_DisplayTrack); }
137
138    const timestamp& get_updated_SpeciesSet(SpeciesSetPtr& specset) {
139        update_SpeciesSet();
140        specset = species;
141        return last_SetChange;
142    }
143};
144
145class SlaveCanvas_internal;
146
147class SlaveCanvas : public CanvasRef, virtual Noncopyable {
148
149    RefPtr<MasterCanvas> last_master;
150    timestamp            last_master_change;
151
152    bool need_SetUpdate;
153    bool need_PositionTrack;
154    bool need_ScrollZoom;
155    bool need_Refresh;
156
157    SpeciesSetPtr         species;
158    SlaveCanvas_internal *internal;
159
160    void track_display_positions();
161    void calc_scroll_zoom();
162
163    void update_set_from_master(MasterCanvas& master) {
164        bool update_only_if_master_changed = false;
165
166        if (!need_SetUpdate) {
167            if (!last_master) need_SetUpdate                = true;
168            else if (last_master != &master) need_SetUpdate = true;
169            else update_only_if_master_changed = true;
170        }
171
172        if (need_SetUpdate || update_only_if_master_changed) {
173            SpeciesSetPtr master_spec;
174            timestamp     master_stamp = master.get_updated_SpeciesSet(master_spec);
175
176            if (update_only_if_master_changed && !need_SetUpdate) {
177                need_SetUpdate = master_stamp.newer_than(last_master_change);
178            }
179            if (need_SetUpdate) {
180                last_master        = &master;
181                last_master_change = master_stamp;
182
183                if (master_spec != species) {
184                    species            = master_spec;
185                    need_PositionTrack = true;
186
187#if defined(DUMP_SYNC)
188                    fprintf(stderr, "DEBUG: updating SlaveCanvas::SpeciesSet (idx=%i, species count=%zu)\n", get_index(), species->size());
189#endif
190                }
191                need_SetUpdate = false;
192            }
193        }
194    }
195
196    void update_tracked_positions() {
197        nt_assert(!need_SetUpdate);
198        if (need_PositionTrack) {
199#if defined(DUMP_SYNC)
200            fprintf(stderr, "DEBUG: SlaveCanvas tracks positions (idx=%i)\n", get_index());
201#endif
202            track_display_positions();
203            need_ScrollZoom    = true;
204            need_PositionTrack = false;
205        }
206    }
207    void update_scroll_zoom() {
208        nt_assert(!need_PositionTrack);
209        if (need_ScrollZoom) {
210#if defined(DUMP_SYNC)
211            fprintf(stderr, "DEBUG: SlaveCanvas updates scroll/zoom (idx=%i)\n", get_index());
212#endif
213            calc_scroll_zoom();
214            need_Refresh    = true;
215            need_ScrollZoom = false;
216        }
217    }
218
219    void refresh_scroll_zoom();
220
221public:
222    SlaveCanvas();
223    ~SlaveCanvas();
224
225    void request_Refresh() {
226        need_Refresh = true;
227    }
228    void request_SetUpdate() {
229        need_SetUpdate = true;
230    }
231
232    void refresh_if_needed(MasterCanvas& master) {
233        update_set_from_master(master);
234        update_tracked_positions();
235        update_scroll_zoom();
236
237        nt_assert(!need_ScrollZoom);
238        if (need_Refresh) {
239            refresh_scroll_zoom();
240            need_Refresh = false;
241        }
242    }
243};
244
245
246
247class ScrollSynchronizer {
248    MasterCanvas source[MAX_NT_WINDOWS];
249    SlaveCanvas  dest[MAX_NT_WINDOWS];
250
251    int  master_index[MAX_NT_WINDOWS];       // index = slave-canvas
252    bool autosynced[MAX_NT_WINDOWS];         // true if canvas is autosynced
253
254    bool autosynced_with(int canvas, int with) const {
255        nt_assert(valid_canvas_index(canvas));
256        nt_assert(valid_canvas_index(with));
257
258        if (autosynced[canvas]) {
259            int master = master_index[canvas];
260            if (master == with) return true;
261            if (valid_canvas_index(master)) return autosynced_with(master, with);
262        }
263        return false;
264    }
265
266    int autosync_master(int slave) const {
267        return autosynced[slave] ? master_index[slave] : NO_SCROLL_SYNC;
268    }
269
270public:
271    ScrollSynchronizer() {
272        for (int i = 0; i<MAX_NT_WINDOWS; ++i) {
273            source[i].define_canvas_index(i);
274            dest[i].define_canvas_index(i);
275        }
276    }
277
278    GB_ERROR define_dependency(int slave_idx, int master_idx, bool auto_sync) {
279        /*! defines master/slave dependency between TREE_canvas'es
280         * @param slave_idx index of slave canvas [0..MAX_NT_WINDOWS-1]
281         * @param master_idx index of master canvas (or NO_SCROLL_SYNC)
282         * @param auto_sync true -> automatically synchronize scrolling
283         * @return error (only if auto_sync is impossible because of sync-loop => auto_sync is ignored!)
284         */
285
286        nt_assert(valid_canvas_index(slave_idx));
287        nt_assert(valid_canvas_index(master_idx) || master_idx == NO_SCROLL_SYNC);
288
289        master_index[slave_idx] = master_idx;
290        autosynced[slave_idx]   = false;
291
292        GB_ERROR error = NULp;
293        if (auto_sync && master_idx != NO_SCROLL_SYNC) {
294            if (autosynced_with(master_idx, slave_idx)) {
295                error = "dependency loop detected";
296            }
297            else {
298                autosynced[slave_idx] = true;
299            }
300        }
301        return error;
302    }
303
304    void announce_update(int canvas_idx) {
305        nt_assert(valid_canvas_index(canvas_idx));
306        source[canvas_idx].announce_update();
307#if defined(DUMP_SYNC_AUTO)
308        fprintf(stderr, "DEBUG: announce_update(canvas_idx=%i)\n", canvas_idx);
309#endif
310    }
311
312    // @@@ add announce_resize (master AND slave!)
313    // @@@ add announce_tree_modified (master AND slave!); shall also handle change to other tree
314    // @@@ add announce_tree_type_changed (master AND slave!)
315
316    GB_ERROR update_explicit(int slave_idx) {
317        /*! perform an unconditional update
318         * (ie. refresh of slave is forced even if internal data was up-to-date)
319         * @param slave_idx index of canvas to update
320         * @return error (eg. if no master defined)
321         */
322
323        int master_idx = master_index[slave_idx];
324
325        GB_ERROR error = NULp;
326        if (valid_canvas_index(master_idx)) {
327#if defined(DUMP_SYNC)
328            fputs("------------------------------\n", stderr);
329            fprintf(stderr, "DEBUG: update_explicit(slave_idx=%i) from master_idx=%i\n", slave_idx, master_idx);
330#endif
331            MasterCanvas& master = source[master_idx];
332            SlaveCanvas&  slave  = dest[slave_idx];
333
334            slave.request_Refresh();
335            slave.refresh_if_needed(master);
336        }
337        else {
338            error = "No master-window defined";
339        }
340        return error;
341    }
342
343    void update_implicit(int slave_idx) {
344        /*! perform an implicit update
345         *
346         * @param slave_idx index of canvas to update
347         * @return error (eg. if no master defined)
348         */
349
350        int master_idx = master_index[slave_idx];
351        if (valid_canvas_index(master_idx)) {
352#if defined(DUMP_SYNC)
353            fputs("------------------------------\n", stderr);
354            fprintf(stderr, "DEBUG: update_implicit(slave_idx=%i) from master_idx=%i\n", slave_idx, master_idx);
355#endif
356            MasterCanvas& master = source[master_idx];
357            SlaveCanvas&  slave  = dest[slave_idx];
358
359            slave.request_SetUpdate();
360            slave.refresh_if_needed(master);
361        }
362    }
363
364    void auto_update() {
365        /*! update all auto-synchronized canvases */
366
367#if defined(DUMP_SYNC_AUTO)
368        fputs("------------------------------\n"
369              "DEBUG: auto_update\n", stderr);
370#endif
371
372        bool check_update[MAX_NT_WINDOWS];
373        for (int i = 0; i<MAX_NT_WINDOWS; ++i) {
374            check_update[i] = autosynced[i];
375        }
376
377        bool need_check = true;
378        while (need_check) {
379            need_check = false;
380
381            for (int slave_idx = 0; slave_idx<MAX_NT_WINDOWS; ++slave_idx) {
382                if (check_update[slave_idx]) {
383                    int master_idx = autosync_master(slave_idx);
384                    if (valid_canvas_index(master_idx)) {
385                        if (check_update[master_idx]) {
386                            // delay (first update master)
387                            need_check = true; // need another loop
388                        }
389                        else {
390#if defined(DUMP_SYNC_AUTO)
391                            fprintf(stderr, "DEBUG: auto_update(slave_idx=%i) from master_idx=%i\n", slave_idx, master_idx);
392#endif
393                            MasterCanvas& master = source[master_idx];
394                            SlaveCanvas&  slave  = dest[slave_idx];
395
396                            slave.refresh_if_needed(master);
397
398                            check_update[slave_idx] = false;
399                        }
400                    }
401                }
402            }
403        }
404    }
405};
406
407
408#else
409#error ScrollSynchronizer.h included twice
410#endif // SCROLLSYNCHRONIZER_H
Note: See TracBrowser for help on using the repository browser.