Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
actionrepository.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
6#include "iconloader_p.h"
8
9#include <QtDesigner/abstractformeditor.h>
10#include <QtDesigner/propertysheet.h>
11#include <QtDesigner/qextensionmanager.h>
12
13#include <QtWidgets/qtoolbutton.h>
14#include <QtWidgets/qheaderview.h>
15#include <QtWidgets/qtoolbar.h>
16#include <QtWidgets/qmenu.h>
17
18#include <QtGui/qpixmap.h>
19#include <QtGui/qaction.h>
20#include <QtGui/qdrag.h>
21#include <QtGui/qevent.h>
22#include <QtGui/qstandarditemmodel.h>
23
24#include <QtCore/qset.h>
25#include <QtCore/qdebug.h>
26#include <QtCore/qmetaobject.h>
27
29
30using namespace Qt::StringLiterals;
31
32namespace {
33 enum { listModeIconSize = 16, iconModeIconSize = 24 };
34}
35
36static constexpr auto actionMimeType = "action-repository/actions"_L1;
37static constexpr auto plainTextMimeType = "text/plain"_L1;
38
39static inline QAction *actionOfItem(const QStandardItem* item)
40{
41 return qvariant_cast<QAction*>(item->data(qdesigner_internal::ActionModel::ActionRole));
42}
43
44namespace qdesigner_internal {
45
46// ----------- ActionModel
50{
52 headers += tr("Name");
53 headers += tr("Used");
54 headers += tr("Text");
55 headers += tr("Shortcut");
56 headers += tr("Checkable");
57 headers += tr("ToolTip");
58 headers += tr("MenuRole");
61}
62
64{
65 removeRows(0, rowCount());
66}
67
69{
70 const int rows = rowCount();
71 for (int i = 0; i < rows; i++)
72 if (action == actionOfItem(item(i)))
73 return i;
74 return -1;
75}
76
78{
80 // need to create the row list ... grrr..
81 if (row >= rowCount())
82 return;
83
85 for (int i = 0; i < NumColumns; i++)
86 list += item(row, i);
87
89}
90
92{
94}
95
115
116// Find the associated menus and toolbars, ignore toolbuttons
118{
122 for (QObject *obj : rc) {
123 if (QWidget *w = qobject_cast<QWidget *>(obj)) {
124 if (qobject_cast<const QMenu *>(w) || qobject_cast<const QToolBar *>(w))
126 }
127 }
128 return result;
129}
130
131// shortcut is a fake property, need to retrieve it via property sheet.
139
147
149 const QIcon &defaultIcon,
151{
152
153 // Tooltip, mostly for icon view mode
155 const QString text = action->text();
156 if (!text.isEmpty())
157 firstTooltip += u'\n' + text;
158
160
163 QIcon icon = action->icon();
164 if (icon.isNull())
166 item->setIcon(icon);
169 // Used
171 const bool used = !associatedDesignerWidgets.isEmpty();
172 item = sl[UsedColumn];
174 if (used) {
176 const auto separator = ", "_L1;
178 for (int i = 0; i < count; i++) {
179 if (i)
182 }
184 } else {
186 }
187 // text
188 item = sl[TextColumn];
189 item->setText(action->text());
191 // shortcut
196 // checkable
198 // ToolTip. This might be multi-line, rich text
202 item->setText(toolTip.replace(u'\n', u' '));
203 // menuRole
204 const auto menuRole = action->menuRole();
207}
208
218
219// Resource images are plain text. The drag needs to be restricted, however.
224
226{
227 return item(row, NameColumn)->text();
228}
229
231{
232 if (action != Qt::CopyAction)
233 return false;
234
236 if (!droppedItem)
237 return false;
238
239
243 return false;
244
246 return true;
247}
248
250{
251 if (!index.isValid())
252 return nullptr;
254 if (!i)
255 return nullptr;
256 return actionOfItem(i);
257}
258
260{
261 for (int r = rowCount() - 1; r >= 0; --r) {
263 if (actionOfItem(stdItem) == a)
264 return indexFromItem(stdItem);
265 }
266 return {};
267}
268
269// helpers
270
271static bool handleImageDragEnterMoveEvent(QDropEvent *event)
272{
273 QtResourceView::ResourceType type;
274 const bool rc = QtResourceView::decodeMimeData(event->mimeData(), &type) && type == QtResourceView::ResourceImage;
275 if (rc)
276 event->acceptProposedAction();
277 else
278 event->ignore();
279 return rc;
280}
281
282static void handleImageDropEvent(const QAbstractItemView *iv, QDropEvent *event, ActionModel *am)
283{
284 const QModelIndex index = iv->indexAt(event->position().toPoint());
285 if (!index.isValid()) {
286 event->ignore();
287 return;
288 }
289
290 if (!handleImageDragEnterMoveEvent(event))
291 return;
292
293 am->dropMimeData(event->mimeData(), event->proposedAction(), index.row(), 0, iv->rootIndex());
294}
295
296// Basically mimic QAbstractItemView's startDrag routine, except that
297// another pixmap is used, we don't want the whole row.
298
312
313// ---------------- ActionTreeView:
314ActionTreeView::ActionTreeView(ActionModel *model, QWidget *parent) :
315 QTreeView(parent),
316 m_model(model)
317{
318 setDragEnabled(true);
319 setAcceptDrops(true);
320 setDropIndicatorShown(true);
321 setDragDropMode(DragDrop);
322 setModel(model);
323 setRootIsDecorated(false);
324 setTextElideMode(Qt::ElideMiddle);
325
326 setModel(model);
327 connect(this, &QTreeView::activated, this, &ActionTreeView::slotActivated);
328 connect(header(), &QHeaderView::sectionDoubleClicked,
329 this, &QTreeView::resizeColumnToContents);
330
331 setIconSize(QSize(listModeIconSize, listModeIconSize));
332
333}
334
336{
337 return m_model->actionAt(currentIndex());
338}
339
340void ActionTreeView::filter(const QString &text)
341{
342 const int rowCount = m_model->rowCount();
343 const bool empty = text.isEmpty();
344 const QModelIndex parent = rootIndex();
345 for (int i = 0; i < rowCount; i++)
346 setRowHidden(i, parent, !empty && !m_model->actionName(i).contains(text, Qt::CaseInsensitive));
347}
348
349void ActionTreeView::dragEnterEvent(QDragEnterEvent *event)
350{
351 handleImageDragEnterMoveEvent(event);
352}
353
354void ActionTreeView::dragMoveEvent(QDragMoveEvent *event)
355{
356 handleImageDragEnterMoveEvent(event);
357}
358
359void ActionTreeView::dropEvent(QDropEvent *event)
360{
361 handleImageDropEvent(this, event, m_model);
362}
363
364void ActionTreeView::focusInEvent(QFocusEvent *event)
365{
366 QTreeView::focusInEvent(event);
367 // Make property editor display current action
368 if (QAction *a = currentAction())
369 emit currentActionChanged(a);
370}
371
372void ActionTreeView::contextMenuEvent(QContextMenuEvent *event)
373{
374 emit actionContextMenuRequested(event, m_model->actionAt(indexAt(event->pos())));
375}
376
377void ActionTreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
378{
379 emit currentActionChanged(m_model->actionAt(current));
380 QTreeView::currentChanged(current, previous);
381}
382
383void ActionTreeView::slotActivated(const QModelIndex &index)
384{
385 emit actionActivated(m_model->actionAt(index), index.column());
386}
387
388void ActionTreeView::startDrag(Qt::DropActions supportedActions)
389{
390 startActionDrag(this, m_model, selectedIndexes(), supportedActions);
391}
392
393// ---------------- ActionListView:
394ActionListView::ActionListView(ActionModel *model, QWidget *parent) :
395 QListView(parent),
396 m_model(model)
397{
398 setDragEnabled(true);
399 setAcceptDrops(true);
400 setDropIndicatorShown(true);
401 setDragDropMode(DragDrop);
402 setModel(model);
403 setTextElideMode(Qt::ElideMiddle);
404 connect(this, &QListView::activated, this, &ActionListView::slotActivated);
405
406 // We actually want 'Static' as the user should be able to
407 // drag away actions only (not to rearrange icons).
408 // We emulate that by not accepting our own
409 // drag data. 'Static' causes the list view to disable drag and drop
410 // on the viewport.
411 setMovement(Snap);
412 setViewMode(IconMode);
413 setIconSize(QSize(iconModeIconSize, iconModeIconSize));
414 setGridSize(QSize(4 * iconModeIconSize, 2 * iconModeIconSize));
415 setSpacing(iconModeIconSize / 3);
416}
417
419{
420 return m_model->actionAt(currentIndex());
421}
422
423void ActionListView::filter(const QString &text)
424{
425 const int rowCount = m_model->rowCount();
426 const bool empty = text.isEmpty();
427 for (int i = 0; i < rowCount; i++)
428 setRowHidden(i, !empty && !m_model->actionName(i).contains(text, Qt::CaseInsensitive));
429}
430
431void ActionListView::dragEnterEvent(QDragEnterEvent *event)
432{
433 handleImageDragEnterMoveEvent(event);
434}
435
436void ActionListView::dragMoveEvent(QDragMoveEvent *event)
437{
438 handleImageDragEnterMoveEvent(event);
439}
440
441void ActionListView::dropEvent(QDropEvent *event)
442{
443 handleImageDropEvent(this, event, m_model);
444}
445
446void ActionListView::focusInEvent(QFocusEvent *event)
447{
448 QListView::focusInEvent(event);
449 // Make property editor display current action
450 if (QAction *a = currentAction())
451 emit currentActionChanged(a);
452}
453
454void ActionListView::contextMenuEvent(QContextMenuEvent *event)
455{
456 emit actionContextMenuRequested(event, m_model->actionAt(indexAt(event->pos())));
457}
458
459void ActionListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
460{
461 emit currentActionChanged(m_model->actionAt(current));
462 QListView::currentChanged(current, previous);
463}
464
465void ActionListView::slotActivated(const QModelIndex &index)
466{
467 emit actionActivated(m_model->actionAt(index));
468}
469
470void ActionListView::startDrag(Qt::DropActions supportedActions)
471{
472 startActionDrag(this, m_model, selectedIndexes(), supportedActions);
473}
474
475// ActionView
476ActionView::ActionView(QWidget *parent) :
477 QStackedWidget(parent),
478 m_model(new ActionModel(this)),
479 m_actionTreeView(new ActionTreeView(m_model)),
480 m_actionListView(new ActionListView(m_model))
481{
482 addWidget(m_actionListView);
483 addWidget(m_actionTreeView);
484 // Wire signals
485 connect(m_actionTreeView, &ActionTreeView::actionContextMenuRequested,
486 this, &ActionView::contextMenuRequested);
487 connect(m_actionListView, &ActionListView::actionContextMenuRequested,
488 this, &ActionView::contextMenuRequested);
489
490 // make it possible for vs integration to reimplement edit action dialog
491 // [which it shouldn't do actually]
492 connect(m_actionListView, &ActionListView::actionActivated,
493 this, [this](QAction *a) { this->activated(a, -1); });
494 connect(m_actionTreeView, &ActionTreeView::actionActivated, this, &ActionView::activated);
495
496 connect(m_actionListView, &ActionListView::currentActionChanged,
497 this, &ActionView::slotCurrentChanged);
498 connect(m_actionTreeView, &ActionTreeView::currentActionChanged,
499 this, &ActionView::slotCurrentChanged);
500
501 connect(m_model, &ActionModel::resourceImageDropped,
502 this, &ActionView::resourceImageDropped);
503
504 // sync selection models
505 QItemSelectionModel *selectionModel = m_actionTreeView->selectionModel();
506 m_actionListView->setSelectionModel(selectionModel);
507 connect(selectionModel, &QItemSelectionModel::selectionChanged,
508 this, &ActionView::selectionChanged);
509}
510
512{
513 return currentWidget() == m_actionListView ? IconView : DetailedView;
514}
515
517{
518 if (viewMode() == lm)
519 return;
520
521 switch (lm) {
522 case IconView:
523 setCurrentWidget(m_actionListView);
524 break;
525 case DetailedView:
526 setCurrentWidget(m_actionTreeView);
527 break;
528 default:
529 break;
530 }
531}
532
533void ActionView::slotCurrentChanged(QAction *action)
534{
535 // emit only for currently visible
536 if (sender() == currentWidget())
538}
539
540void ActionView::filter(const QString &text)
541{
542 m_actionTreeView->filter(text);
543 m_actionListView->filter(text);
544}
545
547{
548 m_actionTreeView->selectAll();
549}
550
552{
553 m_actionTreeView->selectionModel()->clearSelection();
554}
555
556void ActionView::selectAction(QAction *a)
557{
559 if (index.isValid())
561}
562
563void ActionView::setCurrentIndex(const QModelIndex &index)
564{
565 m_actionTreeView->setCurrentIndex(index);
566}
567
569{
570 return m_actionListView->currentAction();
571}
572
573void ActionView::setSelectionMode(QAbstractItemView::SelectionMode sm)
574{
575 m_actionTreeView->setSelectionMode(sm);
576 m_actionListView->setSelectionMode(sm);
577}
578
580{
581 return m_actionListView->selectionMode();
582}
583
585{
586 return m_actionListView->selectionModel()->selection();
587}
588
590{
591 ActionList rc;
592 const QModelIndexList &indexes = selection().indexes();
593 for (const QModelIndex &index : indexes) {
594 if (index.column() == 0)
595 rc += actionOfItem(m_model->itemFromIndex(index));
596 }
597 return rc;
598}
599// ---------- ActionRepositoryMimeData
605
611
616
618{
619
620 // Try to find a suitable pixmap. Grab either widget or icon.
621 const QIcon icon = action->icon();
622 if (!icon.isNull())
623 return icon.pixmap(QSize(22, 22));
624
626 for (QObject *o : associatedObjects) {
628 return tb->grab(QRect(0, 0, -1, -1));
629 }
630
631 // Create a QToolButton
633 tb->setText(action->text());
635 tb->adjustSize();
636 const QPixmap rc = tb->grab(QRect(0, 0, -1, -1));
637 tb->deleteLater();
638 return rc;
639}
640
650
651} // namespace qdesigner_internal
652
653QT_END_NAMESPACE
static QAction * actionOfItem(const QStandardItem *item)
static constexpr auto actionMimeType
static constexpr auto plainTextMimeType
void dragEnterEvent(QDragEnterEvent *event) override
void dropEvent(QDropEvent *event) override
void focusInEvent(QFocusEvent *event) override
void contextMenuEvent(QContextMenuEvent *event) override
void startDrag(Qt::DropActions supportedActions) override
void dragMoveEvent(QDragMoveEvent *event) override
void dropEvent(QDropEvent *event) override
void dragMoveEvent(QDragMoveEvent *event) override
void focusInEvent(QFocusEvent *event) override
void startDrag(Qt::DropActions supportedActions) override
void contextMenuEvent(QContextMenuEvent *event) override
void dragEnterEvent(QDragEnterEvent *event) override
void setSelectionMode(QAbstractItemView::SelectionMode sm)
QAbstractItemView::SelectionMode selectionMode() const
void setCurrentIndex(const QModelIndex &index)
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static void handleImageDropEvent(const QAbstractItemView *iv, QDropEvent *event, ActionModel *am)
static bool handleImageDragEnterMoveEvent(QDropEvent *event)
void startActionDrag(QWidget *dragParent, ActionModel *model, const QModelIndexList &indexes, Qt::DropActions supportedActions)