lyx_mirror/src/frontends/qt4/GuiView.cpp
Abdelrazak Younes 1133419a59 Cleanup LyXFunc::dispatch() following JMarc advice.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@21797 a592a061-630c-0410-9148-cb99ea01b6c8
2007-11-26 14:36:50 +00:00

1542 lines
36 KiB
C++

/**
* \file GuiView.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Lars Gullik Bjønnes
* \author John Levon
* \author Abdelrazak Younes
* \author Peter Kümmel
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "GuiView.h"
#include "Dialog.h"
#include "GuiApplication.h"
#include "GuiWorkArea.h"
#include "GuiKeySymbol.h"
#include "GuiMenubar.h"
#include "GuiToolbar.h"
#include "GuiToolbars.h"
#include "qt_helpers.h"
#include "buffer_funcs.h"
#include "Buffer.h"
#include "BufferList.h"
#include "BufferParams.h"
#include "BufferView.h"
#include "Cursor.h"
#include "debug.h"
#include "ErrorList.h"
#include "FuncRequest.h"
#include "gettext.h"
#include "Intl.h"
#include "Layout.h"
#include "Lexer.h"
#include "LyXFunc.h"
#include "LyX.h"
#include "LyXRC.h"
#include "LyXVC.h"
#include "MenuBackend.h"
#include "Paragraph.h"
#include "TextClass.h"
#include "Text.h"
#include "ToolbarBackend.h"
#include "version.h"
#include "support/FileName.h"
#include "support/lstrings.h"
#include "support/os.h"
#include "support/Timeout.h"
#include <QAction>
#include <QApplication>
#include <QCloseEvent>
#include <QDebug>
#include <QDesktopWidget>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QList>
#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 {
extern bool quitting;
namespace frontend {
using support::bformat;
using support::FileName;
using support::trim;
namespace {
class BackgroundWidget : public QWidget
{
public:
BackgroundWidget()
{
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_);
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(int(toqstr(lyxrc.font_sizes[FONT_SIZE_LARGE]).toDouble()));
pain.setFont(font);
pain.drawText(260, 270, text);
}
void paintEvent(QPaintEvent *)
{
int x = (width() - splash_.width()) / 2;
int y = (height() - splash_.height()) / 2;
QPainter pain(this);
pain.drawPixmap(x, y, splash_);
}
private:
QPixmap splash_;
};
} // namespace anon
typedef boost::shared_ptr<Dialog> DialogPtr;
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();
}
~GuiViewPrivate()
{
delete splitter_;
delete bg_widget_;
delete stack_widget_;
delete menubar_;
delete toolbars_;
}
QMenu * toolBarPopup(GuiView * parent)
{
// FIXME: translation
QMenu * menu = new QMenu(parent);
QActionGroup * iconSizeGroup = new QActionGroup(parent);
QAction * smallIcons = new QAction(iconSizeGroup);
smallIcons->setText(qt_("Small-sized icons"));
smallIcons->setCheckable(true);
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()));
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()));
menu->addAction(bigIcons);
unsigned int cur = parent->iconSize().width();
if ( cur == parent->d.smallIconSize)
smallIcons->setChecked(true);
else if (cur == parent->d.normalIconSize)
normalIcons->setChecked(true);
else if (cur == parent->d.bigIconSize)
bigIcons->setChecked(true);
return menu;
}
void setBackground()
{
stack_widget_->setCurrentWidget(bg_widget_);
bg_widget_->setUpdatesEnabled(true);
}
TabWorkArea * tabWorkArea(int i)
{
return dynamic_cast<TabWorkArea *>(splitter_->widget(i));
}
TabWorkArea * currentTabWorkArea()
{
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;
}
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 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_;
};
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);
#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.
setWindowIcon(QPixmap(":/images/lyx.png"));
#endif
// 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());
}
GuiView::~GuiView()
{
delete &d;
}
void GuiView::close()
{
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();
d.quitting_by_menu_ = false;
}
void GuiView::setFocus()
{
if (d.current_work_area_)
d.current_work_area_->setFocus();
else
QWidget::setFocus();
}
QMenu * GuiView::createPopupMenu()
{
return d.toolBarPopup(this);
}
void GuiView::showEvent(QShowEvent * e)
{
LYXERR(Debug::GUI, "Passed Geometry "
<< size().height() << "x" << size().width()
<< "+" << pos().x() << "+" << pos().y());
if (d.splitter_->count() == 0)
// No work area, switch to the background widget.
d.setBackground();
QMainWindow::showEvent(e);
}
void GuiView::closeEvent(QCloseEvent * close_event)
{
// we may have been called through the close window button
// which bypasses the LFUN machinery.
if (!d.quitting_by_menu_ && guiApp->viewCount() == 1) {
if (!theBufferList().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.
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();
}
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();
return;
}
quitting = true;
// this is the place where we leave the frontend.
// it is the only point at which we start quitting.
close_event->accept();
// quit the event loop
qApp->quit();
}
void GuiView::dragEnterEvent(QDragEnterEvent * event)
{
if (event->mimeData()->hasUrls())
event->accept();
/// \todo Ask lyx-devel is this is enough:
/// if (event->mimeData()->hasFormat("text/plain"))
/// event->acceptProposedAction();
}
void GuiView::dropEvent(QDropEvent* event)
{
QList<QUrl> files = event->mimeData()->urls();
if (files.isEmpty())
return;
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()));
if (!file.empty())
dispatch(FuncRequest(LFUN_FILE_OPEN, file));
}
}
void GuiView::message(docstring const & str)
{
statusBar()->showMessage(toqstr(str));
d.statusbar_timer_.stop();
d.statusbar_timer_.start(3000);
}
void GuiView::smallSizedIcons()
{
setIconSize(QSize(d.smallIconSize, d.smallIconSize));
}
void GuiView::normalSizedIcons()
{
setIconSize(QSize(d.normalIconSize, d.normalIconSize));
}
void GuiView::bigSizedIcons()
{
setIconSize(QSize(d.bigIconSize, d.bigIconSize));
}
void GuiView::clearMessage()
{
if (!hasFocus())
return;
theLyXFunc().setLyXView(this);
statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
d.statusbar_timer_.stop();
}
void GuiView::updateWindowTitle(GuiWorkArea * wa)
{
if (wa != d.current_work_area_)
return;
setWindowTitle(qt_("LyX: ") + wa->windowTitle());
setWindowIconText(wa->windowIconText());
}
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.
updateBufferDependent(true);
updateToolbars();
updateLayoutList();
updateStatusBar();
}
void GuiView::updateStatusBar()
{
// let the user see the explicit message
if (d.statusbar_timer_.isActive())
return;
statusBar()->showMessage(toqstr(theLyXFunc().viewStatusMessage()));
}
bool GuiView::hasFocus() const
{
return qApp->activeWindow() == this;
}
bool GuiView::event(QEvent * e)
{
switch (e->type())
{
// Useful debug code:
//case QEvent::ActivationChange:
//case QEvent::WindowDeactivate:
//case QEvent::Paint:
//case QEvent::Enter:
//case QEvent::Leave:
//case QEvent::HoverEnter:
//case QEvent::HoverLeave:
//case QEvent::HoverMove:
//case QEvent::StatusTip:
//case QEvent::DragEnter:
//case QEvent::DragLeave:
//case QEvent::Drop:
// break;
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.
updateBufferDependent(true);
} else {
setWindowTitle(qt_("LyX"));
setWindowIconText(qt_("LyX"));
}
return QMainWindow::event(e);
}
case QEvent::ShortcutOverride: {
QKeyEvent * ke = static_cast<QKeyEvent*>(e);
if (!d.current_work_area_) {
theLyXFunc().setLyXView(this);
KeySymbol sym;
setKeySymbol(&sym, ke);
theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
e->accept();
return true;
}
if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
KeySymbol sym;
setKeySymbol(&sym, ke);
d.current_work_area_->processKeySym(sym, NoModifier);
e->accept();
return true;
}
}
default:
return QMainWindow::event(e);
}
}
bool GuiView::focusNextPrevChild(bool /*next*/)
{
setFocus();
return true;
}
void GuiView::setBusy(bool yes)
{
if (d.current_work_area_) {
d.current_work_area_->setUpdatesEnabled(!yes);
if (yes)
d.current_work_area_->stopBlinkingCursor();
else
d.current_work_area_->startBlinkingCursor();
}
if (yes)
QApplication::setOverrideCursor(Qt::WaitCursor);
else
QApplication::restoreOverrideCursor();
}
GuiToolbar * GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newline)
{
GuiToolbar * toolBar = new GuiToolbar(tbinfo, *this);
if (tbinfo.flags & ToolbarInfo::TOP) {
if (newline)
addToolBarBreak(Qt::TopToolBarArea);
addToolBar(Qt::TopToolBarArea, toolBar);
}
if (tbinfo.flags & ToolbarInfo::BOTTOM) {
// Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
#if (QT_VERSION >= 0x040202)
if (newline)
addToolBarBreak(Qt::BottomToolBarArea);
#endif
addToolBar(Qt::BottomToolBarArea, toolBar);
}
if (tbinfo.flags & ToolbarInfo::LEFT) {
// Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
#if (QT_VERSION >= 0x040202)
if (newline)
addToolBarBreak(Qt::LeftToolBarArea);
#endif
addToolBar(Qt::LeftToolBarArea, toolBar);
}
if (tbinfo.flags & ToolbarInfo::RIGHT) {
// Qt < 4.2.2 cannot handle ToolBarBreak on non-TOP dock.
#if (QT_VERSION >= 0x040202)
if (newline)
addToolBarBreak(Qt::RightToolBarArea);
#endif
addToolBar(Qt::RightToolBarArea, toolBar);
}
// The following does not work so I cannot restore to exact toolbar location
/*
ToolbarSection::ToolbarInfo & tbinfo = LyX::ref().session().toolbars().load(tbinfo.name);
toolBar->move(tbinfo.posx, tbinfo.posy);
*/
return toolBar;
}
GuiWorkArea * GuiView::workArea(Buffer & buffer)
{
for (int i = 0; i != d.splitter_->count(); ++i) {
GuiWorkArea * wa = d.tabWorkArea(i)->workArea(buffer);
if (wa)
return wa;
}
return 0;
}
GuiWorkArea * GuiView::addWorkArea(Buffer & buffer)
{
// 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);
}
void GuiView::addTabWorkArea()
{
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_);
}
GuiWorkArea const * GuiView::currentWorkArea() const
{
return d.current_work_area_;
}
void GuiView::setCurrentWorkArea(GuiWorkArea * work_area)
{
BOOST_ASSERT(work_area);
// Changing work area can result from opening a file so
// update the toc in any case.
updateToc();
GuiWorkArea * wa = static_cast<GuiWorkArea *>(work_area);
d.current_work_area_ = wa;
for (int i = 0; i != d.splitter_->count(); ++i) {
if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
return;
}
}
void GuiView::removeWorkArea(GuiWorkArea * work_area)
{
BOOST_ASSERT(work_area);
GuiWorkArea * gwa = static_cast<GuiWorkArea *>(work_area);
if (gwa == 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(gwa))
// 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;
}
if (d.current_work_area_)
// This means that we are not closing the current GuiWorkArea;
break;
// Switch to the next GuiWorkArea in the found TabWorkArea.
d.current_work_area_ = twa->currentWorkArea();
break;
}
if (d.splitter_->count() == 0)
// No more work area, switch to the background widget.
d.setBackground();
}
void GuiView::setLayoutDialog(GuiLayoutBox * layout)
{
d.layout_ = layout;
}
void GuiView::updateLayoutList()
{
if (d.layout_)
d.layout_->updateContents(false);
}
void GuiView::updateToolbars()
{
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();
}
Buffer * GuiView::buffer()
{
if (d.current_work_area_)
return &d.current_work_area_->bufferView().buffer();
return 0;
}
Buffer const * GuiView::buffer() const
{
if (d.current_work_area_)
return &d.current_work_area_->bufferView().buffer();
return 0;
}
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);
}
void GuiView::connectBuffer(Buffer & buf)
{
buf.setGuiDelegate(this);
}
void GuiView::disconnectBuffer()
{
if (d.current_work_area_)
d.current_work_area_->bufferView().setGuiDelegate(0);
}
void GuiView::connectBufferView(BufferView & bv)
{
bv.setGuiDelegate(this);
}
void GuiView::disconnectBufferView()
{
if (d.current_work_area_)
d.current_work_area_->bufferView().setGuiDelegate(0);
}
void GuiView::errors(string const & error_type)
{
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_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;
}
bool GuiView::dispatch(FuncRequest const & cmd, bool propagate)
{
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_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:
if (propagate) {
theLyXFunc().setLyXView(this);
lyx::dispatch(cmd);
}
return false;
}
if (bv) {
bv->processUpdateFlags(bv->cursor().result().update());
// We won't need any new update.
bv->cursor().updateFlags(Update::None);
}
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", "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 (switched && dialog->isBufferDependent()) {
if (dialog->isVisibleView() && 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 * 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 == "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;
}
} // namespace frontend
} // namespace lyx
#include "GuiView_moc.cpp"