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