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