source: branches/species/NTREE/ScrollSynchronizer.cxx

Last change on this file was 19613, checked in by westram, 2 months ago
  • reintegrates 'lib' into 'trunk'
    • replace dynamic library AWT by several static libraries: APP, ARB_SPEC, MASKS, CANVAS, MAPKEY, GUI_TK
    • now also check wrong library dependencies for untested units (only4me)
  • adds: log:branches/lib@19578:19612
File size: 10.7 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : ScrollSynchronizer.cxx                            //
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#include "ScrollSynchronizer.h"
12
13#include <TreeDisplay.hxx>
14#include <map>
15
16#if defined(DUMP_SYNC)
17# define DUMP_ADD
18# define DUMP_SCROLL_DETECT
19#endif
20
21
22using namespace AW;
23using namespace std;
24
25inline GBDATA *trackable_species(const AW_click_cd *clickable) {
26    if (!clickable) return NULp;
27
28    ClickedType clicked = (ClickedType)clickable->get_cd2();
29    if (clicked == CL_SPECIES) return (GBDATA*)clickable->get_cd1(); // NDS list
30
31    nt_assert(clicked == CL_NODE || clicked == CL_BRANCH); // unexpected clickable tracked!
32    TreeNode *node = (TreeNode*)clickable->get_cd1();
33    return (node && node->is_leaf()) ? node->gb_node :  NULp;
34}
35
36class AW_trackSpecies_device: public AW_simple_device {
37    SpeciesSet& species;
38
39    void track() {
40        GBDATA *gb_species = trackable_species(get_click_cd());
41
42        if (gb_species) {
43#if defined(DUMP_ADD)
44            bool do_insert = species.find(gb_species) == species.end();
45#else // NDEBUG
46            bool do_insert = true;
47#endif
48            if (do_insert) {
49#if defined(DUMP_ADD)
50                fprintf(stderr, " - adding species #%zu '%s'\n", species.size(), GBT_get_name_or_description(gb_species));
51#endif
52                species.insert(gb_species);
53            }
54        }
55    }
56
57public:
58    AW_trackSpecies_device(AW_common *common_, SpeciesSet& species_) :
59        AW_simple_device(common_),
60        species(species_)
61    {}
62
63    AW_DEVICE_TYPE type() OVERRIDE { return AW_DEVICE_CLICK; }
64    void specific_reset() OVERRIDE {}
65    bool invisible_impl(const Position&, AW_bitset) OVERRIDE { return false; }
66
67    bool line_impl(int, const LineVector& Line, AW_bitset filteri) OVERRIDE {
68        bool drawflag = false;
69        if (filteri & filter) {
70            LineVector transLine = transform(Line);
71            LineVector clippedLine;
72            drawflag = clip(transLine, clippedLine);
73            if (drawflag) track();
74        }
75        return drawflag;
76    }
77    bool text_impl(int, const SizedCstr&, const AW::Position& pos, AW_pos, AW_bitset filteri) OVERRIDE {
78        bool drawflag = false;
79        if (filteri & filter) {
80            if (!is_outside_clip(transform(pos))) track();
81        }
82        return drawflag;
83    }
84};
85
86
87
88SpeciesSetPtr MasterCanvas::track_displayed_species() {
89    // clip_expose
90
91    TREE_canvas *ntw    = get_canvas();
92    AW_window   *aww    = ntw->aww;
93    AW_common   *common = aww->get_common(AW_MIDDLE_AREA);
94
95    SpeciesSetPtr tracked = new SpeciesSet;
96
97    AW_trackSpecies_device device(common, *tracked);
98
99    device.set_filter(AW_TRACK);
100    device.reset();
101
102    const AW_screen_area& rect = ntw->rect;
103
104    device.set_top_clip_border(rect.t);
105    device.set_bottom_clip_border(rect.b);
106    device.set_left_clip_border(rect.l);
107    device.set_right_clip_border(rect.r);
108
109    {
110        GB_transaction ta(ntw->gb_main);
111
112        ntw->init_device(&device);
113        ntw->gfx->show(&device);
114    }
115
116    return tracked;
117}
118
119typedef map< RefPtr<GBDATA>, Rectangle> SpeciesPositions; // world-coordinates
120
121class AW_trackPositions_device: public AW_simple_device {
122    SpeciesSetPtr    species; // @@@ elim (instead "mark" contained species to improve speed?)
123    SpeciesPositions spos;
124
125    void trackPosition(GBDATA *gb_species, const Rectangle& spec_area) {
126        SpeciesPositions::iterator tracked = spos.find(gb_species);
127        if (tracked == spos.end()) { // first track
128            spos[gb_species] = spec_area;
129        }
130        else {
131            tracked->second = bounding_box(tracked->second, spec_area); // combine areas
132        }
133    }
134    void trackPosition(GBDATA *gb_species, const LineVector& spec_vec) {
135        trackPosition(gb_species, Rectangle(spec_vec));
136    }
137    void trackPosition(GBDATA *gb_species, const Position& spec_pos) {
138        trackPosition(gb_species, Rectangle(spec_pos, ZeroVector));
139    }
140
141public:
142    AW_trackPositions_device(AW_common *common_) :
143        AW_simple_device(common_)
144    {}
145
146    void set_species(SpeciesSetPtr species_) { species = species_; }
147    void forget_positions() { spos.clear(); }
148
149    AW_DEVICE_TYPE type() OVERRIDE { return AW_DEVICE_SIZE; }
150    void specific_reset() OVERRIDE {}
151    bool invisible_impl(const Position&, AW_bitset) OVERRIDE { return false; }
152
153    bool line_impl(int, const LineVector& Line, AW_bitset filteri) OVERRIDE {
154        bool drawflag = false;
155        if (filteri & filter) {
156            GBDATA *gb_species = trackable_species(get_click_cd());
157            if (gb_species) {
158                if (species->find(gb_species) != species->end()) {
159                    trackPosition(gb_species, Line);
160                }
161            }
162            drawflag = true;
163        }
164        return drawflag;
165    }
166    bool text_impl(int, const SizedCstr&, const AW::Position& pos, AW_pos, AW_bitset filteri) OVERRIDE {
167        bool drawflag = false;
168        if (filteri & filter) {
169            GBDATA *gb_species = trackable_species(get_click_cd());
170            if (gb_species) {
171                if (species->find(gb_species) != species->end()) {
172                    trackPosition(gb_species, pos);
173                }
174            }
175            drawflag = true;
176        }
177        return drawflag;
178    }
179
180    const SpeciesPositions& get_tracked_positions() const {
181        return spos;
182    }
183};
184
185struct cmp_Rectangles {
186    bool operator()(const Rectangle &r1, const Rectangle &r2) const {
187    double cmp = r1.top()-r2.top(); // upper first
188    if (!cmp) {
189        cmp = r1.bottom()-r2.bottom(); // smaller first
190        if (!cmp) {
191            cmp = r1.left()-r2.left(); // leftmost first
192            if (!cmp) {
193                cmp = r1.right()-r2.right(); // smaller first
194            }
195        }
196    }
197    return cmp<0.0;
198  }
199};
200
201typedef set<Rectangle, cmp_Rectangles> SortedPositions; // world-coordinates
202
203class SlaveCanvas_internal {
204    SortedPositions pos;
205    Vector          viewport_size;
206    Rectangle       best_area; // wanted display area (world coordinates)
207
208public:
209
210    void store_positions_sorted(const SpeciesPositions& spos) {
211        pos.clear();
212        for (SpeciesPositions::const_iterator s = spos.begin(); s != spos.end(); ++s) {
213            pos.insert(s->second);
214        }
215    }
216
217    void announce_viewport_size(const Vector& viewport_size_) {
218        viewport_size = viewport_size_;
219        best_area     = Rectangle();
220    }
221    void calc_best_area();
222    Vector calc_best_scroll_delta(const Rectangle& viewport);
223};
224
225void SlaveCanvas_internal::calc_best_area() {
226    int       best_count  = -1;
227    const int max_species = int(pos.size());
228    int       rest        = max_species;
229
230    SortedPositions::const_iterator end = pos.end();
231    for (SortedPositions::const_iterator s1 = pos.begin(); rest>best_count && s1 != end; ++s1) {
232        const Rectangle& r1 = *s1;
233
234        Rectangle testedViewport(r1.upper_left_corner(), viewport_size);
235        int       count = 1;
236
237        Rectangle contained_area = r1; // bounding box of all species displayable inside testedViewport
238
239        SortedPositions::const_iterator s2 = s1;
240        ++s2;
241        for (; s2 != end; ++s2) {
242            const Rectangle& r2 = *s2;
243            if (r2.overlaps_with(testedViewport)) {
244                ++count;
245                contained_area = contained_area.bounding_box(r2);
246            }
247        }
248
249        nt_assert(count>0);
250
251        if (count>best_count) {
252            best_count = count;
253            best_area  = contained_area;
254
255#if defined(DUMP_SCROLL_DETECT)
256            fprintf(stderr, "Found %i species fitting into area ", count);
257            AW_DUMP(contained_area);
258#endif
259        }
260
261        rest--;
262    }
263}
264
265Vector SlaveCanvas_internal::calc_best_scroll_delta(const Rectangle& viewport) {
266    // in and out are world-coordinates!
267    if (best_area.valid()) {
268        Vector    shift(viewport.width()*-0.1, (best_area.height()-viewport.height())/2);
269        Rectangle wanted_viewport = Rectangle(best_area.upper_left_corner() + shift, viewport.diagonal());
270        return wanted_viewport.upper_left_corner() - viewport.upper_left_corner();
271    }
272    return ZeroVector;
273}
274
275void SlaveCanvas::track_display_positions() {
276    TREE_canvas *ntw    = get_canvas();
277    AW_common   *common = ntw->aww->get_common(AW_MIDDLE_AREA);
278
279    // @@@ use different algo (device) for radial and for other treeviews
280    // below code fits non-radial slave-views:
281
282    AW_trackPositions_device device(common);
283
284    device.set_species(species); // @@@ move to device-ctor?
285    device.forget_positions(); // @@@ not necessary if device is recreated for each tracking
286
287    device.set_filter(AW_TRACK);
288    device.reset(); // @@@ really needed?
289
290    {
291        GB_transaction ta(ntw->gb_main);
292
293        ntw->init_device(&device);
294        ntw->gfx->show(&device);
295    }
296
297    internal->store_positions_sorted(device.get_tracked_positions());
298}
299
300void SlaveCanvas::calc_scroll_zoom() {
301    TREE_canvas *ntw      = get_canvas();
302    AW_device   *device   = ntw->aww->get_device(AW_MIDDLE_AREA);
303    Rectangle    viewport = device->rtransform(Rectangle(ntw->rect, INCLUSIVE_OUTLINE));
304
305    internal->announce_viewport_size(viewport.diagonal());
306    internal->calc_best_area();
307}
308
309void SlaveCanvas::refresh_scroll_zoom() {
310#if defined(DUMP_SYNC)
311    fprintf(stderr, "DEBUG: SlaveCanvas does refresh_scroll_zoom (idx=%i)\n", get_index());
312#endif
313
314    TREE_canvas *ntw      = get_canvas();
315    AW_device   *device   = ntw->aww->get_device(AW_MIDDLE_AREA);
316    Rectangle    viewport = device->rtransform(Rectangle(ntw->rect, INCLUSIVE_OUTLINE));
317    Vector       world_scroll(internal->calc_best_scroll_delta(viewport));
318
319#if defined(DUMP_SCROLL_DETECT)
320    AW_DUMP(viewport);
321    AW_DUMP(world_scroll);
322#endif
323
324    if (world_scroll.has_length()) { // skip scroll if (nearly) nothing happens
325        Vector screen_scroll = device->transform(world_scroll);
326#if defined(DUMP_SCROLL_DETECT)
327        AW_DUMP(screen_scroll);
328#endif
329        ntw->scroll(screen_scroll); // @@@ scroll the canvas (should be done by caller, to avoid recalculation on slave-canvas-resize)
330    }
331
332    // get_canvas()->refresh();
333}
334
335SlaveCanvas::SlaveCanvas() :
336    last_master(NULp),
337    last_master_change(0),
338    need_SetUpdate(true),
339    need_PositionTrack(true),
340    need_ScrollZoom(true),
341    need_Refresh(true)
342{
343    internal = new SlaveCanvas_internal;
344}
345
346SlaveCanvas::~SlaveCanvas() {
347    delete internal;
348}
349
350
Note: See TracBrowser for help on using the repository browser.