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