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