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