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
220
221// Resource images are plain text. The drag needs to be restricted, however.
226
228{
229 return item(row, NameColumn)->text();
230}
231
233{
234 if (action != Qt::CopyAction)
235 return false;
236
238 if (!droppedItem)
239 return false;
240
241
245 return false;
246
248 return true;
249}
250
252{
253 if (!index.isValid())
254 return nullptr;
256 if (!i)
257 return nullptr;
258 return actionOfItem(i);
259}
260
262{
263 for (int r = rowCount() - 1; r >= 0; --r) {
265 if (actionOfItem(stdItem) == a)
266 return indexFromItem(stdItem);
267 }
268 return {};
269}
270
271// helpers
272
273static bool handleImageDragEnterMoveEvent(QDropEvent *event)
274{
275 QtResourceView::ResourceType type;
276 const bool rc = QtResourceView::decodeMimeData(event->mimeData(), &type) && type == QtResourceView::ResourceImage;
277 if (rc)
278 event->acceptProposedAction();
279 else
280 event->ignore();
281 return rc;
282}
283
284static void handleImageDropEvent(const QAbstractItemView *iv, QDropEvent *event, ActionModel *am)
285{
286 const QModelIndex index = iv->indexAt(event->position().toPoint());
287 if (!index.isValid()) {
288 event->ignore();
289 return;
290 }
291
292 if (!handleImageDragEnterMoveEvent(event))
293 return;
294
295 am->dropMimeData(event->mimeData(), event->proposedAction(), index.row(), 0, iv->rootIndex());
296}
297
298// Basically mimic QAbstractItemView's startDrag routine, except that
299// another pixmap is used, we don't want the whole row.
300
314
315// ---------------- ActionTreeView:
316ActionTreeView::ActionTreeView(ActionModel *model, QWidget *parent) :
317 QTreeView(parent),
318 m_model(model)
319{
320 setDragEnabled(true);
321 setAcceptDrops(true);
322 setDropIndicatorShown(true);
323 setDragDropMode(DragDrop);
324 setModel(model);
325 setRootIsDecorated(false);
326 setTextElideMode(Qt::ElideMiddle);
327
328 setModel(model);
329 connect(this, &QTreeView::activated, this, &ActionTreeView::slotActivated);
330 connect(header(), &QHeaderView::sectionDoubleClicked,
331 this, &QTreeView::resizeColumnToContents);
332
333 setIconSize(QSize(listModeIconSize, listModeIconSize));
334
335}
336
338{
339 return m_model->actionAt(currentIndex());
340}
341
342void ActionTreeView::filter(const QString &text)
343{
344 const int rowCount = m_model->rowCount();
345 const bool empty = text.isEmpty();
346 const QModelIndex parent = rootIndex();
347 for (int i = 0; i < rowCount; i++)
348 setRowHidden(i, parent, !empty && !m_model->actionName(i).contains(text, Qt::CaseInsensitive));
349}
350
351void ActionTreeView::dragEnterEvent(QDragEnterEvent *event)
352{
353 handleImageDragEnterMoveEvent(event);
354}
355
356void ActionTreeView::dragMoveEvent(QDragMoveEvent *event)
357{
358 handleImageDragEnterMoveEvent(event);
359}
360
361void ActionTreeView::dropEvent(QDropEvent *event)
362{
363 handleImageDropEvent(this, event, m_model);
364}
365
366void ActionTreeView::focusInEvent(QFocusEvent *event)
367{
368 QTreeView::focusInEvent(event);
369 // Make property editor display current action
370 if (QAction *a = currentAction())
371 emit currentActionChanged(a);
372}
373
374void ActionTreeView::contextMenuEvent(QContextMenuEvent *event)
375{
376 emit actionContextMenuRequested(event, m_model->actionAt(indexAt(event->pos())));
377}
378
379void ActionTreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
380{
381 emit currentActionChanged(m_model->actionAt(current));
382 QTreeView::currentChanged(current, previous);
383}
384
385void ActionTreeView::slotActivated(const QModelIndex &index)
386{
387 emit actionActivated(m_model->actionAt(index), index.column());
388}
389
390void ActionTreeView::startDrag(Qt::DropActions supportedActions)
391{
392 startActionDrag(this, m_model, selectedIndexes(), supportedActions);
393}
394
395// ---------------- ActionListView:
396ActionListView::ActionListView(ActionModel *model, QWidget *parent) :
397 QListView(parent),
398 m_model(model)
399{
400 setDragEnabled(true);
401 setAcceptDrops(true);
402 setDropIndicatorShown(true);
403 setDragDropMode(DragDrop);
404 setModel(model);
405 setTextElideMode(Qt::ElideMiddle);
406 connect(this, &QListView::activated, this, &ActionListView::slotActivated);
407
408 // We actually want 'Static' as the user should be able to
409 // drag away actions only (not to rearrange icons).
410 // We emulate that by not accepting our own
411 // drag data. 'Static' causes the list view to disable drag and drop
412 // on the viewport.
413 setMovement(Snap);
414 setViewMode(IconMode);
415 setIconSize(QSize(iconModeIconSize, iconModeIconSize));
416 setGridSize(QSize(4 * iconModeIconSize, 2 * iconModeIconSize));
417 setSpacing(iconModeIconSize / 3);
418}
419
421{
422 return m_model->actionAt(currentIndex());
423}
424
425void ActionListView::filter(const QString &text)
426{
427 const int rowCount = m_model->rowCount();
428 const bool empty = text.isEmpty();
429 for (int i = 0; i < rowCount; i++)
430 setRowHidden(i, !empty && !m_model->actionName(i).contains(text, Qt::CaseInsensitive));
431}
432
433void ActionListView::dragEnterEvent(QDragEnterEvent *event)
434{
435 handleImageDragEnterMoveEvent(event);
436}
437
438void ActionListView::dragMoveEvent(QDragMoveEvent *event)
439{
440 handleImageDragEnterMoveEvent(event);
441}
442
443void ActionListView::dropEvent(QDropEvent *event)
444{
445 handleImageDropEvent(this, event, m_model);
446}
447
448void ActionListView::focusInEvent(QFocusEvent *event)
449{
450 QListView::focusInEvent(event);
451 // Make property editor display current action
452 if (QAction *a = currentAction())
453 emit currentActionChanged(a);
454}
455
456void ActionListView::contextMenuEvent(QContextMenuEvent *event)
457{
458 emit actionContextMenuRequested(event, m_model->actionAt(indexAt(event->pos())));
459}
460
461void ActionListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
462{
463 emit currentActionChanged(m_model->actionAt(current));
464 QListView::currentChanged(current, previous);
465}
466
467void ActionListView::slotActivated(const QModelIndex &index)
468{
469 emit actionActivated(m_model->actionAt(index));
470}
471
472void ActionListView::startDrag(Qt::DropActions supportedActions)
473{
474 startActionDrag(this, m_model, selectedIndexes(), supportedActions);
475}
476
477// ActionView
478ActionView::ActionView(QWidget *parent) :
479 QStackedWidget(parent),
480 m_model(new ActionModel(this)),
481 m_actionTreeView(new ActionTreeView(m_model)),
482 m_actionListView(new ActionListView(m_model))
483{
484 addWidget(m_actionListView);
485 addWidget(m_actionTreeView);
486 // Wire signals
487 connect(m_actionTreeView, &ActionTreeView::actionContextMenuRequested,
488 this, &ActionView::contextMenuRequested);
489 connect(m_actionListView, &ActionListView::actionContextMenuRequested,
490 this, &ActionView::contextMenuRequested);
491
492 // make it possible for vs integration to reimplement edit action dialog
493 // [which it shouldn't do actually]
494 connect(m_actionListView, &ActionListView::actionActivated,
495 this, [this](QAction *a) { this->activated(a, -1); });
496 connect(m_actionTreeView, &ActionTreeView::actionActivated, this, &ActionView::activated);
497
498 connect(m_actionListView, &ActionListView::currentActionChanged,
499 this, &ActionView::slotCurrentChanged);
500 connect(m_actionTreeView, &ActionTreeView::currentActionChanged,
501 this, &ActionView::slotCurrentChanged);
502
503 connect(m_model, &ActionModel::resourceImageDropped,
504 this, &ActionView::resourceImageDropped);
505
506 // sync selection models
507 QItemSelectionModel *selectionModel = m_actionTreeView->selectionModel();
508 m_actionListView->setSelectionModel(selectionModel);
509 connect(selectionModel, &QItemSelectionModel::selectionChanged,
510 this, &ActionView::selectionChanged);
511}
512
514{
515 return currentWidget() == m_actionListView ? IconView : DetailedView;
516}
517
519{
520 if (viewMode() == lm)
521 return;
522
523 switch (lm) {
524 case IconView:
525 setCurrentWidget(m_actionListView);
526 break;
527 case DetailedView:
528 setCurrentWidget(m_actionTreeView);
529 break;
530 default:
531 break;
532 }
533}
534
535void ActionView::slotCurrentChanged(QAction *action)
536{
537 // emit only for currently visible
538 if (sender() == currentWidget())
540}
541
542void ActionView::filter(const QString &text)
543{
544 m_actionTreeView->filter(text);
545 m_actionListView->filter(text);
546}
547
549{
550 m_actionTreeView->selectAll();
551}
552
554{
555 m_actionTreeView->selectionModel()->clearSelection();
556}
557
558void ActionView::selectAction(QAction *a)
559{
561 if (index.isValid())
563}
564
565void ActionView::setCurrentIndex(const QModelIndex &index)
566{
567 m_actionTreeView->setCurrentIndex(index);
568}
569
571{
572 return m_actionListView->currentAction();
573}
574
575void ActionView::setSelectionMode(QAbstractItemView::SelectionMode sm)
576{
577 m_actionTreeView->setSelectionMode(sm);
578 m_actionListView->setSelectionMode(sm);
579}
580
582{
583 return m_actionListView->selectionMode();
584}
585
587{
588 return m_actionListView->selectionModel()->selection();
589}
590
592{
593 ActionList rc;
594 const QModelIndexList &indexes = selection().indexes();
595 for (const QModelIndex &index : indexes) {
596 if (index.column() == 0)
597 rc += actionOfItem(m_model->itemFromIndex(index));
598 }
599 return rc;
600}
601// ---------- ActionRepositoryMimeData
607
613
618
620{
621
622 // Try to find a suitable pixmap. Grab either widget or icon.
623 const QIcon icon = action->icon();
624 if (!icon.isNull())
625 return icon.pixmap(QSize(22, 22));
626
628 for (QObject *o : associatedObjects) {
630 return tb->grab(QRect(0, 0, -1, -1));
631 }
632
633 // Create a QToolButton
635 tb->setText(action->text());
637 tb->adjustSize();
638 const QPixmap rc = tb->grab(QRect(0, 0, -1, -1));
639 tb->deleteLater();
640 return rc;
641}
642
652
653} // namespace qdesigner_internal
654
655QT_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)