]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/TocWidget.cpp
a47d5daa2e37d104bc3d0aa6ac7b55b013bd75a1
[lyx.git] / src / frontends / qt4 / TocWidget.cpp
1 /**
2  * \file TocWidget.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Abdelrazak Younes
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "TocWidget.h"
15
16 #include "GuiApplication.h"
17 #include "GuiView.h"
18 #include "qt_helpers.h"
19 #include "TocModel.h"
20
21 #include "Buffer.h"
22 #include "FuncRequest.h"
23 #include "LyXFunc.h"
24
25 #include "support/debug.h"
26 #include "support/lassert.h"
27
28 #include <QHeaderView>
29 #include <QTimer>
30
31 #include <vector>
32
33 using namespace std;
34
35 namespace lyx {
36 namespace frontend {
37
38 TocWidget::TocWidget(GuiView & gui_view, QWidget * parent)
39         : QWidget(parent), depth_(0), persistent_(false), gui_view_(gui_view)
40 {
41         setupUi(this);
42
43         moveOutTB->setIcon(QIcon(getPixmap("images/", "promote", "png")));
44         moveInTB->setIcon(QIcon(getPixmap("images/", "demote", "png")));
45         moveUpTB->setIcon(QIcon(getPixmap("images/", "up", "png")));
46         moveDownTB->setIcon(QIcon(getPixmap("images/", "down", "png")));
47         updateTB->setIcon(QIcon(getPixmap("images/", "reload", "png")));
48
49         // avoid flickering
50         tocTV->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
51
52         tocTV->showColumn(0);
53
54         // hide the pointless QHeader for now
55         // in the future, new columns may appear
56         // like labels, bookmarks, etc...
57         // tocTV->header()->hide();
58         tocTV->header()->setVisible(false);
59
60         // Only one item selected at a time.
61         tocTV->setSelectionMode(QAbstractItemView::SingleSelection);
62
63         // The toc types combo won't change its model.
64         typeCO->setModel(gui_view_.tocModels().nameModel());
65
66         // Make sure the buttons are disabled when first shown without a loaded
67         // Buffer.
68         enableControls(false);
69
70         init(QString());
71 }
72
73
74 void TocWidget::on_tocTV_activated(QModelIndex const & index)
75 {
76         goTo(index);
77 }
78
79
80 void TocWidget::on_tocTV_pressed(QModelIndex const & index)
81 {
82         Qt::MouseButtons const button = QApplication::mouseButtons();
83         if (button & Qt::LeftButton) {
84                 goTo(index);
85                 gui_view_.setFocus();
86         }
87 }
88
89
90 void TocWidget::goTo(QModelIndex const & index)
91 {
92         LYXERR(Debug::GUI, "goto " << index.row()
93                 << ", " << index.column());
94
95         gui_view_.tocModels().goTo(current_type_, index);
96 }
97
98
99 void TocWidget::on_updateTB_clicked()
100 {
101         // The backend update can take some time so we disable
102         // the controls while waiting.
103         enableControls(false);
104         gui_view_.tocModels().updateBackend();
105 }
106
107
108 void TocWidget::on_sortCB_stateChanged(int state)
109 {
110         gui_view_.tocModels().sort(current_type_, state == Qt::Checked);
111         updateView();
112 }
113
114 void TocWidget::on_persistentCB_stateChanged(int state)
115 {
116         persistent_ = state == Qt::Checked;
117 }
118
119
120 /* FIXME (Ugras 17/11/06):
121 I have implemented a indexDepth function to get the model indices. In my
122 opinion, somebody should derive a new qvariant class for tocModelItem
123 which saves the string data and depth information. That will save the
124 depth calculation.  */
125
126 static int indexDepth(QModelIndex const & index, int depth = -1)
127 {
128         ++depth;
129         return index.parent() == QModelIndex()
130                 ? depth : indexDepth(index.parent(), depth);
131 }
132
133
134 void TocWidget::on_depthSL_valueChanged(int depth)
135 {
136         if (depth == depth_)
137                 return;
138         setTreeDepth(depth);
139         gui_view_.setFocus();
140 }
141
142
143 void TocWidget::setTreeDepth(int depth)
144 {
145         depth_ = depth;
146         if (!tocTV->model())
147                 return;
148
149 #if QT_VERSION >= 0x040300
150         // this should be faster than our own code below
151         if (depth == 0)
152                 tocTV->collapseAll();
153         else
154                 tocTV->expandToDepth(depth - 1);
155 #else
156         // expanding and then collapsing is probably better,
157         // but my qt 4.1.2 doesn't have expandAll()..
158         //tocTV->expandAll();
159         QModelIndexList indices = tocTV->model()->match(
160                 tocTV->model()->index(0, 0),
161                 Qt::DisplayRole, "*", -1,
162                 Qt::MatchFlags(Qt::MatchWildcard|Qt::MatchRecursive));
163
164         int size = indices.size();
165         for (int i = 0; i < size; i++) {
166                 QModelIndex index = indices[i];
167                 tocTV->setExpanded(index, indexDepth(index) < depth_);
168         }
169 #endif
170 }
171
172
173 void TocWidget::on_typeCO_currentIndexChanged(int index)
174 {
175         current_type_ = typeCO->itemData(index).toString();
176         updateView();
177         gui_view_.setFocus();
178 }
179
180
181 void TocWidget::outline(int func_code)
182 {
183         enableControls(false);
184         QModelIndexList const & list = tocTV->selectionModel()->selectedIndexes();
185         if (list.isEmpty())
186                 return;
187         enableControls(false);
188         goTo(list[0]);
189         dispatch(FuncRequest(static_cast<FuncCode>(func_code)));
190         enableControls(true);
191         gui_view_.setFocus();
192 }
193
194
195 void TocWidget::on_moveUpTB_clicked()
196 {
197         outline(LFUN_OUTLINE_UP);
198 }
199
200
201 void TocWidget::on_moveDownTB_clicked()
202 {
203         outline(LFUN_OUTLINE_DOWN);
204 }
205
206
207 void TocWidget::on_moveInTB_clicked()
208 {
209         outline(LFUN_OUTLINE_IN);
210 }
211
212
213 void TocWidget::on_moveOutTB_clicked()
214 {
215         outline(LFUN_OUTLINE_OUT);
216 }
217
218
219 void TocWidget::select(QModelIndex const & index)
220 {
221         if (!index.isValid()) {
222                 LYXERR(Debug::GUI, "TocWidget::select(): QModelIndex is invalid!");
223                 return;
224         }
225
226         tocTV->scrollTo(index);
227         tocTV->clearSelection();
228         tocTV->setCurrentIndex(index);
229 }
230
231
232 /// Test if outlining operation is possible
233 static bool canOutline(QString const & type)
234 {
235         return type == "tableofcontents";
236 }
237
238
239 void TocWidget::enableControls(bool enable)
240 {
241         updateTB->setEnabled(enable);
242
243         if (!canOutline(current_type_))
244                 enable = false;
245
246         moveUpTB->setEnabled(enable);
247         moveDownTB->setEnabled(enable);
248         moveInTB->setEnabled(enable);
249         moveOutTB->setEnabled(enable);
250         if (!enable) {
251                 depthSL->setMaximum(0);
252                 depthSL->setValue(0);
253         }
254 }
255
256
257 /// Test if synchronized navigation is possible
258 static bool canNavigate(QString const & type)
259 {
260         // It is not possible to have synchronous navigation in a correctl
261         // and efficient way with the label type because Toc::item() do a linear
262         // seatch. Even if fixed, it might even not be desirable to do so if we 
263         // want to support drag&drop of labels and references.
264         return type != "label" && type != "change";
265 }
266
267
268 void TocWidget::updateView()
269 {
270         if (!gui_view_.view()) {
271                 enableControls(false);
272                 typeCO->setEnabled(false);
273                 tocTV->setModel(0);
274                 tocTV->setEnabled(false);
275                 return;
276         }
277         typeCO->setEnabled(true);
278         tocTV->setEnabled(false);
279         tocTV->setUpdatesEnabled(false);
280
281         QAbstractItemModel * toc_model = gui_view_.tocModels().model(current_type_);
282         if (tocTV->model() != toc_model) {
283                 tocTV->setModel(toc_model);
284                 tocTV->setEditTriggers(QAbstractItemView::NoEditTriggers);
285                 if (persistent_)
286                         setTreeDepth(depth_);
287         }
288
289         sortCB->blockSignals(true);
290         sortCB->setChecked(gui_view_.tocModels().isSorted(current_type_));
291         sortCB->blockSignals(false);
292
293         persistentCB->setChecked(persistent_);
294
295         bool controls_enabled = toc_model && toc_model->rowCount() > 0
296                 && !gui_view_.buffer()->isReadonly();
297         enableControls(controls_enabled);
298
299         depthSL->setMaximum(gui_view_.tocModels().depth(current_type_));
300         depthSL->setValue(depth_);
301         if (!persistent_)
302                 setTreeDepth(depth_);
303         if (canNavigate(current_type_))
304                 select(gui_view_.tocModels().currentIndex(current_type_));
305         tocTV->setEnabled(true);
306         tocTV->setUpdatesEnabled(true);
307 }
308
309
310 static QString decodeType(QString const & str)
311 {
312         QString type = str;
313         if (type.contains("tableofcontents")) {
314                 type = "tableofcontents";
315         } else if (type.contains("floatlist")) {
316                 if (type.contains("\"figure"))
317                         type = "figure";
318                 else if (type.contains("\"table"))
319                         type = "table";
320                 else if (type.contains("\"algorithm"))
321                         type = "algorithm";
322         }
323         return type;
324 }
325
326
327 void TocWidget::init(QString const & str)
328 {
329         int new_index;
330         if (str.isEmpty())
331                 new_index = typeCO->findData(current_type_);
332         else
333                 new_index = typeCO->findData(decodeType(str));
334
335         // If everything else fails, settle on the table of contents which is
336         // guaranted to exist.
337         if (new_index == -1) {
338                 current_type_ = "tableofcontents";
339                 new_index = typeCO->findData(current_type_);
340         } else {
341                 current_type_ = typeCO->itemData(new_index).toString();
342         }
343
344         typeCO->blockSignals(true);
345         typeCO->setCurrentIndex(new_index);
346         typeCO->blockSignals(false);
347 }
348
349 } // namespace frontend
350 } // namespace lyx
351
352 #include "moc_TocWidget.cpp"