iPlug2 - C++ Audio Plug-in Framework
IGraphicsPopupMenu.h
Go to the documentation of this file.
1 /*
2  ==============================================================================
3 
4  This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5 
6  See LICENSE.txt for more info.
7 
8  ==============================================================================
9 */
10 
11 #pragma once
12 
13 #include <cmath>
14 #include <cstring>
15 #include <cstdio>
16 #include <cassert>
17 #include <memory>
18 
19 #include "wdlstring.h"
20 #include "ptrlist.h"
21 
29 BEGIN_IPLUG_NAMESPACE
30 BEGIN_IGRAPHICS_NAMESPACE
31 
40 {
41 public:
43 #pragma mark - IPopupMenu::Item
44  class Item
45  {
46  public:
47  enum Flags
48  {
49  kNoFlags = 0,
50  kDisabled = 1 << 0, // item is gray and not selectable
51  kTitle = 1 << 1, // item indicates a title and is not selectable
52  kChecked = 1 << 2, // item has a checkmark
53  kSeparator = 1 << 3 // item is a separator
54  };
55 
56  Item(const char* str, int flags = kNoFlags, int tag = -1)
57  : mFlags(flags)
58  , mTag(tag)
59  {
60  SetText(str);
61  }
62 
63  Item (const char* str, IPopupMenu* pSubMenu)
64  : mSubmenu(pSubMenu)
65  , mFlags(kNoFlags)
66  {
67  SetText(str);
68  }
69 
70  Item(const Item&) = delete;
71  void operator=(const Item&) = delete;
72 
73  ~Item()
74  {
75  }
76 
77  void SetText(const char* str) { mText.Set(str); }
78  const char* GetText() const { return mText.Get(); }; // TODO: Text -> Str!
79 
80  bool GetEnabled() const { return !(mFlags & kDisabled); }
81  bool GetChecked() const { return (mFlags & kChecked) != 0; }
82  bool GetIsTitle() const { return (mFlags & kTitle) != 0; }
83  bool GetIsSeparator() const { return (mFlags & kSeparator) != 0; }
84  int GetTag() const { return mTag; }
85  IPopupMenu* GetSubmenu() const { return mSubmenu.get(); }
86  bool GetIsChoosable() const
87  {
88  if(GetIsTitle()) return false;
89  if(GetIsSeparator()) return false;
90  if(GetSubmenu() != nullptr) return false;
91  if(!GetEnabled()) return false;
92 
93  return true;
94  }
95 
96  void SetEnabled(bool state) { SetFlag(kDisabled, !state); }
97  void SetChecked(bool state) { SetFlag(kChecked, state); }
98  void SetTitle(bool state) {SetFlag(kTitle, state); }
99  void SetSubmenu(IPopupMenu* pSubmenu) { mSubmenu.reset(pSubmenu); }
100 
101  protected:
102  void SetFlag(Flags flag, bool state)
103  {
104  if (state)
105  mFlags |= flag;
106  else
107  mFlags &= ~flag;
108  }
109 
110  WDL_String mText;
111  std::unique_ptr<IPopupMenu> mSubmenu;
112  int mFlags;
113  int mTag = -1;
114  };
115 
116  #pragma mark -
117 
118  IPopupMenu(const char* rootTitle = "", int prefix = 0, bool multicheck = false, const std::initializer_list<const char*>& items = {})
119  : mPrefix(prefix)
120  , mCanMultiCheck(multicheck)
121  , mRootTitle(rootTitle)
122  {
123  for (auto& item : items)
124  AddItem(item);
125  }
126 
127  IPopupMenu(const char* rootTitle, const std::initializer_list<const char*>& items, IPopupFunction func = nullptr)
128  : mPrefix(0)
129  , mCanMultiCheck(false)
130  , mRootTitle(rootTitle)
131  {
132  for (auto& item : items)
133  AddItem(item);
134 
135  SetFunction(func);
136  }
137 
138  IPopupMenu(const IPopupMenu&) = delete;
139  void operator=(const IPopupMenu&) = delete;
140 
141  ~IPopupMenu()
142  {
143  mMenuItems.Empty(true);
144  }
145 
146  static int Sortfunc(const Item **a, const Item **b)
147  {
148  return stricmp((*a)->GetText(),(*b)->GetText());
149  }
150 
151  Item* AddItem(Item* pItem, int index = -1)
152  {
153  if (index == -1)
154  mMenuItems.Add(pItem); // add it to the end
155  else if (index == -2)
156  mMenuItems.InsertSorted(pItem, Sortfunc);
157  else
158  mMenuItems.Insert(index, pItem);
159 
160  return pItem;
161  }
162 
163  Item* AddItem(const char* str, int index = -1, int itemFlags = Item::kNoFlags) { return AddItem(new Item(str, itemFlags), index); }
164 
165  Item* AddItem(const char* str, int index, IPopupMenu* pSubmenu)
166  {
167  assert(pSubmenu->GetFunction() == nullptr); // submenus should not have existing functions
168 
169  if(GetFunction())
170  pSubmenu->SetFunction(GetFunction());
171 
172  return AddItem(new Item(str, pSubmenu), index);
173  }
174 
175  Item* AddItem(const char* str, IPopupMenu* pSubmenu, int index = -1)
176  {
177  assert(pSubmenu->GetFunction() == nullptr); // submenus should not have existing functions
178 
179  if(GetFunction())
180  pSubmenu->SetFunction(GetFunction());
181 
182  return AddItem(new Item(str, pSubmenu), index);
183  }
184 
185  Item* AddSeparator(int index = -1)
186  {
187  Item* pItem = new Item ("", Item::kSeparator);
188  return AddItem(pItem, index);
189  }
190 
191  void RemoveEmptySubmenus()
192  {
193  int n = mMenuItems.GetSize();
194 
195  WDL_PtrList<IPopupMenu::Item> toDelete;
196 
197  for (int i = 0; i < n; i++)
198  {
199  IPopupMenu::Item* pItem = GetItem(i);
200 
201  IPopupMenu* pSubmenu = pItem->GetSubmenu();
202 
203  if(pSubmenu && pSubmenu->NItems() == 0)
204  {
205  toDelete.Add(pItem);
206  }
207  }
208 
209  for (int i = 0; i < toDelete.GetSize(); i++)
210  {
211  mMenuItems.DeletePtr(toDelete.Get(i));
212  }
213  }
214 
215  void SetChosenItemIdx(int index) { mChosenItemIdx = index; };
216  int GetChosenItemIdx() const { return mChosenItemIdx; }
217  int NItems() const { return mMenuItems.GetSize(); }
218  int NItemsPerColumn() const { return mNItemsPerColumn; }
219  void SetNItemsPerColumn(int nItemsPerColumn) { mNItemsPerColumn = nItemsPerColumn; }
220  int GetPrefix() const { return mPrefix; }
221  bool GetCanMultiCheck() const { return mCanMultiCheck; }
222 
223  Item* GetItem(int index)
224  {
225  int nItems = NItems();
226 
227  if (index >= 0 && index < nItems)
228  {
229  return mMenuItems.Get(index);
230  }
231  else
232  {
233  return nullptr;
234  }
235  }
236 
237  Item* GetChosenItem()
238  {
239  return GetItem(mChosenItemIdx);
240  }
241 
242  const char* GetItemText(int index)
243  {
244  Item* pItem = GetItem(index);
245  if(pItem)
246  return pItem->GetText();
247  else
248  return "";
249  }
250 
251  void SetPrefix(int count)
252  {
253  if (count >= 0 && count < 4)
254  {
255  mPrefix = count;
256  }
257  }
258 
259  void SetMultiCheck(bool multicheck) { mCanMultiCheck = multicheck; }
260 
261  void Clear(bool resetEverything = true)
262  {
263  if(resetEverything)
264  {
265  SetPrefix(0);
266  mCanMultiCheck = false;
267  }
268 
269  mMenuItems.Empty(true);
270  }
271 
272  bool CheckItem(int index, bool state)
273  {
274  Item* pItem = mMenuItems.Get(index);
275 
276  if (pItem)
277  {
278  pItem->SetChecked(state);
279  return true;
280  }
281  return false;
282  }
283 
284  void CheckItemAlone(int index)
285  {
286  for (int i = 0; i < mMenuItems.GetSize(); i++)
287  {
288  mMenuItems.Get(i)->SetChecked(i == index);
289  }
290  }
291 
292  bool IsItemChecked(int index)
293  {
294  Item* pItem = mMenuItems.Get(index);
295 
296  if (pItem)
297  return pItem->GetChecked();
298 
299  return false;
300  }
301 
302  void SetFunction(IPopupFunction func)
303  {
304  mPopupFunc = func;
305  }
306 
307  IPopupFunction GetFunction()
308  {
309  return mPopupFunc;
310  }
311 
312  void ExecFunction()
313  {
314  mPopupFunc(this);
315  }
316 
317  const char* GetRootTitle() const
318  {
319  return mRootTitle.Get();
320  }
321 
322  void SetRootTitle(const char* rootTitle)
323  {
324  return mRootTitle.Set(rootTitle);
325  }
326 
327 private:
328  int mNItemsPerColumn = 0; // Windows can divide popup menu into columns
329  int mPrefix; // 0 = no prefix, 1 = numbers no leading zeros, 2 = 1 lz, 3 = 2lz
330  int mChosenItemIdx = -1;
331  bool mCanMultiCheck; // multicheck = 0 doesn't actually prohibit multichecking, you should do that in your code, by calling CheckItemAlone instead of CheckItem
332  WDL_PtrList<Item> mMenuItems;
333  IPopupFunction mPopupFunc = nullptr;
334  WDL_String mRootTitle;
335 };
336 
337 END_IGRAPHICS_NAMESPACE
338 END_IPLUG_NAMESPACE
339 
A class to specify an item of a pop up menu.
A class for setting the contents of a pop up menu.