LeechCraft  0.6.70-18450-gabe19ee3b0
Modular cross-platform feature rich live environment.
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 
14 namespace 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)