VPTissue Reference Manual
PTreeView.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2016 Universiteit Antwerpen
3  *
4  * Licensed under the EUPL, Version 1.1 or as soon they will be approved by
5  * the European Commission - subsequent versions of the EUPL (the "Licence");
6  * You may not use this work except in compliance with the Licence.
7  * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl5
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the Licence is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the Licence for the specific language governing
13  * permissions and limitations under the Licence.
14  */
20 #include "PTreeView.h"
21 
22 #include "MyFindDialog.h"
23 #include "qtmodel/PTreeModel.h"
24 
25 #include <QAction>
26 #include <QApplication>
27 #include <QClipboard>
28 #include <QContextMenuEvent>
29 #include <QDragEnterEvent>
30 #include <QHeaderView>
31 #include <QMenu>
32 #include <QMimeData>
33 
34 namespace SimShell {
35 namespace Gui {
36 
38  : MyTreeView(parent)
39 {
40  m_only_edit_data = false;
41 
42  // performance optimization
43  setUniformRowHeights(true);
44 
45  // drag 'n drop
46  setDragDropMode(QAbstractItemView::DragDrop);
47  setDropIndicatorShown(true);
48  setDefaultDropAction(Qt::MoveAction);
49  setDragEnabled(true);
50  setAcceptDrops(true);
51  setEditTriggers(DoubleClicked | AnyKeyPressed);
52 
53  setAnimated(true);
54 
55  /* actions */
56  m_action_undo = new QAction(QIcon::fromTheme("edit-undo"), "&Undo", this);
57  m_action_redo = new QAction(QIcon::fromTheme("edit-redo"), "&Redo", this);
58  m_action_cut = new QAction(QIcon::fromTheme("edit-cut"), "Cut", this);
59  m_action_copy = new QAction(QIcon::fromTheme("edit-copy"), "Copy", this);
60  m_action_paste = new QAction(QIcon::fromTheme("edit-paste"), "&Paste", this);
61  m_action_move_up = new QAction(QIcon::fromTheme("go-up"), "Move Up", this);
62  m_action_move_down = new QAction(QIcon::fromTheme("go-down"), "Move Down", this);
63  m_action_insert = new QAction(QIcon::fromTheme("list-add"), "Insert", this);
64  m_action_insert_child = new QAction(QIcon::fromTheme("list-add"), "Insert Child", this);
65  m_action_remove = new QAction(QIcon::fromTheme("list-remove"), "Remove", this);
66  m_action_find_dialog = new QAction(QIcon::fromTheme("edit-find"), "&Find...", this);
67  m_action_clear_highlight = new QAction(QIcon::fromTheme("edit-clear"), "&Clear Highlight", this);
68  m_action_expand_all = new QAction(QIcon::fromTheme("list-add"), "Expand All", this);
69  m_action_expand_none = new QAction(QIcon::fromTheme("list-remove"), "Expand None", this);
70 
71  m_action_undo->setShortcut(QKeySequence("Ctrl+Z"));
72  m_action_undo->setShortcutContext(Qt::WidgetShortcut);
73  m_action_undo->setEnabled(false);
74  m_action_redo->setShortcut(QKeySequence("Shift+Ctrl+Z"));
75  m_action_redo->setShortcutContext(Qt::WidgetShortcut);
76  m_action_redo->setEnabled(false);
77  m_action_cut->setShortcut(QKeySequence("Ctrl+X"));
78  m_action_cut->setShortcutContext(Qt::WidgetShortcut);
79  m_action_cut->setEnabled(false);
80  m_action_copy->setShortcut(QKeySequence("Ctrl+C"));
81  m_action_copy->setShortcutContext(Qt::WidgetShortcut);
82  m_action_copy->setEnabled(false);
83  m_action_paste->setShortcut(QKeySequence("Ctrl+V"));
84  m_action_paste->setShortcutContext(Qt::WidgetShortcut);
85  m_action_paste->setEnabled(false);
86  m_action_move_up->setEnabled(false);
87  m_action_move_down->setEnabled(false);
88  m_action_insert->setEnabled(false);
89  m_action_insert_child->setEnabled(false);
90  m_action_remove->setShortcut(QKeySequence("Del"));
91  m_action_remove->setShortcutContext(Qt::WidgetShortcut);
92  m_action_remove->setEnabled(false);
93  m_action_find_dialog->setShortcut(QKeySequence("Ctrl+F"));
94  m_action_find_dialog->setShortcutContext(Qt::WidgetShortcut);
95 
96  // add actions so they can be triggered by shortcut when this view has focus.
97  addAction(m_action_undo);
98  addAction(m_action_redo);
99  addAction(m_action_cut);
100  addAction(m_action_copy);
101  addAction(m_action_paste);
102  addAction(m_action_move_up);
103  addAction(m_action_move_down);
104  addAction(m_action_insert);
105  addAction(m_action_insert_child);
106  addAction(m_action_remove);
107  addAction(m_action_find_dialog);
108  addAction(m_action_clear_highlight);
109 
110  connect(&m_undo_stack, SIGNAL(canUndoChanged(bool)), m_action_undo, SLOT(setEnabled(bool)));
111  connect(&m_undo_stack, SIGNAL(canRedoChanged(bool)), m_action_redo, SLOT(setEnabled(bool)));
112  connect(&m_undo_stack, SIGNAL(cleanChanged(bool)), this, SIGNAL(CleanChanged(bool)));
113  connect(&m_undo_stack, SIGNAL(indexChanged(int)), this, SIGNAL(Edited()));
114  connect(m_action_undo, SIGNAL(triggered()), &m_undo_stack, SLOT(undo()));
115  connect(m_action_redo, SIGNAL(triggered()), &m_undo_stack, SLOT(redo()));
116  connect(m_action_cut, SIGNAL(triggered()), this, SLOT(Cut()));
117  connect(m_action_copy, SIGNAL(triggered()), this, SLOT(Copy()));
118  connect(m_action_paste, SIGNAL(triggered()), this, SLOT(Paste()));
119  connect(m_action_move_up, SIGNAL(triggered()), this, SLOT(MoveUp()));
120  connect(m_action_move_down, SIGNAL(triggered()), this, SLOT(MoveDown()));
121  connect(m_action_insert, SIGNAL(triggered()), this, SLOT(Insert()));
122  connect(m_action_insert_child, SIGNAL(triggered()), this, SLOT(InsertChild()));
123  connect(m_action_remove, SIGNAL(triggered()), this, SLOT(Remove()));
124  connect(m_action_find_dialog, SIGNAL(triggered()), this, SLOT(FindDialog()));
125  connect(m_action_clear_highlight, SIGNAL(triggered()), this, SLOT(ClearHighlight()));
126  connect(m_action_expand_all, SIGNAL(triggered()), this, SLOT(expandAll()));
127  connect(m_action_expand_none, SIGNAL(triggered()), this, SLOT(collapseAll()));
128 
129 
130  QClipboard* clipboard = QApplication::clipboard();
131  connect(clipboard, SIGNAL(dataChanged()), this, SLOT(SetClipboardChanged()));
132 
133  // don't create find dialog until it is shown
134  m_find_dialog = 0;
135 }
136 
137 void PTreeView::ClearHighlight()
138 {
139  if (!model()) {
140  return;
141  }
142  for (QModelIndexList::iterator i = m_results.begin(); i != m_results.end(); i++) {
143  model()->setData(*i, QVariant(), Qt::BackgroundRole);
144  }
145  m_query = "";
146  m_results.clear();
147  m_result = m_results.end();
148  m_action_clear_highlight->setEnabled(false);
149 }
150 
151 void PTreeView::contextMenuEvent(QContextMenuEvent* event)
152 {
153  QMenu menu(this);
154  menu.addAction(m_action_insert);
155  menu.addAction(m_action_insert_child);
156  menu.addSeparator();
157  menu.addAction(m_action_move_up);
158  menu.addAction(m_action_move_down);
159  menu.addSeparator();
160  menu.addAction(m_action_remove);
161  menu.addSeparator();
162  menu.addAction(m_action_cut);
163  menu.addAction(m_action_copy);
164  menu.addAction(m_action_paste);
165  menu.exec(event->globalPos());
166 }
167 
168 void PTreeView::Copy()
169 {
170  if (model()) {
171  QModelIndexList indexes = selectedIndexes();
172  if (indexes.empty()) {
173  return;
174  }
175  QMimeData* mime = model()->mimeData(indexes);
176  QClipboard* clipboard = QApplication::clipboard();
177  clipboard->setMimeData(mime);
178  // ownership of mime is transferred to clipboard, no need to free memory
179  }
180 }
181 
182 void PTreeView::Cut()
183 {
184  if (!model()) {
185  return;
186  }
187 
188  QModelIndexList indexes = selectedIndexes();
189  if (indexes.empty()) {
190  return;
191  }
192  Copy();
193  QModelIndex const & index = indexes.first();
194  model()->removeRow(index.row(), model()->parent(index));
195  selectionModel()->clearSelection();
196  selectionModel()->setCurrentIndex(QModelIndex(), QItemSelectionModel::Clear);
197 }
198 
199 void PTreeView::FindDialog()
200 {
201  if (!m_find_dialog) {
202  m_find_dialog = new MyFindDialog(this);
203  connect(m_find_dialog, SIGNAL(FindNext(QString const &, bool)),
204  this, SLOT(FindNext(QString const &, bool)));
205  }
206  m_find_dialog->show();
207  m_find_dialog->raise();
208  m_find_dialog->activateWindow();
209  m_find_dialog->CorrectFocus();
210 }
211 
212 void PTreeView::FindNext(QString const & new_query, bool match_case)
213 {
214  if (!model()) {
215  return;
216  }
217  Qt::MatchFlags new_flags = Qt::MatchContains | Qt::MatchWrap | Qt::MatchRecursive;
218  if (match_case) {
219  new_flags |= Qt::MatchCaseSensitive;
220  }
221  if (new_query == m_query && new_flags == m_flags && m_result != m_results.end()) {
222  // if same old query ("find next"), jump to next result
223  if (++m_result == m_results.end()) {
224  m_result = m_results.begin();
225  }
226  } else {
227  // un-highlight previous results
228  for (QModelIndexList::iterator i = m_results.begin(); i != m_results.end(); i++) {
229  model()->setData(*i, QVariant(), Qt::BackgroundRole);
230  }
231  if (new_query == "") {
232  m_results.clear();
233  } else {
234  m_results = model()->match(model()->index(0, 0),
235  Qt::DisplayRole, QVariant(new_query), -1, new_flags);
236  }
237  m_result = m_results.begin();
238 
239  m_query = new_query;
240  m_flags = new_flags;
241 
242  // highlight results
243  for (QModelIndexList::iterator i = m_results.begin(); i != m_results.end(); ++i) {
244  model()->setData(*i, QVariant(QBrush(QColor(255, 255, 0))), Qt::BackgroundRole);
245  }
246  }
247 
248  if (m_results.size() == 1) {
249  emit StatusChanged(QString::number(m_results.size()) + " result found");
250  } else {
251  emit StatusChanged(QString::number(m_results.size()) + " results found");
252  }
253 
254  if (m_result != m_results.end()) {
255  selectionModel()->select(*m_result, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
256  scrollTo(*m_result);
257  m_action_clear_highlight->setEnabled(true);
258  } else {
259  m_action_clear_highlight->setEnabled(false);
260  }
261 }
262 
263 QAction* PTreeView::GetUndoAction() const {return m_action_undo;}
264 QAction* PTreeView::GetRedoAction() const {return m_action_redo;}
265 QAction* PTreeView::GetCutAction() const {return m_action_cut;}
266 QAction* PTreeView::GetCopyAction() const {return m_action_copy;}
267 QAction* PTreeView::GetPasteAction() const {return m_action_paste;}
268 QAction* PTreeView::GetMoveUpAction() const {return m_action_move_up;}
269 QAction* PTreeView::GetMoveDownAction() const {return m_action_move_down;}
270 QAction* PTreeView::GetInsertBeforeAction() const {return m_action_insert;}
271 QAction* PTreeView::GetInsertChildAction() const {return m_action_insert_child;}
272 QAction* PTreeView::GetRemoveAction() const {return m_action_remove;}
273 QAction* PTreeView::GetFindDialogAction() const {return m_action_find_dialog;}
274 QAction* PTreeView::GetClearHighlightAction() const {return m_action_clear_highlight;}
275 QAction* PTreeView::GetExpandAllAction() const {return m_action_expand_all;}
276 QAction* PTreeView::GetExpandNoneAction() const {return m_action_expand_none;}
277 
278 
279 void PTreeView::Insert()
280 {
281  QModelIndexList list = selectedIndexes();
282  if (list.size()) {
283  for (auto const & i : list) {
284  if (i.column() == 0) {
285  QModelIndex parent = model()->parent(i);
286  model()->insertRow(i.row(), parent);
287  QModelIndex inserted = model()->index(i.row(), 0, parent);
288  selectionModel()->select(inserted, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
289  selectionModel()->setCurrentIndex(inserted, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
290  }
291  }
292  } else {
293  model()->insertRow(0);
294  QModelIndex inserted = model()->index(0, 0);
295  selectionModel()->select(inserted, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
296  selectionModel()->setCurrentIndex(inserted, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
297  }
298 }
299 
300 void PTreeView::InsertChild()
301 {
302  for (auto const & i : selectedIndexes()) {
303  if (i.column() == 0) {
304  model()->insertRow(0, i);
305  }
306  }
307 }
308 
309 bool PTreeView::IsClean() const
310 {
311  return m_undo_stack.isClean();
312 }
313 
314 bool PTreeView::IsOnlyEditData() const
315 {
316  return m_only_edit_data;
317 }
318 
319 void PTreeView::MoveDown()
320 {
321  for (auto const & i : selectedIndexes()) {
322  if (i.column() == 0) {
323  if (PTreeModel* ptree_model = dynamic_cast<PTreeModel*>(model())) {
324  int row = i.row();
325  QModelIndex parent = ptree_model->parent(i);
326  ptree_model->MoveRow(row, parent, row+1, parent);
327  QModelIndex moved = ptree_model->index(row+1, 0, parent);
328  selectionModel()->select(moved, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
329  selectionModel()->setCurrentIndex(moved, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
330  }
331  }
332  }
333 }
334 
335 void PTreeView::MoveUp()
336 {
337  for (auto const & i : selectedIndexes()) {
338  if (i.column() == 0) {
339  if (PTreeModel* ptree_model = dynamic_cast<PTreeModel*>(model())) {
340  int row = i.row();
341  QModelIndex parent = ptree_model->parent(i);
342  ptree_model->MoveRow(row, parent, row-1, parent);
343  QModelIndex moved = ptree_model->index(row-1, 0, parent);
344  selectionModel()->select(moved, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
345  selectionModel()->setCurrentIndex(moved, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
346  }
347  }
348  }
349 }
350 
351 void PTreeView::Paste()
352 {
353  if (model()) {
354  QClipboard* clipboard = QApplication::clipboard();
355  QMimeData const * mime = clipboard->mimeData();
356 
357  QModelIndexList indexes = selectedIndexes();
358  if (indexes.empty()) {
359  // no selection? paste in root of tree
360  model()->dropMimeData(mime, Qt::CopyAction, 0, 0, QModelIndex());
361  QModelIndex index = model()->index(0,0);
362  selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
363  selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
364  scrollTo(index);
365  } else {
366  // paste before selection
367  QModelIndex index = indexes.first();
368  model()->dropMimeData(mime, Qt::CopyAction, index.row(), index.column(), model()->parent(index));
369  selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
370  selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
371  scrollTo(index);
372  }
373  }
374 }
375 
376 void PTreeView::Remove()
377 {
378  for (auto const & i : selectedIndexes()) {
379  if (i.column() == 0) {
380  model()->removeRow(i.row(), model()->parent(i));
381  }
382  }
383  selectionModel()->clearSelection();
384  selectionModel()->setCurrentIndex(QModelIndex(), QItemSelectionModel::Clear);
385 }
386 
388 {
389  m_undo_stack.setClean();
390 }
391 
392 void PTreeView::SetClipboardChanged()
393 {
394  if (model()) {
395  QClipboard const * clipboard = QApplication::clipboard();
396  if (QMimeData const * mime = clipboard->mimeData())
397  if (mime->hasFormat("text/plain")) {
398  m_action_paste->setEnabled(true);
399  return;
400  }
401  }
402  m_action_paste->setEnabled(false);
403 }
404 
406 {
407  m_undo_stack.clear();
408  MyTreeView::setModel(m);
409 
410  if (m) {
411  if (PTreeModel* ptree_model = dynamic_cast<PTreeModel*>(m)) {
412  // tell PTreeModel to use our undo stack
413  ptree_model->SetUndoStack(&m_undo_stack);
414  }
415 
416  QItemSelectionModel* s = selectionModel();
417  connect(s, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(SetSelectionChanged(const QItemSelection &, const QItemSelection &)));
418 
419  if (!m_only_edit_data) {
420  m_action_insert->setEnabled(true);
421  }
422  } else {
423  m_action_insert->setEnabled(false);
424  }
425 
426  // this will check if clipboard contains pastable data and enable/disable action_paste correspondingly
427  SetClipboardChanged();
428 
429  // selection model doesn't emit selectionChanged() when view is reset, so we have to do it ourselves.
430  SetSelectionChanged(QItemSelection(), QItemSelection());
431 
432  // search was cleared
433  m_action_clear_highlight->setEnabled(false);
434 }
435 
436 void PTreeView::SetOnlyEditData(bool b)
437 {
438  m_only_edit_data = b;
439  if (m_only_edit_data) {
440  setDragDropMode (DragOnly);
441  } else {
442  setDragDropMode (DragDrop);
443  }
444 }
445 
446 void PTreeView::SetSelectionChanged(QItemSelection const &, QItemSelection const &)
447 {
448  if (model() && !selectedIndexes().empty()) {
449  m_action_copy->setEnabled(true);
450  if (!m_only_edit_data) {
451  m_action_cut->setEnabled(true);
452  m_action_paste->setEnabled(true);
453  m_action_move_up->setEnabled(true);
454  m_action_move_down->setEnabled(true);
455  m_action_insert_child->setEnabled(true);
456  m_action_remove->setEnabled(true);
457  } else {
458  m_action_cut->setEnabled(false);
459  m_action_paste->setEnabled(false);
460  m_action_move_up->setEnabled(false);
461  m_action_move_down->setEnabled(false);
462  m_action_insert_child->setEnabled(false);
463  m_action_remove->setEnabled(false);
464  }
465  } else {
466  m_action_cut->setEnabled(false);
467  m_action_copy->setEnabled(false);
468  m_action_paste->setEnabled(false);
469  m_action_move_up->setEnabled(false);
470  m_action_move_down->setEnabled(false);
471  m_action_insert_child->setEnabled(false);
472  m_action_remove->setEnabled(false);
473  }
474 }
475 
476 } // end of namespace Gui
477 } // end of namespace SimShell
see the online Qt documentation
void CorrectFocus()
Sets focus to the text input box so the user can start typing right away.
PTreeView(QWidget *parent=0)
Constructor.
Definition: PTreeView.cpp:37
Interface for MyFindDialog.
void StatusChanged(QString const &message)
Emitted when view has some message to share with, for example, a statusbar.
bool IsClean() const
Test whether the view's internal undo stack is in a clean state.
Definition: PTreeView.cpp:309
virtual void setModel(QAbstractItemModel *model)
Reimplemented from QTreeView, also enables/disables certain actions.
Definition: PTreeView.cpp:405
Qt model reflecting hierarchical structure of a ptree.
Definition: PTreeModel.h:37
virtual void contextMenuEvent(QContextMenuEvent *)
Reimplemented from QWidget.
Definition: PTreeView.cpp:151
see the online Qt documentation
void SetClean()
Set the view's internal undo stack's current state to be the clean state.
Definition: PTreeView.cpp:387
QTreeView with methods to import/export widget layout (x,y,width, ...) in ptree format.
Definition: MyTreeView.h:35
see the online Qt documentation
Interface for PTreeView.
Interface for PTreeModel.
void CleanChanged(bool clean)
Emitted whenever the view's internal undo stack state changes from clean to non-clean, or from non-clean to clean.
Namespace for generic graphical shell for simulators.
Definition: SimSession.h:32
void Edited()
Emitted whenever the data contained in the model changes.