ARB
ScrollSynchronizer.cxx
Go to the documentation of this file.
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 
22 using namespace AW;
23 using namespace std;
24 
25 inline 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 
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 
57 public:
59  AW_simple_device(common_),
60  species(species_)
61  {}
62 
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 
88 SpeciesSetPtr 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 
119 typedef map< RefPtr<GBDATA>, Rectangle> SpeciesPositions; // world-coordinates
120 
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 
141 public:
143  AW_simple_device(common_)
144  {}
145 
146  void set_species(SpeciesSetPtr species_) { species = species_; }
147  void forget_positions() { spos.clear(); }
148 
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 
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 
201 typedef set<Rectangle, cmp_Rectangles> SortedPositions; // world-coordinates
202 
204  SortedPositions pos;
205  Vector viewport_size;
206  Rectangle best_area; // wanted display area (world coordinates)
207 
208 public:
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 
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 
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 
275 void 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 
300 void 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 
309 void 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 
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 
347  delete internal;
348 }
349 
350 
AW::Vector transform(const AW::Vector &vec) const
Definition: aw_device.hxx:144
long AW_bitset
Definition: aw_base.hxx:44
AW_trackSpecies_device(AW_common *common_, SpeciesSet &species_)
bool operator()(const Rectangle &r1, const Rectangle &r2) const
AW_device * get_device(AW_area area)
Definition: AW_window.cxx:537
Vector calc_best_scroll_delta(const Rectangle &viewport)
bool text_impl(int, const SizedCstr &, const AW::Position &pos, AW_pos, AW_bitset filteri) OVERRIDE
double width() const
AW_DEVICE_TYPE type() OVERRIDE
void specific_reset() OVERRIDE
Rectangle bounding_box(const Rectangle &r1, const Rectangle &r2)
ClickedType
double right() const
STL namespace.
void set_species(SpeciesSetPtr species_)
double top() const
AW_trackPositions_device(AW_common *common_)
GBDATA * trackable_species(const AW_click_cd *clickable)
AW_screen_area rect
Definition: canvas.hxx:326
bool line_impl(int, const LineVector &Line, AW_bitset filteri) OVERRIDE
AW_common * get_common(AW_area area)
Definition: AW_window.cxx:564
bool invisible_impl(const Position &, AW_bitset) OVERRIDE
const AW_bitset AW_TRACK
Definition: aw_device.hxx:42
const Vector ZeroVector
double AW_pos
Definition: aw_base.hxx:29
const SpeciesPositions & get_tracked_positions() const
Generic smart pointer.
Definition: smartptr.h:149
#define true
Definition: ureadseq.h:14
AW_DEVICE_TYPE type() OVERRIDE
double height() const
AW_CL get_cd1() const
Definition: aw_device.hxx:347
AW::Vector rtransform(const AW::Vector &vec) const
Definition: aw_device.hxx:145
AW_CL get_cd2() const
Definition: aw_device.hxx:348
bool line_impl(int, const LineVector &Line, AW_bitset filteri) OVERRIDE
const Position & upper_left_corner() const
void store_positions_sorted(const SpeciesPositions &spos)
#define cmp(h1, h2)
Definition: admap.cxx:50
const Vector & diagonal() const
Rectangle bounding_box(const Rectangle &rect) const
bool text_impl(int, const SizedCstr &, const AW::Position &pos, AW_pos, AW_bitset filteri) OVERRIDE
set< Rectangle, cmp_Rectangles > SortedPositions
#define nt_assert(cond)
Definition: NT_local.h:27
std::set< RefPtr< GBDATA > > SpeciesSet
double bottom() const
double left() const
AWT_graphic * gfx
Definition: canvas.hxx:349
bool is_leaf() const
Definition: TreeNode.h:211
map< RefPtr< GBDATA >, Rectangle > SpeciesPositions
void scroll(int delta_x, int delta_y, bool dont_update_scrollbars=false)
Definition: canvas.cxx:702
bool invisible_impl(const Position &, AW_bitset) OVERRIDE
#define OVERRIDE
Definition: cxxforward.h:112
bool overlaps_with(const Rectangle &rect) const
void init_device(AW_device *device)
Definition: canvas.cxx:105
#define NULp
Definition: cxxforward.h:116
AW_DEVICE_TYPE
Definition: aw_device.hxx:48
void announce_viewport_size(const Vector &viewport_size_)
GB_transaction ta(gb_var)
GBDATA * gb_node
Definition: TreeNode.h:173
AW_window * aww
Definition: canvas.hxx:347
GB_CSTR GBT_get_name_or_description(GBDATA *gb_item)
Definition: aditem.cxx:459
GBDATA * gb_main
Definition: canvas.hxx:346
virtual void show(AW_device *device)=0
GB_write_int const char s
Definition: AW_awar.cxx:154