Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
berryMenuManager.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 BlueBerry Platform
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 #include "berryMenuManager.h"
18 
20 #include "berryQActionProperties.h"
22 
23 #include <QMenu>
24 #include <QMenuBar>
25 
26 
27 class QMenuProxy
28 {
29 
30 public:
31 
32  QMenu* menu;
33  QMenuBar* menuBar;
34 
35  enum Type {
36  MenuBar,
37  Menu
38  };
39 
40  QMenuProxy(Type type, QWidget* parent = nullptr)
41  : menu(nullptr), menuBar(nullptr)
42  {
43  switch (type)
44  {
45  case MenuBar: menuBar = new QMenuBar(parent);
46  case Menu: menu = new QMenu(parent);
47  }
48  }
49 
50  bool isMenuBar() const
51  {
52  return menuBar != nullptr;
53  }
54 
55  void setTitle(const QString& title)
56  {
57  if (menu)
58  menu->setTitle(title);
59  }
60 
61  void setIcon(const QIcon& icon)
62  {
63  if (menu)
64  menu->setIcon(icon);
65  }
66 
67  QList<QAction*> actions() const
68  {
69  return menu ? menu->actions() : menuBar->actions();
70  }
71 
72  void removeAction(QAction* action)
73  {
74  menu ? menu->removeAction(action) : menuBar->removeAction(action);
75  }
76 
77  bool isEnabled() const
78  {
79  return menu ? menu->isEnabled() : menuBar->isEnabled();
80  }
81 
82  void setEnabled(bool enabled)
83  {
84  menu ? menu->setEnabled(enabled) : menuBar->setEnabled(enabled);
85  }
86 
87  QAction* getParentItem() const
88  {
89  return menu ? menu->menuAction() : nullptr;
90  }
91 
92 };
93 
94 namespace berry
95 {
96 
97 struct NullOverrides: public IContributionManagerOverrides
98 {
99 public:
100 
101  int GetEnabled(const IContributionItem* /*item*/) const override
102  {
103  return -1;
104  }
105 
106  int GetVisible(const IContributionItem* /*item*/) const override
107  {
108  return -1;
109  }
110 };
111 
112 void MenuManager::HandleAboutToShow()
113 {
114  if (this->removeAllWhenShown)
115  {
116  this->RemoveAll();
117  }
118  emit AboutToShow(this);
119  this->Update(false, false);
120 }
121 
122 void MenuManager::HandleAboutToHide()
123 {
124  emit AboutToHide(this);
125 }
126 
127 MenuManager::MenuManager(const QString& text, const QString& id)
128  : id(id), menu(nullptr), menuItem(nullptr)
129  , menuText(text), parent(nullptr)
130  , removeAllWhenShown(false), visible(true)
131 {
132 
133 }
134 
135 MenuManager::MenuManager(const QString& text, const QIcon& image, const QString& id)
136  : id(id), menu(nullptr), menuItem(nullptr)
137  , menuText(text), image(image)
138  , parent(nullptr), removeAllWhenShown(false), visible(true)
139 {
140 
141 }
142 
144 {
146 }
147 
149 {
150  delete menu;
151 }
152 
153 QMenu* MenuManager::CreateContextMenu(QWidget* parent)
154 {
155  if (!menu)
156  {
157  menu = new QMenuProxy(QMenuProxy::Menu, parent);
158  this->InitializeMenu();
159  }
160  return menu->menu;
161 }
162 
163 QMenuBar* MenuManager::CreateMenuBar(QWidget* parent)
164 {
165  if (!menu)
166  {
167  menu = new QMenuProxy(QMenuProxy::MenuBar, parent);
168  this->Update(false);
169  }
170  return menu->menuBar;
171 }
172 
173 void MenuManager::AddMenuListener(QObject* listener)
174 {
175  this->connect(this, SIGNAL(AboutToShow(IMenuManager*)), listener, SLOT(MenuAboutToShow(IMenuManager*)));
176  this->connect(this, SIGNAL(AboutToHide(IMenuManager*)), listener, SLOT(MenuAboutToHide(IMenuManager*)));
177 }
178 
179 void MenuManager::RemoveMenuListener(QObject* listener)
180 {
181  this->disconnect(listener);
182 }
183 
184 void MenuManager::Fill(QStatusBar* /*parent*/)
185 {
186 }
187 
188 void MenuManager::Fill(QToolBar* /*parent*/, QAction* /*before*/)
189 {
190 }
191 
192 void MenuManager::Fill(QMenu* parent, QAction* before)
193 {
194  this->FillMenu(parent, before);
195 }
196 
197 void MenuManager::Fill(QMenuBar* parent, QAction* before)
198 {
199  this->FillMenu(parent, before);
200 }
201 
202 void MenuManager::FillMenu(QWidget* parent, QAction* before)
203 {
204  if (!menuItem)
205  {
206  menuItem = new QAction(parent);
207  if (parent)
208  {
209  parent->insertAction(before, menuItem);
210  }
211 
212  menuItem->setText(GetMenuText());
213  menuItem->setIcon(image);
214 
215  if(!menu)
216  {
217  menu = new QMenuProxy(QMenuProxy::Menu, parent);
218  }
219 
220  if (!menu->isMenuBar())
221  menuItem->setMenu(menu->menu);
222 
223  this->InitializeMenu();
224 
225  this->SetDirty(true);
226  }
227 }
228 
230 {
231  IContributionItem::Pointer item(this->FindUsingPath(path));
232  if (IMenuManager::Pointer manager = item.Cast<IMenuManager>())
233  {
234  return manager;
235  }
236  return IMenuManager::Pointer(nullptr);
237 }
238 
240 {
241  QString id(path);
242  QString rest;
243  int separator = path.indexOf('/');
244  if (separator != -1)
245  {
246  id = path.left(separator);
247  rest = path.mid(separator + 1);
248  }
249  else
250  {
251  return ContributionManager::Find(path);
252  }
253 
255  if (IMenuManager::Pointer manager = item.Cast<IMenuManager>())
256  {
257  return manager->FindUsingPath(rest);
258  }
259  return IContributionItem::Pointer(nullptr);
260 }
261 
262 QString MenuManager::GetId() const
263 {
264  return id;
265 }
266 
267 QMenu* MenuManager::GetMenu() const
268 {
269  return menu->menu;
270 }
271 
273 {
274  if (definitionId == "")
275  {
276  return menuText;
277  }
278 // ExternalActionManager::ICallback callback = ExternalActionManager
279 // .getInstance().getCallback();
280 // if (callback != null)
281 // {
282 // String shortCut = callback.getAcceleratorText(definitionId);
283 // if (shortCut == null)
284 // {
285 // return menuText;
286 // }
287 // return menuText + "\t" + shortCut; //$NON-NLS-1$
288 // }
289  return menuText;
290 }
291 
293 {
294  return image;
295 }
296 
298 {
299  if (!overrides)
300  {
301  if (!parent)
302  {
303  overrides = new NullOverrides();
304  }
305  else
306  {
307  overrides = parent->GetOverrides();
308  }
310  }
311  return overrides;
312 }
313 
315 {
316  return parent;
317 }
318 
320 {
321  return removeAllWhenShown;
322 }
323 
325 {
326  return false;
327 }
328 
330 {
331  return true;
332 }
333 
335 {
336  return false;
337 }
338 
340 {
341  return false;
342 }
343 
345 {
346  if (!visible)
347  {
348  return false; // short circuit calculations in this case
349  }
350 
351  if (removeAllWhenShown)
352  {
353  // we have no way of knowing if the menu has children
354  return true;
355  }
356 
357  // menus aren't visible if all of its children are invisible (or only contains visible separators).
358  bool visibleChildren = false;
359  foreach(IContributionItem::Pointer item, this->GetItems())
360  {
361  if (item->IsVisible() && !item->IsSeparator())
362  {
363  visibleChildren = true;
364  break;
365  }
366  }
367 
368  return visibleChildren;
369 }
370 
372 {
374  // Can't optimize by short-circuiting when the first dirty manager is encountered,
375  // since non-visible children are not even processed.
376  // That is, it's possible to have a dirty sub-menu under a non-dirty parent menu
377  // even after the parent menu has been updated.
378  // If items are added/removed in the sub-menu, we still need to propagate the dirty flag up,
379  // even if the sub-menu is already dirty, since the result of isVisible() may change
380  // due to the added/removed items.
381  IContributionManager* p = this->GetParent();
382  if (p)
383  {
384  p->MarkDirty();
385  }
386 }
387 
389 {
390 }
391 
393 {
394  overrides = newOverrides;
396 }
397 
399 {
400  parent = manager;
401 }
402 
404 {
405  this->removeAllWhenShown = removeAll;
406 }
407 
408 void MenuManager::SetVisible(bool visible)
409 {
410  this->visible = visible;
411 }
412 
413 void MenuManager::SetCommandId(const QString& definitionId)
414 {
415  this->definitionId = definitionId;
416 }
417 
419 {
420  this->UpdateMenuItem();
421 }
422 
423 void MenuManager::Update(const QString& property)
424 {
425  QList<IContributionItem::Pointer> items = GetItems();
426 
427  for (int i = 0; i < items.size(); i++)
428  {
429  items[i]->Update(property);
430  }
431 
432  if (menu != nullptr && menu->getParentItem() != nullptr)
433  {
434  if (QActionProperties::TEXT == property)
435  {
436  QString text = GetMenuText();
437  if (!text.isNull())
438  {
439  menu->getParentItem()->setText(text);
440  }
441  }
442  else if (QActionProperties::IMAGE == property && !image.isNull())
443  {
444  menu->getParentItem()->setIcon(image);
445  }
446  }
447 }
448 
449 void MenuManager::Update(bool force)
450 {
451  this->Update(force, false);
452 }
453 
454 void MenuManager::UpdateAll(bool force)
455 {
456  this->Update(force, true);
457 }
458 
459 void MenuManager::InitializeMenu()
460 {
461  menu->setTitle(GetMenuText());
462  menu->setIcon(GetImage());
463 
464  if (!menu->isMenuBar())
465  {
466  this->connect(menu->menu, SIGNAL(aboutToShow()), SLOT(HandleAboutToShow()));
467  this->connect(menu->menu, SIGNAL(aboutToHide()), SLOT(HandleAboutToHide()));
468  }
469 
470  // Don't do an update(true) here, in case menu is never opened.
471  // Always do it lazily in handleAboutToShow().
472 }
473 
474 void MenuManager::UpdateMenuItem()
475 {
476  if (menuItem && menu)
477  {
478  bool enabled = removeAllWhenShown || menu->actions().size() > 0;
479  if (menu->isEnabled() != enabled)
480  {
481  menuItem->setEnabled(enabled);
482  }
483  }
484 }
485 
487 {
488  if (menu->isMenuBar())
489  {
490  ci->Fill(menu->menuBar, before);
491  }
492  else
493  {
494  ci->Fill(menu->menu, before);
495  }
496 }
497 
498 void MenuManager::Update(bool force, bool recursive)
499 {
500  if (ContributionManager::IsDirty() || force)
501  {
502  if (menu)
503  {
504  // clean contains all active items without double separators
505  QList<IContributionItem::Pointer> items = this->GetItems();
506  QList<IContributionItem::Pointer> clean;
507  IContributionItem::Pointer separator;
508  foreach (IContributionItem::Pointer ci, items)
509  {
510  if (!ci->IsVisible())
511  {
512  continue;
513  }
514  if (ci->IsSeparator())
515  {
516  // delay creation until necessary
517  // (handles both adjacent separators, and separator at end)
518  separator = ci;
519  }
520  else
521  {
522  if (separator)
523  {
524  if (clean.size() > 0)
525  {
526  clean.push_back(separator);
527  }
528  separator = nullptr;
529  }
530  clean.push_back(ci);
531  }
532  }
533 
534  // remove obsolete (removed or non active)
535  QList<QAction*> mi = menu->actions();
536 
537  for (int i = 0; i < mi.size(); i++)
538  {
539  Object::Pointer data = mi[i]->data().value<Object::Pointer>();
540 
541  if (!data || !clean.contains(data.Cast<IContributionItem>()))
542  {
543  menu->removeAction(mi[i]);
544  }
545  else if (IContributionItem::Pointer ci = data.Cast<IContributionItem>())
546  {
547  if(ci->IsDynamic() && ci->IsDirty())
548  {
549  menu->removeAction(mi[i]);
550  delete mi[i];
551  }
552  }
553  }
554 
555  // add new
556  mi = menu->actions();
557  int srcIx = 0;
558  int destIx = 0;
559 
560  for (QList<IContributionItem::Pointer>::Iterator e = clean.begin();
561  e != clean.end(); ++e)
562  {
565 
566  // get corresponding item in widget
567  if (srcIx < mi.size())
568  {
569  dest = mi[srcIx]->data().value<Object::Pointer>().Cast<IContributionItem>();
570  }
571  else
572  {
573  dest = nullptr;
574  }
575 
576  if (dest && src == dest)
577  {
578  srcIx++;
579  destIx++;
580  }
581  else if (dest && dest->IsSeparator() && src->IsSeparator())
582  {
583  mi[srcIx]->setData(QVariant::fromValue<Object::Pointer>(src));
584  srcIx++;
585  destIx++;
586  }
587  else
588  {
589  int start = menu->actions().size();
590  //qDebug() << "***** Filling item destIx = " << destIx << " (size: " << start << ")";
591  this->DoItemFill(src, destIx >= start ? nullptr : menu->actions().at(destIx));
592  int newItems = menu->actions().size() - start;
593  //qDebug() << "***** New items: " << newItems;
594  for (int i = 0; i < newItems; ++i)
595  {
596  menu->actions().at(destIx++)->setData(QVariant::fromValue<Object::Pointer>(src));
597  }
598  }
599 
600  // May be we can optimize this call. If the menu has just
601  // been created via the call src.fill(fMenuBar, destIx) then
602  // the menu has already been updated with update(true)
603  // (see MenuManager). So if force is true we do it again. But
604  // we can't set force to false since then information for the
605  // sub sub menus is lost.
606  if (recursive)
607  {
608  IContributionItem::Pointer item(src);
610  {
611  item = subItem->GetInnerItem();
612  }
613  if (IMenuManager::Pointer mm = item.Cast<IMenuManager>())
614  {
615  mm->UpdateAll(force);
616  }
617  }
618 
619  }
620 
621  // remove any old menu items not accounted for
622  for (; srcIx < mi.size(); srcIx++)
623  {
624  menu->removeAction(mi[srcIx]);
625  delete mi[srcIx];
626  }
627 
628  this->SetDirty(false);
629  }
630  }
631  else
632  {
633  // I am not dirty. Check if I must recursivly walk down the hierarchy.
634  if (recursive)
635  {
636  foreach (IContributionItem::Pointer ci, this->GetItems())
637  {
638  if (IMenuManager::Pointer mm = ci.Cast<IMenuManager>())
639  {
640  if (mm->IsVisible())
641  {
642  mm->UpdateAll(force);
643  }
644  }
645  }
646  }
647  }
648  this->UpdateMenuItem();
649 }
650 
651 void MenuManager::DumpActionInfo(QMenuProxy* menu)
652 {
653  if (menu->isMenuBar())
654  {
655  //qDebug() << "QMenuBar [" << menu->menuBar << "]";
656  DumpActionInfo(menu->menuBar, 1);
657  }
658  else
659  {
660  //qDebug() << "QMenu [" << menu->menu << "]" << menu->menu;
661  DumpActionInfo(menu->menu, 1);
662  }
663 }
664 
665 void MenuManager::DumpActionInfo(QWidget* widget, int level)
666 {
667  QString indent = " |";
668  for (int i = 0; i < level; ++i) indent += "--";
669  foreach(QAction* action, widget->actions())
670  {
671  qDebug() << qPrintable(indent) << action->text() << "[" << action << "]";
672  if (action->menu())
673  {
674  DumpActionInfo(action->menu(), level+1);
675  }
676  }
677 }
678 
679 }
bool IsDirty() const override
IMenuManager::Pointer FindMenuUsingPath(const QString &path) const override
IContributionItem::Pointer FindUsingPath(const QString &path) const override
bool IsGroupMarker() const override
void DoItemFill(IContributionItem::Pointer ci, QAction *before)
QMenu * GetMenu() const
QList< SmartPointer< IContributionItem > > GetItems() const override
virtual void MarkDirty()=0
Q_SIGNAL void AboutToHide(IMenuManager *mm)
QString GetMenuText() const
void SaveWidgetState() override
void SetCommandId(const QString &definitionId)
bool IsSeparator() const override
berryObjectMacro(MenuManager) private QMenuProxy * menu
bool IsEnabled() const override
IContributionManager * parent
void AddMenuListener(QObject *listener) override
QMenu * CreateContextMenu(QWidget *parent)
bool IsVisible() const override
IContributionManager * GetParent() const
QMenuBar * CreateMenuBar(QWidget *parent)
void UpdateAll(bool force) override
berry::SmartPointer< Self > Pointer
Definition: berryObject.h:88
void MarkDirty() override
SmartPointer< IContributionItem > Find(const QString &id) const override
The custom viewer plugin implements simple viewer functionality presented in a customized look and feel It was developed to demonstrate extensibility and customizability of the blueberry application framework As an example for the GUI customization capabilities provided by the BlueBerry application the custom viewer plugin was developed It features simple viewer functionality presented in a customized look and feel The custom viewer consists of two i e a viewer perspective and a DICOM perspective As part of the viewer an instance of QmitkDataManagerView allows for data selection Visualization of the selected data is then performed by a simple render window view According data can either be directly loaded from file or be imported as DICOM data DICOM import functionality is accessible from the DICOM perspective incorporating the QmitkDicomExternalDataWidget The customization of Qt Stylesheets is used to give the application a non native look and feel This is further emphasized by a Tab Widget like unification of the perspectives with the according perspective bar In addition to an absence of menu
void SetParent(IContributionManager *manager) override
void SetOverrides(SmartPointer< IContributionManagerOverrides > newOverrides)
void RemoveMenuListener(QObject *listener) override
void SetRemoveAllWhenShown(bool removeAll) override
SmartPointer< IContributionManagerOverrides > GetOverrides() override
QString GetId() const override
void Update() override
bool GetRemoveAllWhenShown() const override
SmartPointer< IContributionManagerOverrides > overrides
void SetOverrides(const SmartPointer< IContributionManagerOverrides > &newOverrides)
MenuManager(const QString &text=QString(), const QString &id=QString())
bool IsDynamic() const override
SmartPointer< Other > Cast() const
void Fill(QStatusBar *parent) override
QIcon GetImage() const
void SetVisible(bool visible) override
Q_SIGNAL void AboutToShow(IMenuManager *mm)