Fraxinus  16.5.0-fx-rc1
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxLayoutEditorWidget.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 #include "cxLayoutEditorWidget.h"
33 #include <iostream>
34 #include <QtWidgets>
35 
36 #include "cxTypeConversions.h"
37 #include "cxDefinitionStrings.h"
38 #include "cxUtilHelpers.h"
39 #include "cxViewService.h"
40 
41 namespace cx
42 {
43 
44 
46  QWidget(parent)
47 {
48  mTopLayout = new QVBoxLayout(this);
49  QHBoxLayout* nameLayout = new QHBoxLayout;
50  mTopLayout->addLayout(nameLayout);
51  mRCLayout = new QHBoxLayout;
52  mTopLayout->addLayout(mRCLayout);
53  mLayout = new QGridLayout;
54  mLayout->setMargin(0);
55  mLayout->setSpacing(2);
56  mTopLayout->addLayout(mLayout);
57 
58  mNameEdit = new QLineEdit;
59  connect(mNameEdit, SIGNAL(editingFinished()), this, SLOT(nameChanged()));
60  nameLayout->addWidget(new QLabel("Name"));
61  nameLayout->addWidget(mNameEdit);
62 
63  // create the row/column bar
64  mRowsEdit = new QSpinBox;
65  mRowsEdit->setRange(1,10);
66  mColsEdit = new QSpinBox;
67  mColsEdit->setRange(1,10);
68  connect(mRowsEdit, SIGNAL(valueChanged(int)), this, SLOT(rowsColumnsChangedSlot()));
69  connect(mColsEdit, SIGNAL(valueChanged(int)), this, SLOT(rowsColumnsChangedSlot()));
70  mRCLayout->addWidget(new QLabel("Rows"));
71  mRCLayout->addWidget(mRowsEdit);
72  mRCLayout->addWidget(new QLabel("Columns"));
73  mRCLayout->addWidget(mColsEdit);
74  mRCLayout->addStretch();
75 
76  for (int i=ptNOPLANE+1; i<ptCOUNT; ++i)
77  {
78  PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
79  mPlaneNames[type] = qstring_cast(type);
80  mViewNames.push_back(ViewNamesType(type,View::VIEW_2D, qstring_cast(type)));
81  }
82  mViewNames.push_back(ViewNamesType(ptNOPLANE,View::VIEW_3D, "3D"));
83  mViewNames.push_back(ViewNamesType(ptNOPLANE,View::VIEW_REAL_TIME, "RT"));
84 // mPlaneNames[ptNOPLANE] = "3D";
85 // mPlaneNames[static_cast<PLANE_TYPE>(-1)] = "RT";
86 
87  mSelection = LayoutRegion(-1,-1);
88  initCache();
89 
90  this->updateGrid();
91 }
92 
94 {
95  mViewData = data;
96  this->updateGrid();
97 }
98 
100 {
101  return mViewData;
102 }
103 
104 void LayoutEditorWidget::nameChanged()
105 {
106  mViewData.setName(mNameEdit->text());
107 }
108 
109 void LayoutEditorWidget::contextMenuSlot(const QPoint& point)
110 {
111  //QWidget* sender = dynamic_cast<QWidget*>(this->sender());
112  QPoint pointGlobal = this->mapToGlobal(point);
113  QMenu menu(this);
114 
115  LayoutViewData viewData = this->getViewData(point);
116 
117  QAction* mergeAction = new QAction("merge view", &menu);
118  mergeAction->setEnabled(this->getSelectedViews().size()>1);
119  connect(mergeAction, SIGNAL(triggered()), this, SLOT(mergeActionSlot()));
120  menu.addAction(mergeAction);
121 
122  QAction* splitAction = new QAction("split view", &menu);
123  splitAction->setEnabled(mSelection.span.row!=1 || mSelection.span.col!=1);
124  connect(splitAction, SIGNAL(triggered()), this, SLOT(splitActionSlot()));
125  menu.addAction(splitAction);
126 
127  menu.addSeparator();
128 
129  // actions for view group
130 // int viewGroupCount = static_cast<int>(viewService()->getViewGroups().size());
131  int viewGroupCount = viewService()->groupCount();
132  QActionGroup* groupActions = new QActionGroup(this);
133  for (int i=0; i<viewGroupCount; ++i)
134  {
135  QAction* action = new QAction(QString("Group %1").arg(i), groupActions);
136  action->setData(QVariant(i));
137  action->setCheckable(true);
138  connect(action, SIGNAL(triggered()), this, SLOT(groupActionSlot()));
139  action->setChecked(viewData.mGroup==i);
140 // menu.addAction(action);
141  }
142 
143  //menu.addMenu("View Group")->addActions(groupActions->actions());
144  menu.addActions(groupActions->actions());
145  menu.addSeparator();
146 
147  // actions for view type
148  QActionGroup* typeActions = new QActionGroup(this);
149 // for (std::map<PLANE_TYPE, QString>::iterator iter=mPlaneNames.begin(); iter!=mPlaneNames.end(); ++iter)
150  for (unsigned i=0; i<mViewNames.size(); ++i)
151  {
152  ViewNamesType current = mViewNames[i];
153 // PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
154 // PLANE_TYPE type = iter->first;
155 // QString name = iter->second;
156 
157  QAction* action = new QAction(QString("%1").arg(current.mName), typeActions);
158 // action->setData(QVariant(t));
159  action->setCheckable(true);
160  connect(action, SIGNAL(triggered()), this, SLOT(typeActionSlot()));
161  action->setChecked(viewData.mPlane==current.mPlane && viewData.mType==current.mView);
162  }
163 // for (int i=ptNOPLANE; i<ptCOUNT; ++i)
164 // {
165 // PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
166 //
167 // QAction* action = new QAction(QString("%1").arg(mPlaneNames[type]), typeActions);
168 // action->setData(QVariant(i));
169 // action->setCheckable(true);
170 // connect(action, SIGNAL(triggered()), this, SLOT(typeActionSlot()));
171 // action->setChecked(viewData.mPlane==type);
172 // //menu.addAction(action);
173 // }
174 
175  //menu.addMenu("View Plane Type")->addActions(typeActions->actions());
176  menu.addActions(typeActions->actions());
177 
178  menu.exec(pointGlobal);
179 }
180 
181 void LayoutEditorWidget::splitActionSlot()
182 {
183  mViewData.split(mSelection);
184  this->updateGrid();
185 }
186 
187 void LayoutEditorWidget::mergeActionSlot()
188 {
189  mViewData.merge(mSelection);
190  this->updateGrid();
191 }
192 
193 void LayoutEditorWidget::groupActionSlot()
194 {
195  QAction* sender = dynamic_cast<QAction*>(this->sender());
196  if (!sender)
197  return;
198  int group = sender->data().toInt();
199 
200  std::set<LayoutData::iterator> selection = this->getSelectedViews();
201  for (std::set<LayoutData::iterator>::iterator iter=selection.begin(); iter!=selection.end(); ++iter)
202  (*iter)->mGroup = group;
203 
204  this->updateGrid();
205 }
206 
207 void LayoutEditorWidget::typeActionSlot()
208 {
209  QAction* sender = dynamic_cast<QAction*>(this->sender());
210  if (!sender)
211  return;
212 // PLANE_TYPE type = static_cast<PLANE_TYPE>(sender->data().toInt());
213  ViewNamesType type;
214  for (unsigned i=0; i<mViewNames.size(); ++i)
215  if (mViewNames[i].mName == sender->text())
216  type=mViewNames[i];
217 
218  std::set<LayoutData::iterator> selection = this->getSelectedViews();
219  for (std::set<LayoutData::iterator>::iterator iter=selection.begin(); iter!=selection.end(); ++iter)
220  {
221  (*iter)->mPlane = type.mPlane;
222  (*iter)->mType = type.mView;
223  }
224 
225  this->updateGrid();
226 }
227 
228 void LayoutEditorWidget::mouseMoveEvent(QMouseEvent* event)
229 {
230  this->updateSelection(event->pos());
231 }
232 
233 void LayoutEditorWidget::updateSelection(QPoint pos)
234 {
235  LayoutViewData start = this->getViewData(mClickPos);
236  LayoutViewData stop = this->getViewData(pos);
237  mSelection = merge(start.mRegion, stop.mRegion);
238  this->colorRegion(mSelection, "dimgrey", "lightgrey");
239 }
240 
241 /* Return a set of unique iterators into the layout data,
242  * representing the selected region.
243  */
244 std::set<LayoutData::iterator> LayoutEditorWidget::getSelectedViews()
245 {
246  std::set<LayoutData::iterator> retval;
247  for (int r=mSelection.pos.row; r<mSelection.pos.row+mSelection.span.row; ++r)
248  for (int c=mSelection.pos.col; c<mSelection.pos.col+mSelection.span.col; ++c)
249  retval.insert(mViewData.find(LayoutPosition(r,c)));
250  return retval;
251 }
252 
253 void LayoutEditorWidget::mousePressEvent(QMouseEvent* event)
254 {
255  mClickPos = event->pos();
256 
257  if (event->button()==Qt::RightButton)
258  {
259  // reselect if click is outside old selection
260  if (!mSelection.contains(this->getViewData(mClickPos).mRegion.pos))
261  this->updateSelection(event->pos());
262 
263  //std::cout << "mouse press context" << std::endl;
264  this->contextMenuSlot(event->pos());
265  }
266  else
267  {
268  //std::cout << "mouse press clean" << std::endl;
269  this->updateSelection(event->pos());
270  }
271 }
272 
273 void LayoutEditorWidget::colorRegion(LayoutRegion region, QString selectColor, QString backColor)
274 {
275  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
276  {
277  LayoutPosition pos = iter->mRegion.pos;
278  QString color;
279 
280  if (region.contains(pos))
281  color = selectColor;
282  else
283  color = backColor;
284 
285  mViewDataCache[pos.row][pos.col].mFrame->setStyleSheet(QString("QFrame { background-color: %1 }").arg(color));
286  }
287 }
288 
292 LayoutViewData LayoutEditorWidget::getViewData(QPoint pt)
293 {
294  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
295  {
296  LayoutPosition pos = iter->mRegion.pos;
297  if (!mViewDataCache[pos.row][pos.col].mFrame->geometry().contains(pt))
298  continue;
299 
300  return *iter;
301  }
302 
303  return LayoutViewData();
304 }
305 
309 void LayoutEditorWidget::rowsColumnsChangedSlot()
310 {
311  mViewData.resize(mRowsEdit->value(), mColsEdit->value());
312  this->setSaneGroupIDs();
313  this->updateGrid();
314 }
315 
316 QString LayoutEditorWidget::getViewName(LayoutViewData data) const
317 {
318  for (unsigned i=0; i<mViewNames.size(); ++i)
319  {
320  if (mViewNames[i].mPlane==data.mPlane && mViewNames[i].mView==data.mType)
321  return mViewNames[i].mName;
322  }
323  return "NA";
324 }
325 
329 void LayoutEditorWidget::setSaneGroupIDs()
330 {
331  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
332  {
333  if (iter->mGroup<0)
334  iter->mGroup = 0;
335  }
336 }
337 
342 void LayoutEditorWidget::updateGrid()
343 {
344  //std::cout << "pre update:" << streamXml2String(mViewData) << std::endl;
345 
346  this->clearDisplay();
347 
348  //std::cout << "update start" << std::endl;
349  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
350  {
351  LayoutRegion region = iter->mRegion;
352  GridElement gridData = mViewDataCache[region.pos.row][region.pos.col];
353 
354  // add and show frame in correct position
355  mLayout->addWidget(gridData.mFrame, region.pos.row, region.pos.col, region.span.row, region.span.col);
356  gridData.mFrame->show();
357 
358  // set view text
359  QString name = this->getViewName(*iter);
360  if (iter->mGroup<0 && name.isEmpty())
361  gridData.mLabel->setText("NA");
362  else
363  gridData.mLabel->setText(QString("%1/%2").arg(iter->mGroup).arg(name));
364  }
365 
366  mNameEdit->setText(mViewData.getName());
367 // mRowsEdit->setText(qstring_cast(mViewData.size().row));
368 // mColsEdit->setText(qstring_cast(mViewData.size().col));
369 
370  mRowsEdit->blockSignals(true);
371  mRowsEdit->setValue(mViewData.size().row);
372  mRowsEdit->blockSignals(false);
373 
374  mColsEdit->blockSignals(true);
375  mColsEdit->setValue(mViewData.size().col);
376  mColsEdit->blockSignals(false);
377 
378 // this->colorRegion(LayoutRegion(-1,-1,1,1), "lightgrey", "lightgrey");
379  this->colorRegion(mSelection, "dimgrey", "lightgrey");
380 
381  //this->updateGeometry();
382  //QSize msize = mLayout->minimumSize();
383 // QSize rsize = mGridWidget->size();
384  //std::cout << "minsize: " << msize.width() << "," << msize.height() << std::endl;
385 // std::cout << "real size: " << rsize.width() << "," << rsize.height() << std::endl;
386 
387 // QString color("QFrame { background-color: red }");
388 // mViewData[0][0].mFrame->setStyleSheet(color);
389 
390 // mGridWidget->resize(msize);
391 // this->resize(400,700);
392 // QTimer::singleShot(0, this, SLOT(setNiceSize()));
393 
394  // std::cout << "post update:" << streamXml2String(mViewData) << std::endl;
395 
396 }
397 
398 //void LayoutEditorWidget::setNiceSize()
399 //{
400 // //this->resize(400,700);
401 //}
402 
403 void LayoutEditorWidget::clearDisplay()
404 {
405  for (unsigned r = 0; r < mViewDataCache.size(); ++r)
406  {
407  for (unsigned c = 0; c < mViewDataCache[r].size(); ++c)
408  {
409  mViewDataCache[r][c].mFrame->hide();
410  }
411  }
412 }
413 
414 void LayoutEditorWidget::initCache()
415 {
416  int maxRows = 10;
417  int maxCols = 10;
418  mViewDataCache.resize(maxRows);
419 
420  for (int r = 0; r < maxRows; ++r)
421  {
422  mViewDataCache[r].resize(maxCols);
423 
424  for (int c = 0; c < maxCols; ++c)
425  {
426  QFrame* frame = new QFrame(this);
427  //frame->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
428  frame->setFrameStyle(QFrame::Panel | QFrame::Sunken);
429  frame->setLineWidth(3);
430  frame->setLayout(new QVBoxLayout);
431  QLabel* label = new QLabel("NA", frame);
432  frame->layout()->addWidget(label);
433 
434  mViewDataCache[r][c].mFrame = frame;
435  mViewDataCache[r][c].mLabel = label;
436  }
437  }
438 }
439 
440 
441 } // namespace cx
QString qstring_cast(const T &val)
ViewDataContainer::iterator iterator
Definition: cxLayoutData.h:101
bool contains(LayoutPosition p) const
Definition: cxLayoutData.h:69
LayoutPosition span
size of region
Definition: cxLayoutData.h:67
bool merge(LayoutRegion region)
iterator begin()
Definition: cxLayoutData.h:115
void setName(const QString &name)
Definition: cxLayoutData.h:110
LayoutData getLayoutData() const
iterator find(LayoutPosition pos)
void split(iterator iter)
void resize(int rows, int cols)
LayoutPosition size() const
Definition: cxLayoutData.h:126
ptNOPLANE
a initial plane, if no yet set
Definition: cxDefinitions.h:56
QString getName() const
Definition: cxLayoutData.h:109
void setLayoutData(const LayoutData &data)
cxLogicManager_EXPORT ViewServicePtr viewService()
LayoutPosition pos
start position of region
Definition: cxLayoutData.h:66
iterator end()
Definition: cxLayoutData.h:116
LayoutEditorWidget(QWidget *parent)
LayoutRegion merge(LayoutRegion a, LayoutRegion b)