Lumiera  0.pre.03
»edit your freedom«
ui-coord.hpp
Go to the documentation of this file.
1 /*
2  UI-COORD.hpp - generic topological location addressing scheme within the UI
3 
4  Copyright (C) Lumiera.org
5  2017, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 
82 #ifndef STAGE_INTERACT_UI_COORD_H
83 #define STAGE_INTERACT_UI_COORD_H
84 
85 #include "lib/error.hpp"
86 #include "lib/symbol.hpp"
87 #include "lib/path-array.hpp"
88 #include "lib/nocopy.hpp"
89 #include "lib/util.hpp"
90 
91 #include <cstring>
92 #include <string>
93 #include <vector>
94 #include <utility>
95 
96 
97 namespace stage {
98 namespace interact {
99 
100  namespace error = lumiera::error;
101 
102  using std::string;
103  using lib::Literal;
104  using lib::Symbol;
105  using util::unConst;
106  using util::isnil;
107  using util::min;
108 
109  enum {
110  UIC_INLINE_SIZE = 8
111  };
112 
113  /* === predefined DSL symbols === */
114 
115  enum UIPathElm
116  {
117  UIC_WINDOW,
118  UIC_PERSP,
119  UIC_PANEL,
120  UIC_VIEW,
121  UIC_TAB,
122  UIC_PATH
123  };
124 
125 
126  extern const Symbol UIC_CURRENT_WINDOW;
127  extern const Symbol UIC_FIRST_WINDOW;
128  extern const Symbol UIC_ELIDED;
129 
130 
131 
138  class UICoord
139  : public lib::PathArray<UIC_INLINE_SIZE>
140  {
141 
142  public:
152  template<typename...ARGS>
153  explicit
154  UICoord (ARGS&& ...args) : PathArray(std::forward<ARGS> (args)...) { }
155 
156  UICoord (UICoord&&) = default;
157  UICoord (UICoord const&) = default;
158  UICoord (UICoord& o) : UICoord((UICoord const&)o) { }
159 
160  UICoord& operator= (UICoord const&) = default;
161  UICoord& operator= (UICoord &&) = default;
162 
163 
164 
165  /* === Builder API === */
166 
167  class Builder;
168 
170  UICoord (Builder&& builder);
171 
173  static Builder firstWindow();
174 
176  static Builder currentWindow();
177 
179  static Builder window (Literal windowID);
180 
181  //----- convenience shortcuts to start a copy-builder....
182  Builder persp (Literal perspectiveID) const;
183  Builder panel (Literal panelID)const;
184  Builder view (Literal viewID) const;
185  Builder tab (Literal tabID) const;
186  Builder tab (uint tabIdx) const;
187  Builder noTab () const;
188 
189  //----- convenience shortcuts to start mutation on a copy...
190  Builder path (Literal pathDefinition) const;
191  Builder append (Literal elmID) const;
192  Builder prepend (Literal elmID) const;
193  Builder rebuild() const;
194 
195 
196 
197  /* === named component access === */
198 
199  Literal getWindow() const { return accesComponent (UIC_WINDOW);}
200  Literal getPersp() const { return accesComponent (UIC_PERSP); }
201  Literal getPanel() const { return accesComponent (UIC_PANEL); }
202  Literal getView() const { return accesComponent (UIC_VIEW); }
203  Literal getTab() const { return accesComponent (UIC_TAB); }
204 
205 
206 
207  /* === query functions === */
208 
214  bool
215  isIncomplete() const
216  {
217  return not empty()
218  and isnil (getWindow());
219  }
220 
221  bool
222  isComplete() const
223  {
224  return not empty()
225  and not isnil (getWindow());
226  }
227 
228 
233  bool
234  isExplicit() const
235  {
236  return isComplete()
237  and not util::contains (*this, Symbol::ANY);
238  }
239 
240 
241  bool
242  isPresent (size_t idx) const
243  {
244  Literal* elm = unConst(this)->getPosition(idx);
245  return not isnil(elm)
246  and *elm != Symbol::ANY;
247  }
248 
249 
250  bool
251  isWildcard (size_t idx) const
252  {
253  Literal* elm = unConst(this)->getPosition(idx);
254  return elm
255  and *elm == Symbol::ANY;
256  }
257 
258 
270  bool
271  isExtendedBelow (UICoord const& parent) const
272  {
273  size_t subSiz = this->size(),
274  parSiz = parent.size(),
275  idx = 0;
276 
277  if (parSiz >= subSiz)
278  return false;
279 
280  while (idx < parSiz
281  and ( (*this)[idx]== parent[idx]
282  or Symbol::ANY == parent[idx]
283  or isnil (parent[idx])))
284  ++idx;
285 
286  ENSURE (idx < subSiz);
287  return idx == parSiz;
288  } // meaning: this goes further down
289 
290 
291 
292  /* === String representation === */
293 
294  explicit
295  operator string() const
296  {
297  if (isnil (*this))
298  return "UI:?";
299 
300  string component = getComp();
301  string path = getPath();
302 
303  if (isnil (component))
304  return "UI:?/" + path;
305 
306  if (isnil (path))
307  return "UI:" + component;
308  else
309  return "UI:" + component + "/" + path;
310  }
311 
312  string
313  getComp() const
314  {
315  if (empty()) return "";
316 
317  size_t end = min (size(), UIC_PATH);
318  size_t pos = indexOf (*begin());
319 
320  if (pos >= end)
321  return ""; // empty or path information only
322 
323  string buff;
324  buff.reserve(80);
325 
326  if (0 < pos) // incomplete UI-Coordinates (not anchored)
327  buff += "?";
328 
329  for ( ; pos<end; ++pos )
330  switch (pos) {
331  case UIC_WINDOW:
332  buff += getWindow();
333  break;
334  case UIC_PERSP:
335  buff += "["+getPersp()+"]";
336  break;
337  case UIC_PANEL:
338  buff += "-"+getPanel();
339  break;
340  case UIC_VIEW:
341  buff += "."+getView();
342  break;
343  case UIC_TAB:
344  if (UIC_ELIDED != getTab())
345  buff += "."+getTab();
346  break;
347  default:
348  NOTREACHED ("component index numbering broken");
349  }
350  return buff;
351  }
352 
353  string
354  getPath() const
355  {
356  size_t siz = size();
357  if (siz <= UIC_PATH)
358  return ""; // no path information
359 
360  string buff; // heuristic pre-allocation
361  buff.reserve (10 * (siz - UIC_PATH));
362 
363  iterator elm = pathSeq();
364  if (isnil (*elm))
365  { // irregular case : only a path fragment
366  elm = this->begin();
367  buff += "?/";
368  }
369 
370  for ( ; elm; ++elm )
371  buff += *elm + "/";
372 
373  // chop off last delimiter
374  size_t len = buff.length();
375  ASSERT (len >= 1);
376  buff.resize(len-1);
377  return buff;
378  }
379 
381  iterator
382  pathSeq() const
383  {
384  return size()<= UIC_PATH? end()
385  : iterator{this, unConst(this)->getPosition(UIC_PATH)};
386  }
387 
388 
389  private:
391  friend class Builder;
392 
393  size_t
394  findStartIdx() const
395  {
396  REQUIRE (not empty());
397  return indexOf (*begin());
398  }
399 
400  Literal
401  accesComponent (UIPathElm idx) const
402  {
403  Literal* elm = unConst(this)->getPosition(idx);
404  return elm? *elm : Symbol::EMPTY;
405  }
406 
407  void
408  setComponent (size_t idx, Literal newContent)
409  {
410  Literal* storage = expandPosition (idx);
411  setContent (storage, newContent);
412  }
413 
414 
422  void
423  setTailSequence (size_t idx, Literal newContent)
424  {
425  std::vector<Literal> elms;
426  if (not isnil (newContent))
427  {
428  if (not std::strchr (newContent, '/'))
429  {
430  // single element: just place it as-is
431  // and remove any further content behind
432  elms.emplace_back (newContent);
433  }
434  else
435  { // it is actually a sequence of elements,
436  // which need to be split first, and then
437  // interned into the global symbol table
438  string sequence{newContent};
439  size_t pos = 0;
440  size_t last = 0;
441  while (string::npos != (last = sequence.find ('/', pos)))
442  {
443  elms.emplace_back (Symbol{sequence.substr(pos, last - pos)});
444  pos = last + 1; // delimiter stripped
445  }
446  sequence = sequence.substr(pos);
447  if (not isnil (sequence))
448  elms.emplace_back (Symbol{sequence});
449  } }
450 
451  setTailSequence (idx, elms);
452  }
453 
462  void
463  setTailSequence (size_t idx, std::vector<Literal>& pathElms)
464  {
465  size_t cnt = pathElms.size();
466  expandPosition (idx + cnt); // preallocate
467  for (size_t i=0 ; i < cnt; ++i)
468  setContent (expandPosition(idx + i), pathElms[i]);
469  size_t end = size();
470  for (size_t i = idx+cnt; i<end; ++i)
471  setContent (expandPosition(i), nullptr);
472  }
473 
474 
475  public: /* ===== relational operators : equality and partial order ===== */
476 
477  friend bool
478  operator== (UICoord const& l, UICoord const& r)
479  {
480  return static_cast<PathArray const&> (l) == static_cast<PathArray const&> (r);
481  }
482 
483  friend bool
484  operator< (UICoord const& l, UICoord const& r)
485  {
486  return l.isExtendedBelow (r);
487  }
488 
489  friend bool operator> (UICoord const& l, UICoord const& r) { return (r < l); }
490  friend bool operator<= (UICoord const& l, UICoord const& r) { return (l < r) or (l == r); }
491  friend bool operator>= (UICoord const& l, UICoord const& r) { return (r < l) or (l == r); }
492  friend bool operator!= (UICoord const& l, UICoord const& r) { return not (l == r); }
493  };
494 
495 
496 
497 
498 
499  /* === Builder API === */
500 
501  class LocationClause;
502 
505  {
506  protected:
507  UICoord uic_;
508 
509  template<typename...ARGS>
510  explicit
511  Builder (ARGS&& ...args) : uic_{std::forward<ARGS> (args)...} { }
512  Builder (UICoord && anonRef) : uic_{std::move(anonRef)} { }
513  Builder (UICoord const& base) : uic_{base} { }
514 
516  friend class UICoord;
517 
518  public:
520  Builder (Builder &&) = default;
521 
522  size_t size() const { return uic_.size(); }
523  bool empty() const { return uic_.empty();}
524 
527  UICoord const&
529  {
530  return uic_;
531  }
532 
533 
534  /* == Builder functions == */
535 
538  Builder&&
539  window (Literal windowID)
540  {
541  uic_.setComponent (UIC_WINDOW, windowID);
542  return std::move (*this);
543  }
544 
546  Builder&&
547  persp (Literal perspectiveID)
548  {
549  uic_.setComponent (UIC_PERSP, perspectiveID);
550  return std::move (*this);
551  }
552 
554  Builder&&
555  panel (Literal panelID)
556  {
557  uic_.setComponent (UIC_PANEL, panelID);
558  return std::move (*this);
559  }
560 
562  Builder&&
563  view (Literal viewID)
564  {
565  uic_.setComponent (UIC_VIEW, viewID);
566  return std::move (*this);
567  }
568 
570  Builder&&
571  tab (Literal tabID)
572  {
573  uic_.setComponent (UIC_TAB, tabID);
574  return std::move (*this);
575  }
576 
578  Builder&&
579  tab (uint tabIdx)
580  {
581  uic_.setComponent (UIC_TAB, Symbol{"#"+util::toString (tabIdx)});
582  return std::move (*this);
583  }
584 
587  Builder&&
589  {
590  uic_.setComponent (UIC_TAB, UIC_ELIDED);
591  return std::move (*this);
592  }
593 
594 
599  Builder&&
601  {
602  if (not isnil(elm))
603  uic_.setTailSequence (uic_.size(), elm);
604  return std::move (*this);
605  }
606 
608  Builder&&
609  prepend (Literal elmID)
610  {
611  if (not uic_.isIncomplete())
612  throw error::Logic ("Attempt to prepend "+elmID
613  +" to the complete rooted path "+string(uic_));
614 
615  uic_.setComponent (uic_.findStartIdx() - 1, elmID);
616  return std::move (*this);
617  }
618 
624  Builder&&
625  path (Literal pathDef)
626  {
627  uic_.setTailSequence (UIC_PATH, pathDef);
628  return std::move (*this);
629  }
630 
632  Builder&&
633  truncateTo (size_t depth)
634  {
635  uic_.truncateTo (depth);
636  return std::move (*this);
637  }
638 
640  operator LocationClause();
641  LocationClause create();
642 
643  protected:
644  Builder&&
645  normalise()
646  {
647  uic_.normalise();
648  return std::move (*this);
649  }
650 
651  Builder&&
652  overwrite (size_t depth, Literal newSpec)
653  {
654  Literal* storage = uic_.expandPosition (depth);
655  uic_.setContent (storage, newSpec);
656  return std::move (*this);
657  }
658  };
659 
660 
661 
666  inline
668  : UICoord{std::move (builder.uic_)}
669  {
670  PathArray::normalise();
671  }
672 
673 
677  inline UICoord::Builder
679  {
680  return window (UIC_CURRENT_WINDOW);
681  }
682 
683  inline UICoord::Builder
685  {
686  return window (UIC_FIRST_WINDOW);
687  }
688 
690  inline UICoord::Builder
692  {
693  return Builder{windowID};
694  }
695 
696 
703  inline UICoord::Builder
704  UICoord::persp (Literal perspectiveID) const
705  {
706  return Builder(*this).persp (perspectiveID);
707  }
708 
709  inline UICoord::Builder
710  UICoord::panel (Literal panelID) const
711  {
712  return Builder(*this).panel (panelID);
713  }
714 
715  inline UICoord::Builder
716  UICoord::view (Literal viewID) const
717  {
718  return Builder(*this).view (viewID);
719  }
720 
721  inline UICoord::Builder
722  UICoord::tab (Literal tabID) const
723  {
724  return Builder(*this).tab (tabID);
725  }
726 
727  inline UICoord::Builder
728  UICoord::tab (uint tabIdx) const
729  {
730  return Builder(*this).tab (tabIdx);
731  }
732 
733  inline UICoord::Builder
734  UICoord::noTab () const
735  {
736  return Builder(*this).noTab();
737  }
738 
744  inline UICoord::Builder
745  UICoord::path (Literal pathDefinition) const
746  {
747  return Builder(*this).path (pathDefinition);
748  }
749 
750  inline UICoord::Builder
751  UICoord::append (Literal elmID) const
752  {
753  return Builder(*this).append (elmID);
754  }
755 
756  inline UICoord::Builder
757  UICoord::prepend (Literal elmID) const
758  {
759  return Builder(*this).prepend (elmID);
760  }
761 
762  inline UICoord::Builder
763  UICoord::rebuild () const
764  {
765  return Builder(*this);
766  }
767 
768 
769 
770 }}// namespace stage::interact
771 #endif /*STAGE_INTERACT_UI_COORD_H*/
bool isExplicit() const
Definition: ui-coord.hpp:234
Literal * expandPosition(size_t idx)
Definition: path-array.hpp:473
Builder && truncateTo(size_t depth)
possibly shorten this path specification to a limited depth
Definition: ui-coord.hpp:633
Describe a location within the UI through structural/topological coordinates.
Definition: ui-coord.hpp:138
Builder && path(Literal pathDef)
augment UI coordinates to define a complete local path
Definition: ui-coord.hpp:625
Builder persp(Literal perspectiveID) const
Definition: ui-coord.hpp:704
void setTailSequence(size_t idx, std::vector< Literal > &pathElms)
replace the existing path information with the given elements
Definition: ui-coord.hpp:463
Builder && window(Literal windowID)
change UI coordinate spec to define it to be rooted within the given window
Definition: ui-coord.hpp:539
UICoord(ARGS &&...args)
UI-Coordinates can be created explicitly by specifying a sequence of Literal tokens, which will be used to initialise and then normalise the underlying PathArray.
Definition: ui-coord.hpp:154
Builder path(Literal pathDefinition) const
convenience builder function so set a full path definition
Definition: ui-coord.hpp:745
Builder && persp(Literal perspectiveID)
augment UI coordinates to mandate a specific perspective to be active within the window ...
Definition: ui-coord.hpp:547
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:85
STL namespace.
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:58
Builder && append(Literal elm)
augment UI coordinates by appending a further component at the end.
Definition: ui-coord.hpp:600
const Symbol UIC_ELIDED
indicate that a component is elided or irrelevant here
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
Builder && tab(Literal tabID)
augment UI coordinates to indicate a specific tab within the view"
Definition: ui-coord.hpp:571
Abstraction for path-like topological coordinates.
Definition: path-array.hpp:251
Token or Atom with distinct identity.
Definition: symbol.hpp:126
Mix-Ins to allow or prohibit various degrees of copying and cloning.
A single location specification to be matched and fulfilled.
Builder && panel(Literal panelID)
augment UI coordinates to indicate a specific view to be used
Definition: ui-coord.hpp:555
Marker types to indicate a literal string and a Symbol.
void normalise()
establish the contract of PathArray
Definition: path-array.hpp:518
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:46
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Builder && noTab()
augment UI coordinates to indicate that no tab specification is necessary
Definition: ui-coord.hpp:588
const Symbol UIC_CURRENT_WINDOW
window spec to refer to the current window
iterator pathSeq() const
iterative access to the path sequence section
Definition: ui-coord.hpp:382
PathArray(IndexSeq< prefix... >, IndexSeq< rest... >, ARGS &&...args)
Definition: path-array.hpp:271
bool isExtendedBelow(UICoord const &parent) const
Check if this coordinate spec can be seen as an extension of the given parent coordinates and thus re...
Definition: ui-coord.hpp:271
Lumiera error handling (C++ interface).
Builder && view(Literal viewID)
augment UI coordinates to indicate a specific view to be used
Definition: ui-coord.hpp:563
static Builder window(Literal windowID)
Builder: start definition of UI-Coordinates rooted in given window.
Definition: ui-coord.hpp:691
bool isIncomplete() const
Definition: ui-coord.hpp:215
void setContent(Literal *pos, const char *val)
Definition: path-array.hpp:486
Builder && prepend(Literal elmID)
augment partially defined UI coordinates by extending them towards the root
Definition: ui-coord.hpp:609
Foundation abstraction to implement path-like component sequences.
const Symbol UIC_FIRST_WINDOW
window spec to refer to the first window of the application
size_t indexOf(Literal const &content) const
reverse lookup of actual path content
Definition: path-array.hpp:357
void setTailSequence(size_t idx, Literal newContent)
replace / overwrite existing content starting at given index.
Definition: ui-coord.hpp:423
static Builder firstWindow()
Builder: start definition of UI-Coordinates rooted in the firstWindow
Definition: ui-coord.hpp:684
Adapter for building an implementation of the »Lumiera Forward Iterator« concept. ...
static Builder currentWindow()
Builder: start definition of UI-Coordinates rooted in the currentWindow
Definition: ui-coord.hpp:678
Builder && tab(uint tabIdx)
augment UI coordinates to indicate a tab specified by index number
Definition: ui-coord.hpp:579