1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "qtresourceview_p.h"
30 #include "qtresourcemodel_p.h"
31 #include "qtresourceeditordialog_p.h"
32 #include "iconloader_p.h"
33
34 #include <QtDesigner/abstractformeditor.h>
35 #include <QtDesigner/abstractsettings.h>
36
37 #include <QtWidgets/qtoolbar.h>
38 #include <QtWidgets/qaction.h>
39 #include <QtWidgets/qsplitter.h>
40 #include <QtWidgets/qtreewidget.h>
41 #include <QtWidgets/qlistwidget.h>
42 #include <QtWidgets/qheaderview.h>
43 #include <QtWidgets/qboxlayout.h>
44 #include <QtGui/qpainter.h>
45 #include <QtCore/qfileinfo.h>
46 #include <QtCore/qdir.h>
47 #include <QtCore/qqueue.h>
48 #include <QtGui/qpainter.h>
49 #include <QtWidgets/qdialogbuttonbox.h>
50 #include <QtWidgets/qpushbutton.h>
51 #include <QtWidgets/qmessagebox.h>
52 #include <QtWidgets/qapplication.h>
53 #if QT_CONFIG(clipboard)
54 #include <QtGui/qclipboard.h>
55 #endif
56 #include <QtWidgets/qmenu.h>
57 #include <QtWidgets/qlineedit.h>
58 #include <QtGui/qdrag.h>
59 #include <QtCore/qmimedata.h>
60 #include <QtXml/qdom.h>
61
62 #include <algorithm>
63
64 QT_BEGIN_NAMESPACE
65
66 static const char *elementResourceData = "resource";
67 static const char *typeAttribute = "type";
68 static const char *typeImage = "image";
69 static const char *typeStyleSheet = "stylesheet";
70 static const char *typeOther = "other";
71 static const char *fileAttribute = "file";
72 static const char *SplitterPosition = "SplitterPosition";
73 static const char *Geometry = "Geometry";
74 static const char *ResourceViewDialogC = "ResourceDialog";
75
76 // ---------------- ResourceListWidget: A list widget that has drag enabled
77 class ResourceListWidget : public QListWidget {
78 public:
79 ResourceListWidget(QWidget *parent = nullptr);
80
81 protected:
82 void startDrag(Qt::DropActions supportedActions) override;
83 };
84
ResourceListWidget(QWidget * parent)85 ResourceListWidget::ResourceListWidget(QWidget *parent) :
86 QListWidget(parent)
87 {
88 setDragEnabled(true);
89 }
90
startDrag(Qt::DropActions supportedActions)91 void ResourceListWidget::startDrag(Qt::DropActions supportedActions)
92 {
93 if (supportedActions == Qt::MoveAction)
94 return;
95
96 QListWidgetItem *item = currentItem();
97 if (!item)
98 return;
99
100 const QString filePath = item->data(Qt::UserRole).toString();
101 const QIcon icon = item->icon();
102
103 QMimeData *mimeData = new QMimeData;
104 const QtResourceView::ResourceType type = icon.isNull() ? QtResourceView::ResourceOther : QtResourceView::ResourceImage;
105 mimeData->setText(QtResourceView::encodeMimeData(type , filePath));
106
107 QDrag *drag = new QDrag(this);
108 if (!icon.isNull()) {
109 const QSize size = icon.actualSize(iconSize());
110 drag->setPixmap(icon.pixmap(size));
111 drag->setHotSpot(QPoint(size.width() / 2, size.height() / 2));
112 }
113
114 drag->setMimeData(mimeData);
115 drag->exec(Qt::CopyAction);
116 }
117
118 /* TODO
119
120 1) load the icons in separate thread...Hmm..if Qt is configured with threads....
121 */
122
123 // ---------------------------- QtResourceViewPrivate
124 class QtResourceViewPrivate
125 {
126 QtResourceView *q_ptr = nullptr;
127 Q_DECLARE_PUBLIC(QtResourceView)
128 public:
129 QtResourceViewPrivate(QDesignerFormEditorInterface *core);
130
131 void slotResourceSetActivated(QtResourceSet *resourceSet);
132 void slotCurrentPathChanged(QTreeWidgetItem *);
133 void slotCurrentResourceChanged(QListWidgetItem *);
134 void slotResourceActivated(QListWidgetItem *);
135 void slotEditResources();
136 void slotReloadResources();
137 #if QT_CONFIG(clipboard)
138 void slotCopyResourcePath();
139 #endif
140 void slotListWidgetContextMenuRequested(const QPoint &pos);
141 void slotFilterChanged(const QString &pattern);
142 void createPaths();
143 QTreeWidgetItem *createPath(const QString &path, QTreeWidgetItem *parent);
144 void createResources(const QString &path);
145 void storeExpansionState();
146 void applyExpansionState();
147 void restoreSettings();
148 void saveSettings();
149 void updateActions();
150 void filterOutResources();
151
152 QPixmap makeThumbnail(const QPixmap &pix) const;
153
154 QDesignerFormEditorInterface *m_core;
155 QtResourceModel *m_resourceModel = nullptr;
156 QToolBar *m_toolBar;
157 QWidget *m_filterWidget = nullptr;
158 QTreeWidget *m_treeWidget;
159 QListWidget *m_listWidget;
160 QSplitter *m_splitter = nullptr;
161 QMap<QString, QStringList> m_pathToContents; // full path to contents file names (full path to its resource filenames)
162 QMap<QString, QString> m_pathToParentPath; // full path to full parent path
163 QMap<QString, QStringList> m_pathToSubPaths; // full path to full sub paths
164 QMap<QString, QTreeWidgetItem *> m_pathToItem;
165 QMap<QTreeWidgetItem *, QString> m_itemToPath;
166 QMap<QString, QListWidgetItem *> m_resourceToItem;
167 QMap<QListWidgetItem *, QString> m_itemToResource;
168 QAction *m_editResourcesAction = nullptr;
169 QAction *m_reloadResourcesAction = nullptr;
170 QAction *m_copyResourcePathAction = nullptr;
171
172 QMap<QString, bool> m_expansionState;
173
174 QString m_settingsKey;
175 QString m_filterPattern;
176 bool m_ignoreGuiSignals = false;
177 bool m_resourceEditingEnabled = true;
178 };
179
QtResourceViewPrivate(QDesignerFormEditorInterface * core)180 QtResourceViewPrivate::QtResourceViewPrivate(QDesignerFormEditorInterface *core) :
181 m_core(core),
182 m_toolBar(new QToolBar),
183 m_treeWidget(new QTreeWidget),
184 m_listWidget(new ResourceListWidget)
185 {
186 m_toolBar->setIconSize(QSize(22, 22));
187 }
188
restoreSettings()189 void QtResourceViewPrivate::restoreSettings()
190 {
191 if (m_settingsKey.isEmpty())
192 return;
193
194 QDesignerSettingsInterface *settings = m_core->settingsManager();
195 settings->beginGroup(m_settingsKey);
196
197 m_splitter->restoreState(settings->value(QLatin1String(SplitterPosition)).toByteArray());
198 settings->endGroup();
199 }
200
saveSettings()201 void QtResourceViewPrivate::saveSettings()
202 {
203 if (m_settingsKey.isEmpty())
204 return;
205
206 QDesignerSettingsInterface *settings = m_core->settingsManager();
207 settings->beginGroup(m_settingsKey);
208
209 settings->setValue(QLatin1String(SplitterPosition), m_splitter->saveState());
210 settings->endGroup();
211 }
212
slotEditResources()213 void QtResourceViewPrivate::slotEditResources()
214 {
215 const QString selectedResource
216 = QtResourceEditorDialog::editResources(m_core, m_resourceModel,
217 m_core->dialogGui(), q_ptr);
218 if (!selectedResource.isEmpty())
219 q_ptr->selectResource(selectedResource);
220 }
221
slotReloadResources()222 void QtResourceViewPrivate::slotReloadResources()
223 {
224 if (m_resourceModel) {
225 int errorCount;
226 QString errorMessages;
227 m_resourceModel->reload(&errorCount, &errorMessages);
228 if (errorCount)
229 QtResourceEditorDialog::displayResourceFailures(errorMessages, m_core->dialogGui(), q_ptr);
230 }
231 }
232
233 #if QT_CONFIG(clipboard)
slotCopyResourcePath()234 void QtResourceViewPrivate::slotCopyResourcePath()
235 {
236 const QString path = q_ptr->selectedResource();
237 QClipboard *clipboard = QApplication::clipboard();
238 clipboard->setText(path);
239 }
240 #endif
241
slotListWidgetContextMenuRequested(const QPoint & pos)242 void QtResourceViewPrivate::slotListWidgetContextMenuRequested(const QPoint &pos)
243 {
244 QMenu menu(q_ptr);
245 menu.addAction(m_copyResourcePathAction);
246 menu.exec(m_listWidget->mapToGlobal(pos));
247 }
248
slotFilterChanged(const QString & pattern)249 void QtResourceViewPrivate::slotFilterChanged(const QString &pattern)
250 {
251 m_filterPattern = pattern;
252 filterOutResources();
253 }
254
storeExpansionState()255 void QtResourceViewPrivate::storeExpansionState()
256 {
257 for (auto it = m_pathToItem.cbegin(), end = m_pathToItem.cend(); it != end; ++it)
258 m_expansionState.insert(it.key(), it.value()->isExpanded());
259 }
260
applyExpansionState()261 void QtResourceViewPrivate::applyExpansionState()
262 {
263 for (auto it = m_pathToItem.cbegin(), end = m_pathToItem.cend(); it != end; ++it)
264 it.value()->setExpanded(m_expansionState.value(it.key(), true));
265 }
266
makeThumbnail(const QPixmap & pix) const267 QPixmap QtResourceViewPrivate::makeThumbnail(const QPixmap &pix) const
268 {
269 int w = qMax(48, pix.width());
270 int h = qMax(48, pix.height());
271 QRect imgRect(0, 0, w, h);
272 QImage img(w, h, QImage::Format_ARGB32_Premultiplied);
273 img.fill(0);
274 if (!pix.isNull()) {
275 QRect r(0, 0, pix.width(), pix.height());
276 r.moveCenter(imgRect.center());
277 QPainter p(&img);
278 p.drawPixmap(r.topLeft(), pix);
279 }
280 return QPixmap::fromImage(img);
281 }
282
updateActions()283 void QtResourceViewPrivate::updateActions()
284 {
285 bool resourceActive = false;
286 if (m_resourceModel)
287 resourceActive = m_resourceModel->currentResourceSet();
288
289 m_editResourcesAction->setVisible(m_resourceEditingEnabled);
290 m_editResourcesAction->setEnabled(resourceActive);
291 m_reloadResourcesAction->setEnabled(resourceActive);
292 m_filterWidget->setEnabled(resourceActive);
293 }
294
slotResourceSetActivated(QtResourceSet * resourceSet)295 void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet)
296 {
297 Q_UNUSED(resourceSet);
298
299 updateActions();
300
301 storeExpansionState();
302 const QString currentPath = m_itemToPath.value(m_treeWidget->currentItem());
303 const QString currentResource = m_itemToResource.value(m_listWidget->currentItem());
304 m_treeWidget->clear();
305 m_pathToContents.clear();
306 m_pathToParentPath.clear();
307 m_pathToSubPaths.clear();
308 m_pathToItem.clear();
309 m_itemToPath.clear();
310 m_listWidget->clear();
311 m_resourceToItem.clear();
312 m_itemToResource.clear();
313
314 createPaths();
315 applyExpansionState();
316
317 if (!currentResource.isEmpty())
318 q_ptr->selectResource(currentResource);
319 else if (!currentPath.isEmpty())
320 q_ptr->selectResource(currentPath);
321 filterOutResources();
322 }
323
slotCurrentPathChanged(QTreeWidgetItem * item)324 void QtResourceViewPrivate::slotCurrentPathChanged(QTreeWidgetItem *item)
325 {
326 if (m_ignoreGuiSignals)
327 return;
328
329 m_listWidget->clear();
330 m_resourceToItem.clear();
331 m_itemToResource.clear();
332
333 if (!item)
334 return;
335
336 const QString currentPath = m_itemToPath.value(item);
337 createResources(currentPath);
338 }
339
slotCurrentResourceChanged(QListWidgetItem * item)340 void QtResourceViewPrivate::slotCurrentResourceChanged(QListWidgetItem *item)
341 {
342 m_copyResourcePathAction->setEnabled(item);
343 if (m_ignoreGuiSignals)
344 return;
345
346 emit q_ptr->resourceSelected(m_itemToResource.value(item));
347 }
348
slotResourceActivated(QListWidgetItem * item)349 void QtResourceViewPrivate::slotResourceActivated(QListWidgetItem *item)
350 {
351 if (m_ignoreGuiSignals)
352 return;
353
354 emit q_ptr->resourceActivated(m_itemToResource.value(item));
355 }
356
createPaths()357 void QtResourceViewPrivate::createPaths()
358 {
359 if (!m_resourceModel)
360 return;
361
362 // Resource root up until 4.6 was ':', changed to ":/" as of 4.7
363 const QString root(QStringLiteral(":/"));
364
365 QMap<QString, QString> contents = m_resourceModel->contents();
366 for (auto it = contents.cbegin(), end = contents.cend(); it != end; ++it) {
367 const QFileInfo fi(it.key());
368 QString dirPath = fi.absolutePath();
369 m_pathToContents[dirPath].append(fi.fileName());
370 while (!m_pathToParentPath.contains(dirPath) && dirPath != root) { // create all parent paths
371 const QFileInfo fd(dirPath);
372 const QString parentDirPath = fd.absolutePath();
373 m_pathToParentPath[dirPath] = parentDirPath;
374 m_pathToSubPaths[parentDirPath].append(dirPath);
375 dirPath = parentDirPath;
376 }
377 }
378
379 QQueue<QPair<QString, QTreeWidgetItem *> > pathToParentItemQueue;
380 pathToParentItemQueue.enqueue(qMakePair(root, static_cast<QTreeWidgetItem *>(nullptr)));
381 while (!pathToParentItemQueue.isEmpty()) {
382 QPair<QString, QTreeWidgetItem *> pathToParentItem = pathToParentItemQueue.dequeue();
383 const QString path = pathToParentItem.first;
384 QTreeWidgetItem *item = createPath(path, pathToParentItem.second);
385 const QStringList subPaths = m_pathToSubPaths.value(path);
386 for (const QString &subPath : subPaths)
387 pathToParentItemQueue.enqueue(qMakePair(subPath, item));
388 }
389 }
390
filterOutResources()391 void QtResourceViewPrivate::filterOutResources()
392 {
393 QMap<QString, bool> pathToMatchingContents; // true means the path has any matching contents
394 QMap<QString, bool> pathToVisible; // true means the path has to be shown
395
396 // 1) we go from root path recursively.
397 // 2) we check every path if it contains at least one matching resource - if empty we add it
398 // to pathToMatchingContents and pathToVisible with false, if non empty
399 // we add it with true and change every parent path in pathToVisible to true.
400 // 3) we hide these items which has pathToVisible value false.
401
402 const bool matchAll = m_filterPattern.isEmpty();
403 const QString root(QStringLiteral(":/"));
404
405 QQueue<QString> pathQueue;
406 pathQueue.enqueue(root);
407 while (!pathQueue.isEmpty()) {
408 const QString path = pathQueue.dequeue();
409
410 bool hasContents = matchAll;
411 if (!matchAll) { // the case filter is not empty - we check if the path contains anything
412 // the path contains at least one resource which matches the filter
413 const QStringList fileNames = m_pathToContents.value(path);
414 hasContents =
415 std::any_of(fileNames.cbegin(), fileNames.cend(),
416 [this] (const QString &f) { return f.contains(this->m_filterPattern, Qt::CaseInsensitive); });
417 }
418
419 pathToMatchingContents[path] = hasContents;
420 pathToVisible[path] = hasContents;
421
422 if (hasContents) { // if the path is going to be shown we need to show all its parent paths
423 QString parentPath = m_pathToParentPath.value(path);
424 while (!parentPath.isEmpty()) {
425 QString p = parentPath;
426 if (pathToVisible.value(p)) // parent path is already shown, we break the loop
427 break;
428 pathToVisible[p] = true;
429 parentPath = m_pathToParentPath.value(p);
430 }
431 }
432
433 const QStringList subPaths = m_pathToSubPaths.value(path); // we do the same for children paths
434 for (const QString &subPath : subPaths)
435 pathQueue.enqueue(subPath);
436 }
437
438 // we setup here new path and resource to be activated
439 const QString currentPath = m_itemToPath.value(m_treeWidget->currentItem());
440 QString newCurrentPath = currentPath;
441 QString currentResource = m_itemToResource.value(m_listWidget->currentItem());
442 if (!matchAll) {
443 bool searchForNewPathWithContents = true;
444
445 if (!currentPath.isEmpty()) { // if the currentPath is empty we will search for a new path too
446 QMap<QString, bool>::ConstIterator it = pathToMatchingContents.constFind(currentPath);
447 if (it != pathToMatchingContents.constEnd() && it.value()) // the current item has contents, we don't need to search for another path
448 searchForNewPathWithContents = false;
449 }
450
451 if (searchForNewPathWithContents) {
452 // we find the first path with the matching contents
453 QMap<QString, bool>::ConstIterator itContents = pathToMatchingContents.constBegin();
454 while (itContents != pathToMatchingContents.constEnd()) {
455 if (itContents.value()) {
456 newCurrentPath = itContents.key(); // the new path will be activated
457 break;
458 }
459
460 itContents++;
461 }
462 }
463
464 QFileInfo fi(currentResource);
465 if (!fi.fileName().contains(m_filterPattern, Qt::CaseInsensitive)) { // the case when the current resource is filtered out
466 const QStringList fileNames = m_pathToContents.value(newCurrentPath);
467 // we try to select the first matching resource from the newCurrentPath
468 for (const QString &fileName : fileNames) {
469 if (fileName.contains(m_filterPattern, Qt::CaseInsensitive)) {
470 QDir dirPath(newCurrentPath);
471 currentResource = dirPath.absoluteFilePath(fileName); // the new resource inside newCurrentPath will be activated
472 break;
473 }
474 }
475 }
476 }
477
478 QTreeWidgetItem *newCurrentItem = m_pathToItem.value(newCurrentPath);
479 if (currentPath != newCurrentPath)
480 m_treeWidget->setCurrentItem(newCurrentItem);
481 else
482 slotCurrentPathChanged(newCurrentItem); // trigger filtering on the current path
483
484 QListWidgetItem *currentResourceItem = m_resourceToItem.value(currentResource);
485 if (currentResourceItem) {
486 m_listWidget->setCurrentItem(currentResourceItem);
487 m_listWidget->scrollToItem(currentResourceItem);
488 }
489
490 // hide all paths filtered out
491 for (auto it = pathToVisible.cbegin(), end = pathToVisible.cend(); it != end; ++it) {
492 if (QTreeWidgetItem *item = m_pathToItem.value(it.key()))
493 item->setHidden(!it.value());
494 }
495 }
496
createPath(const QString & path,QTreeWidgetItem * parent)497 QTreeWidgetItem *QtResourceViewPrivate::createPath(const QString &path, QTreeWidgetItem *parent)
498 {
499 QTreeWidgetItem *item = nullptr;
500 if (parent)
501 item = new QTreeWidgetItem(parent);
502 else
503 item = new QTreeWidgetItem(m_treeWidget);
504 m_pathToItem[path] = item;
505 m_itemToPath[item] = path;
506 QString substPath;
507 if (parent) {
508 QFileInfo di(path);
509 substPath = di.fileName();
510 } else {
511 substPath = QStringLiteral("<resource root>");
512 }
513 item->setText(0, substPath);
514 item->setToolTip(0, path);
515 return item;
516 }
517
createResources(const QString & path)518 void QtResourceViewPrivate::createResources(const QString &path)
519 {
520 const bool matchAll = m_filterPattern.isEmpty();
521
522 QDir dir(path);
523 const QStringList fileNames = m_pathToContents.value(path);
524 for (const QString &fileName : fileNames) {
525 const bool showProperty = matchAll || fileName.contains(m_filterPattern, Qt::CaseInsensitive);
526 if (showProperty) {
527 QString filePath = dir.absoluteFilePath(fileName);
528 QFileInfo fi(filePath);
529 if (fi.isFile()) {
530 QListWidgetItem *item = new QListWidgetItem(fi.fileName(), m_listWidget);
531 const QPixmap pix = QPixmap(filePath);
532 if (pix.isNull()) {
533 item->setToolTip(filePath);
534 } else {
535 item->setIcon(QIcon(makeThumbnail(pix)));
536 const QSize size = pix.size();
537 item->setToolTip(QtResourceView::tr("Size: %1 x %2\n%3").arg(size.width()).arg(size.height()).arg(filePath));
538 }
539 item->setFlags(item->flags() | Qt::ItemIsDragEnabled);
540 item->setData(Qt::UserRole, filePath);
541 m_itemToResource[item] = filePath;
542 m_resourceToItem[filePath] = item;
543 }
544 }
545 }
546 }
547
548 // -------------- QtResourceView
549
QtResourceView(QDesignerFormEditorInterface * core,QWidget * parent)550 QtResourceView::QtResourceView(QDesignerFormEditorInterface *core, QWidget *parent) :
551 QWidget(parent),
552 d_ptr(new QtResourceViewPrivate(core))
553 {
554 d_ptr->q_ptr = this;
555
556 QIcon editIcon = QIcon::fromTheme(QStringLiteral("document-properties"), qdesigner_internal::createIconSet(QStringLiteral("edit.png")));
557 d_ptr->m_editResourcesAction = new QAction(editIcon, tr("Edit Resources..."), this);
558 d_ptr->m_toolBar->addAction(d_ptr->m_editResourcesAction);
559 connect(d_ptr->m_editResourcesAction, SIGNAL(triggered()), this, SLOT(slotEditResources()));
560 d_ptr->m_editResourcesAction->setEnabled(false);
561
562 QIcon refreshIcon = QIcon::fromTheme(QStringLiteral("view-refresh"), qdesigner_internal::createIconSet(QStringLiteral("reload.png")));
563 d_ptr->m_reloadResourcesAction = new QAction(refreshIcon, tr("Reload"), this);
564
565 d_ptr->m_toolBar->addAction(d_ptr->m_reloadResourcesAction);
566 connect(d_ptr->m_reloadResourcesAction, SIGNAL(triggered()), this, SLOT(slotReloadResources()));
567 d_ptr->m_reloadResourcesAction->setEnabled(false);
568
569 #if QT_CONFIG(clipboard)
570 QIcon copyIcon = QIcon::fromTheme(QStringLiteral("edit-copy"), qdesigner_internal::createIconSet(QStringLiteral("editcopy.png")));
571 d_ptr->m_copyResourcePathAction = new QAction(copyIcon, tr("Copy Path"), this);
572 connect(d_ptr->m_copyResourcePathAction, SIGNAL(triggered()), this, SLOT(slotCopyResourcePath()));
573 d_ptr->m_copyResourcePathAction->setEnabled(false);
574 #endif
575
576 d_ptr->m_filterWidget = new QWidget(d_ptr->m_toolBar);
577 QHBoxLayout *filterLayout = new QHBoxLayout(d_ptr->m_filterWidget);
578 filterLayout->setContentsMargins(0, 0, 0, 0);
579 QLineEdit *filterLineEdit = new QLineEdit(d_ptr->m_filterWidget);
580 connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotFilterChanged(QString)));
581 filterLineEdit->setPlaceholderText(tr("Filter"));
582 filterLineEdit->setClearButtonEnabled(true);
583 filterLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
584 filterLayout->addWidget(filterLineEdit);
585 d_ptr->m_toolBar->addWidget(d_ptr->m_filterWidget);
586
587 d_ptr->m_splitter = new QSplitter;
588 d_ptr->m_splitter->setChildrenCollapsible(false);
589 d_ptr->m_splitter->addWidget(d_ptr->m_treeWidget);
590 d_ptr->m_splitter->addWidget(d_ptr->m_listWidget);
591
592 QLayout *layout = new QVBoxLayout(this);
593 layout->setContentsMargins(QMargins());
594 layout->setSpacing(0);
595 layout->addWidget(d_ptr->m_toolBar);
596 layout->addWidget(d_ptr->m_splitter);
597
598 d_ptr->m_treeWidget->setColumnCount(1);
599 d_ptr->m_treeWidget->header()->hide();
600 d_ptr->m_treeWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
601
602 d_ptr->m_listWidget->setViewMode(QListView::IconMode);
603 d_ptr->m_listWidget->setResizeMode(QListView::Adjust);
604 d_ptr->m_listWidget->setIconSize(QSize(48, 48));
605 d_ptr->m_listWidget->setGridSize(QSize(64, 64));
606
607 connect(d_ptr->m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
608 this, SLOT(slotCurrentPathChanged(QTreeWidgetItem*)));
609 connect(d_ptr->m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
610 this, SLOT(slotCurrentResourceChanged(QListWidgetItem*)));
611 connect(d_ptr->m_listWidget, SIGNAL(itemActivated(QListWidgetItem*)),
612 this, SLOT(slotResourceActivated(QListWidgetItem*)));
613 d_ptr->m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
614 connect(d_ptr->m_listWidget, SIGNAL(customContextMenuRequested(QPoint)),
615 this, SLOT(slotListWidgetContextMenuRequested(QPoint)));
616 }
617
~QtResourceView()618 QtResourceView::~QtResourceView()
619 {
620 if (!d_ptr->m_settingsKey.isEmpty())
621 d_ptr->saveSettings();
622 }
623
event(QEvent * event)624 bool QtResourceView::event(QEvent *event)
625 {
626 if (event->type() == QEvent::Show) {
627 d_ptr->m_listWidget->scrollToItem(d_ptr->m_listWidget->currentItem());
628 d_ptr->m_treeWidget->scrollToItem(d_ptr->m_treeWidget->currentItem());
629 }
630 return QWidget::event(event);
631 }
632
model() const633 QtResourceModel *QtResourceView::model() const
634 {
635 return d_ptr->m_resourceModel;
636 }
637
selectedResource() const638 QString QtResourceView::selectedResource() const
639 {
640 QListWidgetItem *item = d_ptr->m_listWidget->currentItem();
641 return d_ptr->m_itemToResource.value(item);
642 }
643
selectResource(const QString & resource)644 void QtResourceView::selectResource(const QString &resource)
645 {
646 if (resource.isEmpty())
647 return;
648 QFileInfo fi(resource);
649 QDir dir = fi.absoluteDir();
650 if (fi.isDir())
651 dir = QDir(resource);
652 QString dirPath = dir.absolutePath();
653 const auto cend = d_ptr->m_pathToItem.constEnd();
654 auto it = cend;
655 while ((it = d_ptr->m_pathToItem.constFind(dirPath)) == cend) {
656 if (!dir.cdUp())
657 break;
658 dirPath = dir.absolutePath();
659 }
660 if (it != cend) {
661 QTreeWidgetItem *treeItem = it.value();
662 d_ptr->m_treeWidget->setCurrentItem(treeItem);
663 d_ptr->m_treeWidget->scrollToItem(treeItem);
664 // expand all up to current one is done by qt
665 // list widget is already propagated (currrent changed was sent by qt)
666 QListWidgetItem *item = d_ptr->m_resourceToItem.value(resource);
667 if (item) {
668 d_ptr->m_listWidget->setCurrentItem(item);
669 d_ptr->m_listWidget->scrollToItem(item);
670 }
671 }
672 }
673
settingsKey() const674 QString QtResourceView::settingsKey() const
675 {
676 return d_ptr->m_settingsKey;
677 }
678
setSettingsKey(const QString & key)679 void QtResourceView::setSettingsKey(const QString &key)
680 {
681 if (d_ptr->m_settingsKey == key)
682 return;
683
684 d_ptr->m_settingsKey = key;
685
686 if (key.isEmpty())
687 return;
688
689 d_ptr->restoreSettings();
690 }
691
setResourceModel(QtResourceModel * model)692 void QtResourceView::setResourceModel(QtResourceModel *model)
693 {
694 if (d_ptr->m_resourceModel) {
695 disconnect(d_ptr->m_resourceModel, SIGNAL(resourceSetActivated(QtResourceSet*,bool)),
696 this, SLOT(slotResourceSetActivated(QtResourceSet*)));
697 }
698
699 // clear here
700 d_ptr->m_treeWidget->clear();
701 d_ptr->m_listWidget->clear();
702
703 d_ptr->m_resourceModel = model;
704
705 if (!d_ptr->m_resourceModel)
706 return;
707
708 connect(d_ptr->m_resourceModel, SIGNAL(resourceSetActivated(QtResourceSet*,bool)),
709 this, SLOT(slotResourceSetActivated(QtResourceSet*)));
710
711 // fill new here
712 d_ptr->slotResourceSetActivated(d_ptr->m_resourceModel->currentResourceSet());
713 }
714
isResourceEditingEnabled() const715 bool QtResourceView::isResourceEditingEnabled() const
716 {
717 return d_ptr->m_resourceEditingEnabled;
718 }
719
setResourceEditingEnabled(bool enable)720 void QtResourceView::setResourceEditingEnabled(bool enable)
721 {
722 d_ptr->m_resourceEditingEnabled = enable;
723 d_ptr->updateActions();
724 }
725
setDragEnabled(bool dragEnabled)726 void QtResourceView::setDragEnabled(bool dragEnabled)
727 {
728 d_ptr->m_listWidget->setDragEnabled(dragEnabled);
729 }
730
dragEnabled() const731 bool QtResourceView::dragEnabled() const
732 {
733 return d_ptr->m_listWidget->dragEnabled();
734 }
735
encodeMimeData(ResourceType resourceType,const QString & path)736 QString QtResourceView::encodeMimeData(ResourceType resourceType, const QString &path)
737 {
738 QDomDocument doc;
739 QDomElement elem = doc.createElement(QLatin1String(elementResourceData));
740 switch (resourceType) {
741 case ResourceImage:
742 elem.setAttribute(QLatin1String(typeAttribute), QLatin1String(typeImage));
743 break;
744 case ResourceStyleSheet:
745 elem.setAttribute(QLatin1String(typeAttribute), QLatin1String(typeStyleSheet));
746 break;
747 case ResourceOther:
748 elem.setAttribute(QLatin1String(typeAttribute), QLatin1String(typeOther));
749 break;
750 }
751 elem.setAttribute(QLatin1String(fileAttribute), path);
752 doc.appendChild(elem);
753 return doc.toString();
754 }
755
decodeMimeData(const QMimeData * md,ResourceType * t,QString * file)756 bool QtResourceView::decodeMimeData(const QMimeData *md, ResourceType *t, QString *file)
757 {
758 return md->hasText() ? decodeMimeData(md->text(), t, file) : false;
759 }
760
decodeMimeData(const QString & text,ResourceType * t,QString * file)761 bool QtResourceView::decodeMimeData(const QString &text, ResourceType *t, QString *file)
762 {
763
764 const QString docElementName = QLatin1String(elementResourceData);
765 static const QString docElementString = QLatin1Char('<') + docElementName;
766
767 if (text.isEmpty() || text.indexOf(docElementString) == -1)
768 return false;
769
770 QDomDocument doc;
771 if (!doc.setContent(text))
772 return false;
773
774 const QDomElement domElement = doc.documentElement();
775 if (domElement.tagName() != docElementName)
776 return false;
777
778 if (t) {
779 const QString typeAttr = QLatin1String(typeAttribute);
780 if (domElement.hasAttribute (typeAttr)) {
781 const QString typeValue = domElement.attribute(typeAttr, QLatin1String(typeOther));
782 if (typeValue == QLatin1String(typeImage)) {
783 *t = ResourceImage;
784 } else {
785 *t = typeValue == QLatin1String(typeStyleSheet) ? ResourceStyleSheet : ResourceOther;
786 }
787 }
788 }
789 if (file) {
790 const QString fileAttr = QLatin1String(fileAttribute);
791 if (domElement.hasAttribute(fileAttr)) {
792 *file = domElement.attribute(fileAttr, QString());
793 } else {
794 file->clear();
795 }
796 }
797 return true;
798 }
799
800 // ---------------------------- QtResourceViewDialogPrivate
801
802 class QtResourceViewDialogPrivate
803 {
804 QtResourceViewDialog *q_ptr;
805 Q_DECLARE_PUBLIC(QtResourceViewDialog)
806 public:
807 QtResourceViewDialogPrivate(QDesignerFormEditorInterface *core);
808
slotResourceSelected(const QString & resource)809 void slotResourceSelected(const QString &resource) { setOkButtonEnabled(!resource.isEmpty()); }
setOkButtonEnabled(bool v)810 void setOkButtonEnabled(bool v) { m_box->button(QDialogButtonBox::Ok)->setEnabled(v); }
811
812 QDesignerFormEditorInterface *m_core;
813 QtResourceView *m_view;
814 QDialogButtonBox *m_box;
815 };
816
QtResourceViewDialogPrivate(QDesignerFormEditorInterface * core)817 QtResourceViewDialogPrivate::QtResourceViewDialogPrivate(QDesignerFormEditorInterface *core) :
818 q_ptr(nullptr),
819 m_core(core),
820 m_view(new QtResourceView(core)),
821 m_box(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel))
822 {
823 m_view->setSettingsKey(QLatin1String(ResourceViewDialogC));
824 }
825
826 // ------------ QtResourceViewDialog
QtResourceViewDialog(QDesignerFormEditorInterface * core,QWidget * parent)827 QtResourceViewDialog::QtResourceViewDialog(QDesignerFormEditorInterface *core, QWidget *parent) :
828 QDialog(parent),
829 d_ptr(new QtResourceViewDialogPrivate(core))
830 {
831 setWindowTitle(tr("Select Resource"));
832 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
833 d_ptr->q_ptr = this;
834 QVBoxLayout *layout = new QVBoxLayout(this);
835 layout->addWidget(d_ptr->m_view);
836 layout->addWidget(d_ptr->m_box);
837 connect(d_ptr->m_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
838 connect(d_ptr->m_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
839 connect(d_ptr->m_view, &QtResourceView::resourceActivated, this, &QDialog::accept);
840 connect(d_ptr->m_view, SIGNAL(resourceSelected(QString)), this, SLOT(slotResourceSelected(QString)));
841 d_ptr->setOkButtonEnabled(false);
842 d_ptr->m_view->setResourceModel(core->resourceModel());
843
844 QDesignerSettingsInterface *settings = core->settingsManager();
845 settings->beginGroup(QLatin1String(ResourceViewDialogC));
846
847 const QVariant geometry = settings->value(QLatin1String(Geometry));
848 if (geometry.type() == QVariant::ByteArray) // Used to be a QRect up until 5.4.0, QTBUG-43374.
849 restoreGeometry(geometry.toByteArray());
850
851 settings->endGroup();
852 }
853
~QtResourceViewDialog()854 QtResourceViewDialog::~QtResourceViewDialog()
855 {
856 QDesignerSettingsInterface *settings = d_ptr->m_core->settingsManager();
857 settings->beginGroup(QLatin1String(ResourceViewDialogC));
858
859 settings->setValue(QLatin1String(Geometry), saveGeometry());
860
861 settings->endGroup();
862 }
863
selectedResource() const864 QString QtResourceViewDialog::selectedResource() const
865 {
866 return d_ptr->m_view->selectedResource();
867 }
868
selectResource(const QString & path)869 void QtResourceViewDialog::selectResource(const QString &path)
870 {
871 d_ptr->m_view->selectResource(path);
872 }
873
isResourceEditingEnabled() const874 bool QtResourceViewDialog::isResourceEditingEnabled() const
875 {
876 return d_ptr->m_view->isResourceEditingEnabled();
877 }
878
setResourceEditingEnabled(bool enable)879 void QtResourceViewDialog::setResourceEditingEnabled(bool enable)
880 {
881 d_ptr->m_view->setResourceEditingEnabled(enable);
882 }
883
884 QT_END_NAMESPACE
885
886 #include "moc_qtresourceview_p.cpp"
887