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