Lomiri
WorkspaceModel.cpp
1 /*
2  * Copyright (C) 2017 Canonical Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include "WorkspaceModel.h"
18 #include "WorkspaceManager.h"
19 #include "Workspace.h"
20 #include "Screen.h"
21 
22 #include <QQmlEngine>
23 
24 Q_LOGGING_CATEGORY(WORKSPACES, "Workspaces", QtInfoMsg)
25 
26 #define DEBUG_MSG qCDebug(WORKSPACES).nospace().noquote() << __func__
27 #define INFO_MSG qCInfo(WORKSPACES).nospace().noquote() << __func__
28 
29 WorkspaceModel::WorkspaceModel(QObject *parent)
30  : QAbstractListModel(parent)
31 {
32 }
33 
34 WorkspaceModel::~WorkspaceModel()
35 {
36  qDeleteAll(m_workspaces.toList()); // make a copy so the list doesnt edit itself during delete.
37  m_workspaces.clear();
38 }
39 
40 void WorkspaceModel::append(Workspace *workspace)
41 {
42  insert(m_workspaces.count(), workspace);
43 }
44 
45 void WorkspaceModel::insert(int index, Workspace *workspace)
46 {
47  beginInsertRows(QModelIndex(), index, index);
48 
49  m_workspaces.insert(index, workspace);
50 
51  endInsertRows();
52 
53  Q_EMIT workspaceInserted(index, workspace);
54  Q_EMIT countChanged();
55 }
56 
57 void WorkspaceModel::remove(Workspace *workspace)
58 {
59  int index = m_workspaces.indexOf(workspace);
60  if (index < 0) return;
61 
62  beginRemoveRows(QModelIndex(), index, index);
63 
64  m_workspaces.removeAt(index);
65  insertUnassigned(workspace);
66 
67  endRemoveRows();
68 
69  Q_EMIT workspaceRemoved(workspace);
70  Q_EMIT countChanged();
71 }
72 
73 void WorkspaceModel::move(int from, int to)
74 {
75  if (from == to) return;
76  DEBUG_MSG << " from=" << from << " to=" << to;
77 
78  if (from >= 0 && from < m_workspaces.size() && to >= 0 && to < m_workspaces.size()) {
79  QModelIndex parent;
80 
81  beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
82 #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
83  const auto &window = m_windowModel.takeAt(from);
84  m_workspaces.insert(to, window);
85 #else
86  m_workspaces.move(from, to);
87 #endif
88  endMoveRows();
89 
90  Q_EMIT workspaceMoved(from, to);
91  }
92 }
93 
94 int WorkspaceModel::indexOf(Workspace *workspace) const
95 {
96  return m_workspaces.indexOf(workspace);
97 }
98 
99 Workspace *WorkspaceModel::get(int index) const
100 {
101  if (index < 0 || index >= rowCount()) return nullptr;
102  return m_workspaces.at(index);
103 }
104 
105 int WorkspaceModel::rowCount(const QModelIndex &) const
106 {
107  return m_workspaces.count();
108 }
109 
110 QVariant WorkspaceModel::data(const QModelIndex &index, int role) const
111 {
112  if (index.row() < 0 || index.row() >= m_workspaces.size())
113  return QVariant();
114 
115  if (role == WorkspaceRole) {
116  Workspace *workspace = m_workspaces.at(index.row());
117  return QVariant::fromValue(workspace);
118  } else {
119  return QVariant();
120  }
121 }
122 
123 void WorkspaceModel::sync(WorkspaceModel *proxy)
124 {
125  if (!proxy) return;
126  const auto& proxyList = proxy->list();
127 
128  // check for removals
129  int removedIndexWhichWasActive = -1;
130  QVector<Workspace*> dpCpy(this->list());
131  Q_FOREACH(auto workspace, dpCpy) {
132 
133  bool found = false;
134  Q_FOREACH(auto p, proxyList) {
135  auto workspaceProxy = qobject_cast<ProxyWorkspace*>(p);
136  if (workspaceProxy->proxyObject() == workspace) {
137  found = true;
138  break;
139  }
140  }
141  if (!found) {
142  if (workspace->isActive()) {
143  removedIndexWhichWasActive = indexOf(workspace);
144  }
145  workspace->unassign();
146  }
147  }
148 
149  // existing
150  QSet<Workspace*> newWorkspaces;
151  for (int i = 0; i < proxyList.count(); i++) {
152  auto workspaceProxy = qobject_cast<ProxyWorkspace*>(proxyList[i]);
153  auto workspace = workspaceProxy->proxyObject();
154 
155  int oldIndex = this->indexOf(workspace);
156 
157  if (oldIndex < 0) {
158  workspace->assign(this, QVariant(i));
159  } else if (oldIndex != i) {
160  this->move(oldIndex, i);
161  }
162  newWorkspaces.insert(workspace);
163  }
164 
165  // Make sure we have at least one workspace in the model.
166  if (rowCount() == 0) {
167  Workspace* workspace = WorkspaceManager::instance()->createWorkspace();
168  workspace->assign(this);
169  (new ProxyWorkspace(workspace))->assign(proxy);
170  }
171 
172  if (removedIndexWhichWasActive != -1) {
173  int newActiveIndex = qMin(removedIndexWhichWasActive, this->rowCount()-1);
174  Workspace* newActiveWorkspace = newActiveIndex >= 0 ? this->get(newActiveIndex) : nullptr;
175 
176  WorkspaceManager::instance()->setActiveWorkspace(newActiveWorkspace);
177  }
178 
179  proxy->finishSync();
180  finishSync();
181 }
182 
183 void WorkspaceModel::finishSync()
184 {
185  QSet<Workspace*> dpCpy(m_unassignedWorkspaces);
186  Q_FOREACH(auto workspace, dpCpy) {
187  delete workspace;
188  }
189  m_unassignedWorkspaces.clear();
190 }
191 
192 void WorkspaceModel::insertUnassigned(Workspace *workspace)
193 {
194  m_unassignedWorkspaces.insert(workspace);
195  connect(workspace, &Workspace::assigned, this, [=]() {
196  m_unassignedWorkspaces.remove(workspace);
197  disconnect(workspace, &Workspace::assigned, this, 0);
198  });
199  connect(workspace, &QObject::destroyed, this, [=]() {
200  m_unassignedWorkspaces.remove(workspace);
201  });
202 }
203 
204 
205 ProxyWorkspaceModel::ProxyWorkspaceModel(WorkspaceModel * const model, ProxyScreen* screen)
206  : m_original(model)
207  , m_screen(screen)
208 {
209  Q_FOREACH(auto workspace, model->list()) {
210  auto proxy = new ProxyWorkspace(workspace);
211  QQmlEngine::setObjectOwnership(proxy, QQmlEngine::CppOwnership);
212  proxy->assign(this);
213  }
214  connect(m_original, &WorkspaceModel::workspaceInserted, this, [this](int index, Workspace* inserted) {
215  if (isSyncing()) return;
216 
217  (new ProxyWorkspace(inserted))->assign(this, index);
218  });
219  connect(m_original, &WorkspaceModel::workspaceRemoved, this, [this](Workspace* removed) {
220  if (isSyncing()) return;
221 
222  for (int i = 0; i < rowCount(); i++) {
223  auto workspaceProxy = qobject_cast<ProxyWorkspace*>(get(i));
224  auto w = workspaceProxy->proxyObject();
225  if (w == removed) {
226  remove(workspaceProxy);
227  break;
228  }
229  }
230  });
231  connect(m_original, &WorkspaceModel::workspaceMoved, this, [this](int from, int to) {
232  if (isSyncing()) return;
233 
234  move(from, to);
235  });
236 }
237 
238 void ProxyWorkspaceModel::move(int from, int to)
239 {
240  WorkspaceModel::move(from, to);
241 }
242 
243 bool ProxyWorkspaceModel::isSyncing() const
244 {
245  return m_screen->isSyncing();
246 }
247 
248 void ProxyWorkspaceModel::addWorkspace()
249 {
250  auto newWorkspace = WorkspaceManager::instance()->createWorkspace();
251  m_original->insertUnassigned(newWorkspace);
252 
253  (new ProxyWorkspace(newWorkspace))->assign(this);
254 }