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