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