ProjectSaveView.cpp 12.5 KB
Newer Older
Roman Tolchenov's avatar
Roman Tolchenov committed
1
2
3
#include "MantidQtWidgets/Common/IProjectSerialisable.h"
#include "MantidQtWidgets/Common/WindowIcons.h"
#include "MantidQtWidgets/Common/ProjectSavePresenter.h"
4

5
#include "ProjectSaveView.h"
6

7
8
#include <QFileDialog>

9
10
11
using namespace MantidQt::API;

namespace MantidQt {
12
namespace MantidWidgets {
13

14
15
16
17
18
19
20
21
/**
 * Create a new instance of the view.
 *
 * @param projectName :: the existing project path
 * @param serialiser :: a ProjectSerialiser instance
 * @param windows :: vector of window handles for the open application
 * @param parent :: parent widget for this object
 */
22
23
24
25
26
ProjectSaveView::ProjectSaveView(
    const QString &projectName, ProjectSerialiser &serialiser,
    const std::vector<IProjectSerialisable *> &windows, QWidget *parent)
    : QDialog(parent), m_serialisableWindows(windows.cbegin(), windows.cend()),
      m_serialiser(serialiser) {
27

28
  m_ui.setupUi(this);
29
  m_presenter.reset(new ProjectSavePresenter(this));
30

31
  if (!checkIfNewProject(projectName))
32
33
    m_ui.projectPath->setText(projectName);

34
  m_ui.saveProgressBar->setValue(0);
35

36
  connectSignals();
37
38
}

39
40
41
// IProjectSaveView interface implementations
//==============================================================================

42
43
44
45
/**
 * Get all window handles passed to the view
 * @return a vector of handles to each window
 */
46
std::vector<API::IProjectSerialisable *> ProjectSaveView::getWindows() {
47
48
49
  return m_serialisableWindows;
}

50
51
52
53
/**
 * Get all of the checked workspace names
 * @return a vector of workspace names
 */
54
std::vector<std::string> ProjectSaveView::getCheckedWorkspaceNames() {
55
56
57
  return getItemsWithCheckState(Qt::CheckState::Checked);
}

58
59
60
61
/**
 * Get all of the unchecked workspace names
 * @return a vector of workspace names
 */
62
std::vector<std::string> ProjectSaveView::getUncheckedWorkspaceNames() {
63
64
65
  return getItemsWithCheckState(Qt::CheckState::Unchecked);
}

66
67
68
69
70
71
72
/**
 * Get the project path text
 *
 * This path may or may not exist yet and must be further validated.
 *
 * @return string representing the project path
 */
73
QString ProjectSaveView::getProjectPath() { return m_ui.projectPath->text(); }
74

75
76
77
78
/**
 * Set the project path
 * @param path :: path the project will be saved to
 */
79
80
void ProjectSaveView::setProjectPath(const QString &path) {
  m_ui.projectPath->setText(path);
81
82
}

83
84
85
86
87
88
89
90
/**
 * Update the list of workspaces.
 *
 * This will create one new item in the list for each workspace info
 * object passed
 *
 * @param workspaces :: vector of workspace info objects to add to the view
 */
91
92
void ProjectSaveView::updateWorkspacesList(
    const std::vector<WorkspaceInfo> &workspaces) {
93
  m_ui.workspaceList->clear();
94
95
  for (auto info : workspaces) {
    addWorkspaceItem(info);
96
  }
97
98
  // pad the header for longish workspace names
  m_ui.workspaceList->header()->resizeSection(0, 300);
99
100
}

101
102
103
104
105
106
107
/**
 * Update the included windows list
 *
 * This will create one new item in the list for each window info object passed.
 *
 * @param windows :: vector of window info objects to add to the view
 */
108
109
void ProjectSaveView::updateIncludedWindowsList(
    const std::vector<WindowInfo> &windows) {
110
  m_ui.includedWindows->clear();
111
112
  for (auto info : windows) {
    addWindowItem(m_ui.includedWindows, info);
113
  }
114
115

  resizeWidgetColumns(m_ui.includedWindows);
116
117
}

118
119
120
121
122
123
124
/**
 * Update the excluded windows list
 *
 * This will create one new item in the list for each window info object passed.
 *
 * @param windows :: vector of window info objects to add to the view
 */
125
126
void ProjectSaveView::updateExcludedWindowsList(
    const std::vector<WindowInfo> &windows) {
127
  m_ui.excludedWindows->clear();
128
  for (auto info : windows) {
129
    addWindowItem(m_ui.excludedWindows, info);
130
  }
131
132

  resizeWidgetColumns(m_ui.excludedWindows);
133
}
134

135
136
137
138
/**
 * Remove a list of windows from the included windows list
 * @param windows :: vector of window names to remove from the include list
 */
139
140
void ProjectSaveView::removeFromIncludedWindowsList(
    const std::vector<std::string> &windows) {
141
  for (auto name : windows) {
142
    removeItem(m_ui.includedWindows, name);
143
144
145
  }
}

146
147
148
149
/**
 * Remove a list of windows from the excluded windows list
 * @param windows :: vector of window names to remove from the exclude list
 */
150
151
void ProjectSaveView::removeFromExcludedWindowsList(
    const std::vector<std::string> &windows) {
152
  for (auto name : windows) {
153
    removeItem(m_ui.excludedWindows, name);
154
155
156
  }
}

157
158
159
// Private slots
//==============================================================================

160
161
162
163
164
165
166
167
168
169
/**
 * Slot of handle when a workspace item is changed.
 *
 * When a workspace item is check or unchecked this will notify the presenter to
 * move the windows associated with this workspace to the other (included/
 * excluded) list.
 *
 * @param item :: QTreeWidget item that was modified
 * @param column :: column that was modified
 */
170
void ProjectSaveView::workspaceItemChanged(QTreeWidgetItem *item, int column) {
171
  updateWorkspaceListCheckState(item);
172
  if (item->checkState(column) == Qt::CheckState::Checked) {
173
174
175
176
    m_presenter->notify(ProjectSavePresenter::Notification::CheckWorkspace);
  } else if (item->checkState(column) == Qt::CheckState::Unchecked) {
    m_presenter->notify(ProjectSavePresenter::Notification::UncheckWorkspace);
  }
177
178
}

179
180
181
182
183
184
185
186
187
188
/**
 * Slot to save the project.
 *
 * This will call the project serialiser passed on construction and save the
 * current state of the project. If only certain workspaces have been selected
 * then only a subset of the workspaces/windows will be passed to the project
 * serialiser.
 *
 * @param checked :: unused arguement
 */
189
void ProjectSaveView::save(bool checked) {
190
  UNUSED_ARG(checked);
191

192
  if (m_ui.projectPath->text().isEmpty()) {
193
194
195
196
197
    QMessageBox::warning(this, "Project Save",
                         "Please choose a valid file path", QMessageBox::Ok);
    return;
  }

198
  m_presenter->notify(ProjectSavePresenter::Notification::PrepareProjectFolder);
199
200
  auto wsNames = getCheckedWorkspaceNames();
  auto windowNames = getIncludedWindowNames();
201
  auto filePath = m_ui.projectPath->text();
202
  auto compress = filePath.endsWith(".gz");
203

204
  m_serialiser.save(filePath, wsNames, windowNames, compress);
205
206
  emit projectSaved();

207
  close();
208
209
210
  // Set the result code after calling close() because
  // close() sets it to QDialog::Rejected
  setResult(QDialog::Accepted);
211
212
}

213
214
215
216
217
218
/**
 * Slot to ask the user to find a new project path.
 *
 * This will open a file dialog and let the user choose a new location for the
 * project.
 */
219
void ProjectSaveView::findFilePath() {
220
221
222
223
  QString filter = "MantidPlot project (*.mantid);;";
  filter += "Compressed MantidPlot project (*.mantid.gz)";

  QString selectedFilter;
224
225
  QString filename = QFileDialog::getSaveFileName(this, "Save Project As", "",
                                                  filter, &selectedFilter);
226

227
  m_ui.projectPath->setText(filename);
228
229
230
231
232
}

// Private helper methods
//==============================================================================

233
234
235
236
237
/**
 * Get all items with a given check state from the workspace list
 * @param state :: any of the values in Qt::CheckState
 * @return a vector of names of workspaces that had the state
 */
238
239
std::vector<std::string>
ProjectSaveView::getItemsWithCheckState(const Qt::CheckState state) const {
240
  std::vector<std::string> names;
241
242
243
244
245
246
  for (int i = 0; i < m_ui.workspaceList->topLevelItemCount(); ++i) {
    auto item = m_ui.workspaceList->topLevelItem(i);
    if (item->checkState(0) == state) {
      auto name = item->text(0).toStdString();
      names.push_back(name);
    }
247

248
    // now check the child items and append any that have the check state
249
    for (int i = 0; i < item->childCount(); ++i) {
250
251
252
253
254
255
      auto child = item->child(i);
      if (child->checkState(0) == state) {
        auto childName = child->text(0).toStdString();
        names.push_back(childName);
      }
    }
256
257
258
259
260
261
262
263
264
265
266
  }
  return names;
}

/**
 * Get any included window names
 *
 * These will depend on which workspaces are checked in the view
 *
 * @return vector of included window names
 */
267
std::vector<std::string> ProjectSaveView::getIncludedWindowNames() const {
268
  std::vector<std::string> names;
269
270
271
272
  for (int i = 0; i < m_ui.includedWindows->topLevelItemCount(); ++i) {
    auto item = m_ui.includedWindows->topLevelItem(i);
    auto name = item->text(0).toStdString();
    names.push_back(name);
273
274
275
276
  }
  return names;
}

277
278
279
280
281
/**
 * Remove an item from a QTreeWidget
 * @param widget :: handle to the widget
 * @param name :: name to match widget items with
 */
282
void ProjectSaveView::removeItem(QTreeWidget *widget, const std::string &name) {
283
  auto qname = QString::fromStdString(name);
284
  auto items = widget->findItems(qname, Qt::MatchContains);
285
286
287
288
289
  for (auto item : items) {
    delete item;
  }
}

290
291
292
293
294
/**
 * Add a new window item to the view
 * @param widget :: the widget to add the item to
 * @param info :: window info object with data to add
 */
295
296
void ProjectSaveView::addWindowItem(QTreeWidget *widget,
                                    const WindowInfo &info) {
297
298
299
300
301
302
  QStringList lst;
  WindowIcons icons;
  lst << QString::fromStdString(info.name);
  lst << QString::fromStdString(info.type);

  auto item = new QTreeWidgetItem(lst);
303
  if (!info.icon_id.empty())
304
305
    item->setIcon(0, icons.getIcon(info.type));
  widget->addTopLevelItem(item);
306
307
}

308
309
310
311
/**
 * Add a new workspace item to the view
 * @param info :: workspace info object with data to add
 */
312
void ProjectSaveView::addWorkspaceItem(const WorkspaceInfo &info) {
313
314
  auto item = makeWorkspaceItem(info);

315
  for (auto subInfo : info.subWorkspaces) {
316
317
318
319
320
321
322
323
324
325
326
327
    auto subItem = makeWorkspaceItem(subInfo);
    item->addChild(subItem);
  }

  m_ui.workspaceList->addTopLevelItem(item);
}

/**
 * Build a new QTreeWidgetItem for a WorkspaceInfo
 * @param info :: reference to the WorkspaceInfo to make an item for
 * @return new QTreeWidgetItem for the info object
 */
328
329
QTreeWidgetItem *
ProjectSaveView::makeWorkspaceItem(const WorkspaceInfo &info) const {
330
331
332
333
334
335
336
  QStringList lst;
  lst << QString::fromStdString(info.name);
  lst << QString::fromStdString(info.type);
  lst << QString::fromStdString(info.size);
  lst << QString::number(info.numWindows);

  auto item = new QTreeWidgetItem(lst);
337

338
  if (!info.icon_id.empty())
339
    item->setIcon(0, getQPixmap(info.icon_id));
340
  item->setCheckState(0, Qt::CheckState::Checked);
341

342
  return item;
343
344
}

345
346
347
348
349
/**
 * Check if project path is a new unsaved project or has been saved before
 * @param projectName :: path to the project
 * @return whether the project already exists
 */
350
351
352
353
354
355
bool ProjectSaveView::checkIfNewProject(const QString &projectName) const {
  return (projectName == "untitled" ||
          projectName.endsWith(".opj", Qt::CaseInsensitive) ||
          projectName.endsWith(".ogm", Qt::CaseInsensitive) ||
          projectName.endsWith(".ogw", Qt::CaseInsensitive) ||
          projectName.endsWith(".ogg", Qt::CaseInsensitive));
356
357
}

358
359
360
361
/**
 * Resize the columns of a widget to match the length of the contents
 * @param widget :: the widget to resize
 */
362
void ProjectSaveView::resizeWidgetColumns(QTreeWidget *widget) {
363
364
365
366
  for (int i = 0; i < widget->topLevelItemCount(); ++i)
    widget->resizeColumnToContents(i);
}

367
368
369
/**
 * Connect the internal signals
 */
370
371
372
373
374
375
376
377
void ProjectSaveView::connectSignals() {
  // Connect signal to listen for when workspaces are changed
  // (checked/unchecked)
  connect(m_ui.workspaceList, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this,
          SLOT(workspaceItemChanged(QTreeWidgetItem *, int)));

  connect(m_ui.btnBrowseFilePath, SIGNAL(clicked(bool)), this,
          SLOT(findFilePath()));
378
379
380
381
382
  connect(m_ui.btnSave, SIGNAL(clicked(bool)), this, SLOT(save(bool)));
  connect(m_ui.btnCancel, SIGNAL(clicked(bool)), this, SLOT(close()));

  connect(&m_serialiser, SIGNAL(setProgressBarRange(int, int)),
          m_ui.saveProgressBar, SLOT(setRange(int, int)));
383
384
  connect(&m_serialiser, SIGNAL(setProgressBarValue(int)), m_ui.saveProgressBar,
          SLOT(setValue(int)));
385
}
386
387
388
389
390
391
392
393
394

/**
 * Update other items that are parents/children of this item.
 *
 * This makes sure that the state of the checkboxes are always logically
 * consistent. E.g. is a parent item is checked, so are all its children
 *
 * @param item :: item that has changed check state
 */
395
void ProjectSaveView::updateWorkspaceListCheckState(QTreeWidgetItem *item) {
396
397
398
399
400
401
  // block signals so we don't trigger more updates to widget items
  blockSignals(true);

  // update child check state
  // children should match the check state of the parent item
  auto checkState = item->checkState(0);
402
  for (int i = 0; i < item->childCount(); ++i) {
403
404
405
406
407
    item->child(i)->setCheckState(0, checkState);
  }

  // the parent item should be unchecked if any single child is unchecked
  auto parent = item->parent();
408
  if (parent && checkState == Qt::CheckState::Unchecked)
409
410
411
412
    parent->setCheckState(0, checkState);

  blockSignals(false);
}
413
414
}
}