]> git.lyx.org Git - features.git/blob - src/frontends/qt4/GuiView.cpp
Simplify previous commit
[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_NEXT:
1926         case LFUN_BUFFER_PREVIOUS:
1927                 // because we cycle, it doesn't matter whether on first or last
1928                 if (d.currentTabWorkArea()->count() <= 1)
1929                         enable = false;
1930                 break;
1931         case LFUN_BUFFER_SWITCH:
1932                 // toggle on the current buffer, but do not toggle off
1933                 // the other ones (is that a good idea?)
1934                 if (doc_buffer
1935                         && to_utf8(cmd.argument()) == doc_buffer->absFileName())
1936                         flag.setOnOff(true);
1937                 break;
1938
1939         case LFUN_VC_REGISTER:
1940                 enable = doc_buffer && !doc_buffer->lyxvc().inUse();
1941                 break;
1942         case LFUN_VC_RENAME:
1943                 enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
1944                 break;
1945         case LFUN_VC_COPY:
1946                 enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
1947                 break;
1948         case LFUN_VC_CHECK_IN:
1949                 enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
1950                 break;
1951         case LFUN_VC_CHECK_OUT:
1952                 enable = doc_buffer && doc_buffer->lyxvc().checkOutEnabled();
1953                 break;
1954         case LFUN_VC_LOCKING_TOGGLE:
1955                 enable = doc_buffer && !doc_buffer->isReadonly()
1956                         && doc_buffer->lyxvc().lockingToggleEnabled();
1957                 flag.setOnOff(enable && doc_buffer->lyxvc().locking());
1958                 break;
1959         case LFUN_VC_REVERT:
1960                 enable = doc_buffer && doc_buffer->lyxvc().inUse() && !doc_buffer->isReadonly();
1961                 break;
1962         case LFUN_VC_UNDO_LAST:
1963                 enable = doc_buffer && doc_buffer->lyxvc().undoLastEnabled();
1964                 break;
1965         case LFUN_VC_REPO_UPDATE:
1966                 enable = doc_buffer && doc_buffer->lyxvc().repoUpdateEnabled();
1967                 break;
1968         case LFUN_VC_COMMAND: {
1969                 if (cmd.argument().empty())
1970                         enable = false;
1971                 if (!doc_buffer && contains(cmd.getArg(0), 'D'))
1972                         enable = false;
1973                 break;
1974         }
1975         case LFUN_VC_COMPARE:
1976                 enable = doc_buffer && doc_buffer->lyxvc().prepareFileRevisionEnabled();
1977                 break;
1978
1979         case LFUN_SERVER_GOTO_FILE_ROW:
1980                 break;
1981         case LFUN_FORWARD_SEARCH:
1982                 enable = !(lyxrc.forward_search_dvi.empty() && lyxrc.forward_search_pdf.empty());
1983                 break;
1984
1985         case LFUN_FILE_INSERT_PLAINTEXT:
1986         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
1987                 enable = documentBufferView() && documentBufferView()->cursor().inTexted();
1988                 break;
1989
1990         case LFUN_SPELLING_CONTINUOUSLY:
1991                 flag.setOnOff(lyxrc.spellcheck_continuously);
1992                 break;
1993
1994         default:
1995                 return false;
1996         }
1997
1998         if (!enable)
1999                 flag.setEnabled(false);
2000
2001         return true;
2002 }
2003
2004
2005 static FileName selectTemplateFile()
2006 {
2007         FileDialog dlg(qt_("Select template file"));
2008         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2009         dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2010
2011         FileDialog::Result result = dlg.open(toqstr(lyxrc.template_path),
2012                                  QStringList(qt_("LyX Documents (*.lyx)")));
2013
2014         if (result.first == FileDialog::Later)
2015                 return FileName();
2016         if (result.second.isEmpty())
2017                 return FileName();
2018         return FileName(fromqstr(result.second));
2019 }
2020
2021
2022 Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
2023 {
2024         setBusy(true);
2025
2026         Buffer * newBuffer = 0;
2027         try {
2028                 newBuffer = checkAndLoadLyXFile(filename);
2029         } catch (ExceptionMessage const & e) {
2030                 setBusy(false);
2031                 throw(e);
2032         }
2033         setBusy(false);
2034
2035         if (!newBuffer) {
2036                 message(_("Document not loaded."));
2037                 return 0;
2038         }
2039
2040         setBuffer(newBuffer);
2041         newBuffer->errors("Parse");
2042
2043         if (tolastfiles)
2044                 theSession().lastFiles().add(filename);
2045
2046         return newBuffer;
2047 }
2048
2049
2050 void GuiView::openDocument(string const & fname)
2051 {
2052         string initpath = lyxrc.document_path;
2053
2054         if (documentBufferView()) {
2055                 string const trypath = documentBufferView()->buffer().filePath();
2056                 // If directory is writeable, use this as default.
2057                 if (FileName(trypath).isDirWritable())
2058                         initpath = trypath;
2059         }
2060
2061         string filename;
2062
2063         if (fname.empty()) {
2064                 FileDialog dlg(qt_("Select document to open"));
2065                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2066                 dlg.setButton2(qt_("Examples|#E#e"),
2067                                 toqstr(addPath(package().system_support().absFileName(), "examples")));
2068
2069                 QStringList const filter(qt_("LyX Documents (*.lyx)"));
2070                 FileDialog::Result result =
2071                         dlg.open(toqstr(initpath), filter);
2072
2073                 if (result.first == FileDialog::Later)
2074                         return;
2075
2076                 filename = fromqstr(result.second);
2077
2078                 // check selected filename
2079                 if (filename.empty()) {
2080                         message(_("Canceled."));
2081                         return;
2082                 }
2083         } else
2084                 filename = fname;
2085
2086         // get absolute path of file and add ".lyx" to the filename if
2087         // necessary.
2088         FileName const fullname =
2089                         fileSearch(string(), filename, "lyx", support::may_not_exist);
2090         if (!fullname.empty())
2091                 filename = fullname.absFileName();
2092
2093         if (!fullname.onlyPath().isDirectory()) {
2094                 Alert::warning(_("Invalid filename"),
2095                                 bformat(_("The directory in the given path\n%1$s\ndoes not exist."),
2096                                 from_utf8(fullname.absFileName())));
2097                 return;
2098         }
2099
2100         // if the file doesn't exist and isn't already open (bug 6645),
2101         // let the user create one
2102         if (!fullname.exists() && !theBufferList().exists(fullname) &&
2103             !LyXVC::file_not_found_hook(fullname)) {
2104                 // the user specifically chose this name. Believe him.
2105                 Buffer * const b = newFile(filename, string(), true);
2106                 if (b)
2107                         setBuffer(b);
2108                 return;
2109         }
2110
2111         docstring const disp_fn = makeDisplayPath(filename);
2112         message(bformat(_("Opening document %1$s..."), disp_fn));
2113
2114         docstring str2;
2115         Buffer * buf = loadDocument(fullname);
2116         if (buf) {
2117                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2118                 if (buf->lyxvc().inUse())
2119                         str2 += " " + from_utf8(buf->lyxvc().versionString()) +
2120                                 " " + _("Version control detected.");
2121         } else {
2122                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2123         }
2124         message(str2);
2125 }
2126
2127 // FIXME: clean that
2128 static bool import(GuiView * lv, FileName const & filename,
2129         string const & format, ErrorList & errorList)
2130 {
2131         FileName const lyxfile(support::changeExtension(filename.absFileName(), ".lyx"));
2132
2133         string loader_format;
2134         vector<string> loaders = theConverters().loaders();
2135         if (find(loaders.begin(), loaders.end(), format) == loaders.end()) {
2136                 vector<string>::const_iterator it = loaders.begin();
2137                 vector<string>::const_iterator en = loaders.end();
2138                 for (; it != en; ++it) {
2139                         if (!theConverters().isReachable(format, *it))
2140                                 continue;
2141
2142                         string const tofile =
2143                                 support::changeExtension(filename.absFileName(),
2144                                 formats.extension(*it));
2145                         if (!theConverters().convert(0, filename, FileName(tofile),
2146                                 filename, format, *it, errorList))
2147                                 return false;
2148                         loader_format = *it;
2149                         break;
2150                 }
2151                 if (loader_format.empty()) {
2152                         frontend::Alert::error(_("Couldn't import file"),
2153                                          bformat(_("No information for importing the format %1$s."),
2154                                          formats.prettyName(format)));
2155                         return false;
2156                 }
2157         } else
2158                 loader_format = format;
2159
2160         if (loader_format == "lyx") {
2161                 Buffer * buf = lv->loadDocument(lyxfile);
2162                 if (!buf)
2163                         return false;
2164         } else {
2165                 Buffer * const b = newFile(lyxfile.absFileName(), string(), true);
2166                 if (!b)
2167                         return false;
2168                 lv->setBuffer(b);
2169                 bool as_paragraphs = loader_format == "textparagraph";
2170                 string filename2 = (loader_format == format) ? filename.absFileName()
2171                         : support::changeExtension(filename.absFileName(),
2172                                           formats.extension(loader_format));
2173                 lv->currentBufferView()->insertPlaintextFile(FileName(filename2),
2174                         as_paragraphs);
2175                 guiApp->setCurrentView(lv);
2176                 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
2177         }
2178
2179         return true;
2180 }
2181
2182
2183 void GuiView::importDocument(string const & argument)
2184 {
2185         string format;
2186         string filename = split(argument, format, ' ');
2187
2188         LYXERR(Debug::INFO, format << " file: " << filename);
2189
2190         // need user interaction
2191         if (filename.empty()) {
2192                 string initpath = lyxrc.document_path;
2193                 if (documentBufferView()) {
2194                         string const trypath = documentBufferView()->buffer().filePath();
2195                         // If directory is writeable, use this as default.
2196                         if (FileName(trypath).isDirWritable())
2197                                 initpath = trypath;
2198                 }
2199
2200                 docstring const text = bformat(_("Select %1$s file to import"),
2201                         formats.prettyName(format));
2202
2203                 FileDialog dlg(toqstr(text));
2204                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2205                 dlg.setButton2(qt_("Examples|#E#e"),
2206                         toqstr(addPath(package().system_support().absFileName(), "examples")));
2207
2208                 docstring filter = formats.prettyName(format);
2209                 filter += " (*.{";
2210                 // FIXME UNICODE
2211                 filter += from_utf8(formats.extensions(format));
2212                 filter += "})";
2213
2214                 FileDialog::Result result =
2215                         dlg.open(toqstr(initpath), fileFilters(toqstr(filter)));
2216
2217                 if (result.first == FileDialog::Later)
2218                         return;
2219
2220                 filename = fromqstr(result.second);
2221
2222                 // check selected filename
2223                 if (filename.empty())
2224                         message(_("Canceled."));
2225         }
2226
2227         if (filename.empty())
2228                 return;
2229
2230         // get absolute path of file
2231         FileName const fullname(support::makeAbsPath(filename));
2232
2233         // Can happen if the user entered a path into the dialog
2234         // (see bug #7437)
2235         if (fullname.onlyFileName().empty()) {
2236                 docstring msg = bformat(_("The file name '%1$s' is invalid!\n"
2237                                           "Aborting import."),
2238                                         from_utf8(fullname.absFileName()));
2239                 frontend::Alert::error(_("File name error"), msg);
2240                 message(_("Canceled."));
2241                 return;
2242         }
2243
2244
2245         FileName const lyxfile(support::changeExtension(fullname.absFileName(), ".lyx"));
2246
2247         // Check if the document already is open
2248         Buffer * buf = theBufferList().getBuffer(lyxfile);
2249         if (buf) {
2250                 setBuffer(buf);
2251                 if (!closeBuffer()) {
2252                         message(_("Canceled."));
2253                         return;
2254                 }
2255         }
2256
2257         docstring const displaypath = makeDisplayPath(lyxfile.absFileName(), 30);
2258
2259         // if the file exists already, and we didn't do
2260         // -i lyx thefile.lyx, warn
2261         if (lyxfile.exists() && fullname != lyxfile) {
2262
2263                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2264                         "Do you want to overwrite that document?"), displaypath);
2265                 int const ret = Alert::prompt(_("Overwrite document?"),
2266                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2267
2268                 if (ret == 1) {
2269                         message(_("Canceled."));
2270                         return;
2271                 }
2272         }
2273
2274         message(bformat(_("Importing %1$s..."), displaypath));
2275         ErrorList errorList;
2276         if (import(this, fullname, format, errorList))
2277                 message(_("imported."));
2278         else
2279                 message(_("file not imported!"));
2280
2281         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2282 }
2283
2284
2285 void GuiView::newDocument(string const & filename, bool from_template)
2286 {
2287         FileName initpath(lyxrc.document_path);
2288         if (documentBufferView()) {
2289                 FileName const trypath(documentBufferView()->buffer().filePath());
2290                 // If directory is writeable, use this as default.
2291                 if (trypath.isDirWritable())
2292                         initpath = trypath;
2293         }
2294
2295         string templatefile;
2296         if (from_template) {
2297                 templatefile = selectTemplateFile().absFileName();
2298                 if (templatefile.empty())
2299                         return;
2300         }
2301
2302         Buffer * b;
2303         if (filename.empty())
2304                 b = newUnnamedFile(initpath, to_utf8(_("newfile")), templatefile);
2305         else
2306                 b = newFile(filename, templatefile, true);
2307
2308         if (b)
2309                 setBuffer(b);
2310
2311         // If no new document could be created, it is unsure
2312         // whether there is a valid BufferView.
2313         if (currentBufferView())
2314                 // Ensure the cursor is correctly positioned on screen.
2315                 currentBufferView()->showCursor();
2316 }
2317
2318
2319 void GuiView::insertLyXFile(docstring const & fname)
2320 {
2321         BufferView * bv = documentBufferView();
2322         if (!bv)
2323                 return;
2324
2325         // FIXME UNICODE
2326         FileName filename(to_utf8(fname));
2327         if (filename.empty()) {
2328                 // Launch a file browser
2329                 // FIXME UNICODE
2330                 string initpath = lyxrc.document_path;
2331                 string const trypath = bv->buffer().filePath();
2332                 // If directory is writeable, use this as default.
2333                 if (FileName(trypath).isDirWritable())
2334                         initpath = trypath;
2335
2336                 // FIXME UNICODE
2337                 FileDialog dlg(qt_("Select LyX document to insert"));
2338                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2339                 dlg.setButton2(qt_("Examples|#E#e"),
2340                         toqstr(addPath(package().system_support().absFileName(),
2341                         "examples")));
2342
2343                 FileDialog::Result result = dlg.open(toqstr(initpath),
2344                                          QStringList(qt_("LyX Documents (*.lyx)")));
2345
2346                 if (result.first == FileDialog::Later)
2347                         return;
2348
2349                 // FIXME UNICODE
2350                 filename.set(fromqstr(result.second));
2351
2352                 // check selected filename
2353                 if (filename.empty()) {
2354                         // emit message signal.
2355                         message(_("Canceled."));
2356                         return;
2357                 }
2358         }
2359
2360         bv->insertLyXFile(filename);
2361         bv->buffer().errors("Parse");
2362 }
2363
2364
2365 bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
2366 {
2367         FileName fname = b.fileName();
2368         FileName const oldname = fname;
2369
2370         if (!newname.empty()) {
2371                 // FIXME UNICODE
2372                 fname = support::makeAbsPath(to_utf8(newname), oldname.onlyPath().absFileName());
2373         } else {
2374                 // Switch to this Buffer.
2375                 setBuffer(&b);
2376
2377                 // No argument? Ask user through dialog.
2378                 // FIXME UNICODE
2379                 FileDialog dlg(qt_("Choose a filename to save document as"));
2380                 dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2381                 dlg.setButton2(qt_("Templates|#T#t"), toqstr(lyxrc.template_path));
2382
2383                 if (!isLyXFileName(fname.absFileName()))
2384                         fname.changeExtension(".lyx");
2385
2386                 FileDialog::Result result =
2387                         dlg.save(toqstr(fname.onlyPath().absFileName()),
2388                                    QStringList(qt_("LyX Documents (*.lyx)")),
2389                                          toqstr(fname.onlyFileName()));
2390
2391                 if (result.first == FileDialog::Later)
2392                         return false;
2393
2394                 fname.set(fromqstr(result.second));
2395
2396                 if (fname.empty())
2397                         return false;
2398
2399                 if (!isLyXFileName(fname.absFileName()))
2400                         fname.changeExtension(".lyx");
2401         }
2402
2403         // fname is now the new Buffer location.
2404
2405         // if there is already a Buffer open with this name, we do not want
2406         // to have another one. (the second test makes sure we're not just
2407         // trying to overwrite ourselves, which is fine.)
2408         if (theBufferList().exists(fname) && fname != oldname
2409                   && theBufferList().getBuffer(fname) != &b) {
2410                 docstring const text =
2411                         bformat(_("The file\n%1$s\nis already open in your current session.\n"
2412                             "Please close it before attempting to overwrite it.\n"
2413                             "Do you want to choose a new filename?"),
2414                                 from_utf8(fname.absFileName()));
2415                 int const ret = Alert::prompt(_("Chosen File Already Open"),
2416                         text, 0, 1, _("&Rename"), _("&Cancel"));
2417                 switch (ret) {
2418                 case 0: return renameBuffer(b, docstring(), kind);
2419                 case 1: return false;
2420                 }
2421                 //return false;
2422         }
2423
2424         bool const existsLocal = fname.exists();
2425         bool const existsInVC = LyXVC::fileInVC(fname);
2426         if (existsLocal || existsInVC) {
2427                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2428                 if (kind != LV_WRITE_AS && existsInVC) {
2429                         // renaming to a name that is already in VC
2430                         // would not work
2431                         docstring text = bformat(_("The document %1$s "
2432                                         "is already registered.\n\n"
2433                                         "Do you want to choose a new name?"),
2434                                 file);
2435                         docstring const title = (kind == LV_VC_RENAME) ?
2436                                 _("Rename document?") : _("Copy document?");
2437                         docstring const button = (kind == LV_VC_RENAME) ?
2438                                 _("&Rename") : _("&Copy");
2439                         int const ret = Alert::prompt(title, text, 0, 1,
2440                                 button, _("&Cancel"));
2441                         switch (ret) {
2442                         case 0: return renameBuffer(b, docstring(), kind);
2443                         case 1: return false;
2444                         }
2445                 }
2446
2447                 if (existsLocal) {
2448                         docstring text = bformat(_("The document %1$s "
2449                                         "already exists.\n\n"
2450                                         "Do you want to overwrite that document?"),
2451                                 file);
2452                         int const ret = Alert::prompt(_("Overwrite document?"),
2453                                         text, 0, 2, _("&Overwrite"),
2454                                         _("&Rename"), _("&Cancel"));
2455                         switch (ret) {
2456                         case 0: break;
2457                         case 1: return renameBuffer(b, docstring(), kind);
2458                         case 2: return false;
2459                         }
2460                 }
2461         }
2462
2463         switch (kind) {
2464         case LV_VC_RENAME: {
2465                 string msg = b.lyxvc().rename(fname);
2466                 if (msg.empty())
2467                         return false;
2468                 message(from_utf8(msg));
2469                 break;
2470         }
2471         case LV_VC_COPY: {
2472                 string msg = b.lyxvc().copy(fname);
2473                 if (msg.empty())
2474                         return false;
2475                 message(from_utf8(msg));
2476                 break;
2477         }
2478         case LV_WRITE_AS:
2479                 break;
2480         }
2481         // LyXVC created the file already in case of LV_VC_RENAME or
2482         // LV_VC_COPY, but call saveBuffer() nevertheless to get
2483         // relative paths of included stuff right if we moved e.g. from
2484         // /a/b.lyx to /a/c/b.lyx.
2485
2486         bool const saved = saveBuffer(b, fname);
2487         if (saved)
2488                 b.reload();
2489         return saved;
2490 }
2491
2492
2493 struct PrettyNameComparator
2494 {
2495         bool operator()(Format const *first, Format const *second) const {
2496                 return compare_no_case(translateIfPossible(from_ascii(first->prettyname())),
2497                                        translateIfPossible(from_ascii(second->prettyname()))) <= 0;
2498         }
2499 };
2500
2501
2502 bool GuiView::exportBufferAs(Buffer & b, docstring const & iformat)
2503 {
2504         FileName fname = b.fileName();
2505
2506         FileDialog dlg(qt_("Choose a filename to export the document as"));
2507         dlg.setButton1(qt_("Documents|#o#O"), toqstr(lyxrc.document_path));
2508
2509         QStringList types;
2510         QString const anyformat = qt_("Guess from extension (*.*)");
2511         types << anyformat;
2512         Formats::const_iterator it = formats.begin();
2513         vector<Format const *> export_formats;
2514         for (; it != formats.end(); ++it)
2515                 if (it->documentFormat())
2516                         export_formats.push_back(&(*it));
2517         PrettyNameComparator cmp;
2518         sort(export_formats.begin(), export_formats.end(), cmp);
2519         vector<Format const *>::const_iterator fit = export_formats.begin();
2520         map<QString, string> fmap;
2521         QString filter;
2522         string ext;
2523         for (; fit != export_formats.end(); ++fit) {
2524                 docstring const loc_prettyname =
2525                         translateIfPossible(from_utf8((*fit)->prettyname()));
2526                 QString const loc_filter = toqstr(bformat(from_ascii("%1$s (*.%2$s)"),
2527                                                      loc_prettyname,
2528                                                      from_ascii((*fit)->extension())));
2529                 types << loc_filter;
2530                 fmap[loc_filter] = (*fit)->name();
2531                 if (from_ascii((*fit)->name()) == iformat) {
2532                         filter = loc_filter;
2533                         ext = (*fit)->extension();
2534                 }
2535         }
2536         string ofname = fname.onlyFileName();
2537         if (!ext.empty())
2538                 ofname = support::changeExtension(ofname, ext);
2539         FileDialog::Result result =
2540                 dlg.save(toqstr(fname.onlyPath().absFileName()),
2541                          types,
2542                          toqstr(ofname),
2543                          &filter);
2544         if (result.first != FileDialog::Chosen)
2545                 return false;
2546
2547         string fmt_name;
2548         fname.set(fromqstr(result.second));
2549         if (filter == anyformat)
2550                 fmt_name = formats.getFormatFromExtension(fname.extension());
2551         else
2552                 fmt_name = fmap[filter];
2553         LYXERR(Debug::FILES, "filter=" << fromqstr(filter)
2554                << ", fmt_name=" << fmt_name << ", fname=" << fname.absFileName());
2555
2556         if (fmt_name.empty() || fname.empty())
2557                 return false;
2558
2559         // fname is now the new Buffer location.
2560         if (FileName(fname).exists()) {
2561                 docstring const file = makeDisplayPath(fname.absFileName(), 30);
2562                 docstring text = bformat(_("The document %1$s already "
2563                                            "exists.\n\nDo you want to "
2564                                            "overwrite that document?"),
2565                                          file);
2566                 int const ret = Alert::prompt(_("Overwrite document?"),
2567                         text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
2568                 switch (ret) {
2569                 case 0: break;
2570                 case 1: return exportBufferAs(b, from_ascii(fmt_name));
2571                 case 2: return false;
2572                 }
2573         }
2574
2575         FuncRequest cmd(LFUN_BUFFER_EXPORT, fmt_name + " " + fname.absFileName());
2576         DispatchResult dr;
2577         dispatch(cmd, dr);
2578         return dr.dispatched();
2579 }
2580
2581
2582 bool GuiView::saveBuffer(Buffer & b)
2583 {
2584         return saveBuffer(b, FileName());
2585 }
2586
2587
2588 bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
2589 {
2590         if (workArea(b) && workArea(b)->inDialogMode())
2591                 return true;
2592
2593         if (fn.empty() && b.isUnnamed())
2594                 return renameBuffer(b, docstring());
2595
2596         bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
2597         if (success) {
2598                 theSession().lastFiles().add(b.fileName());
2599                 return true;
2600         }
2601
2602         // Switch to this Buffer.
2603         setBuffer(&b);
2604
2605         // FIXME: we don't tell the user *WHY* the save failed !!
2606         docstring const file = makeDisplayPath(b.absFileName(), 30);
2607         docstring text = bformat(_("The document %1$s could not be saved.\n\n"
2608                                    "Do you want to rename the document and "
2609                                    "try again?"), file);
2610         int const ret = Alert::prompt(_("Rename and save?"),
2611                 text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
2612         switch (ret) {
2613         case 0:
2614                 if (!renameBuffer(b, docstring()))
2615                         return false;
2616                 break;
2617         case 1:
2618                 break;
2619         case 2:
2620                 return false;
2621         }
2622
2623         return saveBuffer(b, fn);
2624 }
2625
2626
2627 bool GuiView::hideWorkArea(GuiWorkArea * wa)
2628 {
2629         return closeWorkArea(wa, false);
2630 }
2631
2632
2633 // We only want to close the buffer if it is not visible in other workareas
2634 // of the same view, nor in other views, and if this is not a child
2635 bool GuiView::closeWorkArea(GuiWorkArea * wa)
2636 {
2637         Buffer & buf = wa->bufferView().buffer();
2638
2639         bool last_wa = d.countWorkAreasOf(buf) == 1
2640                 && !inOtherView(buf) && !buf.parent();
2641
2642         bool close_buffer = last_wa;
2643
2644         if (last_wa) {
2645                 if (lyxrc.close_buffer_with_last_view == "yes")
2646                         ; // Nothing to do
2647                 else if (lyxrc.close_buffer_with_last_view == "no")
2648                         close_buffer = false;
2649                 else {
2650                         docstring file;
2651                         if (buf.isUnnamed())
2652                                 file = from_utf8(buf.fileName().onlyFileName());
2653                         else
2654                                 file = buf.fileName().displayName(30);
2655                         docstring const text = bformat(
2656                                 _("Last view on document %1$s is being closed.\n"
2657                                   "Would you like to close or hide the document?\n"
2658                                   "\n"
2659                                   "Hidden documents can be displayed back through\n"
2660                                   "the menu: View->Hidden->...\n"
2661                                   "\n"
2662                                   "To remove this question, set your preference in:\n"
2663                                   "  Tools->Preferences->Look&Feel->UserInterface\n"
2664                                 ), file);
2665                         int ret = Alert::prompt(_("Close or hide document?"),
2666                                 text, 0, 1, _("&Close"), _("&Hide"));
2667                         close_buffer = (ret == 0);
2668                 }
2669         }
2670
2671         return closeWorkArea(wa, close_buffer);
2672 }
2673
2674
2675 bool GuiView::closeBuffer()
2676 {
2677         GuiWorkArea * wa = currentMainWorkArea();
2678         setCurrentWorkArea(wa);
2679         Buffer & buf = wa->bufferView().buffer();
2680         return wa && closeWorkArea(wa, !buf.parent());
2681 }
2682
2683
2684 void GuiView::writeSession() const {
2685         GuiWorkArea const * active_wa = currentMainWorkArea();
2686         for (int i = 0; i < d.splitter_->count(); ++i) {
2687                 TabWorkArea * twa = d.tabWorkArea(i);
2688                 for (int j = 0; j < twa->count(); ++j) {
2689                         GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
2690                         Buffer & buf = wa->bufferView().buffer();
2691                         theSession().lastOpened().add(buf.fileName(), wa == active_wa);
2692                 }
2693         }
2694 }
2695
2696
2697 bool GuiView::closeBufferAll()
2698 {
2699         // Close the workareas in all other views
2700         QList<int> const ids = guiApp->viewIds();
2701         for (int i = 0; i != ids.size(); ++i) {
2702                 if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
2703                         return false;
2704         }
2705
2706         // Close our own workareas
2707         if (!closeWorkAreaAll())
2708                 return false;
2709
2710         // Now close the hidden buffers. We prevent hidden buffers from being
2711         // dirty, so we can just close them.
2712         theBufferList().closeAll();
2713         return true;
2714 }
2715
2716
2717 bool GuiView::closeWorkAreaAll()
2718 {
2719         setCurrentWorkArea(currentMainWorkArea());
2720
2721         // We might be in a situation that there is still a tabWorkArea, but
2722         // there are no tabs anymore. This can happen when we get here after a
2723         // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
2724         // many TabWorkArea's have no documents anymore.
2725         int empty_twa = 0;
2726
2727         // We have to call count() each time, because it can happen that
2728         // more than one splitter will disappear in one iteration (bug 5998).
2729         for (; d.splitter_->count() > empty_twa; ) {
2730                 TabWorkArea * twa = d.tabWorkArea(empty_twa);
2731
2732                 if (twa->count() == 0)
2733                         ++empty_twa;
2734                 else {
2735                         setCurrentWorkArea(twa->currentWorkArea());
2736                         if (!closeTabWorkArea(twa))
2737                                 return false;
2738                 }
2739         }
2740         return true;
2741 }
2742
2743
2744 bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer)
2745 {
2746         if (!wa)
2747                 return false;
2748
2749         Buffer & buf = wa->bufferView().buffer();
2750
2751         if (close_buffer && GuiViewPrivate::busyBuffers.contains(&buf)) {
2752                 Alert::warning(_("Close document"), 
2753                         _("Document could not be closed because it is being processed by LyX."));
2754                 return false;
2755         }
2756
2757         if (close_buffer)
2758                 return closeBuffer(buf);
2759         else {
2760                 if (!inMultiTabs(wa))
2761                         if (!saveBufferIfNeeded(buf, true))
2762                                 return false;
2763                 removeWorkArea(wa);
2764                 return true;
2765         }
2766 }
2767
2768
2769 bool GuiView::closeBuffer(Buffer & buf)
2770 {
2771         // If we are in a close_event all children will be closed in some time,
2772         // so no need to do it here. This will ensure that the children end up
2773         // in the session file in the correct order. If we close the master
2774         // buffer, we can close or release the child buffers here too.
2775         bool success = true;
2776         if (!closing_) {
2777                 ListOfBuffers clist = buf.getChildren();
2778                 ListOfBuffers::const_iterator it = clist.begin();
2779                 ListOfBuffers::const_iterator const bend = clist.end();
2780                 for (; it != bend; ++it) {
2781                         // If a child is dirty, do not close
2782                         // without user intervention
2783                         //FIXME: should we look in other tabworkareas?
2784                         Buffer * child_buf = *it;
2785                         GuiWorkArea * child_wa = workArea(*child_buf);
2786                         if (child_wa) {
2787                                 if (!closeWorkArea(child_wa, true)) {
2788                                         success = false;
2789                                         break;
2790                                 }
2791                         } else
2792                                 theBufferList().releaseChild(&buf, child_buf);
2793                 }
2794         }
2795         if (success) {
2796                 // goto bookmark to update bookmark pit.
2797                 //FIXME: we should update only the bookmarks related to this buffer!
2798                 LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
2799                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
2800                         guiApp->gotoBookmark(i+1, false, false);
2801
2802                 if (saveBufferIfNeeded(buf, false)) {
2803                         buf.removeAutosaveFile();
2804                         theBufferList().release(&buf);
2805                         return true;
2806                 }
2807         }
2808         // open all children again to avoid a crash because of dangling
2809         // pointers (bug 6603)
2810         buf.updateBuffer();
2811         return false;
2812 }
2813
2814
2815 bool GuiView::closeTabWorkArea(TabWorkArea * twa)
2816 {
2817         while (twa == d.currentTabWorkArea()) {
2818                 twa->setCurrentIndex(twa->count()-1);
2819
2820                 GuiWorkArea * wa = twa->currentWorkArea();
2821                 Buffer & b = wa->bufferView().buffer();
2822
2823                 // We only want to close the buffer if the same buffer is not visible
2824                 // in another view, and if this is not a child and if we are closing
2825                 // a view (not a tabgroup).
2826                 bool const close_buffer =
2827                         !inOtherView(b) && !b.parent() && closing_;
2828
2829                 if (!closeWorkArea(wa, close_buffer))
2830                         return false;
2831         }
2832         return true;
2833 }
2834
2835
2836 bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
2837 {
2838         if (buf.isClean() || buf.paragraphs().empty())
2839                 return true;
2840
2841         // Switch to this Buffer.
2842         setBuffer(&buf);
2843
2844         docstring file;
2845         // FIXME: Unicode?
2846         if (buf.isUnnamed())
2847                 file = from_utf8(buf.fileName().onlyFileName());
2848         else
2849                 file = buf.fileName().displayName(30);
2850
2851         // Bring this window to top before asking questions.
2852         raise();
2853         activateWindow();
2854
2855         int ret;
2856         if (hiding && buf.isUnnamed()) {
2857                 docstring const text = bformat(_("The document %1$s has not been "
2858                                                  "saved yet.\n\nDo you want to save "
2859                                                  "the document?"), file);
2860                 ret = Alert::prompt(_("Save new document?"),
2861                         text, 0, 1, _("&Save"), _("&Cancel"));
2862                 if (ret == 1)
2863                         ++ret;
2864         } else {
2865                 docstring const text = bformat(_("The document %1$s has unsaved changes."
2866                         "\n\nDo you want to save the document or discard the changes?"), file);
2867                 ret = Alert::prompt(_("Save changed document?"),
2868                         text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
2869         }
2870
2871         switch (ret) {
2872         case 0:
2873                 if (!saveBuffer(buf))
2874                         return false;
2875                 break;
2876         case 1:
2877                 // If we crash after this we could have no autosave file
2878                 // but I guess this is really improbable (Jug).
2879                 // Sometimes improbable things happen:
2880                 // - see bug http://www.lyx.org/trac/ticket/6587 (ps)
2881                 // buf.removeAutosaveFile();
2882                 if (hiding)
2883                         // revert all changes
2884                         reloadBuffer(buf);
2885                 buf.markClean();
2886                 break;
2887         case 2:
2888                 return false;
2889         }
2890         return true;
2891 }
2892
2893
2894 bool GuiView::inMultiTabs(GuiWorkArea * wa)
2895 {
2896         Buffer & buf = wa->bufferView().buffer();
2897
2898         for (int i = 0; i != d.splitter_->count(); ++i) {
2899                 GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
2900                 if (wa_ && wa_ != wa)
2901                         return true;
2902         }
2903         return inOtherView(buf);
2904 }
2905
2906
2907 bool GuiView::inOtherView(Buffer & buf)
2908 {
2909         QList<int> const ids = guiApp->viewIds();
2910
2911         for (int i = 0; i != ids.size(); ++i) {
2912                 if (id_ == ids[i])
2913                         continue;
2914
2915                 if (guiApp->view(ids[i]).workArea(buf))
2916                         return true;
2917         }
2918         return false;
2919 }
2920
2921
2922 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
2923 {
2924         if (!documentBufferView())
2925                 return;
2926         
2927         if (TabWorkArea * twa = d.currentTabWorkArea()) {
2928                 Buffer * const curbuf = &documentBufferView()->buffer();
2929                 int nwa = twa->count();
2930                 for (int i = 0; i < nwa; ++i) {
2931                         if (&workArea(i)->bufferView().buffer() == curbuf) {
2932                                 int next_index;
2933                                 if (np == NEXTBUFFER)
2934                                         next_index = (i == nwa - 1 ? 0 : i + 1);
2935                                 else
2936                                         next_index = (i == 0 ? nwa - 1 : i - 1);
2937                                 setBuffer(&workArea(next_index)->bufferView().buffer());
2938                                 break;
2939                         }
2940                 }
2941         }
2942 }
2943
2944
2945 /// make sure the document is saved
2946 static bool ensureBufferClean(Buffer * buffer)
2947 {
2948         LASSERT(buffer, return false);
2949         if (buffer->isClean() && !buffer->isUnnamed())
2950                 return true;
2951
2952         docstring const file = buffer->fileName().displayName(30);
2953         docstring title;
2954         docstring text;
2955         if (!buffer->isUnnamed()) {
2956                 text = bformat(_("The document %1$s has unsaved "
2957                                                  "changes.\n\nDo you want to save "
2958                                                  "the document?"), file);
2959                 title = _("Save changed document?");
2960
2961         } else {
2962                 text = bformat(_("The document %1$s has not been "
2963                                                  "saved yet.\n\nDo you want to save "
2964                                                  "the document?"), file);
2965                 title = _("Save new document?");
2966         }
2967         int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
2968
2969         if (ret == 0)
2970                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
2971
2972         return buffer->isClean() && !buffer->isUnnamed();
2973 }
2974
2975
2976 bool GuiView::reloadBuffer(Buffer & buf)
2977 {
2978         Buffer::ReadStatus status = buf.reload();
2979         return status == Buffer::ReadSuccess;
2980 }
2981
2982
2983 void GuiView::checkExternallyModifiedBuffers()
2984 {
2985         BufferList::iterator bit = theBufferList().begin();
2986         BufferList::iterator const bend = theBufferList().end();
2987         for (; bit != bend; ++bit) {
2988                 Buffer * buf = *bit;
2989                 if (buf->fileName().exists()
2990                         && buf->isExternallyModified(Buffer::checksum_method)) {
2991                         docstring text = bformat(_("Document \n%1$s\n has been externally modified."
2992                                         " Reload now? Any local changes will be lost."),
2993                                         from_utf8(buf->absFileName()));
2994                         int const ret = Alert::prompt(_("Reload externally changed document?"),
2995                                                 text, 0, 1, _("&Reload"), _("&Cancel"));
2996                         if (!ret)
2997                                 reloadBuffer(*buf);
2998                 }
2999         }
3000 }
3001
3002
3003 void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
3004 {
3005         Buffer * buffer = documentBufferView()
3006                 ? &(documentBufferView()->buffer()) : 0;
3007
3008         switch (cmd.action()) {
3009         case LFUN_VC_REGISTER:
3010                 if (!buffer || !ensureBufferClean(buffer))
3011                         break;
3012                 if (!buffer->lyxvc().inUse()) {
3013                         if (buffer->lyxvc().registrer()) {
3014                                 reloadBuffer(*buffer);
3015                                 dr.clearMessageUpdate();
3016                         }
3017                 }
3018                 break;
3019
3020         case LFUN_VC_RENAME:
3021         case LFUN_VC_COPY: {
3022                 if (!buffer || !ensureBufferClean(buffer))
3023                         break;
3024                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3025                         if (buffer->lyxvc().isCheckInWithConfirmation()) {
3026                                 // Some changes are not yet committed.
3027                                 // We test here and not in getStatus(), since
3028                                 // this test is expensive.
3029                                 string log;
3030                                 LyXVC::CommandResult ret =
3031                                         buffer->lyxvc().checkIn(log);
3032                                 dr.setMessage(log);
3033                                 if (ret == LyXVC::ErrorCommand ||
3034                                     ret == LyXVC::VCSuccess)
3035                                         reloadBuffer(*buffer);
3036                                 if (buffer->lyxvc().isCheckInWithConfirmation()) {
3037                                         frontend::Alert::error(
3038                                                 _("Revision control error."),
3039                                                 _("Document could not be checked in."));
3040                                         break;
3041                                 }
3042                         }
3043                         RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
3044                                 LV_VC_RENAME : LV_VC_COPY;
3045                         renameBuffer(*buffer, cmd.argument(), kind);
3046                 }
3047                 break;
3048         }
3049
3050         case LFUN_VC_CHECK_IN:
3051                 if (!buffer || !ensureBufferClean(buffer))
3052                         break;
3053                 if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
3054                         string log;
3055                         LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
3056                         dr.setMessage(log);
3057                         // Only skip reloading if the checkin was cancelled or
3058                         // an error occurred before the real checkin VCS command
3059                         // was executed, since the VCS might have changed the
3060                         // file even if it could not checkin successfully.
3061                         if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
3062                                 reloadBuffer(*buffer);
3063                 }
3064                 break;
3065
3066         case LFUN_VC_CHECK_OUT:
3067                 if (!buffer || !ensureBufferClean(buffer))
3068                         break;
3069                 if (buffer->lyxvc().inUse()) {
3070                         dr.setMessage(buffer->lyxvc().checkOut());
3071                         reloadBuffer(*buffer);
3072                 }
3073                 break;
3074
3075         case LFUN_VC_LOCKING_TOGGLE:
3076                 LASSERT(buffer, return);
3077                 if (!ensureBufferClean(buffer) || buffer->isReadonly())
3078                         break;
3079                 if (buffer->lyxvc().inUse()) {
3080                         string res = buffer->lyxvc().lockingToggle();
3081                         if (res.empty()) {
3082                                 frontend::Alert::error(_("Revision control error."),
3083                                 _("Error when setting the locking property."));
3084                         } else {
3085                                 dr.setMessage(res);
3086                                 reloadBuffer(*buffer);
3087                         }
3088                 }
3089                 break;
3090
3091         case LFUN_VC_REVERT:
3092                 LASSERT(buffer, return);
3093                 if (buffer->lyxvc().revert()) {
3094                         reloadBuffer(*buffer);
3095                         dr.clearMessageUpdate();
3096                 }
3097                 break;
3098
3099         case LFUN_VC_UNDO_LAST:
3100                 LASSERT(buffer, return);
3101                 buffer->lyxvc().undoLast();
3102                 reloadBuffer(*buffer);
3103                 dr.clearMessageUpdate();
3104                 break;
3105
3106         case LFUN_VC_REPO_UPDATE:
3107                 LASSERT(buffer, return);
3108                 if (ensureBufferClean(buffer)) {
3109                         dr.setMessage(buffer->lyxvc().repoUpdate());
3110                         checkExternallyModifiedBuffers();
3111                 }
3112                 break;
3113
3114         case LFUN_VC_COMMAND: {
3115                 string flag = cmd.getArg(0);
3116                 if (buffer && contains(flag, 'R') && !ensureBufferClean(buffer))
3117                         break;
3118                 docstring message;
3119                 if (contains(flag, 'M')) {
3120                         if (!Alert::askForText(message, _("LyX VC: Log Message")))
3121                                 break;
3122                 }
3123                 string path = cmd.getArg(1);
3124                 if (contains(path, "$$p") && buffer)
3125                         path = subst(path, "$$p", buffer->filePath());
3126                 LYXERR(Debug::LYXVC, "Directory: " << path);
3127                 FileName pp(path);
3128                 if (!pp.isReadableDirectory()) {
3129                         lyxerr << _("Directory is not accessible.") << endl;
3130                         break;
3131                 }
3132                 support::PathChanger p(pp);
3133
3134                 string command = cmd.getArg(2);
3135                 if (command.empty())
3136                         break;
3137                 if (buffer) {
3138                         command = subst(command, "$$i", buffer->absFileName());
3139                         command = subst(command, "$$p", buffer->filePath());
3140                 }
3141                 command = subst(command, "$$m", to_utf8(message));
3142                 LYXERR(Debug::LYXVC, "Command: " << command);
3143                 Systemcall one;
3144                 one.startscript(Systemcall::Wait, command);
3145
3146                 if (!buffer)
3147                         break;
3148                 if (contains(flag, 'I'))
3149                         buffer->markDirty();
3150                 if (contains(flag, 'R'))
3151                         reloadBuffer(*buffer);
3152
3153                 break;
3154                 }
3155
3156         case LFUN_VC_COMPARE: {
3157
3158                 if (cmd.argument().empty()) {
3159                         lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, "comparehistory"));
3160                         break;
3161                 }
3162
3163                 string rev1 = cmd.getArg(0);
3164                 string f1, f2;
3165
3166                 // f1
3167                 if (!buffer->lyxvc().prepareFileRevision(rev1, f1))
3168                         break;
3169
3170                 if (isStrInt(rev1) && convert<int>(rev1) <= 0) {
3171                         f2 = buffer->absFileName();
3172                 } else {
3173                         string rev2 = cmd.getArg(1);
3174                         if (rev2.empty())
3175                                 break;
3176                         // f2
3177                         if (!buffer->lyxvc().prepareFileRevision(rev2, f2))
3178                                 break;
3179                 }
3180
3181                 LYXERR(Debug::LYXVC, "Launching comparison for fetched revisions:\n" <<
3182                                         f1 << "\n"  << f2 << "\n" );
3183                 string par = "compare run " + quoteName(f1) + " " + quoteName(f2);
3184                 lyx::dispatch(FuncRequest(LFUN_DIALOG_SHOW, par));
3185                 break;
3186         }
3187
3188         default:
3189                 break;
3190         }
3191 }
3192
3193
3194 void GuiView::openChildDocument(string const & fname)
3195 {
3196         LASSERT(documentBufferView(), return);
3197         Buffer & buffer = documentBufferView()->buffer();
3198         FileName const filename = support::makeAbsPath(fname, buffer.filePath());
3199         documentBufferView()->saveBookmark(false);
3200         Buffer * child = 0;
3201         if (theBufferList().exists(filename)) {
3202                 child = theBufferList().getBuffer(filename);
3203                 setBuffer(child);
3204         } else {
3205                 message(bformat(_("Opening child document %1$s..."),
3206                         makeDisplayPath(filename.absFileName())));
3207                 child = loadDocument(filename, false);
3208         }
3209         // Set the parent name of the child document.
3210         // This makes insertion of citations and references in the child work,
3211         // when the target is in the parent or another child document.
3212         if (child)
3213                 child->setParent(&buffer);
3214 }
3215
3216
3217 bool GuiView::goToFileRow(string const & argument)
3218 {
3219         string file_name;
3220         int row;
3221         size_t i = argument.find_last_of(' ');
3222         if (i != string::npos) {
3223                 file_name = os::internal_path(trim(argument.substr(0, i)));
3224                 istringstream is(argument.substr(i + 1));
3225                 is >> row;
3226                 if (is.fail())
3227                         i = string::npos;
3228         }
3229         if (i == string::npos) {
3230                 LYXERR0("Wrong argument: " << argument);
3231                 return false;
3232         }
3233         Buffer * buf = 0;
3234         string const abstmp = package().temp_dir().absFileName();
3235         string const realtmp = package().temp_dir().realPath();
3236         // We have to use os::path_prefix_is() here, instead of
3237         // simply prefixIs(), because the file name comes from
3238         // an external application and may need case adjustment.
3239         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
3240                 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
3241                 // Needed by inverse dvi search. If it is a file
3242                 // in tmpdir, call the apropriated function.
3243                 // If tmpdir is a symlink, we may have the real
3244                 // path passed back, so we correct for that.
3245                 if (!prefixIs(file_name, abstmp))
3246                         file_name = subst(file_name, realtmp, abstmp);
3247                 buf = theBufferList().getBufferFromTmp(file_name);
3248         } else {
3249                 // Must replace extension of the file to be .lyx
3250                 // and get full path
3251                 FileName const s = fileSearch(string(),
3252                                                   support::changeExtension(file_name, ".lyx"), "lyx");
3253                 // Either change buffer or load the file
3254                 if (theBufferList().exists(s))
3255                         buf = theBufferList().getBuffer(s);
3256                 else if (s.exists()) {
3257                         buf = loadDocument(s);
3258                         if (!buf)
3259                                 return false;
3260                 } else {
3261                         message(bformat(
3262                                         _("File does not exist: %1$s"),
3263                                         makeDisplayPath(file_name)));
3264                         return false;
3265                 }
3266         }
3267         if (!buf) {
3268                 message(bformat(
3269                         _("No buffer for file: %1$s."),
3270                         makeDisplayPath(file_name))
3271                 );
3272                 return false;
3273         }
3274         setBuffer(buf);
3275         documentBufferView()->setCursorFromRow(row);
3276         return true;
3277 }
3278
3279
3280 template<class T>
3281 Buffer::ExportStatus GuiView::GuiViewPrivate::runAndDestroy(const T& func, Buffer const * orig, Buffer * clone, string const & format)
3282 {
3283         Buffer::ExportStatus const status = func(format);
3284
3285         // the cloning operation will have produced a clone of the entire set of
3286         // documents, starting from the master. so we must delete those.
3287         Buffer * mbuf = const_cast<Buffer *>(clone->masterBuffer());
3288         delete mbuf;
3289         busyBuffers.remove(orig);
3290         return status;
3291 }
3292
3293
3294 Buffer::ExportStatus GuiView::GuiViewPrivate::compileAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3295 {
3296         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3297         return runAndDestroy(lyx::bind(mem_func, clone, _1, true), orig, clone, format);
3298 }
3299
3300
3301 Buffer::ExportStatus GuiView::GuiViewPrivate::exportAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3302 {
3303         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &, bool) const = &Buffer::doExport;
3304         return runAndDestroy(lyx::bind(mem_func, clone, _1, false), orig, clone, format);
3305 }
3306
3307
3308 Buffer::ExportStatus GuiView::GuiViewPrivate::previewAndDestroy(Buffer const * orig, Buffer * clone, string const & format)
3309 {
3310         Buffer::ExportStatus (Buffer::* mem_func)(std::string const &) const = &Buffer::preview;
3311         return runAndDestroy(lyx::bind(mem_func, clone, _1), orig, clone, format);
3312 }
3313
3314
3315 bool GuiView::GuiViewPrivate::asyncBufferProcessing(
3316                            string const & argument,
3317                            Buffer const * used_buffer,
3318                            docstring const & msg,
3319                            Buffer::ExportStatus (*asyncFunc)(Buffer const *, Buffer *, string const &),
3320                            Buffer::ExportStatus (Buffer::*syncFunc)(string const &, bool) const,
3321                            Buffer::ExportStatus (Buffer::*previewFunc)(string const &) const)
3322 {
3323         if (!used_buffer)
3324                 return false;
3325
3326         string format = argument;
3327         if (format.empty())
3328                 format = used_buffer->params().getDefaultOutputFormat();
3329         processing_format = format;
3330         if (!msg.empty()) {
3331                 progress_->clearMessages();
3332                 gv_->message(msg);
3333         }
3334 #if EXPORT_in_THREAD
3335         GuiViewPrivate::busyBuffers.insert(used_buffer);
3336         Buffer * cloned_buffer = used_buffer->cloneFromMaster();
3337         if (!cloned_buffer) {
3338                 Alert::error(_("Export Error"),
3339                              _("Error cloning the Buffer."));
3340                 return false;
3341         }
3342         QFuture<Buffer::ExportStatus> f = QtConcurrent::run(
3343                                 asyncFunc,
3344                                 used_buffer,
3345                                 cloned_buffer,
3346                                 format);
3347         setPreviewFuture(f);
3348         last_export_format = used_buffer->params().bufferFormat();
3349         (void) syncFunc;
3350         (void) previewFunc;
3351         // We are asynchronous, so we don't know here anything about the success
3352         return true;
3353 #else
3354         Buffer::ExportStatus status;
3355         if (syncFunc) {
3356                 // TODO check here if it breaks exporting with Qt < 4.4
3357                 status = (used_buffer->*syncFunc)(format, true);
3358         } else if (previewFunc) {
3359                 status = (used_buffer->*previewFunc)(format); 
3360         } else
3361                 return false;
3362         handleExportStatus(gv_, status, format);
3363         (void) asyncFunc;
3364         return (status == Buffer::ExportSuccess 
3365                         || status == Buffer::PreviewSuccess);
3366 #endif
3367 }
3368
3369 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
3370 {
3371         BufferView * bv = currentBufferView();
3372         LASSERT(bv, return);
3373
3374         // Let the current BufferView dispatch its own actions.
3375         bv->dispatch(cmd, dr);
3376         if (dr.dispatched())
3377                 return;
3378
3379         // Try with the document BufferView dispatch if any.
3380         BufferView * doc_bv = documentBufferView();
3381         if (doc_bv && doc_bv != bv) {
3382                 doc_bv->dispatch(cmd, dr);
3383                 if (dr.dispatched())
3384                         return;
3385         }
3386
3387         // Then let the current Cursor dispatch its own actions.
3388         bv->cursor().dispatch(cmd);
3389
3390         // update completion. We do it here and not in
3391         // processKeySym to avoid another redraw just for a
3392         // changed inline completion
3393         if (cmd.origin() == FuncRequest::KEYBOARD) {
3394                 if (cmd.action() == LFUN_SELF_INSERT
3395                         || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed()))
3396                         updateCompletion(bv->cursor(), true, true);
3397                 else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD)
3398                         updateCompletion(bv->cursor(), false, true);
3399                 else
3400                         updateCompletion(bv->cursor(), false, false);
3401         }
3402
3403         dr = bv->cursor().result();
3404 }
3405
3406
3407 void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
3408 {
3409         BufferView * bv = currentBufferView();
3410         // By default we won't need any update.
3411         dr.screenUpdate(Update::None);
3412         // assume cmd will be dispatched
3413         dr.dispatched(true);
3414
3415         Buffer * doc_buffer = documentBufferView()
3416                 ? &(documentBufferView()->buffer()) : 0;
3417
3418         if (cmd.origin() == FuncRequest::TOC) {
3419                 GuiToc * toc = static_cast<GuiToc*>(findOrBuild("toc", false));
3420                 // FIXME: do we need to pass a DispatchResult object here?
3421                 toc->doDispatch(bv->cursor(), cmd);
3422                 return;
3423         }
3424
3425         string const argument = to_utf8(cmd.argument());
3426
3427         switch(cmd.action()) {
3428                 case LFUN_BUFFER_CHILD_OPEN:
3429                         openChildDocument(to_utf8(cmd.argument()));
3430                         break;
3431
3432                 case LFUN_BUFFER_IMPORT:
3433                         importDocument(to_utf8(cmd.argument()));
3434                         break;
3435
3436                 case LFUN_BUFFER_EXPORT: {
3437                         if (!doc_buffer)
3438                                 break;
3439                         FileName target_dir = doc_buffer->fileName().onlyPath();
3440                         string const dest = cmd.getArg(1);
3441                         if (!dest.empty() && FileName::isAbsolute(dest))
3442                                 target_dir = FileName(support::onlyPath(dest));
3443                         // GCC only sees strfwd.h when building merged
3444                         if (::lyx::operator==(cmd.argument(), "custom")) {
3445                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"), dr);
3446                                 break;
3447                         }
3448                         if (!target_dir.isDirWritable()) {
3449                                 exportBufferAs(*doc_buffer, cmd.argument());
3450                                 break;
3451                         }
3452                         /* TODO/Review: Is it a problem to also export the children?
3453                                         See the update_unincluded flag */
3454                         d.asyncBufferProcessing(argument,
3455                                                 doc_buffer,
3456                                                 _("Exporting ..."),
3457                                                 &GuiViewPrivate::exportAndDestroy,
3458                                                 &Buffer::doExport,
3459                                                 0);
3460                         // TODO Inform user about success
3461                         break;
3462                 }
3463
3464                 case LFUN_BUFFER_EXPORT_AS: {
3465                         LASSERT(doc_buffer, break);
3466                         docstring f = cmd.argument();
3467                         if (f.empty())
3468                                 f = from_ascii(doc_buffer->params().getDefaultOutputFormat());
3469                         exportBufferAs(*doc_buffer, f);
3470                         break;
3471                 }
3472
3473                 case LFUN_BUFFER_UPDATE: {
3474                         d.asyncBufferProcessing(argument,
3475                                                 doc_buffer,
3476                                                 _("Exporting ..."),
3477                                                 &GuiViewPrivate::compileAndDestroy,
3478                                                 &Buffer::doExport,
3479                                                 0);
3480                         break;
3481                 }
3482                 case LFUN_BUFFER_VIEW: {
3483                         d.asyncBufferProcessing(argument,
3484                                                 doc_buffer,
3485                                                 _("Previewing ..."),
3486                                                 &GuiViewPrivate::previewAndDestroy,
3487                                                 0,
3488                                                 &Buffer::preview);
3489                         break;
3490                 }
3491                 case LFUN_MASTER_BUFFER_UPDATE: {
3492                         d.asyncBufferProcessing(argument,
3493                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3494                                                 docstring(),
3495                                                 &GuiViewPrivate::compileAndDestroy,
3496                                                 &Buffer::doExport,
3497                                                 0);
3498                         break;
3499                 }
3500                 case LFUN_MASTER_BUFFER_VIEW: {
3501                         d.asyncBufferProcessing(argument,
3502                                                 (doc_buffer ? doc_buffer->masterBuffer() : 0),
3503                                                 docstring(),
3504                                                 &GuiViewPrivate::previewAndDestroy,
3505                                                 0, &Buffer::preview);
3506                         break;
3507                 }
3508                 case LFUN_BUFFER_SWITCH: {
3509                         string const file_name = to_utf8(cmd.argument());
3510                         if (!FileName::isAbsolute(file_name)) {
3511                                 dr.setError(true);
3512                                 dr.setMessage(_("Absolute filename expected."));
3513                                 break;
3514                         }
3515
3516                         Buffer * buffer = theBufferList().getBuffer(FileName(file_name));
3517                         if (!buffer) {
3518                                 dr.setError(true);
3519                                 dr.setMessage(_("Document not loaded"));
3520                                 break;
3521                         }
3522
3523                         // Do we open or switch to the buffer in this view ?
3524                         if (workArea(*buffer)
3525                                   || lyxrc.open_buffers_in_tabs || !documentBufferView()) {
3526                                 setBuffer(buffer);
3527                                 break;
3528                         }
3529
3530                         // Look for the buffer in other views
3531                         QList<int> const ids = guiApp->viewIds();
3532                         int i = 0;
3533                         for (; i != ids.size(); ++i) {
3534                                 GuiView & gv = guiApp->view(ids[i]);
3535                                 if (gv.workArea(*buffer)) {
3536                                         gv.activateWindow();
3537                                         gv.setBuffer(buffer);
3538                                         break;
3539                                 }
3540                         }
3541
3542                         // If necessary, open a new window as a last resort
3543                         if (i == ids.size()) {
3544                                 lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW));
3545                                 lyx::dispatch(cmd);
3546                         }
3547                         break;
3548                 }
3549
3550                 case LFUN_BUFFER_NEXT:
3551                         gotoNextOrPreviousBuffer(NEXTBUFFER);
3552                         break;
3553
3554                 case LFUN_BUFFER_PREVIOUS:
3555                         gotoNextOrPreviousBuffer(PREVBUFFER);
3556                         break;
3557
3558                 case LFUN_COMMAND_EXECUTE: {
3559                         bool const show_it = cmd.argument() != "off";
3560                         // FIXME: this is a hack, "minibuffer" should not be
3561                         // hardcoded.
3562                         if (GuiToolbar * t = toolbar("minibuffer")) {
3563                                 t->setVisible(show_it);
3564                                 if (show_it && t->commandBuffer())
3565                                         t->commandBuffer()->setFocus();
3566                         }
3567                         break;
3568                 }
3569                 case LFUN_DROP_LAYOUTS_CHOICE:
3570                         d.layout_->showPopup();
3571                         break;
3572
3573                 case LFUN_MENU_OPEN:
3574                         if (QMenu * menu = guiApp->menus().menu(toqstr(cmd.argument()), *this))
3575                                 menu->exec(QCursor::pos());
3576                         break;
3577
3578                 case LFUN_FILE_INSERT:
3579                         insertLyXFile(cmd.argument());
3580                         break;
3581
3582                 case LFUN_FILE_INSERT_PLAINTEXT:
3583                 case LFUN_FILE_INSERT_PLAINTEXT_PARA: {
3584                         string const fname = to_utf8(cmd.argument());
3585                         if (!fname.empty() && !FileName::isAbsolute(fname)) {
3586                                 dr.setMessage(_("Absolute filename expected."));
3587                                 break;
3588                         }
3589                         
3590                         FileName filename(fname);
3591                         if (fname.empty()) {
3592                                 FileDialog dlg(qt_("Select file to insert"));
3593
3594                                 FileDialog::Result result = dlg.open(toqstr(bv->buffer().filePath()),
3595                                         QStringList(qt_("All Files (*)")));
3596                                 
3597                                 if (result.first == FileDialog::Later || result.second.isEmpty()) {
3598                                         dr.setMessage(_("Canceled."));
3599                                         break;
3600                                 }
3601
3602                                 filename.set(fromqstr(result.second));
3603                         }
3604
3605                         if (bv) {
3606                                 FuncRequest const new_cmd(cmd, filename.absoluteFilePath());
3607                                 bv->dispatch(new_cmd, dr);
3608                         }
3609                         break;
3610                 }
3611
3612                 case LFUN_BUFFER_RELOAD: {
3613                         LASSERT(doc_buffer, break);
3614
3615                         int ret = 0;
3616                         if (!doc_buffer->isClean()) {
3617                                 docstring const file =
3618                                         makeDisplayPath(doc_buffer->absFileName(), 20);
3619                                 docstring text = bformat(_("Any changes will be lost. "
3620                                         "Are you sure you want to revert to the saved version "
3621                                         "of the document %1$s?"), file);
3622                                 ret = Alert::prompt(_("Revert to saved document?"),
3623                                         text, 1, 1, _("&Revert"), _("&Cancel"));
3624                         }
3625
3626                         if (ret == 0) {
3627                                 doc_buffer->markClean();
3628                                 reloadBuffer(*doc_buffer);
3629                                 dr.forceBufferUpdate();
3630                         }
3631                         break;
3632                 }
3633
3634                 case LFUN_BUFFER_WRITE:
3635                         LASSERT(doc_buffer, break);
3636                         saveBuffer(*doc_buffer);
3637                         break;
3638
3639                 case LFUN_BUFFER_WRITE_AS:
3640                         LASSERT(doc_buffer, break);
3641                         renameBuffer(*doc_buffer, cmd.argument());
3642                         break;
3643
3644                 case LFUN_BUFFER_WRITE_ALL: {
3645                         Buffer * first = theBufferList().first();
3646                         if (!first)
3647                                 break;
3648                         message(_("Saving all documents..."));
3649                         // We cannot use a for loop as the buffer list cycles.
3650                         Buffer * b = first;
3651                         do {
3652                                 if (!b->isClean()) {
3653                                         saveBuffer(*b);
3654                                         LYXERR(Debug::ACTION, "Saved " << b->absFileName());
3655                                 }
3656                                 b = theBufferList().next(b);
3657                         } while (b != first);
3658                         dr.setMessage(_("All documents saved."));
3659                         break;
3660                 }
3661
3662                 case LFUN_BUFFER_CLOSE:
3663                         closeBuffer();
3664                         break;
3665
3666                 case LFUN_BUFFER_CLOSE_ALL:
3667                         closeBufferAll();
3668                         break;
3669
3670                 case LFUN_TOOLBAR_TOGGLE: {
3671                         string const name = cmd.getArg(0);
3672                         if (GuiToolbar * t = toolbar(name))
3673                                 t->toggle();
3674                         break;
3675                 }
3676
3677                 case LFUN_DIALOG_UPDATE: {
3678                         string const name = to_utf8(cmd.argument());
3679                         if (name == "prefs" || name == "document")
3680                                 updateDialog(name, string());
3681                         else if (name == "paragraph")
3682                                 lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
3683                         else if (currentBufferView()) {
3684                                 Inset * inset = currentBufferView()->editedInset(name);
3685                                 // Can only update a dialog connected to an existing inset
3686                                 if (inset) {
3687                                         // FIXME: get rid of this indirection; GuiView ask the inset
3688                                         // if he is kind enough to update itself...
3689                                         FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
3690                                         //FIXME: pass DispatchResult here?
3691                                         inset->dispatch(currentBufferView()->cursor(), fr);
3692                                 }
3693                         }
3694                         break;
3695                 }
3696
3697                 case LFUN_DIALOG_TOGGLE: {
3698                         FuncCode const func_code = isDialogVisible(cmd.getArg(0))
3699                                 ? LFUN_DIALOG_HIDE : LFUN_DIALOG_SHOW;
3700                         dispatch(FuncRequest(func_code, cmd.argument()), dr);
3701                         break;
3702                 }
3703
3704                 case LFUN_DIALOG_DISCONNECT_INSET:
3705                         disconnectDialog(to_utf8(cmd.argument()));
3706                         break;
3707
3708                 case LFUN_DIALOG_HIDE: {
3709                         guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
3710                         break;
3711                 }
3712
3713                 case LFUN_DIALOG_SHOW: {
3714                         string const name = cmd.getArg(0);
3715                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
3716
3717                         if (name == "character") {
3718                                 data = freefont2string();
3719                                 if (!data.empty())
3720                                         showDialog("character", data);
3721                         } else if (name == "latexlog") {
3722                                 Buffer::LogType type;
3723                                 string const logfile = doc_buffer->logName(&type);
3724                                 switch (type) {
3725                                 case Buffer::latexlog:
3726                                         data = "latex ";
3727                                         break;
3728                                 case Buffer::buildlog:
3729                                         data = "literate ";
3730                                         break;
3731                                 }
3732                                 data += Lexer::quoteString(logfile);
3733                                 showDialog("log", data);
3734                         } else if (name == "vclog") {
3735                                 string const data = "vc " +
3736                                         Lexer::quoteString(doc_buffer->lyxvc().getLogFile());
3737                                 showDialog("log", data);
3738                         } else if (name == "symbols") {
3739                                 data = bv->cursor().getEncoding()->name();
3740                                 if (!data.empty())
3741                                         showDialog("symbols", data);
3742                         // bug 5274
3743                         } else if (name == "prefs" && isFullScreen()) {
3744                                 lfunUiToggle("fullscreen");
3745                                 showDialog("prefs", data);
3746                         } else
3747                                 showDialog(name, data);
3748                         break;
3749                 }
3750
3751                 case LFUN_MESSAGE:
3752                         dr.setMessage(cmd.argument());
3753                         break;
3754
3755                 case LFUN_UI_TOGGLE: {
3756                         string arg = cmd.getArg(0);
3757                         if (!lfunUiToggle(arg)) {
3758                                 docstring const msg = "ui-toggle " + _("%1$s unknown command!");
3759                                 dr.setMessage(bformat(msg, from_utf8(arg)));
3760                         }
3761                         // Make sure the keyboard focus stays in the work area.
3762                         setFocus();
3763                         break;
3764                 }
3765
3766                 case LFUN_VIEW_SPLIT: {
3767                         LASSERT(doc_buffer, break);
3768                         string const orientation = cmd.getArg(0);
3769                         d.splitter_->setOrientation(orientation == "vertical"
3770                                 ? Qt::Vertical : Qt::Horizontal);
3771                         TabWorkArea * twa = addTabWorkArea();
3772                         GuiWorkArea * wa = twa->addWorkArea(*doc_buffer, *this);
3773                         setCurrentWorkArea(wa);
3774                         break;
3775                 }
3776                 case LFUN_TAB_GROUP_CLOSE:
3777                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3778                                 closeTabWorkArea(twa);
3779                                 d.current_work_area_ = 0;
3780                                 twa = d.currentTabWorkArea();
3781                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3782                                 if (twa) {
3783                                         // Make sure the work area is up to date.
3784                                         setCurrentWorkArea(twa->currentWorkArea());
3785                                 } else {
3786                                         setCurrentWorkArea(0);
3787                                 }
3788                         }
3789                         break;
3790
3791                 case LFUN_VIEW_CLOSE:
3792                         if (TabWorkArea * twa = d.currentTabWorkArea()) {
3793                                 closeWorkArea(twa->currentWorkArea());
3794                                 d.current_work_area_ = 0;
3795                                 twa = d.currentTabWorkArea();
3796                                 // Switch to the next GuiWorkArea in the found TabWorkArea.
3797                                 if (twa) {
3798                                         // Make sure the work area is up to date.
3799                                         setCurrentWorkArea(twa->currentWorkArea());
3800                                 } else {
3801                                         setCurrentWorkArea(0);
3802                                 }
3803                         }
3804                         break;
3805
3806                 case LFUN_COMPLETION_INLINE:
3807                         if (d.current_work_area_)
3808                                 d.current_work_area_->completer().showInline();
3809                         break;
3810
3811                 case LFUN_COMPLETION_POPUP:
3812                         if (d.current_work_area_)
3813                                 d.current_work_area_->completer().showPopup();
3814                         break;
3815
3816
3817                 case LFUN_COMPLETE:
3818                         if (d.current_work_area_)
3819                                 d.current_work_area_->completer().tab();
3820                         break;
3821
3822                 case LFUN_COMPLETION_CANCEL:
3823                         if (d.current_work_area_) {
3824                                 if (d.current_work_area_->completer().popupVisible())
3825                                         d.current_work_area_->completer().hidePopup();
3826                                 else
3827                                         d.current_work_area_->completer().hideInline();
3828                         }
3829                         break;
3830
3831                 case LFUN_COMPLETION_ACCEPT:
3832                         if (d.current_work_area_)
3833                                 d.current_work_area_->completer().activate();
3834                         break;
3835
3836                 case LFUN_BUFFER_ZOOM_IN:
3837                 case LFUN_BUFFER_ZOOM_OUT:
3838                         if (cmd.argument().empty()) {
3839                                 if (cmd.action() == LFUN_BUFFER_ZOOM_IN)
3840                                         lyxrc.zoom += 20;
3841                                 else
3842                                         lyxrc.zoom -= 20;
3843                         } else
3844                                 lyxrc.zoom += convert<int>(cmd.argument());
3845
3846                         if (lyxrc.zoom < 10)
3847                                 lyxrc.zoom = 10;
3848
3849                         // The global QPixmapCache is used in GuiPainter to cache text
3850                         // painting so we must reset it.
3851                         QPixmapCache::clear();
3852                         guiApp->fontLoader().update();
3853                         lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3854                         break;
3855
3856                 case LFUN_VC_REGISTER:
3857                 case LFUN_VC_RENAME:
3858                 case LFUN_VC_COPY:
3859                 case LFUN_VC_CHECK_IN:
3860                 case LFUN_VC_CHECK_OUT:
3861                 case LFUN_VC_REPO_UPDATE:
3862                 case LFUN_VC_LOCKING_TOGGLE:
3863                 case LFUN_VC_REVERT:
3864                 case LFUN_VC_UNDO_LAST:
3865                 case LFUN_VC_COMMAND:
3866                 case LFUN_VC_COMPARE:
3867                         dispatchVC(cmd, dr);
3868                         break;
3869
3870                 case LFUN_SERVER_GOTO_FILE_ROW:
3871                         goToFileRow(to_utf8(cmd.argument()));
3872                         break;
3873
3874                 case LFUN_FORWARD_SEARCH: {
3875                         Buffer const * doc_master = doc_buffer->masterBuffer();
3876                         FileName const path(doc_master->temppath());
3877                         string const texname = doc_master->isChild(doc_buffer)
3878                                 ? DocFileName(changeExtension(
3879                                         doc_buffer->absFileName(),
3880                                                 "tex")).mangledFileName()
3881                                 : doc_buffer->latexName();
3882                         string const fulltexname = 
3883                                 support::makeAbsPath(texname, doc_master->temppath()).absFileName();
3884                         string const mastername =
3885                                 removeExtension(doc_master->latexName());
3886                         FileName const dviname(addName(path.absFileName(),
3887                                         addExtension(mastername, "dvi")));
3888                         FileName const pdfname(addName(path.absFileName(),
3889                                         addExtension(mastername, "pdf")));
3890                         bool const have_dvi = dviname.exists();
3891                         bool const have_pdf = pdfname.exists();
3892                         if (!have_dvi && !have_pdf) {
3893                                 dr.setMessage(_("Please, preview the document first."));
3894                                 break;
3895                         }
3896                         string outname = dviname.onlyFileName();
3897                         string command = lyxrc.forward_search_dvi;
3898                         if (!have_dvi || (have_pdf &&
3899                             pdfname.lastModified() > dviname.lastModified())) {
3900                                 outname = pdfname.onlyFileName();
3901                                 command = lyxrc.forward_search_pdf;
3902                         }
3903
3904                         DocIterator tmpcur = bv->cursor();
3905                         // Leave math first
3906                         while (tmpcur.inMathed())
3907                                 tmpcur.pop_back();
3908                         int row = tmpcur.inMathed() ? 0 : doc_buffer->texrow().getRowFromIdPos(
3909                                                                 tmpcur.paragraph().id(), tmpcur.pos());
3910                         LYXERR(Debug::ACTION, "Forward search: row:" << row
3911                                 << " id:" << tmpcur.paragraph().id());
3912                         if (!row || command.empty()) {
3913                                 dr.setMessage(_("Couldn't proceed."));
3914                                 break;
3915                         }
3916                         string texrow = convert<string>(row);
3917
3918                         command = subst(command, "$$n", texrow);
3919                         command = subst(command, "$$f", fulltexname);
3920                         command = subst(command, "$$t", texname);
3921                         command = subst(command, "$$o", outname);
3922
3923                         PathChanger p(path);
3924                         Systemcall one;
3925                         one.startscript(Systemcall::DontWait, command);
3926                         break;
3927                 }
3928
3929                 case LFUN_SPELLING_CONTINUOUSLY:
3930                         lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
3931                         dr.screenUpdate(Update::Force | Update::FitCursor);
3932                         break;
3933
3934                 default:
3935                         // The LFUN must be for one of BufferView, Buffer or Cursor;
3936                         // let's try that:
3937                         dispatchToBufferView(cmd, dr);
3938                         break;
3939         }
3940
3941         // Part of automatic menu appearance feature.
3942         if (isFullScreen()) {
3943                 if (menuBar()->isVisible() && lyxrc.full_screen_menubar)
3944                         menuBar()->hide();
3945         }
3946
3947         // Need to update bv because many LFUNs here might have destroyed it
3948         bv = currentBufferView();
3949
3950         // Clear non-empty selections
3951         // (e.g. from a "char-forward-select" followed by "char-backward-select")
3952         if (bv) {
3953                 Cursor & cur = bv->cursor();
3954                 if ((cur.selection() && cur.selBegin() == cur.selEnd())) {
3955                         cur.clearSelection();
3956                 }
3957         }
3958 }
3959
3960
3961 bool GuiView::lfunUiToggle(string const & ui_component)
3962 {
3963         if (ui_component == "scrollbar") {
3964                 // hide() is of no help
3965                 if (d.current_work_area_->verticalScrollBarPolicy() ==
3966                         Qt::ScrollBarAlwaysOff)
3967
3968                         d.current_work_area_->setVerticalScrollBarPolicy(
3969                                 Qt::ScrollBarAsNeeded);
3970                 else
3971                         d.current_work_area_->setVerticalScrollBarPolicy(
3972                                 Qt::ScrollBarAlwaysOff);
3973         } else if (ui_component == "statusbar") {
3974                 statusBar()->setVisible(!statusBar()->isVisible());
3975         } else if (ui_component == "menubar") {
3976                 menuBar()->setVisible(!menuBar()->isVisible());
3977         } else
3978         if (ui_component == "frame") {
3979                 int l, t, r, b;
3980                 getContentsMargins(&l, &t, &r, &b);
3981                 //are the frames in default state?
3982                 d.current_work_area_->setFrameStyle(QFrame::NoFrame);
3983                 if (l == 0) {
3984                         setContentsMargins(-2, -2, -2, -2);
3985                 } else {
3986                         setContentsMargins(0, 0, 0, 0);
3987                 }
3988         } else
3989         if (ui_component == "fullscreen") {
3990                 toggleFullScreen();
3991         } else
3992                 return false;
3993         return true;
3994 }
3995
3996
3997 void GuiView::toggleFullScreen()
3998 {
3999         if (isFullScreen()) {
4000                 for (int i = 0; i != d.splitter_->count(); ++i)
4001                         d.tabWorkArea(i)->setFullScreen(false);
4002                 setContentsMargins(0, 0, 0, 0);
4003                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4004                 restoreLayout();
4005                 menuBar()->show();
4006                 statusBar()->show();
4007         } else {
4008                 // bug 5274
4009                 hideDialogs("prefs", 0);
4010                 for (int i = 0; i != d.splitter_->count(); ++i)
4011                         d.tabWorkArea(i)->setFullScreen(true);
4012                 setContentsMargins(-2, -2, -2, -2);
4013                 saveLayout();
4014                 setWindowState(windowState() ^ Qt::WindowFullScreen);
4015                 if (lyxrc.full_screen_statusbar)
4016                         statusBar()->hide();
4017                 if (lyxrc.full_screen_menubar)
4018                         menuBar()->hide();
4019                 if (lyxrc.full_screen_toolbars) {
4020                         ToolbarMap::iterator end = d.toolbars_.end();
4021                         for (ToolbarMap::iterator it = d.toolbars_.begin(); it != end; ++it)
4022                                 it->second->hide();
4023                 }
4024         }
4025
4026         // give dialogs like the TOC a chance to adapt
4027         updateDialogs();
4028 }
4029
4030
4031 Buffer const * GuiView::updateInset(Inset const * inset)
4032 {
4033         if (!inset)
4034                 return 0;
4035
4036         Buffer const * inset_buffer = &(inset->buffer());
4037
4038         for (int i = 0; i != d.splitter_->count(); ++i) {
4039                 GuiWorkArea * wa = d.tabWorkArea(i)->currentWorkArea();
4040                 if (!wa)
4041                         continue;
4042                 Buffer const * buffer = &(wa->bufferView().buffer());
4043                 if (inset_buffer == buffer)
4044                         wa->scheduleRedraw();
4045         }
4046         return inset_buffer;
4047 }
4048
4049
4050 void GuiView::restartCursor()
4051 {
4052         /* When we move around, or type, it's nice to be able to see
4053          * the cursor immediately after the keypress.
4054          */
4055         if (d.current_work_area_)
4056                 d.current_work_area_->startBlinkingCursor();
4057
4058         // Take this occasion to update the other GUI elements.
4059         updateDialogs();
4060         updateStatusBar();
4061 }
4062
4063
4064 void GuiView::updateCompletion(Cursor & cur, bool start, bool keep)
4065 {
4066         if (d.current_work_area_)
4067                 d.current_work_area_->completer().updateVisibility(cur, start, keep);
4068 }
4069
4070 namespace {
4071
4072 // This list should be kept in sync with the list of insets in
4073 // src/insets/Inset.cpp.  I.e., if a dialog goes with an inset, the
4074 // dialog should have the same name as the inset.
4075 // Changes should be also recorded in LFUN_DIALOG_SHOW doxygen
4076 // docs in LyXAction.cpp.
4077
4078 char const * const dialognames[] = {
4079
4080 "aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
4081 "citation", "compare", "comparehistory", "document", "errorlist", "ert",
4082 "external", "file", "findreplace", "findreplaceadv", "float", "graphics",
4083 "href", "include", "index", "index_print", "info", "listings", "label", "line",
4084 "log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
4085 "nomencl_print", "note", "paragraph", "phantom", "prefs", "print", "ref",
4086 "sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
4087 "thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
4088
4089 char const * const * const end_dialognames =
4090         dialognames + (sizeof(dialognames) / sizeof(char *));
4091
4092 class cmpCStr {
4093 public:
4094         cmpCStr(char const * name) : name_(name) {}
4095         bool operator()(char const * other) {
4096                 return strcmp(other, name_) == 0;
4097         }
4098 private:
4099         char const * name_;
4100 };
4101
4102
4103 bool isValidName(string const & name)
4104 {
4105         return find_if(dialognames, end_dialognames,
4106                                 cmpCStr(name.c_str())) != end_dialognames;
4107 }
4108
4109 } // namespace anon
4110
4111
4112 void GuiView::resetDialogs()
4113 {
4114         // Make sure that no LFUN uses any GuiView.
4115         guiApp->setCurrentView(0);
4116         saveLayout();
4117         saveUISettings();
4118         menuBar()->clear();
4119         constructToolbars();
4120         guiApp->menus().fillMenuBar(menuBar(), this, false);
4121         d.layout_->updateContents(true);
4122         // Now update controls with current buffer.
4123         guiApp->setCurrentView(this);
4124         restoreLayout();
4125         restartCursor();
4126 }
4127
4128
4129 Dialog * GuiView::findOrBuild(string const & name, bool hide_it)
4130 {
4131         if (!isValidName(name))
4132                 return 0;
4133
4134         map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
4135
4136         if (it != d.dialogs_.end()) {
4137                 if (hide_it)
4138                         it->second->hideView();
4139                 return it->second.get();
4140         }
4141
4142         Dialog * dialog = build(name);
4143         d.dialogs_[name].reset(dialog);
4144         if (lyxrc.allow_geometry_session)
4145                 dialog->restoreSession();
4146         if (hide_it)
4147                 dialog->hideView();
4148         return dialog;
4149 }
4150
4151
4152 void GuiView::showDialog(string const & name, string const & data,
4153         Inset * inset)
4154 {
4155         triggerShowDialog(toqstr(name), toqstr(data), inset);
4156 }
4157
4158
4159 void GuiView::doShowDialog(QString const & qname, QString const & qdata,
4160         Inset * inset)
4161 {
4162         if (d.in_show_)
4163                 return;
4164
4165         const string name = fromqstr(qname);
4166         const string data = fromqstr(qdata);
4167
4168         d.in_show_ = true;
4169         try {
4170                 Dialog * dialog = findOrBuild(name, false);
4171                 if (dialog) {
4172                         bool const visible = dialog->isVisibleView();
4173                         dialog->showData(data);
4174                         if (inset && currentBufferView())
4175                                 currentBufferView()->editInset(name, inset);
4176                         // We only set the focus to the new dialog if it was not yet
4177                         // visible in order not to change the existing previous behaviour
4178                         if (visible) {
4179                                 // activateWindow is needed for floating dockviews
4180                                 dialog->asQWidget()->raise();
4181                                 dialog->asQWidget()->activateWindow();
4182                                 dialog->asQWidget()->setFocus();
4183                         }
4184                 }
4185         }
4186         catch (ExceptionMessage const & ex) {
4187                 d.in_show_ = false;
4188                 throw ex;
4189         }
4190         d.in_show_ = false;
4191 }
4192
4193
4194 bool GuiView::isDialogVisible(string const & name) const
4195 {
4196         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4197         if (it == d.dialogs_.end())
4198                 return false;
4199         return it->second.get()->isVisibleView() && !it->second.get()->isClosing();
4200 }
4201
4202
4203 void GuiView::hideDialog(string const & name, Inset * inset)
4204 {
4205         map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
4206         if (it == d.dialogs_.end())
4207                 return;
4208
4209         if (inset) {
4210                 if (!currentBufferView())
4211                         return;
4212                 if (inset != currentBufferView()->editedInset(name))
4213                         return;
4214         }
4215
4216         Dialog * const dialog = it->second.get();
4217         if (dialog->isVisibleView())
4218                 dialog->hideView();
4219         if (currentBufferView())
4220                 currentBufferView()->editInset(name, 0);
4221 }
4222
4223
4224 void GuiView::disconnectDialog(string const & name)
4225 {
4226         if (!isValidName(name))
4227                 return;
4228         if (currentBufferView())
4229                 currentBufferView()->editInset(name, 0);
4230 }
4231
4232
4233 void GuiView::hideAll() const
4234 {
4235         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4236         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4237
4238         for(; it != end; ++it)
4239                 it->second->hideView();
4240 }
4241
4242
4243 void GuiView::updateDialogs()
4244 {
4245         map<string, DialogPtr>::const_iterator it  = d.dialogs_.begin();
4246         map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
4247
4248         for(; it != end; ++it) {
4249                 Dialog * dialog = it->second.get();
4250                 if (dialog) {
4251                         if (dialog->needBufferOpen() && !documentBufferView())
4252                                 hideDialog(fromqstr(dialog->name()), 0);
4253                         else if (dialog->isVisibleView())
4254                                 dialog->checkStatus();
4255                 }
4256         }
4257         updateToolbars();
4258         updateLayoutList();
4259 }
4260
4261 Dialog * createDialog(GuiView & lv, string const & name);
4262
4263 // will be replaced by a proper factory...
4264 Dialog * createGuiAbout(GuiView & lv);
4265 Dialog * createGuiBibtex(GuiView & lv);
4266 Dialog * createGuiChanges(GuiView & lv);
4267 Dialog * createGuiCharacter(GuiView & lv);
4268 Dialog * createGuiCitation(GuiView & lv);
4269 Dialog * createGuiCompare(GuiView & lv);
4270 Dialog * createGuiCompareHistory(GuiView & lv);
4271 Dialog * createGuiDelimiter(GuiView & lv);
4272 Dialog * createGuiDocument(GuiView & lv);
4273 Dialog * createGuiErrorList(GuiView & lv);
4274 Dialog * createGuiExternal(GuiView & lv);
4275 Dialog * createGuiGraphics(GuiView & lv);
4276 Dialog * createGuiInclude(GuiView & lv);
4277 Dialog * createGuiIndex(GuiView & lv);
4278 Dialog * createGuiListings(GuiView & lv);
4279 Dialog * createGuiLog(GuiView & lv);
4280 Dialog * createGuiMathMatrix(GuiView & lv);
4281 Dialog * createGuiNote(GuiView & lv);
4282 Dialog * createGuiParagraph(GuiView & lv);
4283 Dialog * createGuiPhantom(GuiView & lv);
4284 Dialog * createGuiPreferences(GuiView & lv);
4285 Dialog * createGuiPrint(GuiView & lv);
4286 Dialog * createGuiPrintindex(GuiView & lv);
4287 Dialog * createGuiRef(GuiView & lv);
4288 Dialog * createGuiSearch(GuiView & lv);
4289 Dialog * createGuiSearchAdv(GuiView & lv);
4290 Dialog * createGuiSendTo(GuiView & lv);
4291 Dialog * createGuiShowFile(GuiView & lv);
4292 Dialog * createGuiSpellchecker(GuiView & lv);
4293 Dialog * createGuiSymbols(GuiView & lv);
4294 Dialog * createGuiTabularCreate(GuiView & lv);
4295 Dialog * createGuiTexInfo(GuiView & lv);
4296 Dialog * createGuiToc(GuiView & lv);
4297 Dialog * createGuiThesaurus(GuiView & lv);
4298 Dialog * createGuiViewSource(GuiView & lv);
4299 Dialog * createGuiWrap(GuiView & lv);
4300 Dialog * createGuiProgressView(GuiView & lv);
4301
4302
4303
4304 Dialog * GuiView::build(string const & name)
4305 {
4306         LASSERT(isValidName(name), return 0);
4307
4308         Dialog * dialog = createDialog(*this, name);
4309         if (dialog)
4310                 return dialog;
4311
4312         if (name == "aboutlyx")
4313                 return createGuiAbout(*this);
4314         if (name == "bibtex")
4315                 return createGuiBibtex(*this);
4316         if (name == "changes")
4317                 return createGuiChanges(*this);
4318         if (name == "character")
4319                 return createGuiCharacter(*this);
4320         if (name == "citation")
4321                 return createGuiCitation(*this);
4322         if (name == "compare")
4323                 return createGuiCompare(*this);
4324         if (name == "comparehistory")
4325                 return createGuiCompareHistory(*this);
4326         if (name == "document")
4327                 return createGuiDocument(*this);
4328         if (name == "errorlist")
4329                 return createGuiErrorList(*this);
4330         if (name == "external")
4331                 return createGuiExternal(*this);
4332         if (name == "file")
4333                 return createGuiShowFile(*this);
4334         if (name == "findreplace")
4335                 return createGuiSearch(*this);
4336         if (name == "findreplaceadv")
4337                 return createGuiSearchAdv(*this);
4338         if (name == "graphics")
4339                 return createGuiGraphics(*this);
4340         if (name == "include")
4341                 return createGuiInclude(*this);
4342         if (name == "index")
4343                 return createGuiIndex(*this);
4344         if (name == "index_print")
4345                 return createGuiPrintindex(*this);
4346         if (name == "listings")
4347                 return createGuiListings(*this);
4348         if (name == "log")
4349                 return createGuiLog(*this);
4350         if (name == "mathdelimiter")
4351                 return createGuiDelimiter(*this);
4352         if (name == "mathmatrix")
4353                 return createGuiMathMatrix(*this);
4354         if (name == "note")
4355                 return createGuiNote(*this);
4356         if (name == "paragraph")
4357                 return createGuiParagraph(*this);
4358         if (name == "phantom")
4359                 return createGuiPhantom(*this);
4360         if (name == "prefs")
4361                 return createGuiPreferences(*this);
4362         if (name == "print")
4363                 return createGuiPrint(*this);
4364         if (name == "ref")
4365                 return createGuiRef(*this);
4366         if (name == "sendto")
4367                 return createGuiSendTo(*this);
4368         if (name == "spellchecker")
4369                 return createGuiSpellchecker(*this);
4370         if (name == "symbols")
4371                 return createGuiSymbols(*this);
4372         if (name == "tabularcreate")
4373                 return createGuiTabularCreate(*this);
4374         if (name == "texinfo")
4375                 return createGuiTexInfo(*this);
4376         if (name == "thesaurus")
4377                 return createGuiThesaurus(*this);
4378         if (name == "toc")
4379                 return createGuiToc(*this);
4380         if (name == "view-source")
4381                 return createGuiViewSource(*this);
4382         if (name == "wrap")
4383                 return createGuiWrap(*this);
4384         if (name == "progress")
4385                 return createGuiProgressView(*this);
4386
4387         return 0;
4388 }
4389
4390
4391 } // namespace frontend
4392 } // namespace lyx
4393
4394 #include "moc_GuiView.cpp"