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