]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiView.cpp
Include config.h in LyXToolBox.cpp
[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 (lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled()
1556                         && lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onOff(true))
1557                         context |= Toolbars::REVIEW;
1558                 if (lyx::getStatus(FuncRequest(LFUN_IN_MATHMACROTEMPLATE)).enabled())
1559                         context |= Toolbars::MATHMACROTEMPLATE;
1560                 if (lyx::getStatus(FuncRequest(LFUN_IN_IPA)).enabled())
1561                         context |= Toolbars::IPA;
1562                 if (command_execute_)
1563                         context |= Toolbars::MINIBUFFER;
1564
1565                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1566                         it->second->update(context);
1567         } else
1568                 for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
1569                         it->second->update();
1570 }
1571
1572
1573 void GuiView::setBuffer(Buffer * newBuffer)
1574 {
1575         LYXERR(Debug::DEBUG, "Setting buffer: " << newBuffer << endl);
1576         LASSERT(newBuffer, return);
1577         
1578         GuiWorkArea * wa = workArea(*newBuffer);
1579         if (wa == 0) {
1580                 setBusy(true);
1581                 newBuffer->masterBuffer()->updateBuffer();
1582                 setBusy(false);
1583                 wa = addWorkArea(*newBuffer);
1584                 // scroll to the position when the BufferView was last closed
1585                 if (lyxrc.use_lastfilepos) {
1586                         LastFilePosSection::FilePos filepos =
1587                                 theSession().lastFilePos().load(newBuffer->fileName());
1588                         wa->bufferView().moveToPosition(filepos.pit, filepos.pos, 0, 0);
1589                 }
1590         } else {
1591                 //Disconnect the old buffer...there's no new one.
1592                 disconnectBuffer();
1593         }
1594         connectBuffer(*newBuffer);
1595         connectBufferView(wa->bufferView());
1596         setCurrentWorkArea(wa);
1597 }
1598
1599
1600 void GuiView::connectBuffer(Buffer & buf)
1601 {
1602         buf.setGuiDelegate(this);
1603 }
1604
1605
1606 void GuiView::disconnectBuffer()
1607 {
1608         if (d.current_work_area_)
1609                 d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
1610 }
1611
1612
1613 void GuiView::connectBufferView(BufferView & bv)
1614 {
1615         bv.setGuiDelegate(this);
1616 }
1617
1618
1619 void GuiView::disconnectBufferView()
1620 {
1621         if (d.current_work_area_)
1622                 d.current_work_area_->bufferView().setGuiDelegate(0);
1623 }
1624
1625
1626 void GuiView::errors(string const & error_type, bool from_master)
1627 {
1628         BufferView const * const bv = currentBufferView();
1629         if (!bv)
1630                 return;
1631
1632 #if EXPORT_in_THREAD
1633         // We are called with from_master == false by default, so we
1634         // have to figure out whether that is the case or not.
1635         ErrorList & el = bv->buffer().errorList(error_type);
1636         if (el.empty()) {
1637             el = bv->buffer().masterBuffer()->errorList(error_type);
1638             from_master = true;
1639         }
1640 #else
1641         ErrorList const & el = from_master ?
1642                 bv->buffer().masterBuffer()->errorList(error_type) :
1643                 bv->buffer().errorList(error_type);
1644 #endif
1645
1646         if (el.empty())
1647                 return;
1648
1649         string data = error_type;
1650         if (from_master)
1651                 data = "from_master|" + error_type;
1652         showDialog("errorlist", data);
1653 }
1654
1655
1656 void GuiView::updateTocItem(string const & type, DocIterator const & dit)
1657 {
1658         d.toc_models_.updateItem(toqstr(type), dit);
1659 }
1660
1661
1662 void GuiView::structureChanged()
1663 {
1664         d.toc_models_.reset(documentBufferView());
1665         // Navigator needs more than a simple update in this case. It needs to be
1666         // rebuilt.
1667         updateDialog("toc", "");
1668 }
1669
1670
1671 void GuiView::updateDialog(string const & name, string const & data)
1672 {
1673         if (!isDialogVisible(name))
1674                 return;
1675
1676         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
1677         if (it == d.dialogs_.end())
1678                 return;
1679
1680         Dialog * const dialog = it->second.get();
1681         if (dialog->isVisibleView())
1682                 dialog->initialiseParams(data);
1683 }
1684
1685
1686 BufferView * GuiView::documentBufferView()
1687 {
1688         return currentMainWorkArea()
1689                 ? &currentMainWorkArea()->bufferView()
1690                 : 0;
1691 }
1692
1693
1694 BufferView const * GuiView::documentBufferView() const
1695 {
1696         return currentMainWorkArea()
1697                 ? &currentMainWorkArea()->bufferView()
1698                 : 0;
1699 }
1700
1701
1702 BufferView * GuiView::currentBufferView()
1703 {
1704         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1705 }
1706
1707
1708 BufferView const * GuiView::currentBufferView() const
1709 {
1710         return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
1711 }
1712
1713
1714 docstring GuiView::GuiViewPrivate::autosaveAndDestroy(
1715         Buffer const * orig, Buffer * clone)
1716 {
1717         bool const success = clone->autoSave();
1718         delete clone;
1719         busyBuffers.remove(orig);
1720         return success
1721                 ? _("Automatic save done.")
1722                 : _("Automatic save failed!");
1723 }
1724
1725
1726 void GuiView::autoSave()
1727 {
1728         LYXERR(Debug::INFO, "Running autoSave()");
1729
1730         Buffer * buffer = documentBufferView()
1731                 ? &documentBufferView()->buffer() : 0;
1732         if (!buffer) {
1733                 resetAutosaveTimers();
1734                 return;
1735         }
1736
1737         GuiViewPrivate::busyBuffers.insert(buffer);
1738         QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
1739                 buffer, buffer->cloneBufferOnly());
1740         d.autosave_watcher_.setFuture(f);
1741         resetAutosaveTimers();
1742 }
1743
1744
1745 void GuiView::resetAutosaveTimers()
1746 {
1747         if (lyxrc.autosave)
1748                 d.autosave_timeout_.restart();
1749 }
1750
1751
1752 bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
1753 {
1754         bool enable = true;
1755         Buffer * buf = currentBufferView()
1756                 ? &currentBufferView()->buffer() : 0;
1757         Buffer * doc_buffer = documentBufferView()
1758                 ? &(documentBufferView()->buffer()) : 0;
1759
1760 #ifdef Q_OS_MAC
1761         /* In LyX/Mac, when a dialog is open, the menus of the
1762            application can still be accessed without giving focus to
1763            the main window. In this case, we want to disable the menu
1764            entries that are buffer-related.
1765            This code must not be used on Linux and Windows, since it
1766            would disable buffer-related entries when hovering over the
1767            menu (see bug #9574).
1768          */
1769         if (cmd.origin() == FuncRequest::MENU && !hasFocus()) {
1770                 buf = 0;
1771                 doc_buffer = 0;
1772         }
1773 #endif
1774
1775         // Check whether we need a buffer
1776         if (!lyxaction.funcHasFlag(cmd.action(), LyXAction::NoBuffer) && !buf) {
1777                 // no, exit directly
1778                 flag.message(from_utf8(N_("Command not allowed with"
1779                                         "out any document open")));
1780                 flag.setEnabled(false);
1781                 return true;
1782         }
1783
1784         if (cmd.origin() == FuncRequest::TOC) {
1785                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
1786                 if (!toc || !toc->getStatus(documentBufferView()->cursor(), cmd, flag))
1787                         flag.setEnabled(false);
1788                 return true;
1789         }
1790
1791         switch(cmd.action()) {
1792         case LFUN_BUFFER_IMPORT:
1793                 break;
1794
1795         case LFUN_MASTER_BUFFER_UPDATE:
1796         case LFUN_MASTER_BUFFER_VIEW:
1797                 enable = doc_buffer
1798                         && (doc_buffer->parent() != 0
1799                             || doc_buffer->hasChildren())
1800                         && !d.processing_thread_watcher_.isRunning();
1801                 break;
1802
1803         case LFUN_BUFFER_UPDATE:
1804         case LFUN_BUFFER_VIEW: {
1805                 if (!doc_buffer || d.processing_thread_watcher_.isRunning()) {
1806                         enable = false;
1807                         break;
1808                 }
1809                 string format = to_utf8(cmd.argument());
1810                 if (cmd.argument().empty())
1811                         format = doc_buffer->params().getDefaultOutputFormat();
1812                 enable = doc_buffer->params().isExportableFormat(format);
1813                 break;
1814         }
1815
1816         case LFUN_BUFFER_RELOAD:
1817                 enable = doc_buffer && !doc_buffer->isUnnamed()
1818                         && doc_buffer->fileName().exists()
1819                         && (!doc_buffer->isClean()
1820                            || doc_buffer->isExternallyModified(Buffer::timestamp_method));
1821                 break;
1822
1823         case LFUN_BUFFER_CHILD_OPEN:
1824                 enable = doc_buffer != 0;
1825                 break;
1826
1827         case LFUN_BUFFER_WRITE:
1828                 enable = doc_buffer && (doc_buffer->isUnnamed() || !doc_buffer->isClean());
1829                 break;
1830
1831         //FIXME: This LFUN should be moved to GuiApplication.
1832         case LFUN_BUFFER_WRITE_ALL: {
1833                 // We enable the command only if there are some modified buffers
1834                 Buffer * first = theBufferList().first();
1835                 enable = false;
1836                 if (!first)
1837                         break;
1838                 Buffer * b = first;
1839                 // We cannot use a for loop as the buffer list is a cycle.
1840                 do {
1841                         if (!b->isClean()) {
1842                                 enable = true;
1843                                 break;
1844                         }
1845                         b = theBufferList().next(b);
1846                 } while (b != first);
1847                 break;
1848         }
1849
1850         case LFUN_BUFFER_WRITE_AS:
1851         case LFUN_BUFFER_EXPORT_AS:
1852                 enable = doc_buffer != 0;
1853                 break;
1854
1855         case LFUN_BUFFER_CLOSE:
1856         case LFUN_VIEW_CLOSE:
1857                 enable = doc_buffer != 0;
1858                 break;
1859
1860         case LFUN_BUFFER_CLOSE_ALL:
1861                 enable = theBufferList().last() != theBufferList().first();
1862                 break;
1863
1864         case LFUN_VIEW_SPLIT:
1865                 if (cmd.getArg(0) == "vertical")
1866                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1867                                          d.splitter_->orientation() == Qt::Vertical);
1868                 else
1869                         enable = doc_buffer && (d.splitter_->count() == 1 ||
1870                                          d.splitter_->orientation() == Qt::Horizontal);
1871                 break;
1872
1873         case LFUN_TAB_GROUP_CLOSE:
1874                 enable = d.tabWorkAreaCount() > 1;
1875                 break;
1876
1877         case LFUN_TOOLBAR_TOGGLE: {
1878                 string const name = cmd.getArg(0);
1879                 if (GuiToolbar * t = toolbar(name))
1880                         flag.setOnOff(t->isVisible());
1881                 else {
1882                         enable = false;
1883                         docstring const msg =
1884                                 bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name));
1885                         flag.message(msg);
1886                 }
1887                 break;
1888         }
1889
1890         case LFUN_DROP_LAYOUTS_CHOICE:
1891                 enable = buf != 0;
1892                 break;
1893
1894         case LFUN_UI_TOGGLE:
1895                 flag.setOnOff(isFullScreen());
1896                 break;
1897
1898         case LFUN_DIALOG_DISCONNECT_INSET:
1899                 break;
1900
1901         case LFUN_DIALOG_HIDE:
1902                 // FIXME: should we check if the dialog is shown?
1903                 break;
1904
1905         case LFUN_DIALOG_TOGGLE:
1906                 flag.setOnOff(isDialogVisible(cmd.getArg(0)));
1907                 // fall through to set "enable"
1908         case LFUN_DIALOG_SHOW: {
1909                 string const name = cmd.getArg(0);
1910                 if (!doc_buffer)
1911                         enable = name == "aboutlyx"
1912                                 || name == "file" //FIXME: should be removed.
1913                                 || name == "prefs"
1914                                 || name == "texinfo"
1915                                 || name == "progress"
1916                                 || name == "compare";
1917                 else if (name == "character" || name == "symbols"
1918                         || name == "mathdelimiter" || name == "mathmatrix") {
1919                         if (!buf || buf->isReadonly())
1920                                 enable = false;
1921                         else {
1922                                 Cursor const & cur = currentBufferView()->cursor();
1923                                 enable = !(cur.inTexted() && cur.paragraph().isPassThru());
1924                         }
1925                 }
1926                 else if (name == "latexlog")
1927                         enable = FileName(doc_buffer->logName()).isReadableFile();
1928                 else if (name == "spellchecker")
1929                         enable = theSpellChecker() 
1930                                 && !doc_buffer->isReadonly()
1931                                 && !doc_buffer->text().empty();
1932                 else if (name == "vclog")
1933                         enable = doc_buffer->lyxvc().inUse();
1934                 break;
1935         }
1936
1937         case LFUN_DIALOG_UPDATE: {
1938                 string const name = cmd.getArg(0);
1939                 if (!buf)
1940                         enable = name == "prefs";
1941                 break;
1942         }
1943
1944         case LFUN_COMMAND_EXECUTE:
1945         case LFUN_MESSAGE:
1946         case LFUN_MENU_OPEN:
1947                 // Nothing to check.
1948                 break;
1949
1950         case LFUN_COMPLETION_INLINE:
1951                 if (!d.current_work_area_
1952                         || !d.current_work_area_->completer().inlinePossible(
1953                         currentBufferView()->cursor()))
1954                         enable = false;
1955                 break;
1956
1957         case LFUN_COMPLETION_POPUP:
1958                 if (!d.current_work_area_
1959                         || !d.current_work_area_->completer().popupPossible(
1960                         currentBufferView()->cursor()))
1961                         enable = false;
1962                 break;
1963
1964         case LFUN_COMPLETE:
1965                 if (!d.current_work_area_
1966                         || !d.current_work_area_->completer().inlinePossible(
1967                         currentBufferView()->cursor()))
1968                         enable = false;
1969                 break;
1970
1971         case LFUN_COMPLETION_ACCEPT:
1972                 if (!d.current_work_area_
1973                         || (!d.current_work_area_->completer().popupVisible()
1974                         && !d.current_work_area_->completer().inlineVisible()
1975                         && !d.current_work_area_->completer().completionAvailable()))
1976                         enable = false;
1977                 break;
1978
1979         case LFUN_COMPLETION_CANCEL:
1980                 if (!d.current_work_area_
1981                         || (!d.current_work_area_->completer().popupVisible()
1982                         && !d.current_work_area_->completer().inlineVisible()))
1983                         enable = false;
1984                 break;
1985
1986         case LFUN_BUFFER_ZOOM_OUT:
1987                 enable = doc_buffer && lyxrc.zoom > 10;
1988                 break;
1989
1990         case LFUN_BUFFER_ZOOM_IN:
1991                 enable = doc_buffer != 0;
1992                 break;
1993
1994         case LFUN_BUFFER_MOVE_NEXT:
1995         case LFUN_BUFFER_MOVE_PREVIOUS:
1996                 // we do not cycle when moving
1997         case LFUN_BUFFER_NEXT:
1998         case LFUN_BUFFER_PREVIOUS:
1999                 // because we cycle, it doesn't matter whether on first or last
2000                 enable = (d.currentTabWorkArea()->count() > 1);
2001                 break;
2002         case LFUN_BUFFER_SWITCH:
2003                 // toggle on the current buffer, but do not toggle off
2004                 // the other ones (is that a good idea?)
2005                 if (doc_buffer
2006                         && to_utf8(cmd.argument()) == doc_buffer->absFileName())
2007                         flag.setOnOff(true);
2008                 break;
2009
2010         case LFUN_VC_REGISTER:
2011                 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
2012                 break;
2013         case LFUN_VC_RENAME:
2014                 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
2015                 break;
2016         case LFUN_VC_COPY:
2017                 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
2018                 break;
2019         case LFUN_VC_CHECK_IN:
2020                 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
2021                 break;
2022         case LFUN_VC_CHECK_OUT:
2023                 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
2024                 break;
2025         case LFUN_VC_LOCKING_TOGGLE:
2026                 enable = doc_buffer && !doc_buffer->isReadonly()
2027                         && doc_buffer->lyxvc().lockingToggleEnabled();
2028                 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
2029                 break;
2030         case LFUN_VC_REVERT:
2031                 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
2032                 break;
2033         case LFUN_VC_UNDO_LAST:
2034                 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
2035                 break;
2036         case LFUN_VC_REPO_UPDATE:
2037                 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
2038                 break;
2039         case LFUN_VC_COMMAND: {
2040                 if (cmd.argument().empty())
2041                         enable = false;
2042                 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
2043                         enable = false;
2044                 break;
2045         }
2046         case LFUN_VC_COMPARE:
2047                 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
2048                 break;
2049
2050         case LFUN_SERVER_GOTO_FILE_ROW:
2051                 break;
2052         case LFUN_FORWARD_SEARCH:
2053                 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
2054                 break;
2055
2056         case LFUN_FILE_INSERT_PLAINTEXT:
2057         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
2058                 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
2059                 break;
2060
2061         case LFUN_SPELLING_CONTINUOUSLY:
2062                 flag.setOnOff(lyxrc.spellcheck_continuously);
2063                 break;
2064
2065         default:
2066                 return false;
2067         }
2068
2069         if (!enable)
2070                 flag.setEnabled(false);
2071
2072         return true;
2073 }
2074
2075
2076 static FileName selectTemplateFile()
2077 {
2078         FileDialog dlg(qt_("Select template file"));
2079         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2080         dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2081
2082         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2083                                  QStringList(qt_("LyX Documents (*.lyx)")));
2084
2085         if (result.first == FileDialog::Later)
2086                 return FileName();
2087         if (result.second.isEmpty())
2088                 return FileName();
2089         return FileName(fromqstr(result.second));
2090 }
2091
2092
2093 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2094 {
2095         setBusy(true);
2096
2097         Buffer * newBuffer = 0;
2098         try {
2099                 newBuffer = checkAndLoadLyXFile(filename);
2100         } catch (ExceptionMessage const & e) {
2101                 setBusy(false);
2102                 throw(e);
2103         }
2104         setBusy(false);
2105
2106         if (!newBuffer) {
2107                 message(_("Document not loaded."));
2108                 return 0;
2109         }
2110
2111         setBuffer(newBuffer);
2112         newBuffer->errors("Parse");
2113
2114         if (tolastfiles)
2115                 theSession().lastFiles().add(filename);
2116
2117         return newBuffer;
2118 }
2119
2120
2121 void GuiView::openDocument(string const & fname)
2122 {
2123         string initpath = lyxrc.document_path;
2124
2125         if (documentBufferView()) {
2126                 string const trypath = documentBufferView()->buffer().filePath();
2127                 // If directory is writeable, use this as default.
2128                 if (FileName(trypath).isDirWritable())
2129                         initpath = trypath;
2130         }
2131
2132         string filename;
2133
2134         if (fname.empty()) {
2135                 FileDialog dlg(qt_("Select document to open"));
2136                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2137                 dlg.setButton2(qt_("Examples|#E#e"),
2138                                 toqstr(addPath(package().system_support().absFileName(), "examples")));
2139
2140                 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2141                 FileDialog::Result result =
2142                         dlg.open(toqstr(initpath), filter);
2143
2144                 if (result.first == FileDialog::Later)
2145                         return;
2146
2147                 filename = fromqstr(result.second);
2148
2149                 // check selected filename
2150                 if (filename.empty()) {
2151                         message(_("Canceled."));
2152                         return;
2153                 }
2154         } else
2155                 filename = fname;
2156
2157         // get absolute path of file and add ".lyx" to the filename if
2158         // necessary.
2159         FileName const fullname =
2160                         fileSearch(string(), filename, "lyx", support::may_not_exist);
2161         if (!fullname.empty())
2162                 filename = fullname.absFileName();
2163
2164         if (!fullname.onlyPath().isDirectory()) {
2165                 Alert::warning(_("Invalid filename"),
2166                                 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2167                                 from_utf8(fullname.absFileName())));
2168                 return;
2169         }
2170
2171         // if the file doesn't exist and isn't already open (bug 6645),
2172         // let the user create one
2173         if (!fullname.exists() && !theBufferList().exists(fullname) &&
2174             !LyXVC::file_not_found_hook(fullname)) {
2175                 // the user specifically chose this name. Believe him.
2176                 Buffer * const b = newFile(filename, string(), true);
2177                 if (b)
2178                         setBuffer(b);
2179                 return;
2180         }
2181
2182         docstring const disp_fn = makeDisplayPath(filename);
2183         message(bformat(_("Opening document %1$s..."), disp_fn));
2184
2185         docstring str2;
2186         Buffer * buf = loadDocument(fullname);
2187         if (buf) {
2188                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2189                 if (buf->lyxvc().inUse())
2190                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2191                                 " " + _("Version control detected.");
2192         } else {
2193                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2194         }
2195         message(str2);
2196 }
2197
2198 // FIXME: clean that
2199 static bool import(GuiView * lv, FileName const & filename,
2200         string const & format, ErrorList & errorList)
2201 {
2202         FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2203
2204         string loader_format;
2205         vector<string> loaders = theConverters().loaders();
2206         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2207                 vector<string>::const_iterator it = loaders.begin();
2208                 vector<string>::const_iterator en = loaders.end();
2209                 for (; it != en; ++it) {
2210                         if (!theConverters().isReachable(format, *it))
2211                                 continue;
2212
2213                         string const tofile =
2214                                 support::changeExtension(filename.absFileName(),
2215                                 formats.extension(*it));
2216                         if (!theConverters().convert(0, filename, FileName(tofile),
2217                                 filename, format, *it, errorList))
2218                                 return false;
2219                         loader_format = *it;
2220                         break;
2221                 }
2222                 if (loader_format.empty()) {
2223                         frontend::Alert::error(_("Couldn't import file"),
2224                                          bformat(_("No information for importing the format %1$s."),
2225                                          formats.prettyName(format)));
2226                         return false;
2227                 }
2228         } else
2229                 loader_format = format;
2230
2231         if (loader_format == "lyx") {
2232                 Buffer * buf = lv->loadDocument(lyxfile);
2233                 if (!buf)
2234                         return false;
2235         } else {
2236                 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2237                 if (!b)
2238                         return false;
2239                 lv->setBuffer(b);
2240                 bool as_paragraphs = loader_format == "textparagraph";
2241                 string filename2 = (loader_format == format) ? filename.absFileName()
2242                         : support::changeExtension(filename.absFileName(),
2243                                           formats.extension(loader_format));
2244                 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2245                         as_paragraphs);
2246                 guiApp->setCurrentView(lv);
2247                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2248         }
2249
2250         return true;
2251 }
2252
2253
2254 void GuiView::importDocument(string const & argument)
2255 {
2256         string format;
2257         string filename = split(argument, format, ' ');
2258
2259         LYXERR(Debug::INFO, format << " file: " << filename);
2260
2261         // need user interaction
2262         if (filename.empty()) {
2263                 string initpath = lyxrc.document_path;
2264                 if (documentBufferView()) {
2265                         string const trypath = documentBufferView()->buffer().filePath();
2266                         // If directory is writeable, use this as default.
2267                         if (FileName(trypath).isDirWritable())
2268                                 initpath = trypath;
2269                 }
2270
2271                 docstring const text = bformat(_("Select %1$s file to import"),
2272                         formats.prettyName(format));
2273
2274                 FileDialog dlg(toqstr(text));
2275                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2276                 dlg.setButton2(qt_("Examples|#E#e"),
2277                         toqstr(addPath(package().system_support().absFileName(), "examples")));
2278
2279                 docstring filter = formats.prettyName(format);
2280                 filter += " (*.{";
2281                 // FIXME UNICODE
2282                 filter += from_utf8(formats.extensions(format));
2283                 filter += "})";
2284
2285                 FileDialog::Result result =
2286                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2287
2288                 if (result.first == FileDialog::Later)
2289                         return;
2290
2291                 filename = fromqstr(result.second);
2292
2293                 // check selected filename
2294                 if (filename.empty())
2295                         message(_("Canceled."));
2296         }
2297
2298         if (filename.empty())
2299                 return;
2300
2301         // get absolute path of file
2302         FileName const fullname(support::makeAbsPath(filename));
2303
2304         // Can happen if the user entered a path into the dialog
2305         // (see bug #7437)
2306         if (fullname.onlyFileName().empty()) {
2307                 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2308                                           "Aborting import."),
2309                                         from_utf8(fullname.absFileName()));
2310                 frontend::Alert::error(_("File name error"), msg);
2311                 message(_("Canceled."));
2312                 return;
2313         }
2314
2315
2316         FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2317
2318         // Check if the document already is open
2319         Buffer * buf = theBufferList().getBuffer(lyxfile);
2320         if (buf) {
2321                 setBuffer(buf);
2322                 if (!closeBuffer()) {
2323                         message(_("Canceled."));
2324                         return;
2325                 }
2326         }
2327
2328         docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2329
2330         // if the file exists already, and we didn't do
2331         // -i lyx thefile.lyx, warn
2332         if (lyxfile.exists() && fullname != lyxfile) {
2333
2334                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2335                         "Do you want to overwrite that document?"), displaypath);
2336                 int const ret = Alert::prompt(_("Overwrite document?"),
2337                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2338
2339                 if (ret == 1) {
2340                         message(_("Canceled."));
2341                         return;
2342                 }
2343         }
2344
2345         message(bformat(_("Importing %1$s..."), displaypath));
2346         ErrorList errorList;
2347         if (import(this, fullname, format, errorList))
2348                 message(_("imported."));
2349         else
2350                 message(_("file not imported!"));
2351
2352         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2353 }
2354
2355
2356 void GuiView::newDocument(string const & filename, bool from_template)
2357 {
2358         FileName initpath(lyxrc.document_path);
2359         if (documentBufferView()) {
2360                 FileName const trypath(documentBufferView()->buffer().filePath());
2361                 // If directory is writeable, use this as default.
2362                 if (trypath.isDirWritable())
2363                         initpath = trypath;
2364         }
2365
2366         string templatefile;
2367         if (from_template) {
2368                 templatefile = selectTemplateFile().absFileName();
2369                 if (templatefile.empty())
2370                         return;
2371         }
2372
2373         Buffer * b;
2374         if (filename.empty())
2375                 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2376         else
2377                 b = newFile(filename, templatefile, true);
2378
2379         if (b)
2380                 setBuffer(b);
2381
2382         // If no new document could be created, it is unsure
2383         // whether there is a valid BufferView.
2384         if (currentBufferView())
2385                 // Ensure the cursor is correctly positioned on screen.
2386                 currentBufferView()->showCursor();
2387 }
2388
2389
2390 void GuiView::insertLyXFile(docstring const & fname)
2391 {
2392         BufferView * bv = documentBufferView();
2393         if (!bv)
2394                 return;
2395
2396         // FIXME UNICODE
2397         FileName filename(to_utf8(fname));
2398         if (filename.empty()) {
2399                 // Launch a file browser
2400                 // FIXME UNICODE
2401                 string initpath = lyxrc.document_path;
2402                 string const trypath = bv->buffer().filePath();
2403                 // If directory is writeable, use this as default.
2404                 if (FileName(trypath).isDirWritable())
2405                         initpath = trypath;
2406
2407                 // FIXME UNICODE
2408                 FileDialog dlg(qt_("Select LyX document to insert"));
2409                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2410                 dlg.setButton2(qt_("Examples|#E#e"),
2411                         toqstr(addPath(package().system_support().absFileName(),
2412                         "examples")));
2413
2414                 FileDialog::Result result = dlg.open(toqstr(initpath),
2415                                          QStringList(qt_("LyX Documents (*.lyx)")));
2416
2417                 if (result.first == FileDialog::Later)
2418                         return;
2419
2420                 // FIXME UNICODE
2421                 filename.set(fromqstr(result.second));
2422
2423                 // check selected filename
2424                 if (filename.empty()) {
2425                         // emit message signal.
2426                         message(_("Canceled."));
2427                         return;
2428                 }
2429         }
2430
2431         bv->insertLyXFile(filename);
2432         bv->buffer().errors("Parse");
2433 }
2434
2435
2436 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2437 {
2438         FileName fname = b.fileName();
2439         FileName const oldname = fname;
2440
2441         if (!newname.empty()) {
2442                 // FIXME UNICODE
2443                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2444         } else {
2445                 // Switch to this Buffer.
2446                 setBuffer(&b);
2447
2448                 // No argument? Ask user through dialog.
2449                 // FIXME UNICODE
2450                 FileDialog dlg(qt_("Choose a filename to save document as"));
2451                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2452                 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2453
2454                 if (!isLyXFileName(fname.absFileName()))
2455                         fname.changeExtension(".lyx");
2456
2457                 FileDialog::Result result =
2458                         dlg.save(toqstr(fname.onlyPath().absFileName()),
2459                                    QStringList(qt_("LyX Documents (*.lyx)")),
2460                                          toqstr(fname.onlyFileName()));
2461
2462                 if (result.first == FileDialog::Later)
2463                         return false;
2464
2465                 fname.set(fromqstr(result.second));
2466
2467                 if (fname.empty())
2468                         return false;
2469
2470                 if (!isLyXFileName(fname.absFileName()))
2471                         fname.changeExtension(".lyx");
2472         }
2473
2474         // fname is now the new Buffer location.
2475
2476         // if there is already a Buffer open with this name, we do not want
2477         // to have another one. (the second test makes sure we're not just
2478         // trying to overwrite ourselves, which is fine.)
2479         if (theBufferList().exists(fname) && fname != oldname
2480                   && theBufferList().getBuffer(fname) != &b) {
2481                 docstring const text =
2482                         bformat(_("The file\n%1$s\nis already open in your current session.\n"
2483                             "Please close it before attempting to overwrite it.\n"
2484                             "Do you want to choose a new filename?"),
2485                                 from_utf8(fname.absFileName()));
2486                 int const ret = Alert::prompt(_("Chosen File Already Open"),
2487                         text, 0, 1, _("&Rename"), _("&Cancel"));
2488                 switch (ret) {
2489                 case 0: return renameBuffer(b, docstring(), kind);
2490                 case 1: return false;
2491                 }
2492                 //return false;
2493         }
2494
2495         bool const existsLocal = fname.exists();
2496         bool const existsInVC = LyXVC::fileInVC(fname);
2497         if (existsLocal || existsInVC) {
2498                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2499                 if (kind != LV_WRITE_AS && existsInVC) {
2500                         // renaming to a name that is already in VC
2501                         // would not work
2502                         docstring text = bformat(_("The document %1$s "
2503                                         "is already registered.\n\n"
2504                                         "Do you want to choose a new name?"),
2505                                 file);
2506                         docstring const title = (kind == LV_VC_RENAME) ?
2507                                 _("Rename document?") : _("Copy document?");
2508                         docstring const button = (kind == LV_VC_RENAME) ?
2509                                 _("&Rename") : _("&Copy");
2510                         int const ret = Alert::prompt(title, text, 0, 1,
2511                                 button, _("&Cancel"));
2512                         switch (ret) {
2513                         case 0: return renameBuffer(b, docstring(), kind);
2514                         case 1: return false;
2515                         }
2516                 }
2517
2518                 if (existsLocal) {
2519                         docstring text = bformat(_("The document %1$s "
2520                                         "already exists.\n\n"
2521                                         "Do you want to overwrite that document?"),
2522                                 file);
2523                         int const ret = Alert::prompt(_("Overwrite document?"),
2524                                         text, 0, 2, _("&Overwrite"),
2525                                         _("&Rename"), _("&Cancel"));
2526                         switch (ret) {
2527                         case 0: break;
2528                         case 1: return renameBuffer(b, docstring(), kind);
2529                         case 2: return false;
2530                         }
2531                 }
2532         }
2533
2534         switch (kind) {
2535         case LV_VC_RENAME: {
2536                 string msg = b.lyxvc().rename(fname);
2537                 if (msg.empty())
2538                         return false;
2539                 message(from_utf8(msg));
2540                 break;
2541         }
2542         case LV_VC_COPY: {
2543                 string msg = b.lyxvc().copy(fname);
2544                 if (msg.empty())
2545                         return false;
2546                 message(from_utf8(msg));
2547                 break;
2548         }
2549         case LV_WRITE_AS:
2550                 break;
2551         }
2552         // LyXVC created the file already in case of LV_VC_RENAME or
2553         // LV_VC_COPY, but call saveBuffer() nevertheless to get
2554         // relative paths of included stuff right if we moved e.g. from
2555         // /a/b.lyx to /a/c/b.lyx.
2556
2557         bool const saved = saveBuffer(b, fname);
2558         if (saved)
2559                 b.reload();
2560         return saved;
2561 }
2562
2563
2564 struct PrettyNameComparator
2565 {
2566         bool operator()(Format const *first, Format const *second) const {
2567                 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2568                                        translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2569         }
2570 };
2571
2572
2573 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2574 {
2575         FileName fname = b.fileName();
2576
2577         FileDialog dlg(qt_("Choose a filename to export the document as"));
2578         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2579
2580         QStringList types;
2581         QString const anyformat = qt_("Guess from extension (*.*)");
2582         types << anyformat;
2583         Formats::const_iterator it = formats.begin();
2584         vector<Format const *> export_formats;
2585         for (; it != formats.end(); ++it)
2586                 if (it->documentFormat())
2587                         export_formats.push_back(&(*it));
2588         PrettyNameComparator cmp;
2589         sort(export_formats.begin(), export_formats.end(), cmp);
2590         vector<Format const *>::const_iterator fit = export_formats.begin();
2591         map<QString, string> fmap;
2592         QString filter;
2593         string ext;
2594         for (; fit != export_formats.end(); ++fit) {
2595                 docstring const loc_prettyname =
2596                         translateIfPossible(from_utf8((*fit)->prettyname()));
2597                 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2598                                                      loc_prettyname,
2599                                                      from_ascii((*fit)->extension())));
2600                 types << loc_filter;
2601                 fmap[loc_filter] = (*fit)->name();
2602                 if (from_ascii((*fit)->name()) == iformat) {
2603                         filter = loc_filter;
2604                         ext = (*fit)->extension();
2605                 }
2606         }
2607         string ofname = fname.onlyFileName();
2608         if (!ext.empty())
2609                 ofname = support::changeExtension(ofname, ext);
2610         FileDialog::Result result =
2611                 dlg.save(toqstr(fname.onlyPath().absFileName()),
2612                          types,
2613                          toqstr(ofname),
2614                          &filter);
2615         if (result.first != FileDialog::Chosen)
2616                 return false;
2617
2618         string fmt_name;
2619         fname.set(fromqstr(result.second));
2620         if (filter == anyformat)
2621                 fmt_name = formats.getFormatFromExtension(fname.extension());
2622         else
2623                 fmt_name = fmap[filter];
2624         LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2625                << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2626
2627         if (fmt_name.empty() || fname.empty())
2628                 return false;
2629
2630         // fname is now the new Buffer location.
2631         if (FileName(fname).exists()) {
2632                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2633                 docstring text = bformat(_("The document %1$s already "
2634                                            "exists.\n\nDo you want to "
2635                                            "overwrite that document?"),
2636                                          file);
2637                 int const ret = Alert::prompt(_("Overwrite document?"),
2638                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2639                 switch (ret) {
2640                 case 0: break;
2641                 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2642                 case 2: return false;
2643                 }
2644         }
2645
2646         FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2647         DispatchResult dr;
2648         dispatch(cmd, dr);
2649         return dr.dispatched();
2650 }
2651
2652
2653 bool GuiView::saveBuffer(Buffer & b)
2654 {
2655         return saveBuffer(b, FileName());
2656 }
2657
2658
2659 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2660 {
2661         if (workArea(b) && workArea(b)->inDialogMode())
2662                 return true;
2663
2664         if (fn.empty() && b.isUnnamed())
2665                 return renameBuffer(b, docstring());
2666
2667         bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2668         if (success) {
2669                 theSession().lastFiles().add(b.fileName());
2670                 return true;
2671         }
2672
2673         // Switch to this Buffer.
2674         setBuffer(&b);
2675
2676         // FIXME: we don't tell the user *WHY* the save failed !!
2677         docstring const file = makeDisplayPath(b.absFileName(), 30);
2678         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2679                                    "Do you want to rename the document and "
2680                                    "try again?"), file);
2681         int const ret = Alert::prompt(_("Rename and save?"),
2682                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2683         switch (ret) {
2684         case 0:
2685                 if (!renameBuffer(b, docstring()))
2686                         return false;
2687                 break;
2688         case 1:
2689                 break;
2690         case 2:
2691                 return false;
2692         }
2693
2694         return saveBuffer(b, fn);
2695 }
2696
2697
2698 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2699 {
2700         return closeWorkArea(wa, false);
2701 }
2702
2703
2704 // We only want to close the buffer if it is not visible in other workareas
2705 // of the same view, nor in other views, and if this is not a child
2706 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2707 {
2708         Buffer & buf = wa->bufferView().buffer();
2709
2710         bool last_wa = d.countWorkAreasOf(buf) == 1
2711                 && !inOtherView(buf) && !buf.parent();
2712
2713         bool close_buffer = last_wa;
2714
2715         if (last_wa) {
2716                 if (lyxrc.close_buffer_with_last_view == "yes")
2717                         ; // Nothing to do
2718                 else if (lyxrc.close_buffer_with_last_view == "no")
2719                         close_buffer = false;
2720                 else {
2721                         docstring file;
2722                         if (buf.isUnnamed())
2723                                 file = from_utf8(buf.fileName().onlyFileName());
2724                         else
2725                                 file = buf.fileName().displayName(30);
2726                         docstring const text = bformat(
2727                                 _("Last view on document %1$s is being closed.\n"
2728                                   "Would you like to close or hide the document?\n"
2729                                   "\n"
2730                                   "Hidden documents can be displayed back through\n"
2731                                   "the menu: View->Hidden->...\n"
2732                                   "\n"
2733                                   "To remove this question, set your preference in:\n"
2734                                   "  Tools->Preferences->Look&Feel->UserInterface\n"
2735                                 ), file);
2736                         int ret = Alert::prompt(_("Close or hide document?"),
2737                                 text, 0, 1, _("&Close"), _("&Hide"));
2738                         close_buffer = (ret == 0);
2739                 }
2740         }
2741
2742         return closeWorkArea(wa, close_buffer);
2743 }
2744
2745
2746 bool GuiView::closeBuffer()
2747 {
2748         GuiWorkArea * wa = currentMainWorkArea();
2749         setCurrentWorkArea(wa);
2750         Buffer & buf = wa->bufferView().buffer();
2751         return wa && closeWorkArea(wa, !buf.parent());
2752 }
2753
2754
2755 void GuiView::writeSession() const {
2756         GuiWorkArea const * active_wa = currentMainWorkArea();
2757         for (int i = 0; i < d.splitter_->count(); ++i) {
2758                 TabWorkArea * twa = d.tabWorkArea(i);
2759                 for (int j = 0; j < twa->count(); ++j) {
2760                         GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2761                         Buffer & buf = wa->bufferView().buffer();
2762                         theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2763                 }
2764         }
2765 }
2766
2767
2768 bool GuiView::closeBufferAll()
2769 {
2770         // Close the workareas in all other views
2771         QList<int> const ids = guiApp->viewIds();
2772         for (int i = 0; i != ids.size(); ++i) {
2773                 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2774                         return false;
2775         }
2776
2777         // Close our own workareas
2778         if (!closeWorkAreaAll())
2779                 return false;
2780
2781         // Now close the hidden buffers. We prevent hidden buffers from being
2782         // dirty, so we can just close them.
2783         theBufferList().closeAll();
2784         return true;
2785 }
2786
2787
2788 bool GuiView::closeWorkAreaAll()
2789 {
2790         setCurrentWorkArea(currentMainWorkArea());
2791
2792         // We might be in a situation that there is still a tabWorkArea, but
2793         // there are no tabs anymore. This can happen when we get here after a
2794         // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2795         // many TabWorkArea's have no documents anymore.
2796         int empty_twa = 0;
2797
2798         // We have to call count() each time, because it can happen that
2799         // more than one splitter will disappear in one iteration (bug 5998).
2800         while (d.splitter_->count() > empty_twa) {
2801                 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2802
2803                 if (twa->count() == 0)
2804                         ++empty_twa;
2805                 else {
2806                         setCurrentWorkArea(twa->currentWorkArea());
2807                         if (!closeTabWorkArea(twa))
2808                                 return false;
2809                 }
2810         }
2811         return true;
2812 }
2813
2814
2815 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2816 {
2817         if (!wa)
2818                 return false;
2819
2820         Buffer & buf = wa->bufferView().buffer();
2821
2822         if (GuiViewPrivate::busyBuffers.contains(&buf)) {
2823                 Alert::warning(_("Close document"), 
2824                         _("Document could not be closed because it is being processed by LyX."));
2825                 return false;
2826         }
2827
2828         if (close_buffer)
2829                 return closeBuffer(buf);
2830         else {
2831                 if (!inMultiTabs(wa))
2832                         if (!saveBufferIfNeeded(buf, true))
2833                                 return false;
2834                 removeWorkArea(wa);
2835                 return true;
2836         }
2837 }
2838
2839
2840 bool GuiView::closeBuffer(Buffer & buf)
2841 {
2842         // If we are in a close_event all children will be closed in some time,
2843         // so no need to do it here. This will ensure that the children end up
2844         // in the session file in the correct order. If we close the master
2845         // buffer, we can close or release the child buffers here too.
2846         bool success = true;
2847         if (!closing_) {
2848                 ListOfBuffers clist = buf.getChildren();
2849                 ListOfBuffers::const_iterator it = clist.begin();
2850                 ListOfBuffers::const_iterator const bend = clist.end();
2851                 for (; it != bend; ++it) {
2852                         Buffer * child_buf = *it;
2853                         if (theBufferList().isOthersChild(&buf, child_buf)) {
2854                                 child_buf->setParent(0);
2855                                 continue;
2856                         }
2857
2858                         // FIXME: should we look in other tabworkareas?
2859                         // ANSWER: I don't think so. I've tested, and if the child is
2860                         // open in some other window, it closes without a problem.
2861                         GuiWorkArea * child_wa = workArea(*child_buf);
2862                         if (child_wa) {
2863                                 success = closeWorkArea(child_wa, true);
2864                                 if (!success)
2865                                         break;
2866                         } else {
2867                                 // In this case the child buffer is open but hidden.
2868                                 // It therefore should not (MUST NOT) be dirty!
2869                                 LATTEST(child_buf->isClean());
2870                                 theBufferList().release(child_buf);
2871                         }
2872                 }
2873         }
2874         if (success) {
2875                 // goto bookmark to update bookmark pit.
2876                 // FIXME: we should update only the bookmarks related to this buffer!
2877                 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2878                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2879                         guiApp->gotoBookmark(i+1, false, false);
2880
2881                 if (saveBufferIfNeeded(buf, false)) {
2882                         buf.removeAutosaveFile();
2883                         theBufferList().release(&buf);
2884                         return true;
2885                 }
2886         }
2887         // open all children again to avoid a crash because of dangling
2888         // pointers (bug 6603)
2889         buf.updateBuffer();
2890         return false;
2891 }
2892
2893
2894 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2895 {
2896         while (twa == d.currentTabWorkArea()) {
2897                 twa->setCurrentIndex(twa->count() - 1);
2898
2899                 GuiWorkArea * wa = twa->currentWorkArea();
2900                 Buffer & b = wa->bufferView().buffer();
2901
2902                 // We only want to close the buffer if the same buffer is not visible
2903                 // in another view, and if this is not a child and if we are closing
2904                 // a view (not a tabgroup).
2905                 bool const close_buffer =
2906                         !inOtherView(b) && !b.parent() && closing_;
2907
2908                 if (!closeWorkArea(wa, close_buffer))
2909                         return false;
2910         }
2911         return true;
2912 }
2913
2914
2915 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2916 {
2917         if (buf.isClean() || buf.paragraphs().empty())
2918                 return true;
2919
2920         // Switch to this Buffer.
2921         setBuffer(&buf);
2922
2923         docstring file;
2924         // FIXME: Unicode?
2925         if (buf.isUnnamed())
2926                 file = from_utf8(buf.fileName().onlyFileName());
2927         else
2928                 file = buf.fileName().displayName(30);
2929
2930         // Bring this window to top before asking questions.
2931         raise();
2932         activateWindow();
2933
2934         int ret;
2935         if (hiding && buf.isUnnamed()) {
2936                 docstring const text = bformat(_("The document %1$s has not been "
2937                                                  "saved yet.\n\nDo you want to save "
2938                                                  "the document?"), file);
2939                 ret = Alert::prompt(_("Save new document?"),
2940                         text, 0, 1, _("&Save"), _("&Cancel"));
2941                 if (ret == 1)
2942                         ++ret;
2943         } else {
2944                 docstring const text = bformat(_("The document %1$s has unsaved changes."
2945                         "\n\nDo you want to save the document or discard the changes?"), file);
2946                 ret = Alert::prompt(_("Save changed document?"),
2947                         text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2948         }
2949
2950         switch (ret) {
2951         case 0:
2952                 if (!saveBuffer(buf))
2953                         return false;
2954                 break;
2955         case 1:
2956                 // If we crash after this we could have no autosave file
2957                 // but I guess this is really improbable (Jug).
2958                 // Sometimes improbable things happen:
2959                 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2960                 // buf.removeAutosaveFile();
2961                 if (hiding)
2962                         // revert all changes
2963                         reloadBuffer(buf);
2964                 buf.markClean();
2965                 break;
2966         case 2:
2967                 return false;
2968         }
2969         return true;
2970 }
2971
2972
2973 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2974 {
2975         Buffer & buf = wa->bufferView().buffer();
2976
2977         for (int i = 0; i != d.splitter_->count(); ++i) {
2978                 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2979                 if (wa_ && wa_ != wa)
2980                         return true;
2981         }
2982         return inOtherView(buf);
2983 }
2984
2985
2986 bool GuiView::inOtherView(Buffer & buf)
2987 {
2988         QList<int> const ids = guiApp->viewIds();
2989
2990         for (int i = 0; i != ids.size(); ++i) {
2991                 if (id_ == ids[i])
2992                         continue;
2993
2994                 if (guiApp->view(ids[i]).workArea(buf))
2995                         return true;
2996         }
2997         return false;
2998 }
2999
3000
3001 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np, bool const move)
3002 {
3003         if (!documentBufferView())
3004                 return;
3005         
3006         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3007                 Buffer * const curbuf = &documentBufferView()->buffer();
3008                 int nwa = twa->count();
3009                 for (int i = 0; i < nwa; ++i) {
3010                         if (&workArea(i)->bufferView().buffer() == curbuf) {
3011                                 int next_index;
3012                                 if (np == NEXTBUFFER)
3013                                         next_index = (i == nwa - 1 ? 0 : i + 1);
3014                                 else
3015                                         next_index = (i == 0 ? nwa - 1 : i - 1);
3016                                 if (move)
3017                                         twa->moveTab(i, next_index);
3018                                 else
3019                                         setBuffer(&workArea(next_index)->bufferView().buffer());
3020                                 break;
3021                         }
3022                 }
3023         }
3024 }
3025
3026
3027 /// make sure the document is saved
3028 static bool ensureBufferClean(Buffer * buffer)
3029 {
3030         LASSERT(buffer, return false);
3031         if (buffer->isClean() && !buffer->isUnnamed())
3032                 return true;
3033
3034         docstring const file = buffer->fileName().displayName(30);
3035         docstring title;
3036         docstring text;
3037         if (!buffer->isUnnamed()) {
3038                 text = bformat(_("The document %1$s has unsaved "
3039                                                  "changes.\n\nDo you want to save "
3040                                                  "the document?"), file);
3041                 title = _("Save changed document?");
3042
3043         } else {
3044                 text = bformat(_("The document %1$s has not been "
3045                                                  "saved yet.\n\nDo you want to save "
3046                                                  "the document?"), file);
3047                 title = _("Save new document?");
3048         }
3049         int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
3050
3051         if (ret == 0)
3052                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
3053
3054         return buffer->isClean() && !buffer->isUnnamed();
3055 }
3056
3057
3058 bool GuiView::reloadBuffer(Buffer & buf)
3059 {
3060         Buffer::ReadStatus status = buf.reload();
3061         return status == Buffer::ReadSuccess;
3062 }
3063
3064
3065 void GuiView::checkExternallyModifiedBuffers()
3066 {
3067         BufferList::iterator bit = theBufferList().begin();
3068         BufferList::iterator const bend = theBufferList().end();
3069         for (; bit != bend; ++bit) {
3070                 Buffer * buf = *bit;
3071                 if (buf->fileName().exists()
3072                         && buf->isExternallyModified(Buffer::checksum_method)) {
3073                         docstring text = bformat(_("Document \n%1$s\n has been externally modified."
3074                                         " Reload now? Any local changes will be lost."),
3075                                         from_utf8(buf->absFileName()));
3076                         int const ret = Alert::prompt(_("Reload externally changed document?"),
3077                                                 text, 0, 1, _("&Reload"), _("&Cancel"));
3078                         if (!ret)
3079                                 reloadBuffer(*buf);
3080                 }
3081         }
3082 }
3083
3084
3085 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3086 {
3087         Buffer * buffer = documentBufferView()
3088                 ? &(documentBufferView()->buffer()) : 0;
3089
3090         switch (cmd.action()) {
3091         case LFUN_VC_REGISTER:
3092                 if (!buffer || !ensureBufferClean(buffer))
3093                         break;
3094                 if (!buffer->lyxvc().inUse()) {
3095                         if (buffer->lyxvc().registrer()) {
3096                                 reloadBuffer(*buffer);
3097                                 dr.clearMessageUpdate();
3098                         }
3099                 }
3100                 break;
3101
3102         case LFUN_VC_RENAME:
3103         case LFUN_VC_COPY: {
3104                 if (!buffer || !ensureBufferClean(buffer))
3105                         break;
3106                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3107                         if (buffer->lyxvc().isCheckInWithConfirmation()) {
3108                                 // Some changes are not yet committed.
3109                                 // We test here and not in getStatus(), since
3110                                 // this test is expensive.
3111                                 string log;
3112                                 LyXVC::CommandResult ret =
3113                                         buffer->lyxvc().checkIn(log);
3114                                 dr.setMessage(log);
3115                                 if (ret == LyXVC::ErrorCommand ||
3116                                     ret == LyXVC::VCSuccess)
3117                                         reloadBuffer(*buffer);
3118                                 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3119                                         frontend::Alert::error(
3120                                                 _("Revision control error."),
3121                                                 _("Document could not be checked in."));
3122                                         break;
3123                                 }
3124                         }
3125                         RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3126                                 LV_VC_RENAME : LV_VC_COPY;
3127                         renameBuffer(*buffer, cmd.argument(), kind);
3128                 }
3129                 break;
3130         }
3131
3132         case LFUN_VC_CHECK_IN:
3133                 if (!buffer || !ensureBufferClean(buffer))
3134                         break;
3135                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3136                         string log;
3137                         LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3138                         dr.setMessage(log);
3139                         // Only skip reloading if the checkin was cancelled or
3140                         // an error occurred before the real checkin VCS command
3141                         // was executed, since the VCS might have changed the
3142                         // file even if it could not checkin successfully.
3143                         if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3144                                 reloadBuffer(*buffer);
3145                 }
3146                 break;
3147
3148         case LFUN_VC_CHECK_OUT:
3149                 if (!buffer || !ensureBufferClean(buffer))
3150                         break;
3151                 if (buffer->lyxvc().inUse()) {
3152                         dr.setMessage(buffer->lyxvc().checkOut());
3153                         reloadBuffer(*buffer);
3154                 }
3155                 break;
3156
3157         case LFUN_VC_LOCKING_TOGGLE:
3158                 LASSERT(buffer, return);
3159                 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3160                         break;
3161                 if (buffer->lyxvc().inUse()) {
3162                         string res = buffer->lyxvc().lockingToggle();
3163                         if (res.empty()) {
3164                                 frontend::Alert::error(_("Revision control error."),
3165                                 _("Error when setting the locking property."));
3166                         } else {
3167                                 dr.setMessage(res);
3168                                 reloadBuffer(*buffer);
3169                         }
3170                 }
3171                 break;
3172
3173         case LFUN_VC_REVERT:
3174                 LASSERT(buffer, return);
3175                 if (buffer->lyxvc().revert()) {
3176                         reloadBuffer(*buffer);
3177                         dr.clearMessageUpdate();
3178                 }
3179                 break;
3180
3181         case LFUN_VC_UNDO_LAST:
3182                 LASSERT(buffer, return);
3183                 buffer->lyxvc().undoLast();
3184                 reloadBuffer(*buffer);
3185                 dr.clearMessageUpdate();
3186                 break;
3187
3188         case LFUN_VC_REPO_UPDATE:
3189                 LASSERT(buffer, return);
3190                 if (ensureBufferClean(buffer)) {
3191                         dr.setMessage(buffer->lyxvc().repoUpdate());
3192                         checkExternallyModifiedBuffers();
3193                 }
3194                 break;
3195
3196         case LFUN_VC_COMMAND: {
3197                 string flag = cmd.getArg(0);
3198                 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3199                         break;
3200                 docstring message;
3201                 if (contains(flag, 'M')) {
3202                         if (!Alert::askForText(message, _("LyX VC: Log Message")))
3203                                 break;
3204                 }
3205                 string path = cmd.getArg(1);
3206                 if (contains(path, "$$p") && buffer)
3207                         path = subst(path, "$$p", buffer->filePath());
3208                 LYXERR(Debug::LYXVC, "Directory: " << path);
3209                 FileName pp(path);
3210                 if (!pp.isReadableDirectory()) {
3211                         lyxerr << _("Directory is not accessible.") << endl;
3212                         break;
3213                 }
3214                 support::PathChanger p(pp);
3215
3216                 string command = cmd.getArg(2);
3217                 if (command.empty())
3218                         break;
3219                 if (buffer) {
3220                         command = subst(command, "$$i", buffer->absFileName());
3221                         command = subst(command, "$$p", buffer->filePath());
3222                 }
3223                 command = subst(command, "$$m", to_utf8(message));
3224                 LYXERR(Debug::LYXVC, "Command: " << command);
3225                 Systemcall one;
3226                 one.startscript(Systemcall::Wait, command);
3227
3228                 if (!buffer)
3229                         break;
3230                 if (contains(flag, 'I'))
3231                         buffer->markDirty();
3232                 if (contains(flag, 'R'))
3233                         reloadBuffer(*buffer);
3234
3235                 break;
3236                 }
3237
3238         case LFUN_VC_COMPARE: {
3239
3240                 if (cmd.argument().empty()) {
3241                         lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3242                         break;
3243                 }
3244
3245                 string rev1 = cmd.getArg(0);
3246                 string f1, f2;
3247
3248                 // f1
3249                 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3250                         break;
3251
3252                 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3253                         f2 = buffer->absFileName();
3254                 } else {
3255                         string rev2 = cmd.getArg(1);
3256                         if (rev2.empty())
3257                                 break;
3258                         // f2
3259                         if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3260                                 break;
3261                 }
3262
3263                 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3264                                         f1 << "\n"  << f2 << "\n" );
3265                 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3266                 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3267                 break;
3268         }
3269
3270         default:
3271                 break;
3272         }
3273 }
3274
3275
3276 void GuiView::openChildDocument(string const & fname)
3277 {
3278         LASSERT(documentBufferView(), return);
3279         Buffer & buffer = documentBufferView()->buffer();
3280         FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3281         documentBufferView()->saveBookmark(false);
3282         Buffer * child = 0;
3283         if (theBufferList().exists(filename)) {
3284                 child = theBufferList().getBuffer(filename);
3285                 setBuffer(child);
3286         } else {
3287                 message(bformat(_("Opening child document %1$s..."),
3288                         makeDisplayPath(filename.absFileName())));
3289                 child = loadDocument(filename, false);
3290         }
3291         // Set the parent name of the child document.
3292         // This makes insertion of citations and references in the child work,
3293         // when the target is in the parent or another child document.
3294         if (child)
3295                 child->setParent(&buffer);
3296 }
3297
3298
3299 bool GuiView::goToFileRow(string const & argument)
3300 {
3301         string file_name;
3302         int row;
3303         size_t i = argument.find_last_of(' ');
3304         if (i != string::npos) {
3305                 file_name = os::internal_path(trim(argument.substr(0, i)));
3306                 istringstream is(argument.substr(i + 1));
3307                 is >> row;
3308                 if (is.fail())
3309                         i = string::npos;
3310         }
3311         if (i == string::npos) {
3312                 LYXERR0("Wrong argument: " << argument);
3313                 return false;
3314         }
3315         Buffer * buf = 0;
3316         string const abstmp = package().temp_dir().absFileName();
3317         string const realtmp = package().temp_dir().realPath();
3318         // We have to use os::path_prefix_is() here, instead of
3319         // simply prefixIs(), because the file name comes from
3320         // an external application and may need case adjustment.
3321         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3322                 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3323                 // Needed by inverse dvi search. If it is a file
3324                 // in tmpdir, call the apropriated function.
3325                 // If tmpdir is a symlink, we may have the real
3326                 // path passed back, so we correct for that.
3327                 if (!prefixIs(file_name, abstmp))
3328                         file_name = subst(file_name, realtmp, abstmp);
3329                 buf = theBufferList().getBufferFromTmp(file_name);
3330         } else {
3331                 // Must replace extension of the file to be .lyx
3332                 // and get full path
3333                 FileName const s = fileSearch(string(),
3334                                                   support::changeExtension(file_name, ".lyx"), "lyx");
3335                 // Either change buffer or load the file
3336                 if (theBufferList().exists(s))
3337                         buf = theBufferList().getBuffer(s);
3338                 else if (s.exists()) {
3339                         buf = loadDocument(s);
3340                         if (!buf)
3341                                 return false;
3342                 } else {
3343                         message(bformat(
3344                                         _("File does not exist: %1$s"),
3345                                         makeDisplayPath(file_name)));
3346                         return false;
3347                 }
3348         }
3349         if (!buf) {
3350                 message(bformat(
3351                         _("No buffer for file: %1$s."),
3352                         makeDisplayPath(file_name))
3353                 );
3354                 return false;
3355         }
3356         setBuffer(buf);
3357         documentBufferView()->setCursorFromRow(row);
3358         return true;
3359 }
3360
3361
3362 template<class T>
3363 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3364 {
3365         Buffer::ExportStatus const status = func(format);
3366
3367         // the cloning operation will have produced a clone of the entire set of
3368         // documents, starting from the master. so we must delete those.
3369         Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3370         delete mbuf;
3371         busyBuffers.remove(orig);
3372         return status;
3373 }
3374
3375
3376 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3377 {
3378         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3379         return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3380 }
3381
3382
3383 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3384 {
3385         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3386         return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3387 }
3388
3389
3390 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3391 {
3392         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3393         return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3394 }
3395
3396
3397 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3398                            string const & argument,
3399                            Buffer const * used_buffer,
3400                            docstring const & msg,
3401                            Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3402                            Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3403                            Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3404 {
3405         if (!used_buffer)
3406                 return false;
3407
3408         string format = argument;
3409         if (format.empty())
3410                 format = used_buffer->params().getDefaultOutputFormat();
3411         processing_format = format;
3412         if (!msg.empty()) {
3413                 progress_->clearMessages();
3414                 gv_->message(msg);
3415         }
3416 #if EXPORT_in_THREAD
3417         GuiViewPrivate::busyBuffers.insert(used_buffer);
3418         Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3419         if (!cloned_buffer) {
3420                 Alert::error(_("Export Error"),
3421                              _("Error cloning the Buffer."));
3422                 return false;
3423         }
3424         QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3425                                 asyncFunc,
3426                                 used_buffer,
3427                                 cloned_buffer,
3428                                 format);
3429         setPreviewFuture(f);
3430         last_export_format = used_buffer->params().bufferFormat();
3431         (void) syncFunc;
3432         (void) previewFunc;
3433         // We are asynchronous, so we don't know here anything about the success
3434         return true;
3435 #else
3436         Buffer::ExportStatus status;
3437         if (syncFunc) {
3438                 // TODO check here if it breaks exporting with Qt < 4.4
3439                 status = (used_buffer->*syncFunc)(format, true);
3440         } else if (previewFunc) {
3441                 status = (used_buffer->*previewFunc)(format); 
3442         } else
3443                 return false;
3444         handleExportStatus(gv_, status, format);
3445         (void) asyncFunc;
3446         return (status == Buffer::ExportSuccess 
3447                         || status == Buffer::PreviewSuccess);
3448 #endif
3449 }
3450
3451 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3452 {
3453         BufferView * bv = currentBufferView();
3454         LASSERT(bv, return);
3455
3456         // Let the current BufferView dispatch its own actions.
3457         bv->dispatch(cmd, dr);
3458         if (dr.dispatched())
3459                 return;
3460
3461         // Try with the document BufferView dispatch if any.
3462         BufferView * doc_bv = documentBufferView();
3463         if (doc_bv && doc_bv != bv) {
3464                 doc_bv->dispatch(cmd, dr);
3465                 if (dr.dispatched())
3466                         return;
3467         }
3468
3469         // Then let the current Cursor dispatch its own actions.
3470         bv->cursor().dispatch(cmd);
3471
3472         // update completion. We do it here and not in
3473         // processKeySym to avoid another redraw just for a
3474         // changed inline completion
3475         if (cmd.origin() == FuncRequest::KEYBOARD) {
3476                 if (cmd.action() == LFUN_SELF_INSERT
3477                         || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3478                         updateCompletion(bv->cursor(), true, true);
3479                 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3480                         updateCompletion(bv->cursor(), false, true);
3481                 else
3482                         updateCompletion(bv->cursor(), false, false);
3483         }
3484
3485         dr = bv->cursor().result();
3486 }
3487
3488
3489 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3490 {
3491         BufferView * bv = currentBufferView();
3492         // By default we won't need any update.
3493         dr.screenUpdate(Update::None);
3494         // assume cmd will be dispatched
3495         dr.dispatched(true);
3496
3497         Buffer * doc_buffer = documentBufferView()
3498                 ? &(documentBufferView()->buffer()) : 0;
3499
3500         if (cmd.origin() == FuncRequest::TOC) {
3501                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3502                 // FIXME: do we need to pass a DispatchResult object here?
3503                 toc->doDispatch(bv->cursor(), cmd);
3504                 return;
3505         }
3506
3507         string const argument = to_utf8(cmd.argument());
3508
3509         switch(cmd.action()) {
3510                 case LFUN_BUFFER_CHILD_OPEN:
3511                         openChildDocument(to_utf8(cmd.argument()));
3512                         break;
3513
3514                 case LFUN_BUFFER_IMPORT:
3515                         importDocument(to_utf8(cmd.argument()));
3516                         break;
3517
3518                 case LFUN_BUFFER_EXPORT: {
3519                         if (!doc_buffer)
3520                                 break;
3521                         FileName target_dir = doc_buffer->fileName().onlyPath();
3522                         string const dest = cmd.getArg(1);
3523                         if (!dest.empty() && FileName::isAbsolute(dest))
3524                                 target_dir = FileName(support::onlyPath(dest));
3525                         // GCC only sees strfwd.h when building merged
3526                         if (::lyx::operator==(cmd.argument(), "custom")) {
3527                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3528                                 break;
3529                         }
3530                         if (!target_dir.isDirWritable()) {
3531                                 exportBufferAs(*doc_buffer, cmd.argument());
3532                                 break;
3533                         }
3534                         /* TODO/Review: Is it a problem to also export the children?
3535                                         See the update_unincluded flag */
3536                         d.asyncBufferProcessing(argument,
3537                                                 doc_buffer,
3538                                                 _("Exporting ..."),
3539                                                 &GuiViewPrivate::exportAndDestroy,
3540                                                 &Buffer::doExport,
3541                                                 0);
3542                         // TODO Inform user about success
3543                         break;
3544                 }
3545
3546                 case LFUN_BUFFER_EXPORT_AS: {
3547                         LASSERT(doc_buffer, break);
3548                         docstring f = cmd.argument();
3549                         if (f.empty())
3550                                 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3551                         exportBufferAs(*doc_buffer, f);
3552                         break;
3553                 }
3554
3555                 case LFUN_BUFFER_UPDATE: {
3556                         d.asyncBufferProcessing(argument,
3557                                                 doc_buffer,
3558                                                 _("Exporting ..."),
3559                                                 &GuiViewPrivate::compileAndDestroy,
3560                                                 &Buffer::doExport,
3561                                                 0);
3562                         break;
3563                 }
3564                 case LFUN_BUFFER_VIEW: {
3565                         d.asyncBufferProcessing(argument,
3566                                                 doc_buffer,
3567                                                 _("Previewing ..."),
3568                                                 &GuiViewPrivate::previewAndDestroy,
3569                                                 0,
3570                                                 &Buffer::preview);
3571                         break;
3572                 }
3573                 case LFUN_MASTER_BUFFER_UPDATE: {
3574                         d.asyncBufferProcessing(argument,
3575                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3576                                                 docstring(),
3577                                                 &GuiViewPrivate::compileAndDestroy,
3578                                                 &Buffer::doExport,
3579                                                 0);
3580                         break;
3581                 }
3582                 case LFUN_MASTER_BUFFER_VIEW: {
3583                         d.asyncBufferProcessing(argument,
3584                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3585                                                 docstring(),
3586                                                 &GuiViewPrivate::previewAndDestroy,
3587                                                 0, &Buffer::preview);
3588                         break;
3589                 }
3590                 case LFUN_BUFFER_SWITCH: {
3591                         string const file_name = to_utf8(cmd.argument());
3592                         if (!FileName::isAbsolute(file_name)) {
3593                                 dr.setError(true);
3594                                 dr.setMessage(_("Absolute filename expected."));
3595                                 break;
3596                         }
3597
3598                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3599                         if (!buffer) {
3600                                 dr.setError(true);
3601                                 dr.setMessage(_("Document not loaded"));
3602                                 break;
3603                         }
3604
3605                         // Do we open or switch to the buffer in this view ?
3606                         if (workArea(*buffer)
3607                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3608                                 setBuffer(buffer);
3609                                 break;
3610                         }
3611
3612                         // Look for the buffer in other views
3613                         QList<int> const ids = guiApp->viewIds();
3614                         int i = 0;
3615                         for (; i != ids.size(); ++i) {
3616                                 GuiView & gv = guiApp->view(ids[i]);
3617                                 if (gv.workArea(*buffer)) {
3618                                         gv.raise();
3619                                         gv.activateWindow();
3620                                         gv.setFocus();
3621                                         gv.setBuffer(buffer);
3622                                         break;
3623                                 }
3624                         }
3625
3626                         // If necessary, open a new window as a last resort
3627                         if (i == ids.size()) {
3628                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3629                                 lyx::dispatch(cmd);
3630                         }
3631                         break;
3632                 }
3633
3634                 case LFUN_BUFFER_NEXT:
3635                         gotoNextOrPreviousBuffer(NEXTBUFFER, false);
3636                         break;
3637
3638                 case LFUN_BUFFER_MOVE_NEXT:
3639                         gotoNextOrPreviousBuffer(NEXTBUFFER, true);
3640                         break;
3641
3642                 case LFUN_BUFFER_PREVIOUS:
3643                         gotoNextOrPreviousBuffer(PREVBUFFER, false);
3644                         break;
3645
3646                 case LFUN_BUFFER_MOVE_PREVIOUS:
3647                         gotoNextOrPreviousBuffer(PREVBUFFER, true);
3648                         break;
3649
3650                 case LFUN_COMMAND_EXECUTE: {
3651                         command_execute_ = true;
3652                         break;
3653                 }
3654                 case LFUN_DROP_LAYOUTS_CHOICE:
3655                         d.layout_->showPopup();
3656                         break;
3657
3658                 case LFUN_MENU_OPEN:
3659                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3660                                 menu->exec(QCursor::pos());
3661                         break;
3662
3663                 case LFUN_FILE_INSERT:
3664                         insertLyXFile(cmd.argument());
3665                         break;
3666
3667                 case LFUN_FILE_INSERT_PLAINTEXT:
3668                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3669                         string const fname = to_utf8(cmd.argument());
3670                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3671                                 dr.setMessage(_("Absolute filename expected."));
3672                                 break;
3673                         }
3674                         
3675                         FileName filename(fname);
3676                         if (fname.empty()) {
3677                                 FileDialog dlg(qt_("Select file to insert"));
3678
3679                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3680                                         QStringList(qt_("All Files (*)")));
3681                                 
3682                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3683                                         dr.setMessage(_("Canceled."));
3684                                         break;
3685                                 }
3686
3687                                 filename.set(fromqstr(result.second));
3688                         }
3689
3690                         if (bv) {
3691                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3692                                 bv->dispatch(new_cmd, dr);
3693                         }
3694                         break;
3695                 }
3696
3697                 case LFUN_BUFFER_RELOAD: {
3698                         LASSERT(doc_buffer, break);
3699
3700                         int ret = 0;
3701                         if (!doc_buffer->isClean()) {
3702                                 docstring const file =
3703                                         makeDisplayPath(doc_buffer->absFileName(), 20);
3704                                 docstring text = bformat(_("Any changes will be lost. "
3705                                         "Are you sure you want to revert to the saved version "
3706                                         "of the document %1$s?"), file);
3707                                 ret = Alert::prompt(_("Revert to saved document?"),
3708                                         text, 1, 1, _("&Revert"), _("&Cancel"));
3709                         }
3710
3711                         if (ret == 0) {
3712                                 doc_buffer->markClean();
3713                                 reloadBuffer(*doc_buffer);
3714                                 dr.forceBufferUpdate();
3715                         }
3716                         break;
3717                 }
3718
3719                 case LFUN_BUFFER_WRITE:
3720                         LASSERT(doc_buffer, break);
3721                         saveBuffer(*doc_buffer);
3722                         break;
3723
3724                 case LFUN_BUFFER_WRITE_AS:
3725                         LASSERT(doc_buffer, break);
3726                         renameBuffer(*doc_buffer, cmd.argument());
3727                         break;
3728
3729                 case LFUN_BUFFER_WRITE_ALL: {
3730                         Buffer * first = theBufferList().first();
3731                         if (!first)
3732                                 break;
3733                         message(_("Saving all documents..."));
3734                         // We cannot use a for loop as the buffer list cycles.
3735                         Buffer * b = first;
3736                         do {
3737                                 if (!b->isClean()) {
3738                                         saveBuffer(*b);
3739                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3740                                 }
3741                                 b = theBufferList().next(b);
3742                         } while (b != first);
3743                         dr.setMessage(_("All documents saved."));
3744                         break;
3745                 }
3746
3747                 case LFUN_BUFFER_CLOSE:
3748                         closeBuffer();
3749                         break;
3750
3751                 case LFUN_BUFFER_CLOSE_ALL:
3752                         closeBufferAll();
3753                         break;
3754
3755                 case LFUN_TOOLBAR_TOGGLE: {
3756                         string const name = cmd.getArg(0);
3757                         if (GuiToolbar * t = toolbar(name))
3758                                 t->toggle();
3759                         break;
3760                 }
3761
3762                 case LFUN_DIALOG_UPDATE: {
3763                         string const name = to_utf8(cmd.argument());
3764                         if (name == "prefs" || name == "document")
3765                                 updateDialog(name, string());
3766                         else if (name == "paragraph")
3767                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3768                         else if (currentBufferView()) {
3769                                 Inset * inset = currentBufferView()->editedInset(name);
3770                                 // Can only update a dialog connected to an existing inset
3771                                 if (inset) {
3772                                         // FIXME: get rid of this indirection; GuiView ask the inset
3773                                         // if he is kind enough to update itself...
3774                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3775                                         //FIXME: pass DispatchResult here?
3776                                         inset->dispatch(currentBufferView()->cursor(), fr);
3777                                 }
3778                         }
3779                         break;
3780                 }
3781
3782                 case LFUN_DIALOG_TOGGLE: {
3783                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3784                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3785                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
3786                         break;
3787                 }
3788
3789                 case LFUN_DIALOG_DISCONNECT_INSET:
3790                         disconnectDialog(to_utf8(cmd.argument()));
3791                         break;
3792
3793                 case LFUN_DIALOG_HIDE: {
3794                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3795                         break;
3796                 }
3797
3798                 case LFUN_DIALOG_SHOW: {
3799                         string const name = cmd.getArg(0);
3800                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3801
3802                         if (name == "character") {
3803                                 data = freefont2string();
3804                                 if (!data.empty())
3805                                         showDialog("character", data);
3806                         } else if (name == "latexlog") {
3807                                 Buffer::LogType type;
3808                                 string const logfile = doc_buffer->logName(&type);
3809                                 switch (type) {
3810                                 case Buffer::latexlog:
3811                                         data = "latex ";
3812                                         break;
3813                                 case Buffer::buildlog:
3814                                         data = "literate ";
3815                                         break;
3816                                 }
3817                                 data += Lexer::quoteString(logfile);
3818                                 showDialog("log", data);
3819                         } else if (name == "vclog") {
3820                                 string const data = "vc " +
3821                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3822                                 showDialog("log", data);
3823                         } else if (name == "symbols") {
3824                                 data = bv->cursor().getEncoding()->name();
3825                                 if (!data.empty())
3826                                         showDialog("symbols", data);
3827                         // bug 5274
3828                         } else if (name == "prefs" && isFullScreen()) {
3829                                 lfunUiToggle("fullscreen");
3830                                 showDialog("prefs", data);
3831                         } else
3832                                 showDialog(name, data);
3833                         break;
3834                 }
3835
3836                 case LFUN_MESSAGE:
3837                         dr.setMessage(cmd.argument());
3838                         break;
3839
3840                 case LFUN_UI_TOGGLE: {
3841                         string arg = cmd.getArg(0);
3842                         if (!lfunUiToggle(arg)) {
3843                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3844                                 dr.setMessage(bformat(msg, from_utf8(arg)));
3845                         }
3846                         // Make sure the keyboard focus stays in the work area.
3847                         setFocus();
3848                         break;
3849                 }
3850
3851                 case LFUN_VIEW_SPLIT: {
3852                         LASSERT(doc_buffer, break);
3853                         string const orientation = cmd.getArg(0);
3854                         d.splitter_->setOrientation(orientation == "vertical"
3855                                 ? Qt::Vertical : Qt::Horizontal);
3856                         TabWorkArea * twa = addTabWorkArea();
3857                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3858                         setCurrentWorkArea(wa);
3859                         break;
3860                 }
3861                 case LFUN_TAB_GROUP_CLOSE:
3862                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3863                                 closeTabWorkArea(twa);
3864                                 d.current_work_area_ = 0;
3865                                 twa = d.currentTabWorkArea();
3866                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3867                                 if (twa) {
3868                                         // Make sure the work area is up to date.
3869                                         setCurrentWorkArea(twa->currentWorkArea());
3870                                 } else {
3871                                         setCurrentWorkArea(0);
3872                                 }
3873                         }
3874                         break;
3875
3876                 case LFUN_VIEW_CLOSE:
3877                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3878                                 closeWorkArea(twa->currentWorkArea());
3879                                 d.current_work_area_ = 0;
3880                                 twa = d.currentTabWorkArea();
3881                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3882                                 if (twa) {
3883                                         // Make sure the work area is up to date.
3884                                         setCurrentWorkArea(twa->currentWorkArea());
3885                                 } else {
3886                                         setCurrentWorkArea(0);
3887                                 }
3888                         }
3889                         break;
3890
3891                 case LFUN_COMPLETION_INLINE:
3892                         if (d.current_work_area_)
3893                                 d.current_work_area_->completer().showInline();
3894                         break;
3895
3896                 case LFUN_COMPLETION_POPUP:
3897                         if (d.current_work_area_)
3898                                 d.current_work_area_->completer().showPopup();
3899                         break;
3900
3901
3902                 case LFUN_COMPLETE:
3903                         if (d.current_work_area_)
3904                                 d.current_work_area_->completer().tab();
3905                         break;
3906
3907                 case LFUN_COMPLETION_CANCEL:
3908                         if (d.current_work_area_) {
3909                                 if (d.current_work_area_->completer().popupVisible())
3910                                         d.current_work_area_->completer().hidePopup();
3911                                 else
3912                                         d.current_work_area_->completer().hideInline();
3913                         }
3914                         break;
3915
3916                 case LFUN_COMPLETION_ACCEPT:
3917                         if (d.current_work_area_)
3918                                 d.current_work_area_->completer().activate();
3919                         break;
3920
3921                 case LFUN_BUFFER_ZOOM_IN:
3922                 case LFUN_BUFFER_ZOOM_OUT:
3923                         if (cmd.argument().empty()) {
3924                                 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3925                                         lyxrc.zoom += 20;
3926                                 else
3927                                         lyxrc.zoom -= 20;
3928                         } else
3929                                 lyxrc.zoom += convert<int>(cmd.argument());
3930
3931                         if (lyxrc.zoom < 10)
3932                                 lyxrc.zoom = 10;
3933
3934                         // The global QPixmapCache is used in GuiPainter to cache text
3935                         // painting so we must reset it.
3936                         QPixmapCache::clear();
3937                         guiApp->fontLoader().update();
3938                         lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3939                         break;
3940
3941                 case LFUN_VC_REGISTER:
3942                 case LFUN_VC_RENAME:
3943                 case LFUN_VC_COPY:
3944                 case LFUN_VC_CHECK_IN:
3945                 case LFUN_VC_CHECK_OUT:
3946                 case LFUN_VC_REPO_UPDATE:
3947                 case LFUN_VC_LOCKING_TOGGLE:
3948                 case LFUN_VC_REVERT:
3949                 case LFUN_VC_UNDO_LAST:
3950                 case LFUN_VC_COMMAND:
3951                 case LFUN_VC_COMPARE:
3952                         dispatchVC(cmd, dr);
3953                         break;
3954
3955                 case LFUN_SERVER_GOTO_FILE_ROW:
3956                         goToFileRow(to_utf8(cmd.argument()));
3957                         break;
3958
3959                 case LFUN_FORWARD_SEARCH: {
3960                         Buffer const * doc_master = doc_buffer->masterBuffer();
3961                         FileName const path(doc_master->temppath());
3962                         string const texname = doc_master->isChild(doc_buffer)
3963                                 ? DocFileName(changeExtension(
3964                                         doc_buffer->absFileName(),
3965                                                 "tex")).mangledFileName()
3966                                 : doc_buffer->latexName();
3967                         string const fulltexname = 
3968                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3969                         string const mastername =
3970                                 removeExtension(doc_master->latexName());
3971                         FileName const dviname(addName(path.absFileName(),
3972                                         addExtension(mastername, "dvi")));
3973                         FileName const pdfname(addName(path.absFileName(),
3974                                         addExtension(mastername, "pdf")));
3975                         bool const have_dvi = dviname.exists();
3976                         bool const have_pdf = pdfname.exists();
3977                         if (!have_dvi && !have_pdf) {
3978                                 dr.setMessage(_("Please, preview the document first."));
3979                                 break;
3980                         }
3981                         string outname = dviname.onlyFileName();
3982                         string command = lyxrc.forward_search_dvi;
3983                         if (!have_dvi || (have_pdf &&
3984                             pdfname.lastModified() > dviname.lastModified())) {
3985                                 outname = pdfname.onlyFileName();
3986                                 command = lyxrc.forward_search_pdf;
3987                         }
3988
3989                         DocIterator cur = bv->cursor();
3990                         int row = doc_buffer->texrow().rowFromDocIterator(cur).first;
3991                         LYXERR(Debug::ACTION, "Forward search: row:" << row
3992                                    << " cur:" << cur);
3993                         if (row == -1 || command.empty()) {
3994                                 dr.setMessage(_("Couldn't proceed."));
3995                                 break;
3996                         }
3997                         string texrow = convert<string>(row);
3998
3999                         command = subst(command, "$$n", texrow);
4000                         command = subst(command, "$$f", fulltexname);
4001                         command = subst(command, "$$t", texname);
4002                         command = subst(command, "$$o", outname);
4003
4004                         PathChanger p(path);
4005                         Systemcall one;
4006                         one.startscript(Systemcall::DontWait, command);
4007                         break;
4008                 }
4009
4010                 case LFUN_SPELLING_CONTINUOUSLY:
4011                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
4012                         dr.screenUpdate(Update::Force | Update::FitCursor);
4013                         break;
4014
4015                 default:
4016                         // The LFUN must be for one of BufferView, Buffer or Cursor;
4017                         // let's try that:
4018                         dispatchToBufferView(cmd, dr);
4019                         break;
4020         }
4021
4022         // Part of automatic menu appearance feature.
4023         if (isFullScreen()) {
4024                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
4025                         menuBar()->hide();
4026         }
4027
4028         // Need to update bv because many LFUNs here might have destroyed it
4029         bv = currentBufferView();
4030
4031         // Clear non-empty selections
4032         // (e.g. from a "char-forward-select" followed by "char-backward-select")
4033         if (bv) {
4034                 Cursor & cur = bv->cursor();
4035                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
4036                         cur.clearSelection();
4037                 }
4038         }
4039 }
4040
4041
4042 bool GuiView::lfunUiToggle(string const & ui_component)
4043 {
4044         if (ui_component == "scrollbar") {
4045                 // hide() is of no help
4046                 if (d.current_work_area_->verticalScrollBarPolicy() ==
4047                         Qt::ScrollBarAlwaysOff)
4048
4049                         d.current_work_area_->setVerticalScrollBarPolicy(
4050                                 Qt::ScrollBarAsNeeded);
4051                 else
4052                         d.current_work_area_->setVerticalScrollBarPolicy(
4053                                 Qt::ScrollBarAlwaysOff);
4054         } else if (ui_component == "statusbar") {
4055                 statusBar()->setVisible(!statusBar()->isVisible());
4056         } else if (ui_component == "menubar") {
4057                 menuBar()->setVisible(!menuBar()->isVisible());
4058         } else
4059         if (ui_component == "frame") {
4060                 int l, t, r, b;
4061                 getContentsMargins(&l, &t, &r, &b);
4062                 //are the frames in default state?
4063                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
4064                 if (l == 0) {
4065                         setContentsMargins(-2, -2, -2, -2);
4066                 } else {
4067                         setContentsMargins(0, 0, 0, 0);
4068                 }
4069         } else
4070         if (ui_component == "fullscreen") {
4071                 toggleFullScreen();
4072         } else
4073                 return false;
4074         return true;
4075 }
4076
4077
4078 void GuiView::toggleFullScreen()
4079 {
4080         if (isFullScreen()) {
4081                 for (int i = 0; i != d.splitter_->count(); ++i)
4082                         d.tabWorkArea(i)->setFullScreen(false);
4083                 setContentsMargins(0, 0, 0, 0);
4084                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4085                 restoreLayout();
4086                 menuBar()->show();
4087                 statusBar()->show();
4088         } else {
4089                 // bug 5274
4090                 hideDialogs("prefs", 0);
4091                 for (int i = 0; i != d.splitter_->count(); ++i)
4092                         d.tabWorkArea(i)->setFullScreen(true);
4093                 setContentsMargins(-2, -2, -2, -2);
4094                 saveLayout();
4095                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4096                 if (lyxrc.full_screen_statusbar)
4097                         statusBar()->hide();
4098                 if (lyxrc.full_screen_menubar)
4099                         menuBar()->hide();
4100                 if (lyxrc.full_screen_toolbars) {
4101                         ToolbarMap::iterator end = d.toolbars_.end();
4102                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4103                                 it->second->hide();
4104                 }
4105         }
4106
4107         // give dialogs like the TOC a chance to adapt
4108         updateDialogs();
4109 }
4110
4111
4112 Buffer const * GuiView::updateInset(Inset const * inset)
4113 {
4114         if (!inset)
4115                 return 0;
4116
4117         Buffer const * inset_buffer = &(inset->buffer());
4118
4119         for (int i = 0; i != d.splitter_->count(); ++i) {
4120                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4121                 if (!wa)
4122                         continue;
4123                 Buffer const * buffer = &(wa->bufferView().buffer());
4124                 if (inset_buffer == buffer)
4125                         wa->scheduleRedraw();
4126         }
4127         return inset_buffer;
4128 }
4129
4130
4131 void GuiView::restartCursor()
4132 {
4133         /* When we move around, or type, it's nice to be able to see
4134          * the cursor immediately after the keypress.
4135          */
4136         if (d.current_work_area_)
4137                 d.current_work_area_->startBlinkingCursor();
4138
4139         // Take this occasion to update the other GUI elements.
4140         updateDialogs();
4141         updateStatusBar();
4142 }
4143
4144
4145 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4146 {
4147         if (d.current_work_area_)
4148                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4149 }
4150
4151 namespace {
4152
4153 // This list should be kept in sync with the list of insets in
4154 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4155 // dialog should have the same name as the inset.
4156 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4157 // docs in LyXAction.cpp.
4158
4159 char const * const dialognames[] = {
4160
4161 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4162 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4163 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4164 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4165 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4166 "nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
4167 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4168 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4169
4170 char const * const * const end_dialognames =
4171         dialognames + (sizeof(dialognames) / sizeof(char *));
4172
4173 class cmpCStr {
4174 public:
4175         cmpCStr(char const * name) : name_(name) {}
4176         bool operator()(char const * other) {
4177                 return strcmp(other, name_) == 0;
4178         }
4179 private:
4180         char const * name_;
4181 };
4182
4183
4184 bool isValidName(string const & name)
4185 {
4186         return find_if(dialognames, end_dialognames,
4187                                 cmpCStr(name.c_str())) != end_dialognames;
4188 }
4189
4190 } // namespace anon
4191
4192
4193 void GuiView::resetDialogs()
4194 {
4195         // Make sure that no LFUN uses any GuiView.
4196         guiApp->setCurrentView(0);
4197         saveLayout();
4198         saveUISettings();
4199         menuBar()->clear();
4200         constructToolbars();
4201         guiApp->menus().fillMenuBar(menuBar(), this, false);
4202         d.layout_->updateContents(true);
4203         // Now update controls with current buffer.
4204         guiApp->setCurrentView(this);
4205         restoreLayout();
4206         restartCursor();
4207 }
4208
4209
4210 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4211 {
4212         if (!isValidName(name))
4213                 return 0;
4214
4215         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4216
4217         if (it != d.dialogs_.end()) {
4218                 if (hide_it)
4219                         it->second->hideView();
4220                 return it->second.get();
4221         }
4222
4223         Dialog * dialog = build(name);
4224         d.dialogs_[name].reset(dialog);
4225         if (lyxrc.allow_geometry_session)
4226                 dialog->restoreSession();
4227         if (hide_it)
4228                 dialog->hideView();
4229         return dialog;
4230 }
4231
4232
4233 void GuiView::showDialog(string const & name, string const & data,
4234         Inset * inset)
4235 {
4236         triggerShowDialog(toqstr(name), toqstr(data), inset);
4237 }
4238
4239
4240 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4241         Inset * inset)
4242 {
4243         if (d.in_show_)
4244                 return;
4245
4246         const string name = fromqstr(qname);
4247         const string data = fromqstr(qdata);
4248
4249         d.in_show_ = true;
4250         try {
4251                 Dialog * dialog = findOrBuild(name, false);
4252                 if (dialog) {
4253                         bool const visible = dialog->isVisibleView();
4254                         dialog->showData(data);
4255                         if (inset && currentBufferView())
4256                                 currentBufferView()->editInset(name, inset);
4257                         // We only set the focus to the new dialog if it was not yet
4258                         // visible in order not to change the existing previous behaviour
4259                         if (visible) {
4260                                 // activateWindow is needed for floating dockviews
4261                                 dialog->asQWidget()->raise();
4262                                 dialog->asQWidget()->activateWindow();
4263                                 dialog->asQWidget()->setFocus();
4264                         }
4265                 }
4266         }
4267         catch (ExceptionMessage const & ex) {
4268                 d.in_show_ = false;
4269                 throw ex;
4270         }
4271         d.in_show_ = false;
4272 }
4273
4274
4275 bool GuiView::isDialogVisible(string const & name) const
4276 {
4277         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4278         if (it == d.dialogs_.end())
4279                 return false;
4280         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4281 }
4282
4283
4284 void GuiView::hideDialog(string const & name, Inset * inset)
4285 {
4286         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4287         if (it == d.dialogs_.end())
4288                 return;
4289
4290         if (inset) {
4291                 if (!currentBufferView())
4292                         return;
4293                 if (inset != currentBufferView()->editedInset(name))
4294                         return;
4295         }
4296
4297         Dialog * const dialog = it->second.get();
4298         if (dialog->isVisibleView())
4299                 dialog->hideView();
4300         if (currentBufferView())
4301                 currentBufferView()->editInset(name, 0);
4302 }
4303
4304
4305 void GuiView::disconnectDialog(string const & name)
4306 {
4307         if (!isValidName(name))
4308                 return;
4309         if (currentBufferView())
4310                 currentBufferView()->editInset(name, 0);
4311 }
4312
4313
4314 void GuiView::hideAll() const
4315 {
4316         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4317         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4318
4319         for(; it != end; ++it)
4320                 it->second->hideView();
4321 }
4322
4323
4324 void GuiView::updateDialogs()
4325 {
4326         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4327         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4328
4329         for(; it != end; ++it) {
4330                 Dialog * dialog = it->second.get();
4331                 if (dialog) {
4332                         if (dialog->needBufferOpen() && !documentBufferView())
4333                                 hideDialog(fromqstr(dialog->name()), 0);
4334                         else if (dialog->isVisibleView())
4335                                 dialog->checkStatus();
4336                 }
4337         }
4338         updateToolbars();
4339         updateLayoutList();
4340 }
4341
4342 Dialog * createDialog(GuiView & lv, string const & name);
4343
4344 // will be replaced by a proper factory...
4345 Dialog * createGuiAbout(GuiView & lv);
4346 Dialog * createGuiBibtex(GuiView & lv);
4347 Dialog * createGuiChanges(GuiView & lv);
4348 Dialog * createGuiCharacter(GuiView & lv);
4349 Dialog * createGuiCitation(GuiView & lv);
4350 Dialog * createGuiCompare(GuiView & lv);
4351 Dialog * createGuiCompareHistory(GuiView & lv);
4352 Dialog * createGuiDelimiter(GuiView & lv);
4353 Dialog * createGuiDocument(GuiView & lv);
4354 Dialog * createGuiErrorList(GuiView & lv);
4355 Dialog * createGuiExternal(GuiView & lv);
4356 Dialog * createGuiGraphics(GuiView & lv);
4357 Dialog * createGuiInclude(GuiView & lv);
4358 Dialog * createGuiIndex(GuiView & lv);
4359 Dialog * createGuiListings(GuiView & lv);
4360 Dialog * createGuiLog(GuiView & lv);
4361 Dialog * createGuiMathMatrix(GuiView & lv);
4362 Dialog * createGuiNote(GuiView & lv);
4363 Dialog * createGuiParagraph(GuiView & lv);
4364 Dialog * createGuiPhantom(GuiView & lv);
4365 Dialog * createGuiPreferences(GuiView & lv);
4366 Dialog * createGuiPrint(GuiView & lv);
4367 Dialog * createGuiPrintindex(GuiView & lv);
4368 Dialog * createGuiRef(GuiView & lv);
4369 Dialog * createGuiSearch(GuiView & lv);
4370 Dialog * createGuiSearchAdv(GuiView & lv);
4371 Dialog * createGuiSendTo(GuiView & lv);
4372 Dialog * createGuiShowFile(GuiView & lv);
4373 Dialog * createGuiSpellchecker(GuiView & lv);
4374 Dialog * createGuiSymbols(GuiView & lv);
4375 Dialog * createGuiTabularCreate(GuiView & lv);
4376 Dialog * createGuiTexInfo(GuiView & lv);
4377 Dialog * createGuiToc(GuiView & lv);
4378 Dialog * createGuiThesaurus(GuiView & lv);
4379 Dialog * createGuiViewSource(GuiView & lv);
4380 Dialog * createGuiWrap(GuiView & lv);
4381 Dialog * createGuiProgressView(GuiView & lv);
4382
4383
4384
4385 Dialog * GuiView::build(string const & name)
4386 {
4387         LASSERT(isValidName(name), return 0);
4388
4389         Dialog * dialog = createDialog(*this, name);
4390         if (dialog)
4391                 return dialog;
4392
4393         if (name == "aboutlyx")
4394                 return createGuiAbout(*this);
4395         if (name == "bibtex")
4396                 return createGuiBibtex(*this);
4397         if (name == "changes")
4398                 return createGuiChanges(*this);
4399         if (name == "character")
4400                 return createGuiCharacter(*this);
4401         if (name == "citation")
4402                 return createGuiCitation(*this);
4403         if (name == "compare")
4404                 return createGuiCompare(*this);
4405         if (name == "comparehistory")
4406                 return createGuiCompareHistory(*this);
4407         if (name == "document")
4408                 return createGuiDocument(*this);
4409         if (name == "errorlist")
4410                 return createGuiErrorList(*this);
4411         if (name == "external")
4412                 return createGuiExternal(*this);
4413         if (name == "file")
4414                 return createGuiShowFile(*this);
4415         if (name == "findreplace")
4416                 return createGuiSearch(*this);
4417         if (name == "findreplaceadv")
4418                 return createGuiSearchAdv(*this);
4419         if (name == "graphics")
4420                 return createGuiGraphics(*this);
4421         if (name == "include")
4422                 return createGuiInclude(*this);
4423         if (name == "index")
4424                 return createGuiIndex(*this);
4425         if (name == "index_print")
4426                 return createGuiPrintindex(*this);
4427         if (name == "listings")
4428                 return createGuiListings(*this);
4429         if (name == "log")
4430                 return createGuiLog(*this);
4431         if (name == "mathdelimiter")
4432                 return createGuiDelimiter(*this);
4433         if (name == "mathmatrix")
4434                 return createGuiMathMatrix(*this);
4435         if (name == "note")
4436                 return createGuiNote(*this);
4437         if (name == "paragraph")
4438                 return createGuiParagraph(*this);
4439         if (name == "phantom")
4440                 return createGuiPhantom(*this);
4441         if (name == "prefs")
4442                 return createGuiPreferences(*this);
4443         if (name == "ref")
4444                 return createGuiRef(*this);
4445         if (name == "sendto")
4446                 return createGuiSendTo(*this);
4447         if (name == "spellchecker")
4448                 return createGuiSpellchecker(*this);
4449         if (name == "symbols")
4450                 return createGuiSymbols(*this);
4451         if (name == "tabularcreate")
4452                 return createGuiTabularCreate(*this);
4453         if (name == "texinfo")
4454                 return createGuiTexInfo(*this);
4455         if (name == "thesaurus")
4456                 return createGuiThesaurus(*this);
4457         if (name == "toc")
4458                 return createGuiToc(*this);
4459         if (name == "view-source")
4460                 return createGuiViewSource(*this);
4461         if (name == "wrap")
4462                 return createGuiWrap(*this);
4463         if (name == "progress")
4464                 return createGuiProgressView(*this);
4465
4466         return 0;
4467 }
4468
4469
4470 } // namespace frontend
4471 } // namespace lyx
4472
4473 #include "moc_GuiView.cpp"