VPTissue Reference Manual
WorkspaceQtModel.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 "WorkspaceQtModel.h"
21 
22 #include "util/misc/Exception.h"
23 #include "workspace/IWorkspace.h"
24 #include "workspace/IProject.h"
25 #include "workspace/IFile.h"
26 
27 #include <boost/property_tree/ptree.hpp>
28 #include <boost/property_tree/xml_parser.hpp>
29 #include <QDir>
30 #include <QIcon>
31 #include <QFont>
32 #include <functional>
33 
34 namespace SimShell {
35 namespace Gui {
36 
37 using namespace std;
38 using namespace boost::property_tree;
39 using namespace boost::property_tree::xml_parser;
40 using namespace SimPT_Sim::Util;
41 using namespace Ws;
42 using namespace Ws::Event;
43 
44 //--------------------------------------------------------------------------
45 // QAbstractItemModel methods at the end of this file.
46 //--------------------------------------------------------------------------
47 
48 shared_ptr<WorkspaceQtModel> WorkspaceQtModel::Create(const shared_ptr<IWorkspace>& w,
49  Controller::ProjectController* c,
50  QObject* parent)
51 {
52  auto result = shared_ptr<WorkspaceQtModel>(new WorkspaceQtModel(w, c, parent));
53 
54  // Register model with its base class workspace.
55  auto handler = bind(&WorkspaceQtModel::ListenWorkspaceEvent, result.get(), _1);
56  w->Subject<Ws::Event::WorkspaceChanged, std::weak_ptr<const void>>::Register(result, handler);
57 
58  // Register model with all projects in its base class workspace.
59  auto it = result->m_root->workspace.children.begin(); // Children in m_root->workspace.children should be in the same order.
60  for (auto& project : *w) {
61  auto handler = bind(&WorkspaceQtModel::ListenProjectEvent, result.get(), *it, _1);
62  project.second->Subject<Ws::Event::ProjectChanged, std::weak_ptr<const void>>::Register(result, handler);
63  it++;
64  }
65 
66  return result;
67 }
68 
69 WorkspaceQtModel::WorkspaceQtModel(const shared_ptr<IWorkspace>& w,
70  Controller::ProjectController* c,
71  QObject* parent)
72  : QAbstractItemModel(parent),
73  m_workspace(w),
74  m_project_controller(c)
75 {
76  // Construct Item tree from Workspace-inherited protected members.
77  m_root = new Item();
78  for (auto& project : *m_workspace) {
79  auto project_item = new Item(m_root->workspace.children.size(), project.first, project.second.Project());
80  for (auto& file : *(project.second)) {
81  auto file_item = new Item(project_item->project.children.size(), file.first, project_item, file.second);
82  project_item->project.children.push_back(file_item);
83  }
84  m_root->workspace.children.push_back(project_item);
85  }
86 }
87 
88 WorkspaceQtModel::~WorkspaceQtModel()
89 {
90  if (m_root) {
91  delete m_root;
92  }
93 }
94 
95 bool WorkspaceQtModel::Close()
96 {
97  return m_project_controller->PromptClose();
98 }
99 
100 bool WorkspaceQtModel::Close(const QModelIndex& index)
101 {
102  if (!index.isValid()) {
103  throw Exception("WorkspaceQtModel::Close> Invalid index given!");
104  }
105 
106  if (IsOpened(index)) {
107  return m_project_controller->PromptClose();
108  }
109 
110  return (bool) m_project_controller;
111 }
112 
113 void WorkspaceQtModel::ListenWorkspaceEvent(const Ws::Event::WorkspaceChanged& e)
114 {
115  switch (e.GetType())
116  {
117  case Ws::Event::WorkspaceChanged::Type::ProjectAdded:
118  {
119  // Get data from event object.
120  const string name = e.GetName();
121 
122  // Get iterator to added project.
123  auto project_it = m_workspace->Find(name);
124 
125  // Insert new row.
126  int row = FindProjectRowInsert(project_it);
127  insertRow(row);
128 
129  // Create new project item.
130  Item* project_item = new Item(row, project_it->first, project_it->second.Project());
131  m_root->workspace.children[row] = project_item;
132 
133  // Register added project with this.
134  project_it->second->Subject<Ws::Event::ProjectChanged, std::weak_ptr<const void>>::Register(shared_from_this(), bind(&WorkspaceQtModel::ListenProjectEvent, this, project_item, _1));
135 
136  // Add children (files) to project item.
137  for (auto& file : *(project_it->second)) {
138  auto file_item = new Item(project_item->project.children.size(), file.first, project_item, file.second);
139  project_item->project.children.push_back(file_item);
140  }
141 
142  // Emit dataChanged() signal.
143  auto index = createIndex(row, 0, project_item);
144  emit dataChanged(index, index);
145 
146  break;
147  }
148  case Ws::Event::WorkspaceChanged::Type::ProjectRenamed:
149  {
150  // Get data from event object.
151  const string old_name = e.GetOldName();
152  const string new_name = e.GetNewName();
153 
154  // No need to unregister this from renamed project: the shared_ptr pointing to it was already reset, destructing the (inherited) subject.
155 
156  // Remove old row.
157  int old_row = FindProjectRow(old_name);
158  removeRow(old_row);
159 
160  // Insert new row.
161  auto new_it = m_workspace->Find(new_name);
162  int new_row = FindProjectRowInsert(new_it);
163  insertRow(new_row);
164 
165  // Create project item in new row
166  Item* new_item = new Item(new_row, new_it->first, new_it->second.Project());
167  m_root->workspace.children[new_row] = new_item;
168 
169  // Register renamed project with this.
170  new_it->second->Subject<Ws::Event::ProjectChanged, std::weak_ptr<const void>>::Register(shared_from_this(), bind(&WorkspaceQtModel::ListenProjectEvent, this, new_item, _1));
171 
172  // Add children (files) to project item.
173  for (auto& file : *(new_it->second)) {
174  auto file_item = new Item(new_item->project.children.size(), file.first, new_item, file.second);
175  new_item->project.children.push_back(file_item);
176  }
177 
178  // Emit dataChanged() signal.
179  auto new_index = createIndex(new_row, 0, new_item);
180  emit dataChanged(new_index, new_index);
181 
182  break;
183  }
184  case Ws::Event::WorkspaceChanged::Type::ProjectRemoved:
185  {
186  // Get data from event object.
187  string name = e.GetName();
188 
189  // No need to unregister this from removed project: the shared_ptr pointing to it was already reset, destructing the (inherited) subject.
190 
191  // Remove row.
192  int row = FindProjectRow(name);
193  removeRow(row);
194 
195  break;
196  }
197  default:
198  break;
199  }
200 }
201 
202 void WorkspaceQtModel::ListenProjectEvent(Item* project_item, const Ws::Event::ProjectChanged& e)
203 {
204  switch (e.GetType())
205  {
206  case Ws::Event::ProjectChanged::Type::LeafAdded:
207  {
208  // Get data from event object.
209  const string name = e.GetName();
210 
211  // Get iterator to added file.
212  auto file_it = project_item->project.obj->Find(name);
213 
214  // Insert new row.
215  int row = FindFileRowInsert(project_item, file_it);
216  insertRow(row, index(project_item->row, 0));
217 
218  // Create new project item.
219  Item* file_item = new Item(row, file_it->first, project_item, file_it->second);
220  project_item->project.children[row] = file_item;
221 
222  // Emit dataChanged() signal.
223  auto index = createIndex(row, 0, file_item);
224  emit dataChanged(index, index);
225 
226  break;
227  }
228  case Ws::Event::ProjectChanged::Type::LeafRemoved:
229  {
230  // Get data from event object.
231  const string name = e.GetName();
232 
233  // Remove row.
234  int row = FindFileRow(project_item, name);
235  removeRow(row, index(project_item->row, 0));
236 
237  break;
238  }
239  default:
240  break;
241  }
242 }
243 
244 vector<QAction*> WorkspaceQtModel::GetContextMenuActions(const QModelIndex& index) const
245 {
246  if (!index.isValid()) {
247  throw Exception("WorkspaceQtModel::GetContextMenuActions> Invalid index given!");
248  }
249  Item* item = static_cast<Item*>(index.internalPointer());
250  switch (item->type)
251  {
252  case ProjectType:
253  return item->project.obj->GetContextMenuActions();
254  case FileType:
255  return item->file.obj->GetContextMenuActions();
256  default:
257  throw Exception("WorkspaceQtModel::GetContextMenuActions() should only be called on items of ProjectType or FileType.");
258  }
259 }
260 
261 string const& WorkspaceQtModel::GetName(QModelIndex const& index) const
262 {
263  if (!index.isValid()) {
264  throw Exception("WorkspaceQtModel::GetName> Invalid index given!");
265  }
266  Item* item = static_cast<Item*>(index.internalPointer());
267  switch (item->type)
268  {
269  case ProjectType:
270  return item->project.name;
271  case FileType:
272  return item->file.name;
273  default:
274  throw Exception("WorkspaceQtModel::GetName() should only be called on items of ProjectType or FileType.");
275  }
276 }
277 
278 WorkspaceQtModel::ItemType WorkspaceQtModel::GetType(QModelIndex const& index) const
279 {
280  if (!index.isValid()) {
281  throw Exception("WorkspaceQtModel::GetType> Invalid index given!");
282  }
283  return static_cast<Item*>(index.internalPointer())->type;
284 }
285 
286 int WorkspaceQtModel::FindProjectRow(const string& name)
287 {
288  auto& children = m_root->workspace.children;
289  for (auto it = children.begin(); it != children.end(); it++) {
290  if ((*it)->project.name == name) {
291  return it - children.begin();
292  }
293  }
294  return -1;
295 }
296 
297 int WorkspaceQtModel::FindProjectRowInsert(IWorkspace::ConstProjectIterator project_it)
298 {
299  auto next_it = project_it;
300  next_it++;
301  if (next_it == m_workspace->end()) {
302  return m_root->workspace.children.size();
303  } else {
304  return FindProjectRow(next_it->first);
305  }
306 }
307 
308 int WorkspaceQtModel::FindFileRow(Item* project_item, const string& name)
309 {
310  auto& children = project_item->project.children;
311  for (auto it = children.begin(); it != children.end(); it++) {
312  if ((*it)->file.name == name) {
313  return it - children.begin();
314  }
315  }
316  return -1;
317 }
318 
319 int WorkspaceQtModel::FindFileRowInsert(Item* project_item, IProject::ConstFileIterator file_it)
320 {
321  auto next_it = file_it;
322  next_it++;
323  if (next_it == project_item->project.obj->end()) {
324  return project_item->project.children.size();
325  } else {
326  return FindFileRow(project_item, next_it->first);
327  }
328 }
329 
330 bool WorkspaceQtModel::IsOpened(const QModelIndex& index) const
331 {
332  Item* item = static_cast<Item*>(index.internalPointer());
333  switch (item->type) {
334  case ProjectType:
335  if (item->project.obj->IsOpened()) {
336  return true;
337  }
338  break;
339  case FileType:
340  if (item->file.parent->project.obj->IsOpened()) {
341  return true;
342  }
343  break;
344  default:
345  break;
346  }
347 
348  return false;
349 }
350 
351 bool WorkspaceQtModel::Open(const QModelIndex& index)
352 {
353  if (!index.isValid()) {
354  throw Exception("WorkspaceQtModel::Open> Invalid index given!");
355  }
356 
357  if (!m_project_controller->PromptClose())
358  return false;
359 
360  Item* item = static_cast<Item*>(index.internalPointer());
361  switch (item->type) {
362  case ProjectType:
363  {
364  Ws::IWorkspace::ProjectIterator it_p = m_workspace->Find(item->project.name);
365  auto project = it_p->second.Project();
366  project->Open();
367  return m_project_controller->Set(item->project.name, project);
368  }
369  case FileType:
370  {
371  Item* project_item = item->file.parent;
372  Ws::IWorkspace::ProjectIterator it_p = m_workspace->Find(project_item->project.name);
373  auto project = it_p->second.Project();
374  project->Open(item->file.name);
375  return m_project_controller->Set(project_item->project.name, project);
376  }
377  default:
378  return false;
379  }
380 }
381 
382 //--------------------------------------------------------------------------
383 // Implementation of QAbstractItemModel methods.
384 //--------------------------------------------------------------------------
385 
386 int WorkspaceQtModel::columnCount(QModelIndex const& ) const
387 {
388  return 1;
389 }
390 
391 QVariant WorkspaceQtModel::data(QModelIndex const& index, int role) const
392 {
393  if (index.isValid()) {
394  Item* item = static_cast<Item*>(index.internalPointer());
395 
396  bool opened = IsOpened(index);
397 
398  switch (item->type) {
399  case ProjectType:
400  if (role == Qt::DisplayRole) {
401  return QString::fromStdString(item->project.name);
402  } else if (role == Qt::DecorationRole) {
403  if (opened) {
404  return QIcon::fromTheme("folder-open");
405  } else {
406  return QIcon::fromTheme("folder");
407  }
408  } else if (role == Qt::FontRole) {
409  QFont f;
410  if (opened)
411  f.setItalic(true);
412  return f;
413  }
414  break;
415  case FileType:
416  if (role == Qt::DisplayRole) {
417  return QString::fromStdString(item->file.name);
418  } else if (role == Qt::DecorationRole) {
419  return QIcon::fromTheme("text-x-generic");
420  } else if (role == Qt::FontRole) {
421  QFont f;
422  if (opened)
423  f.setItalic(true);
424  return f;
425  }
426  break;
427  default:
428  return QVariant();
429  }
430  }
431  return QVariant();
432 }
433 
434 Qt::ItemFlags WorkspaceQtModel::flags(QModelIndex const & index) const
435 {
436  Qt::ItemFlags flag = 0;
437  if (index.isValid()) {
438  flag = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
439  }
440  return flag;
441 }
442 
443 QVariant WorkspaceQtModel::headerData(int , Qt::Orientation , int ) const
444 {
445  return QVariant();
446 }
447 
448 QModelIndex WorkspaceQtModel::index(int row, int column, QModelIndex const& parent) const
449 {
450  if (!hasIndex(row, column, parent)) {
451  return QModelIndex();
452  }
453 
454  Item* parent_item;
455  if (!parent.isValid()) {
456  parent_item = m_root;
457  } else {
458  parent_item = static_cast<Item*>(parent.internalPointer());
459  }
460 
461  switch (parent_item->type) {
462  case RootType:
463  return createIndex(row, column, parent_item->workspace.children[row]);
464  case ProjectType:
465  return createIndex(row, column, parent_item->project.children[row]);
466  default:
467  return QModelIndex();
468  }
469 }
470 
471 bool WorkspaceQtModel::insertRow(int row, QModelIndex const& parent)
472 {
473  return insertRows(row, 1, parent);
474 }
475 
476 bool WorkspaceQtModel::insertRows(int row, int count, QModelIndex const& parent)
477 {
478  Item* item;
479  if (parent.isValid()) {
480  item = static_cast<Item*>(parent.internalPointer());
481  } else {
482  item = m_root;
483  }
484 
485  if (row < 0) {
486  return false;
487  }
488 
489  vector<Item*>* children = nullptr;
490 
491  switch (item->type) {
492  case RootType:
493  children = &item->workspace.children;
494  break;
495  case ProjectType:
496  children = &item->project.children;
497  break;
498  default:
499  // Other types cannot have children.
500  return false;
501  }
502 
503  if (row > (int) children->size()) {
504  return false;
505  }
506 
507  beginInsertRows(parent, row, row + count - 1);
508  for (int i = 0; i < count; i++) {
509  children->insert(children->begin() + row + i, 0);
510  }
511  // Update row numbers of items following inserted item.
512  for (int i = row + count; i < static_cast<int>(children->size()); i++) {
513  (*children)[i]->row += count;
514  }
515  endInsertRows();
516 
517  return true;
518 }
519 
520 QModelIndex WorkspaceQtModel::parent(QModelIndex const& index) const
521 {
522  if (!index.isValid()) {
523  return QModelIndex();
524  }
525  Item* child = static_cast<Item*>(index.internalPointer());
526  if (child->type == FileType) {
527  Item* project = child->file.parent;
528  auto it = find(m_root->workspace.children.begin(), m_root->workspace.children.end(), project);
529  return createIndex(it - m_root->workspace.children.begin(), 0, project);
530  } else {
531  return QModelIndex();
532  }
533 }
534 
535 bool WorkspaceQtModel::removeRow(int row, QModelIndex const& parent)
536 {
537  return removeRows(row, 1, parent);
538 }
539 
540 bool WorkspaceQtModel::removeRows(int row, int count, QModelIndex const& parent)
541 {
542  Item* item;
543  if (parent.isValid()) {
544  item = static_cast<Item*>(parent.internalPointer());
545  } else {
546  item = m_root;
547  }
548  if (row < 0) {
549  return false;
550  }
551 
552  vector<Item*>* children = nullptr;
553 
554  switch (item->type) {
555  case RootType:
556  children = &item->workspace.children;
557  break;
558  case ProjectType:
559  children = &item->project.children;
560  break;
561  default:
562  return false;
563  }
564 
565  if (row + count > (int) children->size()) {
566  return false;
567  }
568 
569  beginRemoveRows(parent, row, row + count - 1);
570  for (int i = 0; i < count; i++) {
571  delete (*children)[row + i];
572  children->erase(children->begin() + row + i);
573  }
574  endRemoveRows();
575 
576  return true;
577 }
578 
579 int WorkspaceQtModel::rowCount(QModelIndex const& parent) const
580 {
581  Item* item;
582  if (!parent.isValid()) {
583  item = m_root;
584  } else {
585  item = static_cast<Item*>(parent.internalPointer());
586  }
587  switch (item->type) {
588  case RootType:
589  return item->workspace.children.size();
590  case ProjectType:
591  return item->project.children.size();
592  default:
593  return 0;
594  }
595 }
596 
597 } // end of namespace Gui
598 } // end of namespace SimShell
Interface for IFile.
STL namespace.
see the online Qt documentation
Namespace for miscellaneous utilities.
Definition: PTreeFile.cpp:44
Interface for WorkspaceQtModel.
Extremely simple Exception root class.
Definition: Exception.h:28
Interface for IProject.
Event used to inform some observer that the workspace has changed.
Interface for IWorkspace.
Event used to inform some observer that a project has changed.
const std::string GetOldName() const
In case type is ProjectRenamed, get old name of renamed project.
Subject in Observer pattern.
Definition: Subject.h:34
Header file for Exception class.
see the online Qt documentation
Namespace for generic graphical shell for simulators.
Definition: SimSession.h:32
const std::string GetName() const
In case type is ProjectAdded or ProjectRemoved, get name of project.
const std::string GetNewName() const
In case type is ProjectRenamed, get new name of renamed project.