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