]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
New LFUN lyx-activate to focus LyX window
[lyx.git] / src / frontends / qt4 / GuiView.cpp
1 /**
2  * \file GuiView.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  * \author Abdelrazak Younes
9  * \author Peter Kümmel
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "GuiView.h"
17
18 #include "Dialog.h"
19 #include "DispatchResult.h"
20 #include "FileDialog.h"
21 #include "FontLoader.h"
22 #include "GuiApplication.h"
23 #include "GuiCommandBuffer.h"
24 #include "GuiCompleter.h"
25 #include "GuiKeySymbol.h"
26 #include "GuiToc.h"
27 #include "GuiToolbar.h"
28 #include "GuiWorkArea.h"
29 #include "GuiProgress.h"
30 #include "LayoutBox.h"
31 #include "Menus.h"
32 #include "TocModel.h"
33
34 #include "qt_helpers.h"
35 #include "support/filetools.h"
36
37 #include "frontends/alert.h"
38 #include "frontends/KeySymbol.h"
39
40 #include "buffer_funcs.h"
41 #include "Buffer.h"
42 #include "BufferList.h"
43 #include "BufferParams.h"
44 #include "BufferView.h"
45 #include "Compare.h"
46 #include "Converter.h"
47 #include "Cursor.h"
48 #include "CutAndPaste.h"
49 #include "Encoding.h"
50 #include "ErrorList.h"
51 #include "Format.h"
52 #include "FuncStatus.h"
53 #include "FuncRequest.h"
54 #include "Intl.h"
55 #include "Layout.h"
56 #include "Lexer.h"
57 #include "LyXAction.h"
58 #include "LyX.h"
59 #include "LyXRC.h"
60 #include "LyXVC.h"
61 #include "Paragraph.h"
62 #include "SpellChecker.h"
63 #include "Session.h"
64 #include "TexRow.h"
65 #include "TextClass.h"
66 #include "Text.h"
67 #include "Toolbars.h"
68 #include "version.h"
69
70 #include "support/convert.h"
71 #include "support/debug.h"
72 #include "support/ExceptionMessage.h"
73 #include "support/FileName.h"
74 #include "support/filetools.h"
75 #include "support/gettext.h"
76 #include "support/filetools.h"
77 #include "support/ForkedCalls.h"
78 #include "support/lassert.h"
79 #include "support/lstrings.h"
80 #include "support/os.h"
81 #include "support/Package.h"
82 #include "support/PathChanger.h"
83 #include "support/Systemcall.h"
84 #include "support/Timeout.h"
85 #include "support/ProgressInterface.h"
86
87 #include <QAction>
88 #include <QApplication>
89 #include <QCloseEvent>
90 #include <QDebug>
91 #include <QDesktopWidget>
92 #include <QDragEnterEvent>
93 #include <QDropEvent>
94 #include <QFuture>
95 #include <QFutureWatcher>
96 #include <QLabel>
97 #include <QList>
98 #include <QMenu>
99 #include <QMenuBar>
100 #include <QMimeData>
101 #include <QMovie>
102 #include <QPainter>
103 #include <QPixmap>
104 #include <QPixmapCache>
105 #include <QPoint>
106 #include <QPushButton>
107 #include <QScrollBar>
108 #include <QSettings>
109 #include <QShowEvent>
110 #include <QSplitter>
111 #include <QStackedWidget>
112 #include <QStatusBar>
113 #if QT_VERSION >= 0x050000
114 #include <QSvgRenderer>
115 #endif
116 #include <QtConcurrentRun>
117 #include <QTime>
118 #include <QTimer>
119 #include <QToolBar>
120 #include <QUrl>
121
122
123
124 // sync with GuiAlert.cpp
125 #define EXPORT_in_THREAD 1
126
127
128 #include "support/bind.h"
129
130 #include <sstream>
131
132 #ifdef HAVE_SYS_TIME_H
133 # include <sys/time.h>
134 #endif
135 #ifdef HAVE_UNISTD_H
136 # include <unistd.h>
137 #endif
138
139
140 using namespace std;
141 using namespace lyx::support;
142
143 namespace lyx {
144
145 using support::addExtension;
146 using support::changeExtension;
147 using support::removeExtension;
148
149 namespace frontend {
150
151 namespace {
152
153 class BackgroundWidget : public QWidget
154 {
155 public:
156         BackgroundWidget(int width, int height)
157                 : width_(width), height_(height)
158         {
159                 LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
160                 if (!lyxrc.show_banner)
161                         return;
162                 /// The text to be written on top of the pixmap
163                 QString const text = lyx_version ?
164                         qt_("version ") + lyx_version : qt_("unknown version");
165 #if QT_VERSION >= 0x050000
166                 QString imagedir = "images/";
167                 FileName fname = imageLibFileSearch(imagedir, "banner", "svgz");
168                 QSvgRenderer svgRenderer(toqstr(fname.absFileName()));
169                 if (svgRenderer.isValid()) {
170                         splash_ = QPixmap(splashSize());
171                         QPainter painter(&splash_);
172                         svgRenderer.render(&painter);
173                         splash_.setDevicePixelRatio(pixelRatio());
174                 } else {
175                         splash_ = getPixmap("images/", "banner", "png");
176                 }
177 #else
178                 splash_ = getPixmap("images/", "banner", "svgz,png");
179 #endif
180
181                 QPainter pain(&splash_);
182                 pain.setPen(QColor(0, 0, 0));
183                 qreal const fsize = fontSize();
184                 QPointF const position = textPosition();
185                 LYXERR(Debug::GUI,
186                         "widget pixel ratio: " << pixelRatio() <<
187                         " splash pixel ratio: " << splashPixelRatio() <<
188                         " version text size,position: " << fsize << "@" << position.x() << "+" << position.y());
189                 QFont font;
190                 // The font used to display the version info
191                 font.setStyleHint(QFont::SansSerif);
192                 font.setWeight(QFont::Bold);
193                 font.setPointSizeF(fsize);
194                 pain.setFont(font);
195                 pain.drawText(position, text);
196                 setFocusPolicy(Qt::StrongFocus);
197         }
198
199         void paintEvent(QPaintEvent *)
200         {
201                 int const w = width_;
202                 int const h = height_;
203                 int const x = (width() - w) / 2;
204                 int const y = (height() - h) / 2;
205                 LYXERR(Debug::GUI,
206                         "widget pixel ratio: " << pixelRatio() <<
207                         " splash pixel ratio: " << splashPixelRatio() <<
208                         " paint pixmap: " << w << "x" << h << "@" << x << "+" << y);
209                 QPainter pain(this);
210                 pain.drawPixmap(x, y, w, h, splash_);
211         }
212
213         void keyPressEvent(QKeyEvent * ev)
214         {
215                 KeySymbol sym;
216                 setKeySymbol(&sym, ev);
217                 if (sym.isOK()) {
218                         guiApp->processKeySym(sym, q_key_state(ev->modifiers()));
219                         ev->accept();
220                 } else {
221                         ev->ignore();
222                 }
223         }
224
225 private:
226         QPixmap splash_;
227         int const width_;
228         int const height_;
229
230         /// Current ratio between physical pixels and device-independent pixels
231         double pixelRatio() const {
232 #if QT_VERSION >= 0x050000
233                 return devicePixelRatio();
234 #else
235                 return 1.0;
236 #endif
237         }
238
239         qreal fontSize() const {
240                 return toqstr(lyxrc.font_sizes[FONT_SIZE_NORMAL]).toDouble();
241         }
242
243         QPointF textPosition() const {
244                 return QPointF(width_/2 - 18, height_/2 + 45);
245         }
246
247         QSize splashSize() const {
248                 return QSize(
249                         static_cast<unsigned int>(width_ * pixelRatio()),
250                         static_cast<unsigned int>(height_ * pixelRatio()));
251         }
252
253         /// Ratio between physical pixels and device-independent pixels of splash image
254         double splashPixelRatio() const {
255 #if QT_VERSION >= 0x050000
256                 return splash_.devicePixelRatio();
257 #else
258                 return 1.0;
259 #endif
260         }
261 };
262
263
264 /// Toolbar store providing access to individual toolbars by name.
265 typedef map<string, GuiToolbar *> ToolbarMap;
266
267 typedef shared_ptr<Dialog> DialogPtr;
268
269 } // namespace anon
270
271
272 class GuiView::GuiViewPrivate
273 {
274         /// noncopyable
275         GuiViewPrivate(GuiViewPrivate const &);
276         void operator=(GuiViewPrivate const &);
277 public:
278         GuiViewPrivate(GuiView * gv)
279                 : gv_(gv), current_work_area_(0), current_main_work_area_(0),
280                 layout_(0), autosave_timeout_(5000),
281                 in_show_(false)
282         {
283                 // hardcode here the platform specific icon size
284                 smallIconSize = 16;  // scaling problems
285                 normalIconSize = 20; // ok, default if iconsize.png is missing
286                 bigIconSize = 26;       // better for some math icons
287                 hugeIconSize = 32;      // better for hires displays
288                 giantIconSize = 48;
289
290                 // if it exists, use width of iconsize.png as normal size
291                 QString const dir = toqstr(addPath("images", lyxrc.icon_set));
292                 FileName const fn = lyx::libFileSearch(dir, "iconsize.png");
293                 if (!fn.empty()) {
294                         QImage image(toqstr(fn.absFileName()));
295                         if (image.width() < int(smallIconSize))
296                                 normalIconSize = smallIconSize;
297                         else if (image.width() > int(giantIconSize))
298                                 normalIconSize = giantIconSize;
299                         else
300                                 normalIconSize = image.width();
301                 }
302
303                 splitter_ = new QSplitter;
304                 bg_widget_ = new BackgroundWidget(400, 250);
305                 stack_widget_ = new QStackedWidget;
306                 stack_widget_->addWidget(bg_widget_);
307                 stack_widget_->addWidget(splitter_);
308                 setBackground();
309
310                 // TODO cleanup, remove the singleton, handle multiple Windows?
311                 progress_ = ProgressInterface::instance();
312                 if (!dynamic_cast<GuiProgress*>(progress_)) {
313                         progress_ = new GuiProgress;  // TODO who deletes it
314                         ProgressInterface::setInstance(progress_);
315                 }
316                 QObject::connect(
317                                 dynamic_cast<GuiProgress*>(progress_),
318                                 SIGNAL(updateStatusBarMessage(QString const&)),
319                                 gv, SLOT(updateStatusBarMessage(QString const&)));
320                 QObject::connect(
321                                 dynamic_cast<GuiProgress*>(progress_),
322                                 SIGNAL(clearMessageText()),
323                                 gv, SLOT(clearMessageText()));
324         }
325
326         ~GuiViewPrivate()
327         {
328                 delete splitter_;
329                 delete bg_widget_;
330                 delete stack_widget_;
331         }
332
333         QMenu * toolBarPopup(GuiView * parent)
334         {
335                 // FIXME: translation
336                 QMenu * menu = new QMenu(parent);
337                 QActionGroup * iconSizeGroup = new QActionGroup(parent);
338
339                 QAction * smallIcons = new QAction(iconSizeGroup);
340                 smallIcons->setText(qt_("Small-sized icons"));
341                 smallIcons->setCheckable(true);
342                 QObject::connect(smallIcons, SIGNAL(triggered()),
343                         parent, SLOT(smallSizedIcons()));
344                 menu->addAction(smallIcons);
345
346                 QAction * normalIcons = new QAction(iconSizeGroup);
347                 normalIcons->setText(qt_("Normal-sized icons"));
348                 normalIcons->setCheckable(true);
349                 QObject::connect(normalIcons, SIGNAL(triggered()),
350                         parent, SLOT(normalSizedIcons()));
351                 menu->addAction(normalIcons);
352
353                 QAction * bigIcons = new QAction(iconSizeGroup);
354                 bigIcons->setText(qt_("Big-sized icons"));
355                 bigIcons->setCheckable(true);
356                 QObject::connect(bigIcons, SIGNAL(triggered()),
357                         parent, SLOT(bigSizedIcons()));
358                 menu->addAction(bigIcons);
359
360                 QAction * hugeIcons = new QAction(iconSizeGroup);
361                 hugeIcons->setText(qt_("Huge-sized icons"));
362                 hugeIcons->setCheckable(true);
363                 QObject::connect(hugeIcons, SIGNAL(triggered()),
364                         parent, SLOT(hugeSizedIcons()));
365                 menu->addAction(hugeIcons);
366
367                 QAction * giantIcons = new QAction(iconSizeGroup);
368                 giantIcons->setText(qt_("Giant-sized icons"));
369                 giantIcons->setCheckable(true);
370                 QObject::connect(giantIcons, SIGNAL(triggered()),
371                         parent, SLOT(giantSizedIcons()));
372                 menu->addAction(giantIcons);
373
374                 unsigned int cur = parent->iconSize().width();
375                 if ( cur == parent->d.smallIconSize)
376                         smallIcons->setChecked(true);
377                 else if (cur == parent->d.normalIconSize)
378                         normalIcons->setChecked(true);
379                 else if (cur == parent->d.bigIconSize)
380                         bigIcons->setChecked(true);
381                 else if (cur == parent->d.hugeIconSize)
382                         hugeIcons->setChecked(true);
383                 else if (cur == parent->d.giantIconSize)
384                         giantIcons->setChecked(true);
385
386                 return menu;
387         }
388
389         void setBackground()
390         {
391                 stack_widget_->setCurrentWidget(bg_widget_);
392                 bg_widget_->setUpdatesEnabled(true);
393                 bg_widget_->setFocus();
394         }
395
396         int tabWorkAreaCount()
397         {
398                 return splitter_->count();
399         }
400
401         TabWorkArea * tabWorkArea(int i)
402         {
403                 return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
404         }
405
406         TabWorkArea * currentTabWorkArea()
407         {
408                 int areas = tabWorkAreaCount();
409                 if (areas == 1)
410                         // The first TabWorkArea is always the first one, if any.
411                         return tabWorkArea(0);
412
413                 for (int i = 0; i != areas;  ++i) {
414                         TabWorkArea * twa = tabWorkArea(i);
415                         if (current_main_work_area_ == twa->currentWorkArea())
416                                 return twa;
417                 }
418
419                 // None has the focus so we just take the first one.
420                 return tabWorkArea(0);
421         }
422
423         int countWorkAreasOf(Buffer & buf)
424         {
425                 int areas = tabWorkAreaCount();
426                 int count = 0;
427                 for (int i = 0; i != areas;  ++i) {
428                         TabWorkArea * twa = tabWorkArea(i);
429                         if (twa->workArea(buf))
430                                 ++count;
431                 }
432                 return count;
433         }
434
435         void setPreviewFuture(QFuture<Buffer::ExportStatus> const & f)
436         {
437                 if (processing_thread_watcher_.isRunning()) {
438                         // we prefer to cancel this preview in order to keep a snappy
439                         // interface.
440                         return;
441                 }
442                 processing_thread_watcher_.setFuture(f);
443         }
444
445 public:
446         GuiView * gv_;
447         GuiWorkArea * current_work_area_;
448         GuiWorkArea * current_main_work_area_;
449         QSplitter * splitter_;
450         QStackedWidget * stack_widget_;
451         BackgroundWidget * bg_widget_;
452         /// view's toolbars
453         ToolbarMap toolbars_;
454         ProgressInterface* progress_;
455         /// The main layout box.
456         /**
457          * \warning Don't Delete! The layout box is actually owned by
458          * whichever toolbar contains it. All the GuiView class needs is a
459          * means of accessing it.
460          *
461          * FIXME: replace that with a proper model so that we are not limited
462          * to only one dialog.
463          */
464         LayoutBox * layout_;
465
466         ///
467         map<string, DialogPtr> dialogs_;
468
469         unsigned int smallIconSize;
470         unsigned int normalIconSize;
471         unsigned int bigIconSize;
472         unsigned int hugeIconSize;
473         unsigned int giantIconSize;
474         ///
475         QTimer statusbar_timer_;
476         /// auto-saving of buffers
477         Timeout autosave_timeout_;
478         /// flag against a race condition due to multiclicks, see bug #1119
479         bool in_show_;
480
481         ///
482         TocModels toc_models_;
483
484         ///
485         QFutureWatcher<docstring> autosave_watcher_;
486         QFutureWatcher<Buffer::ExportStatus> processing_thread_watcher_;
487         ///
488         string last_export_format;
489         string processing_format;
490
491         static QSet<Buffer const *> busyBuffers;
492         static Buffer::ExportStatus previewAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
493         static Buffer::ExportStatus exportAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
494         static Buffer::ExportStatus compileAndDestroy(Buffer const * orig, Buffer * buffer, string const & format);
495         static docstring autosaveAndDestroy(Buffer const * orig, Buffer * buffer);
496
497         template<class T>
498         static Buffer::ExportStatus runAndDestroy(const T& func, Buffer const * orig, Buffer * buffer, string const & format);
499
500         // TODO syncFunc/previewFunc: use bind
501         bool asyncBufferProcessing(string const & argument,
502                                    Buffer const * used_buffer,
503                                    docstring const & msg,
504                                    Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
505                                    Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
506                                    Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const);
507
508         QVector<GuiWorkArea*> guiWorkAreas();
509 };
510
511 QSet<Buffer const *> GuiView::GuiViewPrivate::busyBuffers;
512
513
514 GuiView::GuiView(int id)
515         : d(*new GuiViewPrivate(this)), id_(id), closing_(false), busy_(0),
516           command_execute_(false)
517 {
518         // GuiToolbars *must* be initialised before the menu bar.
519         normalSizedIcons(); // at least on Mac the default is 32 otherwise, which is huge
520         constructToolbars();
521
522         // set ourself as the current view. This is needed for the menu bar
523         // filling, at least for the static special menu item on Mac. Otherwise
524         // they are greyed out.
525         guiApp->setCurrentView(this);
526
527         // Fill up the menu bar.
528         guiApp->menus().fillMenuBar(menuBar(), this, true);
529
530         setCentralWidget(d.stack_widget_);
531
532         // Start autosave timer
533         if (lyxrc.autosave) {
534                 d.autosave_timeout_.timeout.connect(bind(&GuiView::autoSave, this));
535                 d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
536                 d.autosave_timeout_.start();
537         }
538         connect(&d.statusbar_timer_, SIGNAL(timeout()),
539                 this, SLOT(clearMessage()));
540
541         // We don't want to keep the window in memory if it is closed.
542         setAttribute(Qt::WA_DeleteOnClose, true);
543
544 #if !(defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && !defined(Q_OS_MAC)
545         // QIcon::fromTheme was introduced in Qt 4.6
546 #if (QT_VERSION >= 0x040600)
547         // assign an icon to main form. We do not do it under Qt/Win or Qt/Mac,
548         // since the icon is provided in the application bundle. We use a themed
549         // version when available and use the bundled one as fallback.
550         setWindowIcon(QIcon::fromTheme("lyx", getPixmap("images/", "lyx", "svg,png")));
551 #else
552         setWindowIcon(getPixmap("images/", "lyx", "svg,png"));
553 #endif
554
555 #endif
556         resetWindowTitleAndIconText();
557
558         // use tabbed dock area for multiple docks
559         // (such as "source" and "messages")
560         setDockOptions(QMainWindow::ForceTabbedDocks);
561
562         // For Drag&Drop.
563         setAcceptDrops(true);
564
565         // add busy indicator to statusbar
566         QLabel * busylabel = new QLabel(statusBar());
567         statusBar()->addPermanentWidget(busylabel);
568         search_mode mode = theGuiApp()->imageSearchMode();
569         QString fn = toqstr(lyx::libFileSearch("images", "busy", "gif", mode).absFileName());
570         QMovie * busyanim = new QMovie(fn, QByteArray(), busylabel);
571         busylabel->setMovie(busyanim);
572         busyanim->start();
573         busylabel->hide();
574
575         connect(&d.processing_thread_watcher_, SIGNAL(started()), 
576                 busylabel, SLOT(show()));
577         connect(&d.processing_thread_watcher_, SIGNAL(finished()), 
578                 busylabel, SLOT(hide()));
579
580         statusBar()->setSizeGripEnabled(true);
581         updateStatusBar();
582
583         connect(&d.autosave_watcher_, SIGNAL(finished()), this,
584                 SLOT(autoSaveThreadFinished()));
585
586         connect(&d.processing_thread_watcher_, SIGNAL(started()), this,
587                 SLOT(processingThreadStarted()));
588         connect(&d.processing_thread_watcher_, SIGNAL(finished()), this,
589                 SLOT(processingThreadFinished()));
590
591         connect(this, SIGNAL(triggerShowDialog(QString const &, QString const &, Inset *)),
592                 SLOT(doShowDialog(QString const &, QString const &, Inset *)));
593
594         // Forbid too small unresizable window because it can happen
595         // with some window manager under X11.
596         setMinimumSize(300, 200);
597
598         if (lyxrc.allow_geometry_session) {
599                 // Now take care of session management.
600                 if (restoreLayout())
601                         return;
602         }
603
604         // no session handling, default to a sane size.
605         setGeometry(50, 50, 690, 510);
606         initToolbars();
607
608         // clear session data if any.
609         QSettings settings;
610         settings.remove("views");
611 }
612
613
614 GuiView::~GuiView()
615 {
616         delete &d;
617 }
618
619
620 QVector<GuiWorkArea*> GuiView::GuiViewPrivate::guiWorkAreas()
621 {
622         QVector<GuiWorkArea*> areas;
623         for (int i = 0; i < tabWorkAreaCount(); i++) {
624                 TabWorkArea* ta = tabWorkArea(i);
625                 for (int u = 0; u < ta->count(); u++) {
626                         areas << ta->workArea(u);
627                 }
628         }
629         return areas;
630 }
631
632 static void handleExportStatus(GuiView * view, Buffer::ExportStatus status,
633         string const & format)
634 {
635         docstring const fmt = formats.prettyName(format);
636         docstring msg;
637         switch (status) {
638         case Buffer::ExportSuccess:
639                 msg = bformat(_("Successful export to format: %1$s"), fmt);
640                 break;
641         case Buffer::ExportCancel:
642                 msg = _("Document export cancelled.");
643                 break;
644         case Buffer::ExportError:
645         case Buffer::ExportNoPathToFormat:
646         case Buffer::ExportTexPathHasSpaces:
647         case Buffer::ExportConverterError:
648                 msg = bformat(_("Error while exporting format: %1$s"), fmt);
649                 break;
650         case Buffer::PreviewSuccess:
651                 msg = bformat(_("Successful preview of format: %1$s"), fmt);
652                 break;
653         case Buffer::PreviewError:
654                 msg = bformat(_("Error while previewing format: %1$s"), fmt);
655                 break;
656         }
657         view->message(msg);
658 }
659
660
661 void GuiView::processingThreadStarted()
662 {
663 }
664
665
666 void GuiView::processingThreadFinished()
667 {
668         QFutureWatcher<Buffer::ExportStatus> const * watcher =
669                 static_cast<QFutureWatcher<Buffer::ExportStatus> const *>(sender());
670
671         Buffer::ExportStatus const status = watcher->result();
672         handleExportStatus(this, status, d.processing_format);
673         
674         updateToolbars();
675         BufferView const * const bv = currentBufferView();
676         if (bv && !bv->buffer().errorList("Export").empty()) {
677                 errors("Export");
678                 return;
679         }
680         errors(d.last_export_format);
681 }
682
683
684 void GuiView::autoSaveThreadFinished()
685 {
686         QFutureWatcher<docstring> const * watcher =
687                 static_cast<QFutureWatcher<docstring> const *>(sender());
688         message(watcher->result());
689         updateToolbars();
690 }
691
692
693 void GuiView::saveLayout() const
694 {
695         QSettings settings;
696         settings.beginGroup("views");
697         settings.beginGroup(QString::number(id_));
698 #if defined(Q_WS_X11) || defined(QPA_XCB)
699         settings.setValue("pos", pos());
700         settings.setValue("size", size());
701 #else
702         settings.setValue("geometry", saveGeometry());
703 #endif
704         settings.setValue("layout", saveState(0));
705         settings.setValue("icon_size", iconSize());
706 }
707
708
709 void GuiView::saveUISettings() const
710 {
711         // Save the toolbar private states
712         ToolbarMap::iterator end = d.toolbars_.end();
713         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
714                 it->second->saveSession();
715         // Now take care of all other dialogs
716         map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
717         for (; it!= d.dialogs_.end(); ++it)
718                 it->second->saveSession();
719 }
720
721
722 bool GuiView::restoreLayout()
723 {
724         QSettings settings;
725         settings.beginGroup("views");
726         settings.beginGroup(QString::number(id_));
727         QString const icon_key = "icon_size";
728         if (!settings.contains(icon_key))
729                 return false;
730
731         //code below is skipped when when ~/.config/LyX is (re)created
732         QSize icon_size = settings.value(icon_key).toSize();
733         // Check whether session size changed.
734         if (icon_size.width() != int(d.smallIconSize) &&
735             icon_size.width() != int(d.normalIconSize) &&
736             icon_size.width() != int(d.bigIconSize) &&
737             icon_size.width() != int(d.hugeIconSize) &&
738             icon_size.width() != int(d.giantIconSize)) {
739                 icon_size.setWidth(d.normalIconSize);
740                 icon_size.setHeight(d.normalIconSize);
741         }
742         setIconSize(icon_size);
743
744 #if defined(Q_WS_X11) || defined(QPA_XCB)
745         QPoint pos = settings.value("pos", QPoint(50, 50)).toPoint();
746         QSize size = settings.value("size", QSize(690, 510)).toSize();
747         resize(size);
748         move(pos);
749 #else
750         // Work-around for bug #6034: the window ends up in an undetermined
751         // state when trying to restore a maximized window when it is
752         // already maximized.
753         if (!(windowState() & Qt::WindowMaximized))
754                 if (!restoreGeometry(settings.value("geometry").toByteArray()))
755                         setGeometry(50, 50, 690, 510);
756 #endif
757         // Make sure layout is correctly oriented.
758         setLayoutDirection(qApp->layoutDirection());
759
760         // Allow the toc and view-source dock widget to be restored if needed.
761         Dialog * dialog;
762         if ((dialog = findOrBuild("toc", true)))
763                 // see bug 5082. At least setup title and enabled state.
764                 // Visibility will be adjusted by restoreState below.
765                 dialog->prepareView();
766         if ((dialog = findOrBuild("view-source", true)))
767                 dialog->prepareView();
768         if ((dialog = findOrBuild("progress", true)))
769                 dialog->prepareView();
770
771         if (!restoreState(settings.value("layout").toByteArray(), 0))
772                 initToolbars();
773         
774         // init the toolbars that have not been restored
775         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
776         Toolbars::Infos::iterator end = guiApp->toolbars().end();
777         for (; cit != end; ++cit) {
778                 GuiToolbar * tb = toolbar(cit->name);
779                 if (tb && !tb->isRestored())
780                         initToolbar(cit->name);
781         }
782
783         updateDialogs();
784         return true;
785 }
786
787
788 GuiToolbar * GuiView::toolbar(string const & name)
789 {
790         ToolbarMap::iterator it = d.toolbars_.find(name);
791         if (it != d.toolbars_.end())
792                 return it->second;
793
794         LYXERR(Debug::GUI, "Toolbar::display: no toolbar named " << name);
795         return 0;
796 }
797
798
799 void GuiView::constructToolbars()
800 {
801         ToolbarMap::iterator it = d.toolbars_.begin();
802         for (; it != d.toolbars_.end(); ++it)
803                 delete it->second;
804         d.toolbars_.clear();
805
806         // I don't like doing this here, but the standard toolbar
807         // destroys this object when it's destroyed itself (vfr)
808         d.layout_ = new LayoutBox(*this);
809         d.stack_widget_->addWidget(d.layout_);
810         d.layout_->move(0,0);
811
812         // extracts the toolbars from the backend
813         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
814         Toolbars::Infos::iterator end = guiApp->toolbars().end();
815         for (; cit != end; ++cit)
816                 d.toolbars_[cit->name] =  new GuiToolbar(*cit, *this);
817 }
818
819
820 void GuiView::initToolbars()
821 {
822         // extracts the toolbars from the backend
823         Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
824         Toolbars::Infos::iterator end = guiApp->toolbars().end();
825         for (; cit != end; ++cit)
826                 initToolbar(cit->name);
827 }
828
829
830 void GuiView::initToolbar(string const & name)
831 {
832         GuiToolbar * tb = toolbar(name);
833         if (!tb)
834                 return;
835         int const visibility = guiApp->toolbars().defaultVisibility(name);
836         bool newline = !(visibility & Toolbars::SAMEROW);
837         tb->setVisible(false);
838         tb->setVisibility(visibility);
839
840         if (visibility & Toolbars::TOP) {
841                 if (newline)
842                         addToolBarBreak(Qt::TopToolBarArea);
843                 addToolBar(Qt::TopToolBarArea, tb);
844         }
845
846         if (visibility & Toolbars::BOTTOM) {
847                 if (newline)
848                         addToolBarBreak(Qt::BottomToolBarArea);
849                 addToolBar(Qt::BottomToolBarArea, tb);
850         }
851
852         if (visibility & Toolbars::LEFT) {
853                 if (newline)
854                         addToolBarBreak(Qt::LeftToolBarArea);
855                 addToolBar(Qt::LeftToolBarArea, tb);
856         }
857
858         if (visibility & Toolbars::RIGHT) {
859                 if (newline)
860                         addToolBarBreak(Qt::RightToolBarArea);
861                 addToolBar(Qt::RightToolBarArea, tb);
862         }
863
864         if (visibility & Toolbars::ON)
865                 tb->setVisible(true);
866 }
867
868
869 TocModels & GuiView::tocModels()
870 {
871         return d.toc_models_;
872 }
873
874
875 void GuiView::setFocus()
876 {
877         LYXERR(Debug::DEBUG, "GuiView::setFocus()" << this);
878         QMainWindow::setFocus();
879 }
880
881
882 bool GuiView::hasFocus() const
883 {
884         if (currentWorkArea())
885                 return currentWorkArea()->hasFocus();
886         if (currentMainWorkArea())
887                 return currentMainWorkArea()->hasFocus();
888         return d.bg_widget_->hasFocus();
889 }
890
891
892 void GuiView::focusInEvent(QFocusEvent * e)
893 {
894         LYXERR(Debug::DEBUG, "GuiView::focusInEvent()" << this);
895         QMainWindow::focusInEvent(e);
896         // Make sure guiApp points to the correct view.
897         guiApp->setCurrentView(this);
898         if (currentWorkArea())
899                 currentWorkArea()->setFocus();
900         else if (currentMainWorkArea())
901                 currentMainWorkArea()->setFocus();
902         else
903                 d.bg_widget_->setFocus();
904 }
905
906
907 QMenu * GuiView::createPopupMenu()
908 {
909         return d.toolBarPopup(this);
910 }
911
912
913 void GuiView::showEvent(QShowEvent * e)
914 {
915         LYXERR(Debug::GUI, "Passed Geometry "
916                 << size().height() << "x" << size().width()
917                 << "+" << pos().x() << "+" << pos().y());
918
919         if (d.splitter_->count() == 0)
920                 // No work area, switch to the background widget.
921                 d.setBackground();
922
923         updateToolbars();
924         QMainWindow::showEvent(e);
925 }
926
927
928 bool GuiView::closeScheduled()
929 {
930         closing_ = true;
931         return close();
932 }
933
934
935 bool GuiView::prepareAllBuffersForLogout()
936 {
937         Buffer * first = theBufferList().first();
938         if (!first)
939                 return true;
940
941         // First, iterate over all buffers and ask the users if unsaved
942         // changes should be saved.
943         // We cannot use a for loop as the buffer list cycles.
944         Buffer * b = first;
945         do {
946                 if (!saveBufferIfNeeded(const_cast<Buffer &>(*b), false))
947                         return false;
948                 b = theBufferList().next(b);
949         } while (b != first);
950
951         // Next, save session state
952         // When a view/window was closed before without quitting LyX, there
953         // are already entries in the lastOpened list.
954         theSession().lastOpened().clear();
955         writeSession();
956
957         return true;
958 }
959
960
961 /** Destroy only all tabbed WorkAreas. Destruction of other WorkAreas
962  ** is responsibility of the container (e.g., dialog)
963  **/
964 void GuiView::closeEvent(QCloseEvent * close_event)
965 {
966         LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
967
968         if (!GuiViewPrivate::busyBuffers.isEmpty()) {
969                 Alert::warning(_("Exit LyX"), 
970                         _("LyX could not be closed because documents are being processed by LyX."));
971                 close_event->setAccepted(false);
972                 return;
973         }
974
975         // If the user pressed the x (so we didn't call closeView
976         // programmatically), we want to clear all existing entries.
977         if (!closing_)
978                 theSession().lastOpened().clear();
979         closing_ = true;
980
981         writeSession();
982
983         // it can happen that this event arrives without selecting the view,
984         // e.g. when clicking the close button on a background window.
985         setFocus();
986         if (!closeWorkAreaAll()) {
987                 closing_ = false;
988                 close_event->ignore();
989                 return;
990         }
991
992         // Make sure that nothing will use this to be closed View.
993         guiApp->unregisterView(this);
994
995         if (isFullScreen()) {
996                 // Switch off fullscreen before closing.
997                 toggleFullScreen();
998                 updateDialogs();
999         }
1000
1001         // Make sure the timer time out will not trigger a statusbar update.
1002         d.statusbar_timer_.stop();
1003
1004         // Saving fullscreen requires additional tweaks in the toolbar code.
1005         // It wouldn't also work under linux natively.
1006         if (lyxrc.allow_geometry_session) {
1007                 saveLayout();
1008                 saveUISettings();
1009         }
1010
1011         close_event->accept();
1012 }
1013
1014
1015 void GuiView::dragEnterEvent(QDragEnterEvent * event)
1016 {
1017         if (event->mimeData()->hasUrls())
1018                 event->accept();
1019         /// \todo Ask lyx-devel is this is enough:
1020         /// if (event->mimeData()->hasFormat("text/plain"))
1021         ///     event->acceptProposedAction();
1022 }
1023
1024
1025 void GuiView::dropEvent(QDropEvent * event)
1026 {
1027         QList<QUrl> files = event->mimeData()->urls();
1028         if (files.isEmpty())
1029                 return;
1030
1031         LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
1032         for (int i = 0; i != files.size(); ++i) {
1033                 string const file = os::internal_path(fromqstr(
1034                         files.at(i).toLocalFile()));
1035                 if (file.empty())
1036                         continue;
1037
1038                 string const ext = support::getExtension(file);
1039                 vector<const Format *> found_formats;
1040
1041                 // Find all formats that have the correct extension.
1042                 vector<const Format *> const & import_formats
1043                         = theConverters().importableFormats();
1044                 vector<const Format *>::const_iterator it = import_formats.begin();
1045                 for (; it != import_formats.end(); ++it)
1046                         if ((*it)->hasExtension(ext))
1047                                 found_formats.push_back(*it);
1048
1049                 FuncRequest cmd;
1050                 if (found_formats.size() >= 1) {
1051                         if (found_formats.size() > 1) {
1052                                 //FIXME: show a dialog to choose the correct importable format
1053                                 LYXERR(Debug::FILES,
1054                                         "Multiple importable formats found, selecting first");
1055                         }
1056                         string const arg = found_formats[0]->name() + " " + file;
1057                         cmd = FuncRequest(LFUN_BUFFER_IMPORT, arg);
1058                 }
1059                 else {
1060                         //FIXME: do we have to explicitly check whether it's a lyx file?
1061                         LYXERR(Debug::FILES,
1062                                 "No formats found, trying to open it as a lyx file");
1063                         cmd = FuncRequest(LFUN_FILE_OPEN, file);
1064                 }
1065                 // add the functions to the queue
1066                 guiApp->addToFuncRequestQueue(cmd);
1067                 event->accept();
1068         }
1069         // now process the collected functions. We perform the events
1070         // asynchronously. This prevents potential problems in case the
1071         // BufferView is closed within an event.
1072         guiApp->processFuncRequestQueueAsync();
1073 }
1074
1075
1076 void GuiView::message(docstring const & str)
1077 {
1078         if (ForkedProcess::iAmAChild())
1079                 return;
1080
1081         // call is moved to GUI-thread by GuiProgress
1082         d.progress_->appendMessage(toqstr(str));
1083 }
1084
1085
1086 void GuiView::clearMessageText()
1087 {
1088         message(docstring());
1089 }
1090
1091
1092 void GuiView::updateStatusBarMessage(QString const & str)
1093 {
1094         statusBar()->showMessage(str);
1095         d.statusbar_timer_.stop();
1096         d.statusbar_timer_.start(3000);
1097 }
1098
1099
1100 void GuiView::smallSizedIcons()
1101 {
1102         setIconSize(QSize(d.smallIconSize, d.smallIconSize));
1103 }
1104
1105
1106 void GuiView::normalSizedIcons()
1107 {
1108         setIconSize(QSize(d.normalIconSize, d.normalIconSize));
1109 }
1110
1111
1112 void GuiView::bigSizedIcons()
1113 {
1114         setIconSize(QSize(d.bigIconSize, d.bigIconSize));
1115 }
1116
1117
1118 void GuiView::hugeSizedIcons()
1119 {
1120         setIconSize(QSize(d.hugeIconSize, d.hugeIconSize));
1121 }
1122
1123
1124 void GuiView::giantSizedIcons()
1125 {
1126         setIconSize(QSize(d.giantIconSize, d.giantIconSize));
1127 }
1128
1129
1130 void GuiView::clearMessage()
1131 {
1132         // FIXME: This code was introduced in r19643 to fix bug #4123. However,
1133         // the hasFocus function mostly returns false, even if the focus is on
1134         // a workarea in this view.
1135         //if (!hasFocus())
1136         //      return;
1137         showMessage();
1138         d.statusbar_timer_.stop();
1139 }
1140
1141
1142 void GuiView::updateWindowTitle(GuiWorkArea * wa)
1143 {
1144         if (wa != d.current_work_area_
1145                 || wa->bufferView().buffer().isInternal())
1146                 return;
1147         setWindowTitle(qt_("LyX: ") + wa->windowTitle());
1148         setWindowIconText(wa->windowIconText());
1149 #if (QT_VERSION >= 0x040400)
1150         // Sets the path for the window: this is used by OSX to 
1151         // allow a context click on the title bar showing a menu
1152         // with the path up to the file
1153         setWindowFilePath(toqstr(wa->bufferView().buffer().absFileName()));
1154 #endif
1155 }
1156
1157
1158 void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
1159 {
1160         if (d.current_work_area_)
1161                 QObject::disconnect(d.current_work_area_, SIGNAL(busy(bool)),
1162                         this, SLOT(setBusy(bool)));
1163         disconnectBuffer();
1164         disconnectBufferView();
1165         connectBufferView(wa->bufferView());
1166         connectBuffer(wa->bufferView().buffer());
1167         d.current_work_area_ = wa;
1168         QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
1169                 this, SLOT(updateWindowTitle(GuiWorkArea *)));
1170         QObject::connect(wa, SIGNAL(busy(bool)), this, SLOT(setBusy(bool)));
1171         updateWindowTitle(wa);
1172
1173         structureChanged();
1174
1175         // The document settings needs to be reinitialised.
1176         updateDialog("document", "");
1177
1178         // Buffer-dependent dialogs must be updated. This is done here because
1179         // some dialogs require buffer()->text.
1180         updateDialogs();
1181 }
1182
1183
1184 void GuiView::on_lastWorkAreaRemoved()
1185 {
1186         if (closing_)
1187                 // We already are in a close event. Nothing more to do.
1188                 return;
1189
1190         if (d.splitter_->count() > 1)
1191                 // We have a splitter so don't close anything.
1192                 return;
1193
1194         // Reset and updates the dialogs.
1195         d.toc_models_.reset(0);
1196         updateDialog("document", "");
1197         updateDialogs();
1198
1199         resetWindowTitleAndIconText();
1200         updateStatusBar();
1201
1202         if (lyxrc.open_buffers_in_tabs)
1203                 // Nothing more to do, the window should stay open.
1204                 return;
1205
1206         if (guiApp->viewIds().size() > 1) {
1207                 close();
1208                 return;
1209         }
1210
1211 #ifdef Q_OS_MAC
1212         // On Mac we also close the last window because the application stay
1213         // resident in memory. On other platforms we don't close the last
1214         // window because this would quit the application.
1215         close();
1216 #endif
1217 }
1218
1219
1220 void GuiView::updateStatusBar()
1221 {
1222         // let the user see the explicit message
1223         if (d.statusbar_timer_.isActive())
1224                 return;
1225
1226         showMessage();
1227 }
1228
1229
1230 void GuiView::showMessage()
1231 {
1232         if (busy_)
1233                 return;
1234         QString msg = toqstr(theGuiApp()->viewStatusMessage());
1235         if (msg.isEmpty()) {
1236                 BufferView const * bv = currentBufferView();
1237                 if (bv)
1238                         msg = toqstr(bv->cursor().currentState());
1239                 else
1240                         msg = qt_("Welcome to LyX!");
1241         }
1242         statusBar()->showMessage(msg);
1243 }
1244
1245
1246 bool GuiView::event(QEvent * e)
1247 {
1248         switch (e->type())
1249         {
1250         // Useful debug code:
1251         //case QEvent::ActivationChange:
1252         //case QEvent::WindowDeactivate:
1253         //case QEvent::Paint:
1254         //case QEvent::Enter:
1255         //case QEvent::Leave:
1256         //case QEvent::HoverEnter:
1257         //case QEvent::HoverLeave:
1258         //case QEvent::HoverMove:
1259         //case QEvent::StatusTip:
1260         //case QEvent::DragEnter:
1261         //case QEvent::DragLeave:
1262         //case QEvent::Drop:
1263         //      break;
1264
1265         case QEvent::WindowActivate: {
1266                 GuiView * old_view = guiApp->currentView();
1267                 if (this == old_view) {
1268                         setFocus();
1269                         return QMainWindow::event(e);
1270                 }
1271                 if (old_view && old_view->currentBufferView()) {
1272                         // save current selection to the selection buffer to allow
1273                         // middle-button paste in this window.
1274                         cap::saveSelection(old_view->currentBufferView()->cursor());
1275                 }
1276                 guiApp->setCurrentView(this);
1277                 if (d.current_work_area_) {
1278                         BufferView & bv = d.current_work_area_->bufferView();
1279                         connectBufferView(bv);
1280                         connectBuffer(bv.buffer());
1281                         // The document structure, name and dialogs might have
1282                         // changed in another view.
1283                         structureChanged();
1284                         // The document settings needs to be reinitialised.
1285                         updateDialog("document", "");
1286                         updateDialogs();
1287                 } else {
1288                         resetWindowTitleAndIconText();
1289                 }
1290                 setFocus();
1291                 return QMainWindow::event(e);
1292         }
1293
1294         case QEvent::ShortcutOverride: {
1295                 // See bug 4888
1296                 if (isFullScreen() && menuBar()->isHidden()) {
1297                         QKeyEvent * ke = static_cast<QKeyEvent*>(e);
1298                         // FIXME: we should also try to detect special LyX shortcut such as
1299                         // Alt-P and Alt-M. Right now there is a hack in
1300                         // GuiWorkArea::processKeySym() that hides again the menubar for
1301                         // those cases.
1302                         if (ke->modifiers() & Qt::AltModifier && ke->key() != Qt::Key_Alt) {
1303                                 menuBar()->show();
1304                                 return QMainWindow::event(e);
1305                         }
1306                 }
1307                 return QMainWindow::event(e);
1308         }
1309
1310         default:
1311                 return QMainWindow::event(e);
1312         }
1313 }
1314
1315 void GuiView::resetWindowTitleAndIconText()
1316 {
1317         setWindowTitle(qt_("LyX"));
1318         setWindowIconText(qt_("LyX"));
1319 }
1320
1321 bool GuiView::focusNextPrevChild(bool /*next*/)
1322 {
1323         setFocus();
1324         return true;
1325 }
1326
1327
1328 bool GuiView::busy() const
1329 {
1330         return busy_ > 0;
1331 }
1332
1333
1334 void GuiView::setBusy(bool busy)
1335 {
1336         bool const busy_before = busy_ > 0;
1337         busy ? ++busy_ : --busy_;
1338         if ((busy_ > 0) == busy_before)
1339                 // busy state didn't change
1340                 return;
1341
1342         if (busy) {
1343                 QApplication::setOverrideCursor(Qt::WaitCursor);
1344                 return;
1345         }
1346         QApplication::restoreOverrideCursor();
1347         updateLayoutList();     
1348 }
1349
1350
1351 void GuiView::resetCommandExecute()
1352 {
1353         command_execute_ = false;
1354         updateToolbars();
1355 }
1356
1357
1358 double GuiView::pixelRatio() const
1359 {
1360 #if QT_VERSION >= 0x050000
1361         return devicePixelRatio();
1362 #else
1363         return 1.0;
1364 #endif
1365 }
1366         
1367         
1368 GuiWorkArea * GuiView::workArea(int index)
1369 {
1370         if (TabWorkArea * twa = d.currentTabWorkArea())
1371                 if (index < twa->count())
1372                         return dynamic_cast<GuiWorkArea *>(twa->widget(index));
1373         return 0;
1374 }
1375
1376
1377 GuiWorkArea * GuiView::workArea(Buffer & buffer)
1378 {
1379         if (currentWorkArea()
1380                 && &currentWorkArea()->bufferView().buffer() == &buffer)
1381                 return currentWorkArea();
1382         if (TabWorkArea * twa = d.currentTabWorkArea())
1383                 return twa->workArea(buffer);
1384         return 0;
1385 }
1386
1387
1388 GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
1389 {
1390         // Automatically create a TabWorkArea if there are none yet.
1391         TabWorkArea * tab_widget = d.splitter_->count()
1392                 ? d.currentTabWorkArea() : addTabWorkArea();
1393         return tab_widget->addWorkArea(buffer, *this);
1394 }
1395
1396
1397 TabWorkArea * GuiView::addTabWorkArea()
1398 {
1399         TabWorkArea * twa = new TabWorkArea;
1400         QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
1401                 this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
1402         QObject::connect(twa, SIGNAL(lastWorkAreaRemoved()),
1403                          this, SLOT(on_lastWorkAreaRemoved()));
1404
1405         d.splitter_->addWidget(twa);
1406         d.stack_widget_->setCurrentWidget(d.splitter_);
1407         return twa;
1408 }
1409
1410
1411 GuiWorkArea const * GuiView::currentWorkArea() const
1412 {
1413         return d.current_work_area_;
1414 }
1415
1416
1417 GuiWorkArea * GuiView::currentWorkArea()
1418 {
1419         return d.current_work_area_;
1420 }
1421
1422
1423 GuiWorkArea const * GuiView::currentMainWorkArea() const
1424 {
1425         if (!d.currentTabWorkArea())
1426                 return 0;
1427         return d.currentTabWorkArea()->currentWorkArea();
1428 }
1429
1430
1431 GuiWorkArea * GuiView::currentMainWorkArea()
1432 {
1433         if (!d.currentTabWorkArea())
1434                 return 0;
1435         return d.currentTabWorkArea()->currentWorkArea();
1436 }
1437
1438
1439 void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
1440 {
1441         LYXERR(Debug::DEBUG, "Setting current wa: " << wa << endl);
1442         if (!wa) {
1443                 d.current_work_area_ = 0;
1444                 d.setBackground();
1445                 return;
1446         }
1447
1448         // FIXME: I've no clue why this is here and why it accesses
1449         //  theGuiApp()->currentView, which might be 0 (bug 6464).
1450         //  See also 27525 (vfr).
1451         if (theGuiApp()->currentView() == this
1452                   && theGuiApp()->currentView()->currentWorkArea() == wa)
1453                 return;
1454
1455         if (currentBufferView())
1456                 cap::saveSelection(currentBufferView()->cursor());
1457
1458         theGuiApp()->setCurrentView(this);
1459         d.current_work_area_ = wa;
1460         
1461         // We need to reset this now, because it will need to be
1462         // right if the tabWorkArea gets reset in the for loop. We
1463         // will change it back if we aren't in that case.
1464         GuiWorkArea * const old_cmwa = d.current_main_work_area_;
1465         d.current_main_work_area_ = wa;
1466
1467         for (int i = 0; i != d.splitter_->count(); ++i) {
1468                 if (d.tabWorkArea(i)->setCurrentWorkArea(wa)) {
1469                         LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() 
1470                                 << ", Current main wa: " << currentMainWorkArea());
1471                         return;
1472                 }
1473         }
1474         
1475         d.current_main_work_area_ = old_cmwa;
1476         
1477         LYXERR(Debug::DEBUG, "This is not a tabbed wa");
1478         on_currentWorkAreaChanged(wa);
1479         BufferView & bv = wa->bufferView();
1480         bv.cursor().fixIfBroken();
1481         bv.updateMetrics();
1482         wa->setUpdatesEnabled(true);
1483         LYXERR(Debug::DEBUG, "Current wa: " << currentWorkArea() << ", Current main wa: " << currentMainWorkArea());
1484 }
1485
1486
1487 void GuiView::removeWorkArea(GuiWorkArea * wa)
1488 {
1489         LASSERT(wa, return);
1490         if (wa == d.current_work_area_) {
1491                 disconnectBuffer();
1492                 disconnectBufferView();
1493                 d.current_work_area_ = 0;
1494                 d.current_main_work_area_ = 0;
1495         }
1496
1497         bool found_twa = false;
1498         for (int i = 0; i != d.splitter_->count(); ++i) {
1499                 TabWorkArea * twa = d.tabWorkArea(i);
1500                 if (twa->removeWorkArea(wa)) {
1501                         // Found in this tab group, and deleted the GuiWorkArea.
1502                         found_twa = true;
1503                         if (twa->count() != 0) {
1504                                 if (d.current_work_area_ == 0)
1505                                         // This means that we are closing the current GuiWorkArea, so
1506                                         // switch to the next GuiWorkArea in the found TabWorkArea.
1507                                         setCurrentWorkArea(twa->currentWorkArea());
1508                         } else {
1509                                 // No more WorkAreas in this tab group, so delete it.
1510                                 delete twa;
1511                         }
1512                         break;
1513                 }
1514         }
1515
1516         // It is not a tabbed work area (i.e., the search work area), so it
1517         // should be deleted by other means.
1518         LASSERT(found_twa, return);
1519
1520         if (d.current_work_area_ == 0) {
1521                 if (d.splitter_->count() != 0) {
1522                         TabWorkArea * twa = d.currentTabWorkArea();
1523                         setCurrentWorkArea(twa->currentWorkArea());
1524                 } else {
1525                         // No more work areas, switch to the background widget.
1526                         setCurrentWorkArea(0);
1527                 }
1528         }
1529 }
1530
1531
1532 LayoutBox * GuiView::getLayoutDialog() const
1533 {
1534         return d.layout_;
1535 }
1536
1537
1538 void GuiView::updateLayoutList()
1539 {
1540         if (d.layout_)
1541                 d.layout_->updateContents(false);
1542 }
1543
1544
1545 void GuiView::updateToolbars()
1546 {
1547         ToolbarMap::iterator end = d.toolbars_.end();
1548         if (d.current_work_area_) {
1549                 int context = 0;
1550                 if (d.current_work_area_->bufferView().cursor().inMathed()
1551                         && !d.current_work_area_->bufferView().cursor().inRegexped())
1552                         context |= Toolbars::MATH;
1553                 if (lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled())
1554                         context |= Toolbars::TABLE;
1555                 if (currentBufferView()->buffer().areChangesPresent()
1556                     || (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1557                         && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1558                     || (lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).enabled()
1559                         && lyx::getStatus(FuncRequest(LFUN_CHANGES_OUTPUT)).onOff(true)))
1560                         context |= Toolbars::REVIEW;
1561                 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1562                         context |= Toolbars::MATHMACROTEMPLATE;
1563                 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1564                         context |= Toolbars::IPA;
1565                 if (command_execute_)
1566                         context |= Toolbars::MINIBUFFER;
1567
1568                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1569                         it->second->update(context);
1570         } else
1571                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1572                         it->second->update();
1573 }
1574
1575
1576 void GuiView::setBuffer(Buffer * newBuffer)
1577 {
1578         LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1579         LASSERT(newBuffer, return);
1580         
1581         GuiWorkArea * wa = workArea(*newBuffer);
1582         if (wa == 0) {
1583                 setBusy(true);
1584                 newBuffer->masterBuffer()->updateBuffer();
1585                 setBusy(false);
1586                 wa = addWorkArea(*newBuffer);
1587                 // scroll to the position when the BufferView was last closed
1588                 if (lyxrc.use_lastfilepos) {
1589                         LastFilePosSection::FilePos filepos =
1590                                 theSession().lastFilePos().load(newBuffer->fileName());
1591                         wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1592                 }
1593         } else {
1594                 //Disconnect the old buffer...there's no new one.
1595                 disconnectBuffer();
1596         }
1597         connectBuffer(*newBuffer);
1598         connectBufferView(wa->bufferView());
1599         setCurrentWorkArea(wa);
1600 }
1601
1602
1603 void GuiView::connectBuffer(Buffer & buf)
1604 {
1605         buf.setGuiDelegate(this);
1606 }
1607
1608
1609 void GuiView::disconnectBuffer()
1610 {
1611         if (d.current_work_area_)
1612                 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1613 }
1614
1615
1616 void GuiView::connectBufferView(BufferView & bv)
1617 {
1618         bv.setGuiDelegate(this);
1619 }
1620
1621
1622 void GuiView::disconnectBufferView()
1623 {
1624         if (d.current_work_area_)
1625                 d.current_work_area_->bufferView().setGuiDelegate(0);
1626 }
1627
1628
1629 void GuiView::errors(string const & error_type, bool from_master)
1630 {
1631         BufferView const * const bv = currentBufferView();
1632         if (!bv)
1633                 return;
1634
1635 #if EXPORT_in_THREAD
1636         // We are called with from_master == false by default, so we
1637         // have to figure out whether that is the case or not.
1638         ErrorList & el = bv->buffer().errorList(error_type);
1639         if (el.empty()) {
1640             el = bv->buffer().masterBuffer()->errorList(error_type);
1641             from_master = true;
1642         }
1643 #else
1644         ErrorList const & el = from_master ?
1645                 bv->buffer().masterBuffer()->errorList(error_type) :
1646                 bv->buffer().errorList(error_type);
1647 #endif
1648
1649         if (el.empty())
1650                 return;
1651
1652         string data = error_type;
1653         if (from_master)
1654                 data = "from_master|" + error_type;
1655         showDialog("errorlist", data);
1656 }
1657
1658
1659 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1660 {
1661         d.toc_models_.updateItem(toqstr(type), dit);
1662 }
1663
1664
1665 void GuiView::structureChanged()
1666 {
1667         // FIXME: This is slightly expensive, though less than the tocBackend update
1668         // (#9880). This also resets the view in the Toc Widget (#6675).
1669         d.toc_models_.reset(documentBufferView());
1670         // Navigator needs more than a simple update in this case. It needs to be
1671         // rebuilt.
1672         updateDialog("toc", "");
1673 }
1674
1675
1676 void GuiView::updateDialog(string const & name, string const & data)
1677 {
1678         if (!isDialogVisible(name))
1679                 return;
1680
1681         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1682         if (it == d.dialogs_.end())
1683                 return;
1684
1685         Dialog * const dialog = it->second.get();
1686         if (dialog->isVisibleView())
1687                 dialog->initialiseParams(data);
1688 }
1689
1690
1691 BufferView * GuiView::documentBufferView()
1692 {
1693         return currentMainWorkArea()
1694                 ? &currentMainWorkArea()->bufferView()
1695                 : 0;
1696 }
1697
1698
1699 BufferView const * GuiView::documentBufferView() const
1700 {
1701         return currentMainWorkArea()
1702                 ? &currentMainWorkArea()->bufferView()
1703                 : 0;
1704 }
1705
1706
1707 BufferView * GuiView::currentBufferView()
1708 {
1709         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1710 }
1711
1712
1713 BufferView const * GuiView::currentBufferView() const
1714 {
1715         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1716 }
1717
1718
1719 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1720         Buffer const * orig, Buffer * clone)
1721 {
1722         bool const success = clone->autoSave();
1723         delete clone;
1724         busyBuffers.remove(orig);
1725         return success
1726                 ? _("Automatic save done.")
1727                 : _("Automatic save failed!");
1728 }
1729
1730
1731 void GuiView::autoSave()
1732 {
1733         LYXERR(Debug::INFO, "Running autoSave()");
1734
1735         Buffer * buffer = documentBufferView()
1736                 ? &documentBufferView()->buffer() : 0;
1737         if (!buffer) {
1738                 resetAutosaveTimers();
1739                 return;
1740         }
1741
1742         GuiViewPrivate::busyBuffers.insert(buffer);
1743         QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1744                 buffer, buffer->cloneBufferOnly());
1745         d.autosave_watcher_.setFuture(f);
1746         resetAutosaveTimers();
1747 }
1748
1749
1750 void GuiView::resetAutosaveTimers()
1751 {
1752         if (lyxrc.autosave)
1753                 d.autosave_timeout_.restart();
1754 }
1755
1756
1757 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1758 {
1759         bool enable = true;
1760         Buffer * buf = currentBufferView()
1761                 ? &currentBufferView()->buffer() : 0;
1762         Buffer * doc_buffer = documentBufferView()
1763                 ? &(documentBufferView()->buffer()) : 0;
1764
1765 #ifdef Q_OS_MAC
1766         /* In LyX/Mac, when a dialog is open, the menus of the
1767            application can still be accessed without giving focus to
1768            the main window. In this case, we want to disable the menu
1769            entries that are buffer-related.
1770            This code must not be used on Linux and Windows, since it
1771            would disable buffer-related entries when hovering over the
1772            menu (see bug #9574).
1773          */
1774         if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1775                 buf = 0;
1776                 doc_buffer = 0;
1777         }
1778 #endif
1779
1780         // Check whether we need a buffer
1781         if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1782                 // no, exit directly
1783                 flag.message(from_utf8(N_("Command not allowed with"
1784                                         "out any document open")));
1785                 flag.setEnabled(false);
1786                 return true;
1787         }
1788
1789         if (cmd.origin() == FuncRequest::TOC) {
1790                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1791                 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1792                         flag.setEnabled(false);
1793                 return true;
1794         }
1795
1796         switch(cmd.action()) {
1797         case LFUN_BUFFER_IMPORT:
1798                 break;
1799
1800         case LFUN_MASTER_BUFFER_UPDATE:
1801         case LFUN_MASTER_BUFFER_VIEW:
1802                 enable = doc_buffer
1803                         && (doc_buffer->parent() != 0
1804                             || doc_buffer->hasChildren())
1805                         && !d.processing_thread_watcher_.isRunning();
1806                 break;
1807
1808         case LFUN_BUFFER_UPDATE:
1809         case LFUN_BUFFER_VIEW: {
1810                 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1811                         enable = false;
1812                         break;
1813                 }
1814                 string format = to_utf8(cmd.argument());
1815                 if (cmd.argument().empty())
1816                         format = doc_buffer->params().getDefaultOutputFormat();
1817                 enable = doc_buffer->params().isExportableFormat(format);
1818                 break;
1819         }
1820
1821         case LFUN_BUFFER_RELOAD:
1822                 enable = doc_buffer && !doc_buffer->isUnnamed()
1823                         && doc_buffer->fileName().exists()
1824                         && (!doc_buffer->isClean()
1825                            || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1826                 break;
1827
1828         case LFUN_BUFFER_CHILD_OPEN:
1829                 enable = doc_buffer != 0;
1830                 break;
1831
1832         case LFUN_BUFFER_WRITE:
1833                 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1834                 break;
1835
1836         //FIXME: This LFUN should be moved to GuiApplication.
1837         case LFUN_BUFFER_WRITE_ALL: {
1838                 // We enable the command only if there are some modified buffers
1839                 Buffer * first = theBufferList().first();
1840                 enable = false;
1841                 if (!first)
1842                         break;
1843                 Buffer * b = first;
1844                 // We cannot use a for loop as the buffer list is a cycle.
1845                 do {
1846                         if (!b->isClean()) {
1847                                 enable = true;
1848                                 break;
1849                         }
1850                         b = theBufferList().next(b);
1851                 } while (b != first);
1852                 break;
1853         }
1854
1855         case LFUN_BUFFER_WRITE_AS:
1856         case LFUN_BUFFER_EXPORT_AS:
1857                 enable = doc_buffer != 0;
1858                 break;
1859
1860         case LFUN_BUFFER_CLOSE:
1861         case LFUN_VIEW_CLOSE:
1862                 enable = doc_buffer != 0;
1863                 break;
1864
1865         case LFUN_BUFFER_CLOSE_ALL:
1866                 enable = theBufferList().last() != theBufferList().first();
1867                 break;
1868
1869         case LFUN_VIEW_SPLIT:
1870                 if (cmd.getArg(0) == "vertical")
1871                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1872                                          d.splitter_->orientation() == Qt::Vertical);
1873                 else
1874                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1875                                          d.splitter_->orientation() == Qt::Horizontal);
1876                 break;
1877
1878         case LFUN_TAB_GROUP_CLOSE:
1879                 enable = d.tabWorkAreaCount() > 1;
1880                 break;
1881
1882         case LFUN_TOOLBAR_TOGGLE: {
1883                 string const name = cmd.getArg(0);
1884                 if (GuiToolbar * t = toolbar(name))
1885                         flag.setOnOff(t->isVisible());
1886                 else {
1887                         enable = false;
1888                         docstring const msg =
1889                                 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1890                         flag.message(msg);
1891                 }
1892                 break;
1893         }
1894
1895         case LFUN_DROP_LAYOUTS_CHOICE:
1896                 enable = buf != 0;
1897                 break;
1898
1899         case LFUN_UI_TOGGLE:
1900                 flag.setOnOff(isFullScreen());
1901                 break;
1902
1903         case LFUN_DIALOG_DISCONNECT_INSET:
1904                 break;
1905
1906         case LFUN_DIALOG_HIDE:
1907                 // FIXME: should we check if the dialog is shown?
1908                 break;
1909
1910         case LFUN_DIALOG_TOGGLE:
1911                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1912                 // fall through to set "enable"
1913         case LFUN_DIALOG_SHOW: {
1914                 string const name = cmd.getArg(0);
1915                 if (!doc_buffer)
1916                         enable = name == "aboutlyx"
1917                                 || name == "file" //FIXME: should be removed.
1918                                 || name == "prefs"
1919                                 || name == "texinfo"
1920                                 || name == "progress"
1921                                 || name == "compare";
1922                 else if (name == "character" || name == "symbols"
1923                         || name == "mathdelimiter" || name == "mathmatrix") {
1924                         if (!buf || buf->isReadonly())
1925                                 enable = false;
1926                         else {
1927                                 Cursor const & cur = currentBufferView()->cursor();
1928                                 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1929                         }
1930                 }
1931                 else if (name == "latexlog")
1932                         enable = FileName(doc_buffer->logName()).isReadableFile();
1933                 else if (name == "spellchecker")
1934                         enable = theSpellChecker() 
1935                                 && !doc_buffer->isReadonly()
1936                                 && !doc_buffer->text().empty();
1937                 else if (name == "vclog")
1938                         enable = doc_buffer->lyxvc().inUse();
1939                 break;
1940         }
1941
1942         case LFUN_DIALOG_UPDATE: {
1943                 string const name = cmd.getArg(0);
1944                 if (!buf)
1945                         enable = name == "prefs";
1946                 break;
1947         }
1948
1949         case LFUN_COMMAND_EXECUTE:
1950         case LFUN_MESSAGE:
1951         case LFUN_MENU_OPEN:
1952                 // Nothing to check.
1953                 break;
1954
1955         case LFUN_COMPLETION_INLINE:
1956                 if (!d.current_work_area_
1957                         || !d.current_work_area_->completer().inlinePossible(
1958                         currentBufferView()->cursor()))
1959                         enable = false;
1960                 break;
1961
1962         case LFUN_COMPLETION_POPUP:
1963                 if (!d.current_work_area_
1964                         || !d.current_work_area_->completer().popupPossible(
1965                         currentBufferView()->cursor()))
1966                         enable = false;
1967                 break;
1968
1969         case LFUN_COMPLETE:
1970                 if (!d.current_work_area_
1971                         || !d.current_work_area_->completer().inlinePossible(
1972                         currentBufferView()->cursor()))
1973                         enable = false;
1974                 break;
1975
1976         case LFUN_COMPLETION_ACCEPT:
1977                 if (!d.current_work_area_
1978                         || (!d.current_work_area_->completer().popupVisible()
1979                         && !d.current_work_area_->completer().inlineVisible()
1980                         && !d.current_work_area_->completer().completionAvailable()))
1981                         enable = false;
1982                 break;
1983
1984         case LFUN_COMPLETION_CANCEL:
1985                 if (!d.current_work_area_
1986                         || (!d.current_work_area_->completer().popupVisible()
1987                         && !d.current_work_area_->completer().inlineVisible()))
1988                         enable = false;
1989                 break;
1990
1991         case LFUN_BUFFER_ZOOM_OUT:
1992                 enable = doc_buffer && lyxrc.zoom > 10;
1993                 if (lyxrc.zoom <= 10)
1994                         flag.message(_("Zoom level cannot be less than 10%."));
1995                 break;
1996
1997         case LFUN_BUFFER_ZOOM_IN:
1998                 enable = doc_buffer != 0;
1999                 break;
2000
2001         case LFUN_BUFFER_MOVE_NEXT:
2002         case LFUN_BUFFER_MOVE_PREVIOUS:
2003                 // we do not cycle when moving
2004         case LFUN_BUFFER_NEXT:
2005         case LFUN_BUFFER_PREVIOUS:
2006                 // because we cycle, it doesn't matter whether on first or last
2007                 enable = (d.currentTabWorkArea()->count() > 1);
2008                 break;
2009         case LFUN_BUFFER_SWITCH:
2010                 // toggle on the current buffer, but do not toggle off
2011                 // the other ones (is that a good idea?)
2012                 if (doc_buffer
2013                         && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2014                         flag.setOnOff(true);
2015                 break;
2016
2017         case LFUN_VC_REGISTER:
2018                 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2019                 break;
2020         case LFUN_VC_RENAME:
2021                 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2022                 break;
2023         case LFUN_VC_COPY:
2024                 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2025                 break;
2026         case LFUN_VC_CHECK_IN:
2027                 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2028                 break;
2029         case LFUN_VC_CHECK_OUT:
2030                 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2031                 break;
2032         case LFUN_VC_LOCKING_TOGGLE:
2033                 enable = doc_buffer && !doc_buffer->isReadonly()
2034                         && doc_buffer->lyxvc().lockingToggleEnabled();
2035                 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2036                 break;
2037         case LFUN_VC_REVERT:
2038                 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2039                 break;
2040         case LFUN_VC_UNDO_LAST:
2041                 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2042                 break;
2043         case LFUN_VC_REPO_UPDATE:
2044                 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2045                 break;
2046         case LFUN_VC_COMMAND: {
2047                 if (cmd.argument().empty())
2048                         enable = false;
2049                 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2050                         enable = false;
2051                 break;
2052         }
2053         case LFUN_VC_COMPARE:
2054                 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2055                 break;
2056
2057         case LFUN_SERVER_GOTO_FILE_ROW:
2058         case LFUN_LYX_ACTIVATE:
2059                 break;
2060         case LFUN_FORWARD_SEARCH:
2061                 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2062                 break;
2063
2064         case LFUN_FILE_INSERT_PLAINTEXT:
2065         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2066                 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2067                 break;
2068
2069         case LFUN_SPELLING_CONTINUOUSLY:
2070                 flag.setOnOff(lyxrc.spellcheck_continuously);
2071                 break;
2072
2073         default:
2074                 return false;
2075         }
2076
2077         if (!enable)
2078                 flag.setEnabled(false);
2079
2080         return true;
2081 }
2082
2083
2084 static FileName selectTemplateFile()
2085 {
2086         FileDialog dlg(qt_("Select template file"));
2087         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2088         dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2089
2090         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2091                                  QStringList(qt_("LyX Documents (*.lyx)")));
2092
2093         if (result.first == FileDialog::Later)
2094                 return FileName();
2095         if (result.second.isEmpty())
2096                 return FileName();
2097         return FileName(fromqstr(result.second));
2098 }
2099
2100
2101 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2102 {
2103         setBusy(true);
2104
2105         Buffer * newBuffer = 0;
2106         try {
2107                 newBuffer = checkAndLoadLyXFile(filename);
2108         } catch (ExceptionMessage const & e) {
2109                 setBusy(false);
2110                 throw(e);
2111         }
2112         setBusy(false);
2113
2114         if (!newBuffer) {
2115                 message(_("Document not loaded."));
2116                 return 0;
2117         }
2118
2119         setBuffer(newBuffer);
2120         newBuffer->errors("Parse");
2121
2122         if (tolastfiles)
2123                 theSession().lastFiles().add(filename);
2124
2125         return newBuffer;
2126 }
2127
2128
2129 void GuiView::openDocument(string const & fname)
2130 {
2131         string initpath = lyxrc.document_path;
2132
2133         if (documentBufferView()) {
2134                 string const trypath = documentBufferView()->buffer().filePath();
2135                 // If directory is writeable, use this as default.
2136                 if (FileName(trypath).isDirWritable())
2137                         initpath = trypath;
2138         }
2139
2140         string filename;
2141
2142         if (fname.empty()) {
2143                 FileDialog dlg(qt_("Select document to open"));
2144                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2145                 dlg.setButton2(qt_("Examples|#E#e"),
2146                                 toqstr(addPath(package().system_support().absFileName(), "examples")));
2147
2148                 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2149                 FileDialog::Result result =
2150                         dlg.open(toqstr(initpath), filter);
2151
2152                 if (result.first == FileDialog::Later)
2153                         return;
2154
2155                 filename = fromqstr(result.second);
2156
2157                 // check selected filename
2158                 if (filename.empty()) {
2159                         message(_("Canceled."));
2160                         return;
2161                 }
2162         } else
2163                 filename = fname;
2164
2165         // get absolute path of file and add ".lyx" to the filename if
2166         // necessary.
2167         FileName const fullname =
2168                         fileSearch(string(), filename, "lyx", support::may_not_exist);
2169         if (!fullname.empty())
2170                 filename = fullname.absFileName();
2171
2172         if (!fullname.onlyPath().isDirectory()) {
2173                 Alert::warning(_("Invalid filename"),
2174                                 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2175                                 from_utf8(fullname.absFileName())));
2176                 return;
2177         }
2178
2179         // if the file doesn't exist and isn't already open (bug 6645),
2180         // let the user create one
2181         if (!fullname.exists() && !theBufferList().exists(fullname) &&
2182             !LyXVC::file_not_found_hook(fullname)) {
2183                 // the user specifically chose this name. Believe him.
2184                 Buffer * const b = newFile(filename, string(), true);
2185                 if (b)
2186                         setBuffer(b);
2187                 return;
2188         }
2189
2190         docstring const disp_fn = makeDisplayPath(filename);
2191         message(bformat(_("Opening document %1$s..."), disp_fn));
2192
2193         docstring str2;
2194         Buffer * buf = loadDocument(fullname);
2195         if (buf) {
2196                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2197                 if (buf->lyxvc().inUse())
2198                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2199                                 " " + _("Version control detected.");
2200         } else {
2201                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2202         }
2203         message(str2);
2204 }
2205
2206 // FIXME: clean that
2207 static bool import(GuiView * lv, FileName const & filename,
2208         string const & format, ErrorList & errorList)
2209 {
2210         FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2211
2212         string loader_format;
2213         vector<string> loaders = theConverters().loaders();
2214         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2215                 vector<string>::const_iterator it = loaders.begin();
2216                 vector<string>::const_iterator en = loaders.end();
2217                 for (; it != en; ++it) {
2218                         if (!theConverters().isReachable(format, *it))
2219                                 continue;
2220
2221                         string const tofile =
2222                                 support::changeExtension(filename.absFileName(),
2223                                 formats.extension(*it));
2224                         if (!theConverters().convert(0, filename, FileName(tofile),
2225                                 filename, format, *it, errorList))
2226                                 return false;
2227                         loader_format = *it;
2228                         break;
2229                 }
2230                 if (loader_format.empty()) {
2231                         frontend::Alert::error(_("Couldn't import file"),
2232                                          bformat(_("No information for importing the format %1$s."),
2233                                          formats.prettyName(format)));
2234                         return false;
2235                 }
2236         } else
2237                 loader_format = format;
2238
2239         if (loader_format == "lyx") {
2240                 Buffer * buf = lv->loadDocument(lyxfile);
2241                 if (!buf)
2242                         return false;
2243         } else {
2244                 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2245                 if (!b)
2246                         return false;
2247                 lv->setBuffer(b);
2248                 bool as_paragraphs = loader_format == "textparagraph";
2249                 string filename2 = (loader_format == format) ? filename.absFileName()
2250                         : support::changeExtension(filename.absFileName(),
2251                                           formats.extension(loader_format));
2252                 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2253                         as_paragraphs);
2254                 guiApp->setCurrentView(lv);
2255                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2256         }
2257
2258         return true;
2259 }
2260
2261
2262 void GuiView::importDocument(string const & argument)
2263 {
2264         string format;
2265         string filename = split(argument, format, ' ');
2266
2267         LYXERR(Debug::INFO, format << " file: " << filename);
2268
2269         // need user interaction
2270         if (filename.empty()) {
2271                 string initpath = lyxrc.document_path;
2272                 if (documentBufferView()) {
2273                         string const trypath = documentBufferView()->buffer().filePath();
2274                         // If directory is writeable, use this as default.
2275                         if (FileName(trypath).isDirWritable())
2276                                 initpath = trypath;
2277                 }
2278
2279                 docstring const text = bformat(_("Select %1$s file to import"),
2280                         formats.prettyName(format));
2281
2282                 FileDialog dlg(toqstr(text));
2283                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2284                 dlg.setButton2(qt_("Examples|#E#e"),
2285                         toqstr(addPath(package().system_support().absFileName(), "examples")));
2286
2287                 docstring filter = formats.prettyName(format);
2288                 filter += " (*.{";
2289                 // FIXME UNICODE
2290                 filter += from_utf8(formats.extensions(format));
2291                 filter += "})";
2292
2293                 FileDialog::Result result =
2294                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2295
2296                 if (result.first == FileDialog::Later)
2297                         return;
2298
2299                 filename = fromqstr(result.second);
2300
2301                 // check selected filename
2302                 if (filename.empty())
2303                         message(_("Canceled."));
2304         }
2305
2306         if (filename.empty())
2307                 return;
2308
2309         // get absolute path of file
2310         FileName const fullname(support::makeAbsPath(filename));
2311
2312         // Can happen if the user entered a path into the dialog
2313         // (see bug #7437)
2314         if (fullname.onlyFileName().empty()) {
2315                 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2316                                           "Aborting import."),
2317                                         from_utf8(fullname.absFileName()));
2318                 frontend::Alert::error(_("File name error"), msg);
2319                 message(_("Canceled."));
2320                 return;
2321         }
2322
2323
2324         FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2325
2326         // Check if the document already is open
2327         Buffer * buf = theBufferList().getBuffer(lyxfile);
2328         if (buf) {
2329                 setBuffer(buf);
2330                 if (!closeBuffer()) {
2331                         message(_("Canceled."));
2332                         return;
2333                 }
2334         }
2335
2336         docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2337
2338         // if the file exists already, and we didn't do
2339         // -i lyx thefile.lyx, warn
2340         if (lyxfile.exists() && fullname != lyxfile) {
2341
2342                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2343                         "Do you want to overwrite that document?"), displaypath);
2344                 int const ret = Alert::prompt(_("Overwrite document?"),
2345                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2346
2347                 if (ret == 1) {
2348                         message(_("Canceled."));
2349                         return;
2350                 }
2351         }
2352
2353         message(bformat(_("Importing %1$s..."), displaypath));
2354         ErrorList errorList;
2355         if (import(this, fullname, format, errorList))
2356                 message(_("imported."));
2357         else
2358                 message(_("file not imported!"));
2359
2360         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2361 }
2362
2363
2364 void GuiView::newDocument(string const & filename, bool from_template)
2365 {
2366         FileName initpath(lyxrc.document_path);
2367         if (documentBufferView()) {
2368                 FileName const trypath(documentBufferView()->buffer().filePath());
2369                 // If directory is writeable, use this as default.
2370                 if (trypath.isDirWritable())
2371                         initpath = trypath;
2372         }
2373
2374         string templatefile;
2375         if (from_template) {
2376                 templatefile = selectTemplateFile().absFileName();
2377                 if (templatefile.empty())
2378                         return;
2379         }
2380
2381         Buffer * b;
2382         if (filename.empty())
2383                 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2384         else
2385                 b = newFile(filename, templatefile, true);
2386
2387         if (b)
2388                 setBuffer(b);
2389
2390         // If no new document could be created, it is unsure
2391         // whether there is a valid BufferView.
2392         if (currentBufferView())
2393                 // Ensure the cursor is correctly positioned on screen.
2394                 currentBufferView()->showCursor();
2395 }
2396
2397
2398 void GuiView::insertLyXFile(docstring const & fname)
2399 {
2400         BufferView * bv = documentBufferView();
2401         if (!bv)
2402                 return;
2403
2404         // FIXME UNICODE
2405         FileName filename(to_utf8(fname));
2406         if (filename.empty()) {
2407                 // Launch a file browser
2408                 // FIXME UNICODE
2409                 string initpath = lyxrc.document_path;
2410                 string const trypath = bv->buffer().filePath();
2411                 // If directory is writeable, use this as default.
2412                 if (FileName(trypath).isDirWritable())
2413                         initpath = trypath;
2414
2415                 // FIXME UNICODE
2416                 FileDialog dlg(qt_("Select LyX document to insert"));
2417                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2418                 dlg.setButton2(qt_("Examples|#E#e"),
2419                         toqstr(addPath(package().system_support().absFileName(),
2420                         "examples")));
2421
2422                 FileDialog::Result result = dlg.open(toqstr(initpath),
2423                                          QStringList(qt_("LyX Documents (*.lyx)")));
2424
2425                 if (result.first == FileDialog::Later)
2426                         return;
2427
2428                 // FIXME UNICODE
2429                 filename.set(fromqstr(result.second));
2430
2431                 // check selected filename
2432                 if (filename.empty()) {
2433                         // emit message signal.
2434                         message(_("Canceled."));
2435                         return;
2436                 }
2437         }
2438
2439         bv->insertLyXFile(filename);
2440         bv->buffer().errors("Parse");
2441 }
2442
2443
2444 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2445 {
2446         FileName fname = b.fileName();
2447         FileName const oldname = fname;
2448
2449         if (!newname.empty()) {
2450                 // FIXME UNICODE
2451                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2452         } else {
2453                 // Switch to this Buffer.
2454                 setBuffer(&b);
2455
2456                 // No argument? Ask user through dialog.
2457                 // FIXME UNICODE
2458                 FileDialog dlg(qt_("Choose a filename to save document as"));
2459                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2460                 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2461
2462                 if (!isLyXFileName(fname.absFileName()))
2463                         fname.changeExtension(".lyx");
2464
2465                 FileDialog::Result result =
2466                         dlg.save(toqstr(fname.onlyPath().absFileName()),
2467                                    QStringList(qt_("LyX Documents (*.lyx)")),
2468                                          toqstr(fname.onlyFileName()));
2469
2470                 if (result.first == FileDialog::Later)
2471                         return false;
2472
2473                 fname.set(fromqstr(result.second));
2474
2475                 if (fname.empty())
2476                         return false;
2477
2478                 if (!isLyXFileName(fname.absFileName()))
2479                         fname.changeExtension(".lyx");
2480         }
2481
2482         // fname is now the new Buffer location.
2483
2484         // if there is already a Buffer open with this name, we do not want
2485         // to have another one. (the second test makes sure we're not just
2486         // trying to overwrite ourselves, which is fine.)
2487         if (theBufferList().exists(fname) && fname != oldname
2488                   && theBufferList().getBuffer(fname) != &b) {
2489                 docstring const text =
2490                         bformat(_("The file\n%1$s\nis already open in your current session.\n"
2491                             "Please close it before attempting to overwrite it.\n"
2492                             "Do you want to choose a new filename?"),
2493                                 from_utf8(fname.absFileName()));
2494                 int const ret = Alert::prompt(_("Chosen File Already Open"),
2495                         text, 0, 1, _("&Rename"), _("&Cancel"));
2496                 switch (ret) {
2497                 case 0: return renameBuffer(b, docstring(), kind);
2498                 case 1: return false;
2499                 }
2500                 //return false;
2501         }
2502
2503         bool const existsLocal = fname.exists();
2504         bool const existsInVC = LyXVC::fileInVC(fname);
2505         if (existsLocal || existsInVC) {
2506                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2507                 if (kind != LV_WRITE_AS && existsInVC) {
2508                         // renaming to a name that is already in VC
2509                         // would not work
2510                         docstring text = bformat(_("The document %1$s "
2511                                         "is already registered.\n\n"
2512                                         "Do you want to choose a new name?"),
2513                                 file);
2514                         docstring const title = (kind == LV_VC_RENAME) ?
2515                                 _("Rename document?") : _("Copy document?");
2516                         docstring const button = (kind == LV_VC_RENAME) ?
2517                                 _("&Rename") : _("&Copy");
2518                         int const ret = Alert::prompt(title, text, 0, 1,
2519                                 button, _("&Cancel"));
2520                         switch (ret) {
2521                         case 0: return renameBuffer(b, docstring(), kind);
2522                         case 1: return false;
2523                         }
2524                 }
2525
2526                 if (existsLocal) {
2527                         docstring text = bformat(_("The document %1$s "
2528                                         "already exists.\n\n"
2529                                         "Do you want to overwrite that document?"),
2530                                 file);
2531                         int const ret = Alert::prompt(_("Overwrite document?"),
2532                                         text, 0, 2, _("&Overwrite"),
2533                                         _("&Rename"), _("&Cancel"));
2534                         switch (ret) {
2535                         case 0: break;
2536                         case 1: return renameBuffer(b, docstring(), kind);
2537                         case 2: return false;
2538                         }
2539                 }
2540         }
2541
2542         switch (kind) {
2543         case LV_VC_RENAME: {
2544                 string msg = b.lyxvc().rename(fname);
2545                 if (msg.empty())
2546                         return false;
2547                 message(from_utf8(msg));
2548                 break;
2549         }
2550         case LV_VC_COPY: {
2551                 string msg = b.lyxvc().copy(fname);
2552                 if (msg.empty())
2553                         return false;
2554                 message(from_utf8(msg));
2555                 break;
2556         }
2557         case LV_WRITE_AS:
2558                 break;
2559         }
2560         // LyXVC created the file already in case of LV_VC_RENAME or
2561         // LV_VC_COPY, but call saveBuffer() nevertheless to get
2562         // relative paths of included stuff right if we moved e.g. from
2563         // /a/b.lyx to /a/c/b.lyx.
2564
2565         bool const saved = saveBuffer(b, fname);
2566         if (saved)
2567                 b.reload();
2568         return saved;
2569 }
2570
2571
2572 struct PrettyNameComparator
2573 {
2574         bool operator()(Format const *first, Format const *second) const {
2575                 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2576                                        translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2577         }
2578 };
2579
2580
2581 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2582 {
2583         FileName fname = b.fileName();
2584
2585         FileDialog dlg(qt_("Choose a filename to export the document as"));
2586         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2587
2588         QStringList types;
2589         QString const anyformat = qt_("Guess from extension (*.*)");
2590         types << anyformat;
2591         Formats::const_iterator it = formats.begin();
2592         vector<Format const *> export_formats;
2593         for (; it != formats.end(); ++it)
2594                 if (it->documentFormat())
2595                         export_formats.push_back(&(*it));
2596         PrettyNameComparator cmp;
2597         sort(export_formats.begin(), export_formats.end(), cmp);
2598         vector<Format const *>::const_iterator fit = export_formats.begin();
2599         map<QString, string> fmap;
2600         QString filter;
2601         string ext;
2602         for (; fit != export_formats.end(); ++fit) {
2603                 docstring const loc_prettyname =
2604                         translateIfPossible(from_utf8((*fit)->prettyname()));
2605                 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2606                                                      loc_prettyname,
2607                                                      from_ascii((*fit)->extension())));
2608                 types << loc_filter;
2609                 fmap[loc_filter] = (*fit)->name();
2610                 if (from_ascii((*fit)->name()) == iformat) {
2611                         filter = loc_filter;
2612                         ext = (*fit)->extension();
2613                 }
2614         }
2615         string ofname = fname.onlyFileName();
2616         if (!ext.empty())
2617                 ofname = support::changeExtension(ofname, ext);
2618         FileDialog::Result result =
2619                 dlg.save(toqstr(fname.onlyPath().absFileName()),
2620                          types,
2621                          toqstr(ofname),
2622                          &filter);
2623         if (result.first != FileDialog::Chosen)
2624                 return false;
2625
2626         string fmt_name;
2627         fname.set(fromqstr(result.second));
2628         if (filter == anyformat)
2629                 fmt_name = formats.getFormatFromExtension(fname.extension());
2630         else
2631                 fmt_name = fmap[filter];
2632         LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2633                << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2634
2635         if (fmt_name.empty() || fname.empty())
2636                 return false;
2637
2638         // fname is now the new Buffer location.
2639         if (FileName(fname).exists()) {
2640                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2641                 docstring text = bformat(_("The document %1$s already "
2642                                            "exists.\n\nDo you want to "
2643                                            "overwrite that document?"),
2644                                          file);
2645                 int const ret = Alert::prompt(_("Overwrite document?"),
2646                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2647                 switch (ret) {
2648                 case 0: break;
2649                 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2650                 case 2: return false;
2651                 }
2652         }
2653
2654         FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2655         DispatchResult dr;
2656         dispatch(cmd, dr);
2657         return dr.dispatched();
2658 }
2659
2660
2661 bool GuiView::saveBuffer(Buffer & b)
2662 {
2663         return saveBuffer(b, FileName());
2664 }
2665
2666
2667 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2668 {
2669         if (workArea(b) && workArea(b)->inDialogMode())
2670                 return true;
2671
2672         if (fn.empty() && b.isUnnamed())
2673                 return renameBuffer(b, docstring());
2674
2675         bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2676         if (success) {
2677                 theSession().lastFiles().add(b.fileName());
2678                 return true;
2679         }
2680
2681         // Switch to this Buffer.
2682         setBuffer(&b);
2683
2684         // FIXME: we don't tell the user *WHY* the save failed !!
2685         docstring const file = makeDisplayPath(b.absFileName(), 30);
2686         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2687                                    "Do you want to rename the document and "
2688                                    "try again?"), file);
2689         int const ret = Alert::prompt(_("Rename and save?"),
2690                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2691         switch (ret) {
2692         case 0:
2693                 if (!renameBuffer(b, docstring()))
2694                         return false;
2695                 break;
2696         case 1:
2697                 break;
2698         case 2:
2699                 return false;
2700         }
2701
2702         return saveBuffer(b, fn);
2703 }
2704
2705
2706 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2707 {
2708         return closeWorkArea(wa, false);
2709 }
2710
2711
2712 // We only want to close the buffer if it is not visible in other workareas
2713 // of the same view, nor in other views, and if this is not a child
2714 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2715 {
2716         Buffer & buf = wa->bufferView().buffer();
2717
2718         bool last_wa = d.countWorkAreasOf(buf) == 1
2719                 && !inOtherView(buf) && !buf.parent();
2720
2721         bool close_buffer = last_wa;
2722
2723         if (last_wa) {
2724                 if (lyxrc.close_buffer_with_last_view == "yes")
2725                         ; // Nothing to do
2726                 else if (lyxrc.close_buffer_with_last_view == "no")
2727                         close_buffer = false;
2728                 else {
2729                         docstring file;
2730                         if (buf.isUnnamed())
2731                                 file = from_utf8(buf.fileName().onlyFileName());
2732                         else
2733                                 file = buf.fileName().displayName(30);
2734                         docstring const text = bformat(
2735                                 _("Last view on document %1$s is being closed.\n"
2736                                   "Would you like to close or hide the document?\n"
2737                                   "\n"
2738                                   "Hidden documents can be displayed back through\n"
2739                                   "the menu: View->Hidden->...\n"
2740                                   "\n"
2741                                   "To remove this question, set your preference in:\n"
2742                                   "  Tools->Preferences->Look&Feel->UserInterface\n"
2743                                 ), file);
2744                         int ret = Alert::prompt(_("Close or hide document?"),
2745                                 text, 0, 1, _("&Close"), _("&Hide"));
2746                         close_buffer = (ret == 0);
2747                 }
2748         }
2749
2750         return closeWorkArea(wa, close_buffer);
2751 }
2752
2753
2754 bool GuiView::closeBuffer()
2755 {
2756         GuiWorkArea * wa = currentMainWorkArea();
2757         // coverity complained about this
2758         // it seems unnecessary, but perhaps is worth the check
2759         LASSERT(wa, return false);
2760
2761         setCurrentWorkArea(wa);
2762         Buffer & buf = wa->bufferView().buffer();
2763         return closeWorkArea(wa, !buf.parent());
2764 }
2765
2766
2767 void GuiView::writeSession() const {
2768         GuiWorkArea const * active_wa = currentMainWorkArea();
2769         for (int i = 0; i < d.splitter_->count(); ++i) {
2770                 TabWorkArea * twa = d.tabWorkArea(i);
2771                 for (int j = 0; j < twa->count(); ++j) {
2772                         GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2773                         Buffer & buf = wa->bufferView().buffer();
2774                         theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2775                 }
2776         }
2777 }
2778
2779
2780 bool GuiView::closeBufferAll()
2781 {
2782         // Close the workareas in all other views
2783         QList<int> const ids = guiApp->viewIds();
2784         for (int i = 0; i != ids.size(); ++i) {
2785                 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2786                         return false;
2787         }
2788
2789         // Close our own workareas
2790         if (!closeWorkAreaAll())
2791                 return false;
2792
2793         // Now close the hidden buffers. We prevent hidden buffers from being
2794         // dirty, so we can just close them.
2795         theBufferList().closeAll();
2796         return true;
2797 }
2798
2799
2800 bool GuiView::closeWorkAreaAll()
2801 {
2802         setCurrentWorkArea(currentMainWorkArea());
2803
2804         // We might be in a situation that there is still a tabWorkArea, but
2805         // there are no tabs anymore. This can happen when we get here after a
2806         // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2807         // many TabWorkArea's have no documents anymore.
2808         int empty_twa = 0;
2809
2810         // We have to call count() each time, because it can happen that
2811         // more than one splitter will disappear in one iteration (bug 5998).
2812         while (d.splitter_->count() > empty_twa) {
2813                 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2814
2815                 if (twa->count() == 0)
2816                         ++empty_twa;
2817                 else {
2818                         setCurrentWorkArea(twa->currentWorkArea());
2819                         if (!closeTabWorkArea(twa))
2820                                 return false;
2821                 }
2822         }
2823         return true;
2824 }
2825
2826
2827 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2828 {
2829         if (!wa)
2830                 return false;
2831
2832         Buffer & buf = wa->bufferView().buffer();
2833
2834         if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2835                 Alert::warning(_("Close document"), 
2836                         _("Document could not be closed because it is being processed by LyX."));
2837                 return false;
2838         }
2839
2840         if (close_buffer)
2841                 return closeBuffer(buf);
2842         else {
2843                 if (!inMultiTabs(wa))
2844                         if (!saveBufferIfNeeded(buf, true))
2845                                 return false;
2846                 removeWorkArea(wa);
2847                 return true;
2848         }
2849 }
2850
2851
2852 bool GuiView::closeBuffer(Buffer & buf)
2853 {
2854         // If we are in a close_event all children will be closed in some time,
2855         // so no need to do it here. This will ensure that the children end up
2856         // in the session file in the correct order. If we close the master
2857         // buffer, we can close or release the child buffers here too.
2858         bool success = true;
2859         if (!closing_) {
2860                 ListOfBuffers clist = buf.getChildren();
2861                 ListOfBuffers::const_iterator it = clist.begin();
2862                 ListOfBuffers::const_iterator const bend = clist.end();
2863                 for (; it != bend; ++it) {
2864                         Buffer * child_buf = *it;
2865                         if (theBufferList().isOthersChild(&buf, child_buf)) {
2866                                 child_buf->setParent(0);
2867                                 continue;
2868                         }
2869
2870                         // FIXME: should we look in other tabworkareas?
2871                         // ANSWER: I don't think so. I've tested, and if the child is
2872                         // open in some other window, it closes without a problem.
2873                         GuiWorkArea * child_wa = workArea(*child_buf);
2874                         if (child_wa) {
2875                                 success = closeWorkArea(child_wa, true);
2876                                 if (!success)
2877                                         break;
2878                         } else {
2879                                 // In this case the child buffer is open but hidden.
2880                                 // It therefore should not (MUST NOT) be dirty!
2881                                 LATTEST(child_buf->isClean());
2882                                 theBufferList().release(child_buf);
2883                         }
2884                 }
2885         }
2886         if (success) {
2887                 // goto bookmark to update bookmark pit.
2888                 // FIXME: we should update only the bookmarks related to this buffer!
2889                 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2890                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2891                         guiApp->gotoBookmark(i+1, false, false);
2892
2893                 if (saveBufferIfNeeded(buf, false)) {
2894                         buf.removeAutosaveFile();
2895                         theBufferList().release(&buf);
2896                         return true;
2897                 }
2898         }
2899         // open all children again to avoid a crash because of dangling
2900         // pointers (bug 6603)
2901         buf.updateBuffer();
2902         return false;
2903 }
2904
2905
2906 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2907 {
2908         while (twa == d.currentTabWorkArea()) {
2909                 twa->setCurrentIndex(twa->count() - 1);
2910
2911                 GuiWorkArea * wa = twa->currentWorkArea();
2912                 Buffer & b = wa->bufferView().buffer();
2913
2914                 // We only want to close the buffer if the same buffer is not visible
2915                 // in another view, and if this is not a child and if we are closing
2916                 // a view (not a tabgroup).
2917                 bool const close_buffer =
2918                         !inOtherView(b) && !b.parent() && closing_;
2919
2920                 if (!closeWorkArea(wa, close_buffer))
2921                         return false;
2922         }
2923         return true;
2924 }
2925
2926
2927 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2928 {
2929         if (buf.isClean() || buf.paragraphs().empty())
2930                 return true;
2931
2932         // Switch to this Buffer.
2933         setBuffer(&buf);
2934
2935         docstring file;
2936         // FIXME: Unicode?
2937         if (buf.isUnnamed())
2938                 file = from_utf8(buf.fileName().onlyFileName());
2939         else
2940                 file = buf.fileName().displayName(30);
2941
2942         // Bring this window to top before asking questions.
2943         raise();
2944         activateWindow();
2945
2946         int ret;
2947         if (hiding && buf.isUnnamed()) {
2948                 docstring const text = bformat(_("The document %1$s has not been "
2949                                                  "saved yet.\n\nDo you want to save "
2950                                                  "the document?"), file);
2951                 ret = Alert::prompt(_("Save new document?"),
2952                         text, 0, 1, _("&Save"), _("&Cancel"));
2953                 if (ret == 1)
2954                         ++ret;
2955         } else {
2956                 docstring const text = bformat(_("The document %1$s has unsaved changes."
2957                         "\n\nDo you want to save the document or discard the changes?"), file);
2958                 ret = Alert::prompt(_("Save changed document?"),
2959                         text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2960         }
2961
2962         switch (ret) {
2963         case 0:
2964                 if (!saveBuffer(buf))
2965                         return false;
2966                 break;
2967         case 1:
2968                 // If we crash after this we could have no autosave file
2969                 // but I guess this is really improbable (Jug).
2970                 // Sometimes improbable things happen:
2971                 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2972                 // buf.removeAutosaveFile();
2973                 if (hiding)
2974                         // revert all changes
2975                         reloadBuffer(buf);
2976                 buf.markClean();
2977                 break;
2978         case 2:
2979                 return false;
2980         }
2981         return true;
2982 }
2983
2984
2985 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2986 {
2987         Buffer & buf = wa->bufferView().buffer();
2988
2989         for (int i = 0; i != d.splitter_->count(); ++i) {
2990                 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2991                 if (wa_ && wa_ != wa)
2992                         return true;
2993         }
2994         return inOtherView(buf);
2995 }
2996
2997
2998 bool GuiView::inOtherView(Buffer & buf)
2999 {
3000         QList<int> const ids = guiApp->viewIds();
3001
3002         for (int i = 0; i != ids.size(); ++i) {
3003                 if (id_ == ids[i])
3004                         continue;
3005
3006                 if (guiApp->view(ids[i]).workArea(buf))
3007                         return true;
3008         }
3009         return false;
3010 }
3011
3012
3013 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3014 {
3015         if (!documentBufferView())
3016                 return;
3017         
3018         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3019                 Buffer * const curbuf = &documentBufferView()->buffer();
3020                 int nwa = twa->count();
3021                 for (int i = 0; i < nwa; ++i) {
3022                         if (&workArea(i)->bufferView().buffer() == curbuf) {
3023                                 int next_index;
3024                                 if (np == NEXTBUFFER)
3025                                         next_index = (i == nwa - 1 ? 0 : i + 1);
3026                                 else
3027                                         next_index = (i == 0 ? nwa - 1 : i - 1);
3028                                 if (move)
3029                                         twa->moveTab(i, next_index);
3030                                 else
3031                                         setBuffer(&workArea(next_index)->bufferView().buffer());
3032                                 break;
3033                         }
3034                 }
3035         }
3036 }
3037
3038
3039 /// make sure the document is saved
3040 static bool ensureBufferClean(Buffer * buffer)
3041 {
3042         LASSERT(buffer, return false);
3043         if (buffer->isClean() && !buffer->isUnnamed())
3044                 return true;
3045
3046         docstring const file = buffer->fileName().displayName(30);
3047         docstring title;
3048         docstring text;
3049         if (!buffer->isUnnamed()) {
3050                 text = bformat(_("The document %1$s has unsaved "
3051                                                  "changes.\n\nDo you want to save "
3052                                                  "the document?"), file);
3053                 title = _("Save changed document?");
3054
3055         } else {
3056                 text = bformat(_("The document %1$s has not been "
3057                                                  "saved yet.\n\nDo you want to save "
3058                                                  "the document?"), file);
3059                 title = _("Save new document?");
3060         }
3061         int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3062
3063         if (ret == 0)
3064                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3065
3066         return buffer->isClean() && !buffer->isUnnamed();
3067 }
3068
3069
3070 bool GuiView::reloadBuffer(Buffer & buf)
3071 {
3072         Buffer::ReadStatus status = buf.reload();
3073         return status == Buffer::ReadSuccess;
3074 }
3075
3076
3077 void GuiView::checkExternallyModifiedBuffers()
3078 {
3079         BufferList::iterator bit = theBufferList().begin();
3080         BufferList::iterator const bend = theBufferList().end();
3081         for (; bit != bend; ++bit) {
3082                 Buffer * buf = *bit;
3083                 if (buf->fileName().exists()
3084                         && buf->isExternallyModified(Buffer::checksum_method)) {
3085                         docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3086                                         " Reload now? Any local changes will be lost."),
3087                                         from_utf8(buf->absFileName()));
3088                         int const ret = Alert::prompt(_("Reload externally changed document?"),
3089                                                 text, 0, 1, _("&Reload"), _("&Cancel"));
3090                         if (!ret)
3091                                 reloadBuffer(*buf);
3092                 }
3093         }
3094 }
3095
3096
3097 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3098 {
3099         Buffer * buffer = documentBufferView()
3100                 ? &(documentBufferView()->buffer()) : 0;
3101
3102         switch (cmd.action()) {
3103         case LFUN_VC_REGISTER:
3104                 if (!buffer || !ensureBufferClean(buffer))
3105                         break;
3106                 if (!buffer->lyxvc().inUse()) {
3107                         if (buffer->lyxvc().registrer()) {
3108                                 reloadBuffer(*buffer);
3109                                 dr.clearMessageUpdate();
3110                         }
3111                 }
3112                 break;
3113
3114         case LFUN_VC_RENAME:
3115         case LFUN_VC_COPY: {
3116                 if (!buffer || !ensureBufferClean(buffer))
3117                         break;
3118                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3119                         if (buffer->lyxvc().isCheckInWithConfirmation()) {
3120                                 // Some changes are not yet committed.
3121                                 // We test here and not in getStatus(), since
3122                                 // this test is expensive.
3123                                 string log;
3124                                 LyXVC::CommandResult ret =
3125                                         buffer->lyxvc().checkIn(log);
3126                                 dr.setMessage(log);
3127                                 if (ret == LyXVC::ErrorCommand ||
3128                                     ret == LyXVC::VCSuccess)
3129                                         reloadBuffer(*buffer);
3130                                 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3131                                         frontend::Alert::error(
3132                                                 _("Revision control error."),
3133                                                 _("Document could not be checked in."));
3134                                         break;
3135                                 }
3136                         }
3137                         RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3138                                 LV_VC_RENAME : LV_VC_COPY;
3139                         renameBuffer(*buffer, cmd.argument(), kind);
3140                 }
3141                 break;
3142         }
3143
3144         case LFUN_VC_CHECK_IN:
3145                 if (!buffer || !ensureBufferClean(buffer))
3146                         break;
3147                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3148                         string log;
3149                         LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3150                         dr.setMessage(log);
3151                         // Only skip reloading if the checkin was cancelled or
3152                         // an error occurred before the real checkin VCS command
3153                         // was executed, since the VCS might have changed the
3154                         // file even if it could not checkin successfully.
3155                         if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3156                                 reloadBuffer(*buffer);
3157                 }
3158                 break;
3159
3160         case LFUN_VC_CHECK_OUT:
3161                 if (!buffer || !ensureBufferClean(buffer))
3162                         break;
3163                 if (buffer->lyxvc().inUse()) {
3164                         dr.setMessage(buffer->lyxvc().checkOut());
3165                         reloadBuffer(*buffer);
3166                 }
3167                 break;
3168
3169         case LFUN_VC_LOCKING_TOGGLE:
3170                 LASSERT(buffer, return);
3171                 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3172                         break;
3173                 if (buffer->lyxvc().inUse()) {
3174                         string res = buffer->lyxvc().lockingToggle();
3175                         if (res.empty()) {
3176                                 frontend::Alert::error(_("Revision control error."),
3177                                 _("Error when setting the locking property."));
3178                         } else {
3179                                 dr.setMessage(res);
3180                                 reloadBuffer(*buffer);
3181                         }
3182                 }
3183                 break;
3184
3185         case LFUN_VC_REVERT:
3186                 LASSERT(buffer, return);
3187                 if (buffer->lyxvc().revert()) {
3188                         reloadBuffer(*buffer);
3189                         dr.clearMessageUpdate();
3190                 }
3191                 break;
3192
3193         case LFUN_VC_UNDO_LAST:
3194                 LASSERT(buffer, return);
3195                 buffer->lyxvc().undoLast();
3196                 reloadBuffer(*buffer);
3197                 dr.clearMessageUpdate();
3198                 break;
3199
3200         case LFUN_VC_REPO_UPDATE:
3201                 LASSERT(buffer, return);
3202                 if (ensureBufferClean(buffer)) {
3203                         dr.setMessage(buffer->lyxvc().repoUpdate());
3204                         checkExternallyModifiedBuffers();
3205                 }
3206                 break;
3207
3208         case LFUN_VC_COMMAND: {
3209                 string flag = cmd.getArg(0);
3210                 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3211                         break;
3212                 docstring message;
3213                 if (contains(flag, 'M')) {
3214                         if (!Alert::askForText(message, _("LyX VC: Log Message")))
3215                                 break;
3216                 }
3217                 string path = cmd.getArg(1);
3218                 if (contains(path, "$$p") && buffer)
3219                         path = subst(path, "$$p", buffer->filePath());
3220                 LYXERR(Debug::LYXVC, "Directory: " << path);
3221                 FileName pp(path);
3222                 if (!pp.isReadableDirectory()) {
3223                         lyxerr << _("Directory is not accessible.") << endl;
3224                         break;
3225                 }
3226                 support::PathChanger p(pp);
3227
3228                 string command = cmd.getArg(2);
3229                 if (command.empty())
3230                         break;
3231                 if (buffer) {
3232                         command = subst(command, "$$i", buffer->absFileName());
3233                         command = subst(command, "$$p", buffer->filePath());
3234                 }
3235                 command = subst(command, "$$m", to_utf8(message));
3236                 LYXERR(Debug::LYXVC, "Command: " << command);
3237                 Systemcall one;
3238                 one.startscript(Systemcall::Wait, command);
3239
3240                 if (!buffer)
3241                         break;
3242                 if (contains(flag, 'I'))
3243                         buffer->markDirty();
3244                 if (contains(flag, 'R'))
3245                         reloadBuffer(*buffer);
3246
3247                 break;
3248                 }
3249
3250         case LFUN_VC_COMPARE: {
3251                 if (cmd.argument().empty()) {
3252                         lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3253                         break;
3254                 }
3255
3256                 string rev1 = cmd.getArg(0);
3257                 string f1, f2;
3258
3259                 // f1
3260                 // it seems safe to assume we have a buffer
3261                 // coverity[FORWARD_NULL]
3262                 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3263                         break;
3264
3265                 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3266                         f2 = buffer->absFileName();
3267                 } else {
3268                         string rev2 = cmd.getArg(1);
3269                         if (rev2.empty())
3270                                 break;
3271                         // f2
3272                         if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3273                                 break;
3274                 }
3275
3276                 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3277                                         f1 << "\n"  << f2 << "\n" );
3278                 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3279                 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3280                 break;
3281         }
3282
3283         default:
3284                 break;
3285         }
3286 }
3287
3288
3289 void GuiView::openChildDocument(string const & fname)
3290 {
3291         LASSERT(documentBufferView(), return);
3292         Buffer & buffer = documentBufferView()->buffer();
3293         FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3294         documentBufferView()->saveBookmark(false);
3295         Buffer * child = 0;
3296         if (theBufferList().exists(filename)) {
3297                 child = theBufferList().getBuffer(filename);
3298                 setBuffer(child);
3299         } else {
3300                 message(bformat(_("Opening child document %1$s..."),
3301                         makeDisplayPath(filename.absFileName())));
3302                 child = loadDocument(filename, false);
3303         }
3304         // Set the parent name of the child document.
3305         // This makes insertion of citations and references in the child work,
3306         // when the target is in the parent or another child document.
3307         if (child)
3308                 child->setParent(&buffer);
3309 }
3310
3311
3312 bool GuiView::goToFileRow(string const & argument)
3313 {
3314         string file_name;
3315         int row;
3316         size_t i = argument.find_last_of(' ');
3317         if (i != string::npos) {
3318                 file_name = os::internal_path(trim(argument.substr(0, i)));
3319                 istringstream is(argument.substr(i + 1));
3320                 is >> row;
3321                 if (is.fail())
3322                         i = string::npos;
3323         }
3324         if (i == string::npos) {
3325                 LYXERR0("Wrong argument: " << argument);
3326                 return false;
3327         }
3328         Buffer * buf = 0;
3329         string const abstmp = package().temp_dir().absFileName();
3330         string const realtmp = package().temp_dir().realPath();
3331         // We have to use os::path_prefix_is() here, instead of
3332         // simply prefixIs(), because the file name comes from
3333         // an external application and may need case adjustment.
3334         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3335                 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3336                 // Needed by inverse dvi search. If it is a file
3337                 // in tmpdir, call the apropriated function.
3338                 // If tmpdir is a symlink, we may have the real
3339                 // path passed back, so we correct for that.
3340                 if (!prefixIs(file_name, abstmp))
3341                         file_name = subst(file_name, realtmp, abstmp);
3342                 buf = theBufferList().getBufferFromTmp(file_name);
3343         } else {
3344                 // Must replace extension of the file to be .lyx
3345                 // and get full path
3346                 FileName const s = fileSearch(string(),
3347                                                   support::changeExtension(file_name, ".lyx"), "lyx");
3348                 // Either change buffer or load the file
3349                 if (theBufferList().exists(s))
3350                         buf = theBufferList().getBuffer(s);
3351                 else if (s.exists()) {
3352                         buf = loadDocument(s);
3353                         if (!buf)
3354                                 return false;
3355                 } else {
3356                         message(bformat(
3357                                         _("File does not exist: %1$s"),
3358                                         makeDisplayPath(file_name)));
3359                         return false;
3360                 }
3361         }
3362         if (!buf) {
3363                 message(bformat(
3364                         _("No buffer for file: %1$s."),
3365                         makeDisplayPath(file_name))
3366                 );
3367                 return false;
3368         }
3369         setBuffer(buf);
3370         documentBufferView()->setCursorFromRow(row);
3371         return true;
3372 }
3373
3374
3375 template<class T>
3376 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3377 {
3378         Buffer::ExportStatus const status = func(format);
3379
3380         // the cloning operation will have produced a clone of the entire set of
3381         // documents, starting from the master. so we must delete those.
3382         Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3383         delete mbuf;
3384         busyBuffers.remove(orig);
3385         return status;
3386 }
3387
3388
3389 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3390 {
3391         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3392         return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3393 }
3394
3395
3396 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3397 {
3398         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3399         return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3400 }
3401
3402
3403 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3404 {
3405         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3406         return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3407 }
3408
3409
3410 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3411                            string const & argument,
3412                            Buffer const * used_buffer,
3413                            docstring const & msg,
3414                            Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3415                            Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3416                            Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3417 {
3418         if (!used_buffer)
3419                 return false;
3420
3421         string format = argument;
3422         if (format.empty())
3423                 format = used_buffer->params().getDefaultOutputFormat();
3424         processing_format = format;
3425         if (!msg.empty()) {
3426                 progress_->clearMessages();
3427                 gv_->message(msg);
3428         }
3429 #if EXPORT_in_THREAD
3430         GuiViewPrivate::busyBuffers.insert(used_buffer);
3431         Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3432         if (!cloned_buffer) {
3433                 Alert::error(_("Export Error"),
3434                              _("Error cloning the Buffer."));
3435                 return false;
3436         }
3437         QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3438                                 asyncFunc,
3439                                 used_buffer,
3440                                 cloned_buffer,
3441                                 format);
3442         setPreviewFuture(f);
3443         last_export_format = used_buffer->params().bufferFormat();
3444         (void) syncFunc;
3445         (void) previewFunc;
3446         // We are asynchronous, so we don't know here anything about the success
3447         return true;
3448 #else
3449         Buffer::ExportStatus status;
3450         if (syncFunc) {
3451                 status = (used_buffer->*syncFunc)(format, true);
3452         } else if (previewFunc) {
3453                 status = (used_buffer->*previewFunc)(format); 
3454         } else
3455                 return false;
3456         handleExportStatus(gv_, status, format);
3457         (void) asyncFunc;
3458         return (status == Buffer::ExportSuccess 
3459                         || status == Buffer::PreviewSuccess);
3460 #endif
3461 }
3462
3463 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3464 {
3465         BufferView * bv = currentBufferView();
3466         LASSERT(bv, return);
3467
3468         // Let the current BufferView dispatch its own actions.
3469         bv->dispatch(cmd, dr);
3470         if (dr.dispatched())
3471                 return;
3472
3473         // Try with the document BufferView dispatch if any.
3474         BufferView * doc_bv = documentBufferView();
3475         if (doc_bv && doc_bv != bv) {
3476                 doc_bv->dispatch(cmd, dr);
3477                 if (dr.dispatched())
3478                         return;
3479         }
3480
3481         // Then let the current Cursor dispatch its own actions.
3482         bv->cursor().dispatch(cmd);
3483
3484         // update completion. We do it here and not in
3485         // processKeySym to avoid another redraw just for a
3486         // changed inline completion
3487         if (cmd.origin() == FuncRequest::KEYBOARD) {
3488                 if (cmd.action() == LFUN_SELF_INSERT
3489                         || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3490                         updateCompletion(bv->cursor(), true, true);
3491                 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3492                         updateCompletion(bv->cursor(), false, true);
3493                 else
3494                         updateCompletion(bv->cursor(), false, false);
3495         }
3496
3497         dr = bv->cursor().result();
3498 }
3499
3500
3501 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3502 {
3503         BufferView * bv = currentBufferView();
3504         // By default we won't need any update.
3505         dr.screenUpdate(Update::None);
3506         // assume cmd will be dispatched
3507         dr.dispatched(true);
3508
3509         Buffer * doc_buffer = documentBufferView()
3510                 ? &(documentBufferView()->buffer()) : 0;
3511
3512         if (cmd.origin() == FuncRequest::TOC) {
3513                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3514                 // FIXME: do we need to pass a DispatchResult object here?
3515                 toc->doDispatch(bv->cursor(), cmd);
3516                 return;
3517         }
3518
3519         string const argument = to_utf8(cmd.argument());
3520
3521         switch(cmd.action()) {
3522                 case LFUN_BUFFER_CHILD_OPEN:
3523                         openChildDocument(to_utf8(cmd.argument()));
3524                         break;
3525
3526                 case LFUN_BUFFER_IMPORT:
3527                         importDocument(to_utf8(cmd.argument()));
3528                         break;
3529
3530                 case LFUN_BUFFER_EXPORT: {
3531                         if (!doc_buffer)
3532                                 break;
3533                         // GCC only sees strfwd.h when building merged
3534                         if (::lyx::operator==(cmd.argument(), "custom")) {
3535                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3536                                 break;
3537                         }
3538
3539                         string const dest = cmd.getArg(1);
3540                         FileName target_dir;
3541                         if (!dest.empty() && FileName::isAbsolute(dest))
3542                                 target_dir = FileName(support::onlyPath(dest));
3543                         else
3544                                 target_dir = doc_buffer->fileName().onlyPath();
3545
3546                         if ((dest.empty() && doc_buffer->isUnnamed())
3547                             || !target_dir.isDirWritable()) {
3548                                 exportBufferAs(*doc_buffer, cmd.argument());
3549                                 break;
3550                         }
3551                         /* TODO/Review: Is it a problem to also export the children?
3552                                         See the update_unincluded flag */
3553                         d.asyncBufferProcessing(argument,
3554                                                 doc_buffer,
3555                                                 _("Exporting ..."),
3556                                                 &GuiViewPrivate::exportAndDestroy,
3557                                                 &Buffer::doExport,
3558                                                 0);
3559                         // TODO Inform user about success
3560                         break;
3561                 }
3562
3563                 case LFUN_BUFFER_EXPORT_AS: {
3564                         LASSERT(doc_buffer, break);
3565                         docstring f = cmd.argument();
3566                         if (f.empty())
3567                                 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3568                         exportBufferAs(*doc_buffer, f);
3569                         break;
3570                 }
3571
3572                 case LFUN_BUFFER_UPDATE: {
3573                         d.asyncBufferProcessing(argument,
3574                                                 doc_buffer,
3575                                                 _("Exporting ..."),
3576                                                 &GuiViewPrivate::compileAndDestroy,
3577                                                 &Buffer::doExport,
3578                                                 0);
3579                         break;
3580                 }
3581                 case LFUN_BUFFER_VIEW: {
3582                         d.asyncBufferProcessing(argument,
3583                                                 doc_buffer,
3584                                                 _("Previewing ..."),
3585                                                 &GuiViewPrivate::previewAndDestroy,
3586                                                 0,
3587                                                 &Buffer::preview);
3588                         break;
3589                 }
3590                 case LFUN_MASTER_BUFFER_UPDATE: {
3591                         d.asyncBufferProcessing(argument,
3592                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3593                                                 docstring(),
3594                                                 &GuiViewPrivate::compileAndDestroy,
3595                                                 &Buffer::doExport,
3596                                                 0);
3597                         break;
3598                 }
3599                 case LFUN_MASTER_BUFFER_VIEW: {
3600                         d.asyncBufferProcessing(argument,
3601                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3602                                                 docstring(),
3603                                                 &GuiViewPrivate::previewAndDestroy,
3604                                                 0, &Buffer::preview);
3605                         break;
3606                 }
3607                 case LFUN_BUFFER_SWITCH: {
3608                         string const file_name = to_utf8(cmd.argument());
3609                         if (!FileName::isAbsolute(file_name)) {
3610                                 dr.setError(true);
3611                                 dr.setMessage(_("Absolute filename expected."));
3612                                 break;
3613                         }
3614
3615                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3616                         if (!buffer) {
3617                                 dr.setError(true);
3618                                 dr.setMessage(_("Document not loaded"));
3619                                 break;
3620                         }
3621
3622                         // Do we open or switch to the buffer in this view ?
3623                         if (workArea(*buffer)
3624                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3625                                 setBuffer(buffer);
3626                                 break;
3627                         }
3628
3629                         // Look for the buffer in other views
3630                         QList<int> const ids = guiApp->viewIds();
3631                         int i = 0;
3632                         for (; i != ids.size(); ++i) {
3633                                 GuiView & gv = guiApp->view(ids[i]);
3634                                 if (gv.workArea(*buffer)) {
3635                                         gv.raise();
3636                                         gv.activateWindow();
3637                                         gv.setFocus();
3638                                         gv.setBuffer(buffer);
3639                                         break;
3640                                 }
3641                         }
3642
3643                         // If necessary, open a new window as a last resort
3644                         if (i == ids.size()) {
3645                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3646                                 lyx::dispatch(cmd);
3647                         }
3648                         break;
3649                 }
3650
3651                 case LFUN_BUFFER_NEXT:
3652                         gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3653                         break;
3654
3655                 case LFUN_BUFFER_MOVE_NEXT:
3656                         gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3657                         break;
3658
3659                 case LFUN_BUFFER_PREVIOUS:
3660                         gotoNextOrPreviousBuffer(PREVBUFFER, false);
3661                         break;
3662
3663                 case LFUN_BUFFER_MOVE_PREVIOUS:
3664                         gotoNextOrPreviousBuffer(PREVBUFFER, true);
3665                         break;
3666
3667                 case LFUN_COMMAND_EXECUTE: {
3668                         command_execute_ = true;
3669                         break;
3670                 }
3671                 case LFUN_DROP_LAYOUTS_CHOICE:
3672                         d.layout_->showPopup();
3673                         break;
3674
3675                 case LFUN_MENU_OPEN:
3676                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3677                                 menu->exec(QCursor::pos());
3678                         break;
3679
3680                 case LFUN_FILE_INSERT:
3681                         insertLyXFile(cmd.argument());
3682                         break;
3683
3684                 case LFUN_FILE_INSERT_PLAINTEXT:
3685                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3686                         string const fname = to_utf8(cmd.argument());
3687                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3688                                 dr.setMessage(_("Absolute filename expected."));
3689                                 break;
3690                         }
3691                         
3692                         FileName filename(fname);
3693                         if (fname.empty()) {
3694                                 FileDialog dlg(qt_("Select file to insert"));
3695
3696                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3697                                         QStringList(qt_("All Files (*)")));
3698                                 
3699                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3700                                         dr.setMessage(_("Canceled."));
3701                                         break;
3702                                 }
3703
3704                                 filename.set(fromqstr(result.second));
3705                         }
3706
3707                         if (bv) {
3708                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3709                                 bv->dispatch(new_cmd, dr);
3710                         }
3711                         break;
3712                 }
3713
3714                 case LFUN_BUFFER_RELOAD: {
3715                         LASSERT(doc_buffer, break);
3716
3717                         int ret = 0;
3718                         if (!doc_buffer->isClean()) {
3719                                 docstring const file =
3720                                         makeDisplayPath(doc_buffer->absFileName(), 20);
3721                                 docstring text = bformat(_("Any changes will be lost. "
3722                                         "Are you sure you want to revert to the saved version "
3723                                         "of the document %1$s?"), file);
3724                                 ret = Alert::prompt(_("Revert to saved document?"),
3725                                         text, 1, 1, _("&Revert"), _("&Cancel"));
3726                         }
3727
3728                         if (ret == 0) {
3729                                 doc_buffer->markClean();
3730                                 reloadBuffer(*doc_buffer);
3731                                 dr.forceBufferUpdate();
3732                         }
3733                         break;
3734                 }
3735
3736                 case LFUN_BUFFER_WRITE:
3737                         LASSERT(doc_buffer, break);
3738                         saveBuffer(*doc_buffer);
3739                         break;
3740
3741                 case LFUN_BUFFER_WRITE_AS:
3742                         LASSERT(doc_buffer, break);
3743                         renameBuffer(*doc_buffer, cmd.argument());
3744                         break;
3745
3746                 case LFUN_BUFFER_WRITE_ALL: {
3747                         Buffer * first = theBufferList().first();
3748                         if (!first)
3749                                 break;
3750                         message(_("Saving all documents..."));
3751                         // We cannot use a for loop as the buffer list cycles.
3752                         Buffer * b = first;
3753                         do {
3754                                 if (!b->isClean()) {
3755                                         saveBuffer(*b);
3756                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3757                                 }
3758                                 b = theBufferList().next(b);
3759                         } while (b != first);
3760                         dr.setMessage(_("All documents saved."));
3761                         break;
3762                 }
3763
3764                 case LFUN_BUFFER_CLOSE:
3765                         closeBuffer();
3766                         break;
3767
3768                 case LFUN_BUFFER_CLOSE_ALL:
3769                         closeBufferAll();
3770                         break;
3771
3772                 case LFUN_TOOLBAR_TOGGLE: {
3773                         string const name = cmd.getArg(0);
3774                         if (GuiToolbar * t = toolbar(name))
3775                                 t->toggle();
3776                         break;
3777                 }
3778
3779                 case LFUN_DIALOG_UPDATE: {
3780                         string const name = to_utf8(cmd.argument());
3781                         if (name == "prefs" || name == "document")
3782                                 updateDialog(name, string());
3783                         else if (name == "paragraph")
3784                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3785                         else if (currentBufferView()) {
3786                                 Inset * inset = currentBufferView()->editedInset(name);
3787                                 // Can only update a dialog connected to an existing inset
3788                                 if (inset) {
3789                                         // FIXME: get rid of this indirection; GuiView ask the inset
3790                                         // if he is kind enough to update itself...
3791                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3792                                         //FIXME: pass DispatchResult here?
3793                                         inset->dispatch(currentBufferView()->cursor(), fr);
3794                                 }
3795                         }
3796                         break;
3797                 }
3798
3799                 case LFUN_DIALOG_TOGGLE: {
3800                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3801                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3802                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
3803                         break;
3804                 }
3805
3806                 case LFUN_DIALOG_DISCONNECT_INSET:
3807                         disconnectDialog(to_utf8(cmd.argument()));
3808                         break;
3809
3810                 case LFUN_DIALOG_HIDE: {
3811                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3812                         break;
3813                 }
3814
3815                 case LFUN_DIALOG_SHOW: {
3816                         string const name = cmd.getArg(0);
3817                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3818
3819                         if (name == "character") {
3820                                 data = freefont2string();
3821                                 if (!data.empty())
3822                                         showDialog("character", data);
3823                         } else if (name == "latexlog") {
3824                                 Buffer::LogType type;
3825                                 string const logfile = doc_buffer->logName(&type);
3826                                 switch (type) {
3827                                 case Buffer::latexlog:
3828                                         data = "latex ";
3829                                         break;
3830                                 case Buffer::buildlog:
3831                                         data = "literate ";
3832                                         break;
3833                                 }
3834                                 data += Lexer::quoteString(logfile);
3835                                 showDialog("log", data);
3836                         } else if (name == "vclog") {
3837                                 string const data = "vc " +
3838                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3839                                 showDialog("log", data);
3840                         } else if (name == "symbols") {
3841                                 data = bv->cursor().getEncoding()->name();
3842                                 if (!data.empty())
3843                                         showDialog("symbols", data);
3844                         // bug 5274
3845                         } else if (name == "prefs" && isFullScreen()) {
3846                                 lfunUiToggle("fullscreen");
3847                                 showDialog("prefs", data);
3848                         } else
3849                                 showDialog(name, data);
3850                         break;
3851                 }
3852
3853                 case LFUN_MESSAGE:
3854                         dr.setMessage(cmd.argument());
3855                         break;
3856
3857                 case LFUN_UI_TOGGLE: {
3858                         string arg = cmd.getArg(0);
3859                         if (!lfunUiToggle(arg)) {
3860                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3861                                 dr.setMessage(bformat(msg, from_utf8(arg)));
3862                         }
3863                         // Make sure the keyboard focus stays in the work area.
3864                         setFocus();
3865                         break;
3866                 }
3867
3868                 case LFUN_VIEW_SPLIT: {
3869                         LASSERT(doc_buffer, break);
3870                         string const orientation = cmd.getArg(0);
3871                         d.splitter_->setOrientation(orientation == "vertical"
3872                                 ? Qt::Vertical : Qt::Horizontal);
3873                         TabWorkArea * twa = addTabWorkArea();
3874                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3875                         setCurrentWorkArea(wa);
3876                         break;
3877                 }
3878                 case LFUN_TAB_GROUP_CLOSE:
3879                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3880                                 closeTabWorkArea(twa);
3881                                 d.current_work_area_ = 0;
3882                                 twa = d.currentTabWorkArea();
3883                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3884                                 if (twa) {
3885                                         // Make sure the work area is up to date.
3886                                         setCurrentWorkArea(twa->currentWorkArea());
3887                                 } else {
3888                                         setCurrentWorkArea(0);
3889                                 }
3890                         }
3891                         break;
3892
3893                 case LFUN_VIEW_CLOSE:
3894                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3895                                 closeWorkArea(twa->currentWorkArea());
3896                                 d.current_work_area_ = 0;
3897                                 twa = d.currentTabWorkArea();
3898                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3899                                 if (twa) {
3900                                         // Make sure the work area is up to date.
3901                                         setCurrentWorkArea(twa->currentWorkArea());
3902                                 } else {
3903                                         setCurrentWorkArea(0);
3904                                 }
3905                         }
3906                         break;
3907
3908                 case LFUN_COMPLETION_INLINE:
3909                         if (d.current_work_area_)
3910                                 d.current_work_area_->completer().showInline();
3911                         break;
3912
3913                 case LFUN_COMPLETION_POPUP:
3914                         if (d.current_work_area_)
3915                                 d.current_work_area_->completer().showPopup();
3916                         break;
3917
3918
3919                 case LFUN_COMPLETE:
3920                         if (d.current_work_area_)
3921                                 d.current_work_area_->completer().tab();
3922                         break;
3923
3924                 case LFUN_COMPLETION_CANCEL:
3925                         if (d.current_work_area_) {
3926                                 if (d.current_work_area_->completer().popupVisible())
3927                                         d.current_work_area_->completer().hidePopup();
3928                                 else
3929                                         d.current_work_area_->completer().hideInline();
3930                         }
3931                         break;
3932
3933                 case LFUN_COMPLETION_ACCEPT:
3934                         if (d.current_work_area_)
3935                                 d.current_work_area_->completer().activate();
3936                         break;
3937
3938                 case LFUN_BUFFER_ZOOM_IN:
3939                 case LFUN_BUFFER_ZOOM_OUT: {
3940                         // use a signed temp to avoid overflow
3941                         int zoom = lyxrc.zoom;
3942                         if (cmd.argument().empty()) {
3943                                 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3944                                         zoom += 20;
3945                                 else
3946                                         zoom -= 20;
3947                         } else
3948                                 zoom += convert<int>(cmd.argument());
3949
3950                         if (zoom < 10)
3951                                 zoom = 10;
3952                         lyxrc.zoom = zoom;
3953
3954                         dr.setMessage(bformat(_("Zoom level is now %1$d%"), lyxrc.zoom));
3955
3956                         // The global QPixmapCache is used in GuiPainter to cache text
3957                         // painting so we must reset it.
3958                         QPixmapCache::clear();
3959                         guiApp->fontLoader().update();
3960                         lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3961                         break;
3962                 }
3963
3964                 case LFUN_VC_REGISTER:
3965                 case LFUN_VC_RENAME:
3966                 case LFUN_VC_COPY:
3967                 case LFUN_VC_CHECK_IN:
3968                 case LFUN_VC_CHECK_OUT:
3969                 case LFUN_VC_REPO_UPDATE:
3970                 case LFUN_VC_LOCKING_TOGGLE:
3971                 case LFUN_VC_REVERT:
3972                 case LFUN_VC_UNDO_LAST:
3973                 case LFUN_VC_COMMAND:
3974                 case LFUN_VC_COMPARE:
3975                         dispatchVC(cmd, dr);
3976                         break;
3977
3978                 case LFUN_SERVER_GOTO_FILE_ROW:
3979                         goToFileRow(to_utf8(cmd.argument()));
3980                         break;
3981
3982                 case LFUN_LYX_ACTIVATE:
3983                         activateWindow();
3984                         break;
3985
3986                 case LFUN_FORWARD_SEARCH: {
3987                 // it seems safe to assume we have a document buffer, since
3988                 // getStatus wants one.
3989                 // coverity[FORWARD_NULL]
3990                         Buffer const * doc_master = doc_buffer->masterBuffer();
3991                         FileName const path(doc_master->temppath());
3992                         string const texname = doc_master->isChild(doc_buffer)
3993                                 ? DocFileName(changeExtension(
3994                                         doc_buffer->absFileName(),
3995                                                 "tex")).mangledFileName()
3996                                 : doc_buffer->latexName();
3997                         string const fulltexname = 
3998                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3999                         string const mastername =
4000                                 removeExtension(doc_master->latexName());
4001                         FileName const dviname(addName(path.absFileName(),
4002                                         addExtension(mastername, "dvi")));
4003                         FileName const pdfname(addName(path.absFileName(),
4004                                         addExtension(mastername, "pdf")));
4005                         bool const have_dvi = dviname.exists();
4006                         bool const have_pdf = pdfname.exists();
4007                         if (!have_dvi && !have_pdf) {
4008                                 dr.setMessage(_("Please, preview the document first."));
4009                                 break;
4010                         }
4011                         string outname = dviname.onlyFileName();
4012                         string command = lyxrc.forward_search_dvi;
4013                         if (!have_dvi || (have_pdf &&
4014                             pdfname.lastModified() > dviname.lastModified())) {
4015                                 outname = pdfname.onlyFileName();
4016                                 command = lyxrc.forward_search_pdf;
4017                         }
4018
4019                         DocIterator cur = bv->cursor();
4020                         int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
4021                         LYXERR(Debug::ACTION, "Forward search: row:" << row
4022                                    << " cur:" << cur);
4023                         if (row == -1 || command.empty()) {
4024                                 dr.setMessage(_("Couldn't proceed."));
4025                                 break;
4026                         }
4027                         string texrow = convert<string>(row);
4028
4029                         command = subst(command, "$$n", texrow);
4030                         command = subst(command, "$$f", fulltexname);
4031                         command = subst(command, "$$t", texname);
4032                         command = subst(command, "$$o", outname);
4033
4034                         PathChanger p(path);
4035                         Systemcall one;
4036                         one.startscript(Systemcall::DontWait, command);
4037                         break;
4038                 }
4039
4040                 case LFUN_SPELLING_CONTINUOUSLY:
4041                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4042                         dr.screenUpdate(Update::Force | Update::FitCursor);
4043                         break;
4044
4045                 default:
4046                         // The LFUN must be for one of BufferView, Buffer or Cursor;
4047                         // let's try that:
4048                         dispatchToBufferView(cmd, dr);
4049                         break;
4050         }
4051
4052         // Part of automatic menu appearance feature.
4053         if (isFullScreen()) {
4054                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4055                         menuBar()->hide();
4056         }
4057
4058         // Need to update bv because many LFUNs here might have destroyed it
4059         bv = currentBufferView();
4060
4061         // Clear non-empty selections
4062         // (e.g. from a "char-forward-select" followed by "char-backward-select")
4063         if (bv) {
4064                 Cursor & cur = bv->cursor();
4065                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4066                         cur.clearSelection();
4067                 }
4068         }
4069 }
4070
4071
4072 bool GuiView::lfunUiToggle(string const & ui_component)
4073 {
4074         if (ui_component == "scrollbar") {
4075                 // hide() is of no help
4076                 if (d.current_work_area_->verticalScrollBarPolicy() ==
4077                         Qt::ScrollBarAlwaysOff)
4078
4079                         d.current_work_area_->setVerticalScrollBarPolicy(
4080                                 Qt::ScrollBarAsNeeded);
4081                 else
4082                         d.current_work_area_->setVerticalScrollBarPolicy(
4083                                 Qt::ScrollBarAlwaysOff);
4084         } else if (ui_component == "statusbar") {
4085                 statusBar()->setVisible(!statusBar()->isVisible());
4086         } else if (ui_component == "menubar") {
4087                 menuBar()->setVisible(!menuBar()->isVisible());
4088         } else
4089         if (ui_component == "frame") {
4090                 int l, t, r, b;
4091                 getContentsMargins(&l, &t, &r, &b);
4092                 //are the frames in default state?
4093                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4094                 if (l == 0) {
4095                         setContentsMargins(-2, -2, -2, -2);
4096                 } else {
4097                         setContentsMargins(0, 0, 0, 0);
4098                 }
4099         } else
4100         if (ui_component == "fullscreen") {
4101                 toggleFullScreen();
4102         } else
4103                 return false;
4104         return true;
4105 }
4106
4107
4108 void GuiView::toggleFullScreen()
4109 {
4110         if (isFullScreen()) {
4111                 for (int i = 0; i != d.splitter_->count(); ++i)
4112                         d.tabWorkArea(i)->setFullScreen(false);
4113                 setContentsMargins(0, 0, 0, 0);
4114                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4115                 restoreLayout();
4116                 menuBar()->show();
4117                 statusBar()->show();
4118         } else {
4119                 // bug 5274
4120                 hideDialogs("prefs", 0);
4121                 for (int i = 0; i != d.splitter_->count(); ++i)
4122                         d.tabWorkArea(i)->setFullScreen(true);
4123                 setContentsMargins(-2, -2, -2, -2);
4124                 saveLayout();
4125                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4126                 if (lyxrc.full_screen_statusbar)
4127                         statusBar()->hide();
4128                 if (lyxrc.full_screen_menubar)
4129                         menuBar()->hide();
4130                 if (lyxrc.full_screen_toolbars) {
4131                         ToolbarMap::iterator end = d.toolbars_.end();
4132                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4133                                 it->second->hide();
4134                 }
4135         }
4136
4137         // give dialogs like the TOC a chance to adapt
4138         updateDialogs();
4139 }
4140
4141
4142 Buffer const * GuiView::updateInset(Inset const * inset)
4143 {
4144         if (!inset)
4145                 return 0;
4146
4147         Buffer const * inset_buffer = &(inset->buffer());
4148
4149         for (int i = 0; i != d.splitter_->count(); ++i) {
4150                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4151                 if (!wa)
4152                         continue;
4153                 Buffer const * buffer = &(wa->bufferView().buffer());
4154                 if (inset_buffer == buffer)
4155                         wa->scheduleRedraw();
4156         }
4157         return inset_buffer;
4158 }
4159
4160
4161 void GuiView::restartCursor()
4162 {
4163         /* When we move around, or type, it's nice to be able to see
4164          * the cursor immediately after the keypress.
4165          */
4166         if (d.current_work_area_)
4167                 d.current_work_area_->startBlinkingCursor();
4168
4169         // Take this occasion to update the other GUI elements.
4170         updateDialogs();
4171         updateStatusBar();
4172 }
4173
4174
4175 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4176 {
4177         if (d.current_work_area_)
4178                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4179 }
4180
4181 namespace {
4182
4183 // This list should be kept in sync with the list of insets in
4184 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4185 // dialog should have the same name as the inset.
4186 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4187 // docs in LyXAction.cpp.
4188
4189 char const * const dialognames[] = {
4190
4191 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4192 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4193 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4194 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4195 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4196 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4197 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4198 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4199
4200 char const * const * const end_dialognames =
4201         dialognames + (sizeof(dialognames) / sizeof(char *));
4202
4203 class cmpCStr {
4204 public:
4205         cmpCStr(char const * name) : name_(name) {}
4206         bool operator()(char const * other) {
4207                 return strcmp(other, name_) == 0;
4208         }
4209 private:
4210         char const * name_;
4211 };
4212
4213
4214 bool isValidName(string const & name)
4215 {
4216         return find_if(dialognames, end_dialognames,
4217                                 cmpCStr(name.c_str())) != end_dialognames;
4218 }
4219
4220 } // namespace anon
4221
4222
4223 void GuiView::resetDialogs()
4224 {
4225         // Make sure that no LFUN uses any GuiView.
4226         guiApp->setCurrentView(0);
4227         saveLayout();
4228         saveUISettings();
4229         menuBar()->clear();
4230         constructToolbars();
4231         guiApp->menus().fillMenuBar(menuBar(), this, false);
4232         d.layout_->updateContents(true);
4233         // Now update controls with current buffer.
4234         guiApp->setCurrentView(this);
4235         restoreLayout();
4236         restartCursor();
4237 }
4238
4239
4240 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4241 {
4242         if (!isValidName(name))
4243                 return 0;
4244
4245         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4246
4247         if (it != d.dialogs_.end()) {
4248                 if (hide_it)
4249                         it->second->hideView();
4250                 return it->second.get();
4251         }
4252
4253         Dialog * dialog = build(name);
4254         d.dialogs_[name].reset(dialog);
4255         if (lyxrc.allow_geometry_session)
4256                 dialog->restoreSession();
4257         if (hide_it)
4258                 dialog->hideView();
4259         return dialog;
4260 }
4261
4262
4263 void GuiView::showDialog(string const & name, string const & data,
4264         Inset * inset)
4265 {
4266         triggerShowDialog(toqstr(name), toqstr(data), inset);
4267 }
4268
4269
4270 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4271         Inset * inset)
4272 {
4273         if (d.in_show_)
4274                 return;
4275
4276         const string name = fromqstr(qname);
4277         const string data = fromqstr(qdata);
4278
4279         d.in_show_ = true;
4280         try {
4281                 Dialog * dialog = findOrBuild(name, false);
4282                 if (dialog) {
4283                         bool const visible = dialog->isVisibleView();
4284                         dialog->showData(data);
4285                         if (inset && currentBufferView())
4286                                 currentBufferView()->editInset(name, inset);
4287                         // We only set the focus to the new dialog if it was not yet
4288                         // visible in order not to change the existing previous behaviour
4289                         if (visible) {
4290                                 // activateWindow is needed for floating dockviews
4291                                 dialog->asQWidget()->raise();
4292                                 dialog->asQWidget()->activateWindow();
4293                                 dialog->asQWidget()->setFocus();
4294                         }
4295                 }
4296         }
4297         catch (ExceptionMessage const & ex) {
4298                 d.in_show_ = false;
4299                 throw ex;
4300         }
4301         d.in_show_ = false;
4302 }
4303
4304
4305 bool GuiView::isDialogVisible(string const & name) const
4306 {
4307         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4308         if (it == d.dialogs_.end())
4309                 return false;
4310         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4311 }
4312
4313
4314 void GuiView::hideDialog(string const & name, Inset * inset)
4315 {
4316         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4317         if (it == d.dialogs_.end())
4318                 return;
4319
4320         if (inset) {
4321                 if (!currentBufferView())
4322                         return;
4323                 if (inset != currentBufferView()->editedInset(name))
4324                         return;
4325         }
4326
4327         Dialog * const dialog = it->second.get();
4328         if (dialog->isVisibleView())
4329                 dialog->hideView();
4330         if (currentBufferView())
4331                 currentBufferView()->editInset(name, 0);
4332 }
4333
4334
4335 void GuiView::disconnectDialog(string const & name)
4336 {
4337         if (!isValidName(name))
4338                 return;
4339         if (currentBufferView())
4340                 currentBufferView()->editInset(name, 0);
4341 }
4342
4343
4344 void GuiView::hideAll() const
4345 {
4346         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4347         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4348
4349         for(; it != end; ++it)
4350                 it->second->hideView();
4351 }
4352
4353
4354 void GuiView::updateDialogs()
4355 {
4356         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4357         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4358
4359         for(; it != end; ++it) {
4360                 Dialog * dialog = it->second.get();
4361                 if (dialog) {
4362                         if (dialog->needBufferOpen() && !documentBufferView())
4363                                 hideDialog(fromqstr(dialog->name()), 0);
4364                         else if (dialog->isVisibleView())
4365                                 dialog->checkStatus();
4366                 }
4367         }
4368         updateToolbars();
4369         updateLayoutList();
4370 }
4371
4372 Dialog * createDialog(GuiView & lv, string const & name);
4373
4374 // will be replaced by a proper factory...
4375 Dialog * createGuiAbout(GuiView & lv);
4376 Dialog * createGuiBibtex(GuiView & lv);
4377 Dialog * createGuiChanges(GuiView & lv);
4378 Dialog * createGuiCharacter(GuiView & lv);
4379 Dialog * createGuiCitation(GuiView & lv);
4380 Dialog * createGuiCompare(GuiView & lv);
4381 Dialog * createGuiCompareHistory(GuiView & lv);
4382 Dialog * createGuiDelimiter(GuiView & lv);
4383 Dialog * createGuiDocument(GuiView & lv);
4384 Dialog * createGuiErrorList(GuiView & lv);
4385 Dialog * createGuiExternal(GuiView & lv);
4386 Dialog * createGuiGraphics(GuiView & lv);
4387 Dialog * createGuiInclude(GuiView & lv);
4388 Dialog * createGuiIndex(GuiView & lv);
4389 Dialog * createGuiListings(GuiView & lv);
4390 Dialog * createGuiLog(GuiView & lv);
4391 Dialog * createGuiMathMatrix(GuiView & lv);
4392 Dialog * createGuiNote(GuiView & lv);
4393 Dialog * createGuiParagraph(GuiView & lv);
4394 Dialog * createGuiPhantom(GuiView & lv);
4395 Dialog * createGuiPreferences(GuiView & lv);
4396 Dialog * createGuiPrint(GuiView & lv);
4397 Dialog * createGuiPrintindex(GuiView & lv);
4398 Dialog * createGuiRef(GuiView & lv);
4399 Dialog * createGuiSearch(GuiView & lv);
4400 Dialog * createGuiSearchAdv(GuiView & lv);
4401 Dialog * createGuiSendTo(GuiView & lv);
4402 Dialog * createGuiShowFile(GuiView & lv);
4403 Dialog * createGuiSpellchecker(GuiView & lv);
4404 Dialog * createGuiSymbols(GuiView & lv);
4405 Dialog * createGuiTabularCreate(GuiView & lv);
4406 Dialog * createGuiTexInfo(GuiView & lv);
4407 Dialog * createGuiToc(GuiView & lv);
4408 Dialog * createGuiThesaurus(GuiView & lv);
4409 Dialog * createGuiViewSource(GuiView & lv);
4410 Dialog * createGuiWrap(GuiView & lv);
4411 Dialog * createGuiProgressView(GuiView & lv);
4412
4413
4414
4415 Dialog * GuiView::build(string const & name)
4416 {
4417         LASSERT(isValidName(name), return 0);
4418
4419         Dialog * dialog = createDialog(*this, name);
4420         if (dialog)
4421                 return dialog;
4422
4423         if (name == "aboutlyx")
4424                 return createGuiAbout(*this);
4425         if (name == "bibtex")
4426                 return createGuiBibtex(*this);
4427         if (name == "changes")
4428                 return createGuiChanges(*this);
4429         if (name == "character")
4430                 return createGuiCharacter(*this);
4431         if (name == "citation")
4432                 return createGuiCitation(*this);
4433         if (name == "compare")
4434                 return createGuiCompare(*this);
4435         if (name == "comparehistory")
4436                 return createGuiCompareHistory(*this);
4437         if (name == "document")
4438                 return createGuiDocument(*this);
4439         if (name == "errorlist")
4440                 return createGuiErrorList(*this);
4441         if (name == "external")
4442                 return createGuiExternal(*this);
4443         if (name == "file")
4444                 return createGuiShowFile(*this);
4445         if (name == "findreplace")
4446                 return createGuiSearch(*this);
4447         if (name == "findreplaceadv")
4448                 return createGuiSearchAdv(*this);
4449         if (name == "graphics")
4450                 return createGuiGraphics(*this);
4451         if (name == "include")
4452                 return createGuiInclude(*this);
4453         if (name == "index")
4454                 return createGuiIndex(*this);
4455         if (name == "index_print")
4456                 return createGuiPrintindex(*this);
4457         if (name == "listings")
4458                 return createGuiListings(*this);
4459         if (name == "log")
4460                 return createGuiLog(*this);
4461         if (name == "mathdelimiter")
4462                 return createGuiDelimiter(*this);
4463         if (name == "mathmatrix")
4464                 return createGuiMathMatrix(*this);
4465         if (name == "note")
4466                 return createGuiNote(*this);
4467         if (name == "paragraph")
4468                 return createGuiParagraph(*this);
4469         if (name == "phantom")
4470                 return createGuiPhantom(*this);
4471         if (name == "prefs")
4472                 return createGuiPreferences(*this);
4473         if (name == "ref")
4474                 return createGuiRef(*this);
4475         if (name == "sendto")
4476                 return createGuiSendTo(*this);
4477         if (name == "spellchecker")
4478                 return createGuiSpellchecker(*this);
4479         if (name == "symbols")
4480                 return createGuiSymbols(*this);
4481         if (name == "tabularcreate")
4482                 return createGuiTabularCreate(*this);
4483         if (name == "texinfo")
4484                 return createGuiTexInfo(*this);
4485         if (name == "thesaurus")
4486                 return createGuiThesaurus(*this);
4487         if (name == "toc")
4488                 return createGuiToc(*this);
4489         if (name == "view-source")
4490                 return createGuiViewSource(*this);
4491         if (name == "wrap")
4492                 return createGuiWrap(*this);
4493         if (name == "progress")
4494                 return createGuiProgressView(*this);
4495
4496         return 0;
4497 }
4498
4499
4500 } // namespace frontend
4501 } // namespace lyx
4502
4503 #include "moc_GuiView.cpp"