The BufferView/WorkArea/LyXView reorg a.k.a Multiple WorkAreas:

* Buffer:
- get rid of cursor_ and anchor_
- ~Buffer(): update the labels of its master buffer before closing
- closing(): pass the Buffer address.

* BufferView():
- BufferView(): needs a valid Buffer (should be const in the future.
- most of the change is about removing all test of buffer_ nullity.
- resize(): deleted.
- setBuffer(): deleted.

* Application:
- newLyXView(): simplification
- updated design description in Application.h

* Gui/GuiImplementation: remove all WorkAreas and BufferView creation/Deletion. Workareas are directly handled by  LyXView/GuiView and BufferView is created/delete by WorkArea.

* LyXView/GuiView: implement the new design

What is not working yet:
- the close tab button: it is implemented but does not show up.


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@19686 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Abdelrazak Younes 2007-08-21 07:33:46 +00:00
parent 7206f23872
commit 1356543c45
23 changed files with 773 additions and 971 deletions

View File

@ -22,6 +22,7 @@
#include "Bullet.h"
#include "Chktex.h"
#include "debug.h"
#include "DocIterator.h"
#include "Encoding.h"
#include "ErrorList.h"
#include "Exporter.h"
@ -227,7 +228,12 @@ Buffer::~Buffer()
// here the buffer should take care that it is
// saved properly, before it goes into the void.
closing();
Buffer * master = getMasterBuffer();
if (master != this && use_gui)
// We are closing buf which was a child document so we
// must update the labels and section numbering of its master
// Buffer.
updateLabels(*master);
if (!temppath().empty() && !destroyDir(FileName(temppath()))) {
Alert::warning(_("Could not remove temporary directory"),
@ -237,6 +243,8 @@ Buffer::~Buffer()
// Remove any previewed LaTeX snippets associated with this buffer.
graphics::Previews::get().removeLoader(*this);
closing(this);
}
@ -1707,13 +1715,6 @@ void Buffer::buildMacros()
}
void Buffer::saveCursor(StableDocIterator cur, StableDocIterator anc)
{
cursor_ = cur;
anchor_ = anc;
}
void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
Inset::Code code)
{

View File

@ -25,7 +25,6 @@
#include <iosfwd>
#include <string>
#include <map>
#include <utility>
#include <vector>
@ -50,7 +49,6 @@ class OutputParams;
class ParConstIterator;
class ParIterator;
class ParagraphList;
class StableDocIterator;
class TeXErrors;
class TexRow;
class TocBackend;
@ -159,7 +157,7 @@ public:
/// Reset autosave timers for all users.
boost::signal<void()> resetAutosaveTimers;
/// This signal is emitting if the buffer is being closed.
boost::signal<void()> closing;
boost::signal<void(Buffer *)> closing;
/** Save file.
@ -381,12 +379,6 @@ public:
///
void insertMacro(docstring const & name, MacroData const & data);
///
void saveCursor(StableDocIterator cursor, StableDocIterator anchor);
///
StableDocIterator getCursor() const { return cursor_; }
///
StableDocIterator getAnchor() const { return anchor_; }
///
void changeRefsIfUnique(docstring const & from, docstring const & to,
Inset::Code code);
@ -417,11 +409,6 @@ private:
/// The pointer never changes although *pimpl_'s contents may.
boost::scoped_ptr<Impl> const pimpl_;
/// Save the cursor Position on Buffer switch
/// this would not be needed if every Buffer would have
/// it's BufferView, this should be FIXED in future.
StableDocIterator cursor_;
StableDocIterator anchor_;
/// A cache for the bibfiles (including bibfiles of loaded child
/// documents), needed for appropriate update of natbib labels.
mutable std::vector<support::FileName> bibfilesCache_;

View File

@ -122,14 +122,21 @@ T * getInsetByCode(Cursor & cur, Inset::Code code)
} // anon namespace
BufferView::BufferView()
: width_(0), height_(0), buffer_(0), wh_(0),
BufferView::BufferView(Buffer & buf)
: width_(0), height_(0), buffer_(buf), wh_(0),
cursor_(*this),
multiparsel_cache_(false), anchor_ref_(0), offset_ref_(0),
intl_(new Intl), last_inset_(0)
{
xsel_cache_.set = false;
intl_->initKeyMapper(lyxrc.use_kbmap);
cursor_.push(buffer_.inset());
cursor_.resetAnchor();
buffer_.text().setCurrentFont(cursor_);
if (graphics::Previews::status() != LyXRC::PREVIEW_OFF)
graphics::Previews::get().generateBufferPreviews(buffer_);
}
@ -138,124 +145,15 @@ BufferView::~BufferView()
}
Buffer * BufferView::buffer() const
Buffer * BufferView::buffer()
{
return buffer_;
return &buffer_;
}
Buffer * BufferView::setBuffer(Buffer * b)
Buffer const * BufferView::buffer() const
{
LYXERR(Debug::INFO) << BOOST_CURRENT_FUNCTION
<< "[ b = " << b << "]" << endl;
if (buffer_) {
// Save the current selection if any
cap::saveSelection(cursor_);
// Save the actual cursor position and anchor inside the
// buffer so that it can be restored in case we rechange
// to this buffer later on.
buffer_->saveCursor(cursor_.selectionBegin(),
cursor_.selectionEnd());
// update bookmark pit of the current buffer before switch
for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i) {
BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(i);
if (buffer()->fileName() != bm.filename.absFilename())
continue;
// if top_id or bottom_pit, bottom_pos has been changed, update bookmark
// see http://bugzilla.lyx.org/show_bug.cgi?id=3092
pit_type new_pit;
pos_type new_pos;
int new_id;
boost::tie(new_pit, new_pos, new_id) = moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
}
// current buffer is going to be switched-off, save cursor pos
// Ideally, the whole cursor stack should be saved, but session
// currently can only handle bottom (whole document) level pit and pos.
// That is to say, if a cursor is in a nested inset, it will be
// restore to the left of the top level inset.
LyX::ref().session().lastFilePos().save(FileName(buffer_->fileName()),
boost::tie(cursor_.bottom().pit(), cursor_.bottom().pos()) );
}
// If we're quitting lyx, don't bother updating stuff
if (quitting) {
buffer_ = 0;
return 0;
}
//FIXME Fix for bug 3440 is here.
// If we are closing current buffer, switch to the first in
// buffer list.
if (!b) {
LYXERR(Debug::INFO) << BOOST_CURRENT_FUNCTION
<< " No Buffer!" << endl;
// We are closing the buffer, use the first buffer as current
//FIXME 3440
// if (last_buffer_) buffer_ = last_buffer_;
// also check that this is in theBufferList()?
buffer_ = theBufferList().first();
} else {
//FIXME 3440
// last_buffer = buffer_;
// Set current buffer
buffer_ = b;
}
// Reset old cursor
cursor_ = Cursor(*this);
anchor_ref_ = 0;
offset_ref_ = 0;
if (!buffer_)
return 0;
LYXERR(Debug::INFO) << BOOST_CURRENT_FUNCTION
<< "Buffer addr: " << buffer_ << endl;
cursor_.push(buffer_->inset());
cursor_.resetAnchor();
buffer_->text().setCurrentFont(cursor_);
// Update the metrics now that we have a proper Cursor.
updateMetrics(false);
// FIXME: This code won't be needed once we switch to
// "one Buffer" / "one BufferView".
if (buffer_->getCursor().size() > 0 &&
buffer_->getAnchor().size() > 0)
{
cursor_.setCursor(buffer_->getAnchor().asDocIterator(&(buffer_->inset())));
cursor_.resetAnchor();
cursor_.setCursor(buffer_->getCursor().asDocIterator(&(buffer_->inset())));
cursor_.setSelection();
// do not set selection to the new buffer because we
// only paste recent selection.
// Make sure that the restored cursor is not broken. This can happen for
// example if this Buffer has been modified by another view.
cursor_.fixIfBroken();
if (fitCursor())
// Update the metrics if the cursor new position was off screen.
updateMetrics(false);
}
if (graphics::Previews::status() != LyXRC::PREVIEW_OFF)
graphics::Previews::get().generateBufferPreviews(*buffer_);
return buffer_;
}
void BufferView::resize()
{
if (!buffer_)
return;
LYXERR(Debug::DEBUG) << BOOST_CURRENT_FUNCTION << endl;
updateMetrics(false);
return &buffer_;
}
@ -298,17 +196,11 @@ bool BufferView::update(Update::flags flags)
<< "[fitcursor = " << (flags & Update::FitCursor)
<< ", forceupdate = " << (flags & Update::Force)
<< ", singlepar = " << (flags & Update::SinglePar)
<< "] buffer: " << buffer_ << endl;
// Check needed to survive LyX startup
if (!buffer_)
return false;
LYXERR(Debug::WORKAREA) << "BufferView::update" << std::endl;
<< "] buffer: " << &buffer_ << endl;
// Update macro store
if (!(cursor().inMathed() && cursor().inMacroMode()))
buffer_->buildMacros();
buffer_.buildMacros();
// Now do the first drawing step if needed. This consists on updating
// the CoordCache in updateMetrics().
@ -360,14 +252,7 @@ bool BufferView::update(Update::flags flags)
void BufferView::updateScrollbar()
{
if (!buffer_) {
LYXERR(Debug::DEBUG) << BOOST_CURRENT_FUNCTION
<< " no text in updateScrollbar" << endl;
scrollbarParameters_.reset();
return;
}
Text & t = buffer_->text();
Text & t = buffer_.text();
TextMetrics & tm = text_metrics_[&t];
int const parsize = int(t.paragraphs().size() - 1);
@ -432,10 +317,7 @@ void BufferView::scrollDocView(int value)
LYXERR(Debug::GUI) << BOOST_CURRENT_FUNCTION
<< "[ value = " << value << "]" << endl;
if (!buffer_)
return;
Text & t = buffer_->text();
Text & t = buffer_.text();
TextMetrics & tm = text_metrics_[&t];
float const bar = value / float(wh_ * t.paragraphs().size());
@ -453,10 +335,7 @@ void BufferView::scrollDocView(int value)
void BufferView::setCursorFromScrollbar()
{
if (!buffer_)
return;
Text & t = buffer_->text();
Text & t = buffer_.text();
int const height = 2 * defaultRowHeight();
int const first = height;
@ -469,14 +348,14 @@ void BufferView::setCursorFromScrollbar()
case bv_funcs::CUR_ABOVE:
// We reset the cursor because bv_funcs::status() does not
// work when the cursor is within mathed.
cur.reset(buffer_->inset());
cur.reset(buffer_.inset());
t.setCursorFromCoordinates(cur, 0, first);
cur.clearSelection();
break;
case bv_funcs::CUR_BELOW:
// We reset the cursor because bv_funcs::status() does not
// work when the cursor is within mathed.
cur.reset(buffer_->inset());
cur.reset(buffer_.inset());
t.setCursorFromCoordinates(cur, 0, last);
cur.clearSelection();
break;
@ -484,7 +363,7 @@ void BufferView::setCursorFromScrollbar()
int const y = bv_funcs::getPos(*this, cur, cur.boundary()).y_;
int const newy = min(last, max(y, first));
if (y != newy) {
cur.reset(buffer_->inset());
cur.reset(buffer_.inset());
t.setCursorFromCoordinates(cur, 0, newy);
}
}
@ -508,7 +387,7 @@ void BufferView::saveBookmark(unsigned int idx)
// pit and pos will be updated with bottom level pit/pos
// when lyx exits.
LyX::ref().session().bookmarks().save(
FileName(buffer_->fileName()),
FileName(buffer_.fileName()),
cursor_.bottom().pit(),
cursor_.bottom().pos(),
cursor_.paragraph().id(),
@ -530,8 +409,8 @@ boost::tuple<pit_type, pos_type, int> BufferView::moveToPosition(pit_type bottom
// This is the case for a 'live' bookmark when unique paragraph ID
// is used to track bookmarks.
if (top_id > 0) {
ParIterator par = buffer_->getParFromID(top_id);
if (par != buffer_->par_iterator_end()) {
ParIterator par = buffer_.getParFromID(top_id);
if (par != buffer_.par_iterator_end()) {
DocIterator dit = makeDocIterator(par, min(par->size(), top_pos));
// Some slices of the iterator may not be
// reachable (e.g. closed collapsable inset)
@ -556,8 +435,8 @@ boost::tuple<pit_type, pos_type, int> BufferView::moveToPosition(pit_type bottom
// restoration is inaccurate. If a bookmark was within an inset,
// it will be restored to the left of the outmost inset that contains
// the bookmark.
if (static_cast<size_t>(bottom_pit) < buffer_->paragraphs().size()) {
DocIterator it = doc_iterator_begin(buffer_->inset());
if (static_cast<size_t>(bottom_pit) < buffer_.paragraphs().size()) {
DocIterator it = doc_iterator_begin(buffer_.inset());
it.pit() = bottom_pit;
it.pos() = min(bottom_pos, it.paragraph().size());
setCursor(it);
@ -613,10 +492,10 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd)
switch (cmd.action) {
case LFUN_UNDO:
flag.enabled(!buffer_->undostack().empty());
flag.enabled(!buffer_.undostack().empty());
break;
case LFUN_REDO:
flag.enabled(!buffer_->redostack().empty());
flag.enabled(!buffer_.redostack().empty());
break;
case LFUN_FILE_INSERT:
case LFUN_FILE_INSERT_PLAINTEXT_PARA:
@ -656,12 +535,12 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd)
case LFUN_CHANGES_TRACK:
flag.enabled(true);
flag.setOnOff(buffer_->params().trackChanges);
flag.setOnOff(buffer_.params().trackChanges);
break;
case LFUN_CHANGES_OUTPUT:
flag.enabled(buffer_);
flag.setOnOff(buffer_->params().outputChanges);
flag.enabled(true);
flag.setOnOff(buffer_.params().outputChanges);
break;
case LFUN_CHANGES_MERGE:
@ -672,11 +551,11 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd)
// In principle, these command should only be enabled if there
// is a change in the document. However, without proper
// optimizations, this will inevitably result in poor performance.
flag.enabled(buffer_);
flag.enabled(true);
break;
case LFUN_BUFFER_TOGGLE_COMPRESSION: {
flag.setOnOff(buffer_->params().compressed);
flag.setOnOff(buffer_.params().compressed);
break;
}
@ -702,10 +581,6 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
<< " button[" << cmd.button() << ']'
<< endl;
// FIXME: this should not be possible.
if (!buffer_)
return Update::None;
Cursor & cur = cursor_;
// Default Update flags.
Update::flags updateFlags = Update::Force | Update::FitCursor;
@ -774,7 +649,9 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
case LFUN_PARAGRAPH_GOTO: {
int const id = convert<int>(to_utf8(cmd.argument()));
int i = 0;
for (Buffer * b = buffer_; i == 0 || b != buffer_; b = theBufferList().next(b)) {
for (Buffer * b = &buffer_; i == 0 || b != &buffer_;
b = theBufferList().next(b)) {
ParIterator par = b->getParFromID(id);
if (par == b->par_iterator_end()) {
LYXERR(Debug::INFO)
@ -786,7 +663,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
<< " found in buffer `"
<< b->fileName() << "'." << endl;
if (b == buffer_) {
if (b == &buffer_) {
// Set the cursor
setCursor(makeDocIterator(par, 0));
} else {
@ -806,20 +683,20 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
case LFUN_OUTLINE_UP:
toc::outline(toc::Up, cursor_);
cursor_.text()->setCursor(cursor_, cursor_.pit(), 0);
updateLabels(*buffer_);
updateLabels(buffer_);
break;
case LFUN_OUTLINE_DOWN:
toc::outline(toc::Down, cursor_);
cursor_.text()->setCursor(cursor_, cursor_.pit(), 0);
updateLabels(*buffer_);
updateLabels(buffer_);
break;
case LFUN_OUTLINE_IN:
toc::outline(toc::In, cursor_);
updateLabels(*buffer_);
updateLabels(buffer_);
break;
case LFUN_OUTLINE_OUT:
toc::outline(toc::Out, cursor_);
updateLabels(*buffer_);
updateLabels(buffer_);
break;
case LFUN_NOTE_NEXT:
@ -835,12 +712,12 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
}
case LFUN_CHANGES_TRACK:
buffer_->params().trackChanges = !buffer_->params().trackChanges;
buffer_.params().trackChanges = !buffer_.params().trackChanges;
break;
case LFUN_CHANGES_OUTPUT:
buffer_->params().outputChanges = !buffer_->params().outputChanges;
if (buffer_->params().outputChanges) {
buffer_.params().outputChanges = !buffer_.params().outputChanges;
if (buffer_.params().outputChanges) {
bool dvipost = LaTeXFeatures::isAvailable("dvipost");
bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
LaTeXFeatures::isAvailable("xcolor");
@ -872,21 +749,21 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
case LFUN_ALL_CHANGES_ACCEPT:
// select complete document
cursor_.reset(buffer_->inset());
cursor_.reset(buffer_.inset());
cursor_.selHandle(true);
buffer_->text().cursorBottom(cursor_);
buffer_.text().cursorBottom(cursor_);
// accept everything in a single step to support atomic undo
buffer_->text().acceptOrRejectChanges(cursor_, Text::ACCEPT);
buffer_.text().acceptOrRejectChanges(cursor_, Text::ACCEPT);
break;
case LFUN_ALL_CHANGES_REJECT:
// select complete document
cursor_.reset(buffer_->inset());
cursor_.reset(buffer_.inset());
cursor_.selHandle(true);
buffer_->text().cursorBottom(cursor_);
buffer_.text().cursorBottom(cursor_);
// reject everything in a single step to support atomic undo
// Note: reject does not work recursively; the user may have to repeat the operation
buffer_->text().acceptOrRejectChanges(cursor_, Text::REJECT);
buffer_.text().acceptOrRejectChanges(cursor_, Text::REJECT);
break;
case LFUN_WORD_FIND:
@ -945,7 +822,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
Inset::BIBTEX_CODE);
if (inset) {
if (inset->addDatabase(to_utf8(cmd.argument())))
buffer_->updateBibfilesCache();
buffer_.updateBibfilesCache();
}
break;
}
@ -957,7 +834,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
Inset::BIBTEX_CODE);
if (inset) {
if (inset->delDatabase(to_utf8(cmd.argument())))
buffer_->updateBibfilesCache();
buffer_.updateBibfilesCache();
}
break;
}
@ -968,8 +845,8 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
from = cur.selectionBegin();
to = cur.selectionEnd();
} else {
from = doc_iterator_begin(buffer_->inset());
to = doc_iterator_end(buffer_->inset());
from = doc_iterator_begin(buffer_.inset());
to = doc_iterator_end(buffer_.inset());
}
int const count = countWords(from, to);
docstring message;
@ -994,7 +871,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
case LFUN_BUFFER_TOGGLE_COMPRESSION:
// turn compression on/off
buffer_->params().compressed = !buffer_->params().compressed;
buffer_.params().compressed = !buffer_.params().compressed;
break;
case LFUN_NEXT_INSET_TOGGLE: {
@ -1031,9 +908,6 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
docstring const BufferView::requestSelection()
{
if (!buffer_)
return docstring();
Cursor & cur = cursor_;
if (!cur.selection()) {
@ -1056,7 +930,6 @@ docstring const BufferView::requestSelection()
void BufferView::clearSelection()
{
if (buffer_) {
cursor_.clearSelection();
// Clear the selection buffer. Otherwise a subsequent
// middle-mouse-button paste would use the selection buffer,
@ -1065,8 +938,7 @@ void BufferView::clearSelection()
xsel_cache_.set = false;
// The buffer did not really change, but this causes the
// redraw we need because we cleared the selection above.
buffer_->changed();
}
buffer_.changed();
}
@ -1078,9 +950,7 @@ void BufferView::workAreaResize(int width, int height)
// The complete text metrics will be redone.
text_metrics_.clear();
if (buffer_)
resize();
updateMetrics(false);
}
@ -1140,12 +1010,8 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
// LFUN_FILE_OPEN generated by drag-and-drop.
FuncRequest cmd = cmd0;
// E.g. Qt mouse press when no buffer
if (!buffer_)
return false;
Cursor cur(*this);
cur.push(buffer_->inset());
cur.push(buffer_.inset());
cur.selection() = cursor_.selection();
// Either the inset under the cursor or the
@ -1158,7 +1024,7 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
// Get inset under mouse, if there is one.
Inset const * covering_inset =
getCoveringInset(buffer_->text(), cmd.x, cmd.y);
getCoveringInset(buffer_.text(), cmd.x, cmd.y);
if (covering_inset == last_inset_)
// Same inset, no need to do anything...
return false;
@ -1208,7 +1074,7 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
}
// Build temporary cursor.
Inset * inset = buffer_->text().editXY(cur, cmd.x, cmd.y);
Inset * inset = buffer_.text().editXY(cur, cmd.x, cmd.y);
// Put anchor at the same position.
cur.resetAnchor();
@ -1239,10 +1105,7 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
void BufferView::scroll(int /*lines*/)
{
// if (!buffer_)
// return;
//
// Text const * t = &buffer_->text();
// Text const * t = buffer_.text();
// int const line_height = defaultRowHeight();
//
// // The new absolute coordinate
@ -1262,21 +1125,21 @@ void BufferView::setCursorFromRow(int row)
int tmpid = -1;
int tmppos = -1;
buffer_->texrow().getIdFromRow(row, tmpid, tmppos);
buffer_.texrow().getIdFromRow(row, tmpid, tmppos);
cursor_.reset(buffer_->inset());
cursor_.reset(buffer_.inset());
if (tmpid == -1)
buffer_->text().setCursor(cursor_, 0, 0);
buffer_.text().setCursor(cursor_, 0, 0);
else
buffer_->text().setCursor(cursor_, buffer_->getParFromID(tmpid).pit(), tmppos);
buffer_.text().setCursor(cursor_, buffer_.getParFromID(tmpid).pit(), tmppos);
}
void BufferView::gotoLabel(docstring const & label)
{
for (InsetIterator it = inset_iterator_begin(buffer_->inset()); it; ++it) {
for (InsetIterator it = inset_iterator_begin(buffer_.inset()); it; ++it) {
vector<docstring> labels;
it->getLabelList(*buffer_, labels);
it->getLabelList(buffer_, labels);
if (std::find(labels.begin(), labels.end(), label) != labels.end()) {
setCursor(it);
update();
@ -1343,10 +1206,10 @@ bool BufferView::checkDepm(Cursor & cur, Cursor & old)
if (!changed)
return false;
updateLabels(*buffer_);
updateLabels(buffer_);
updateMetrics(false);
buffer_->changed();
buffer_.changed();
return true;
}
@ -1442,7 +1305,7 @@ ViewMetricsInfo const & BufferView::viewMetricsInfo()
// FIXME: We should split-up updateMetrics() for the singlepar case.
void BufferView::updateMetrics(bool singlepar)
{
Text & buftext = buffer_->text();
Text & buftext = buffer_.text();
TextMetrics & tm = textMetrics(&buftext);
pit_type size = int(buftext.paragraphs().size());
@ -1571,13 +1434,10 @@ void BufferView::menuInsertLyXFile(string const & filenm)
// Launch a file browser
// FIXME UNICODE
string initpath = lyxrc.document_path;
if (buffer_) {
string const trypath = buffer_->filePath();
string const trypath = buffer_.filePath();
// If directory is writeable, use this as default.
if (isDirWriteable(FileName(trypath)))
initpath = trypath;
}
// FIXME UNICODE
FileDialog fileDlg(_("Select LyX document to insert"),
@ -1617,7 +1477,7 @@ void BufferView::menuInsertLyXFile(string const & filenm)
docstring res;
Buffer buf("", false);
if (lyx::loadLyXFile(&buf, FileName(filename))) {
ErrorList & el = buffer_->errorList("Parse");
ErrorList & el = buffer_.errorList("Parse");
// Copy the inserted document error list into the current buffer one.
el = buf.errorList("Parse");
recordUndo(cursor_);
@ -1629,8 +1489,8 @@ void BufferView::menuInsertLyXFile(string const & filenm)
// emit message signal.
message(bformat(res, disp_fn));
buffer_->errors("Parse");
resize();
buffer_.errors("Parse");
updateMetrics(false);
}
} // namespace lyx

View File

@ -79,20 +79,14 @@ struct ScrollbarParameters
*/
class BufferView : boost::noncopyable {
public:
BufferView();
///
BufferView(Buffer & buffer);
~BufferView();
/// set the buffer we are viewing.
/// \todo FIXME: eventually, we will create a new BufferView
/// when switching Buffers, so this method should go.
/// returns the buffer currently set
Buffer * setBuffer(Buffer * b);
/// return the buffer being viewed.
Buffer * buffer() const;
/// resize the BufferView.
void resize();
Buffer * buffer();
Buffer const * buffer() const;
/// perform pending metrics updates.
/** \c Update::FitCursor means first to do a FitCursor, and to
@ -269,7 +263,7 @@ private:
///
CoordCache coord_cache_;
///
Buffer * buffer_;
Buffer & buffer_;
/// Estimated average par height for scrollbar.
int wh_;

View File

@ -77,7 +77,15 @@ bool Importer::Import(LyXView * lv, FileName const & filename,
if (loader_format == "lyx") {
lv->loadLyXFile(lyxfile);
Buffer * buf = lv->loadLyXFile(lyxfile);
if (!buf) {
// we are done
lv->message(_("file not imported!"));
return false;
}
updateLabels(*buf);
lv->setBuffer(buf);
lv->showErrorList("Parse");
} else {
Buffer * const b = newFile(lyxfile.absFilename(), string(), true);
if (b)

View File

@ -435,11 +435,12 @@ int LyX::exec(int & argc, char * argv[])
}
BufferList::iterator begin = pimpl_->buffer_list_.begin();
BufferList::iterator end = pimpl_->buffer_list_.end();
bool final_success = false;
for (BufferList::iterator I = begin; I != end; ++I) {
for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); ++I) {
Buffer * buf = *I;
if (buf != buf->getMasterBuffer())
continue;
bool success = false;
buf->dispatch(batch_command, &success);
final_success |= success;
@ -634,25 +635,36 @@ void LyX::restoreGuiSession()
if (!pimpl_->files_to_load_.empty()) {
for_each(pimpl_->files_to_load_.begin(),
pimpl_->files_to_load_.end(),
bind(&LyXView::loadLyXFile, view, _1, true, false, false));
bind(&LyXView::loadLyXFile, view, _1, true));
// clear this list to save a few bytes of RAM
pimpl_->files_to_load_.clear();
pimpl_->session_->lastOpened().clear();
return;
}
if (!lyxrc.load_session)
return;
} else if (lyxrc.load_session) {
vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
// do not add to the lastfile list since these files are restored from
// last session, and should be already there (regular files), or should
// not be added at all (help files).
for_each(lastopened.begin(), lastopened.end(),
bind(&LyXView::loadLyXFile, view, _1, false, false, false));
bind(&LyXView::loadLyXFile, view, _1, false));
// clear this list to save a few bytes of RAM
pimpl_->session_->lastOpened().clear();
}
BufferList::iterator I = pimpl_->buffer_list_.begin();
BufferList::iterator end = pimpl_->buffer_list_.end();
for (; I != end; ++I) {
Buffer * buf = *I;
if (buf != buf->getMasterBuffer())
continue;
updateLabels(*buf);
}
// FIXME: Switch to the last loaded Buffer. This must not be the first one
// because the Buffer won't be connected in this case. The correct solution
// would be to avoid the manual connection of the current Buffer in LyXView.
view->setBuffer(pimpl_->buffer_list_.last());
}

View File

@ -124,7 +124,7 @@ void LyXAction::init()
{ LFUN_BUFFER_NEW, "buffer-new", NoBuffer },
{ LFUN_BUFFER_NEW_TEMPLATE,"buffer-new-template", NoBuffer },
{ LFUN_BUFFER_RELOAD, "buffer-reload", ReadOnly },
{ LFUN_BUFFER_SWITCH, "buffer-switch", ReadOnly },
{ LFUN_BUFFER_SWITCH, "buffer-switch", NoBuffer | ReadOnly },
{ LFUN_BUFFER_TOGGLE_READ_ONLY, "buffer-toggle-read-only", ReadOnly },
{ LFUN_BUFFER_UPDATE, "buffer-update", ReadOnly },
{ LFUN_BUFFER_VIEW, "buffer-view", ReadOnly },

View File

@ -85,8 +85,9 @@
#include "frontends/KeySymbol.h"
#include "frontends/LyXView.h"
#include "frontends/Menubar.h"
#include "frontends/Toolbars.h"
#include "frontends/Selection.h"
#include "frontends/Toolbars.h"
#include "frontends/WorkArea.h"
#include "support/environment.h"
#include "support/FileFilterList.h"
@ -221,7 +222,7 @@ void LyXFunc::initKeySequences(KeyMap * kb)
void LyXFunc::setLyXView(LyXView * lv)
{
if (!quitting && lyx_view_ && lyx_view_ != lv)
if (!quitting && lyx_view_ && lyx_view_->view() && lyx_view_ != lv)
// save current selection to the selection buffer to allow
// middle-button paste in another window
cap::saveSelection(lyx_view_->view()->cursor());
@ -385,6 +386,12 @@ void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
} else {
dispatch(func);
}
/* When we move around, or type, it's nice to be able to see
* the cursor immediately after the keypress.
*/
if (lyx_view_ && lyx_view_->currentWorkArea())
lyx_view_->currentWorkArea()->startBlinkingCursor();
}
@ -903,15 +910,17 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
// --- Menus -----------------------------------------------
case LFUN_BUFFER_NEW:
menuNew(argument, false);
updateFlags = Update::None;
break;
case LFUN_BUFFER_NEW_TEMPLATE:
menuNew(argument, true);
updateFlags = Update::None;
break;
case LFUN_BUFFER_CLOSE:
closeBuffer();
view()->update();
updateFlags = Update::None;
break;
case LFUN_BUFFER_WRITE:
@ -1187,7 +1196,13 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
}
lyx_view_->message(bformat(_("Opening help file %1$s..."),
makeDisplayPath(fname.absFilename())));
lyx_view_->loadLyXFile(fname, false);
Buffer * buf = lyx_view_->loadLyXFile(fname, false);
if (buf) {
updateLabels(*buf);
lyx_view_->setBuffer(buf);
lyx_view_->showErrorList("Parse");
}
updateFlags = Update::None;
break;
}
@ -1241,29 +1256,31 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
case LFUN_BUFFER_SWITCH:
BOOST_ASSERT(lyx_view_);
lyx_view_->setBuffer(theBufferList().getBuffer(argument));
updateFlags = Update::Force;
updateFlags = Update::None;
break;
case LFUN_BUFFER_NEXT:
BOOST_ASSERT(lyx_view_);
lyx_view_->setBuffer(theBufferList().next(lyx_view_->buffer()));
updateFlags = Update::Force;
updateFlags = Update::None;
break;
case LFUN_BUFFER_PREVIOUS:
BOOST_ASSERT(lyx_view_);
lyx_view_->setBuffer(theBufferList().previous(lyx_view_->buffer()));
updateFlags = Update::Force;
updateFlags = Update::None;
break;
case LFUN_FILE_NEW:
BOOST_ASSERT(lyx_view_);
newFile(*lyx_view_, argument);
updateFlags = Update::None;
break;
case LFUN_FILE_OPEN:
BOOST_ASSERT(lyx_view_);
open(argument);
updateFlags = Update::None;
break;
case LFUN_DROP_LAYOUTS_CHOICE:
@ -1296,24 +1313,35 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
int row;
istringstream is(argument);
is >> file_name >> row;
if (prefixIs(file_name, package().temp_dir().absFilename())) {
Buffer * buf = 0;
bool loaded = false;
if (prefixIs(file_name, package().temp_dir().absFilename()))
// Needed by inverse dvi search. If it is a file
// in tmpdir, call the apropriated function
lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
} else {
buf = theBufferList().getBufferFromTmp(file_name);
else {
// Must replace extension of the file to be .lyx
// and get full path
FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
// Either change buffer or load the file
if (theBufferList().exists(s.absFilename())) {
lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
} else {
lyx_view_->loadLyXFile(s);
if (theBufferList().exists(s.absFilename()))
buf = theBufferList().getBuffer(s.absFilename());
else {
buf = lyx_view_->loadLyXFile(s);
loaded = true;
}
}
if (!buf) {
updateFlags = Update::None;
break;
}
updateLabels(*buf);
lyx_view_->setBuffer(buf);
view()->setCursorFromRow(row);
if (loaded)
lyx_view_->showErrorList("Parse");
updateFlags = Update::FitCursor;
break;
}
@ -1472,36 +1500,33 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
}
case LFUN_BUFFER_CHILD_OPEN: {
// takes an optional argument, "|bool", at the end
// indicating whether this file is being opened automatically
// by LyX itself, in which case we will not want to switch
// buffers after opening. The default is false, so in practice
// it is used only when true.
BOOST_ASSERT(lyx_view_);
int const arglength = argument.length();
FileName filename;
bool autoOpen = false;
if (argument.substr(arglength - 5, 5) == "|true") {
autoOpen = true;
filename = makeAbsPath(argument.substr(0, arglength - 5),
lyx_view_->buffer()->filePath());
} else if (argument.substr(arglength - 6, 6) == "|false") {
filename = makeAbsPath(argument.substr(0, arglength - 6),
lyx_view_->buffer()->filePath());
} else filename =
makeAbsPath(argument, lyx_view_->buffer()->filePath());
BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
Buffer * parent = lyx_view_->buffer();
FileName filename = makeAbsPath(argument, parent->filePath());
view()->saveBookmark(false);
Buffer * child = 0;
bool parsed = false;
if (theBufferList().exists(filename.absFilename())) {
Buffer * buf = theBufferList().getBuffer(filename.absFilename());
if (!autoOpen)
lyx_view_->setBuffer(buf, true);
else
buf->setParentName(lyx_view_->buffer()->fileName());
} else
lyx_view_->loadLyXFile(filename, true, true, autoOpen);
child = theBufferList().getBuffer(filename.absFilename());
} else {
setMessage(bformat(_("Opening child document %1$s..."),
makeDisplayPath(filename.absFilename())));
child = lyx_view_->loadLyXFile(filename, true);
parsed = true;
}
if (child) {
// Set the parent name of the child document.
// This makes insertion of citations and references in the child work,
// when the target is in the parent or another child document.
child->setParentName(parent->fileName());
updateLabels(*child->getMasterBuffer());
lyx_view_->setBuffer(child);
if (parsed)
lyx_view_->showErrorList("Parse");
}
// If a screen update is required (in case where auto_open is false),
// loadLyXFile() would have taken care of it already. Otherwise we shall
// setBuffer() would have taken care of it already. Otherwise we shall
// reset the update flag because it can cause a circular problem.
// See bug 3970.
updateFlags = Update::None;
@ -1997,10 +2022,8 @@ void LyXFunc::menuNew(string const & name, bool fromTemplate)
}
Buffer * const b = newFile(filename, templname, !name.empty());
if (b) {
updateLabels(*b);
if (b)
lyx_view_->setBuffer(b);
}
}
@ -2060,7 +2083,11 @@ void LyXFunc::open(string const & fname)
lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
docstring str2;
if (lyx_view_->loadLyXFile(fullname)) {
Buffer * buf = lyx_view_->loadLyXFile(fullname);
if (buf) {
updateLabels(*buf);
lyx_view_->setBuffer(buf);
lyx_view_->showErrorList("Parse");
str2 = bformat(_("Document %1$s opened."), disp_fn);
} else {
str2 = bformat(_("Could not open document %1$s"), disp_fn);
@ -2169,8 +2196,19 @@ void LyXFunc::closeBuffer()
void LyXFunc::reloadBuffer()
{
FileName filename(lyx_view_->buffer()->fileName());
docstring const disp_fn = makeDisplayPath(filename.absFilename());
docstring str;
closeBuffer();
lyx_view_->loadLyXFile(filename);
Buffer * buf = lyx_view_->loadLyXFile(filename);
if (buf) {
updateLabels(*buf);
lyx_view_->setBuffer(buf);
lyx_view_->showErrorList("Parse");
str = bformat(_("Document %1$s reloaded."), disp_fn);
} else {
str = bformat(_("Could not reload document %1$s"), disp_fn);
}
lyx_view_->message(str);
}
// Each "lyx_view_" should have it's own message method. lyxview and

View File

@ -52,14 +52,11 @@ LyXView & Application::createView(unsigned int width,
const std::string & geometryArg)
{
LyXView & view = gui().createRegisteredView();
int view_id = view.id();
theLyXFunc().setLyXView(&view);
/*int workArea_id_ =*/ gui().newWorkArea(width, height, view_id);
view.init();
view.setGeometry(width, height, posx, posy, maximized, iconSizeXY, geometryArg);
view.setFocus();
setCurrentView(view);

View File

@ -34,29 +34,56 @@ class Selection;
There should be only one instance of this class. No Qt object
initialisation should be done before the instanciation of this class.
\todo The work areas handling could be moved to a base virtual class
common to all frontends.
Model/View/Controller separation at frontend level in LyX-qt4:
BufferList (N Buffers)
|
Buffer-a
Buffer-b
Buffer-c
Buffer-d
Application (this is the frontend really, should probably be renamed).
|
LyXView-1 (M1 WorkAreas, M1 <= N)
| |
| <tab-widget>
| | (many)
| WorkArea-1
| |
| BufferView <-----------> Buffer-c
| |
| Cursor
|
LyXView-2 (M2 WorkAreas, M2 <= N, M2 independent of M1)
|
...
Model/View/Controller separation in LyX:
1) The Model: \c Buffer
The Buffer is the in-memory representation of a LyX file format. The
Buffer does not (should not) have any information on what part of it
is represented on screen. There is one unique Buffer per opened LyX
file.
file. A Buffer may or may not be represented on screen; typically, a
child document does not have an associated BufferView unless the user
choose to visualize it.
2) The Controller: \c BufferView / \c Painter
2) The Controller: \c BufferView / \c Painter \c Cursor
The BufferView is a tool used by the view that translates a part of
the Buffer contents into drawing routines. The BufferView asks each
inset of the Buffer to draw itself onto the screen using the Painter.
There can be only one Buffer displayed in a BufferView. While there
is the possibility to switch Buffer inside the BufferView, the goal
is to instantiate a new BufferView on each Buffer switch.
The BufferView is a tool used by the view (\sa WorkArea) that
translates a part of the Buffer contents into drawing routines. The
BufferView asks each inset of the Buffer to draw itself onto the
screen using the Painter. There can be only one Buffer displayed in
a BufferView and it is set on construction. Ideally, a BufferView
should not be able to change the contents of its associated Buffer.
A BufferView is instanciated and destroyed by a \c WorkArea; it is
automatically destroyed by the parent WorkArea when its Buffer is
closed.
\todo Instantiate a new BufferView on each Buffer switch.
\todo Move all Buffer changing LFUN to LyXFunc or Cursor.
\todo BufferView::buffer() should only offer const access.
The \c Painter is just a virtual interface to formalize each kind of
drawing routines (text, line, rectangle, etc).
@ -69,9 +96,10 @@ common to all frontends.
3) The View: \c WorkArea (and it's qt4 specialisation GuiWorkArea)
This contains the real screen area where the drawing is done by the
Painter. One WorkArea holds one unique \c BufferView. While it could be
possible that multiple WorkArea share one BufferView, this is not
possible right now.
Painter. One WorkArea holds one unique \c BufferView. While it could
be possible that multiple WorkArea share one BufferView, this is not
something desirable because a BufferView is dependent of the WorkArea
size.
The WorkArea also provide a scrollbar which position is translated
into scrolling command to the inner \c BufferView.
@ -84,18 +112,32 @@ common to all frontends.
4) The Window: \c LyXView (and its qt4 specialisation \c GuiView)
This is a full window containing a menubar, toolbars, a tabbar and a
WorkArea. One LyXView could in theory contain multiple WorkArea
(ex: with split window) but this number is limited to one only for
now. In any case, there would be only one WorkArea that gets the focus
This is a full window containing a menubar, toolbars and a central
widget. A LyXView is in charge of creating and closing a View for a
given Buffer.
In the qt4 specialisation, \c GuiView, the central widget is a tab
widget. Each tab is reverved to the visualisation of one Buffer and
contains one WorkArea. In the qt4 frontend, one LyXView thus contains
multiple WorkAreas but this number can limited to one for another
frontend. The idea is that the kernel should not know how a Buffer
is displayed on screen; it's the frontend business.
In the future, we may also have multiple Workareas showing
simultaneously in the same GuiView (ex: with split window).
\todo Implement split-window
In any case, there would be only one WorkArea that gets the focus
at a time.
Now, concerning the TabBar versus TabWidget issue. Right now, there is
only one WorkArea and the TabBar just used to tell the BufferView inside
the WorkArea to switch to this another Buffer.
With our current implementation using a QTabWidget, each Tab own its
own \c WorkArea. Clicking on a tab switch a WorkArea and not really
a Buffer. LFUN_BUFFER_SWITCH will tell the frontend to search the
WorkArea associated to this Buffer. The WorkArea is automatically
created if not already present.
A WorkArea is connected to the Buffer::closing signal and is thus
automatically destroyed when its Buffer is closed.
With a TabWidget, each Tab would own its own \c WorkArea. Clicking on a tab
would switch a WorkArea instead of a Buffer.
*/
class Application
{

View File

@ -52,11 +52,6 @@ public:
return view_ids_;
}
virtual int newWorkArea(unsigned int width, unsigned int height, int view_id) = 0;
///
virtual WorkArea & workArea(int id) = 0;
protected:
std::vector<int> view_ids_;

