LeechCraft 0.6.70-17335-ge406ffdcaf
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
menumodeladapter.cpp
Go to the documentation of this file.
1/**********************************************************************
2 * LeechCraft - modular cross-platform feature rich internet client.
3 * Copyright (C) 2006-2014 Georg Rudoy
4 *
5 * Distributed under the Boost Software License, Version 1.0.
6 * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7 **********************************************************************/
8
9#include "menumodeladapter.h"
10#include <QAbstractItemModel>
11#include <QMenu>
12#include <QtDebug>
13
14namespace LC::Util
15{
16 namespace
17 {
18 class MenuModelManager : public QObject
19 {
20 const std::function<void (QModelIndex)> ClickHandler_;
21 QMenu& Menu_;
22 QAbstractItemModel& Model_;
23
24 const MenuModelOptions Options_;
25 public:
26 explicit MenuModelManager (QMenu& menu,
27 QAbstractItemModel& model,
28 std::function<void (QModelIndex)> clickHandler,
29 MenuModelOptions options)
30 : QObject { &menu }
31 , ClickHandler_ { std::move (clickHandler) }
32 , Menu_ { menu }
33 , Model_ { model }
34 , Options_ { std::move (options) }
35 {
36 connect (&model,
37 &QObject::destroyed,
38 this,
39 &QObject::deleteLater);
40
41 RegenerateMenu ();
42
43 connect (&model,
44 &QAbstractItemModel::modelReset,
45 this,
46 &MenuModelManager::RegenerateMenu);
47 connect (&model,
48 &QAbstractItemModel::rowsMoved,
49 this,
50 &MenuModelManager::MoveRows);
51 connect (&model,
52 &QAbstractItemModel::rowsRemoved,
53 this,
54 &MenuModelManager::RemoveRows);
55 connect (&model,
56 &QAbstractItemModel::rowsInserted,
57 this,
58 &MenuModelManager::InsertRows);
59 }
60 private:
61 void RegenerateMenu ()
62 {
63 Menu_.clear ();
64
65 const auto rc = Model_.rowCount ();
66 if (rc)
67 Menu_.addActions (MakeActionsForRange (0, rc - 1));
68
69 if (!Options_.AdditionalActions_.isEmpty ())
70 {
71 Menu_.addSeparator ();
72 Menu_.addActions (Options_.AdditionalActions_);
73 }
74 }
75
76 void MoveRows (const QModelIndex& parent, int start, int end,
77 const QModelIndex& destParent, int row)
78 {
79 if (!CheckRootParent (parent, destParent) || !CheckIndexes (start, end, row))
80 return;
81
82 const auto& actions = Menu_.actions ();
83 const auto& moved = actions.mid (start, end - start + 1);
84 const auto& target = actions [row];
85 Menu_.insertActions (target, moved);
86 }
87
88 void RemoveRows (const QModelIndex& parent, int first, int last)
89 {
90 if (!CheckRootParent (parent) || !CheckIndexes (first, last))
91 return;
92
93 const auto& actions = Menu_.actions ();
94 for (const auto action : actions.mid (first, last - first + 1))
95 Menu_.removeAction (action);
96 }
97
98 QList<QAction*> MakeActionsForRange (int first, int last)
99 {
100 QList<QAction*> actions;
101 actions.reserve (last - first + 1);
102 for (int i = first; i <= last; ++i)
103 {
104 const auto& idx = Model_.index (i, 0);
105 const auto& icon = idx.data (Qt::DecorationRole).value<QIcon> ();
106 const auto& text = idx.data (Qt::DisplayRole).toString ();
107 const auto& tooltip = idx.data (Qt::ToolTipRole).toString ();
108
109 const auto act = new QAction { icon, text, &Menu_ };
110 act->setToolTip (tooltip);
111 connect (act,
112 &QAction::triggered,
113 this,
114 [this, idx = QPersistentModelIndex { idx }] { ClickHandler_ (idx); });
115 actions << act;
116 }
117 return actions;
118 }
119
120 void InsertRows (const QModelIndex& parent, int first, int last)
121 {
122 if (!CheckRootParent (parent))
123 return;
124
125 const auto& newActions = MakeActionsForRange (first, last);
126 const auto& existing = Menu_.actions ();
127 const auto curActionsCount = existing.size ();
128 if (first == curActionsCount)
129 Menu_.addActions (newActions);
130 else if (first < curActionsCount)
131 Menu_.insertActions (existing [first], newActions);
132 else
133 {
134 qWarning () << "invalid actions position" << &Menu_ << &Model_ << curActionsCount << first << last;
135 qDeleteAll (newActions);
136 }
137 }
138
139 template<typename... Idxs>
140 bool CheckRootParent (const Idxs&... idxes)
141 {
142 if ((idxes.isValid () || ...))
143 {
144 ((qWarning () << "ignoring row operations for non-root indexes" << &Menu_ << &Model_) << ... << idxes);
145 return false;
146 }
147
148 return true;
149 }
150
151 template<typename... Idxs>
152 bool CheckIndexes (Idxs... idxes)
153 {
154 if (((idxes >= Menu_.actions ().size ()) || ...))
155 {
156 ((qWarning () << "out of bounds indexes" << &Menu_ << &Model_) << ... << idxes);
157 RegenerateMenu ();
158 return false;
159 }
160
161 return true;
162 }
163 };
164 }
165
166 void SetMenuModel (QMenu& menu,
167 QAbstractItemModel& model,
168 std::function<void (QModelIndex)> clickHandler,
169 MenuModelOptions options)
170 {
171 static QHash<QMenu*, MenuModelManager*> menu2manager;
172 if (auto mgr = menu2manager.take (&menu))
173 delete mgr;
174
175 const auto mgr = new MenuModelManager { menu, model, std::move (clickHandler), std::move (options) };
176 menu2manager [&menu] = mgr;
177 }
178}
void SetMenuModel(QMenu &menu, QAbstractItemModel &model, std::function< void(QModelIndex)> clickHandler, MenuModelOptions options)