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