View File

@ -70,8 +70,7 @@ namespace frontend {
docstring current_layout;
LyXView::LyXView(int id)
: work_area_(0),
toolbars_(new Toolbars(*this)),
: toolbars_(new Toolbars(*this)),
autosave_timeout_(new Timeout(5000)),
dialogs_(new Dialogs(*this)),
controlcommand_(new ControlCommandBuffer(*this)), id_(id)
@ -88,152 +87,78 @@ LyXView::LyXView(int id)
LyXView::~LyXView()
{
disconnectBuffer();
disconnectBufferView();
}
// FIXME, there's only one WorkArea per LyXView possible for now.
void LyXView::setWorkArea(WorkArea * work_area)
Buffer * LyXView::buffer()
{
BOOST_ASSERT(work_area);
work_area_ = work_area;
work_area_ids_.clear();
work_area_ids_.push_back(work_area_->id());
WorkArea * work_area = currentWorkArea();
if (work_area)
return work_area->bufferView().buffer();
return 0;
}
// FIXME, there's only one WorkArea per LyXView possible for now.
WorkArea const * LyXView::currentWorkArea() const
Buffer const * LyXView::buffer() const
{
return work_area_;
WorkArea const * work_area = currentWorkArea();
if (work_area)
return work_area->bufferView().buffer();
return 0;
}
// FIXME, there's only one WorkArea per LyXView possible for now.
WorkArea * LyXView::currentWorkArea()
{
return work_area_;
}
Buffer * LyXView::buffer() const
{
BOOST_ASSERT(work_area_);
return work_area_->bufferView().buffer();
}
void LyXView::setBuffer(Buffer * b, bool child_document)
void LyXView::setBuffer(Buffer * newBuffer)
{
busy(true);
BOOST_ASSERT(work_area_);
Buffer * oldBuffer = work_area_->bufferView().buffer();
Buffer * oldBuffer = buffer();
if (oldBuffer == newBuffer) {
busy(false);
return;
}
// parentfilename will be used in case when we switch to a child
// document (hence when child_document is true)
string parentfilename;
if (oldBuffer)
parentfilename = oldBuffer->fileName();
if (!b && theBufferList().empty())
getDialogs().hideBufferDependent();
Buffer * newBuffer = work_area_->bufferView().setBuffer(b);
if (newBuffer) {
//Are we closing an oldBuffer which was a child document?
if (!b && oldBuffer && oldBuffer->getMasterBuffer() != oldBuffer)
// Update the labels and section numbering of its master Buffer.
updateLabels(*oldBuffer->getMasterBuffer());
//Are we opening a new child document?
else if (child_document && newBuffer->getMasterBuffer() != oldBuffer) {
// Set the parent name of the child document.
// This makes insertion of citations and references in the child work,
// when the target is in the parent or another child document.
newBuffer->setParentName(parentfilename);
// Update the labels and section numbering to the new master Buffer.
WorkArea * wa = workArea(*newBuffer);
if (wa == 0) {
updateLabels(*newBuffer->getMasterBuffer());
}
//Now that all the updating of the old buffer has been done, we can
//connect the new buffer. Note that this will also disconnect the old
//buffer, if such there is.
//FIXME Is it clear that this should go right here? Or should it go
//earlier before the previous if (in which case we'd remove the "else")?
connectBuffer(*newBuffer);
/* FIXME: We need to rebuild the Toc dialog before the others even
if it will be rebuilt again in the next line. This avoid a crash when
other dialogs are rebuilt before the Toc dialog. The reason is
that closing a Buffer triggers an update of all opened dialogs
when dispatching LFUN_DIALOG_UPDATE (hence the patch).
The path is as following:
setBuffer() -> updateBufferDependent() -> RestoreButton() -> LFUN
The problem here is that the Toc dialog has not been
reconstructed (because it comes after in the list of dialogs). */
updateToc();
// Buffer-dependent dialogs should be updated or
// hidden. This should go here because some dialogs (eg ToC)
// require bv_->text.
getDialogs().updateBufferDependent(true);
wa = addWorkArea(*newBuffer);
} else
//Disconnect the old buffer...there's no new one.
disconnectBuffer();
connectBuffer(*newBuffer);
connectBufferView(wa->bufferView());
setCurrentWorkArea(wa);
if (quitting)
return;
updateMenubar();
updateToolbars();
updateLayoutChoice();
updateWindowTitle();
updateStatusBar();
updateTab();
busy(false);
work_area_->redraw();
}
bool LyXView::loadLyXFile(FileName const & filename, bool tolastfiles,
bool child_document, bool auto_open)
Buffer * LyXView::loadLyXFile(FileName const & filename, bool tolastfiles)
{
busy(true);
BOOST_ASSERT(work_area_);
string parentfilename;
Buffer * oldBuffer = work_area_->bufferView().buffer();
Buffer * oldBuffer = buffer();
if (oldBuffer)
parentfilename = oldBuffer->fileName();
bool alreadyLoaded = checkIfLoaded(filename);
Buffer * newBuffer = checkAndLoadLyXFile(filename);
if (!newBuffer) {
message(_("Document not loaded."));
updateStatusBar();
busy(false);
work_area_->redraw();
return false;
return 0;
}
if (child_document && newBuffer != oldBuffer) {
// Set the parent name of the child document.
// This makes insertion of citations and references in the child work,
// when the target is in the parent or another child document.
newBuffer->setParentName(parentfilename);
message(bformat(_("Opening child document %1$s..."),
makeDisplayPath(filename.absFilename())));
}
WorkArea * wa = addWorkArea(*newBuffer);
// Update the labels and section numbering.
updateLabels(*newBuffer->getMasterBuffer());
bool const parse_error = !newBuffer->errorList("Parse").empty();
bool const need_switch = parse_error || !auto_open;
if (need_switch) {
setBuffer(newBuffer, child_document);
if (!alreadyLoaded) {
if (parse_error)
showErrorList("Parse");
// scroll to the position when the file was last closed
if (lyxrc.use_lastfilepos) {
pit_type pit;
@ -241,24 +166,16 @@ bool LyXView::loadLyXFile(FileName const & filename, bool tolastfiles,
boost::tie(pit, pos) = LyX::ref().session().lastFilePos().load(filename);
// if successfully move to pit (returned par_id is not zero),
// update metrics and reset font
if (work_area_->bufferView().moveToPosition(pit, pos, 0, 0).get<1>()) {
if (work_area_->bufferView().fitCursor())
work_area_->bufferView().updateMetrics(false);
newBuffer->text().setCurrentFont(work_area_->bufferView().cursor());
updateMenubar();
updateToolbars();
updateLayoutChoice();
updateStatusBar();
work_area_->redraw();
}
}
if (tolastfiles)
LyX::ref().session().lastFiles().add(filename);
BufferView & bv = wa->bufferView();
if (bv.moveToPosition(pit, pos, 0, 0).get<1>()) {
if (bv.fitCursor())
bv.updateMetrics(false);
newBuffer->text().setCurrentFont(bv.cursor());
}
}
busy(false);
return true;
return newBuffer;
}
@ -267,11 +184,6 @@ void LyXView::connectBuffer(Buffer & buf)
if (errorsConnection_.connected())
disconnectBuffer();
BOOST_ASSERT(work_area_);
bufferChangedConnection_ =
buf.changed.connect(
boost::bind(&WorkArea::redraw, work_area_));
bufferStructureChangedConnection_ =
buf.getMasterBuffer()->structureChanged.connect(
boost::bind(&LyXView::updateToc, this));
@ -299,30 +211,26 @@ void LyXView::connectBuffer(Buffer & buf)
readonlyConnection_ =
buf.readonly.connect(
boost::bind(&LyXView::showReadonly, this, _1));
closingConnection_ =
buf.closing.connect(
boost::bind(&LyXView::setBuffer, this, (Buffer *)0, false));
}
void LyXView::disconnectBuffer()
{
errorsConnection_.disconnect();
bufferChangedConnection_.disconnect();
bufferStructureChangedConnection_.disconnect();
messageConnection_.disconnect();
busyConnection_.disconnect();
titleConnection_.disconnect();
timerConnection_.disconnect();
readonlyConnection_.disconnect();
closingConnection_.disconnect();
layout_changed_connection_.disconnect();
}
void LyXView::connectBufferView(BufferView & bv)
{
message_connection_ = bv.message.connect(
boost::bind(&LyXView::message, this, _1));
show_dialog_connection_ = bv.showDialog.connect(
boost::bind(&LyXView::showDialog, this, _1));
show_dialog_with_data_connection_ = bv.showDialogWithData.connect(
@ -338,6 +246,7 @@ void LyXView::connectBufferView(BufferView & bv)
void LyXView::disconnectBufferView()
{
message_connection_.disconnect();
show_dialog_connection_.disconnect();
show_dialog_with_data_connection_.disconnect();
show_inset_dialog_connection_.disconnect();
@ -387,10 +296,10 @@ void LyXView::showReadonly(bool)
}
BufferView * LyXView::view() const
BufferView * LyXView::view()
{
BOOST_ASSERT(work_area_);
return &work_area_->bufferView();
WorkArea * wa = currentWorkArea();
return wa? &wa->bufferView() : 0;
}
@ -402,9 +311,10 @@ void LyXView::updateToc()
void LyXView::updateToolbars()
{
BOOST_ASSERT(work_area_);
WorkArea * wa = currentWorkArea();
if (wa) {
bool const math =
work_area_->bufferView().cursor().inMathed();
wa->bufferView().cursor().inMathed();
bool const table =
lyx::getStatus(FuncRequest(LFUN_LAYOUT_TABULAR)).enabled();
bool const review =
@ -412,6 +322,9 @@ void LyXView::updateToolbars()
lyx::getStatus(FuncRequest(LFUN_CHANGES_TRACK)).onoff(true);
toolbars_->update(math, table, review);
} else
toolbars_->update(false, false, false);
// update redaonly status of open dialogs. This could also be in
// updateMenubar(), but since updateToolbars() and updateMenubar()
// are always called together it is only here.
@ -449,7 +362,7 @@ void LyXView::autoSave()
{
LYXERR(Debug::INFO) << "Running autoSave()" << endl;
if (view()->buffer())
if (buffer())
lyx::autoSave(view());
}
@ -464,7 +377,7 @@ void LyXView::resetAutosaveTimer()
void LyXView::updateLayoutChoice()
{
// Don't show any layouts without a buffer
if (!view()->buffer()) {
if (!buffer()) {
toolbars_->clearLayoutList();
return;
}
@ -474,8 +387,7 @@ void LyXView::updateLayoutChoice()
current_layout = buffer()->params().getTextClass().defaultLayoutName();
}
BOOST_ASSERT(work_area_);
docstring const & layout = work_area_->bufferView().cursor().
docstring const & layout = currentWorkArea()->bufferView().cursor().
innerParagraph().layout()->name();
if (layout != current_layout) {
@ -490,42 +402,50 @@ void LyXView::updateWindowTitle()
docstring maximize_title = lyx::from_ascii("LyX");
docstring minimize_title = lyx::from_ascii("LyX");
if (view()->buffer()) {
string const cur_title = buffer()->fileName();
Buffer * buf = buffer();
if (buf) {
string const cur_title = buf->fileName();
if (!cur_title.empty()) {
maximize_title += ": " + makeDisplayPath(cur_title, 30);
minimize_title = lyx::from_utf8(onlyFilename(cur_title));
if (!buffer()->isClean()) {
if (!buf->isClean()) {
maximize_title += _(" (changed)");
minimize_title += lyx::char_type('*');
}
if (buffer()->isReadonly())
if (buf->isReadonly())
maximize_title += _(" (read only)");
}
}
setWindowTitle(maximize_title, minimize_title);
updateTab();
}
void LyXView::dispatch(FuncRequest const & cmd)
{
string const argument = to_utf8(cmd.argument());
switch(cmd.action) {
case LFUN_BUFFER_SWITCH:
setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
break;
default:
theLyXFunc().setLyXView(this);
lyx::dispatch(cmd);
}
}
Buffer const * const LyXView::updateInset(Inset const * inset) const
Buffer const * const LyXView::updateInset(Inset const * inset)
{
Buffer const * buffer_ptr = 0;
if (inset) {
BOOST_ASSERT(work_area_);
work_area_->scheduleRedraw();
WorkArea * work_area = currentWorkArea();
if (!work_area)
return 0;
buffer_ptr = work_area_->bufferView().buffer();
if (inset) {
BOOST_ASSERT(work_area);
work_area->scheduleRedraw();
}
return buffer_ptr;
return work_area->bufferView().buffer();
}
} // namespace frontend

View File

@ -70,16 +70,19 @@ public:
virtual void setFocus() = 0;
std::vector<int> const & workAreaIds() const { return work_area_ids_; }
/// FIXME: rename to setCurrentWorkArea()
void setWorkArea(WorkArea * work_area);
///
virtual WorkArea * workArea(Buffer & buffer) = 0;
///
virtual WorkArea * addWorkArea(Buffer & buffer) = 0;
///
virtual void setCurrentWorkArea(WorkArea * work_area) = 0;
///
virtual void removeWorkArea(WorkArea * work_area) = 0;
/// return the current WorkArea (the one that has the focus).
WorkArea const * currentWorkArea() const;
virtual WorkArea const * currentWorkArea() const = 0;
/// FIXME: This non-const access is needed because of
/// a mis-designed \c ControlSpellchecker.
WorkArea * currentWorkArea();
virtual WorkArea * currentWorkArea() = 0;
/**
* This is called after the concrete view has been created.
@ -114,14 +117,12 @@ public:
//@{ generic accessor functions
/** return the current buffer view
Returned as a shared_ptr so that anything wanting to cache the
buffer view can do so safely using a boost::weak_ptr.
*/
BufferView * view() const;
/// \return the current buffer view.
BufferView * view();
/// return the buffer currently shown in this window
Buffer * buffer() const;
/// \return the buffer currently shown in this window
Buffer * buffer();
Buffer const * buffer() const;
/// return the toolbar for this view
Toolbars & getToolbars() { return *toolbars_.get(); }
@ -141,14 +142,11 @@ public:
//@}
/// load a buffer into the current workarea.
bool loadLyXFile(support::FileName const & name, ///< File to load.
bool tolastfiles = true, ///< append to the "Open recent" menu?
bool child_document = false, ///< Is this a child document?
bool auto_open = false); ///< Is this being opened by LyX itself?
Buffer * loadLyXFile(support::FileName const & name, ///< File to load.
bool tolastfiles = true); ///< append to the "Open recent" menu?
/// set a buffer to the current workarea.
void setBuffer(Buffer * b, ///< \c Buffer to set.
bool child_document = false); ///< Is this a child document?
void setBuffer(Buffer * b); ///< \c Buffer to set.
/// updates the possible layouts selectable
void updateLayoutChoice();
@ -176,9 +174,6 @@ public:
/// updates the title of the window
void updateWindowTitle();
/// updates the tab view
virtual void updateTab() = 0;
/// reset autosave timer
void resetAutosaveTimer();
@ -188,7 +183,7 @@ public:
/** redraw \c inset in all the BufferViews in which it is currently
* visible. If successful return a pointer to the owning Buffer.
*/
Buffer const * const updateInset(Inset const *) const;
Buffer const * const updateInset(Inset const *);
/// returns true if this view has the focus.
virtual bool hasFocus() const = 0;
@ -196,17 +191,15 @@ public:
/// show the error list to the user
void showErrorList(std::string const &);
protected:
/// connect to signals in the given BufferView
void connectBufferView(BufferView & bv);
/// disconnect from signals in the given BufferView
void disconnectBufferView();
protected:
/// current work area (screen view of a BufferView).
/**
\todo FIXME: there is only one workArea per LyXView for now.
*/
frontend::WorkArea * work_area_;
/// connect to signals in the given buffer
void connectBuffer(Buffer & buf);
/// disconnect from signals in the given buffer
void disconnectBuffer();
/// view's menubar
boost::scoped_ptr<Menubar> menubar_;
@ -231,8 +224,6 @@ private:
/// dialogs for this view
boost::scoped_ptr<Dialogs> dialogs_;
/// buffer changed signal connection
boost::signals::connection bufferChangedConnection_;
/// buffer structure changed signal connection
boost::signals::connection bufferStructureChangedConnection_;
/// buffer errors signal connection
@ -247,18 +238,6 @@ private:
boost::signals::connection timerConnection_;
/// buffer readonly status changed signal connection
boost::signals::connection readonlyConnection_;
/// buffer closing signal connection
boost::signals::connection closingConnection_;
/// connect to signals in the given buffer
void connectBuffer(Buffer & buf);
/// disconnect from signals in the given buffer
/// NOTE: Do not call this unless you really want no buffer
/// to be connected---for example, when closing the last open
/// buffer. If you are switching buffers, just call
/// connectBuffer(), and the old buffer will be disconnected
/// automatically. This ensures that we do not leave LyX in a
/// state in which no buffer is connected.
void disconnectBuffer();
/// BufferView messages signal connection
//@{
@ -298,7 +277,6 @@ protected:
private:
int id_;
std::vector<int> work_area_ids_;
};
} // namespace frontend

View File

@ -17,26 +17,27 @@
#include "frontends/Application.h"
#include "frontends/FontMetrics.h"
#include "FuncRequest.h"
#include "LyXFunc.h"
#include "frontends/LyXView.h"
#include "BufferView.h"
#include "Buffer.h"
#include "BufferParams.h"
#include "Color.h"
#include "CoordCache.h"
#include "Cursor.h"
#include "debug.h"
#include "Language.h"
#include "Color.h"
#include "Font.h"
#include "FuncRequest.h"
#include "Language.h"
#include "LyX.h"
#include "LyXFunc.h"
#include "LyXRC.h"
#include "Text.h"
#include "LyXView.h"
#include "MetricsInfo.h"
#include "Text.h"
#include "gettext.h"
#include "support/ForkedcallsController.h"
#include "support/FileName.h"
#include <boost/utility.hpp>
#include <boost/bind.hpp>
@ -64,41 +65,56 @@ boost::signals::connection timecon;
namespace lyx {
namespace frontend {
WorkArea::WorkArea(int id, LyXView & lyx_view)
: buffer_view_(0), lyx_view_(lyx_view), greyed_out_(true),
id_(id), cursor_visible_(false), cursor_timeout_(400)
WorkArea::WorkArea(Buffer & buffer, LyXView & lv)
: buffer_view_(new BufferView(buffer)), lyx_view_(&lv),
cursor_visible_(false), cursor_timeout_(400)
{
// Start loading the pixmap as soon as possible
//if (lyxrc.show_banner) {
// showBanner();
//}
// Setup the signals
timecon = cursor_timeout_.timeout
.connect(boost::bind(&WorkArea::toggleCursor, this));
bufferChangedConnection_ =
buffer.changed.connect(
boost::bind(&WorkArea::redraw, this));
bufferClosingConnection_ =
buffer.closing.connect(
boost::bind(&WorkArea::close, this));
cursor_timeout_.start();
}
void WorkArea::setBufferView(BufferView * buffer_view)
WorkArea::~WorkArea()
{
if (buffer_view_) {
message_connection_.disconnect();
lyx_view_.disconnectBufferView();
}
bufferChangedConnection_.disconnect();
bufferClosingConnection_.disconnect();
hideCursor();
buffer_view_ = buffer_view;
toggleCursor();
// current buffer is going to be switched-off, save cursor pos
// Ideally, the whole cursor stack should be saved, but session
// currently can only handle bottom (whole document) level pit and pos.
// That is to say, if a cursor is in a nested inset, it will be
// restore to the left of the top level inset.
Cursor & cur = buffer_view_->cursor();
LyX::ref().session().lastFilePos().save(
support::FileName(buffer_view_->buffer()->fileName()),
boost::tie(cur.bottom().pit(), cur.bottom().pos()) );
message_connection_ = buffer_view_->message.connect(
boost::bind(&WorkArea::displayMessage, this, _1));
lyx_view_.connectBufferView(*buffer_view);
delete buffer_view_;
}
void WorkArea::close()
{
lyx_view_->removeWorkArea(this);
}
//void WorkArea::setLyXView(LyXView * lyx_view)
//{
// lyx_view_ = lyx_view;
//}
BufferView & WorkArea::bufferView()
{
return *buffer_view_;
@ -127,16 +143,13 @@ void WorkArea::startBlinkingCursor()
void WorkArea::redraw()
{
if (!buffer_view_ || !buffer_view_->buffer()) {
greyed_out_ = true;
// The argument here are meaningless.
expose(1,1,1,1);
if (!isVisible())
// No need to redraw in this case.
return;
}
// No need to do anything if this is the current view. The BufferView
// metrics are already up to date.
if (&lyx_view_ != theApp()->currentView()) {
if (lyx_view_ != theApp()->currentView()) {
// FIXME: it would be nice to optimize for the off-screen case.
buffer_view_->updateMetrics(false);
buffer_view_->cursor().fixIfBroken();
@ -152,7 +165,6 @@ void WorkArea::redraw()
}
ViewMetricsInfo const & vi = buffer_view_->viewMetricsInfo();
greyed_out_ = false;
LYXERR(Debug::WORKAREA) << "WorkArea::redraw screen" << endl;
@ -176,13 +188,8 @@ void WorkArea::processKeySym(KeySymbolPtr key, key_modifier::state state)
// the blinking cursor.
stopBlinkingCursor();
theLyXFunc().setLyXView(&lyx_view_);
theLyXFunc().setLyXView(lyx_view_);
theLyXFunc().processKeySym(key, state);
/* When we move around, or type, it's nice to be able to see
* the cursor immediately after the keypress.
*/
startBlinkingCursor();
}
@ -190,11 +197,11 @@ void WorkArea::dispatch(FuncRequest const & cmd0, key_modifier::state k)
{
// Handle drag&drop
if (cmd0.action == LFUN_FILE_OPEN) {
lyx_view_.dispatch(cmd0);
lyx_view_->dispatch(cmd0);
return;
}
theLyXFunc().setLyXView(&lyx_view_);
theLyXFunc().setLyXView(lyx_view_);
FuncRequest cmd;
@ -222,9 +229,9 @@ void WorkArea::dispatch(FuncRequest const & cmd0, key_modifier::state k)
// Skip these when selecting
if (cmd.action != LFUN_MOUSE_MOTION) {
lyx_view_.updateLayoutChoice();
lyx_view_.updateMenubar();
lyx_view_.updateToolbars();
lyx_view_->updateLayoutChoice();
lyx_view_->updateMenubar();
lyx_view_->updateToolbars();
}
// GUI tweaks except with mouse motion with no button pressed.
@ -233,7 +240,7 @@ void WorkArea::dispatch(FuncRequest const & cmd0, key_modifier::state k)
// Slight hack: this is only called currently when we
// clicked somewhere, so we force through the display
// of the new status here.
lyx_view_.clearMessage();
lyx_view_->clearMessage();
// Show the cursor immediately after any operation.
startBlinkingCursor();
@ -243,12 +250,12 @@ void WorkArea::dispatch(FuncRequest const & cmd0, key_modifier::state k)
void WorkArea::resizeBufferView()
{
lyx_view_.busy(true);
lyx_view_.message(_("Formatting document..."));
lyx_view_->busy(true);
lyx_view_->message(_("Formatting document..."));
buffer_view_->workAreaResize(width(), height());
lyx_view_.updateLayoutChoice();
lyx_view_.clearMessage();
lyx_view_.busy(false);
lyx_view_->updateLayoutChoice();
lyx_view_->clearMessage();
lyx_view_->busy(false);
}
@ -268,7 +275,7 @@ void WorkArea::scrollBufferView(int position)
redraw();
if (lyxrc.cursor_follows_scrollbar) {
buffer_view_->setCursorFromScrollbar();
lyx_view_.updateLayoutChoice();
lyx_view_->updateLayoutChoice();
}
// Show the cursor immediately after any operation.
startBlinkingCursor();
@ -280,9 +287,6 @@ void WorkArea::showCursor()
if (cursor_visible_)
return;
if (!buffer_view_->buffer())
return;
CursorShape shape = BAR_SHAPE;
Text const & text = *buffer_view_->cursor().innerText();
@ -332,8 +336,6 @@ void WorkArea::hideCursor()
void WorkArea::toggleCursor()
{
if (buffer_view_->buffer()) {
if (cursor_visible_)
hideCursor();
else
@ -344,16 +346,9 @@ void WorkArea::toggleCursor()
// to the rest of LyX.
ForkedcallsController & fcc = ForkedcallsController::get();
fcc.handleCompletedProcesses();
}
cursor_timeout_.restart();
}
void WorkArea::displayMessage(lyx::docstring const & message)
{
lyx_view_.message(message);
}
} // namespace frontend
} // namespace lyx

View File

@ -25,7 +25,7 @@
#undef CursorShape
namespace lyx {
class Buffer;
class BufferView;
class FuncRequest;
@ -53,13 +53,13 @@ enum CursorShape {
*/
class WorkArea : public boost::signals::trackable {
public:
WorkArea(int id, LyXView & lyx_view);
///
WorkArea(Buffer & buffer, LyXView & lv);
virtual ~WorkArea() {}
virtual ~WorkArea();
int const id() const { return id_; }
void setBufferView(BufferView * buffer_view);
///
void setLyXView(LyXView & lv) { lyx_view_ = &lv; }
///
BufferView & bufferView();
@ -69,6 +69,9 @@ public:
/// \return true if has the keyboard input focus.
virtual bool hasFocus() const = 0;
/// \return true if has this WorkArea is visible.
virtual bool isVisible() const = 0;
/// return the width of the work area in pixels
virtual int width() const = 0;
@ -95,12 +98,17 @@ public:
/// Process Key pressed event.
/// This needs to be public because it is accessed externally by GuiView.
void processKeySym(KeySymbolPtr key, key_modifier::state state);
protected:
/// cause the display of the given area of the work area
virtual void expose(int x, int y, int w, int h) = 0;
///
void dispatch(FuncRequest const & cmd0,
key_modifier::state = key_modifier::none);
/// close this work area.
/// Slot for Buffer::closing boost signal.
void close();
///
void resizeBufferView();
///
@ -120,25 +128,20 @@ protected:
///
BufferView * buffer_view_;
///
LyXView & lyx_view_;
///
bool greyed_out_;
LyXView * lyx_view_;
private:
///
int id_;
///
void displayMessage(docstring const &);
/// buffer messages signal connection
boost::signals::connection message_connection_;
/// is the cursor currently displayed
bool cursor_visible_;
///
Timeout cursor_timeout_;
/// buffer changed signal connection
boost::signals::connection bufferChangedConnection_;
/// buffer closing signal connection
boost::signals::connection bufferClosingConnection_;
};
} // namespace frontend

View File

@ -42,6 +42,7 @@
#include <QFileOpenEvent>
#include <QLocale>
#include <QLibraryInfo>
#include <QPixmapCache>
#include <QTextCodec>
#include <QTimer>
#include <QTranslator>
@ -147,6 +148,10 @@ GuiApplication::GuiApplication(int & argc, char ** argv)
LoaderQueue::setPriority(10,100);
guiApp = this;
// Set the cache to 5120 kilobytes which corresponds to screen size of
// 1280 by 1024 pixels with a color depth of 32 bits.
QPixmapCache::setCacheLimit(5120);
}

View File

@ -12,22 +12,12 @@
#include <config.h>
// This include must be declared before everything else because
// of boost/Qt/LyX clash...
#include "GuiImplementation.h"
#include "GuiView.h"
#include "GuiImplementation.h"
#include "GuiWorkArea.h"
#include "BufferView.h"
#include "BufferList.h"
#include "FuncRequest.h"
#include "LyXFunc.h"
#include <QApplication>
using boost::shared_ptr;
namespace
{
@ -49,7 +39,6 @@ namespace frontend {
GuiImplementation::GuiImplementation()
{
view_ids_.clear();
work_area_ids_.clear();
}
@ -74,9 +63,6 @@ bool GuiImplementation::unregisterView(int id)
std::map<int, GuiView *>::iterator it;
for (it = views_.begin(); it != views_.end(); ++it) {
if (it->first == id) {
std::vector<int> const & wa_ids = it->second->workAreaIds();
for (size_t i = 0; i < wa_ids.size(); ++i)
work_areas_.erase(wa_ids[i]);
views_.erase(id);
break;
}
@ -108,9 +94,7 @@ bool GuiImplementation::closeAllViews()
}
views_.clear();
work_areas_.clear();
view_ids_.clear();
work_area_ids_.clear();
return true;
}
@ -122,43 +106,6 @@ LyXView& GuiImplementation::view(int id) const
}
std::vector<int> const & GuiImplementation::workAreaIds()
{
updateIds(work_areas_, work_area_ids_);
return work_area_ids_;
}
int GuiImplementation::newWorkArea(unsigned int w, unsigned int h, int view_id)
{
updateIds(views_, view_ids_);
int id = 0;
while (work_areas_.find(id) != work_areas_.end())
id++;
GuiView * view = views_[view_id];
work_areas_.insert(std::pair<int, GuiWorkArea *>
(id, new GuiWorkArea(w, h, id, *view)));
// FIXME BufferView creation should be independant of WorkArea creation
buffer_views_[id].reset(new BufferView);
work_areas_[id]->setBufferView(buffer_views_[id].get());
view->setWorkArea(work_areas_[id]);
view->initTab(work_areas_[id]);
return id;
}
WorkArea& GuiImplementation::workArea(int id)
{
BOOST_ASSERT(work_areas_.find(id) != work_areas_.end());
return *work_areas_[id];
}
} // namespace frontend
} // namespace lyx

View File

@ -22,7 +22,6 @@
namespace lyx {
namespace frontend {
class GuiWorkArea;
class GuiView;
class LyXView;
@ -44,9 +43,6 @@ public:
virtual LyXView& view(int id) const;
virtual int newWorkArea(unsigned int width, unsigned int height, int view_id);
virtual WorkArea& workArea(int id);
private:
/// Multiple views container.
@ -56,22 +52,6 @@ private:
* \sa Qt::WA_DeleteOnClose attribute.
*/
std::map<int, GuiView *> views_;
/// Multiple workareas container.
/**
* Warning: This must not be a smart pointer as the destruction of the
* object is handled by Qt when its parent view is closed.
*/
std::map<int, GuiWorkArea *> work_areas_;
///
/// view of a buffer. Eventually there will be several.
std::map<int, boost::shared_ptr<BufferView> > buffer_views_;
std::vector<int> const & workAreaIds();
std::vector<int> work_area_ids_;
};
} // namespace frontend

View File

@ -24,6 +24,7 @@
#include "qt_helpers.h"
#include "frontends/Application.h"
#include "frontends/Dialogs.h"
#include "frontends/Gui.h"
#include "frontends/WorkArea.h"
@ -43,6 +44,7 @@
#include "LyXRC.h"
#include "MenuBackend.h"
#include "Session.h"
#include "version.h"
#include <QAction>
#include <QApplication>
@ -50,17 +52,16 @@
#include <QDesktopWidget>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QHBoxLayout>
#include <QList>
#include <QMimeData>
#include <QPainter>
#include <QPixmap>
#include <QPushButton>
#include <QStackedWidget>
#include <QStatusBar>
#include <QToolBar>
#include <QTabBar>
#include <QTabWidget>
#include <QUrl>
#include <QVBoxLayout>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
@ -81,84 +82,59 @@ namespace {
int const statusbar_timer_value = 3000;
class TabWidget : public QWidget
class BackgroundWidget: public QWidget
{
QHBoxLayout* hlayout;
public:
QTabBar* tabbar;
QPushButton* closeTabButton;
void hideTabsIfNecessary()
BackgroundWidget(QString const & file, QString const & text)
{
if (tabbar->count() > 1) {
tabbar->show();
closeTabButton->show();
} else {
tabbar->hide();
closeTabButton->hide();
}
splash_ = new QPixmap(file);
if (!splash_) {
lyxerr << "could not load splash screen: '" << fromqstr(file) << "'" << endl;
return;
}
TabWidget(QWidget* w, bool topTabBar)
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]));
pain.setFont(font);
pain.drawText(260, 270, text);
}
void paintEvent(QPaintEvent * ev)
{
closeTabButton = new QPushButton(this);
FileName const file = support::libFileSearch("images", "closetab", "xpm");
if (!file.empty()) {
QPixmap pm(toqstr(file.absFilename()));
closeTabButton->setIcon(QIcon(pm));
closeTabButton->setMaximumSize(pm.size());
closeTabButton->setFlat(true);
} else {
closeTabButton->setText("Close");
}
closeTabButton->setCursor(Qt::ArrowCursor);
closeTabButton->setToolTip(tr("Close tab"));
closeTabButton->setEnabled(true);
if (!splash_)
return;
tabbar = new QTabBar;
#if QT_VERSION >= 0x040200
tabbar->setUsesScrollButtons(true);
#endif
hlayout = new QHBoxLayout;
QVBoxLayout* vlayout = new QVBoxLayout;
hlayout->addWidget(tabbar);
hlayout->addWidget(closeTabButton);
if (topTabBar) {
vlayout->addLayout(hlayout);
vlayout->addWidget(w);
} else {
tabbar->setShape(QTabBar::RoundedSouth);
vlayout->addWidget(w);
vlayout->addLayout(hlayout);
}
vlayout->setMargin(0);
vlayout->setSpacing(0);
hlayout->setMargin(0);
setLayout(vlayout);
hideTabsIfNecessary();
int x = (width() - splash_->width()) / 2;
int y = (height() - splash_->height()) / 2;
QPainter pain(this);
pain.drawPixmap(x, y, *splash_);
}
void clearTabbar()
{
for (int i = tabbar->count() - 1; i >= 0; --i)
tabbar->removeTab(i);
}
private:
QPixmap * splash_;
};
} // namespace anon
struct GuiView::GuiViewPrivate
{
vector<string> tabnames;
string cur_title;
TabWidget* tabWidget;
int posx_offset;
int posy_offset;
GuiViewPrivate() : tabWidget(0), posx_offset(0), posy_offset(0)
QTabWidget * tab_widget_;
QStackedWidget * stack_widget_;
BackgroundWidget * bg_widget_;
GuiViewPrivate() : posx_offset(0), posy_offset(0)
{}
unsigned int smallIconSize;
@ -201,6 +177,28 @@ struct GuiView::GuiViewPrivate
return menu;
}
void initBackground()
{
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;
bg_widget_ = new BackgroundWidget(toqstr(file.absFilename()), text);
}
void setBackground()
{
if (!bg_widget_)
return;
stack_widget_->setCurrentWidget(bg_widget_);
bg_widget_->setUpdatesEnabled(true);
}
};
@ -228,6 +226,44 @@ GuiView::GuiView(int id)
setWindowIcon(QPixmap(toqstr(iconname.absFilename())));
#endif
d.tab_widget_ = new QTabWidget;
QPushButton * closeTabButton = new QPushButton(this);
FileName const file = support::libFileSearch("images", "closetab", "xpm");
if (!file.empty()) {
QPixmap pm(toqstr(file.absFilename()));
closeTabButton->setIcon(QIcon(pm));
closeTabButton->setMaximumSize(pm.size());
closeTabButton->setFlat(true);
} else {
closeTabButton->setText("Close");
}
closeTabButton->setCursor(Qt::ArrowCursor);
closeTabButton->setToolTip(tr("Close tab"));
closeTabButton->setEnabled(true);
QObject::connect(d.tab_widget_, SIGNAL(currentChanged(int)),
this, SLOT(currentTabChanged(int)));
QObject::connect(closeTabButton, SIGNAL(clicked()),
this, SLOT(closeCurrentTab()));
d.tab_widget_->setCornerWidget(closeTabButton);
#if QT_VERSION >= 0x040200
d.tab_widget_->setUsesScrollButtons(true);
#endif
d.initBackground();
if (d.bg_widget_) {
lyxerr << "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);
}
@ -250,8 +286,8 @@ void GuiView::close()
void GuiView::setFocus()
{
BOOST_ASSERT(work_area_);
static_cast<GuiWorkArea *>(work_area_)->setFocus();
if (d.tab_widget_->count())
d.tab_widget_->currentWidget()->setFocus();
}
@ -274,14 +310,8 @@ void GuiView::init()
QObject::connect(&statusbar_timer_, SIGNAL(timeout()),
this, SLOT(update_view_state_qt()));
BOOST_ASSERT(work_area_);
if (!work_area_->bufferView().buffer() && !theBufferList().empty())
setBuffer(theBufferList().first());
// make sure the buttons are disabled if needed
updateToolbars();
updateLayoutChoice();
updateMenubar();
if (d.stack_widget_)
d.stack_widget_->setCurrentWidget(d.bg_widget_);
}
@ -311,15 +341,6 @@ void GuiView::closeEvent(QCloseEvent * close_event)
quitting = true;
if (view()->buffer()) {
// save cursor position for opened files to .lyx/session
// only bottom (whole doc) level pit and pos is saved.
LyX::ref().session().lastFilePos().save(
FileName(buffer()->fileName()),
boost::tie(view()->cursor().bottom().pit(),
view()->cursor().bottom().pos()));
}
// this is the place where we leave the frontend.
// it is the only point at which we start quitting.
saveGeometry();
@ -502,6 +523,8 @@ void GuiView::setGeometry(unsigned int width,
#endif
}
d.setBackground();
show();
// For an unknown reason, the Window title update is not effective for
@ -610,56 +633,6 @@ void GuiView::update_view_state_qt()
}
void GuiView::initTab(QWidget* workarea)
{
// construct the TabWidget with 'false' to have the tabbar at the bottom
d.tabWidget = new TabWidget(workarea, true);
setCentralWidget(d.tabWidget);
QObject::connect(d.tabWidget->tabbar, SIGNAL(currentChanged(int)),
this, SLOT(currentTabChanged(int)));
QObject::connect(d.tabWidget->closeTabButton, SIGNAL(clicked()),
this, SLOT(closeCurrentTab()));
}
void GuiView::updateTab()
{
std::vector<string> const & names = theBufferList().getFileNames();
string cur_title;
if (view()->buffer()) {
cur_title = view()->buffer()->fileName();
}
// avoid unnecessary tabbar rebuild:
// check if something has changed
if (d.tabnames == names && d.cur_title == cur_title)
return;
d.tabnames = names;
d.cur_title = cur_title;
QTabBar & tabbar = *d.tabWidget->tabbar;
// update when all is done
tabbar.blockSignals(true);
// remove all tab bars
d.tabWidget->clearTabbar();
// rebuild tabbar and function map from scratch
if (names.size() > 1) {
for(size_t i = 0; i < names.size(); i++) {
tabbar.addTab(toqstr(makeDisplayPath(names[i], 30)));
// set current tab
if (names[i] == cur_title)
tabbar.setCurrentIndex(i);
}
}
tabbar.blockSignals(false);
d.tabWidget->hideTabsIfNecessary();
}
void GuiView::closeCurrentTab()
{
dispatch(FuncRequest(LFUN_BUFFER_CLOSE));
@ -668,8 +641,32 @@ void GuiView::closeCurrentTab()
void GuiView::currentTabChanged(int i)
{
BOOST_ASSERT(i >= 0 && size_type(i) < d.tabnames.size());
dispatch(FuncRequest(LFUN_BUFFER_SWITCH, d.tabnames[i]));
disconnectBuffer();
disconnectBufferView();
GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(d.tab_widget_->widget(i));
BOOST_ASSERT(wa);
BufferView & bv = wa->bufferView();
connectBufferView(bv);
connectBuffer(*bv.buffer());
bv.updateMetrics(false);
bv.cursor().fixIfBroken();
wa->setUpdatesEnabled(true);
wa->redraw();
wa->setFocus();
updateToc();
// Buffer-dependent dialogs should be updated or
// hidden. This should go here because some dialogs (eg ToC)
// require bv_->text.
getDialogs().updateBufferDependent(true);
updateMenubar();
updateToolbars();
updateLayoutChoice();
updateWindowTitle();
updateStatusBar();
lyxerr << "currentTabChanged " << i
<< "File" << wa->bufferView().buffer()->fileName() << endl;
}
@ -741,11 +738,18 @@ bool GuiView::event(QEvent * e)
case QEvent::ShortcutOverride: {
QKeyEvent * ke = static_cast<QKeyEvent*>(e);
if (d.tab_widget_->count() == 0) {
theLyXFunc().setLyXView(this);
boost::shared_ptr<QKeySymbol> sym(new QKeySymbol);
sym->set(ke);
theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
e->accept();
return true;
}
if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
boost::shared_ptr<QKeySymbol> sym(new QKeySymbol);
sym->set(ke);
BOOST_ASSERT(work_area_);
work_area_->processKeySym(sym, key_modifier::none);
currentWorkArea()->processKeySym(sym, key_modifier::none);
e->accept();
return true;
}
@ -773,17 +777,20 @@ void GuiView::show()
void GuiView::busy(bool yes)
{
BOOST_ASSERT(work_area_);
static_cast<GuiWorkArea *>(work_area_)->setUpdatesEnabled(!yes);
if (d.tab_widget_->count()) {
GuiWorkArea * wa = dynamic_cast<GuiWorkArea *>(d.tab_widget_->currentWidget());
BOOST_ASSERT(wa);
wa->setUpdatesEnabled(!yes);
if (yes)
wa->stopBlinkingCursor();
else
wa->startBlinkingCursor();
}
if (yes) {
work_area_->stopBlinkingCursor();
if (yes)
QApplication::setOverrideCursor(Qt::WaitCursor);
}
else {
work_area_->startBlinkingCursor();
else
QApplication::restoreOverrideCursor();
}
}
@ -833,6 +840,96 @@ Toolbars::ToolbarPtr GuiView::makeToolbar(ToolbarInfo const & tbinfo, bool newli
return Toolbars::ToolbarPtr(Tb);
}
WorkArea * 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)
return wa;
}
return 0;
}
WorkArea * GuiView::addWorkArea(Buffer & buffer)
{
GuiWorkArea * wa = new GuiWorkArea(buffer, *this);
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_);
return wa;
}
WorkArea * GuiView::currentWorkArea()
{
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());
}
WorkArea 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());
}
void GuiView::setCurrentWorkArea(WorkArea * 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 = dynamic_cast<GuiWorkArea *>(work_area);
BOOST_ASSERT(wa);
d.tab_widget_->setCurrentWidget(wa);
wa->setFocus();
}
void GuiView::removeWorkArea(WorkArea * work_area)
{
BOOST_ASSERT(work_area);
if (work_area == currentWorkArea()) {
disconnectBuffer();
disconnectBufferView();
}
// removing a work area often results from closing a file so
// update the toc in any case.
updateToc();
GuiWorkArea * gwa = dynamic_cast<GuiWorkArea *>(work_area);
BOOST_ASSERT(gwa);
int index = d.tab_widget_->indexOf(gwa);
d.tab_widget_->removeTab(index);
delete gwa;
if (d.tab_widget_->count()) {
// make sure the next work area is enabled.
d.tab_widget_->currentWidget()->setUpdatesEnabled(true);
return;
}
getDialogs().hideBufferDependent();
if (d.stack_widget_) {
// No more work area, switch to the background widget.
d.setBackground();
}
}
} // namespace frontend
} // namespace lyx

