2003-06-20 12:46:28 +00:00
|
|
|
|
/**
|
2007-04-26 04:41:58 +00:00
|
|
|
|
* \file buffer_funcs.cpp
|
2003-06-20 12:46:28 +00:00
|
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
|
* Licence details can be found in the file COPYING.
|
|
|
|
|
*
|
2003-08-23 00:17:00 +00:00
|
|
|
|
* \author Lars Gullik Bj<EFBFBD>nnes
|
2003-06-20 12:46:28 +00:00
|
|
|
|
* \author Alfredo Braunstein
|
|
|
|
|
*
|
2003-08-23 00:17:00 +00:00
|
|
|
|
* Full author contact details are available in file CREDITS.
|
2003-06-20 12:46:28 +00:00
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include "buffer_funcs.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "Buffer.h"
|
|
|
|
|
#include "BufferList.h"
|
|
|
|
|
#include "BufferParams.h"
|
2007-08-12 21:43:58 +00:00
|
|
|
|
#include "debug.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "DocIterator.h"
|
|
|
|
|
#include "Counters.h"
|
|
|
|
|
#include "ErrorList.h"
|
2005-02-25 11:55:36 +00:00
|
|
|
|
#include "Floating.h"
|
|
|
|
|
#include "FloatList.h"
|
2003-06-20 12:46:28 +00:00
|
|
|
|
#include "gettext.h"
|
2007-08-13 18:11:43 +00:00
|
|
|
|
#include "InsetIterator.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "Language.h"
|
2003-09-06 18:38:02 +00:00
|
|
|
|
#include "LaTeX.h"
|
2007-08-13 18:11:43 +00:00
|
|
|
|
#include "LyX.h"
|
2007-09-20 20:44:08 +00:00
|
|
|
|
#include "lyxlayout_ptr_fwd.h"
|
2007-04-29 19:53:54 +00:00
|
|
|
|
#include "TextClass.h"
|
This is one of a series of patches that will merge the layout modules development in personal/branches/rgheck back into the tree.
Design goal: Allow the use of layout "modules", which are to LaTeX packages as layout files are to LaTeX document classes. Thus, one could have a module that defined certain character styles, environments, commands, or what have you, and include it in various documents, each of which uses a different document class, without having to modify the layout files themselves. For example, a theorems.module could be used with article.layout to provide support for theorem-type environments, without having to modify article.layout itself, and the same module could be used with book.layout, etc.
This first patch does some reworking of the infrastructrue. We need to distinguish between the TextClass that a particular document is using and the layout of that document, since modules, in particular, can modify the layout. The solution adopted here is to add a TextClass pointer to BufferParams, which will hold the layout. The layout itself is then constructed from the TextClass the document is using. At present, this is completely trivial, but that will change when modules are added.
The pointer in question is a boost::shared_ptr. This is needed because CutAndPaste saves a copy of the layout with each cut or copied selection. We cannot assume the selection vanishes when the document is closed, so there are two options: (i) keep a list of all the layouts that have ever been used by any document; (ii) used some kind of smart pointer. The latter seems preferable, as the former would waste memory. More importantly, the use of a smart pointer allows modules to be modified on disk and then reloaded while LyX is running, and it will eventually allow the same for layout files.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@19756 a592a061-630c-0410-9148-cb99ea01b6c8
2007-08-23 16:41:13 +00:00
|
|
|
|
#include "TextClassList.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "Paragraph.h"
|
2005-04-19 09:04:25 +00:00
|
|
|
|
#include "paragraph_funcs.h"
|
2006-03-23 20:11:06 +00:00
|
|
|
|
#include "ParagraphList.h"
|
2005-02-25 11:55:36 +00:00
|
|
|
|
#include "ParagraphParameters.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "ParIterator.h"
|
|
|
|
|
#include "LyXVC.h"
|
|
|
|
|
#include "TexRow.h"
|
2007-08-15 13:47:32 +00:00
|
|
|
|
#include "Text.h"
|
2006-11-11 00:35:14 +00:00
|
|
|
|
#include "TocBackend.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "VCBackend.h"
|
2003-06-20 12:46:28 +00:00
|
|
|
|
|
2007-04-28 20:44:46 +00:00
|
|
|
|
#include "frontends/alert.h"
|
2003-06-20 12:46:28 +00:00
|
|
|
|
|
2007-04-25 01:24:38 +00:00
|
|
|
|
#include "insets/InsetBibitem.h"
|
|
|
|
|
#include "insets/InsetInclude.h"
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
2003-09-06 18:38:02 +00:00
|
|
|
|
#include "support/filetools.h"
|
2005-01-31 10:42:26 +00:00
|
|
|
|
#include "support/fs_extras.h"
|
2003-06-20 12:46:28 +00:00
|
|
|
|
#include "support/lyxlib.h"
|
|
|
|
|
|
2004-11-06 15:23:12 +00:00
|
|
|
|
#include <boost/bind.hpp>
|
2005-01-31 10:42:26 +00:00
|
|
|
|
#include <boost/filesystem/operations.hpp>
|
2004-11-06 15:23:12 +00:00
|
|
|
|
|
2007-07-17 17:40:44 +00:00
|
|
|
|
using std::min;
|
|
|
|
|
using std::string;
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
|
|
|
|
|
|
namespace lyx {
|
|
|
|
|
|
2006-04-16 14:19:25 +00:00
|
|
|
|
using namespace std;
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
|
using support::bformat;
|
2006-11-26 21:30:39 +00:00
|
|
|
|
using support::FileName;
|
2006-10-21 00:16:43 +00:00
|
|
|
|
using support::libFileSearch;
|
2006-12-17 12:12:17 +00:00
|
|
|
|
using support::makeAbsPath;
|
2006-10-21 00:16:43 +00:00
|
|
|
|
using support::makeDisplayPath;
|
|
|
|
|
using support::onlyFilename;
|
|
|
|
|
using support::onlyPath;
|
|
|
|
|
using support::unlink;
|
2003-06-20 12:46:28 +00:00
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
|
namespace Alert = frontend::Alert;
|
2005-01-31 10:42:26 +00:00
|
|
|
|
namespace fs = boost::filesystem;
|
2003-06-30 23:56:22 +00:00
|
|
|
|
|
2003-06-20 12:46:28 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
2006-12-02 16:07:15 +00:00
|
|
|
|
bool readFile(Buffer * const b, FileName const & s)
|
2003-06-20 12:46:28 +00:00
|
|
|
|
{
|
2005-01-05 20:21:27 +00:00
|
|
|
|
BOOST_ASSERT(b);
|
|
|
|
|
|
2003-06-20 12:46:28 +00:00
|
|
|
|
// File information about normal file
|
2006-12-02 16:07:15 +00:00
|
|
|
|
if (!fs::exists(s.toFilesystemEncoding())) {
|
|
|
|
|
docstring const file = makeDisplayPath(s.absFilename(), 50);
|
2006-09-11 08:54:10 +00:00
|
|
|
|
docstring text = bformat(_("The specified document\n%1$s"
|
|
|
|
|
"\ncould not be read."), file);
|
|
|
|
|
Alert::error(_("Could not read document"), text);
|
2003-06-20 12:46:28 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if emergency save file exists and is newer.
|
2006-12-02 16:07:15 +00:00
|
|
|
|
FileName const e(s.absFilename() + ".emergency");
|
2004-03-26 17:49:08 +00:00
|
|
|
|
|
2006-12-02 16:07:15 +00:00
|
|
|
|
if (fs::exists(e.toFilesystemEncoding()) &&
|
|
|
|
|
fs::exists(s.toFilesystemEncoding()) &&
|
|
|
|
|
fs::last_write_time(e.toFilesystemEncoding()) > fs::last_write_time(s.toFilesystemEncoding()))
|
2004-03-26 17:49:08 +00:00
|
|
|
|
{
|
2006-12-02 16:07:15 +00:00
|
|
|
|
docstring const file = makeDisplayPath(s.absFilename(), 20);
|
2006-09-11 08:54:10 +00:00
|
|
|
|
docstring const text =
|
|
|
|
|
bformat(_("An emergency save of the document "
|
2005-01-05 20:21:27 +00:00
|
|
|
|
"%1$s exists.\n\n"
|
2006-09-11 08:54:10 +00:00
|
|
|
|
"Recover emergency save?"), file);
|
|
|
|
|
switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
|
|
|
|
|
_("&Recover"), _("&Load Original"),
|
|
|
|
|
_("&Cancel")))
|
2004-03-26 17:49:08 +00:00
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
// the file is not saved if we load the emergency file.
|
|
|
|
|
b->markDirty();
|
2006-12-17 12:12:17 +00:00
|
|
|
|
return b->readFile(e);
|
2004-03-26 17:49:08 +00:00
|
|
|
|
case 1:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
2003-06-20 12:46:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-26 17:49:08 +00:00
|
|
|
|
// Now check if autosave file is newer.
|
2006-12-02 16:07:15 +00:00
|
|
|
|
FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
|
2004-03-26 17:49:08 +00:00
|
|
|
|
|
2006-12-02 16:07:15 +00:00
|
|
|
|
if (fs::exists(a.toFilesystemEncoding()) &&
|
|
|
|
|
fs::exists(s.toFilesystemEncoding()) &&
|
|
|
|
|
fs::last_write_time(a.toFilesystemEncoding()) > fs::last_write_time(s.toFilesystemEncoding()))
|
2004-03-26 17:49:08 +00:00
|
|
|
|
{
|
2006-12-02 16:07:15 +00:00
|
|
|
|
docstring const file = makeDisplayPath(s.absFilename(), 20);
|
2006-09-11 08:54:10 +00:00
|
|
|
|
docstring const text =
|
|
|
|
|
bformat(_("The backup of the document "
|
2005-01-05 20:21:27 +00:00
|
|
|
|
"%1$s is newer.\n\nLoad the "
|
2006-09-11 08:54:10 +00:00
|
|
|
|
"backup instead?"), file);
|
|
|
|
|
switch (Alert::prompt(_("Load backup?"), text, 0, 2,
|
|
|
|
|
_("&Load backup"), _("Load &original"),
|
|
|
|
|
_("&Cancel") ))
|
2004-03-26 17:49:08 +00:00
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
// the file is not saved if we load the autosave file.
|
|
|
|
|
b->markDirty();
|
2006-12-17 12:12:17 +00:00
|
|
|
|
return b->readFile(a);
|
2004-03-26 17:49:08 +00:00
|
|
|
|
case 1:
|
|
|
|
|
// Here we delete the autosave
|
2006-12-02 16:07:15 +00:00
|
|
|
|
unlink(a);
|
2004-03-26 17:49:08 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
2003-06-20 12:46:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2006-12-17 12:12:17 +00:00
|
|
|
|
return b->readFile(s);
|
2003-06-20 12:46:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace anon
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-12-02 16:07:15 +00:00
|
|
|
|
bool loadLyXFile(Buffer * b, FileName const & s)
|
2003-06-20 12:46:28 +00:00
|
|
|
|
{
|
2005-01-05 20:21:27 +00:00
|
|
|
|
BOOST_ASSERT(b);
|
|
|
|
|
|
2006-12-02 16:07:15 +00:00
|
|
|
|
if (fs::is_readable(s.toFilesystemEncoding())) {
|
2003-06-20 12:46:28 +00:00
|
|
|
|
if (readFile(b, s)) {
|
2003-09-09 09:47:59 +00:00
|
|
|
|
b->lyxvc().file_found_hook(s);
|
2006-12-02 16:07:15 +00:00
|
|
|
|
if (!fs::is_writable(s.toFilesystemEncoding()))
|
2005-01-31 10:42:26 +00:00
|
|
|
|
b->setReadonly(true);
|
2003-06-20 12:46:28 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2005-01-31 10:42:26 +00:00
|
|
|
|
} else {
|
2006-12-02 16:07:15 +00:00
|
|
|
|
docstring const file = makeDisplayPath(s.absFilename(), 20);
|
2003-06-20 12:46:28 +00:00
|
|
|
|
// Here we probably should run
|
|
|
|
|
if (LyXVC::file_not_found_hook(s)) {
|
2006-09-11 08:54:10 +00:00
|
|
|
|
docstring const text =
|
|
|
|
|
bformat(_("Do you want to retrieve the document"
|
|
|
|
|
" %1$s from version control?"), file);
|
|
|
|
|
int const ret = Alert::prompt(_("Retrieve from version control?"),
|
|
|
|
|
text, 0, 1, _("&Retrieve"), _("&Cancel"));
|
2003-06-20 12:46:28 +00:00
|
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
|
// How can we know _how_ to do the checkout?
|
|
|
|
|
// With the current VC support it has to be,
|
|
|
|
|
// a RCS file since CVS do not have special ,v files.
|
|
|
|
|
RCS::retrieve(s);
|
|
|
|
|
return loadLyXFile(b, s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-19 16:03:47 +00:00
|
|
|
|
|
2007-08-06 04:14:26 +00:00
|
|
|
|
bool checkIfLoaded(FileName const & fn)
|
|
|
|
|
{
|
|
|
|
|
return theBufferList().getBuffer(fn.absFilename());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-06-19 16:03:47 +00:00
|
|
|
|
Buffer * checkAndLoadLyXFile(FileName const & filename)
|
|
|
|
|
{
|
|
|
|
|
// File already open?
|
2007-08-06 04:14:26 +00:00
|
|
|
|
Buffer * checkBuffer = theBufferList().getBuffer(filename.absFilename());
|
|
|
|
|
if (checkBuffer) {
|
|
|
|
|
if (checkBuffer->isClean())
|
|
|
|
|
return checkBuffer;
|
2007-06-19 16:03:47 +00:00
|
|
|
|
docstring const file = makeDisplayPath(filename.absFilename(), 20);
|
2007-08-06 04:14:26 +00:00
|
|
|
|
docstring text = bformat(_(
|
|
|
|
|
"The document %1$s is already loaded and has unsaved changes.\n"
|
|
|
|
|
"Do you want to abandon your changes and reload the version on disk?"), file);
|
|
|
|
|
if (Alert::prompt(_("Reload saved document?"),
|
|
|
|
|
text, 0, 1, _("&Reload"), _("&Keep Changes")))
|
|
|
|
|
return checkBuffer;
|
2007-06-19 16:03:47 +00:00
|
|
|
|
|
|
|
|
|
// FIXME: should be LFUN_REVERT
|
2007-08-06 04:14:26 +00:00
|
|
|
|
if (theBufferList().close(checkBuffer, false))
|
2007-06-19 16:03:47 +00:00
|
|
|
|
// Load it again.
|
|
|
|
|
return checkAndLoadLyXFile(filename);
|
|
|
|
|
else
|
|
|
|
|
// The file could not be closed.
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isFileReadable(filename)) {
|
|
|
|
|
Buffer * b = theBufferList().newBuffer(filename.absFilename());
|
|
|
|
|
if (!lyx::loadLyXFile(b, filename)) {
|
|
|
|
|
theBufferList().release(b);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
docstring text = bformat(_("The document %1$s does not yet "
|
|
|
|
|
"exist.\n\nDo you want to create a new document?"),
|
|
|
|
|
from_utf8(filename.absFilename()));
|
2007-06-20 21:07:32 +00:00
|
|
|
|
if (!Alert::prompt(_("Create new document?"),
|
2007-06-19 16:03:47 +00:00
|
|
|
|
text, 0, 1, _("&Create"), _("Cancel")))
|
|
|
|
|
return newFile(filename.absFilename(), string(), true);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-27 10:24:13 +00:00
|
|
|
|
// FIXME newFile() should probably be a member method of Application...
|
2003-06-30 23:56:22 +00:00
|
|
|
|
Buffer * newFile(string const & filename, string const & templatename,
|
2005-01-05 20:21:27 +00:00
|
|
|
|
bool const isNamed)
|
2003-06-20 12:46:28 +00:00
|
|
|
|
{
|
|
|
|
|
// get a free buffer
|
2006-10-11 17:24:46 +00:00
|
|
|
|
Buffer * b = theBufferList().newBuffer(filename);
|
2005-01-05 20:21:27 +00:00
|
|
|
|
BOOST_ASSERT(b);
|
2003-06-20 12:46:28 +00:00
|
|
|
|
|
2006-12-17 12:12:17 +00:00
|
|
|
|
FileName tname;
|
2003-06-20 12:46:28 +00:00
|
|
|
|
// use defaults.lyx as a default template if it exists.
|
|
|
|
|
if (templatename.empty())
|
2006-12-17 12:12:17 +00:00
|
|
|
|
tname = libFileSearch("templates", "defaults.lyx");
|
2003-06-20 12:46:28 +00:00
|
|
|
|
else
|
2006-12-27 10:56:11 +00:00
|
|
|
|
tname = makeAbsPath(templatename);
|
2003-06-20 12:46:28 +00:00
|
|
|
|
|
|
|
|
|
if (!tname.empty()) {
|
2003-07-28 14:40:29 +00:00
|
|
|
|
if (!b->readFile(tname)) {
|
2006-12-17 12:12:17 +00:00
|
|
|
|
docstring const file = makeDisplayPath(tname.absFilename(), 50);
|
2006-09-11 08:54:10 +00:00
|
|
|
|
docstring const text = bformat(
|
|
|
|
|
_("The specified document template\n%1$s\ncould not be read."),
|
|
|
|
|
file);
|
|
|
|
|
Alert::error(_("Could not read template"), text);
|
2006-10-11 17:24:46 +00:00
|
|
|
|
theBufferList().release(b);
|
2006-07-17 15:13:49 +00:00
|
|
|
|
return 0;
|
2003-06-20 12:46:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isNamed) {
|
|
|
|
|
b->setUnnamed();
|
|
|
|
|
b->setFileName(filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b->setReadonly(false);
|
2003-10-22 14:40:24 +00:00
|
|
|
|
b->fully_loaded(true);
|
2003-06-20 12:46:28 +00:00
|
|
|
|
|
|
|
|
|
return b;
|
|
|
|
|
}
|
2003-06-24 20:42:15 +00:00
|
|
|
|
|
|
|
|
|
|
2006-08-13 16:16:43 +00:00
|
|
|
|
void bufferErrors(Buffer const & buf, TeXErrors const & terr,
|
|
|
|
|
ErrorList & errorList)
|
2003-06-24 20:42:15 +00:00
|
|
|
|
{
|
|
|
|
|
TeXErrors::Errors::const_iterator cit = terr.begin();
|
|
|
|
|
TeXErrors::Errors::const_iterator end = terr.end();
|
|
|
|
|
|
|
|
|
|
for (; cit != end; ++cit) {
|
2005-06-03 08:48:04 +00:00
|
|
|
|
int id_start = -1;
|
|
|
|
|
int pos_start = -1;
|
|
|
|
|
int errorrow = cit->error_in_line;
|
2005-11-29 13:39:03 +00:00
|
|
|
|
bool found = buf.texrow().getIdFromRow(errorrow, id_start,
|
2006-04-05 23:56:29 +00:00
|
|
|
|
pos_start);
|
2005-06-03 08:48:04 +00:00
|
|
|
|
int id_end = -1;
|
|
|
|
|
int pos_end = -1;
|
|
|
|
|
do {
|
|
|
|
|
++errorrow;
|
2005-11-29 13:39:03 +00:00
|
|
|
|
found = buf.texrow().getIdFromRow(errorrow, id_end,
|
2006-04-05 23:56:29 +00:00
|
|
|
|
pos_end);
|
2005-11-29 13:39:03 +00:00
|
|
|
|
} while (found && id_start == id_end && pos_start == pos_end);
|
2005-06-03 08:48:04 +00:00
|
|
|
|
|
2006-11-12 10:58:00 +00:00
|
|
|
|
errorList.push_back(ErrorItem(cit->error_desc,
|
|
|
|
|
cit->error_text, id_start, pos_start, pos_end));
|
2003-06-24 20:42:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-04-09 00:26:19 +00:00
|
|
|
|
string const bufferFormat(Buffer const & buffer)
|
2003-06-24 20:42:15 +00:00
|
|
|
|
{
|
2006-08-23 10:57:45 +00:00
|
|
|
|
if (buffer.isDocBook())
|
2003-06-24 20:42:15 +00:00
|
|
|
|
return "docbook";
|
|
|
|
|
else if (buffer.isLiterate())
|
|
|
|
|
return "literate";
|
|
|
|
|
else
|
|
|
|
|
return "latex";
|
|
|
|
|
}
|
2004-12-27 16:30:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int countWords(DocIterator const & from, DocIterator const & to)
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
bool inword = false;
|
|
|
|
|
for (DocIterator dit = from ; dit != to ; dit.forwardPos()) {
|
|
|
|
|
// Copied and adapted from isLetter() in ControlSpellChecker
|
|
|
|
|
if (dit.inTexted()
|
|
|
|
|
&& dit.pos() != dit.lastpos()
|
|
|
|
|
&& dit.paragraph().isLetter(dit.pos())
|
2006-10-19 12:49:11 +00:00
|
|
|
|
&& !dit.paragraph().isDeleted(dit.pos())) {
|
2004-12-27 16:30:27 +00:00
|
|
|
|
if (!inword) {
|
|
|
|
|
++count;
|
|
|
|
|
inword = true;
|
|
|
|
|
}
|
|
|
|
|
} else if (inword)
|
|
|
|
|
inword = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
|
depth_type getDepth(DocIterator const & it)
|
2005-02-25 11:55:36 +00:00
|
|
|
|
{
|
2006-10-21 00:16:43 +00:00
|
|
|
|
depth_type depth = 0;
|
2006-04-05 23:56:29 +00:00
|
|
|
|
for (size_t i = 0 ; i < it.depth() ; ++i)
|
2005-08-03 20:21:11 +00:00
|
|
|
|
if (!it[i].inset().inMathed())
|
|
|
|
|
depth += it[i].paragraph().getDepth() + 1;
|
|
|
|
|
// remove 1 since the outer inset does not count
|
|
|
|
|
return depth - 1;
|
|
|
|
|
}
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
|
depth_type getItemDepth(ParIterator const & it)
|
2006-04-05 23:56:29 +00:00
|
|
|
|
{
|
2005-08-03 20:21:11 +00:00
|
|
|
|
Paragraph const & par = *it;
|
|
|
|
|
LYX_LABEL_TYPES const labeltype = par.layout()->labeltype;
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
2005-08-03 20:21:11 +00:00
|
|
|
|
if (labeltype != LABEL_ENUMERATE && labeltype != LABEL_ITEMIZE)
|
2006-04-05 23:56:29 +00:00
|
|
|
|
return 0;
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
2005-08-03 20:21:11 +00:00
|
|
|
|
// this will hold the lowest depth encountered up to now.
|
2006-10-21 00:16:43 +00:00
|
|
|
|
depth_type min_depth = getDepth(it);
|
2005-08-03 20:21:11 +00:00
|
|
|
|
ParIterator prev_it = it;
|
2005-02-25 11:55:36 +00:00
|
|
|
|
while (true) {
|
2005-08-03 20:21:11 +00:00
|
|
|
|
if (prev_it.pit())
|
|
|
|
|
--prev_it.top().pit();
|
|
|
|
|
else {
|
|
|
|
|
// start of nested inset: go to outer par
|
|
|
|
|
prev_it.pop_back();
|
|
|
|
|
if (prev_it.empty()) {
|
|
|
|
|
// start of document: nothing to do
|
|
|
|
|
return 0;
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
2005-08-03 20:21:11 +00:00
|
|
|
|
}
|
2006-04-05 23:56:29 +00:00
|
|
|
|
|
2005-08-03 20:21:11 +00:00
|
|
|
|
// We search for the first paragraph with same label
|
|
|
|
|
// that is not more deeply nested.
|
|
|
|
|
Paragraph & prev_par = *prev_it;
|
2006-10-21 00:16:43 +00:00
|
|
|
|
depth_type const prev_depth = getDepth(prev_it);
|
2006-04-05 23:56:29 +00:00
|
|
|
|
if (labeltype == prev_par.layout()->labeltype) {
|
2005-08-03 20:21:11 +00:00
|
|
|
|
if (prev_depth < min_depth) {
|
|
|
|
|
return prev_par.itemdepth + 1;
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
2005-08-03 20:21:11 +00:00
|
|
|
|
else if (prev_depth == min_depth) {
|
|
|
|
|
return prev_par.itemdepth;
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2005-08-03 20:21:11 +00:00
|
|
|
|
min_depth = std::min(min_depth, prev_depth);
|
|
|
|
|
// small optimization: if we are at depth 0, we won't
|
|
|
|
|
// find anything else
|
|
|
|
|
if (prev_depth == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-08-03 20:21:11 +00:00
|
|
|
|
bool needEnumCounterReset(ParIterator const & it)
|
2005-02-25 11:55:36 +00:00
|
|
|
|
{
|
2005-08-03 20:21:11 +00:00
|
|
|
|
Paragraph const & par = *it;
|
|
|
|
|
BOOST_ASSERT(par.layout()->labeltype == LABEL_ENUMERATE);
|
2006-10-21 00:16:43 +00:00
|
|
|
|
depth_type const cur_depth = par.getDepth();
|
2005-08-03 20:21:11 +00:00
|
|
|
|
ParIterator prev_it = it;
|
|
|
|
|
while (prev_it.pit()) {
|
|
|
|
|
--prev_it.top().pit();
|
|
|
|
|
Paragraph const & prev_par = *prev_it;
|
|
|
|
|
if (prev_par.getDepth() <= cur_depth)
|
|
|
|
|
return prev_par.layout()->labeltype != LABEL_ENUMERATE;
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
2005-08-03 20:21:11 +00:00
|
|
|
|
// start of nested inset: reset
|
|
|
|
|
return true;
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-04-16 14:19:25 +00:00
|
|
|
|
// set the label of a paragraph. This includes the counters.
|
2007-08-12 21:43:58 +00:00
|
|
|
|
void setLabel(Buffer const & buf, ParIterator & it)
|
2005-02-25 11:55:36 +00:00
|
|
|
|
{
|
2007-08-12 21:43:58 +00:00
|
|
|
|
TextClass const & textclass = buf.params().getTextClass();
|
|
|
|
|
Paragraph & par = it.paragraph();
|
2007-08-23 19:59:07 +00:00
|
|
|
|
LayoutPtr const & layout = par.layout();
|
2005-02-25 11:55:36 +00:00
|
|
|
|
Counters & counters = textclass.counters();
|
|
|
|
|
|
2007-07-11 12:52:50 +00:00
|
|
|
|
if (par.params().startOfAppendix()) {
|
|
|
|
|
// FIXME: only the counter corresponding to toplevel
|
|
|
|
|
// sectionning should be reset
|
|
|
|
|
counters.reset();
|
|
|
|
|
counters.appendix(true);
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
2007-07-11 12:52:50 +00:00
|
|
|
|
par.params().appendix(counters.appendix());
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
2005-08-03 20:21:11 +00:00
|
|
|
|
// Compute the item depth of the paragraph
|
|
|
|
|
par.itemdepth = getItemDepth(it);
|
|
|
|
|
|
2005-02-25 11:55:36 +00:00
|
|
|
|
if (layout->margintype == MARGIN_MANUAL) {
|
|
|
|
|
if (par.params().labelWidthString().empty())
|
2007-01-15 16:58:14 +00:00
|
|
|
|
par.params().labelWidthString(par.translateIfPossible(layout->labelstring(), buf.params()));
|
2005-02-25 11:55:36 +00:00
|
|
|
|
} else {
|
2007-01-15 16:58:14 +00:00
|
|
|
|
par.params().labelWidthString(docstring());
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-08-17 09:15:04 +00:00
|
|
|
|
switch(layout->labeltype) {
|
|
|
|
|
case LABEL_COUNTER:
|
2005-04-19 09:04:25 +00:00
|
|
|
|
if (layout->toclevel <= buf.params().secnumdepth
|
2005-05-12 10:16:04 +00:00
|
|
|
|
&& (layout->latextype != LATEX_ENVIRONMENT
|
2005-04-19 09:04:25 +00:00
|
|
|
|
|| isFirstInSequence(it.pit(), it.plist()))) {
|
2006-10-20 22:17:16 +00:00
|
|
|
|
counters.step(layout->counter);
|
2007-01-15 16:58:14 +00:00
|
|
|
|
par.params().labelString(
|
|
|
|
|
par.expandLabel(layout, buf.params()));
|
2007-01-11 16:38:08 +00:00
|
|
|
|
} else
|
2007-01-15 16:58:14 +00:00
|
|
|
|
par.params().labelString(docstring());
|
2007-08-17 09:15:04 +00:00
|
|
|
|
break;
|
2007-01-11 16:38:08 +00:00
|
|
|
|
|
2007-08-17 09:15:04 +00:00
|
|
|
|
case LABEL_ITEMIZE: {
|
2005-02-25 11:55:36 +00:00
|
|
|
|
// At some point of time we should do something more
|
|
|
|
|
// clever here, like:
|
|
|
|
|
// par.params().labelString(
|
2006-11-13 16:53:49 +00:00
|
|
|
|
// buf.params().user_defined_bullet(par.itemdepth).getText());
|
2005-02-25 11:55:36 +00:00
|
|
|
|
// for now, use a simple hardcoded label
|
2007-08-12 21:43:58 +00:00
|
|
|
|
docstring itemlabel;
|
2005-02-25 11:55:36 +00:00
|
|
|
|
switch (par.itemdepth) {
|
|
|
|
|
case 0:
|
2006-10-21 00:16:43 +00:00
|
|
|
|
itemlabel = char_type(0x2022);
|
2005-02-25 11:55:36 +00:00
|
|
|
|
break;
|
|
|
|
|
case 1:
|
2006-10-21 00:16:43 +00:00
|
|
|
|
itemlabel = char_type(0x2013);
|
2005-02-25 11:55:36 +00:00
|
|
|
|
break;
|
|
|
|
|
case 2:
|
2006-10-21 00:16:43 +00:00
|
|
|
|
itemlabel = char_type(0x2217);
|
2005-02-25 11:55:36 +00:00
|
|
|
|
break;
|
|
|
|
|
case 3:
|
2007-01-11 16:38:08 +00:00
|
|
|
|
itemlabel = char_type(0x2219); // or 0x00b7
|
2005-02-25 11:55:36 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2007-01-15 16:58:14 +00:00
|
|
|
|
par.params().labelString(itemlabel);
|
2007-08-17 09:15:04 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
2007-08-17 09:15:04 +00:00
|
|
|
|
case LABEL_ENUMERATE: {
|
|
|
|
|
// FIXME: Yes I know this is a really, really! bad solution
|
2005-02-25 11:55:36 +00:00
|
|
|
|
// (Lgb)
|
2006-10-21 00:16:43 +00:00
|
|
|
|
docstring enumcounter = from_ascii("enum");
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
|
|
|
|
switch (par.itemdepth) {
|
|
|
|
|
case 2:
|
|
|
|
|
enumcounter += 'i';
|
|
|
|
|
case 1:
|
|
|
|
|
enumcounter += 'i';
|
|
|
|
|
case 0:
|
|
|
|
|
enumcounter += 'i';
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
enumcounter += "iv";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// not a valid enumdepth...
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-03 20:21:11 +00:00
|
|
|
|
// Maybe we have to reset the enumeration counter.
|
|
|
|
|
if (needEnumCounterReset(it))
|
|
|
|
|
counters.reset(enumcounter);
|
|
|
|
|
|
2005-02-25 11:55:36 +00:00
|
|
|
|
counters.step(enumcounter);
|
|
|
|
|
|
2005-09-09 14:52:55 +00:00
|
|
|
|
string format;
|
|
|
|
|
|
|
|
|
|
switch (par.itemdepth) {
|
|
|
|
|
case 0:
|
|
|
|
|
format = N_("\\arabic{enumi}.");
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
format = N_("(\\alph{enumii})");
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
format = N_("\\roman{enumiii}.");
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
format = N_("\\Alph{enumiv}.");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// not a valid enumdepth...
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-15 16:58:14 +00:00
|
|
|
|
par.params().labelString(counters.counterLabel(
|
|
|
|
|
par.translateIfPossible(from_ascii(format), buf.params())));
|
2007-01-11 16:38:08 +00:00
|
|
|
|
|
2007-08-17 09:15:04 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case LABEL_SENSITIVE: {
|
2007-08-12 21:43:58 +00:00
|
|
|
|
string const & type = counters.current_float();
|
|
|
|
|
docstring full_label;
|
|
|
|
|
if (type.empty())
|
|
|
|
|
full_label = buf.B_("Senseless!!! ");
|
|
|
|
|
else {
|
|
|
|
|
docstring name = buf.B_(textclass.floats().getType(type).name());
|
|
|
|
|
if (counters.hasCounter(from_utf8(type))) {
|
|
|
|
|
counters.step(from_utf8(type));
|
|
|
|
|
full_label = bformat(from_ascii("%1$s %2$s:"),
|
|
|
|
|
name,
|
2007-08-16 11:12:56 +00:00
|
|
|
|
counters.theCounter(from_utf8(type)));
|
2007-08-12 21:43:58 +00:00
|
|
|
|
} else
|
|
|
|
|
full_label = bformat(from_ascii("%1$s #:"), name);
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
2007-08-12 21:43:58 +00:00
|
|
|
|
par.params().labelString(full_label);
|
2007-08-17 09:15:04 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
2007-08-17 09:15:04 +00:00
|
|
|
|
case LABEL_NO_LABEL:
|
2007-01-15 16:58:14 +00:00
|
|
|
|
par.params().labelString(docstring());
|
2007-08-17 09:15:04 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case LABEL_MANUAL:
|
|
|
|
|
case LABEL_TOP_ENVIRONMENT:
|
|
|
|
|
case LABEL_CENTERED_TOP_ENVIRONMENT:
|
|
|
|
|
case LABEL_STATIC:
|
|
|
|
|
case LABEL_BIBLIO:
|
2007-01-15 16:58:14 +00:00
|
|
|
|
par.params().labelString(
|
2007-08-17 09:15:04 +00:00
|
|
|
|
par.translateIfPossible(layout->labelstring(),
|
|
|
|
|
buf.params()));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // anon namespace
|
|
|
|
|
|
2007-08-12 21:43:58 +00:00
|
|
|
|
void updateLabels(Buffer const & buf, ParIterator & parit)
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(parit.pit() == 0);
|
|
|
|
|
|
|
|
|
|
depth_type maxdepth = 0;
|
|
|
|
|
pit_type const lastpit = parit.lastpit();
|
|
|
|
|
for ( ; parit.pit() <= lastpit ; ++parit.pit()) {
|
|
|
|
|
// reduce depth if necessary
|
|
|
|
|
parit->params().depth(min(parit->params().depth(), maxdepth));
|
|
|
|
|
maxdepth = parit->getMaxDepthAfter();
|
|
|
|
|
|
|
|
|
|
// set the counter for this paragraph
|
|
|
|
|
setLabel(buf, parit);
|
|
|
|
|
|
|
|
|
|
// Now the insets
|
|
|
|
|
InsetList::const_iterator iit = parit->insetlist.begin();
|
|
|
|
|
InsetList::const_iterator end = parit->insetlist.end();
|
|
|
|
|
for (; iit != end; ++iit) {
|
|
|
|
|
parit.pos() = iit->pos;
|
|
|
|
|
iit->inset->updateLabels(buf, parit);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-25 11:55:36 +00:00
|
|
|
|
|
2007-08-14 16:59:59 +00:00
|
|
|
|
// FIXME: buf should should be const because updateLabels() modifies
|
|
|
|
|
// the contents of the paragraphs.
|
2006-11-13 16:53:49 +00:00
|
|
|
|
void updateLabels(Buffer const & buf, bool childonly)
|
2005-02-25 11:55:36 +00:00
|
|
|
|
{
|
2007-07-11 12:52:50 +00:00
|
|
|
|
Buffer const * const master = buf.getMasterBuffer();
|
2006-11-13 16:53:49 +00:00
|
|
|
|
// Use the master text class also for child documents
|
2007-07-11 12:52:50 +00:00
|
|
|
|
TextClass const & textclass = master->params().getTextClass();
|
2006-11-13 16:53:49 +00:00
|
|
|
|
|
|
|
|
|
if (!childonly) {
|
|
|
|
|
// If this is a child document start with the master
|
|
|
|
|
if (master != &buf) {
|
|
|
|
|
updateLabels(*master);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// start over the counters
|
|
|
|
|
textclass.counters().reset();
|
|
|
|
|
}
|
2006-09-09 11:16:28 +00:00
|
|
|
|
|
2007-08-14 16:59:59 +00:00
|
|
|
|
Buffer & cbuf = const_cast<Buffer &>(buf);
|
|
|
|
|
|
|
|
|
|
if (buf.text().empty()) {
|
2007-08-15 13:47:32 +00:00
|
|
|
|
// FIXME: we don't call continue with updateLabels()
|
|
|
|
|
// here because it crashes on newly created documents.
|
|
|
|
|
// But the TocBackend needs to be initialised
|
|
|
|
|
// nonetheless so we update the tocBackend manually.
|
2007-08-14 16:59:59 +00:00
|
|
|
|
cbuf.tocBackend().update();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-12 21:43:58 +00:00
|
|
|
|
// do the real work
|
|
|
|
|
ParIterator parit = par_iterator_begin(buf.inset());
|
|
|
|
|
updateLabels(buf, parit);
|
2006-04-22 18:48:28 +00:00
|
|
|
|
|
2007-03-12 11:23:41 +00:00
|
|
|
|
cbuf.tocBackend().update();
|
2007-05-17 19:19:37 +00:00
|
|
|
|
if (!childonly)
|
|
|
|
|
cbuf.structureChanged();
|
2007-09-04 14:34:42 +00:00
|
|
|
|
// FIXME
|
|
|
|
|
// the embedding signal is emitted with structureChanged signal
|
|
|
|
|
// this is inaccurate so these two will be separated later.
|
|
|
|
|
cbuf.embeddedFiles().update();
|
|
|
|
|
cbuf.embeddingChanged();
|
2005-02-25 11:55:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-03-12 11:23:41 +00:00
|
|
|
|
void checkBufferStructure(Buffer & buffer, ParIterator const & par_it)
|
|
|
|
|
{
|
2007-06-14 11:49:26 +00:00
|
|
|
|
if (par_it->layout()->toclevel != Layout::NOT_IN_TOC) {
|
2007-05-17 19:19:37 +00:00
|
|
|
|
Buffer * master = buffer.getMasterBuffer();
|
|
|
|
|
master->tocBackend().updateItem(par_it);
|
|
|
|
|
master->structureChanged();
|
2007-03-12 11:23:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
This is one of a series of patches that will merge the layout modules development in personal/branches/rgheck back into the tree.
Design goal: Allow the use of layout "modules", which are to LaTeX packages as layout files are to LaTeX document classes. Thus, one could have a module that defined certain character styles, environments, commands, or what have you, and include it in various documents, each of which uses a different document class, without having to modify the layout files themselves. For example, a theorems.module could be used with article.layout to provide support for theorem-type environments, without having to modify article.layout itself, and the same module could be used with book.layout, etc.
This first patch does some reworking of the infrastructrue. We need to distinguish between the TextClass that a particular document is using and the layout of that document, since modules, in particular, can modify the layout. The solution adopted here is to add a TextClass pointer to BufferParams, which will hold the layout. The layout itself is then constructed from the TextClass the document is using. At present, this is completely trivial, but that will change when modules are added.
The pointer in question is a boost::shared_ptr. This is needed because CutAndPaste saves a copy of the layout with each cut or copied selection. We cannot assume the selection vanishes when the document is closed, so there are two options: (i) keep a list of all the layouts that have ever been used by any document; (ii) used some kind of smart pointer. The latter seems preferable, as the former would waste memory. More importantly, the use of a smart pointer allows modules to be modified on disk and then reloaded while LyX is running, and it will eventually allow the same for layout files.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@19756 a592a061-630c-0410-9148-cb99ea01b6c8
2007-08-23 16:41:13 +00:00
|
|
|
|
textclass_type defaultTextclass()
|
|
|
|
|
{
|
|
|
|
|
// We want to return the article class. if `first' is
|
|
|
|
|
// true in the returned pair, then `second' is the textclass
|
|
|
|
|
// number; if it is false, second is 0. In both cases, second
|
|
|
|
|
// is what we want.
|
|
|
|
|
return textclasslist.numberOfClass("article").second;
|
|
|
|
|
}
|
|
|
|
|
|
2007-08-13 18:11:43 +00:00
|
|
|
|
|
|
|
|
|
void loadChildDocuments(Buffer const & buf)
|
|
|
|
|
{
|
|
|
|
|
bool parse_error = false;
|
|
|
|
|
|
|
|
|
|
for (InsetIterator it = inset_iterator_begin(buf.inset()); it; ++it) {
|
|
|
|
|
if (it->lyxCode() != Inset::INCLUDE_CODE)
|
|
|
|
|
continue;
|
|
|
|
|
InsetInclude const & inset = static_cast<InsetInclude const &>(*it);
|
|
|
|
|
InsetCommandParams const & ip = inset.params();
|
|
|
|
|
Buffer * child = loadIfNeeded(buf, ip);
|
|
|
|
|
if (!child)
|
|
|
|
|
continue;
|
|
|
|
|
parse_error |= !child->errorList("Parse").empty();
|
|
|
|
|
loadChildDocuments(*child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (use_gui && buf.getMasterBuffer() == &buf)
|
|
|
|
|
updateLabels(buf);
|
|
|
|
|
}
|
2006-10-21 00:16:43 +00:00
|
|
|
|
} // namespace lyx
|