1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "resourceview.h"
27
28 #include "undocommands_p.h"
29
30 #include <coreplugin/fileutils.h>
31 #include <coreplugin/icore.h>
32
33 #include <QDebug>
34
35 #include <QAction>
36 #include <QApplication>
37 #include <QFileDialog>
38 #include <QHeaderView>
39 #include <QInputDialog>
40 #include <QMenu>
41 #include <QMouseEvent>
42 #include <QUndoStack>
43
44 using namespace ResourceEditor;
45 using namespace ResourceEditor::Internal;
46
ResourceView(RelativeResourceModel * model,QUndoStack * history,QWidget * parent)47 ResourceView::ResourceView(RelativeResourceModel *model, QUndoStack *history, QWidget *parent) :
48 Utils::TreeView(parent),
49 m_qrcModel(model),
50 m_history(history),
51 m_mergeId(-1)
52 {
53 advanceMergeId();
54 setModel(m_qrcModel);
55 setContextMenuPolicy(Qt::CustomContextMenu);
56 setEditTriggers(EditKeyPressed);
57
58 header()->hide();
59
60 connect(this, &QWidget::customContextMenuRequested, this, &ResourceView::showContextMenu);
61 connect(this, &QAbstractItemView::activated, this, &ResourceView::onItemActivated);
62 }
63
64 ResourceView::~ResourceView() = default;
65
findSamePlacePostDeletionModelIndex(int & row,QModelIndex & parent) const66 void ResourceView::findSamePlacePostDeletionModelIndex(int &row, QModelIndex &parent) const
67 {
68 // Concept:
69 // - Make selection stay on same Y level
70 // - Enable user to hit delete several times in row
71 const bool hasLowerBrother = m_qrcModel->hasIndex(row + 1,
72 0, parent);
73 if (hasLowerBrother) {
74 // First or mid child -> lower brother
75 // o
76 // +--o
77 // +-[o] <-- deleted
78 // +--o <-- chosen
79 // o
80 // --> return unmodified
81 } else {
82 if (parent == QModelIndex()) {
83 // Last prefix node
84 if (row == 0) {
85 // Last and only prefix node
86 // [o] <-- deleted
87 // +--o
88 // +--o
89 row = -1;
90 parent = QModelIndex();
91 } else {
92 const QModelIndex upperBrother = m_qrcModel->index(row - 1,
93 0, parent);
94 if (m_qrcModel->hasChildren(upperBrother)) {
95 // o
96 // +--o <-- selected
97 // [o] <-- deleted
98 row = m_qrcModel->rowCount(upperBrother) - 1;
99 parent = upperBrother;
100 } else {
101 // o
102 // o <-- selected
103 // [o] <-- deleted
104 row--;
105 }
106 }
107 } else {
108 // Last file node
109 const bool hasPrefixBelow = m_qrcModel->hasIndex(parent.row() + 1,
110 parent.column(), QModelIndex());
111 if (hasPrefixBelow) {
112 // Last child or parent with lower brother -> lower brother of parent
113 // o
114 // +--o
115 // +-[o] <-- deleted
116 // o <-- chosen
117 row = parent.row() + 1;
118 parent = QModelIndex();
119 } else {
120 const bool onlyChild = row == 0;
121 if (onlyChild) {
122 // Last and only child of last parent -> parent
123 // o <-- chosen
124 // +-[o] <-- deleted
125 row = parent.row();
126 parent = m_qrcModel->parent(parent);
127 } else {
128 // Last child of last parent -> upper brother
129 // o
130 // +--o <-- chosen
131 // +-[o] <-- deleted
132 row--;
133 }
134 }
135 }
136 }
137 }
138
removeEntry(const QModelIndex & index)139 EntryBackup * ResourceView::removeEntry(const QModelIndex &index)
140 {
141 Q_ASSERT(m_qrcModel);
142 return m_qrcModel->removeEntry(index);
143 }
144
existingFilesSubtracted(int prefixIndex,const QStringList & fileNames) const145 QStringList ResourceView::existingFilesSubtracted(int prefixIndex, const QStringList &fileNames) const
146 {
147 return m_qrcModel->existingFilesSubtracted(prefixIndex, fileNames);
148 }
149
addFiles(int prefixIndex,const QStringList & fileNames,int cursorFile,int & firstFile,int & lastFile)150 void ResourceView::addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile,
151 int &firstFile, int &lastFile)
152 {
153 Q_ASSERT(m_qrcModel);
154 m_qrcModel->addFiles(prefixIndex, fileNames, cursorFile, firstFile, lastFile);
155
156 // Expand prefix node
157 const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex());
158 if (prefixModelIndex.isValid())
159 this->setExpanded(prefixModelIndex, true);
160 }
161
removeFiles(int prefixIndex,int firstFileIndex,int lastFileIndex)162 void ResourceView::removeFiles(int prefixIndex, int firstFileIndex, int lastFileIndex)
163 {
164 Q_ASSERT(prefixIndex >= 0 && prefixIndex < m_qrcModel->rowCount(QModelIndex()));
165 const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex());
166 Q_ASSERT(prefixModelIndex != QModelIndex());
167 Q_ASSERT(firstFileIndex >= 0 && firstFileIndex < m_qrcModel->rowCount(prefixModelIndex));
168 Q_ASSERT(lastFileIndex >= 0 && lastFileIndex < m_qrcModel->rowCount(prefixModelIndex));
169
170 for (int i = lastFileIndex; i >= firstFileIndex; i--) {
171 const QModelIndex index = m_qrcModel->index(i, 0, prefixModelIndex);
172 delete removeEntry(index);
173 }
174 }
175
keyPressEvent(QKeyEvent * e)176 void ResourceView::keyPressEvent(QKeyEvent *e)
177 {
178 if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)
179 emit removeItem();
180 else
181 Utils::TreeView::keyPressEvent(e);
182 }
183
addPrefix()184 QModelIndex ResourceView::addPrefix()
185 {
186 const QModelIndex idx = m_qrcModel->addNewPrefix();
187 selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
188 return idx;
189 }
190
nonExistingFiles()191 QList<QModelIndex> ResourceView::nonExistingFiles()
192 {
193 return m_qrcModel->nonExistingFiles();
194 }
195
refresh()196 void ResourceView::refresh()
197 {
198 m_qrcModel->refresh();
199 QModelIndex idx = currentIndex();
200 setModel(nullptr);
201 setModel(m_qrcModel);
202 setCurrentIndex(idx);
203 expandAll();
204 }
205
fileNamesToAdd()206 QStringList ResourceView::fileNamesToAdd()
207 {
208 return QFileDialog::getOpenFileNames(this, tr("Open File"),
209 m_qrcModel->absolutePath(QString()),
210 tr("All files (*)"));
211 }
212
currentAlias() const213 QString ResourceView::currentAlias() const
214 {
215 const QModelIndex current = currentIndex();
216 if (!current.isValid())
217 return QString();
218 return m_qrcModel->alias(current);
219 }
220
currentPrefix() const221 QString ResourceView::currentPrefix() const
222 {
223 const QModelIndex current = currentIndex();
224 if (!current.isValid())
225 return QString();
226 const QModelIndex preindex = m_qrcModel->prefixIndex(current);
227 QString prefix, file;
228 m_qrcModel->getItem(preindex, prefix, file);
229 return prefix;
230 }
231
currentLanguage() const232 QString ResourceView::currentLanguage() const
233 {
234 const QModelIndex current = currentIndex();
235 if (!current.isValid())
236 return QString();
237 const QModelIndex preindex = m_qrcModel->prefixIndex(current);
238 return m_qrcModel->lang(preindex);
239 }
240
currentResourcePath() const241 QString ResourceView::currentResourcePath() const
242 {
243 const QModelIndex current = currentIndex();
244 if (!current.isValid())
245 return QString();
246
247 const QString alias = m_qrcModel->alias(current);
248 if (!alias.isEmpty())
249 return QLatin1Char(':') + currentPrefix() + QLatin1Char('/') + alias;
250
251 return QLatin1Char(':') + currentPrefix() + QLatin1Char('/') + m_qrcModel->relativePath(m_qrcModel->file(current));
252 }
253
getCurrentValue(NodeProperty property) const254 QString ResourceView::getCurrentValue(NodeProperty property) const
255 {
256 switch (property) {
257 case AliasProperty: return currentAlias();
258 case PrefixProperty: return currentPrefix();
259 case LanguageProperty: return currentLanguage();
260 default: Q_ASSERT(false); return QString(); // Kill warning
261 }
262 }
263
changeValue(const QModelIndex & nodeIndex,NodeProperty property,const QString & value)264 void ResourceView::changeValue(const QModelIndex &nodeIndex, NodeProperty property,
265 const QString &value)
266 {
267 switch (property) {
268 case AliasProperty: m_qrcModel->changeAlias(nodeIndex, value); return;
269 case PrefixProperty: m_qrcModel->changePrefix(nodeIndex, value); return;
270 case LanguageProperty: m_qrcModel->changeLang(nodeIndex, value); return;
271 default: Q_ASSERT(false);
272 }
273 }
274
onItemActivated(const QModelIndex & index)275 void ResourceView::onItemActivated(const QModelIndex &index)
276 {
277 const QString fileName = m_qrcModel->file(index);
278 if (fileName.isEmpty())
279 return;
280 emit itemActivated(fileName);
281 }
282
showContextMenu(const QPoint & pos)283 void ResourceView::showContextMenu(const QPoint &pos)
284 {
285 const QModelIndex index = indexAt(pos);
286 const QString fileName = m_qrcModel->file(index);
287 if (fileName.isEmpty())
288 return;
289 emit contextMenuShown(mapToGlobal(pos), fileName);
290 }
291
advanceMergeId()292 void ResourceView::advanceMergeId()
293 {
294 m_mergeId++;
295 if (m_mergeId < 0)
296 m_mergeId = 0;
297 }
298
addUndoCommand(const QModelIndex & nodeIndex,NodeProperty property,const QString & before,const QString & after)299 void ResourceView::addUndoCommand(const QModelIndex &nodeIndex, NodeProperty property,
300 const QString &before, const QString &after)
301 {
302 QUndoCommand * const command = new ModifyPropertyCommand(this, nodeIndex, property,
303 m_mergeId, before, after);
304 m_history->push(command);
305 }
306
setCurrentAlias(const QString & before,const QString & after)307 void ResourceView::setCurrentAlias(const QString &before, const QString &after)
308 {
309 const QModelIndex current = currentIndex();
310 if (!current.isValid())
311 return;
312
313 addUndoCommand(current, AliasProperty, before, after);
314 }
315
setCurrentPrefix(const QString & before,const QString & after)316 void ResourceView::setCurrentPrefix(const QString &before, const QString &after)
317 {
318 const QModelIndex current = currentIndex();
319 if (!current.isValid())
320 return;
321 const QModelIndex preindex = m_qrcModel->prefixIndex(current);
322
323 addUndoCommand(preindex, PrefixProperty, before, after);
324 }
325
setCurrentLanguage(const QString & before,const QString & after)326 void ResourceView::setCurrentLanguage(const QString &before, const QString &after)
327 {
328 const QModelIndex current = currentIndex();
329 if (!current.isValid())
330 return;
331 const QModelIndex preindex = m_qrcModel->prefixIndex(current);
332
333 addUndoCommand(preindex, LanguageProperty, before, after);
334 }
335
isPrefix(const QModelIndex & index) const336 bool ResourceView::isPrefix(const QModelIndex &index) const
337 {
338 if (!index.isValid())
339 return false;
340 const QModelIndex preindex = m_qrcModel->prefixIndex(index);
341 if (preindex == index)
342 return true;
343 return false;
344 }
345
fileName() const346 QString ResourceView::fileName() const
347 {
348 return m_qrcModel->fileName();
349 }
350
setResourceDragEnabled(bool e)351 void ResourceView::setResourceDragEnabled(bool e)
352 {
353 setDragEnabled(e);
354 m_qrcModel->setResourceDragEnabled(e);
355 }
356
resourceDragEnabled() const357 bool ResourceView::resourceDragEnabled() const
358 {
359 return m_qrcModel->resourceDragEnabled();
360 }
361