View File

@ -75,8 +75,6 @@ public:
virtual void clearMessage();
virtual bool hasFocus() const;
virtual void updateTab();
/// show - display the top-level window
void show();
@ -86,8 +84,6 @@ public:
/// menu item has been selected
void activated(FuncRequest const &);
void initTab(QWidget* workArea);
QMenu* createPopupMenu();
Q_SIGNALS:
@ -118,6 +114,19 @@ protected:
///
virtual void moveEvent(QMoveEvent * e);
/// \return the \c Workarea associated to \p Buffer
/// \retval 0 if no \c WorkArea is found.
WorkArea * workArea(Buffer & buffer);
/// Add a \c WorkArea
/// \return the \c Workarea associated to \p Buffer
/// \retval 0 if no \c WorkArea is found.
WorkArea * addWorkArea(Buffer & buffer);
void setCurrentWorkArea(WorkArea * work_area);
void removeWorkArea(WorkArea * work_area);
WorkArea const * currentWorkArea() const;
WorkArea * currentWorkArea();
private:
///
void dragEnterEvent(QDragEnterEvent * ev);

View File

@ -173,8 +173,8 @@ SyntheticMouseEvent::SyntheticMouseEvent()
{}
GuiWorkArea::GuiWorkArea(int w, int h, int id, LyXView & lyx_view)
: WorkArea(id, lyx_view), need_resize_(false), schedule_redraw_(false),
GuiWorkArea::GuiWorkArea(Buffer & buf, LyXView & lv)
: WorkArea(buf, lv), need_resize_(false), schedule_redraw_(false),
preedit_lines_(1)
{
screen_ = QPixmap(viewport()->width(), viewport()->height());
@ -196,8 +196,6 @@ GuiWorkArea::GuiWorkArea(int w, int h, int id, LyXView & lyx_view)
viewport()->setCursor(Qt::IBeamCursor);
resize(w, h);
synthetic_mouse_event_.timeout.timeout.connect(
boost::bind(&GuiWorkArea::generateSyntheticMouseEvent,
this));
@ -258,17 +256,13 @@ void GuiWorkArea::focusInEvent(QFocusEvent * /*event*/)
// if (theApp() == 0 || &lyx_view_ == theApp()->currentView())
// return;
theApp()->setCurrentView(lyx_view_);
theApp()->setCurrentView(*lyx_view_);
// Repaint the whole screen.
// Note: this is different from redraw() as only the backing pixmap
// will be redrawn, which is cheap.
viewport()->repaint();
// FIXME: it would be better to send a signal "newBuffer()"
// in BufferList that could be connected to the different tabbars.
lyx_view_.updateTab();
startBlinkingCursor();
}
@ -459,49 +453,6 @@ void GuiWorkArea::update(int x, int y, int w, int h)
}
void GuiWorkArea::doGreyOut(QLPainter & pain)
{
pain.fillRectangle(0, 0, width(), height(),
Color::bottomarea);
//if (!lyxrc.show_banner)
// return;
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;
QPixmap pm(toqstr(file.absFilename()));
if (!pm) {
lyxerr << "could not load splash screen: '" << file << "'" << endl;
return;
}
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]));
int const w = pm.width();
int const h = pm.height();
int x = (width() - w) / 2;
int y = (height() - h) / 2;
pain.drawPixmap(x, y, pm);
x += 260;
y += 270;
pain.setPen(QColor(255, 255, 0));
pain.setFont(font);
pain.drawText(x, y, text);
}
void GuiWorkArea::paintEvent(QPaintEvent * ev)
{
QRect const rc = ev->rect();
@ -538,14 +489,6 @@ void GuiWorkArea::expose(int x, int y, int w, int h)
void GuiWorkArea::updateScreen()
{
QLPainter pain(&screen_);
if (greyed_out_) {
LYXERR(Debug::GUI) << "splash screen requested" << endl;
verticalScrollBar()->hide();
doGreyOut(pain);
return;
}
verticalScrollBar()->show();
paintText(*buffer_view_, pain);
}
@ -554,11 +497,9 @@ void GuiWorkArea::updateScreen()
void GuiWorkArea::showCursor(int x, int y, int h, CursorShape shape)
{
if (schedule_redraw_) {
if (buffer_view_ && buffer_view_->buffer()) {
buffer_view_->update(Update::Force);
updateScreen();
viewport()->update(QRect(0, 0, viewport()->width(), viewport()->height()));
}
schedule_redraw_ = false;
// Show the cursor immediately after the update.
hideCursor();
@ -586,11 +527,6 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
docstring const & preedit_string
= qstring_to_ucs4(e->preeditString());
if(greyed_out_) {
e->ignore();
return;
}
if (!commit_string.isEmpty()) {
LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION

View File

@ -88,10 +88,11 @@ class GuiWorkArea : public QAbstractScrollArea, public WorkArea
public:
///
GuiWorkArea(int width, int height, int id, LyXView & lyx_view);
GuiWorkArea(Buffer & buffer, LyXView & lv);
///
bool hasFocus() const { return QAbstractScrollArea::hasFocus(); }
bool isVisible() const { return QAbstractScrollArea::isVisible(); }
/// return the width of the content pane
virtual int width() const { return viewport()->width(); }

View File

@ -386,37 +386,34 @@ Buffer * getChildBuffer(Buffer const & buffer, InsetCommandParams const & params
} // namespace anon
Buffer * loadIfNeeded(Buffer const & buffer, InsetCommandParams const & params)
/// return true if the file is or got loaded.
Buffer * loadIfNeeded(Buffer const & parent, InsetCommandParams const & params)
{
if (isVerbatim(params) || isListings(params))
return 0;
FileName const included_file = includedFilename(buffer, params);
string const parent_filename = parent.fileName();
FileName const included_file = makeAbsPath(to_utf8(params["filename"]),
onlyPath(parent_filename));
if (!isLyXFilename(included_file.absFilename()))
return 0;
Buffer * buf = theBufferList().getBuffer(included_file.absFilename());
if (!buf) {
Buffer * child = theBufferList().getBuffer(included_file.absFilename());
if (!child) {
// the readonly flag can/will be wrong, not anymore I think.
if (!fs::exists(included_file.toFilesystemEncoding()))
return false;
if (use_gui) {
lyx::dispatch(FuncRequest(LFUN_BUFFER_CHILD_OPEN,
included_file.absFilename() + "|true"));
buf = theBufferList().getBuffer(included_file.absFilename());
}
else {
buf = theBufferList().newBuffer(included_file.absFilename());
if (!loadLyXFile(buf, included_file)) {
return 0;
child = theBufferList().newBuffer(included_file.absFilename());
if (!loadLyXFile(child, included_file)) {
//close the buffer we just opened
theBufferList().close(buf, false);
return false;
theBufferList().close(child, false);
return 0;
}
}
}
buf->setParentName(parentFilename(buffer));
return buf;
child->setParentName(parent_filename);
return child;
}
@ -768,7 +765,7 @@ InsetInclude::getBibfilesCache(Buffer const & buffer) const
bool InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
{
BOOST_ASSERT(mi.base.bv && mi.base.bv->buffer());
BOOST_ASSERT(mi.base.bv);
bool use_preview = false;
if (RenderPreview::status() != LyXRC::PREVIEW_OFF) {
@ -801,7 +798,7 @@ void InsetInclude::draw(PainterInfo & pi, int x, int y) const
{
setPosCache(pi, x, y);
BOOST_ASSERT(pi.base.bv && pi.base.bv->buffer());
BOOST_ASSERT(pi.base.bv);
bool use_preview = false;
if (RenderPreview::status() != LyXRC::PREVIEW_OFF) {