1 /*
2 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
3 SPDX-FileContributor: Stephen Kelly <stephen@kdab.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 #include "breadcrumbnavigationwidget.h"
9
10 #include "dynamictreemodel.h"
11 #include "dynamictreewidget.h"
12
13 #include <QHBoxLayout>
14 #include <QListView>
15 #include <QSplitter>
16 #include <QTreeView>
17
18 #include "kbreadcrumbselectionmodel.h"
19 #include "kselectionproxymodel.h"
20
21 #define SON(object) object->setObjectName(QStringLiteral(#object))
22
CurrentItemLabel(QAbstractItemModel * model,QWidget * parent,Qt::WindowFlags f)23 CurrentItemLabel::CurrentItemLabel(QAbstractItemModel *model, QWidget *parent, Qt::WindowFlags f)
24 : QLabel(parent, f)
25 , m_model(model)
26 {
27 connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), SLOT(dataChanged(QModelIndex, QModelIndex)));
28 connect(model, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(rowsInserted(QModelIndex, int, int)));
29 connect(model, SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(rowsRemoved(QModelIndex, int, int)));
30 connect(model, SIGNAL(modelReset()), SLOT(modelReset()));
31
32 if (!m_model->hasChildren()) {
33 setText(QStringLiteral("No selection"));
34 }
35 }
36
dataChanged(const QModelIndex &,const QModelIndex &)37 void CurrentItemLabel::dataChanged(const QModelIndex &, const QModelIndex &)
38 {
39 setText(m_model->index(0, 0).data().toString());
40 }
41
rowsInserted(const QModelIndex &,int,int)42 void CurrentItemLabel::rowsInserted(const QModelIndex &, int, int)
43 {
44 setText(m_model->index(0, 0).data().toString());
45 }
46
rowsRemoved(const QModelIndex &,int,int)47 void CurrentItemLabel::rowsRemoved(const QModelIndex &, int, int)
48 {
49 if (!m_model->hasChildren()) {
50 setText(QStringLiteral("No selection"));
51 return;
52 }
53 setText(m_model->index(0, 0).data().toString());
54 }
55
modelReset()56 void CurrentItemLabel::modelReset()
57 {
58 if (!m_model->hasChildren()) {
59 setText(QStringLiteral("No selection"));
60 }
61 setText(m_model->index(0, 0).data().toString());
62 }
63
KBreadcrumbNavigationProxyModel(QItemSelectionModel * selectionModel,QObject * parent)64 KBreadcrumbNavigationProxyModel::KBreadcrumbNavigationProxyModel(QItemSelectionModel *selectionModel, QObject *parent)
65 : KSelectionProxyModel(selectionModel, parent)
66 {
67 }
68
data(const QModelIndex & index,int role) const69 QVariant KBreadcrumbNavigationProxyModel::data(const QModelIndex &index, int role) const
70 {
71 if (rowCount() > 2 && index.row() == 0 && role == Qt::DisplayRole) {
72 QModelIndex sourceIndex = mapToSource(index);
73 QStringList dataList;
74 while (sourceIndex.isValid()) {
75 dataList.prepend(sourceIndex.data().toString());
76 sourceIndex = sourceIndex.parent();
77 }
78 return dataList.join(QLatin1String(" > "));
79 }
80 return KSelectionProxyModel::data(index, role);
81 }
82
setShowHiddenAscendantData(bool showHiddenAscendantData)83 void KBreadcrumbNavigationProxyModel::setShowHiddenAscendantData(bool showHiddenAscendantData)
84 {
85 m_showHiddenAscendantData = showHiddenAscendantData;
86 }
87
showHiddenAscendantData() const88 bool KBreadcrumbNavigationProxyModel::showHiddenAscendantData() const
89 {
90 return m_showHiddenAscendantData;
91 }
92
KNavigatingProxyModel(QItemSelectionModel * selectionModel,QObject * parent)93 KNavigatingProxyModel::KNavigatingProxyModel(QItemSelectionModel *selectionModel, QObject *parent)
94 : KSelectionProxyModel(selectionModel, parent)
95 , m_selectionModel(selectionModel)
96 {
97 }
98
setSourceModel(QAbstractItemModel * sourceModel)99 void KNavigatingProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
100 {
101 connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(navigationSelectionChanged(QItemSelection, QItemSelection)));
102
103 KSelectionProxyModel::setSourceModel(sourceModel);
104 updateNavigation();
105 }
106
navigationSelectionChanged(const QItemSelection &,const QItemSelection &)107 void KNavigatingProxyModel::navigationSelectionChanged(const QItemSelection &, const QItemSelection &)
108 {
109 updateNavigation();
110 }
111
updateNavigation()112 void KNavigatingProxyModel::updateNavigation()
113 {
114 if (!sourceModel()) {
115 return;
116 }
117
118 if (m_selectionModel->selection().isEmpty()) {
119 setFilterBehavior(KSelectionProxyModel::ExactSelection);
120 QModelIndex top = sourceModel()->index(0, 0);
121 QModelIndex bottom = sourceModel()->index(sourceModel()->rowCount() - 1, 0);
122
123 disconnect(m_selectionModel,
124 SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
125 this,
126 SLOT(navigationSelectionChanged(QItemSelection, QItemSelection)));
127 m_selectionModel->select(QItemSelection(top, bottom), QItemSelectionModel::Select);
128 connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(navigationSelectionChanged(QItemSelection, QItemSelection)));
129 } else if (filterBehavior() != KSelectionProxyModel::ChildrenOfExactSelection) {
130 setFilterBehavior(KSelectionProxyModel::ChildrenOfExactSelection);
131 }
132 }
133
modelReset()134 void KNavigatingProxyModel::modelReset()
135 {
136 updateNavigation();
137 }
138
data(const QModelIndex & index,int role) const139 QVariant KNavigatingProxyModel::data(const QModelIndex &index, int role) const
140 {
141 if (role == Qt::DisplayRole && sourceModel()->hasChildren(mapToSource(index))) {
142 return QString("+ " + KSelectionProxyModel::data(index, role).toString());
143 }
144 return KSelectionProxyModel::data(index, role);
145 }
146
KForwardingItemSelectionModel(QAbstractItemModel * model,QItemSelectionModel * selectionModel,QObject * parent)147 KForwardingItemSelectionModel::KForwardingItemSelectionModel(QAbstractItemModel *model, QItemSelectionModel *selectionModel, QObject *parent)
148 : QItemSelectionModel(model, parent)
149 , m_selectionModel(selectionModel)
150 , m_direction(Forward)
151 {
152 Q_ASSERT(model == selectionModel->model());
153 connect(selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(navigationSelectionChanged(QItemSelection, QItemSelection)));
154 }
155
KForwardingItemSelectionModel(QAbstractItemModel * model,QItemSelectionModel * selectionModel,Direction direction,QObject * parent)156 KForwardingItemSelectionModel::KForwardingItemSelectionModel(QAbstractItemModel *model,
157 QItemSelectionModel *selectionModel,
158 Direction direction,
159 QObject *parent)
160 : QItemSelectionModel(model, parent)
161 , m_selectionModel(selectionModel)
162 , m_direction(direction)
163 {
164 Q_ASSERT(model == selectionModel->model());
165 if (m_direction == Forward) {
166 connect(selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(navigationSelectionChanged(QItemSelection, QItemSelection)));
167 }
168 }
169
select(const QModelIndex & index,QItemSelectionModel::SelectionFlags command)170 void KForwardingItemSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
171 {
172 if (m_direction == Reverse) {
173 m_selectionModel->select(index, command);
174 } else {
175 QItemSelectionModel::select(index, command);
176 }
177 }
178
select(const QItemSelection & selection,QItemSelectionModel::SelectionFlags command)179 void KForwardingItemSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
180 {
181 if (m_direction == Reverse) {
182 m_selectionModel->select(selection, command);
183 } else {
184 QItemSelectionModel::select(selection, command);
185 }
186 }
187
navigationSelectionChanged(const QItemSelection & selected,const QItemSelection &)188 void KForwardingItemSelectionModel::navigationSelectionChanged(const QItemSelection &selected, const QItemSelection &)
189 {
190 select(selected, ClearAndSelect);
191 }
192
BreadcrumbNavigationWidget(QWidget * parent,Qt::WindowFlags f)193 BreadcrumbNavigationWidget::BreadcrumbNavigationWidget(QWidget *parent, Qt::WindowFlags f)
194 : QWidget(parent, f)
195 {
196 DynamicTreeModel *rootModel = new DynamicTreeModel(this);
197 QSplitter *splitter = new QSplitter(this);
198 QHBoxLayout *layout = new QHBoxLayout(this);
199 layout->addWidget(splitter);
200
201 DynamicTreeWidget *dynamicTree = new DynamicTreeWidget(rootModel, splitter);
202 dynamicTree->treeView()->setSelectionMode(QAbstractItemView::SingleSelection);
203 dynamicTree->setInitialTree(
204 QLatin1String("- 1"
205 "- - 2"
206 "- - 2"
207 "- - - 3"
208 "- - - - 4"
209 "- - - - - 5"
210 "- - 2"
211 "- 6"
212 "- 6"
213 "- 6"
214 "- - 7"
215 "- - - 8"
216 "- - - 8"
217 "- - - - 9"
218 "- - - - - 10"
219 "- - - 8"
220 "- - - 8"
221 "- - 8"
222 "- 16"
223 "- - 17"
224 "- - - 18"
225 "- - - - 19"
226 "- - - - - 20"));
227
228 QList<QItemSelectionModel *> selectionModelList;
229
230 QSplitter *vSplitter = new QSplitter(Qt::Vertical, splitter);
231
232 QItemSelectionModel *rootSelectionModel = new QItemSelectionModel(rootModel, this);
233 SON(rootSelectionModel);
234
235 dynamicTree->treeView()->setSelectionModel(rootSelectionModel);
236
237 KBreadcrumbSelectionModel *breadcrumbOnlyProxySelector2 =
238 new KBreadcrumbSelectionModel(rootSelectionModel, KBreadcrumbSelectionModel::MakeBreadcrumbSelectionInOther, this);
239 SON(breadcrumbOnlyProxySelector2);
240 breadcrumbOnlyProxySelector2->setActualSelectionIncluded(false);
241
242 KBreadcrumbNavigationProxyModel *breadcrumbNavigationModel = new KBreadcrumbNavigationProxyModel(breadcrumbOnlyProxySelector2, this);
243 SON(breadcrumbNavigationModel);
244 breadcrumbNavigationModel->setSourceModel(rootModel);
245 breadcrumbNavigationModel->setFilterBehavior(KSelectionProxyModel::ExactSelection);
246
247 QListView *breadcrumbView = new QListView(vSplitter);
248 // SON(breadcrumbNavigationModel);
249 breadcrumbView->setModel(breadcrumbNavigationModel);
250
251 // This shouldn't operate on rootSelectionModel. It should operate on oneway instead?
252 KLinkItemSelectionModel *breadcrumbViewSelectionModel = new KLinkItemSelectionModel(breadcrumbNavigationModel, rootSelectionModel, this);
253 SON(breadcrumbViewSelectionModel);
254
255 KForwardingItemSelectionModel *oneway2 =
256 new KForwardingItemSelectionModel(breadcrumbNavigationModel, breadcrumbViewSelectionModel, KForwardingItemSelectionModel::Reverse);
257 SON(oneway2);
258
259 breadcrumbView->setSelectionModel(oneway2);
260
261 KSelectionProxyModel *currentItemSelectionModel = new KSelectionProxyModel(rootSelectionModel, this);
262 currentItemSelectionModel->setFilterBehavior(KSelectionProxyModel::ExactSelection);
263 currentItemSelectionModel->setSourceModel(rootModel);
264 SON(currentItemSelectionModel);
265
266 new CurrentItemLabel(currentItemSelectionModel, vSplitter);
267
268 QListView *selectionView = new QListView(vSplitter);
269
270 // Need a one-way connection from rootSelectionModel to rootSelectionModel2
271
272 KForwardingItemSelectionModel *oneway = new KForwardingItemSelectionModel(rootModel, rootSelectionModel);
273
274 KNavigatingProxyModel *navigatingProxyModel = new KNavigatingProxyModel(oneway, this);
275 SON(navigatingProxyModel);
276 navigatingProxyModel->setSourceModel(rootModel);
277 selectionView->setModel(navigatingProxyModel);
278
279 KLinkItemSelectionModel *selectedChildrenSelectionModel = new KLinkItemSelectionModel(navigatingProxyModel, rootSelectionModel, this);
280
281 selectionView->setSelectionModel(selectedChildrenSelectionModel);
282 }
283