X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBuffer.cpp;h=d8296e9795a5721e06dec63ab1f0656d794adcb7;hb=9577dae301866e69016c26300826114a611ea779;hp=aabb5fc4719b80d54d01fc17d1115997331f15f1;hpb=e7ccaba274e3396d87446c0bb1b6a49f4fbaf0f6;p=lyx.git diff --git a/src/Buffer.cpp b/src/Buffer.cpp index aabb5fc471..d8296e9795 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -3,7 +3,7 @@ * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * - * \author Lars Gullik Bjønnes + * \author Lars Gullik Bjønnes * \author Stefan Schimanski * * Full author contact details are available in file CREDITS. @@ -24,12 +24,15 @@ #include "Chktex.h" #include "Converter.h" #include "Counters.h" +#include "DispatchResult.h" #include "DocIterator.h" #include "Encoding.h" #include "ErrorList.h" #include "Exporter.h" #include "Format.h" #include "FuncRequest.h" +#include "FuncStatus.h" +#include "IndicesList.h" #include "InsetIterator.h" #include "InsetList.h" #include "Language.h" @@ -44,12 +47,14 @@ #include "output_docbook.h" #include "output.h" #include "output_latex.h" +#include "output_xhtml.h" #include "output_plaintext.h" #include "paragraph_funcs.h" #include "Paragraph.h" #include "ParagraphParameters.h" #include "ParIterator.h" #include "PDFOptions.h" +#include "SpellChecker.h" #include "sgml.h" #include "TexRow.h" #include "TexStream.h" @@ -59,6 +64,7 @@ #include "Undo.h" #include "VCBackend.h" #include "version.h" +#include "WordLangTuple.h" #include "WordList.h" #include "insets/InsetBibitem.h" @@ -79,6 +85,7 @@ #include "support/lassert.h" #include "support/convert.h" #include "support/debug.h" +#include "support/docstring_list.h" #include "support/ExceptionMessage.h" #include "support/FileName.h" #include "support/FileNameList.h" @@ -91,6 +98,7 @@ #include "support/os.h" #include "support/Package.h" #include "support/Path.h" +#include "support/Systemcall.h" #include "support/textutils.h" #include "support/types.h" @@ -101,6 +109,7 @@ #include #include #include +#include #include #include #include @@ -115,13 +124,25 @@ namespace os = support::os; namespace { -int const LYX_FORMAT = 339; //rgh: removed modules +// Do not remove the comment below, so we get merge conflict in +// independent branches. Instead add your own. +int const LYX_FORMAT = 361; // jspitzm: bibliography custom width typedef map DepClean; typedef map > RefCache; +void showPrintError(string const & name) +{ + docstring str = bformat(_("Could not print the document %1$s.\n" + "Check that your printer is set up correctly."), + makeDisplayPath(name, 50)); + Alert::error(_("Print document failed"), str); +} + } // namespace anon +class BufferSet : public std::set {}; + class Buffer::Impl { public: @@ -133,13 +154,13 @@ public: wa_->closeAll(); delete wa_; } + delete inset; } - + BufferParams params; LyXVC lyxvc; FileName temppath; mutable TexRow texrow; - Buffer const * parent_buffer; /// need to regenerate .tex? DepClean dep_clean; @@ -176,7 +197,7 @@ public: /// which maps the macro definition position to the scope and the MacroData. NamePositionScopeMacroMap macros; bool macro_lock; - + /// positions of child buffers in the buffer typedef map BufferPositionMap; typedef pair ScopeBuffer; @@ -204,9 +225,9 @@ public: /// documents), needed for appropriate update of natbib labels. mutable support::FileNameList bibfilesCache_; - // FIXME The caching mechanism could be improved. At present, we have a + // FIXME The caching mechanism could be improved. At present, we have a // cache for each Buffer, that caches all the bibliography info for that - // Buffer. A more efficient solution would be to have a global cache per + // Buffer. A more efficient solution would be to have a global cache per // file, and then to construct the Buffer's bibinfo from that. /// A cache for bibliography info mutable BiblioInfo bibinfo_; @@ -218,7 +239,23 @@ public: mutable RefCache ref_cache_; /// our Text that should be wrapped in an InsetText - InsetText inset; + InsetText * inset; + + /// This is here to force the test to be done whenever parent_buffer + /// is accessed. + Buffer const * parent() const { + // if parent_buffer is not loaded, then it has been unloaded, + // which means that parent_buffer is an invalid pointer. So we + // set it to null in that case. + if (!theBufferList().isLoaded(parent_buffer)) + parent_buffer = 0; + return parent_buffer; + } + /// + void setParent(Buffer const * pb) { parent_buffer = pb; } +private: + /// So we can force access via the accessors. + mutable Buffer const * parent_buffer; }; @@ -242,11 +279,11 @@ static FileName createBufferTmpDir() Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_) - : parent_buffer(0), lyx_clean(true), bak_clean(true), unnamed(false), + : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_), filename(file), file_fully_loaded(false), - toc_backend(&parent), macro_lock(false), timestamp_(0), + toc_backend(&parent), macro_lock(false), timestamp_(0), checksum_(0), wa_(0), undo_(parent), bibinfoCacheValid_(false), - inset(parent) + parent_buffer(0) { temppath = createBufferTmpDir(); lyxvc.setBuffer(&parent); @@ -260,10 +297,9 @@ Buffer::Buffer(string const & file, bool readonly) { LYXERR(Debug::INFO, "Buffer::Buffer()"); - d->inset.setBuffer(*this); - d->inset.initParagraphs(*this); - d->inset.setAutoBreakRows(true); - d->inset.getText(0)->setMacrocontextPosition(par_iterator_begin()); + d->inset = new InsetText(*this); + d->inset->setAutoBreakRows(true); + d->inset->getText(0)->setMacrocontextPosition(par_iterator_begin()); } @@ -276,12 +312,21 @@ Buffer::~Buffer() // GuiView already destroyed gui_ = 0; + if (d->unnamed && d->filename.extension() == "internal") { + // No need to do additional cleanups for internal buffer. + delete d; + return; + } // loop over children Impl::BufferPositionMap::iterator it = d->children_positions.begin(); Impl::BufferPositionMap::iterator end = d->children_positions.end(); - for (; it != end; ++it) - theBufferList().releaseChild(this, const_cast(it->first)); + for (; it != end; ++it) { + Buffer * child = const_cast(it->first); + // The child buffer might have been closed already. + if (theBufferList().isLoaded(child)) + theBufferList().releaseChild(this, child); + } // clear references to children in macro tables d->children_positions.clear(); @@ -316,13 +361,13 @@ frontend::WorkAreaManager & Buffer::workAreaManager() const Text & Buffer::text() const { - return const_cast(d->inset.text_); + return d->inset->text(); } Inset & Buffer::inset() const { - return const_cast(d->inset); + return *d->inset; } @@ -368,6 +413,12 @@ string const Buffer::temppath() const } +TexRow & Buffer::texrow() +{ + return d->texrow; +} + + TexRow const & Buffer::texrow() const { return d->texrow; @@ -409,10 +460,12 @@ string Buffer::logName(LogType * type) const FileName const fname(addName(temppath(), onlyFilename(changeExtension(filename, ".log")))); + + // FIXME: how do we know this is the name of the build log? FileName const bname( addName(path, onlyFilename( changeExtension(filename, - formats.extension("literate") + ".out")))); + formats.extension(bufferFormat()) + ".out")))); // If no Latex log or Build log is newer, show Build log @@ -474,7 +527,9 @@ int Buffer::readHeader(Lexer & lex) params().clearLayoutModules(); params().clearRemovedModules(); params().pdfoptions().clear(); - + params().indiceslist().clear(); + params().backgroundcolor = lyx::rgbFromHexName("#ffffff"); + for (int i = 0; i < 4; ++i) { params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i]; params().temp_bullet(i) = ITEMIZE_DEFAULTS[i]; @@ -523,7 +578,7 @@ int Buffer::readHeader(Lexer & lex) errorList.push_back(ErrorItem(_("Document header error"), s, -1, 0, 0)); } - + params().makeDocumentClass(); return unknown_tokens; @@ -532,7 +587,7 @@ int Buffer::readHeader(Lexer & lex) // Uwe C. Schroeder // changed to be public and have one parameter -// Returns false if "\end_document" is not read (Asger) +// Returns true if "\end_document" is not read (Asger) bool Buffer::readDocument(Lexer & lex) { ErrorList & errorList = d->errorLists["Parse"]; @@ -551,19 +606,19 @@ bool Buffer::readDocument(Lexer & lex) if (params().outputChanges) { bool dvipost = LaTeXFeatures::isAvailable("dvipost"); - bool xcolorsoul = LaTeXFeatures::isAvailable("soul") && + bool xcolorulem = LaTeXFeatures::isAvailable("ulem") && LaTeXFeatures::isAvailable("xcolor"); - if (!dvipost && !xcolorsoul) { + if (!dvipost && !xcolorulem) { Alert::warning(_("Changes not shown in LaTeX output"), _("Changes will not be highlighted in LaTeX output, " - "because neither dvipost nor xcolor/soul are installed.\n" + "because neither dvipost nor xcolor/ulem are installed.\n" "Please install these packages or redefine " "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); - } else if (!xcolorsoul) { + } else if (!xcolorulem) { Alert::warning(_("Changes not shown in LaTeX output"), _("Changes will not be highlighted in LaTeX output " - "when using pdflatex, because xcolor and soul are not installed.\n" + "when using pdflatex, because xcolor and ulem are not installed.\n" "Please install both packages or redefine " "\\lyxadded and \\lyxdeleted in the LaTeX preamble.")); } @@ -573,13 +628,34 @@ bool Buffer::readDocument(Lexer & lex) FileName const master_file = makeAbsPath(params().master, onlyPath(absFileName())); if (isLyXFilename(master_file.absFilename())) { - Buffer * master = checkAndLoadLyXFile(master_file); - d->parent_buffer = master; + Buffer * master = + checkAndLoadLyXFile(master_file, true); + if (master) { + // necessary e.g. after a reload + // to re-register the child (bug 5873) + // FIXME: clean up updateMacros (here, only + // child registering is needed). + master->updateMacros(); + // set master as master buffer, but only + // if we are a real child + if (master->isChild(this)) + setParent(master); + // if the master is not fully loaded + // it is probably just loading this + // child. No warning needed then. + else if (master->isFullyLoaded()) + LYXERR0("The master '" + << params().master + << "' assigned to this document (" + << absFileName() + << ") does not include " + "this document. Ignoring the master assignment."); + } } } // read main text - bool const res = text().read(*this, lex, errorList, &(d->inset)); + bool const res = text().read(*this, lex, errorList, d->inset); updateMacros(); updateMacroInstances(); @@ -620,11 +696,8 @@ void Buffer::insertStringAsLines(ParagraphList & pars, ++pos; space_inserted = true; } else { - const pos_type n = 8 - pos % 8; - for (pos_type i = 0; i < n; ++i) { - par.insertChar(pos, ' ', font, params().trackChanges); - ++pos; - } + par.insertChar(pos, *cit, font, params().trackChanges); + ++pos; space_inserted = true; } } else if (!isPrintable(*cit)) { @@ -673,6 +746,8 @@ bool Buffer::readFile(FileName const & filename) { FileName fname(filename); + params().compressed = fname.isZippedFile(); + // remove dummy empty par paragraphs().clear(); Lexer lex; @@ -745,7 +820,7 @@ Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename, bformat(_("%1$s is from a different" " version of LyX, but a temporary" " file for converting it could" - " not be created."), + " not be created."), from_utf8(filename.absFilename()))); return failure; } @@ -755,7 +830,7 @@ Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename, bformat(_("%1$s is from a different" " version of LyX, but the" " conversion script lyx2lyx" - " could not be found."), + " could not be found."), from_utf8(filename.absFilename()))); return failure; } @@ -774,7 +849,7 @@ Buffer::ReadStatus Buffer::readFile(Lexer & lex, FileName const & filename, Alert::error(_("Conversion script failed"), bformat(_("%1$s is from a different version" " of LyX, but the lyx2lyx script" - " failed to convert it."), + " failed to convert it."), from_utf8(filename.absFilename()))); return failure; } else { @@ -875,7 +950,7 @@ bool Buffer::writeFile(FileName const & fname) const return false; } - removeAutosaveFile(d->filename.absFilename()); + removeAutosaveFile(); saveCheckSum(d->filename); message(str + _(" done.")); @@ -947,13 +1022,17 @@ bool Buffer::write(ostream & ofs) const bool Buffer::makeLaTeXFile(FileName const & fname, string const & original_path, - OutputParams const & runparams, + OutputParams const & runparams_in, bool output_preamble, bool output_body) const { + OutputParams runparams = runparams_in; + if (params().useXetex) + runparams.flavor = OutputParams::XETEX; + string const encoding = runparams.encoding->iconvName(); LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "..."); - odocfstream ofs; + ofdocstream ofs; try { ofs.reset(encoding); } catch (iconv_codecvt_facet_exception & e) { lyxerr << "Caught iconv exception: " << e.what() << endl; @@ -986,7 +1065,7 @@ bool Buffer::makeLaTeXFile(FileName const & fname, "representable in the chosen encoding.\n" "Changing the document encoding to utf8 could help."), e.par_id, e.pos, e.pos + 1)); - failed_export = true; + failed_export = true; } catch (iconv_codecvt_facet_exception & e) { errorList.push_back(ErrorItem(_("iconv conversion failed"), @@ -1023,6 +1102,9 @@ void Buffer::writeLaTeXSource(odocstream & os, OutputParams runparams = runparams_in; + // Classify the unicode characters appearing in math insets + Encodings::initUnicodeMath(*this); + // validate the buffer. LYXERR(Debug::LATEX, " Validating buffer..."); LaTeXFeatures features(*this, params(), runparams); @@ -1045,11 +1127,11 @@ void Buffer::writeLaTeXSource(odocstream & os, // because then the macros will not get the right "redefinition" // flag as they don't see the parent macros which are output before. updateMacros(); - + // fold macros if possible, still with parent buffer as the // macros will be put in the prefix anyway. updateMacroInstances(); - + // There are a few differences between nice LaTeX and usual files: // usual is \batchmode and has a // special input@path to allow the including of figures @@ -1088,22 +1170,24 @@ void Buffer::writeLaTeXSource(odocstream & os, // Write the preamble runparams.use_babel = params().writeLaTeX(os, features, d->texrow); + runparams.use_japanese = features.isRequired("japanese"); + if (!output_body) return; // make the body. os << "\\begin{document}\n"; d->texrow.newline(); - + // output the parent macros MacroSet::iterator it = parentMacros.begin(); MacroSet::iterator end = parentMacros.end(); for (; it != end; ++it) - (*it)->write(os, true); + (*it)->write(os, true); } // output_preamble d->texrow.start(paragraphs().begin()->id(), 0); - + LYXERR(Debug::INFO, "preamble finished, now the body."); // if we are doing a real file with body, even if this is the @@ -1111,8 +1195,8 @@ void Buffer::writeLaTeXSource(odocstream & os, // This happens for example if only a child document is printed. Buffer const * save_parent = 0; if (output_preamble) { - save_parent = d->parent_buffer; - d->parent_buffer = 0; + save_parent = d->parent(); + d->setParent(0); } // the real stuff @@ -1120,7 +1204,7 @@ void Buffer::writeLaTeXSource(odocstream & os, // Restore the parenthood if needed if (output_preamble) - d->parent_buffer = save_parent; + d->setParent(save_parent); // add this just in case after all the paragraphs os << endl; @@ -1167,7 +1251,7 @@ void Buffer::makeDocBookFile(FileName const & fname, { LYXERR(Debug::LATEX, "makeDocBookFile..."); - odocfstream ofs; + ofdocstream ofs; if (!openFileWrite(ofs, fname)) return; @@ -1231,7 +1315,7 @@ void Buffer::writeDocBookSource(odocstream & os, string const & fname, if (runparams.flavor == OutputParams::XML) top += params().language->code(); else - top += params().language->code().substr(0,2); + top += params().language->code().substr(0, 2); top += '"'; if (!params().options.empty()) { @@ -1254,6 +1338,65 @@ void Buffer::writeDocBookSource(odocstream & os, string const & fname, } +void Buffer::makeLyXHTMLFile(FileName const & fname, + OutputParams const & runparams, + bool const body_only) const +{ + LYXERR(Debug::LATEX, "makeLYXHTMLFile..."); + + ofdocstream ofs; + if (!openFileWrite(ofs, fname)) + return; + + writeLyXHTMLSource(ofs, runparams, body_only); + + ofs.close(); + if (ofs.fail()) + lyxerr << "File '" << fname << "' was not closed properly." << endl; +} + + +void Buffer::writeLyXHTMLSource(odocstream & os, + OutputParams const & runparams, + bool const only_body) const +{ + LaTeXFeatures features(*this, params(), runparams); + validate(features); + + d->texrow.reset(); + + if (!only_body) { + os << "\n"; + // FIXME Language should be set properly. + os << "\n"; + // FIXME Header + os << "\n"; + // FIXME Presumably need to set this right + os << "\n"; + // FIXME Get this during validation? What about other meta-data? + os << "TBA\n"; + + os << features.getTClassHTMLPreamble(); + + os << '\n'; + + docstring const styleinfo = features.getTClassHTMLStyles(); + if (!styleinfo.empty()) { + os << "\n"; + } + os << "\n\n"; + } + + params().documentClass().counters().reset(); + xhtmlParagraphs(paragraphs(), *this, os, runparams); + if (!only_body) + os << "\n\n"; +} + + // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?) // Other flags: -wall -v0 -x int Buffer::runChktex() @@ -1313,8 +1456,9 @@ void Buffer::validate(LaTeXFeatures & features) const void Buffer::getLabelList(vector & list) const { // If this is a child document, use the parent's list instead. - if (d->parent_buffer) { - d->parent_buffer->getLabelList(list); + Buffer const * const pbuf = d->parent(); + if (pbuf) { + pbuf->getLabelList(list); return; } @@ -1329,11 +1473,12 @@ void Buffer::getLabelList(vector & list) const } -void Buffer::updateBibfilesCache() const +void Buffer::updateBibfilesCache(UpdateScope scope) const { // If this is a child document, use the parent's cache instead. - if (d->parent_buffer) { - d->parent_buffer->updateBibfilesCache(); + Buffer const * const pbuf = d->parent(); + if (pbuf && scope != UpdateChildOnly) { + pbuf->updateBibfilesCache(); return; } @@ -1351,7 +1496,7 @@ void Buffer::updateBibfilesCache() const static_cast(*it); inset.updateBibfilesCache(); support::FileNameList const & bibfiles = - inset.getBibfilesCache(*this); + inset.getBibfilesCache(); d->bibfilesCache_.insert(d->bibfilesCache_.end(), bibfiles.begin(), bibfiles.end()); @@ -1362,28 +1507,29 @@ void Buffer::updateBibfilesCache() const } -void Buffer::invalidateBibinfoCache() +void Buffer::invalidateBibinfoCache() { d->bibinfoCacheValid_ = false; } -support::FileNameList const & Buffer::getBibfilesCache() const +support::FileNameList const & Buffer::getBibfilesCache(UpdateScope scope) const { // If this is a child document, use the parent's cache instead. - if (d->parent_buffer) - return d->parent_buffer->getBibfilesCache(); + Buffer const * const pbuf = d->parent(); + if (pbuf && scope != UpdateChildOnly) + return pbuf->getBibfilesCache(); // We update the cache when first used instead of at loading time. if (d->bibfilesCache_.empty()) - const_cast(this)->updateBibfilesCache(); + const_cast(this)->updateBibfilesCache(scope); return d->bibfilesCache_; } BiblioInfo const & Buffer::masterBibInfo() const -{ +{ // if this is a child document and the parent is already loaded // use the parent's list instead [ale990412] Buffer const * const tmp = masterBuffer(); @@ -1436,41 +1582,207 @@ void Buffer::markDepClean(string const & name) } -bool Buffer::dispatch(string const & command, bool * result) +bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag) +{ + switch (cmd.action) { + case LFUN_BUFFER_EXPORT: { + docstring const arg = cmd.argument(); + bool enable = arg == "custom" || isExportable(to_utf8(arg)); + if (!enable) + flag.message(bformat( + _("Don't know how to export to format: %1$s"), arg)); + flag.setEnabled(enable); + break; + } + + case LFUN_BRANCH_ACTIVATE: + case LFUN_BRANCH_DEACTIVATE: { + BranchList const & branchList = params().branchlist(); + docstring const branchName = cmd.argument(); + flag.setEnabled(!branchName.empty() + && branchList.find(branchName)); + break; + } + + case LFUN_BUFFER_PRINT: + // if no Buffer is present, then of course we won't be called! + flag.setEnabled(true); + break; + + default: + return false; + } + return true; +} + + +void Buffer::dispatch(string const & command, DispatchResult & result) { return dispatch(lyxaction.lookupFunc(command), result); } -bool Buffer::dispatch(FuncRequest const & func, bool * result) +// NOTE We can end up here even if we have no GUI, because we are called +// by LyX::exec to handled command-line requests. So we may need to check +// whether we have a GUI or not. The boolean use_gui holds this information. +void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr) { + // We'll set this back to false if need be. bool dispatched = true; switch (func.action) { - case LFUN_BUFFER_EXPORT: { - bool const tmp = doExport(to_utf8(func.argument()), false); - if (result) - *result = tmp; + case LFUN_BUFFER_EXPORT: { + bool success = doExport(to_utf8(func.argument()), false); + dr.setError(success); + if (!success) + dr.setMessage(bformat(_("Error exporting to format: %1$s."), + func.argument())); + break; + } + + case LFUN_BRANCH_ACTIVATE: + case LFUN_BRANCH_DEACTIVATE: { + BranchList & branchList = params().branchlist(); + docstring const branchName = func.argument(); + // the case without a branch name is handled elsewhere + if (branchName.empty()) { + dispatched = false; break; } + Branch * branch = branchList.find(branchName); + if (!branch) { + LYXERR0("Branch " << branchName << " does not exist."); + dr.setError(true); + docstring const msg = + bformat(_("Branch \"%1$s\" does not exist."), branchName); + dr.setMessage(msg); + } else { + branch->setSelected(func.action == LFUN_BRANCH_ACTIVATE); + dr.setError(false); + dr.update(Update::Force); + } + break; + } - case LFUN_BRANCH_ACTIVATE: - case LFUN_BRANCH_DEACTIVATE: { - BranchList & branchList = params().branchlist(); - docstring const branchName = func.argument(); - Branch * branch = branchList.find(branchName); - if (!branch) - LYXERR0("Branch " << branchName << " does not exist."); - else - branch->setSelected(func.action == LFUN_BRANCH_ACTIVATE); - if (result) - *result = true; + case LFUN_BUFFER_PRINT: { + // we'll assume there's a problem until we succeed + dr.setError(true); + string target = func.getArg(0); + string target_name = func.getArg(1); + string command = func.getArg(2); + + if (target.empty() + || target_name.empty() + || command.empty()) { + LYXERR0("Unable to parse " << func.argument()); + docstring const msg = + bformat(_("Unable to parse \"%1$s\""), func.argument()); + dr.setMessage(msg); + break; + } + if (target != "printer" && target != "file") { + LYXERR0("Unrecognized target \"" << target << '"'); + docstring const msg = + bformat(_("Unrecognized target \"%1$s\""), from_utf8(target)); + dr.setMessage(msg); + break; } - default: - dispatched = false; + if (!doExport("dvi", true)) { + showPrintError(absFileName()); + dr.setMessage(_("Error exporting to DVI.")); + break; + } + + // Push directory path. + string const path = temppath(); + // Prevent the compiler from optimizing away p + FileName pp(path); + PathChanger p(pp); + + // there are three cases here: + // 1. we print to a file + // 2. we print directly to a printer + // 3. we print using a spool command (print to file first) + Systemcall one; + int res = 0; + string const dviname = changeExtension(latexName(true), "dvi"); + + if (target == "printer") { + if (!lyxrc.print_spool_command.empty()) { + // case 3: print using a spool + string const psname = changeExtension(dviname,".ps"); + command += ' ' + lyxrc.print_to_file + + quoteName(psname) + + ' ' + + quoteName(dviname); + + string command2 = lyxrc.print_spool_command + ' '; + if (target_name != "default") { + command2 += lyxrc.print_spool_printerprefix + + target_name + + ' '; + } + command2 += quoteName(psname); + // First run dvips. + // If successful, then spool command + res = one.startscript(Systemcall::Wait, command); + + if (res == 0) { + // If there's no GUI, we have to wait on this command. Otherwise, + // LyX deletes the temporary directory, and with it the spooled + // file, before it can be printed!! + Systemcall::Starttype stype = use_gui ? + Systemcall::DontWait : Systemcall::Wait; + res = one.startscript(stype, command2); + } + } else { + // case 2: print directly to a printer + if (target_name != "default") + command += ' ' + lyxrc.print_to_printer + target_name + ' '; + // as above.... + Systemcall::Starttype stype = use_gui ? + Systemcall::DontWait : Systemcall::Wait; + res = one.startscript(stype, command + quoteName(dviname)); + } + + } else { + // case 1: print to a file + FileName const filename(makeAbsPath(target_name, filePath())); + FileName const dvifile(makeAbsPath(dviname, path)); + if (filename.exists()) { + docstring text = bformat( + _("The file %1$s already exists.\n\n" + "Do you want to overwrite that file?"), + makeDisplayPath(filename.absFilename())); + if (Alert::prompt(_("Overwrite file?"), + text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0) + break; + } + command += ' ' + lyxrc.print_to_file + + quoteName(filename.toFilesystemEncoding()) + + ' ' + + quoteName(dvifile.toFilesystemEncoding()); + // as above.... + Systemcall::Starttype stype = use_gui ? + Systemcall::DontWait : Systemcall::Wait; + res = one.startscript(stype, command); + } + + if (res == 0) + dr.setError(false); + else { + dr.setMessage(_("Error running external commands.")); + showPrintError(absFileName()); + } + break; + } + + default: + dispatched = false; + break; } - return dispatched; + dr.dispatched(dispatched); } @@ -1498,17 +1810,18 @@ bool Buffer::isMultiLingual() const DocIterator Buffer::getParFromID(int const id) const { + Buffer * buf = const_cast(this); if (id < 0) { // John says this is called with id == -1 from undo lyxerr << "getParFromID(), id: " << id << endl; - return doc_iterator_end(inset()); + return doc_iterator_end(buf); } - for (DocIterator it = doc_iterator_begin(inset()); !it.atEnd(); it.forwardPar()) + for (DocIterator it = doc_iterator_begin(buf); !it.atEnd(); it.forwardPar()) if (it.paragraph().id() == id) return it; - return doc_iterator_end(inset()); + return doc_iterator_end(buf); } @@ -1520,25 +1833,25 @@ bool Buffer::hasParWithID(int const id) const ParIterator Buffer::par_iterator_begin() { - return ParIterator(doc_iterator_begin(inset())); + return ParIterator(doc_iterator_begin(this)); } ParIterator Buffer::par_iterator_end() { - return ParIterator(doc_iterator_end(inset())); + return ParIterator(doc_iterator_end(this)); } ParConstIterator Buffer::par_iterator_begin() const { - return lyx::par_const_iterator_begin(inset()); + return ParConstIterator(doc_iterator_begin(this)); } ParConstIterator Buffer::par_iterator_end() const { - return lyx::par_const_iterator_end(inset()); + return ParConstIterator(doc_iterator_end(this)); } @@ -1570,7 +1883,7 @@ bool Buffer::isExternallyModified(CheckMethod method) const { LASSERT(d->filename.exists(), /**/); // if method == timestamp, check timestamp before checksum - return (method == checksum_method + return (method == checksum_method || d->timestamp_ != d->filename.lastModified()) && d->checksum_ != d->filename.checksum(); } @@ -1663,23 +1976,50 @@ bool Buffer::isReadonly() const void Buffer::setParent(Buffer const * buffer) { // Avoids recursive include. - d->parent_buffer = buffer == this ? 0 : buffer; + d->setParent(buffer == this ? 0 : buffer); updateMacros(); } -Buffer const * Buffer::parent() +Buffer const * Buffer::parent() const +{ + return d->parent(); +} + + +void Buffer::collectRelatives(BufferSet & bufs) const +{ + bufs.insert(this); + if (parent()) + parent()->collectRelatives(bufs); + + // loop over children + Impl::BufferPositionMap::iterator it = d->children_positions.begin(); + Impl::BufferPositionMap::iterator end = d->children_positions.end(); + for (; it != end; ++it) + bufs.insert(const_cast(it->first)); +} + + +std::vector Buffer::allRelatives() const { - return d->parent_buffer; + BufferSet bufs; + collectRelatives(bufs); + BufferSet::iterator it = bufs.begin(); + std::vector ret; + for (; it != bufs.end(); ++it) + ret.push_back(*it); + return ret; } Buffer const * Buffer::masterBuffer() const { - if (!d->parent_buffer) + Buffer const * const pbuf = d->parent(); + if (!pbuf) return this; - - return d->parent_buffer->masterBuffer(); + + return pbuf->masterBuffer(); } @@ -1689,6 +2029,35 @@ bool Buffer::isChild(Buffer * child) const } +DocIterator Buffer::firstChildPosition(Buffer const * child) +{ + Impl::BufferPositionMap::iterator it; + it = d->children_positions.find(child); + if (it == d->children_positions.end()) + return DocIterator(this); + return it->second; +} + + +std::vector Buffer::getChildren() const +{ + std::vector clist; + // loop over children + Impl::BufferPositionMap::iterator it = d->children_positions.begin(); + Impl::BufferPositionMap::iterator end = d->children_positions.end(); + for (; it != end; ++it) { + Buffer * child = const_cast(it->first); + clist.push_back(child); + // there might be grandchildren + std::vector glist = child->getChildren(); + for (vector::const_iterator git = glist.begin(); + git != glist.end(); ++git) + clist.push_back(*git); + } + return clist; +} + + template typename M::iterator greatest_below(M & m, typename M::key_type const & x) { @@ -1700,11 +2069,11 @@ typename M::iterator greatest_below(M & m, typename M::key_type const & x) return m.end(); it--; - return it; + return it; } -MacroData const * Buffer::getBufferMacro(docstring const & name, +MacroData const * Buffer::getBufferMacro(docstring const & name, DocIterator const & pos) const { LYXERR(Debug::MACROS, "Searching for " << to_ascii(name) << " at " << pos); @@ -1716,14 +2085,14 @@ MacroData const * Buffer::getBufferMacro(docstring const & name, // we haven't found anything yet DocIterator bestPos = par_iterator_begin(); MacroData const * bestData = 0; - + // find macro definitions for name Impl::NamePositionScopeMacroMap::iterator nameIt - = d->macros.find(name); + = d->macros.find(name); if (nameIt != d->macros.end()) { // find last definition in front of pos or at pos itself Impl::PositionScopeMacroMap::const_iterator it - = greatest_below(nameIt->second, pos); + = greatest_below(nameIt->second, pos); if (it != nameIt->second.end()) { while (true) { // scope ends behind pos? @@ -1735,7 +2104,7 @@ MacroData const * Buffer::getBufferMacro(docstring const & name, bestData = &it->second.second; break; } - + // try previous macro if there is one if (it == nameIt->second.begin()) break; @@ -1746,7 +2115,7 @@ MacroData const * Buffer::getBufferMacro(docstring const & name, // find macros in included files Impl::PositionScopeBufferMap::const_iterator it - = greatest_below(d->position_to_children, pos); + = greatest_below(d->position_to_children, pos); if (it == d->position_to_children.end()) // no children before return bestData; @@ -1775,7 +2144,7 @@ MacroData const * Buffer::getBufferMacro(docstring const & name, break; --it; } - + // return the best macro we have found return bestData; } @@ -1785,7 +2154,7 @@ MacroData const * Buffer::getMacro(docstring const & name, DocIterator const & pos, bool global) const { if (d->macro_lock) - return 0; + return 0; // query buffer macros MacroData const * data = getBufferMacro(name, pos); @@ -1793,9 +2162,10 @@ MacroData const * Buffer::getMacro(docstring const & name, return data; // If there is a master buffer, query that - if (d->parent_buffer) { + Buffer const * const pbuf = d->parent(); + if (pbuf) { d->macro_lock = true; - MacroData const * macro = d->parent_buffer->getMacro( + MacroData const * macro = pbuf->getMacro( name, *this, false); d->macro_lock = false; if (macro) @@ -1849,15 +2219,14 @@ void Buffer::updateMacros(DocIterator & it, DocIterator & scope) const InsetList::const_iterator end = insets.end(); for (; iit != end; ++iit) { it.pos() = iit->pos; - + // is it a nested text inset? if (iit->inset->asInsetText()) { // Inset needs its own scope? - InsetText const * itext - = iit->inset->asInsetText(); + InsetText const * itext = iit->inset->asInsetText(); bool newScope = itext->isMacroScope(); - // scope which ends just behind the inset + // scope which ends just behind the inset DocIterator insetScope = it; ++insetScope.pos(); @@ -1867,25 +2236,24 @@ void Buffer::updateMacros(DocIterator & it, DocIterator & scope) const it.pop_back(); continue; } - + // is it an external file? if (iit->inset->lyxCode() == INCLUDE_CODE) { // get buffer of external file - InsetCommand const & inset - = static_cast(*iit->inset); - InsetCommandParams const & ip = inset.params(); + InsetInclude const & inset = + static_cast(*iit->inset); d->macro_lock = true; - Buffer * child = loadIfNeeded(*this, ip); + Buffer * child = inset.getChildBuffer(); d->macro_lock = false; if (!child) - continue; + continue; // register its position, but only when it is // included first in the buffer - if (d->children_positions.find(child) - == d->children_positions.end()) - d->children_positions[child] = it; - + if (d->children_positions.find(child) == + d->children_positions.end()) + d->children_positions[child] = it; + // register child with its scope d->position_to_children[it] = Impl::ScopeBuffer(scope, child); continue; @@ -1893,10 +2261,10 @@ void Buffer::updateMacros(DocIterator & it, DocIterator & scope) const if (iit->inset->lyxCode() != MATHMACRO_CODE) continue; - + // get macro data - MathMacroTemplate & macroTemplate - = static_cast(*iit->inset); + MathMacroTemplate & macroTemplate = + static_cast(*iit->inset); MacroContext mc(*this, it); macroTemplate.updateToContext(mc); @@ -1947,8 +2315,8 @@ void Buffer::updateMacroInstances() const { LYXERR(Debug::MACROS, "updateMacroInstances for " << d->filename.onlyFileName()); - DocIterator it = doc_iterator_begin(inset()); - DocIterator end = doc_iterator_end(inset()); + DocIterator it = doc_iterator_begin(this); + DocIterator end = doc_iterator_end(this); for (; it != end; it.forwardPos()) { // look for MathData cells in InsetMathNest insets Inset * inset = it.nextInset(); @@ -1976,7 +2344,7 @@ void Buffer::listMacroNames(MacroNameSet & macros) const return; d->macro_lock = true; - + // loop over macro names Impl::NamePositionScopeMacroMap::iterator nameIt = d->macros.begin(); Impl::NamePositionScopeMacroMap::iterator nameEnd = d->macros.end(); @@ -1990,31 +2358,33 @@ void Buffer::listMacroNames(MacroNameSet & macros) const it->first->listMacroNames(macros); // call parent - if (d->parent_buffer) - d->parent_buffer->listMacroNames(macros); + Buffer const * const pbuf = d->parent(); + if (pbuf) + pbuf->listMacroNames(macros); - d->macro_lock = false; + d->macro_lock = false; } void Buffer::listParentMacros(MacroSet & macros, LaTeXFeatures & features) const { - if (!d->parent_buffer) + Buffer const * const pbuf = d->parent(); + if (!pbuf) return; - + MacroNameSet names; - d->parent_buffer->listMacroNames(names); - + pbuf->listMacroNames(names); + // resolve macros MacroNameSet::iterator it = names.begin(); MacroNameSet::iterator end = names.end(); for (; it != end; ++it) { // defined? - MacroData const * data = - d->parent_buffer->getMacro(*it, *this, false); + MacroData const * data = + pbuf->getMacro(*it, *this, false); if (data) { macros.insert(data); - + // we cannot access the original MathMacroTemplate anymore // here to calls validate method. So we do its work here manually. // FIXME: somehow make the template accessible here. @@ -2027,7 +2397,7 @@ void Buffer::listParentMacros(MacroSet & macros, LaTeXFeatures & features) const Buffer::References & Buffer::references(docstring const & label) { - if (d->parent_buffer) + if (d->parent()) return const_cast(masterBuffer())->references(label); RefCache::iterator it = d->ref_cache_.find(label); @@ -2062,7 +2432,7 @@ InsetLabel const * Buffer::insetLabel(docstring const & label) const void Buffer::clearReferenceCache() const { - if (!d->parent_buffer) + if (!d->parent()) d->ref_cache_.clear(); } @@ -2099,24 +2469,26 @@ void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to, void Buffer::getSourceCode(odocstream & os, pit_type par_begin, - pit_type par_end, bool full_source) + pit_type par_end, bool full_source) const { OutputParams runparams(¶ms().encoding()); runparams.nice = true; - runparams.flavor = OutputParams::LATEX; + runparams.flavor = params().useXetex ? + OutputParams::XETEX : OutputParams::LATEX; runparams.linelen = lyxrc.plaintext_linelen; // No side effect of file copying and image conversion runparams.dryrun = true; - d->texrow.reset(); if (full_source) { os << "% " << _("Preview source code") << "\n\n"; + d->texrow.reset(); d->texrow.newline(); d->texrow.newline(); - if (isLatex()) - writeLaTeXSource(os, filePath(), runparams, true, true); - else + if (isDocBook()) writeDocBookSource(os, absFileName(), runparams, false); + else + // latex or literate + writeLaTeXSource(os, string(), runparams, true, true); } else { runparams.par_begin = par_begin; runparams.par_end = par_end; @@ -2131,14 +2503,16 @@ void Buffer::getSourceCode(odocstream & os, pit_type par_begin, convert(par_end - 1)) << "\n\n"; } - d->texrow.newline(); - d->texrow.newline(); + TexRow texrow; + texrow.reset(); + texrow.newline(); + texrow.newline(); // output paragraphs - if (isLatex()) - latexParagraphs(*this, text(), os, d->texrow, runparams); - else - // DocBook + if (isDocBook()) docbookParagraphs(paragraphs(), *this, os, runparams); + else + // latex or literate + latexParagraphs(*this, text(), os, texrow, runparams); } } @@ -2154,6 +2528,14 @@ ErrorList & Buffer::errorList(string const & type) const } +void Buffer::updateTocItem(std::string const & type, + DocIterator const & dit) const +{ + if (gui_) + gui_->updateTocItem(type, dit); +} + + void Buffer::structureChanged() const { if (gui_) @@ -2203,6 +2585,12 @@ void Buffer::resetAutosaveTimers() const } +bool Buffer::hasGuiDelegate() const +{ + return gui_; +} + + void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui) { gui_ = gui; @@ -2225,7 +2613,7 @@ public: /// int start() { - command_ = to_utf8(bformat(_("Auto-saving %1$s"), + command_ = to_utf8(bformat(_("Auto-saving %1$s"), from_utf8(fname_.absFilename()))); return run(DontWait); } @@ -2282,6 +2670,39 @@ int AutoSaveBuffer::generateChild() } // namespace anon +FileName Buffer::getAutosaveFilename() const +{ + // if the document is unnamed try to save in the backup dir, else + // in the default document path, and as a last try in the filePath, + // which will most often be the temporary directory + string fpath; + if (isUnnamed()) + fpath = lyxrc.backupdir_path.empty() ? lyxrc.document_path + : lyxrc.backupdir_path; + if (!isUnnamed() || fpath.empty() || !FileName(fpath).exists()) + fpath = filePath(); + + string const fname = "#" + d->filename.onlyFileName() + "#"; + return makeAbsPath(fname, fpath); +} + + +void Buffer::removeAutosaveFile() const +{ + FileName const f = getAutosaveFilename(); + if (f.exists()) + f.removeFile(); +} + + +void Buffer::moveAutosaveFile(support::FileName const & oldauto) const +{ + FileName const newauto = getAutosaveFilename(); + if (!(oldauto == newauto || oldauto.moveTo(newauto))) + LYXERR0("Unable to remove autosave file `" << oldauto << "'!"); +} + + // Perfect target for a thread... void Buffer::autoSave() const { @@ -2293,14 +2714,7 @@ void Buffer::autoSave() const // emit message signal. message(_("Autosaving current document...")); - - // create autosave filename - string fname = filePath(); - fname += '#'; - fname += d->filename.onlyFileName(); - fname += '#'; - - AutoSaveBuffer autosave(*this, FileName(fname)); + AutoSaveBuffer autosave(*this, getAutosaveFilename()); autosave.start(); markBakClean(); @@ -2310,14 +2724,38 @@ void Buffer::autoSave() const string Buffer::bufferFormat() const { - if (isDocBook()) - return "docbook"; - if (isLiterate()) - return "literate"; - return "latex"; + string format = params().documentClass().outputFormat(); + if (format == "latex") { + if (params().useXetex) + return "xetex"; + if (params().encoding().package() == Encoding::japanese) + return "platex"; + } + return format; +} + + +string Buffer::getDefaultOutputFormat() const +{ + if (!params().defaultOutputFormat.empty() + && params().defaultOutputFormat != "default") + return params().defaultOutputFormat; + typedef vector Formats; + Formats formats = exportableFormats(true); + if (isDocBook() + || isLiterate() + || params().useXetex + || params().encoding().package() == Encoding::japanese) { + if (formats.empty()) + return string(); + // return the first we find + return formats.front()->name(); + } + return lyxrc.default_view_format; } + bool Buffer::doExport(string const & format, bool put_in_tempdir, string & result_file) const { @@ -2364,6 +2802,8 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir, if (backend_format == "text") writePlaintextFile(*this, FileName(filename), runparams); // no backend + else if (backend_format == "xhtml") + makeLyXHTMLFile(FileName(filename), runparams); else if (backend_format == "lyx") writeFile(FileName(filename)); // Docbook backend @@ -2493,6 +2933,7 @@ vector Buffer::backends() const v.push_back("pdflatex"); } v.push_back("text"); + v.push_back("xhtml"); v.push_back("lyx"); return v; } @@ -2618,4 +3059,367 @@ void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const } +void Buffer::setBuffersForInsets() const +{ + inset().setBuffer(const_cast(*this)); +} + + +void Buffer::updateLabels(UpdateScope scope) const +{ + // Use the master text class also for child documents + Buffer const * const master = masterBuffer(); + DocumentClass const & textclass = master->params().documentClass(); + + // keep the buffers to be children in this set. If the call from the + // master comes back we can see which of them were actually seen (i.e. + // via an InsetInclude). The remaining ones in the set need still be updated. + static std::set bufToUpdate; + if (scope == UpdateMaster) { + // If this is a child document start with the master + if (master != this) { + bufToUpdate.insert(this); + master->updateLabels(); + // Do this here in case the master has no gui associated with it. Then, + // the TocModel is not updated and TocModel::toc_ is invalid (bug 5699). + if (!master->gui_) + structureChanged(); + + // was buf referenced from the master (i.e. not in bufToUpdate anymore)? + if (bufToUpdate.find(this) == bufToUpdate.end()) + return; + } + + // start over the counters in the master + textclass.counters().reset(); + } + + // update will be done below for this buffer + bufToUpdate.erase(this); + + // update all caches + clearReferenceCache(); + updateMacros(); + + Buffer & cbuf = const_cast(*this); + + LASSERT(!text().paragraphs().empty(), /**/); + + // do the real work + ParIterator parit = cbuf.par_iterator_begin(); + updateLabels(parit); + + if (master != this) + // TocBackend update will be done later. + return; + + cbuf.tocBackend().update(); + if (scope == UpdateMaster) + cbuf.structureChanged(); +} + + +static depth_type getDepth(DocIterator const & it) +{ + depth_type depth = 0; + for (size_t i = 0 ; i < it.depth() ; ++i) + if (!it[i].inset().inMathed()) + depth += it[i].paragraph().getDepth() + 1; + // remove 1 since the outer inset does not count + return depth - 1; +} + +static depth_type getItemDepth(ParIterator const & it) +{ + Paragraph const & par = *it; + LabelType const labeltype = par.layout().labeltype; + + if (labeltype != LABEL_ENUMERATE && labeltype != LABEL_ITEMIZE) + return 0; + + // this will hold the lowest depth encountered up to now. + depth_type min_depth = getDepth(it); + ParIterator prev_it = it; + while (true) { + 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; + } + } + + // We search for the first paragraph with same label + // that is not more deeply nested. + Paragraph & prev_par = *prev_it; + depth_type const prev_depth = getDepth(prev_it); + if (labeltype == prev_par.layout().labeltype) { + if (prev_depth < min_depth) + return prev_par.itemdepth + 1; + if (prev_depth == min_depth) + return prev_par.itemdepth; + } + min_depth = 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; + } +} + + +static bool needEnumCounterReset(ParIterator const & it) +{ + Paragraph const & par = *it; + LASSERT(par.layout().labeltype == LABEL_ENUMERATE, /**/); + depth_type const cur_depth = par.getDepth(); + 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; + } + // start of nested inset: reset + return true; +} + + +// set the label of a paragraph. This includes the counters. +static void setLabel(Buffer const & buf, ParIterator & it) +{ + BufferParams const & bp = buf.masterBuffer()->params(); + DocumentClass const & textclass = bp.documentClass(); + Paragraph & par = it.paragraph(); + Layout const & layout = par.layout(); + Counters & counters = textclass.counters(); + + if (par.params().startOfAppendix()) { + // FIXME: only the counter corresponding to toplevel + // sectionning should be reset + counters.reset(); + counters.appendix(true); + } + par.params().appendix(counters.appendix()); + + // Compute the item depth of the paragraph + par.itemdepth = getItemDepth(it); + + if (layout.margintype == MARGIN_MANUAL + || layout.latextype == LATEX_BIB_ENVIRONMENT) { + if (par.params().labelWidthString().empty()) + par.params().labelWidthString(par.translateIfPossible(layout.labelstring(), bp)); + } else { + par.params().labelWidthString(docstring()); + } + + switch(layout.labeltype) { + case LABEL_COUNTER: + if (layout.toclevel <= bp.secnumdepth + && (layout.latextype != LATEX_ENVIRONMENT + || isFirstInSequence(it.pit(), it.plist()))) { + counters.step(layout.counter); + par.params().labelString( + par.expandLabel(layout, bp)); + } else + par.params().labelString(docstring()); + break; + + case LABEL_ITEMIZE: { + // At some point of time we should do something more + // clever here, like: + // par.params().labelString( + // bp.user_defined_bullet(par.itemdepth).getText()); + // for now, use a simple hardcoded label + docstring itemlabel; + switch (par.itemdepth) { + case 0: + itemlabel = char_type(0x2022); + break; + case 1: + itemlabel = char_type(0x2013); + break; + case 2: + itemlabel = char_type(0x2217); + break; + case 3: + itemlabel = char_type(0x2219); // or 0x00b7 + break; + } + par.params().labelString(itemlabel); + break; + } + + case LABEL_ENUMERATE: { + docstring enumcounter = layout.counter.empty() ? from_ascii("enum") : layout.counter; + + 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; + } + + // Maybe we have to reset the enumeration counter. + if (needEnumCounterReset(it)) + counters.reset(enumcounter); + counters.step(enumcounter); + + par.params().labelString(counters.theCounter(enumcounter)); + + break; + } + + case LABEL_SENSITIVE: { + 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, + counters.theCounter(from_utf8(type))); + } else + full_label = bformat(from_ascii("%1$s #:"), name); + } + par.params().labelString(full_label); + break; + } + + case LABEL_NO_LABEL: + par.params().labelString(docstring()); + break; + + case LABEL_MANUAL: + case LABEL_TOP_ENVIRONMENT: + case LABEL_CENTERED_TOP_ENVIRONMENT: + case LABEL_STATIC: + case LABEL_BIBLIO: + par.params().labelString( + par.translateIfPossible(layout.labelstring(), bp)); + break; + } +} + + +void Buffer::updateLabels(ParIterator & parit) const +{ + LASSERT(parit.pit() == 0, /**/); + + // set the position of the text in the buffer to be able + // to resolve macros in it. This has nothing to do with + // labels, but by putting it here we avoid implementing + // a whole bunch of traversal routines just for this call. + parit.text()->setMacrocontextPosition(parit); + + 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(*this, 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(parit); + } + } +} + + +bool Buffer::nextWord(DocIterator & from, DocIterator & to, + docstring & word) const +{ + bool inword = false; + bool ignoreword = false; + string lang_code; + // Go backward a bit if needed in order to return the word currently + // pointed by 'from'. + while (from && from.pos() && isLetter(from)) + from.backwardPos(); + // OK, we start from here. + to = from; + while (to.depth()) { + if (isLetter(to)) { + if (!inword) { + inword = true; + ignoreword = false; + from = to; + word.clear(); + lang_code = to.paragraph().getFontSettings(params(), + to.pos()).language()->code(); + } + // Insets like optional hyphens and ligature + // break are part of a word. + if (!to.paragraph().isInset(to.pos())) { + char_type const c = to.paragraph().getChar(to.pos()); + word += c; + if (isDigit(c)) + ignoreword = true; + } + } else { // !isLetter(cur) + if (inword && !word.empty() && !ignoreword) + return true; + inword = false; + } + to.forwardPos(); + } + from = to; + word.clear(); + return false; +} + + +int Buffer::spellCheck(DocIterator & from, DocIterator & to, + WordLangTuple & word_lang, docstring_list & suggestions) const +{ + int progress = 0; + SpellChecker::Result res = SpellChecker::OK; + SpellChecker * speller = theSpellChecker(); + suggestions.clear(); + docstring word; + while (nextWord(from, to, word)) { + ++progress; + string lang_code = lyxrc.spellchecker_use_alt_lang + ? lyxrc.spellchecker_alt_lang + : from.paragraph().getFontSettings(params(), from.pos()).language()->code(); + WordLangTuple wl(word, lang_code); + res = speller->check(wl); + // ... just bail out if the spellchecker reports an error. + if (!speller->error().empty()) { + throw ExceptionMessage(WarningException, + _("The spellchecker has failed."), speller->error()); + } + if (res != SpellChecker::OK && res != SpellChecker::IGNORED_WORD) { + word_lang = wl; + break; + } + from = to; + } + while (!(word = speller->nextMiss()).empty()) + suggestions.push_back(word); + return progress; +} + } // namespace lyx