DUDS
Distributed Update of Data from Something
Menu.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the DUDS project. It is subject to the BSD-style
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at https://github.com/jjackowski/duds/blob/master/LICENSE.
5  * No part of DUDS, including this file, may be copied, modified, propagated,
6  * or distributed except according to the terms contained in the LICENSE file.
7  *
8  * Copyright (C) 2019 Jeff Jackowski
9  */
10 #include <duds/ui/menu/Menu.hpp>
14 #include <duds/general/Errors.hpp>
15 
16 namespace duds { namespace ui { namespace menu {
17 
18 Menu::Menu(std::size_t reserve) :
19 lockCnt(0), updateIdx(0), invis(0), toggles(0) {
20  items.reserve(reserve);
21 }
22 
23 Menu::Menu(const std::string &title, std::size_t reserve) :
24 lockCnt(0), updateIdx(0), invis(0), toggles(0), lbl(title) {
25  items.reserve(reserve);
26 }
27 
29  if (lockOwner != std::this_thread::get_id()) {
30  // obtain lock; may block
31  block.lock();
32  // assure unlock state is good
33  assert(!lockCnt && (lockOwner == std::thread::id()));
34  // set data for recursive locking
35  lockOwner = std::this_thread::get_id();
36  }
37  // always count lock for recursive locking
38  ++lockCnt;
39 }
40 
42  // an exclusive lock is required
43  assert(lockCnt && (lockOwner == std::this_thread::get_id()));
44  // decrement lock count; check for need to unlock
45  if (!--lockCnt) {
46  // clear thread owner
47  lockOwner = std::thread::id();
48  // unlock
49  block.unlock();
50  }
51 }
52 
53 Menu::ItemVec::const_iterator Menu::iterator(std::size_t index) const {
54  // bounds check
55  if (index >= items.size()) {
57  MenuObject(shared_from_this()) <<
58  MenuItemIndex(index)
59  );
60  }
61  return items.begin() + index;
62 }
63 
64 void Menu::clear() noexcept {
65  // an exclusive lock is required
66  assert(lockCnt && (lockOwner == std::this_thread::get_id()));
67  // only change if not empty
68  if (!items.empty()) {
69  // clear out all items
70  items.clear();
71  invis = 0;
72  toggles = 0;
73  // record that a change has occurred
74  ++updateIdx;
75  }
76 }
77 
78 void Menu::title(const std::string &newTitle) noexcept {
79  // an exclusive lock is required
80  assert(lockCnt && (lockOwner == std::this_thread::get_id()));
81  // change the title
82  lbl = newTitle;
83  // record that a change has occurred
84  ++updateIdx;
85 }
86 
87 void Menu::addView(const std::shared_ptr<MenuView> &view) {
88  exclusiveLock();
89  views[view.get()] = view;
91 }
92 
94  void (MenuView::* eventFunc)(std::size_t),
95  std::size_t idx
96 ) {
97  ViewMap::iterator iter = views.begin();
98  while (iter != views.end()) {
99  // attempt to get a shared pointer to the view
100  MenuViewSptr view = iter->second.lock();
101  // check for existence
102  if (view) {
103  // call the given function
104  (view.get()->*eventFunc)(idx);
105  // advance to the next view
106  ++iter;
107  } else {
108  // remove destructed view
109  iter = views.erase(iter);
110  }
111  }
112 }
113 
114 void Menu::append(std::shared_ptr<MenuItem> &&mi) {
115  // an exclusive lock is required
116  assert(lockCnt && (lockOwner == std::this_thread::get_id()));
117  if (mi) {
118  items.emplace_back(std::move(mi));
119  items.back()->parent = this;
120  // update invisble count
121  if (items.back()->isInvisible()) {
122  ++invis;
123  }
124  // update toggle count
125  if (items.back()->isToggle()) {
126  ++toggles;
127  }
128  ++updateIdx;
129  } else {
130  DUDS_THROW_EXCEPTION(MenuNoItemError() << MenuObject(shared_from_this()));
131  }
132 }
133 
134 void Menu::append(const std::shared_ptr<MenuItem> &mi) {
135  // A copy is always made for storage in the items vector. Make the copy
136  // here and forward it for storage.
137  std::shared_ptr<MenuItem> item(mi);
138  append(std::move(item));
139 }
140 
141 void Menu::insert(std::size_t index, std::shared_ptr<MenuItem> &&mi) {
142  // an exclusive lock is required
143  assert(lockCnt && (lockOwner == std::this_thread::get_id()));
144  if (mi) {
145  // bounds check
146  if (index > items.size()) {
148  MenuObject(shared_from_this()) <<
149  MenuItemIndex(index)
150  );
151  }
152  ItemVec::iterator iter = items.insert(
153  items.begin() + index, std::move(mi)
154  );
155  (*iter)->parent = this;
156  // update invisble count
157  if ((*iter)->isInvisible()) {
158  ++invis;
159  }
160  // update toggle count
161  if ((*iter)->isToggle()) {
162  ++toggles;
163  }
164  // note a change has occured
165  ++updateIdx;
167  } else {
168  DUDS_THROW_EXCEPTION(MenuNoItemError() << MenuObject(shared_from_this()));
169  }
170 }
171 
172 void Menu::insert(std::size_t index, const std::shared_ptr<MenuItem> &mi) {
173  // A copy is always made for storage in the items vector. Make the copy
174  // here and forward it for storage.
175  std::shared_ptr<MenuItem> item(mi);
176  insert(index, std::move(item));
177 }
178 
179 void Menu::remove(const std::shared_ptr<MenuItem> &mi) {
180  // an exclusive lock is required
181  assert(lockCnt && (lockOwner == std::this_thread::get_id()));
182  if (mi) {
183  // search
184  ItemVec::iterator iter = std::find(items.begin(), items.end(), mi);
185  // not there?
186  if (iter == items.end()) {
188  MenuObject(shared_from_this()) <<
189  MenuItemObject(mi)
190  );
191  }
192  std::size_t idx = iter - items.begin();
193  // remove the item
194  items.erase(iter);
195  // disown the item
196  mi->parent = nullptr;
197  // note a change has occured
198  ++updateIdx;
200  } else {
201  DUDS_THROW_EXCEPTION(MenuNoItemError() << MenuObject(shared_from_this()));
202  }
203 }
204 
205 void Menu::remove(std::size_t index) {
206  // an exclusive lock is required
207  assert(lockCnt && (lockOwner == std::this_thread::get_id()));
208  // bounds check
209  if (index > items.size()) {
211  MenuObject(shared_from_this()) <<
212  MenuItemIndex(index)
213  );
214  }
215  ItemVec::iterator iter = items.begin() + index;
216  // get a weak pointer to the item
217  std::weak_ptr<MenuItem> mi(*iter);
218  // remove the item
219  items.erase(iter);
220  // does it still exist?
221  std::shared_ptr<MenuItem> smi = mi.lock();
222  if (smi) {
223  // disown the item
224  smi->parent = nullptr;
225  }
226  // note a change has occured
227  ++updateIdx;
229 }
230 
231 } } }
std::shared_ptr< MenuView > MenuViewSptr
A shared pointer to a MenuView.
Definition: MenuView.hpp:390
void exclusiveUnlock()
Performs a recursive exclusive unlock on block.
Definition: Menu.cpp:41
std::thread::id lockOwner
Used with block to implement recursive locking.
Definition: Menu.hpp:72
std::size_t toggles
The number of items that are toggles.
Definition: Menu.hpp:85
void informViews(void(MenuView::*eventFunc)(std::size_t), std::size_t idx)
Calls a function on each view using this menu, and provides the index of a menu item.
Definition: Menu.cpp:93
std::shared_timed_mutex block
Used to enable thread-safe operations.
Definition: Menu.hpp:68
void insertion(std::size_t idx)
Responds to the menu inserting a menu item to a spot other than the end of the menu.
Definition: MenuView.cpp:243
int lockCnt
The number of exclusive locks held by the thread indicated by lockOwner.
Definition: Menu.hpp:90
void removal(std::size_t idx)
Responds to the menu removing a menu item.
Definition: MenuView.cpp:262
ItemVec items
The store of menu items for the menu.
Definition: Menu.hpp:59
A required MenuItem object is missing.
Definition: MenuErrors.hpp:28
move_impl move(unsigned int c, unsigned int r)
Display stream manipulator that moves the display cursor to the given location.
void append(std::shared_ptr< MenuItem > &&mi)
Appends a new item to the end of the menu.
Definition: Menu.cpp:114
void addView(const std::shared_ptr< MenuView > &view)
Stores a weak reference to the given view for the purpose of informing the view of any item insertion...
Definition: Menu.cpp:87
boost::error_info< struct Info_MenuItemIndex, std::size_t > MenuItemIndex
The index (position) of the MenuItem involved in an error.
Definition: MenuErrors.hpp:85
ViewMap views
The views; used to inform the views that menu items have been added or removed.
Definition: Menu.hpp:64
boost::error_info< struct Info_Menu, std::shared_ptr< const Menu > > MenuObject
The Menu object that is involved in an error.
Definition: MenuErrors.hpp:100
int updateIdx
This value is incremented every time the menu is changed.
Definition: Menu.hpp:97
std::size_t invis
The number of items that are currently flagged as invisible.
Definition: Menu.hpp:81
void insert(std::size_t index, std::shared_ptr< MenuItem > &&mi)
Inserts a new item into the menu.
Definition: Menu.cpp:141
void exclusiveLock()
Performs a recursive exclusive lock on block.
Definition: Menu.cpp:28
ItemVec::const_iterator iterator(std::size_t index) const
Returns an iterator to the MenuItem object at the given position.
Definition: Menu.cpp:53
An attempt was made to inset a MenuItem beyond the bounds of the menu.
Definition: MenuErrors.hpp:33
Keeps track of the selected menu item, and updates it based on user input.
Definition: MenuView.hpp:46
void clear() noexcept
Removes all items from the menu.
Definition: Menu.cpp:64
boost::error_info< struct Info_MenuItem, std::shared_ptr< const MenuItem > > MenuItemObject
The MenuItem object that is involved in an error.
Definition: MenuErrors.hpp:94
const std::string & title() const
Returns the title of the menu.
Definition: Menu.hpp:143
std::string lbl
The menu&#39;s name; optional.
Definition: Menu.hpp:76
Menu(std::size_t reserve=0)
Construct a new menu.
Definition: Menu.cpp:18
The specified MenuItem does not exist in the Menu.
Definition: MenuErrors.hpp:38
void remove(const std::shared_ptr< MenuItem > &mi)
Removes an item from the menu.
Definition: Menu.cpp:179
General errors.
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48