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
64QT_BEGIN_NAMESPACE
65
66static const char *elementResourceData = "resource";
67static const char *typeAttribute = "type";
68static const char *typeImage = "image";
69static const char *typeStyleSheet = "stylesheet";
70static const char *typeOther = "other";
71static const char *fileAttribute = "file";
72static const char *SplitterPosition = "SplitterPosition";
73static const char *Geometry = "Geometry";
74static const char *ResourceViewDialogC = "ResourceDialog";
75
76// ---------------- ResourceListWidget: A list widget that has drag enabled
77class ResourceListWidget : public QListWidget {
78public:
79 ResourceListWidget(QWidget *parent = nullptr);
80
81protected:
82 void startDrag(Qt::DropActions supportedActions) override;
83};
84
85ResourceListWidget::ResourceListWidget(QWidget *parent) :
86 QListWidget(parent)
87{
88 setDragEnabled(true);
89}
90
91void 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(role: 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(resourceType: type , path: filePath));
106
107 QDrag *drag = new QDrag(this);
108 if (!icon.isNull()) {
109 const QSize size = icon.actualSize(size: 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(supportedActions: 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
124class QtResourceViewPrivate
125{
126 QtResourceView *q_ptr = nullptr;
127 Q_DECLARE_PUBLIC(QtResourceView)
128public:
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
180QtResourceViewPrivate::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
189void QtResourceViewPrivate::restoreSettings()
190{
191 if (m_settingsKey.isEmpty())
192 return;
193
194 QDesignerSettingsInterface *settings = m_core->settingsManager();
195 settings->beginGroup(prefix: m_settingsKey);
196
197 m_splitter->restoreState(state: settings->value(key: QLatin1String(SplitterPosition)).toByteArray());
198 settings->endGroup();
199}
200
201void QtResourceViewPrivate::saveSettings()
202{
203 if (m_settingsKey.isEmpty())
204 return;
205
206 QDesignerSettingsInterface *settings = m_core->settingsManager();
207 settings->beginGroup(prefix: m_settingsKey);
208
209 settings->setValue(key: QLatin1String(SplitterPosition), value: m_splitter->saveState());
210 settings->endGroup();
211}
212
213void QtResourceViewPrivate::slotEditResources()
214{
215 const QString selectedResource
216 = QtResourceEditorDialog::editResources(core: m_core, model: m_resourceModel,
217 dlgGui: m_core->dialogGui(), parent: q_ptr);
218 if (!selectedResource.isEmpty())
219 q_ptr->selectResource(resource: selectedResource);
220}
221
222void QtResourceViewPrivate::slotReloadResources()
223{
224 if (m_resourceModel) {
225 int errorCount;
226 QString errorMessages;
227 m_resourceModel->reload(errorCount: &errorCount, errorMessages: &errorMessages);
228 if (errorCount)
229 QtResourceEditorDialog::displayResourceFailures(logOutput: errorMessages, dlgGui: m_core->dialogGui(), parent: q_ptr);
230 }
231}
232
233#if QT_CONFIG(clipboard)
234void QtResourceViewPrivate::slotCopyResourcePath()
235{
236 const QString path = q_ptr->selectedResource();
237 QClipboard *clipboard = QApplication::clipboard();
238 clipboard->setText(path);
239}
240#endif
241
242void QtResourceViewPrivate::slotListWidgetContextMenuRequested(const QPoint &pos)
243{
244 QMenu menu(q_ptr);
245 menu.addAction(action: m_copyResourcePathAction);
246 menu.exec(pos: m_listWidget->mapToGlobal(pos));
247}
248
249void QtResourceViewPrivate::slotFilterChanged(const QString &pattern)
250{
251 m_filterPattern = pattern;
252 filterOutResources();
253}
254
255void QtResourceViewPrivate::storeExpansionState()
256{
257 for (auto it = m_pathToItem.cbegin(), end = m_pathToItem.cend(); it != end; ++it)
258 m_expansionState.insert(akey: it.key(), avalue: it.value()->isExpanded());
259}
260
261void QtResourceViewPrivate::applyExpansionState()
262{
263 for (auto it = m_pathToItem.cbegin(), end = m_pathToItem.cend(); it != end; ++it)
264 it.value()->setExpanded(m_expansionState.value(akey: it.key(), adefaultValue: true));
265}
266
267QPixmap QtResourceViewPrivate::makeThumbnail(const QPixmap &pix) const
268{
269 int w = qMax(a: 48, b: pix.width());
270 int h = qMax(a: 48, b: pix.height());
271 QRect imgRect(0, 0, w, h);
272 QImage img(w, h, QImage::Format_ARGB32_Premultiplied);
273 img.fill(pixel: 0);
274 if (!pix.isNull()) {
275 QRect r(0, 0, pix.width(), pix.height());
276 r.moveCenter(p: imgRect.center());
277 QPainter p(&img);
278 p.drawPixmap(p: r.topLeft(), pm: pix);
279 }
280 return QPixmap::fromImage(image: img);
281}
282
283void 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
295void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet)
296{
297 Q_UNUSED(resourceSet);
298
299 updateActions();
300
301 storeExpansionState();
302 const QString currentPath = m_itemToPath.value(akey: m_treeWidget->currentItem());
303 const QString currentResource = m_itemToResource.value(akey: 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(resource: currentResource);
319 else if (!currentPath.isEmpty())
320 q_ptr->selectResource(resource: currentPath);
321 filterOutResources();
322}
323
324void 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(akey: item);
337 createResources(path: currentPath);
338}
339
340void QtResourceViewPrivate::slotCurrentResourceChanged(QListWidgetItem *item)
341{
342 m_copyResourcePathAction->setEnabled(item);
343 if (m_ignoreGuiSignals)
344 return;
345
346 emit q_ptr->resourceSelected(resource: m_itemToResource.value(akey: item));
347}
348
349void QtResourceViewPrivate::slotResourceActivated(QListWidgetItem *item)
350{
351 if (m_ignoreGuiSignals)
352 return;
353
354 emit q_ptr->resourceActivated(resource: m_itemToResource.value(akey: item));
355}
356
357void 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(t: fi.fileName());
370 while (!m_pathToParentPath.contains(akey: 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(t: dirPath);
375 dirPath = parentDirPath;
376 }
377 }
378
379 QQueue<QPair<QString, QTreeWidgetItem *> > pathToParentItemQueue;
380 pathToParentItemQueue.enqueue(t: qMakePair(x: root, y: 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, parent: pathToParentItem.second);
385 const QStringList subPaths = m_pathToSubPaths.value(akey: path);
386 for (const QString &subPath : subPaths)
387 pathToParentItemQueue.enqueue(t: qMakePair(x: subPath, y: item));
388 }
389}
390
391void 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(t: 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(akey: path);
414 hasContents =
415 std::any_of(first: fileNames.cbegin(), last: fileNames.cend(),
416 pred: [this] (const QString &f) { return f.contains(s: this->m_filterPattern, cs: 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(akey: path);
424 while (!parentPath.isEmpty()) {
425 QString p = parentPath;
426 if (pathToVisible.value(akey: p)) // parent path is already shown, we break the loop
427 break;
428 pathToVisible[p] = true;
429 parentPath = m_pathToParentPath.value(akey: p);
430 }
431 }
432
433 const QStringList subPaths = m_pathToSubPaths.value(akey: path); // we do the same for children paths
434 for (const QString &subPath : subPaths)
435 pathQueue.enqueue(t: subPath);
436 }
437
438 // we setup here new path and resource to be activated
439 const QString currentPath = m_itemToPath.value(akey: m_treeWidget->currentItem());
440 QString newCurrentPath = currentPath;
441 QString currentResource = m_itemToResource.value(akey: 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(akey: 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(s: m_filterPattern, cs: Qt::CaseInsensitive)) { // the case when the current resource is filtered out
466 const QStringList fileNames = m_pathToContents.value(akey: newCurrentPath);
467 // we try to select the first matching resource from the newCurrentPath
468 for (const QString &fileName : fileNames) {
469 if (fileName.contains(s: m_filterPattern, cs: 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(akey: newCurrentPath);
479 if (currentPath != newCurrentPath)
480 m_treeWidget->setCurrentItem(newCurrentItem);
481 else
482 slotCurrentPathChanged(item: newCurrentItem); // trigger filtering on the current path
483
484 QListWidgetItem *currentResourceItem = m_resourceToItem.value(akey: currentResource);
485 if (currentResourceItem) {
486 m_listWidget->setCurrentItem(currentResourceItem);
487 m_listWidget->scrollToItem(item: 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(akey: it.key()))
493 item->setHidden(!it.value());
494 }
495}
496
497QTreeWidgetItem *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(column: 0, atext: substPath);
514 item->setToolTip(column: 0, atoolTip: path);
515 return item;
516}
517
518void 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(akey: path);
524 for (const QString &fileName : fileNames) {
525 const bool showProperty = matchAll || fileName.contains(s: m_filterPattern, cs: 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(s: "Size: %1 x %2\n%3").arg(a: size.width()).arg(a: size.height()).arg(a: filePath));
538 }
539 item->setFlags(item->flags() | Qt::ItemIsDragEnabled);
540 item->setData(role: Qt::UserRole, value: filePath);
541 m_itemToResource[item] = filePath;
542 m_resourceToItem[filePath] = item;
543 }
544 }
545 }
546}
547
548// -------------- QtResourceView
549
550QtResourceView::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"), fallback: qdesigner_internal::createIconSet(QStringLiteral("edit.png")));
557 d_ptr->m_editResourcesAction = new QAction(editIcon, tr(s: "Edit Resources..."), this);
558 d_ptr->m_toolBar->addAction(action: d_ptr->m_editResourcesAction);
559 connect(sender: d_ptr->m_editResourcesAction, SIGNAL(triggered()), receiver: this, SLOT(slotEditResources()));
560 d_ptr->m_editResourcesAction->setEnabled(false);
561
562 QIcon refreshIcon = QIcon::fromTheme(QStringLiteral("view-refresh"), fallback: qdesigner_internal::createIconSet(QStringLiteral("reload.png")));
563 d_ptr->m_reloadResourcesAction = new QAction(refreshIcon, tr(s: "Reload"), this);
564
565 d_ptr->m_toolBar->addAction(action: d_ptr->m_reloadResourcesAction);
566 connect(sender: d_ptr->m_reloadResourcesAction, SIGNAL(triggered()), receiver: this, SLOT(slotReloadResources()));
567 d_ptr->m_reloadResourcesAction->setEnabled(false);
568
569#if QT_CONFIG(clipboard)
570 QIcon copyIcon = QIcon::fromTheme(QStringLiteral("edit-copy"), fallback: qdesigner_internal::createIconSet(QStringLiteral("editcopy.png")));
571 d_ptr->m_copyResourcePathAction = new QAction(copyIcon, tr(s: "Copy Path"), this);
572 connect(sender: d_ptr->m_copyResourcePathAction, SIGNAL(triggered()), receiver: 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(left: 0, top: 0, right: 0, bottom: 0);
579 QLineEdit *filterLineEdit = new QLineEdit(d_ptr->m_filterWidget);
580 connect(sender: filterLineEdit, SIGNAL(textChanged(QString)), receiver: this, SLOT(slotFilterChanged(QString)));
581 filterLineEdit->setPlaceholderText(tr(s: "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(widget: 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(widget: d_ptr->m_treeWidget);
590 d_ptr->m_splitter->addWidget(widget: d_ptr->m_listWidget);
591
592 QLayout *layout = new QVBoxLayout(this);
593 layout->setContentsMargins(QMargins());
594 layout->setSpacing(0);
595 layout->addWidget(w: d_ptr->m_toolBar);
596 layout->addWidget(w: 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(sender: d_ptr->m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
608 receiver: this, SLOT(slotCurrentPathChanged(QTreeWidgetItem*)));
609 connect(sender: d_ptr->m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
610 receiver: this, SLOT(slotCurrentResourceChanged(QListWidgetItem*)));
611 connect(sender: d_ptr->m_listWidget, SIGNAL(itemActivated(QListWidgetItem*)),
612 receiver: this, SLOT(slotResourceActivated(QListWidgetItem*)));
613 d_ptr->m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
614 connect(sender: d_ptr->m_listWidget, SIGNAL(customContextMenuRequested(QPoint)),
615 receiver: this, SLOT(slotListWidgetContextMenuRequested(QPoint)));
616}
617
618QtResourceView::~QtResourceView()
619{
620 if (!d_ptr->m_settingsKey.isEmpty())
621 d_ptr->saveSettings();
622}
623
624bool QtResourceView::event(QEvent *event)
625{
626 if (event->type() == QEvent::Show) {
627 d_ptr->m_listWidget->scrollToItem(item: d_ptr->m_listWidget->currentItem());
628 d_ptr->m_treeWidget->scrollToItem(item: d_ptr->m_treeWidget->currentItem());
629 }
630 return QWidget::event(event);
631}
632
633QtResourceModel *QtResourceView::model() const
634{
635 return d_ptr->m_resourceModel;
636}
637
638QString QtResourceView::selectedResource() const
639{
640 QListWidgetItem *item = d_ptr->m_listWidget->currentItem();
641 return d_ptr->m_itemToResource.value(akey: item);
642}
643
644void 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(akey: 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(item: 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(akey: resource);
667 if (item) {
668 d_ptr->m_listWidget->setCurrentItem(item);
669 d_ptr->m_listWidget->scrollToItem(item);
670 }
671 }
672}
673
674QString QtResourceView::settingsKey() const
675{
676 return d_ptr->m_settingsKey;
677}
678
679void 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
692void QtResourceView::setResourceModel(QtResourceModel *model)
693{
694 if (d_ptr->m_resourceModel) {
695 disconnect(sender: d_ptr->m_resourceModel, SIGNAL(resourceSetActivated(QtResourceSet*,bool)),
696 receiver: 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(sender: d_ptr->m_resourceModel, SIGNAL(resourceSetActivated(QtResourceSet*,bool)),
709 receiver: this, SLOT(slotResourceSetActivated(QtResourceSet*)));
710
711 // fill new here
712 d_ptr->slotResourceSetActivated(resourceSet: d_ptr->m_resourceModel->currentResourceSet());
713}
714
715bool QtResourceView::isResourceEditingEnabled() const
716{
717 return d_ptr->m_resourceEditingEnabled;
718}
719
720void QtResourceView::setResourceEditingEnabled(bool enable)
721{
722 d_ptr->m_resourceEditingEnabled = enable;
723 d_ptr->updateActions();
724}
725
726void QtResourceView::setDragEnabled(bool dragEnabled)
727{
728 d_ptr->m_listWidget->setDragEnabled(dragEnabled);
729}
730
731bool QtResourceView::dragEnabled() const
732{
733 return d_ptr->m_listWidget->dragEnabled();
734}
735
736QString QtResourceView::encodeMimeData(ResourceType resourceType, const QString &path)
737{
738 QDomDocument doc;
739 QDomElement elem = doc.createElement(tagName: QLatin1String(elementResourceData));
740 switch (resourceType) {
741 case ResourceImage:
742 elem.setAttribute(name: QLatin1String(typeAttribute), value: QLatin1String(typeImage));
743 break;
744 case ResourceStyleSheet:
745 elem.setAttribute(name: QLatin1String(typeAttribute), value: QLatin1String(typeStyleSheet));
746 break;
747 case ResourceOther:
748 elem.setAttribute(name: QLatin1String(typeAttribute), value: QLatin1String(typeOther));
749 break;
750 }
751 elem.setAttribute(name: QLatin1String(fileAttribute), value: path);
752 doc.appendChild(newChild: elem);
753 return doc.toString();
754}
755
756bool QtResourceView::decodeMimeData(const QMimeData *md, ResourceType *t, QString *file)
757{
758 return md->hasText() ? decodeMimeData(text: md->text(), t, file) : false;
759}
760
761bool 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(s: 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 (name: typeAttr)) {
781 const QString typeValue = domElement.attribute(name: typeAttr, defValue: 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(name: fileAttr)) {
792 *file = domElement.attribute(name: fileAttr, defValue: QString());
793 } else {
794 file->clear();
795 }
796 }
797 return true;
798}
799
800// ---------------------------- QtResourceViewDialogPrivate
801
802class QtResourceViewDialogPrivate
803{
804 QtResourceViewDialog *q_ptr;
805 Q_DECLARE_PUBLIC(QtResourceViewDialog)
806public:
807 QtResourceViewDialogPrivate(QDesignerFormEditorInterface *core);
808
809 void slotResourceSelected(const QString &resource) { setOkButtonEnabled(!resource.isEmpty()); }
810 void setOkButtonEnabled(bool v) { m_box->button(which: QDialogButtonBox::Ok)->setEnabled(v); }
811
812 QDesignerFormEditorInterface *m_core;
813 QtResourceView *m_view;
814 QDialogButtonBox *m_box;
815};
816
817QtResourceViewDialogPrivate::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
827QtResourceViewDialog::QtResourceViewDialog(QDesignerFormEditorInterface *core, QWidget *parent) :
828 QDialog(parent),
829 d_ptr(new QtResourceViewDialogPrivate(core))
830{
831 setWindowTitle(tr(s: "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(sender: d_ptr->m_box, signal: &QDialogButtonBox::accepted, receiver: this, slot: &QDialog::accept);
838 connect(sender: d_ptr->m_box, signal: &QDialogButtonBox::rejected, receiver: this, slot: &QDialog::reject);
839 connect(sender: d_ptr->m_view, signal: &QtResourceView::resourceActivated, receiver: this, slot: &QDialog::accept);
840 connect(sender: d_ptr->m_view, SIGNAL(resourceSelected(QString)), receiver: 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(prefix: QLatin1String(ResourceViewDialogC));
846
847 const QVariant geometry = settings->value(key: QLatin1String(Geometry));
848 if (geometry.type() == QVariant::ByteArray) // Used to be a QRect up until 5.4.0, QTBUG-43374.
849 restoreGeometry(geometry: geometry.toByteArray());
850
851 settings->endGroup();
852}
853
854QtResourceViewDialog::~QtResourceViewDialog()
855{
856 QDesignerSettingsInterface *settings = d_ptr->m_core->settingsManager();
857 settings->beginGroup(prefix: QLatin1String(ResourceViewDialogC));
858
859 settings->setValue(key: QLatin1String(Geometry), value: saveGeometry());
860
861 settings->endGroup();
862}
863
864QString QtResourceViewDialog::selectedResource() const
865{
866 return d_ptr->m_view->selectedResource();
867}
868
869void QtResourceViewDialog::selectResource(const QString &path)
870{
871 d_ptr->m_view->selectResource(resource: path);
872}
873
874bool QtResourceViewDialog::isResourceEditingEnabled() const
875{
876 return d_ptr->m_view->isResourceEditingEnabled();
877}
878
879void QtResourceViewDialog::setResourceEditingEnabled(bool enable)
880{
881 d_ptr->m_view->setResourceEditingEnabled(enable);
882}
883
884QT_END_NAMESPACE
885
886#include "moc_qtresourceview_p.cpp"
887

source code of qttools/src/designer/src/lib/shared/qtresourceview.cpp