Fraxinus  17.12-rc2
An IGT application
cxLayoutInteractor.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 #include "cxLayoutInteractor.h"
34 #include "cxLayoutEditorWidget.h"
35 #include <QMenu>
36 #include <QMessageBox>
37 #include <QDialogButtonBox>
38 #include <QMetaMethod>
39 #include "boost/bind.hpp"
40 #include "libQtSignalAdapters/Qt2Func.h"
41 #include "libQtSignalAdapters/ConnectionFactories.h"
42 #include <QVBoxLayout>
43 #include "cxLayoutRepository.h"
44 #include "cxViewService.h"
45 
46 namespace cx
47 {
48 
49 LayoutInteractor::LayoutInteractor(ViewServicePtr viewService, QObject* parent) :
50  QObject(parent),
51  mViewService(viewService)
52 {
53  mSecondaryLayoutActionGroup = NULL;
54  mLayoutActionGroup = NULL;
55  this->createActions();
56  connect(viewService.get(), SIGNAL(activeLayoutChanged()), this, SLOT(layoutChangedSlot()));
57 }
58 
59 LayoutRepositoryPtr LayoutInteractor::getRepo()
60 {
61  return mViewService->getLayoutRepository();
62 }
63 
64 void LayoutInteractor::createActions()
65 {
66  mNewLayoutAction = new QAction(tr("New Layout"), this);
67  mNewLayoutAction->setToolTip("Create a new Custom Layout");
68  connect(mNewLayoutAction, SIGNAL(triggered()), this, SLOT(newCustomLayoutSlot()));
69  mEditLayoutAction = new QAction(tr("Edit Layout"), this);
70  mEditLayoutAction->setToolTip("Edit the current Custom Layout");
71  connect(mEditLayoutAction, SIGNAL(triggered()), this, SLOT(editCustomLayoutSlot()));
72  mDeleteLayoutAction = new QAction(tr("Delete Layout"), this);
73  mDeleteLayoutAction->setToolTip("Delete the current Custom Layout");
74  connect(mDeleteLayoutAction, SIGNAL(triggered()), this, SLOT(deleteCustomLayoutSlot()));
75 }
76 
78 {
79  mMenu = menu;
80 
81  mMenu->addAction(mNewLayoutAction);
82  mMenu->addAction(mEditLayoutAction);
83  mMenu->addAction(mDeleteLayoutAction);
84 
85  mSecondaryLayoutMenu = new QMenu("Secondary Layout", mMenu);
86  mMenu->addMenu(mSecondaryLayoutMenu);
87 
88  mMenu->addSeparator();
89 
90  this->layoutChangedSlot();
91 }
92 
93 
94 void LayoutInteractor::newCustomLayoutSlot()
95 {
96  LayoutData data = this->executeLayoutEditorDialog("New Custom Layout", true);
97  if (data.getUid().isEmpty())
98  return;
99  this->getRepo()->insert(data);
100  mViewService->setActiveLayout(data.getUid());
101 }
102 
103 void LayoutInteractor::editCustomLayoutSlot()
104 {
105  LayoutData data = this->executeLayoutEditorDialog("Edit Current Layout", false);
106  if (data.getUid().isEmpty())
107  return;
108  this->getRepo()->insert(data);
109 }
110 
111 void LayoutInteractor::deleteCustomLayoutSlot()
112 {
113  if (QMessageBox::question(NULL, "Delete current layout", "Do you really want to delete the current layout?",
114  QMessageBox::Cancel | QMessageBox::Ok) != QMessageBox::Ok)
115  return;
116  this->getRepo()->erase(mViewService->getActiveLayout());
117  mViewService->setActiveLayout(this->getRepo()->getAvailable().front()); // revert to existing state
118 }
119 
122 void LayoutInteractor::layoutChangedSlot()
123 {
124  if (!mMenu)
125  return;
126  if (!this->getRepo())
127  return;
128  // reset list of available layouts
129 
130  this->deepDeleteActionGroup(mLayoutActionGroup);
131  mLayoutActionGroup = this->createLayoutActionGroup(0);
132  mMenu->addActions(mLayoutActionGroup->actions());
133 
134  this->deepDeleteActionGroup(mSecondaryLayoutActionGroup);
135  mSecondaryLayoutActionGroup = this->createLayoutActionGroup(1);
136  mSecondaryLayoutMenu->addActions(mSecondaryLayoutActionGroup->actions());
137 
138  bool editable = this->getRepo()->isCustom(mViewService->getActiveLayout());
139  mEditLayoutAction->setEnabled(editable);
140  mDeleteLayoutAction->setEnabled(editable);
141 }
142 
143 void LayoutInteractor::deepDeleteActionGroup(QActionGroup* actionGroup)
144 {
145  //Make sure all actions in the group are deleted - possibly removes a few memory leaks
146  if (actionGroup)
147  {
148  QList<QAction*> actionList = actionGroup->actions();
149  for (int i = 0; i < actionList.size(); i++)
150  delete actionList.at(i);
151  }
152 
153  delete actionGroup;
154 }
155 
159 LayoutData LayoutInteractor::executeLayoutEditorDialog(QString title, bool createNew)
160 {
161  boost::shared_ptr<QDialog> dialog(new QDialog(NULL, Qt::Dialog));
162  dialog->setWindowTitle(title);
163  QVBoxLayout* layout = new QVBoxLayout(dialog.get());
164  layout->setMargin(0);
165 
166  LayoutEditorWidget* editor = new LayoutEditorWidget(mViewService, dialog.get());
167 
168  LayoutData data = this->getRepo()->get(mViewService->getActiveLayout());
169 
170  if (createNew)
171  {
172  data.resetUid(this->getRepo()->generateUid());
173  }
174  editor->setLayoutData(data);
175  layout->addWidget(editor);
176 
177  QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
178  connect(buttonBox, SIGNAL(accepted()), dialog.get(), SLOT(accept()));
179  connect(buttonBox, SIGNAL(rejected()), dialog.get(), SLOT(reject()));
180  layout->addWidget(buttonBox);
181 
182  if (!dialog->exec())
183  return LayoutData();
184 
185  return editor->getLayoutData();
186 }
187 
188 QActionGroup* LayoutInteractor::createLayoutActionGroup(int widgetIndex)
189 {
190  QActionGroup* retval = new QActionGroup(this);
191  retval->setExclusive(true);
192  if (!this->getRepo())
193  return retval;
194 
195 
196  // add default layouts
197  std::vector<QString> layouts = this->getRepo()->getAvailable();
198  int defaultLayouts = 0;
199  for (unsigned i = 0; i < layouts.size(); ++i)
200  {
201  if (!this->getRepo()->isCustom(layouts[i]))
202  {
203  this->addLayoutAction(layouts[i], retval, widgetIndex);
204  defaultLayouts++;
205  }
206  }
207 
208  // add separator
209  QAction* sep = new QAction(retval);
210  sep->setSeparator(true);
211  //retval->addAction(sep);
212 
213  if (defaultLayouts != layouts.size())
214  {
215  QAction* action = new QAction("Custom", retval);
216  action->setEnabled(false);
217  }
218 
219  // add custom layouts
220  for (unsigned i = 0; i < layouts.size(); ++i)
221  {
222  if (this->getRepo()->isCustom(layouts[i]))
223  this->addLayoutAction(layouts[i], retval, widgetIndex);
224  }
225 
226  // set checked status
227  QString type = mViewService->getActiveLayout(widgetIndex);
228  QList<QAction*> actions = retval->actions();
229  for (int i = 0; i < actions.size(); ++i)
230  {
231  if (actions[i]->data().toString() == type)
232  actions[i]->setChecked(true);
233  }
234 
235  return retval;
236 }
237 
240 QAction* LayoutInteractor::addLayoutAction(QString layout, QActionGroup* group, int widgetIndex)
241 {
242  LayoutData data = this->getRepo()->get(layout);
243  if (data.isEmpty())
244  {
245  QAction* sep = new QAction(group);
246  sep->setSeparator(true);
247  }
248  QAction* action = new QAction(data.getName(), group);
249  action->setEnabled(!data.isEmpty());
250  action->setCheckable(!data.isEmpty());
251  action->setData(QVariant(layout));
252 
253 // std::cout << "** " << layout << "-" << widgetIndex << std::endl;
254  QtSignalAdapters::connect0<void()>(
255  action,
256  SIGNAL(triggered()),
257  boost::bind(&LayoutInteractor::setActiveLayout, this, layout, widgetIndex));
258 
259  return action;
260 }
261 
262 void LayoutInteractor::setActiveLayout(QString layout, int widgetIndex)
263 {
264 // std::cout << "*slot* " << layout << "-" << widgetIndex << std::endl;
265  mViewService->setActiveLayout(layout, widgetIndex);
266 }
267 
268 } // namespace cx
269 
boost::shared_ptr< class LayoutRepository > LayoutRepositoryPtr
void connectToMenu(QMenu *menu)
boost::shared_ptr< class ViewService > ViewServicePtr
QString getUid() const
Definition: cxLayoutData.h:109
LayoutData getLayoutData() const
LayoutInteractor(ViewServicePtr viewService, QObject *parent=NULL)
QString getName() const
Definition: cxLayoutData.h:110
void setLayoutData(const LayoutData &data)
bool isEmpty() const
Definition: cxLayoutData.h:123
Namespace for all CustusX production code.