#include "GuiView.h"
-#include "GuiImplementation.h"
+#include "Dialog.h"
+#include "frontends/FileDialog.h"
+#include "GuiApplication.h"
#include "GuiWorkArea.h"
#include "GuiKeySymbol.h"
#include "GuiMenubar.h"
#include "GuiToolbar.h"
#include "GuiToolbars.h"
-#include "qt_helpers.h"
+#include "GuiProgress.h"
-#include "frontends/Application.h"
-#include "frontends/Dialogs.h"
-#include "frontends/Gui.h"
-#include "frontends/WorkArea.h"
+#include "qt_helpers.h"
-#include "support/filetools.h"
-#include "support/convert.h"
-#include "support/lstrings.h"
-#include "support/os.h"
+#include "frontends/alert.h"
+#include "buffer_funcs.h"
#include "Buffer.h"
+#include "BufferList.h"
#include "BufferParams.h"
#include "BufferView.h"
-#include "BufferList.h"
#include "Cursor.h"
-#include "debug.h"
+#include "support/debug.h"
+#include "ErrorList.h"
#include "FuncRequest.h"
+#include "support/gettext.h"
+#include "Intl.h"
#include "Layout.h"
-#include "LyX.h"
+#include "Lexer.h"
#include "LyXFunc.h"
+#include "LyX.h"
#include "LyXRC.h"
+#include "LyXVC.h"
#include "MenuBackend.h"
#include "Paragraph.h"
-#include "Session.h"
+#include "TextClass.h"
+#include "Text.h"
#include "ToolbarBackend.h"
#include "version.h"
-#include <boost/current_function.hpp>
+#include "support/FileFilterList.h"
+#include "support/FileName.h"
+#include "support/filetools.h"
+#include "support/lstrings.h"
+#include "support/os.h"
+#include "support/Package.h"
+#include "support/Timeout.h"
#include <QAction>
#include <QApplication>
#include <QCloseEvent>
+#include <QDebug>
#include <QDesktopWidget>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMenu>
#include <QPainter>
#include <QPixmap>
+#include <QPoint>
#include <QPushButton>
+#include <QSettings>
+#include <QShowEvent>
+#include <QSplitter>
#include <QStackedWidget>
#include <QStatusBar>
+#include <QTimer>
#include <QToolBar>
#include <QUrl>
+#include <boost/assert.hpp>
+#include <boost/bind.hpp>
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
using std::endl;
using std::string;
using std::vector;
namespace lyx {
-using support::FileName;
-using support::libFileSearch;
-using support::makeDisplayPath;
-
extern bool quitting;
namespace frontend {
-namespace {
+using support::addPath;
+using support::bformat;
+using support::FileFilterList;
+using support::FileName;
+using support::makeAbsPath;
+using support::makeDisplayPath;
+using support::package;
+using support::removeAutosaveFile;
+using support::trim;
-int const statusbar_timer_value = 3000;
+namespace {
class BackgroundWidget : public QWidget
{
public:
- BackgroundWidget(QString const & file, QString const & text)
+ BackgroundWidget()
{
- splash_ = new QPixmap(file);
- if (!splash_) {
- lyxerr << "could not load splash screen: '" << fromqstr(file) << "'" << endl;
- return;
- }
+ LYXERR(Debug::GUI, "show banner: " << lyxrc.show_banner);
+ /// The text to be written on top of the pixmap
+ QString const text = lyx_version ? lyx_version : qt_("unknown version");
+ splash_ = QPixmap(":/images/banner.png");
- QPainter pain(splash_);
+ QPainter pain(&splash_);
pain.setPen(QColor(255, 255, 0));
QFont font;
// The font used to display the version info
font.setStyleHint(QFont::SansSerif);
font.setWeight(QFont::Bold);
- font.setPointSize(convert<int>(lyxrc.font_sizes[Font::SIZE_LARGE]));
+ font.setPointSize(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
pain.setFont(font);
pain.drawText(260, 270, text);
}
void paintEvent(QPaintEvent *)
{
- if (!splash_)
- return;
-
- int x = (width() - splash_->width()) / 2;
- int y = (height() - splash_->height()) / 2;
+ int x = (width() - splash_.width()) / 2;
+ int y = (height() - splash_.height()) / 2;
QPainter pain(this);
- pain.drawPixmap(x, y, *splash_);
+ pain.drawPixmap(x, y, splash_);
}
private:
- QPixmap * splash_;
+ QPixmap splash_;
};
-};
-
-
-struct GuiViewBase::GuiViewPrivate
-{
- string cur_title;
+} // namespace anon
- int posx_offset;
- int posy_offset;
- TabWorkArea * tab_widget_;
- QStackedWidget * stack_widget_;
- BackgroundWidget * bg_widget_;
- /// view's menubar
- GuiMenubar * menubar_;
- /// view's toolbars
- GuiToolbars * toolbars_;
- ///
- docstring current_layout;
+typedef boost::shared_ptr<Dialog> DialogPtr;
- GuiViewPrivate() : posx_offset(0), posy_offset(0) {}
+struct GuiView::GuiViewPrivate
+{
+ GuiViewPrivate()
+ : current_work_area_(0), layout_(0),
+ quitting_by_menu_(false), autosave_timeout_(5000), in_show_(false)
+ {
+ // hardcode here the platform specific icon size
+ smallIconSize = 14; // scaling problems
+ normalIconSize = 20; // ok, default
+ bigIconSize = 26; // better for some math icons
+
+ splitter_ = new QSplitter;
+ bg_widget_ = new BackgroundWidget;
+ stack_widget_ = new QStackedWidget;
+ stack_widget_->addWidget(bg_widget_);
+ stack_widget_->addWidget(splitter_);
+ setBackground();
+ }
- unsigned int smallIconSize;
- unsigned int normalIconSize;
- unsigned int bigIconSize;
- // static needed by "New Window"
- static unsigned int lastIconSize;
+ ~GuiViewPrivate()
+ {
+ delete splitter_;
+ delete bg_widget_;
+ delete stack_widget_;
+ delete menubar_;
+ delete toolbars_;
+ }
- QMenu * toolBarPopup(GuiViewBase * parent)
+ QMenu * toolBarPopup(GuiView * parent)
{
// FIXME: translation
QMenu * menu = new QMenu(parent);
QAction * smallIcons = new QAction(iconSizeGroup);
smallIcons->setText(qt_("Small-sized icons"));
smallIcons->setCheckable(true);
- QObject::connect(smallIcons, SIGNAL(triggered()), parent, SLOT(smallSizedIcons()));
+ QObject::connect(smallIcons, SIGNAL(triggered()),
+ parent, SLOT(smallSizedIcons()));
menu->addAction(smallIcons);
QAction * normalIcons = new QAction(iconSizeGroup);
normalIcons->setText(qt_("Normal-sized icons"));
normalIcons->setCheckable(true);
- QObject::connect(normalIcons, SIGNAL(triggered()), parent, SLOT(normalSizedIcons()));
+ QObject::connect(normalIcons, SIGNAL(triggered()),
+ parent, SLOT(normalSizedIcons()));
menu->addAction(normalIcons);
QAction * bigIcons = new QAction(iconSizeGroup);
bigIcons->setText(qt_("Big-sized icons"));
bigIcons->setCheckable(true);
- QObject::connect(bigIcons, SIGNAL(triggered()), parent, SLOT(bigSizedIcons()));
+ QObject::connect(bigIcons, SIGNAL(triggered()),
+ parent, SLOT(bigSizedIcons()));
menu->addAction(bigIcons);
unsigned int cur = parent->iconSize().width();
return menu;
}
- void initBackground()
+ void setBackground()
{
- bg_widget_ = 0;
- LYXERR(Debug::GUI) << "show banner: " << lyxrc.show_banner << endl;
- /// The text to be written on top of the pixmap
- QString const text = lyx_version ? QString(lyx_version) : qt_("unknown version");
- FileName const file = support::libFileSearch("images", "banner", "png");
- if (file.empty())
- return;
+ stack_widget_->setCurrentWidget(bg_widget_);
+ bg_widget_->setUpdatesEnabled(true);
+ }
- bg_widget_ = new BackgroundWidget(toqstr(file.absFilename()), text);
+ TabWorkArea * tabWorkArea(int i)
+ {
+ return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
}
- void setBackground()
+ TabWorkArea * currentTabWorkArea()
{
- if (!bg_widget_)
- return;
+ if (splitter_->count() == 1)
+ // The first TabWorkArea is always the first one, if any.
+ return tabWorkArea(0);
+
+ TabWorkArea * tab_widget = 0;
+ for (int i = 0; i != splitter_->count(); ++i) {
+ QWidget * w = splitter_->widget(i);
+ if (!w->hasFocus())
+ continue;
+ tab_widget = dynamic_cast<TabWorkArea *>(w);
+ if (tab_widget)
+ break;
+ }
- stack_widget_->setCurrentWidget(bg_widget_);
- bg_widget_->setUpdatesEnabled(true);
+ return tab_widget;
}
-};
+public:
+ GuiWorkArea * current_work_area_;
+ QSplitter * splitter_;
+ QStackedWidget * stack_widget_;
+ BackgroundWidget * bg_widget_;
+ /// view's menubar
+ GuiMenubar * menubar_;
+ /// view's toolbars
+ GuiToolbars * toolbars_;
+ /// The main layout box.
+ /**
+ * \warning Don't Delete! The layout box is actually owned by
+ * whichever toolbar contains it. All the GuiView class needs is a
+ * means of accessing it.
+ *
+ * FIXME: replace that with a proper model so that we are not limited
+ * to only one dialog.
+ */
+ GuiLayoutBox * layout_;
+
+ ///
+ std::map<std::string, Inset *> open_insets_;
+
+ ///
+ std::map<std::string, DialogPtr> dialogs_;
-unsigned int GuiViewBase::GuiViewPrivate::lastIconSize = 0;
+ unsigned int smallIconSize;
+ unsigned int normalIconSize;
+ unsigned int bigIconSize;
+ ///
+ QTimer statusbar_timer_;
+ /// are we quitting by the menu?
+ bool quitting_by_menu_;
+ /// auto-saving of buffers
+ Timeout autosave_timeout_;
+ /// flag against a race condition due to multiclicks, see bug #1119
+ bool in_show_;
+};
-GuiViewBase::GuiViewBase(int id)
- : QMainWindow(), LyXView(id), quitting_by_menu_(false),
- d(*new GuiViewPrivate)
+GuiView::GuiView(int id)
+ : d(*new GuiViewPrivate), id_(id)
{
+ // GuiToolbars *must* be initialised before GuiMenubar.
+ d.toolbars_ = new GuiToolbars(*this);
+ d.menubar_ = new GuiMenubar(this, menubackend);
+
+ setCentralWidget(d.stack_widget_);
+
+ // Start autosave timer
+ if (lyxrc.autosave) {
+ d.autosave_timeout_.timeout.connect(boost::bind(&GuiView::autoSave, this));
+ d.autosave_timeout_.setTimeout(lyxrc.autosave * 1000);
+ d.autosave_timeout_.start();
+ }
+ connect(&d.statusbar_timer_, SIGNAL(timeout()),
+ this, SLOT(clearMessage()));
+
// Qt bug? signal lastWindowClosed does not work
setAttribute(Qt::WA_QuitOnClose, false);
setAttribute(Qt::WA_DeleteOnClose, true);
-
- // hardcode here the platform specific icon size
- d.smallIconSize = 14; // scaling problems
- d.normalIconSize = 20; // ok, default
- d.bigIconSize = 26; // better for some math icons
-
#ifndef Q_WS_MACX
// assign an icon to main form. We do not do it under Qt/Mac,
// since the icon is provided in the application bundle.
- FileName const iconname = libFileSearch("images", "lyx", "png");
- if (!iconname.empty())
- setWindowIcon(QPixmap(toqstr(iconname.absFilename())));
+ setWindowIcon(QPixmap(":/images/lyx.png"));
#endif
- d.tab_widget_ = new TabWorkArea;
- QObject::connect(d.tab_widget_, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
- this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
-
- d.initBackground();
- if (d.bg_widget_) {
- LYXERR(Debug::GUI) << "stack widget!" << endl;
- d.stack_widget_ = new QStackedWidget;
- d.stack_widget_->addWidget(d.bg_widget_);
- d.stack_widget_->addWidget(d.tab_widget_);
- setCentralWidget(d.stack_widget_);
- } else {
- d.stack_widget_ = 0;
- setCentralWidget(d.tab_widget_);
- }
-
// For Drag&Drop.
setAcceptDrops(true);
+
+ statusBar()->setSizeGripEnabled(true);
+
+ // Forbid too small unresizable window because it can happen
+ // with some window manager under X11.
+ setMinimumSize(300, 200);
+
+ if (!lyxrc.allow_geometry_session)
+ // No session handling, default to a sane size.
+ setGeometry(50, 50, 690, 510);
+
+ // Now take care of session management.
+ QSettings settings;
+ QString const key = "view-" + QString::number(id_);
+#ifdef Q_WS_X11
+ QPoint pos = settings.value(key + "/pos", QPoint(50, 50)).toPoint();
+ QSize size = settings.value(key + "/size", QSize(690, 510)).toSize();
+ resize(size);
+ move(pos);
+#else
+ if (!restoreGeometry(settings.value(key + "/geometry").toByteArray()))
+ setGeometry(50, 50, 690, 510);
+#endif
+ setIconSize(settings.value(key + "/icon_size").toSize());
}
-GuiViewBase::~GuiViewBase()
+GuiView::~GuiView()
{
- delete d.menubar_;
- delete d.toolbars_;
delete &d;
}
-void GuiViewBase::close()
+void GuiView::close()
{
- quitting_by_menu_ = true;
- d.tab_widget_->closeAll();
+ d.quitting_by_menu_ = true;
+ d.current_work_area_ = 0;
+ for (int i = 0; i != d.splitter_->count(); ++i) {
+ TabWorkArea * twa = d.tabWorkArea(i);
+ if (twa)
+ twa->closeAll();
+ }
QMainWindow::close();
- quitting_by_menu_ = false;
+ d.quitting_by_menu_ = false;
}
-void GuiViewBase::setFocus()
+void GuiView::setFocus()
{
- if (d.tab_widget_->count())
- d.tab_widget_->currentWidget()->setFocus();
+ if (d.current_work_area_)
+ d.current_work_area_->setFocus();
+ else
+ QWidget::setFocus();
}
-QMenu* GuiViewBase::createPopupMenu()
+QMenu * GuiView::createPopupMenu()
{
return d.toolBarPopup(this);
}
-void GuiViewBase::init()
+void GuiView::showEvent(QShowEvent * e)
{
- // GuiToolbars *must* be initialised before GuiMenubar.
- d.toolbars_ = new GuiToolbars(*this);
- // FIXME: GuiToolbars::init() cannot be integrated in the ctor
- // because LyXFunc::getStatus() needs a properly initialized
- // GuiToolbars object (for LFUN_TOOLBAR_TOGGLE).
- d.toolbars_->init();
- d.menubar_ = new GuiMenubar(this, menubackend);
-
- statusBar()->setSizeGripEnabled(true);
+ LYXERR(Debug::GUI, "Passed Geometry "
+ << size().height() << "x" << size().width()
+ << "+" << pos().x() << "+" << pos().y());
- QObject::connect(&statusbar_timer_, SIGNAL(timeout()),
- this, SLOT(update_view_state_qt()));
+ if (d.splitter_->count() == 0)
+ // No work area, switch to the background widget.
+ d.setBackground();
- if (d.stack_widget_)
- d.stack_widget_->setCurrentWidget(d.bg_widget_);
+ QMainWindow::showEvent(e);
}
-void GuiViewBase::closeEvent(QCloseEvent * close_event)
+void GuiView::closeEvent(QCloseEvent * close_event)
{
// we may have been called through the close window button
// which bypasses the LFUN machinery.
- if (!quitting_by_menu_ && theApp()->gui().viewIds().size() == 1) {
- if (!theBufferList().quitWriteAll()) {
+ if (!d.quitting_by_menu_ && guiApp->viewCount() == 1) {
+ if (!quitWriteAll()) {
close_event->ignore();
return;
}
// Make sure that no LFUN use this close to be closed View.
theLyXFunc().setLyXView(0);
// Make sure the timer time out will not trigger a statusbar update.
- statusbar_timer_.stop();
+ d.statusbar_timer_.stop();
+
+ if (lyxrc.allow_geometry_session) {
+ QSettings settings;
+ QString const key = "view-" + QString::number(id_);
+#ifdef Q_WS_X11
+ settings.setValue(key + "/pos", pos());
+ settings.setValue(key + "/size", size());
+#else
+ settings.setValue(key + "/geometry", saveGeometry());
+#endif
+ settings.setValue(key + "/icon_size", iconSize());
+ d.toolbars_->saveToolbarInfo();
+ }
- theApp()->gui().unregisterView(id());
- if (!theApp()->gui().viewIds().empty()) {
+ guiApp->unregisterView(id_);
+ if (guiApp->viewCount() > 0) {
// Just close the window and do nothing else if this is not the
// last window.
close_event->accept();
// this is the place where we leave the frontend.
// it is the only point at which we start quitting.
- saveGeometry();
close_event->accept();
// quit the event loop
qApp->quit();
}
-void GuiViewBase::dragEnterEvent(QDragEnterEvent * event)
+void GuiView::dragEnterEvent(QDragEnterEvent * event)
{
if (event->mimeData()->hasUrls())
event->accept();
}
-void GuiViewBase::dropEvent(QDropEvent* event)
+void GuiView::dropEvent(QDropEvent* event)
{
QList<QUrl> files = event->mimeData()->urls();
if (files.isEmpty())
return;
- LYXERR(Debug::GUI) << BOOST_CURRENT_FUNCTION
- << " got URLs!" << endl;
+ LYXERR(Debug::GUI, "GuiView::dropEvent: got URLs!");
for (int i = 0; i != files.size(); ++i) {
string const file = support::os::internal_path(fromqstr(
files.at(i).toLocalFile()));
}
-void GuiViewBase::saveGeometry()
-{
- static bool done = false;
- if (done)
- return;
- else
- done = true;
-
- // FIXME:
- // change the ifdef to 'geometry = normalGeometry();' only
- // when Trolltech has fixed the broken normalGeometry on X11:
- // http://www.trolltech.com/developer/task-tracker/index_html?id=119684+&method=entry
- // Then also the moveEvent, resizeEvent, and the
- // code for floatingGeometry_ can be removed;
- // adjust GuiViewBase::setGeometry()
-
- QRect normal_geometry;
- int maximized;
-#ifdef Q_WS_WIN
- normal_geometry = normalGeometry();
- if (isMaximized()) {
- maximized = CompletelyMaximized;
- } else {
- maximized = NotMaximized;
- }
-#else
- normal_geometry = updateFloatingGeometry();
-
- QDesktopWidget& dw = *qApp->desktop();
- QRect desk = dw.availableGeometry(dw.primaryScreen());
- // Qt bug on Linux: load completely maximized, vert max. save-> frameGeometry().height() is wrong
- if (isMaximized() && desk.width() <= frameGeometry().width() && desk.height() <= frameGeometry().height()) {
- maximized = CompletelyMaximized;
- // maximizing does not work when the window is allready hor. or vert. maximized
- // Tested only on KDE
- int dh = frameGeometry().height() - height();
- if (desk.height() <= normal_geometry.height() + dh)
- normal_geometry.setHeight(normal_geometry.height() - 1);
- int dw = frameGeometry().width() - width();
- if (desk.width() <= normal_geometry.width() + dw)
- normal_geometry.setWidth(normal_geometry.width() - 1);
- } else if (desk.height() <= frameGeometry().height()) {
- maximized = VerticallyMaximized;
- } else if (desk.width() <= frameGeometry().width()) {
- maximized = HorizontallyMaximized;
- } else {
- maximized = NotMaximized;
- }
-
-
-#endif
- // save windows size and position
- Session & session = LyX::ref().session();
- session.sessionInfo().save("WindowWidth", convert<string>(normal_geometry.width()));
- session.sessionInfo().save("WindowHeight", convert<string>(normal_geometry.height()));
- session.sessionInfo().save("WindowMaximized", convert<string>(maximized));
- session.sessionInfo().save("IconSizeXY", convert<string>(iconSize().width()));
- if (lyxrc.geometry_xysaved) {
- session.sessionInfo().save("WindowPosX", convert<string>(normal_geometry.x() + d.posx_offset));
- session.sessionInfo().save("WindowPosY", convert<string>(normal_geometry.y() + d.posy_offset));
- }
- d.toolbars_->saveToolbarInfo();
-}
-
-
-void GuiViewBase::setGeometry(unsigned int width,
- unsigned int height,
- int posx, int posy,
- int maximized,
- unsigned int iconSizeXY,
- const string & geometryArg)
-{
- // use last value (not at startup)
- if (d.lastIconSize != 0)
- setIconSize(d.lastIconSize);
- else if (iconSizeXY != 0)
- setIconSize(iconSizeXY);
- else
- setIconSize(d.normalIconSize);
-
- // only true when the -geometry option was NOT used
- if (width != 0 && height != 0) {
- if (posx != -1 && posy != -1) {
- // if there are startup positioning problems:
- // http://doc.trolltech.com/4.2/qdesktopwidget.html
- QDesktopWidget& dw = *qApp->desktop();
- if (dw.isVirtualDesktop()) {
- if(!dw.geometry().contains(posx, posy)) {
- posx = 50;
- posy = 50;
- }
- } else {
- // Which system doesn't use a virtual desktop?
- // TODO save also last screen number and check if it is still availabe.
- }
-#ifdef Q_WS_WIN
- // FIXME: use setGeometry only when Trolltech has fixed the qt4/X11 bug
- QWidget::setGeometry(posx, posy, width, height);
-#else
- resize(width, height);
- move(posx, posy);
-#endif
- } else {
- resize(width, height);
- }
-
- // remember original size
- floatingGeometry_ = QRect(posx, posy, width, height);
-
- if (maximized != NotMaximized) {
- if (maximized == CompletelyMaximized) {
- setWindowState(Qt::WindowMaximized);
- } else {
-#ifndef Q_WS_WIN
- // TODO How to set by the window manager?
- // setWindowState(Qt::WindowVerticallyMaximized);
- // is not possible
- QDesktopWidget& dw = *qApp->desktop();
- QRect desk = dw.availableGeometry(dw.primaryScreen());
- if (maximized == VerticallyMaximized)
- resize(width, desk.height());
- if (maximized == HorizontallyMaximized)
- resize(desk.width(), height);
-#endif
- }
- }
- }
- else
- {
- // FIXME: move this code into parse_geometry() (LyX.cpp)
-#ifdef Q_WS_WIN
- int x, y;
- int w, h;
- QRegExp re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)([+-][0-9]*)){0,1}" );
- re.indexIn(toqstr(geometryArg.c_str()));
- w = re.cap(1).toInt();
- h = re.cap(2).toInt();
- x = re.cap(3).toInt();
- y = re.cap(4).toInt();
- QWidget::setGeometry( x, y, w, h );
-#else
- // silence warning
- (void)geometryArg;
-#endif
- }
-
- d.setBackground();
-
- show();
-
- // For an unknown reason, the Window title update is not effective for
- // the second windows up until it is shown on screen (Qt bug?).
- updateWindowTitle();
-
- // after show geometry() has changed (Qt bug?)
- // we compensate the drift when storing the position
- d.posx_offset = 0;
- d.posy_offset = 0;
- if (width != 0 && height != 0)
- if (posx != -1 && posy != -1) {
-#ifdef Q_WS_WIN
- d.posx_offset = posx - normalGeometry().x();
- d.posy_offset = posy - normalGeometry().y();
-#else
-#ifndef Q_WS_MACX
- if (maximized == NotMaximized) {
- d.posx_offset = posx - geometry().x();
- d.posy_offset = posy - geometry().y();
- }
-#endif
-#endif
- }
-}
-
-
-void GuiViewBase::setWindowTitle(docstring const & t, docstring const & it)
-{
- QString title = windowTitle();
- QString new_title = toqstr(t);
- if (title != new_title) {
- QMainWindow::setWindowTitle(new_title);
- QMainWindow::setWindowIconText(toqstr(it));
- }
- if (Buffer const * buf = buffer())
- d.tab_widget_->setTabText(d.tab_widget_->currentIndex(),
- toqstr(makeDisplayPath(buf->fileName(), 30)));
-}
-
-
-void GuiViewBase::message(docstring const & str)
+void GuiView::message(docstring const & str)
{
statusBar()->showMessage(toqstr(str));
- statusbar_timer_.stop();
- statusbar_timer_.start(statusbar_timer_value);
-}
-
-
-void GuiViewBase::clearMessage()
-{
- update_view_state_qt();
+ d.statusbar_timer_.stop();
+ d.statusbar_timer_.start(3000);
}
-void GuiViewBase::setIconSize(unsigned int size)
+void GuiView::smallSizedIcons()
{
- d.lastIconSize = size;
- QMainWindow::setIconSize(QSize(size, size));
+ setIconSize(QSize(d.smallIconSize, d.smallIconSize));
}
-void GuiViewBase::smallSizedIcons()
+void GuiView::normalSizedIcons()
{
- setIconSize(d.smallIconSize);
+ setIconSize(QSize(d.normalIconSize, d.normalIconSize));
}
-void GuiViewBase::normalSizedIcons()
+void GuiView::bigSizedIcons()
{
- setIconSize(d.normalIconSize);
+ setIconSize(QSize(d.bigIconSize, d.bigIconSize));
}
-void GuiViewBase::bigSizedIcons()
+void GuiView::clearMessage()
{
- setIconSize(d.bigIconSize);
+ if (!hasFocus())
+ return;
+ theLyXFunc().setLyXView(this);
+ statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
+ d.statusbar_timer_.stop();
}
-void GuiViewBase::update_view_state_qt()
+void GuiView::updateWindowTitle(GuiWorkArea * wa)
{
- if (!hasFocus())
+ if (wa != d.current_work_area_)
return;
- theLyXFunc().setLyXView(this);
- statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
- statusbar_timer_.stop();
+ setWindowTitle(qt_("LyX: ") + wa->windowTitle());
+ setWindowIconText(wa->windowIconText());
}
-void GuiViewBase::on_currentWorkAreaChanged(GuiWorkArea * wa)
+void GuiView::on_currentWorkAreaChanged(GuiWorkArea * wa)
{
disconnectBuffer();
disconnectBufferView();
connectBufferView(wa->bufferView());
connectBuffer(wa->bufferView().buffer());
+ d.current_work_area_ = wa;
+ QObject::connect(wa, SIGNAL(titleChanged(GuiWorkArea *)),
+ this, SLOT(updateWindowTitle(GuiWorkArea *)));
+ updateWindowTitle(wa);
updateToc();
// Buffer-dependent dialogs should be updated or
// hidden. This should go here because some dialogs (eg ToC)
// require bv_->text.
- getDialogs().updateBufferDependent(true);
+ updateBufferDependent(true);
updateToolbars();
- updateLayoutChoice();
- updateWindowTitle();
+ updateLayoutList();
updateStatusBar();
}
-void GuiViewBase::updateStatusBar()
+void GuiView::updateStatusBar()
{
// let the user see the explicit message
- if (statusbar_timer_.isActive())
+ if (d.statusbar_timer_.isActive())
return;
statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
}
-void GuiViewBase::activated(FuncRequest const & func)
-{
- dispatch(func);
-}
-
-
-bool GuiViewBase::hasFocus() const
+bool GuiView::hasFocus() const
{
return qApp->activeWindow() == this;
}
-QRect GuiViewBase::updateFloatingGeometry()
-{
- QDesktopWidget& dw = *qApp->desktop();
- QRect desk = dw.availableGeometry(dw.primaryScreen());
- // remember only non-maximized sizes
- if (!isMaximized() && desk.width() > frameGeometry().width() && desk.height() > frameGeometry().height()) {
- floatingGeometry_ = QRect(x(), y(), width(), height());
- }
- return floatingGeometry_;
-}
-
-
-void GuiViewBase::resizeEvent(QResizeEvent *)
-{
- updateFloatingGeometry();
-}
-
-
-void GuiViewBase::moveEvent(QMoveEvent *)
-{
- updateFloatingGeometry();
-}
-
-
-bool GuiViewBase::event(QEvent * e)
+bool GuiView::event(QEvent * e)
{
switch (e->type())
{
//case QEvent::Drop:
// break;
- case QEvent::WindowActivate:
- theApp()->setCurrentView(*this);
- if (d.tab_widget_->count()) {
- GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(
- d.tab_widget_->currentWidget());
- BOOST_ASSERT(wa);
- BufferView & bv = wa->bufferView();
+ case QEvent::WindowActivate: {
+ guiApp->setCurrentView(*this);
+ if (d.current_work_area_) {
+ BufferView & bv = d.current_work_area_->bufferView();
connectBufferView(bv);
connectBuffer(bv.buffer());
// The document structure, name and dialogs might have
// changed in another view.
- updateWindowTitle();
- getDialogs().updateBufferDependent(true);
+ updateBufferDependent(true);
+ } else {
+ setWindowTitle(qt_("LyX"));
+ setWindowIconText(qt_("LyX"));
}
return QMainWindow::event(e);
-
+ }
case QEvent::ShortcutOverride: {
QKeyEvent * ke = static_cast<QKeyEvent*>(e);
- if (d.tab_widget_->count() == 0) {
+ if (!d.current_work_area_) {
theLyXFunc().setLyXView(this);
KeySymbol sym;
setKeySymbol(&sym, ke);
if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
KeySymbol sym;
setKeySymbol(&sym, ke);
- currentWorkArea()->processKeySym(sym, NoModifier);
+ d.current_work_area_->processKeySym(sym, NoModifier);
e->accept();
return true;
}
}
-bool GuiViewBase::focusNextPrevChild(bool /*next*/)
+bool GuiView::focusNextPrevChild(bool /*next*/)
{
setFocus();
return true;
}
-void GuiViewBase::showView()
-{
- QMainWindow::setWindowTitle(qt_("LyX"));
- QMainWindow::show();
- updateFloatingGeometry();
-}
-
-
-void GuiViewBase::busy(bool yes)
+void GuiView::setBusy(bool yes)
{
- if (d.tab_widget_->count()) {
- GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(d.tab_widget_->currentWidget());
- BOOST_ASSERT(wa);
- wa->setUpdatesEnabled(!yes);
+ if (d.current_work_area_) {
+ d.current_work_area_->setUpdatesEnabled(!yes);
if (yes)
- wa->stopBlinkingCursor();
+ d.current_work_area_->stopBlinkingCursor();
else
- wa->startBlinkingCursor();
+ d.current_work_area_->startBlinkingCursor();
}
if (yes)
}
-GuiToolbar * GuiViewBase::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
+GuiToolbar * GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
{
GuiToolbar * toolBar = new GuiToolbar(tbinfo, *this);
}
-WorkArea * GuiViewBase::workArea(Buffer & buffer)
+GuiWorkArea * GuiView::workArea(Buffer & buffer)
{
- for (int i = 0; i != d.tab_widget_->count(); ++i) {
- GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(d.tab_widget_->widget(i));
- BOOST_ASSERT(wa);
- if (&wa->bufferView().buffer() == &buffer)
+ for (int i = 0; i != d.splitter_->count(); ++i) {
+ GuiWorkArea * wa = d.tabWorkArea(i)->workArea(buffer);
+ if (wa)
return wa;
}
return 0;
}
-WorkArea * GuiViewBase::addWorkArea(Buffer & buffer)
+GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
{
- GuiWorkArea * wa = new GuiWorkArea(buffer, *this);
- wa->setUpdatesEnabled(false);
- d.tab_widget_->addTab(wa, toqstr(makeDisplayPath(buffer.fileName(), 30)));
- wa->bufferView().updateMetrics(false);
- if (d.stack_widget_)
- d.stack_widget_->setCurrentWidget(d.tab_widget_);
- // Hide tabbar if there's only one tab.
- d.tab_widget_->showBar(d.tab_widget_->count() > 1);
- return wa;
+
+ // Automatically create a TabWorkArea if there are none yet.
+ if (!d.splitter_->count())
+ addTabWorkArea();
+
+ TabWorkArea * tab_widget = d.currentTabWorkArea();
+ return tab_widget->addWorkArea(buffer, *this);
}
-WorkArea * GuiViewBase::currentWorkArea()
+void GuiView::addTabWorkArea()
{
- if (d.tab_widget_->count() == 0)
- return 0;
- BOOST_ASSERT(dynamic_cast<GuiWorkArea *>(d.tab_widget_->currentWidget()));
- return dynamic_cast<GuiWorkArea *>(d.tab_widget_->currentWidget());
+ TabWorkArea * twa = new TabWorkArea;
+ QObject::connect(twa, SIGNAL(currentWorkAreaChanged(GuiWorkArea *)),
+ this, SLOT(on_currentWorkAreaChanged(GuiWorkArea *)));
+ d.splitter_->addWidget(twa);
+ d.stack_widget_->setCurrentWidget(d.splitter_);
}
-WorkArea const * GuiViewBase::currentWorkArea() const
+GuiWorkArea const * GuiView::currentWorkArea() const
{
- if (d.tab_widget_->count() == 0)
- return 0;
- BOOST_ASSERT(dynamic_cast<GuiWorkArea const *>(d.tab_widget_->currentWidget()));
- return dynamic_cast<GuiWorkArea const *>(d.tab_widget_->currentWidget());
+ return d.current_work_area_;
}
-void GuiViewBase::setCurrentWorkArea(WorkArea * work_area)
+void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
{
- BOOST_ASSERT(work_area);
+ BOOST_ASSERT(wa);
// Changing work area can result from opening a file so
// update the toc in any case.
updateToc();
- GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(work_area);
- BOOST_ASSERT(wa);
- d.tab_widget_->setCurrentWorkArea(wa);
+ d.current_work_area_ = wa;
+ for (int i = 0; i != d.splitter_->count(); ++i) {
+ if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
+ return;
+ }
}
-void GuiViewBase::removeWorkArea(WorkArea * work_area)
+void GuiView::removeWorkArea(GuiWorkArea * wa)
{
- BOOST_ASSERT(work_area);
- if (work_area == currentWorkArea()) {
+ BOOST_ASSERT(wa);
+ if (wa == d.current_work_area_) {
disconnectBuffer();
disconnectBufferView();
+ hideBufferDependent();
+ d.current_work_area_ = 0;
}
- // removing a work area often results from closing a file so
- // update the toc in any case.
- updateToc();
+ for (int i = 0; i != d.splitter_->count(); ++i) {
+ TabWorkArea * twa = d.tabWorkArea(i);
+ if (!twa->removeWorkArea(wa))
+ // Not found in this tab group.
+ continue;
+
+ // We found and removed the GuiWorkArea.
+ if (!twa->count()) {
+ // No more WorkAreas in this tab group, so delete it.
+ delete twa;
+ break;
+ }
- GuiWorkArea * gwa = dynamic_cast<GuiWorkArea *>(work_area);
- BOOST_ASSERT(gwa);
- d.tab_widget_->removeWorkArea(gwa);
+ if (d.current_work_area_)
+ // This means that we are not closing the current GuiWorkArea;
+ break;
- getDialogs().hideBufferDependent();
+ // Switch to the next GuiWorkArea in the found TabWorkArea.
+ d.current_work_area_ = twa->currentWorkArea();
+ break;
+ }
- if (d.tab_widget_->count() == 0 && d.stack_widget_)
+ if (d.splitter_->count() == 0)
// No more work area, switch to the background widget.
d.setBackground();
}
-void GuiViewBase::showMiniBuffer(bool visible)
+void GuiView::setLayoutDialog(GuiLayoutBox * layout)
{
- d.toolbars_->showCommandBuffer(visible);
+ d.layout_ = layout;
}
-void GuiViewBase::openMenu(docstring const & name)
+void GuiView::updateLayoutList()
{
- d.menubar_->openByName(toqstr(name));
+ if (d.layout_)
+ d.layout_->updateContents(false);
}
-void GuiViewBase::openLayoutList()
+void GuiView::updateToolbars()
{
- d.toolbars_->openLayoutList();
+ if (d.current_work_area_) {
+ bool const math =
+ d.current_work_area_->bufferView().cursor().inMathed();
+ bool const table =
+ lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
+ bool const review =
+ lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
+ lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
+
+ d.toolbars_->update(math, table, review);
+ } else
+ d.toolbars_->update(false, false, false);
+
+ // update read-only status of open dialogs.
+ checkStatus();
}
-void GuiViewBase::updateLayoutChoice()
+Buffer * GuiView::buffer()
{
- // Don't show any layouts without a buffer
- if (!buffer()) {
- d.toolbars_->clearLayoutList();
- return;
- }
+ if (d.current_work_area_)
+ return &d.current_work_area_->bufferView().buffer();
+ return 0;
+}
- // Update the layout display
- if (d.toolbars_->updateLayoutList(buffer()->params().getTextClassPtr())) {
- d.current_layout = buffer()->params().getTextClass().defaultLayoutName();
- }
- docstring const & layout = currentWorkArea()->bufferView().cursor().
- innerParagraph().layout()->name();
+Buffer const * GuiView::buffer() const
+{
+ if (d.current_work_area_)
+ return &d.current_work_area_->bufferView().buffer();
+ return 0;
+}
+
- if (layout != d.current_layout) {
- d.toolbars_->setLayout(layout);
- d.current_layout = layout;
+void GuiView::setBuffer(Buffer * newBuffer)
+{
+ BOOST_ASSERT(newBuffer);
+ setBusy(true);
+
+ GuiWorkArea * wa = workArea(*newBuffer);
+ if (wa == 0) {
+ updateLabels(*newBuffer->masterBuffer());
+ wa = addWorkArea(*newBuffer);
+ } else {
+ //Disconnect the old buffer...there's no new one.
+ disconnectBuffer();
}
+ connectBuffer(*newBuffer);
+ connectBufferView(wa->bufferView());
+ setCurrentWorkArea(wa);
+
+ setBusy(false);
}
-bool GuiViewBase::isToolbarVisible(std::string const & id)
+void GuiView::connectBuffer(Buffer & buf)
{
- return d.toolbars_->visible(id);
+ buf.setGuiDelegate(this);
}
-void GuiViewBase::updateToolbars()
+
+void GuiView::disconnectBuffer()
{
- WorkArea * wa = currentWorkArea();
- if (wa) {
- bool const math =
- wa->bufferView().cursor().inMathed();
- bool const table =
- lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
- bool const review =
- lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).enabled() &&
- lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
+ if (d.current_work_area_)
+ d.current_work_area_->bufferView().setGuiDelegate(0);
+}
- d.toolbars_->update(math, table, review);
- } else
- d.toolbars_->update(false, false, false);
- // update read-only status of open dialogs.
- getDialogs().checkStatus();
+void GuiView::connectBufferView(BufferView & bv)
+{
+ bv.setGuiDelegate(this);
}
-ToolbarInfo * GuiViewBase::getToolbarInfo(string const & name)
+void GuiView::disconnectBufferView()
{
- return d.toolbars_->getToolbarInfo(name);
+ if (d.current_work_area_)
+ d.current_work_area_->bufferView().setGuiDelegate(0);
}
-void GuiViewBase::toggleToolbarState(string const & name, bool allowauto)
+void GuiView::errors(string const & error_type)
{
- // it is possible to get current toolbar status like this,...
- // but I decide to obey the order of ToolbarBackend::flags
- // and disregard real toolbar status.
- // toolbars_->saveToolbarInfo();
- //
- // toggle state on/off/auto
- d.toolbars_->toggleToolbarState(name, allowauto);
- // update toolbar
- updateToolbars();
+ ErrorList & el = buffer()->errorList(error_type);
+ if (!el.empty())
+ showDialog("errorlist", error_type);
+}
+
+
+void GuiView::updateDialog(string const & name, string const & data)
+{
+ if (!isDialogVisible(name))
+ return;
+
+ std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
+ if (it == d.dialogs_.end())
+ return;
+
+ Dialog * const dialog = it->second.get();
+ if (dialog->isVisibleView())
+ dialog->updateData(data);
+}
+
+
+BufferView * GuiView::view()
+{
+ return d.current_work_area_ ? &d.current_work_area_->bufferView() : 0;
+}
+
+
+void GuiView::updateToc()
+{
+ updateDialog("toc", "");
+}
+
+
+void GuiView::updateEmbeddedFiles()
+{
+ updateDialog("embedding", "");
+}
+
+
+void GuiView::autoSave()
+{
+ LYXERR(Debug::INFO, "Running autoSave()");
+
+ if (buffer())
+ view()->buffer().autoSave();
+}
+
+
+void GuiView::resetAutosaveTimers()
+{
+ if (lyxrc.autosave)
+ d.autosave_timeout_.restart();
+}
+
+
+FuncStatus GuiView::getStatus(FuncRequest const & cmd)
+{
+ FuncStatus flag;
+ bool enable = true;
+ Buffer * buf = buffer();
+
+ /* In LyX/Mac, when a dialog is open, the menus of the
+ application can still be accessed without giving focus to
+ the main window. In this case, we want to disable the menu
+ entries that are buffer-related.
+
+ Note that this code is not perfect, as bug 1941 attests:
+ http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
+ */
+ if (cmd.origin == FuncRequest::MENU && !hasFocus())
+ buf = 0;
+
+ switch(cmd.action) {
+ case LFUN_BUFFER_WRITE:
+ enable = buf && (buf->isUnnamed() || !buf->isClean());
+ break;
+
+ case LFUN_BUFFER_WRITE_AS:
+ enable = buf;
+ break;
+
+ case LFUN_TOOLBAR_TOGGLE:
+ flag.setOnOff(d.toolbars_->visible(cmd.getArg(0)));
+ break;
+
+ case LFUN_DIALOG_TOGGLE:
+ flag.setOnOff(isDialogVisible(cmd.getArg(0)));
+ // fall through to set "enable"
+ case LFUN_DIALOG_SHOW: {
+ string const name = cmd.getArg(0);
+ if (!buf)
+ enable = name == "aboutlyx"
+ || name == "file" //FIXME: should be removed.
+ || name == "prefs"
+ || name == "texinfo";
+ else if (name == "print")
+ enable = buf->isExportable("dvi")
+ && lyxrc.print_command != "none";
+ else if (name == "character") {
+ if (!view())
+ enable = false;
+ else {
+ InsetCode ic = view()->cursor().inset().lyxCode();
+ enable = ic != ERT_CODE && ic != LISTINGS_CODE;
+ }
+ }
+ else if (name == "latexlog")
+ enable = FileName(buf->logName()).isReadableFile();
+ else if (name == "spellchecker")
+#if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
+ enable = !buf->isReadonly();
+#else
+ enable = false;
+#endif
+ else if (name == "vclog")
+ enable = buf->lyxvc().inUse();
+ break;
+ }
+
+ case LFUN_DIALOG_UPDATE: {
+ string const name = cmd.getArg(0);
+ if (!buf)
+ enable = name == "prefs";
+ break;
+ }
+
+ case LFUN_INSET_APPLY: {
+ if (!buf) {
+ enable = false;
+ break;
+ }
+ string const name = cmd.getArg(0);
+ Inset * inset = getOpenInset(name);
+ if (inset) {
+ FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
+ FuncStatus fs;
+ if (!inset->getStatus(view()->cursor(), fr, fs)) {
+ // Every inset is supposed to handle this
+ BOOST_ASSERT(false);
+ }
+ flag |= fs;
+ } else {
+ FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
+ flag |= getStatus(fr);
+ }
+ enable = flag.enabled();
+ break;
+ }
+
+ default:
+ if (!view()) {
+ enable = false;
+ break;
+ }
+ }
+
+ if (!enable)
+ flag.enabled(false);
+
+ return flag;
+}
+
+
+static FileName selectTemplateFile()
+{
+ FileDialog dlg(_("Select template file"));
+ dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
+ dlg.setButton1(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
+
+ FileDialog::Result result =
+ dlg.open(from_utf8(lyxrc.template_path),
+ FileFilterList(_("LyX Documents (*.lyx)")),
+ docstring());
+
+ if (result.first == FileDialog::Later)
+ return FileName();
+ if (result.second.empty())
+ return FileName();
+ return FileName(to_utf8(result.second));
+}
+
+
+void GuiView::newDocument(string const & filename, bool from_template)
+{
+ FileName initpath(lyxrc.document_path);
+ Buffer * buf = buffer();
+ if (buf) {
+ FileName const trypath(buf->filePath());
+ // If directory is writeable, use this as default.
+ if (trypath.isDirWritable())
+ initpath = trypath;
+ }
+
+ string templatefile = from_template ?
+ selectTemplateFile().absFilename() : string();
+ Buffer * b;
+ if (filename.empty())
+ b = newUnnamedFile(templatefile, initpath);
+ else
+ b = newFile(filename, templatefile, true);
+
+ if (b)
+ setBuffer(b);
+}
+
+
+void GuiView::insertLyXFile(docstring const & fname)
+{
+ BufferView * bv = view();
+ if (!bv)
+ return;
+
+ // FIXME UNICODE
+ FileName filename(to_utf8(fname));
+
+ if (!filename.empty()) {
+ bv->insertLyXFile(filename);
+ return;
+ }
+
+ // Launch a file browser
+ // FIXME UNICODE
+ string initpath = lyxrc.document_path;
+ string const trypath = bv->buffer().filePath();
+ // If directory is writeable, use this as default.
+ if (FileName(trypath).isDirWritable())
+ initpath = trypath;
+
+ // FIXME UNICODE
+ FileDialog dlg(_("Select LyX document to insert"), LFUN_FILE_INSERT);
+ dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
+ dlg.setButton2(_("Examples|#E#e"),
+ from_utf8(addPath(package().system_support().absFilename(),
+ "examples")));
+
+ FileDialog::Result result =
+ dlg.open(from_utf8(initpath),
+ FileFilterList(_("LyX Documents (*.lyx)")),
+ docstring());
+
+ if (result.first == FileDialog::Later)
+ return;
+
+ // FIXME UNICODE
+ filename.set(to_utf8(result.second));
+
+ // check selected filename
+ if (filename.empty()) {
+ // emit message signal.
+ message(_("Canceled."));
+ return;
+ }
+
+ bv->insertLyXFile(filename);
+}
+
+
+void GuiView::insertPlaintextFile(docstring const & fname,
+ bool asParagraph)
+{
+ BufferView * bv = view();
+ if (!bv)
+ return;
+
+ // FIXME UNICODE
+ FileName filename(to_utf8(fname));
+
+ if (!filename.empty()) {
+ bv->insertPlaintextFile(filename, asParagraph);
+ return;
+ }
+
+ FileDialog dlg(_("Select file to insert"), (asParagraph ?
+ LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
+
+ FileDialog::Result result = dlg.open(from_utf8(bv->buffer().filePath()),
+ FileFilterList(), docstring());
+
+ if (result.first == FileDialog::Later)
+ return;
+
+ // FIXME UNICODE
+ filename.set(to_utf8(result.second));
+
+ // check selected filename
+ if (filename.empty()) {
+ // emit message signal.
+ message(_("Canceled."));
+ return;
+ }
+
+ bv->insertPlaintextFile(filename, asParagraph);
+}
+
+
+bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
+{
+ FileName fname = b.fileName();
+ FileName const oldname = fname;
+
+ if (!newname.empty()) {
+ // FIXME UNICODE
+ fname = makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
+ } else {
+ // Switch to this Buffer.
+ setBuffer(&b);
+
+ /// No argument? Ask user through dialog.
+ // FIXME UNICODE
+ FileDialog dlg(_("Choose a filename to save document as"),
+ LFUN_BUFFER_WRITE_AS);
+ dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
+ dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
+
+ if (!support::isLyXFilename(fname.absFilename()))
+ fname.changeExtension(".lyx");
+
+ support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
+
+ FileDialog::Result result =
+ dlg.save(from_utf8(fname.onlyPath().absFilename()),
+ filter,
+ from_utf8(fname.onlyFileName()));
+
+ if (result.first == FileDialog::Later)
+ return false;
+
+ fname.set(to_utf8(result.second));
+
+ if (fname.empty())
+ return false;
+
+ if (!support::isLyXFilename(fname.absFilename()))
+ fname.changeExtension(".lyx");
+ }
+
+ if (FileName(fname).exists()) {
+ docstring const file = makeDisplayPath(fname.absFilename(), 30);
+ docstring text = bformat(_("The document %1$s already "
+ "exists.\n\nDo you want to "
+ "overwrite that document?"),
+ file);
+ int const ret = Alert::prompt(_("Overwrite document?"),
+ text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
+ switch (ret) {
+ case 0: break;
+ case 1: return renameBuffer(b, docstring());
+ case 2: return false;
+ }
+ }
+
+ // Ok, change the name of the buffer
+ b.setFileName(fname.absFilename());
+ b.markDirty();
+ bool unnamed = b.isUnnamed();
+ b.setUnnamed(false);
+ b.saveCheckSum(fname);
+
+ if (!saveBuffer(b)) {
+ b.setFileName(oldname.absFilename());
+ b.setUnnamed(unnamed);
+ b.saveCheckSum(oldname);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool GuiView::saveBuffer(Buffer & b)
+{
+ if (b.isUnnamed())
+ return renameBuffer(b, docstring());
+
+ if (b.save()) {
+ LyX::ref().session().lastFiles().add(b.fileName());
+ return true;
+ }
+
+ // Switch to this Buffer.
+ setBuffer(&b);
+
+ // FIXME: we don't tell the user *WHY* the save failed !!
+ docstring const file = makeDisplayPath(b.absFileName(), 30);
+ docstring text = bformat(_("The document %1$s could not be saved.\n\n"
+ "Do you want to rename the document and "
+ "try again?"), file);
+ int const ret = Alert::prompt(_("Rename and save?"),
+ text, 0, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
+ switch (ret) {
+ case 0:
+ if (!renameBuffer(b, docstring()))
+ return false;
+ break;
+ case 1:
+ return false;
+ case 2:
+ break;
+ }
+
+ return saveBuffer(b);
+}
+
+
+bool GuiView::closeBuffer()
+{
+ Buffer * buf = buffer();
+ return buf && closeBuffer(*buf);
+}
+
+
+bool GuiView::closeBuffer(Buffer & buf)
+{
+ if (buf.isClean() || buf.paragraphs().empty()) {
+ theBufferList().release(&buf);
+ return true;
+ }
+ // Switch to this Buffer.
+ setBuffer(&buf);
+
+ docstring file;
+ // FIXME: Unicode?
+ if (buf.isUnnamed())
+ file = from_utf8(buf.fileName().onlyFileName());
+ else
+ file = buf.fileName().displayName(30);
+
+ docstring const text = bformat(_("The document %1$s has unsaved changes."
+ "\n\nDo you want to save the document or discard the changes?"), file);
+ int const ret = Alert::prompt(_("Save changed document?"),
+ text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
+
+ switch (ret) {
+ case 0:
+ if (!saveBuffer(buf))
+ return false;
+ break;
+ case 1:
+ // if we crash after this we could
+ // have no autosave file but I guess
+ // this is really improbable (Jug)
+ removeAutosaveFile(buf.absFileName());
+ break;
+ case 2:
+ return false;
+ }
+
+ // save file names to .lyx/session
+ // if master/slave are both open, do not save slave since it
+ // will be automatically loaded when the master is loaded
+ if (buf.masterBuffer() == &buf)
+ LyX::ref().session().lastOpened().add(buf.fileName());
+
+ theBufferList().release(&buf);
+ return true;
+}
+
+
+bool GuiView::quitWriteAll()
+{
+ while (!theBufferList().empty()) {
+ Buffer * b = theBufferList().first();
+ if (!closeBuffer(*b))
+ return false;
+ }
+ return true;
+}
+
+
+bool GuiView::dispatch(FuncRequest const & cmd)
+{
+ BufferView * bv = view();
+ // By default we won't need any update.
+ if (bv)
+ bv->cursor().updateFlags(Update::None);
+
+ switch(cmd.action) {
+ case LFUN_BUFFER_SWITCH:
+ setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
+ break;
+
+ case LFUN_BUFFER_NEXT:
+ setBuffer(theBufferList().next(buffer()));
+ break;
+
+ case LFUN_BUFFER_PREVIOUS:
+ setBuffer(theBufferList().previous(buffer()));
+ break;
+
+ case LFUN_COMMAND_EXECUTE: {
+ bool const show_it = cmd.argument() != "off";
+ d.toolbars_->showCommandBuffer(show_it);
+ break;
+ }
+ case LFUN_DROP_LAYOUTS_CHOICE:
+ if (d.layout_)
+ d.layout_->showPopup();
+ break;
+
+ case LFUN_MENU_OPEN:
+ d.menubar_->openByName(toqstr(cmd.argument()));
+ break;
+
+ case LFUN_FILE_INSERT:
+ insertLyXFile(cmd.argument());
+ break;
+ case LFUN_FILE_INSERT_PLAINTEXT_PARA:
+ insertPlaintextFile(cmd.argument(), true);
+ break;
+
+ case LFUN_FILE_INSERT_PLAINTEXT:
+ insertPlaintextFile(cmd.argument(), false);
+ break;
+
+ case LFUN_BUFFER_WRITE:
+ if (bv)
+ saveBuffer(bv->buffer());
+ break;
+
+ case LFUN_BUFFER_WRITE_AS:
+ if (bv)
+ renameBuffer(bv->buffer(), cmd.argument());
+ break;
+
+ case LFUN_BUFFER_WRITE_ALL: {
+ Buffer * first = theBufferList().first();
+ if (!first)
+ break;
+ message(_("Saving all documents..."));
+ // We cannot use a for loop as the buffer list cycles.
+ Buffer * b = first;
+ do {
+ if (b->isClean())
+ continue;
+ saveBuffer(*b);
+ LYXERR(Debug::ACTION, "Saved " << b->absFileName());
+ b = theBufferList().next(b);
+ } while (b != first);
+ message(_("All documents saved."));
+ break;
+ }
+
+ case LFUN_TOOLBAR_TOGGLE: {
+ string const name = cmd.getArg(0);
+ bool const allowauto = cmd.getArg(1) == "allowauto";
+ // it is possible to get current toolbar status like this,...
+ // but I decide to obey the order of ToolbarBackend::flags
+ // and disregard real toolbar status.
+ // toolbars_->saveToolbarInfo();
+ //
+ // toggle state on/off/auto
+ d.toolbars_->toggleToolbarState(name, allowauto);
+ // update toolbar
+ updateToolbars();
+
+ ToolbarInfo * tbi = d.toolbars_->getToolbarInfo(name);
+ if (!tbi) {
+ message(bformat(_("Unknown toolbar \"%1$s\""), from_utf8(name)));
+ break;
+ }
+ docstring state;
+ if (tbi->flags & ToolbarInfo::ON)
+ state = _("on");
+ else if (tbi->flags & ToolbarInfo::OFF)
+ state = _("off");
+ else if (tbi->flags & ToolbarInfo::AUTO)
+ state = _("auto");
+
+ message(bformat(_("Toolbar \"%1$s\" state set to %2$s"),
+ _(tbi->gui_name), state));
+ break;
+ }
+
+ case LFUN_DIALOG_UPDATE: {
+ string const name = to_utf8(cmd.argument());
+ // Can only update a dialog connected to an existing inset
+ Inset * inset = getOpenInset(name);
+ if (inset) {
+ FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
+ inset->dispatch(view()->cursor(), fr);
+ } else if (name == "paragraph") {
+ lyx::dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
+ } else if (name == "prefs") {
+ updateDialog(name, string());
+ }
+ break;
+ }
+
+ case LFUN_DIALOG_TOGGLE: {
+ if (isDialogVisible(cmd.getArg(0)))
+ dispatch(FuncRequest(LFUN_DIALOG_HIDE, cmd.argument()));
+ else
+ dispatch(FuncRequest(LFUN_DIALOG_SHOW, cmd.argument()));
+ break;
+ }
+
+ case LFUN_DIALOG_DISCONNECT_INSET:
+ disconnectDialog(to_utf8(cmd.argument()));
+ break;
+
+ case LFUN_DIALOG_HIDE: {
+ if (quitting)
+ break;
+ guiApp->hideDialogs(to_utf8(cmd.argument()), 0);
+ break;
+ }
+
+ case LFUN_DIALOG_SHOW: {
+ string const name = cmd.getArg(0);
+ string data = trim(to_utf8(cmd.argument()).substr(name.size()));
+
+ if (name == "character") {
+ data = freefont2string();
+ if (!data.empty())
+ showDialog("character", data);
+ } else if (name == "latexlog") {
+ Buffer::LogType type;
+ string const logfile = buffer()->logName(&type);
+ switch (type) {
+ case Buffer::latexlog:
+ data = "latex ";
+ break;
+ case Buffer::buildlog:
+ data = "literate ";
+ break;
+ }
+ data += Lexer::quoteString(logfile);
+ showDialog("log", data);
+ } else if (name == "vclog") {
+ string const data = "vc " +
+ Lexer::quoteString(buffer()->lyxvc().getLogFile());
+ showDialog("log", data);
+ } else
+ showDialog(name, data);
+ break;
+ }
+
+ case LFUN_INSET_APPLY: {
+ string const name = cmd.getArg(0);
+ Inset * inset = getOpenInset(name);
+ if (inset) {
+ FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
+ inset->dispatch(view()->cursor(), fr);
+ } else {
+ FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
+ lyx::dispatch(fr);
+ }
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+
+Buffer const * GuiView::updateInset(Inset const * inset)
+{
+ if (!d.current_work_area_)
+ return 0;
+
+ if (inset)
+ d.current_work_area_->scheduleRedraw();
+
+ return &d.current_work_area_->bufferView().buffer();
+}
+
+
+void GuiView::restartCursor()
+{
+ /* When we move around, or type, it's nice to be able to see
+ * the cursor immediately after the keypress.
+ */
+ if (d.current_work_area_)
+ d.current_work_area_->startBlinkingCursor();
+
+ // Take this occasion to update the toobars and layout list.
+ updateLayoutList();
+ updateToolbars();
+}
+
+namespace {
+
+// This list should be kept in sync with the list of insets in
+// src/insets/Inset.cpp. I.e., if a dialog goes with an inset, the
+// dialog should have the same name as the inset.
+
+char const * const dialognames[] = {
+"aboutlyx", "bibitem", "bibtex", "box", "branch", "changes", "character",
+"citation", "document", "embedding", "errorlist", "ert", "external", "file",
+"findreplace", "float", "graphics", "include", "index", "nomenclature", "label", "log",
+"mathdelimiter", "mathmatrix", "note", "paragraph",
+"prefs", "print", "ref", "sendto", "spellchecker","tabular", "tabularcreate",
+
+#ifdef HAVE_LIBAIKSAURUS
+"thesaurus",
+#endif
+
+"texinfo", "toc", "href", "view-source", "latex-progress", "vspace", "wrap", "listings" };
+
+char const * const * const end_dialognames =
+ dialognames + (sizeof(dialognames) / sizeof(char *));
+
+class cmpCStr {
+public:
+ cmpCStr(char const * name) : name_(name) {}
+ bool operator()(char const * other) {
+ return strcmp(other, name_) == 0;
+ }
+private:
+ char const * name_;
+};
+
+
+bool isValidName(string const & name)
+{
+ return std::find_if(dialognames, end_dialognames,
+ cmpCStr(name.c_str())) != end_dialognames;
+}
+
+} // namespace anon
+
+
+void GuiView::resetDialogs()
+{
+ // Make sure that no LFUN uses any LyXView.
+ theLyXFunc().setLyXView(0);
+ d.toolbars_->init();
+ d.menubar_->init();
+ if (d.layout_)
+ d.layout_->updateContents(true);
+ // Now update controls with current buffer.
+ theLyXFunc().setLyXView(this);
+ restartCursor();
+}
+
+
+Dialog * GuiView::find_or_build(string const & name)
+{
+ if (!isValidName(name))
+ return 0;
+
+ std::map<string, DialogPtr>::iterator it = d.dialogs_.find(name);
+
+ if (it != d.dialogs_.end())
+ return it->second.get();
+
+ d.dialogs_[name].reset(build(name));
+ return d.dialogs_[name].get();
+}
+
+
+void GuiView::showDialog(string const & name, string const & data,
+ Inset * inset)
+{
+ if (d.in_show_)
+ return;
+
+ d.in_show_ = true;
+ Dialog * dialog = find_or_build(name);
+ if (dialog) {
+ dialog->showData(data);
+ if (inset)
+ d.open_insets_[name] = inset;
+ }
+ d.in_show_ = false;
+}
+
+
+bool GuiView::isDialogVisible(string const & name) const
+{
+ std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
+ if (it == d.dialogs_.end())
+ return false;
+ return it->second.get()->isVisibleView();
+}
+
+
+void GuiView::hideDialog(string const & name, Inset * inset)
+{
+ // Don't send the signal if we are quitting, because on MSVC it is
+ // destructed before the cut stack in CutAndPaste.cpp, and this method
+ // is called from some inset destructor if the cut stack is not empty
+ // on exit.
+ if (quitting)
+ return;
+
+ std::map<string, DialogPtr>::const_iterator it = d.dialogs_.find(name);
+ if (it == d.dialogs_.end())
+ return;
+
+ if (inset && inset != getOpenInset(name))
+ return;
+
+ Dialog * const dialog = it->second.get();
+ if (dialog->isVisibleView())
+ dialog->hide();
+ d.open_insets_[name] = 0;
+}
+
+
+void GuiView::disconnectDialog(string const & name)
+{
+ if (!isValidName(name))
+ return;
+
+ if (d.open_insets_.find(name) != d.open_insets_.end())
+ d.open_insets_[name] = 0;
+}
+
+
+Inset * GuiView::getOpenInset(string const & name) const
+{
+ if (!isValidName(name))
+ return 0;
+
+ std::map<string, Inset *>::const_iterator it = d.open_insets_.find(name);
+ return it == d.open_insets_.end() ? 0 : it->second;
+}
+
+
+void GuiView::hideAll() const
+{
+ std::map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
+ std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
+
+ for(; it != end; ++it)
+ it->second->hide();
+}
+
+
+void GuiView::hideBufferDependent() const
+{
+ std::map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
+ std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
+
+ for(; it != end; ++it) {
+ Dialog * dialog = it->second.get();
+ if (dialog->isBufferDependent())
+ dialog->hide();
+ }
+}
+
+
+void GuiView::updateBufferDependent(bool switched) const
+{
+ std::map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
+ std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
+
+ for(; it != end; ++it) {
+ Dialog * dialog = it->second.get();
+ if (!dialog->isVisibleView())
+ continue;
+ if (switched && dialog->isBufferDependent()) {
+ if (dialog->initialiseParams(""))
+ dialog->updateView();
+ else
+ dialog->hide();
+ } else {
+ // A bit clunky, but the dialog will request
+ // that the kernel provides it with the necessary
+ // data.
+ dialog->updateDialog();
+ }
+ }
+}
+
+
+void GuiView::checkStatus()
+{
+ std::map<string, DialogPtr>::const_iterator it = d.dialogs_.begin();
+ std::map<string, DialogPtr>::const_iterator end = d.dialogs_.end();
+
+ for(; it != end; ++it) {
+ Dialog * const dialog = it->second.get();
+ if (dialog && dialog->isVisibleView())
+ dialog->checkStatus();
+ }
+}
+
+
+
+// will be replaced by a proper factory...
+Dialog * createGuiAbout(GuiView & lv);
+Dialog * createGuiBibitem(GuiView & lv);
+Dialog * createGuiBibtex(GuiView & lv);
+Dialog * createGuiBox(GuiView & lv);
+Dialog * createGuiBranch(GuiView & lv);
+Dialog * createGuiChanges(GuiView & lv);
+Dialog * createGuiCharacter(GuiView & lv);
+Dialog * createGuiCitation(GuiView & lv);
+Dialog * createGuiDelimiter(GuiView & lv);
+Dialog * createGuiDocument(GuiView & lv);
+Dialog * createGuiErrorList(GuiView & lv);
+Dialog * createGuiERT(GuiView & lv);
+Dialog * createGuiExternal(GuiView & lv);
+Dialog * createGuiFloat(GuiView & lv);
+Dialog * createGuiGraphics(GuiView & lv);
+Dialog * createGuiInclude(GuiView & lv);
+Dialog * createGuiIndex(GuiView & lv);
+Dialog * createGuiLabel(GuiView & lv);
+Dialog * createGuiListings(GuiView & lv);
+Dialog * createGuiLog(GuiView & lv);
+Dialog * createGuiMathMatrix(GuiView & lv);
+Dialog * createGuiNomenclature(GuiView & lv);
+Dialog * createGuiNote(GuiView & lv);
+Dialog * createGuiParagraph(GuiView & lv);
+Dialog * createGuiPreferences(GuiView & lv);
+Dialog * createGuiPrint(GuiView & lv);
+Dialog * createGuiRef(GuiView & lv);
+Dialog * createGuiSearch(GuiView & lv);
+Dialog * createGuiSendTo(GuiView & lv);
+Dialog * createGuiShowFile(GuiView & lv);
+Dialog * createGuiSpellchecker(GuiView & lv);
+Dialog * createGuiTabularCreate(GuiView & lv);
+Dialog * createGuiTabular(GuiView & lv);
+Dialog * createGuiTexInfo(GuiView & lv);
+Dialog * createGuiToc(GuiView & lv);
+Dialog * createGuiThesaurus(GuiView & lv);
+Dialog * createGuiHyperlink(GuiView & lv);
+Dialog * createGuiVSpace(GuiView & lv);
+Dialog * createGuiViewSource(GuiView & lv);
+Dialog * createGuiProgress(GuiView & lv);
+Dialog * createGuiWrap(GuiView & lv);
+
+
+Dialog * GuiView::build(string const & name)
+{
+ BOOST_ASSERT(isValidName(name));
+
+ if (name == "aboutlyx")
+ return createGuiAbout(*this);
+ if (name == "bibitem")
+ return createGuiBibitem(*this);
+ if (name == "bibtex")
+ return createGuiBibtex(*this);
+ if (name == "box")
+ return createGuiBox(*this);
+ if (name == "branch")
+ return createGuiBranch(*this);
+ if (name == "changes")
+ return createGuiChanges(*this);
+ if (name == "character")
+ return createGuiCharacter(*this);
+ if (name == "citation")
+ return createGuiCitation(*this);
+ if (name == "document")
+ return createGuiDocument(*this);
+ if (name == "errorlist")
+ return createGuiErrorList(*this);
+ if (name == "ert")
+ return createGuiERT(*this);
+ if (name == "external")
+ return createGuiExternal(*this);
+ if (name == "file")
+ return createGuiShowFile(*this);
+ if (name == "findreplace")
+ return createGuiSearch(*this);
+ if (name == "float")
+ return createGuiFloat(*this);
+ if (name == "graphics")
+ return createGuiGraphics(*this);
+ if (name == "include")
+ return createGuiInclude(*this);
+ if (name == "index")
+ return createGuiIndex(*this);
+ if (name == "nomenclature")
+ return createGuiNomenclature(*this);
+ if (name == "label")
+ return createGuiLabel(*this);
+ if (name == "log")
+ return createGuiLog(*this);
+ if (name == "view-source")
+ return createGuiViewSource(*this);
+ if (name == "latex-progress")
+ return createGuiProgress(*this);
+ if (name == "mathdelimiter")
+ return createGuiDelimiter(*this);
+ if (name == "mathmatrix")
+ return createGuiMathMatrix(*this);
+ if (name == "note")
+ return createGuiNote(*this);
+ if (name == "paragraph")
+ return createGuiParagraph(*this);
+ if (name == "prefs")
+ return createGuiPreferences(*this);
+ if (name == "print")
+ return createGuiPrint(*this);
+ if (name == "ref")
+ return createGuiRef(*this);
+ if (name == "sendto")
+ return createGuiSendTo(*this);
+ if (name == "spellchecker")
+ return createGuiSpellchecker(*this);
+ if (name == "tabular")
+ return createGuiTabular(*this);
+ if (name == "tabularcreate")
+ return createGuiTabularCreate(*this);
+ if (name == "texinfo")
+ return createGuiTexInfo(*this);
+#ifdef HAVE_LIBAIKSAURUS
+ if (name == "thesaurus")
+ return createGuiThesaurus(*this);
+#endif
+ if (name == "toc")
+ return createGuiToc(*this);
+ if (name == "href")
+ return createGuiHyperlink(*this);
+ if (name == "vspace")
+ return createGuiVSpace(*this);
+ if (name == "wrap")
+ return createGuiWrap(*this);
+ if (name == "listings")
+ return createGuiListings(*this);
+
+ return 0;
}