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