ARB
ScrollSynchronizer.h
Go to the documentation of this file.
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 
32 CONSTEXPR_INLINE bool valid_canvas_index(int idx) { return idx>=0 && idx<MAX_NT_WINDOWS; }
33 
34 class timestamp {
35  unsigned t;
36 public:
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 
51 typedef std::set< RefPtr<GBDATA> > SpeciesSet;
52 
54 
55 inline 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 }
61 inline bool operator != (const SpeciesSetPtr& spec1, const SpeciesSetPtr& spec2) {
62  return !(spec1 == spec2);
63 }
64 
65 class CanvasRef {
66  int index;
67  mutable TREE_canvas *canvas;
68 public:
70  index(-1),
71  canvas(NULp)
72  {}
73 
74  void define_canvas_index(int i) {
75  nt_assert(index == -1);
77 
78  index = i;
79  }
80 
81  int get_index() const {
83  return index;
84  }
86  if (!canvas) {
88  nt_assert(canvas);
89  }
90  return canvas;
91  }
92 };
93 
94 class 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 
129 public:
131  last_Refresh(8),
132  last_DisplayTrack(7),
133  last_SetChange(6)
134  {}
135 
136  void announce_update() { last_Refresh.setNewerThan(last_DisplayTrack); }
137 
139  update_SpeciesSet();
140  specset = species;
141  return last_SetChange;
142  }
143 };
144 
146 
147 class 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 
221 public:
222  SlaveCanvas();
223  ~SlaveCanvas();
224 
226  need_Refresh = true;
227  }
229  need_SetUpdate = true;
230  }
231 
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 
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));
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 
270 public:
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) {
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) {
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) {
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() {
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
CONSTEXPR_INLINE bool valid_canvas_index(int idx)
void request_SetUpdate()
bool older_than(const timestamp &other) const
bool newer_than(const timestamp &other) const
void announce_update(int canvas_idx)
bool isNull() const
test if SmartPtr is NULp
Definition: smartptr.h:248
TREE_canvas * NT_get_canvas_by_index(int idx)
Definition: NT_extern.cxx:942
#define NO_SCROLL_SYNC
TREE_canvas * get_canvas() const
int get_index() const
SmartPtr< SpeciesSet > SpeciesSetPtr
#define MAX_NT_WINDOWS
Definition: NT_local.h:31
void define_canvas_index(int i)
static void error(const char *msg)
Definition: mkptypes.cxx:96
timestamp(unsigned i)
static timestamp before(const timestamp &t)
#define CONSTEXPR_INLINE
Definition: cxxforward.h:92
static timestamp after(const timestamp &t)
GB_ERROR update_explicit(int slave_idx)
#define nt_assert(cond)
Definition: NT_local.h:27
std::set< RefPtr< GBDATA > > SpeciesSet
void update_implicit(int slave_idx)
void setOlderThan(const timestamp &other)
fputs(TRACE_PREFIX, stderr)
GB_ERROR define_dependency(int slave_idx, int master_idx, bool auto_sync)
const timestamp & get_updated_SpeciesSet(SpeciesSetPtr &specset)
void setNewerThan(const timestamp &other)
#define NULp
Definition: cxxforward.h:97
bool sameObject(const SmartPtr< T, C > &other) const
Definition: smartptr.h:277
bool operator!=(const SpeciesSetPtr &spec1, const SpeciesSetPtr &spec2)
void refresh_if_needed(MasterCanvas &master)
bool operator==(const SpeciesSetPtr &spec1, const SpeciesSetPtr &spec2)