]> git.lyx.org Git - lyx.git/blob - src/frontends/LyXView.cpp
SCons: msvc does not need this /TP option any more after we rename .C => .cpp. Also...
[lyx.git] / src / frontends / LyXView.cpp
1 /**
2  * \file LyXView.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author John Levon
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "LyXView.h"
15
16 #include "Dialogs.h"
17 #include "Toolbars.h"
18 #include "Menubar.h"
19 #include "WorkArea.h"
20 #include "Gui.h"
21
22 #include "Buffer.h"
23 #include "buffer_funcs.h"
24 #include "BufferList.h"
25 #include "BufferParams.h"
26 #include "BufferView.h"
27 #include "bufferview_funcs.h"
28 #include "Cursor.h"
29 #include "debug.h"
30 #include "ErrorList.h"
31 #include "FuncRequest.h"
32 #include "gettext.h"
33 #include "Intl.h"
34 #include "callback.h"
35 #include "LyX.h"
36 #include "LyXFunc.h"
37 #include "LyXRC.h"
38 #include "Text.h"
39 #include "MenuBackend.h"
40 #include "Paragraph.h"
41
42 #include "controllers/ControlCommandBuffer.h"
43
44 #include "support/lstrings.h"
45 #include "support/filetools.h" // OnlyFilename()
46 #include "support/Timeout.h"
47
48 #include <boost/bind.hpp>
49
50
51 #ifdef HAVE_SYS_TIME_H
52 # include <sys/time.h>
53 #endif
54 #ifdef HAVE_UNISTD_H
55 # include <unistd.h>
56 #endif
57
58 using std::endl;
59 using std::string;
60
61 namespace lyx {
62
63 using support::bformat;
64 using support::FileName;
65 using support::makeDisplayPath;
66 using support::onlyFilename;
67
68 namespace frontend {
69
70 docstring current_layout;
71
72 LyXView::LyXView(int id)
73         : toolbars_(new Toolbars(*this)),
74           autosave_timeout_(new Timeout(5000)),
75           dialogs_(new Dialogs(*this)),
76           controlcommand_(new ControlCommandBuffer(*this)), id_(id)
77 {
78         // Start autosave timer
79         if (lyxrc.autosave) {
80                 autosave_timeout_->timeout.connect(boost::bind(&LyXView::autoSave, this));
81                 autosave_timeout_->setTimeout(lyxrc.autosave * 1000);
82                 autosave_timeout_->start();
83         }
84 }
85
86
87 LyXView::~LyXView()
88 {
89         disconnectBuffer();
90         disconnectBufferView();
91 }
92
93
94 Buffer * LyXView::buffer()
95 {
96         WorkArea * work_area = currentWorkArea();
97         if (work_area)
98                 return &work_area->bufferView().buffer();
99         return 0;
100 }
101
102
103 Buffer const * LyXView::buffer() const
104 {
105         WorkArea const * work_area = currentWorkArea();
106         if (work_area)
107                 return &work_area->bufferView().buffer();
108         return 0;
109 }
110
111
112 void LyXView::setBuffer(Buffer * newBuffer)
113 {
114         busy(true);
115
116         Buffer * oldBuffer = buffer();
117         if (oldBuffer == newBuffer) {
118                 busy(false);
119                 return;
120         }
121
122         // parentfilename will be used in case when we switch to a child
123         // document (hence when child_document is true)
124         string parentfilename;
125         if (oldBuffer)
126                 parentfilename = oldBuffer->fileName();
127
128         WorkArea * wa = workArea(*newBuffer);
129         if (wa == 0) {
130                 updateLabels(*newBuffer->getMasterBuffer());
131                 wa = addWorkArea(*newBuffer);
132         } else
133                 //Disconnect the old buffer...there's no new one.
134                 disconnectBuffer();
135         connectBuffer(*newBuffer);
136         connectBufferView(wa->bufferView());
137         setCurrentWorkArea(wa);
138
139         busy(false);
140 }
141
142
143 Buffer * LyXView::loadLyXFile(FileName const & filename, bool tolastfiles)
144 {
145         busy(true);
146         string parentfilename;
147         Buffer * oldBuffer = buffer();
148         if (oldBuffer)
149                 parentfilename = oldBuffer->fileName();
150
151         Buffer * newBuffer = checkAndLoadLyXFile(filename);
152
153         if (!newBuffer) {
154                 message(_("Document not loaded."));
155                 updateStatusBar();
156                 busy(false);
157                 return 0;
158         }
159
160         WorkArea * wa = addWorkArea(*newBuffer);
161
162         // scroll to the position when the file was last closed
163         if (lyxrc.use_lastfilepos) {
164                 pit_type pit;
165                 pos_type pos;
166                 boost::tie(pit, pos) = LyX::ref().session().lastFilePos().load(filename);
167                 // if successfully move to pit (returned par_id is not zero),
168                 // update metrics and reset font
169                 BufferView & bv = wa->bufferView();
170                 if (bv.moveToPosition(pit, pos, 0, 0).get<1>()) {
171                         if (bv.fitCursor())
172                                 bv.updateMetrics(false);
173                         newBuffer->text().setCurrentFont(bv.cursor());
174                 }
175         }
176
177         busy(false);
178         return newBuffer;
179 }
180
181
182 void LyXView::connectBuffer(Buffer & buf)
183 {
184         if (errorsConnection_.connected())
185                 disconnectBuffer();
186
187         bufferStructureChangedConnection_ =
188                 buf.getMasterBuffer()->structureChanged.connect(
189                         boost::bind(&LyXView::updateToc, this));
190
191         errorsConnection_ =
192                 buf.errors.connect(
193                         boost::bind(&LyXView::showErrorList, this, _1));
194
195         messageConnection_ =
196                 buf.message.connect(
197                         boost::bind(&LyXView::message, this, _1));
198
199         busyConnection_ =
200                 buf.busy.connect(
201                         boost::bind(&LyXView::busy, this, _1));
202
203         titleConnection_ =
204                 buf.updateTitles.connect(
205                         boost::bind(&LyXView::updateWindowTitle, this));
206
207         timerConnection_ =
208                 buf.resetAutosaveTimers.connect(
209                         boost::bind(&LyXView::resetAutosaveTimer, this));
210
211         readonlyConnection_ =
212                 buf.readonly.connect(
213                         boost::bind(&LyXView::showReadonly, this, _1));
214 }
215
216
217 void LyXView::disconnectBuffer()
218 {
219         errorsConnection_.disconnect();
220         bufferStructureChangedConnection_.disconnect();
221         messageConnection_.disconnect();
222         busyConnection_.disconnect();
223         titleConnection_.disconnect();
224         timerConnection_.disconnect();
225         readonlyConnection_.disconnect();
226         layout_changed_connection_.disconnect();
227 }
228
229
230 void LyXView::connectBufferView(BufferView & bv)
231 {
232         message_connection_ = bv.message.connect(
233                         boost::bind(&LyXView::message, this, _1));
234         show_dialog_connection_ = bv.showDialog.connect(
235                         boost::bind(&LyXView::showDialog, this, _1));
236         show_dialog_with_data_connection_ = bv.showDialogWithData.connect(
237                         boost::bind(&LyXView::showDialogWithData, this, _1, _2));
238         show_inset_dialog_connection_ = bv.showInsetDialog.connect(
239                         boost::bind(&LyXView::showInsetDialog, this, _1, _2, _3));
240         update_dialog_connection_ = bv.updateDialog.connect(
241                         boost::bind(&LyXView::updateDialog, this, _1, _2));
242         layout_changed_connection_ = bv.layoutChanged.connect(
243                         boost::bind(&Toolbars::setLayout, toolbars_.get(), _1));
244 }
245
246
247 void LyXView::disconnectBufferView()
248 {
249         message_connection_.disconnect();
250         show_dialog_connection_.disconnect();
251         show_dialog_with_data_connection_.disconnect();
252         show_inset_dialog_connection_.disconnect();
253         update_dialog_connection_.disconnect();
254 }
255
256
257 void LyXView::showErrorList(string const & error_type)
258 {
259         ErrorList & el = buffer()->errorList(error_type);
260         if (!el.empty()) {
261                 getDialogs().show("errorlist", error_type);
262         }
263 }
264
265
266 void LyXView::showDialog(string const & name)
267 {
268         getDialogs().show(name);
269 }
270
271
272 void LyXView::showDialogWithData(string const & name, string const & data)
273 {
274         getDialogs().show(name, data);
275 }
276
277
278 void LyXView::showInsetDialog(string const & name, string const & data,
279                 Inset * inset)
280 {
281         getDialogs().show(name, data, inset);
282 }
283
284
285 void LyXView::updateDialog(string const & name, string const & data)
286 {
287         if (getDialogs().visible(name))
288                 getDialogs().update(name, data);
289 }
290
291
292 void LyXView::showReadonly(bool)
293 {
294         updateWindowTitle();
295         getDialogs().updateBufferDependent(false);
296 }
297
298
299 BufferView * LyXView::view()
300 {
301         WorkArea * wa = currentWorkArea();
302         return wa? &wa->bufferView() : 0;
303 }
304
305
306 void LyXView::updateToc()
307 {
308         updateDialog("toc", "");
309 }
310
311
312 void LyXView::updateToolbars()
313 {
314         WorkArea * wa = currentWorkArea();
315         if (wa) {
316                 bool const math =
317                         wa->bufferView().cursor().inMathed();
318                 bool const table =
319                         lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
320                 bool const review =
321                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
322                         lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
323
324                 toolbars_->update(math, table, review);
325         } else
326                 toolbars_->update(false, false, false);
327
328         // update redaonly status of open dialogs. This could also be in
329         // updateMenubar(), but since updateToolbars() and updateMenubar()
330         // are always called together it is only here.
331         getDialogs().checkStatus();
332 }
333
334
335 ToolbarInfo * LyXView::getToolbarInfo(string const & name)
336 {
337         return toolbars_->getToolbarInfo(name);
338 }
339
340
341 void LyXView::toggleToolbarState(string const & name, bool allowauto)
342 {
343         // it is possible to get current toolbar status like this,...
344         // but I decide to obey the order of ToolbarBackend::flags
345         // and disregard real toolbar status.
346         // toolbars_->saveToolbarInfo();
347         //
348         // toggle state on/off/auto
349         toolbars_->toggleToolbarState(name, allowauto);
350         // update toolbar
351         updateToolbars();
352 }
353
354
355 void LyXView::updateMenubar()
356 {
357         menubar_->update();
358 }
359
360
361 void LyXView::autoSave()
362 {
363         LYXERR(Debug::INFO) << "Running autoSave()" << endl;
364
365         if (buffer())
366                 lyx::autoSave(view());
367 }
368
369
370 void LyXView::resetAutosaveTimer()
371 {
372         if (lyxrc.autosave)
373                 autosave_timeout_->restart();
374 }
375
376
377 void LyXView::updateLayoutChoice()
378 {
379         // Don't show any layouts without a buffer
380         if (!buffer()) {
381                 toolbars_->clearLayoutList();
382                 return;
383         }
384
385         // Update the layout display
386         if (toolbars_->updateLayoutList(buffer()->params().textclass)) {
387                 current_layout = buffer()->params().getTextClass().defaultLayoutName();
388         }
389
390         docstring const & layout = currentWorkArea()->bufferView().cursor().
391                 innerParagraph().layout()->name();
392
393         if (layout != current_layout) {
394                 toolbars_->setLayout(layout);
395                 current_layout = layout;
396         }
397 }
398
399
400 void LyXView::updateWindowTitle()
401 {
402         docstring maximize_title = lyx::from_ascii("LyX");
403         docstring minimize_title = lyx::from_ascii("LyX");
404
405         Buffer * buf = buffer();
406         if (buf) {
407                 string const cur_title = buf->fileName();
408                 if (!cur_title.empty()) {
409                         maximize_title += ": " + makeDisplayPath(cur_title, 30);
410                         minimize_title = lyx::from_utf8(onlyFilename(cur_title));
411                         if (!buf->isClean()) {
412                                 maximize_title += _(" (changed)");
413                                 minimize_title += lyx::char_type('*');
414                         }
415                         if (buf->isReadonly())
416                                 maximize_title += _(" (read only)");
417                 }
418         }
419
420         setWindowTitle(maximize_title, minimize_title);
421 }
422
423
424 void LyXView::dispatch(FuncRequest const & cmd)
425 {
426         string const argument = to_utf8(cmd.argument());
427         switch(cmd.action) {
428                 case LFUN_BUFFER_SWITCH:
429                         setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
430                         break;
431                 default:
432                         theLyXFunc().setLyXView(this);
433                         lyx::dispatch(cmd);
434         }
435 }
436
437
438 Buffer const * const LyXView::updateInset(Inset const * inset)
439 {
440         WorkArea * work_area = currentWorkArea();
441         if (!work_area)
442                 return 0;
443
444         if (inset) {
445                 BOOST_ASSERT(work_area);
446                 work_area->scheduleRedraw();
447         }
448         return &work_area->bufferView().buffer();
449 }
450
451 } // namespace frontend
452 } // namespace lyx