X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fbuffer_funcs.cpp;h=17aab767c2c91c04b3a86a78533a7c5d97cd87da;hb=cca78e3c8ae27431323746abd64f9d7db017099d;hp=7a8618cc1564d3ebf30557a68dfcabacd507ba5f;hpb=9092bffc32c3a9206818f82265ff9a6db4bc7fbe;p=lyx.git diff --git a/src/buffer_funcs.cpp b/src/buffer_funcs.cpp index 7a8618cc15..17aab767c2 100644 --- a/src/buffer_funcs.cpp +++ b/src/buffer_funcs.cpp @@ -21,172 +21,102 @@ #include "ErrorList.h" #include "Floating.h" #include "FloatList.h" -#include "gettext.h" +#include "InsetList.h" #include "Language.h" #include "LaTeX.h" +#include "Layout.h" +#include "LyX.h" #include "TextClass.h" #include "Paragraph.h" #include "paragraph_funcs.h" #include "ParagraphList.h" #include "ParagraphParameters.h" #include "ParIterator.h" -#include "LyXVC.h" #include "TexRow.h" +#include "Text.h" #include "TocBackend.h" -#include "VCBackend.h" #include "frontends/alert.h" #include "insets/InsetBibitem.h" -#include "insets/InsetCaption.h" #include "insets/InsetInclude.h" -#include "insets/InsetTabular.h" +#include "support/lassert.h" +#include "support/convert.h" +#include "support/debug.h" #include "support/filetools.h" -#include "support/fs_extras.h" -#include "support/lyxlib.h" - -#include -#include - - -namespace lyx { +#include "support/gettext.h" +#include "support/lstrings.h" +#include "support/textutils.h" using namespace std; +using namespace lyx::support; -using support::bformat; -using support::FileName; -using support::libFileSearch; -using support::makeAbsPath; -using support::makeDisplayPath; -using support::onlyFilename; -using support::onlyPath; -using support::unlink; - -using std::min; -using std::string; +namespace lyx { namespace Alert = frontend::Alert; -namespace fs = boost::filesystem; -namespace { -bool readFile(Buffer * const b, FileName const & s) +Buffer * checkAndLoadLyXFile(FileName const & filename) { - BOOST_ASSERT(b); - - // File information about normal file - if (!fs::exists(s.toFilesystemEncoding())) { - docstring const file = makeDisplayPath(s.absFilename(), 50); - docstring text = bformat(_("The specified document\n%1$s" - "\ncould not be read."), file); - Alert::error(_("Could not read document"), text); - return false; + // File already open? + Buffer * checkBuffer = theBufferList().getBuffer(filename); + if (checkBuffer) { + if (checkBuffer->isClean()) + return checkBuffer; + docstring const file = makeDisplayPath(filename.absFilename(), 20); + 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; + + // FIXME: should be LFUN_REVERT + theBufferList().release(checkBuffer); + // Load it again. + return checkAndLoadLyXFile(filename); } - // Check if emergency save file exists and is newer. - FileName const e(s.absFilename() + ".emergency"); - - if (fs::exists(e.toFilesystemEncoding()) && - fs::exists(s.toFilesystemEncoding()) && - fs::last_write_time(e.toFilesystemEncoding()) > fs::last_write_time(s.toFilesystemEncoding())) - { - docstring const file = makeDisplayPath(s.absFilename(), 20); - docstring const text = - bformat(_("An emergency save of the document " - "%1$s exists.\n\n" - "Recover emergency save?"), file); - switch (Alert::prompt(_("Load emergency save?"), text, 0, 2, - _("&Recover"), _("&Load Original"), - _("&Cancel"))) - { - case 0: - // the file is not saved if we load the emergency file. - b->markDirty(); - return b->readFile(e); - case 1: - break; - default: - return false; + if (filename.exists()) { + if (!filename.isReadableFile()) { + docstring text = bformat(_("The file %1$s exists but is not " + "readable by the current user."), + from_utf8(filename.absFilename())); + Alert::error(_("File not readable!"), text); + return 0; } - } - - // Now check if autosave file is newer. - FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#'); - - if (fs::exists(a.toFilesystemEncoding()) && - fs::exists(s.toFilesystemEncoding()) && - fs::last_write_time(a.toFilesystemEncoding()) > fs::last_write_time(s.toFilesystemEncoding())) - { - docstring const file = makeDisplayPath(s.absFilename(), 20); - docstring const text = - bformat(_("The backup of the document " - "%1$s is newer.\n\nLoad the " - "backup instead?"), file); - switch (Alert::prompt(_("Load backup?"), text, 0, 2, - _("&Load backup"), _("Load &original"), - _("&Cancel") )) - { - case 0: - // the file is not saved if we load the autosave file. - b->markDirty(); - return b->readFile(a); - case 1: - // Here we delete the autosave - unlink(a); - break; - default: - return false; + Buffer * b = theBufferList().newBuffer(filename.absFilename()); + if (!b) + // Buffer creation is not possible. + return 0; + if (!b->loadLyXFile(filename)) { + theBufferList().release(b); + return 0; } + return b; } - return b->readFile(s); -} - - -} // namespace anon - + docstring text = bformat(_("The document %1$s does not yet " + "exist.\n\nDo you want to create a new document?"), + from_utf8(filename.absFilename())); + if (!Alert::prompt(_("Create new document?"), + text, 0, 1, _("&Create"), _("Cancel"))) + return newFile(filename.absFilename(), string(), true); -bool loadLyXFile(Buffer * b, FileName const & s) -{ - BOOST_ASSERT(b); - - if (fs::is_readable(s.toFilesystemEncoding())) { - if (readFile(b, s)) { - b->lyxvc().file_found_hook(s); - if (!fs::is_writable(s.toFilesystemEncoding())) - b->setReadonly(true); - return true; - } - } else { - docstring const file = makeDisplayPath(s.absFilename(), 20); - // Here we probably should run - if (LyXVC::file_not_found_hook(s)) { - 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")); - - 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; + return 0; } + // FIXME newFile() should probably be a member method of Application... Buffer * newFile(string const & filename, string const & templatename, bool const isNamed) { // get a free buffer Buffer * b = theBufferList().newBuffer(filename); - BOOST_ASSERT(b); + if (!b) + // Buffer creation is not possible. + return 0; FileName tname; // use defaults.lyx as a default template if it exists. @@ -213,46 +143,25 @@ Buffer * newFile(string const & filename, string const & templatename, } b->setReadonly(false); - b->fully_loaded(true); + b->setFullyLoaded(true); return b; } -void bufferErrors(Buffer const & buf, TeXErrors const & terr, - ErrorList & errorList) +Buffer * newUnnamedFile(string const & templatename, FileName const & path) { - TeXErrors::Errors::const_iterator cit = terr.begin(); - TeXErrors::Errors::const_iterator end = terr.end(); - - for (; cit != end; ++cit) { - int id_start = -1; - int pos_start = -1; - int errorrow = cit->error_in_line; - bool found = buf.texrow().getIdFromRow(errorrow, id_start, - pos_start); - int id_end = -1; - int pos_end = -1; - do { - ++errorrow; - found = buf.texrow().getIdFromRow(errorrow, id_end, - pos_end); - } while (found && id_start == id_end && pos_start == pos_end); - - errorList.push_back(ErrorItem(cit->error_desc, - cit->error_text, id_start, pos_start, pos_end)); + static int newfile_number; + + FileName filename(path, + "newfile" + convert(++newfile_number) + ".lyx"); + while (theBufferList().exists(filename) + || filename.isReadableFile()) { + ++newfile_number; + filename.set(path, + "newfile" + convert(newfile_number) + ".lyx"); } -} - - -string const bufferFormat(Buffer const & buffer) -{ - if (buffer.isDocBook()) - return "docbook"; - else if (buffer.isLiterate()) - return "literate"; - else - return "latex"; + return newFile(filename.absFilename(), templatename, false); } @@ -278,6 +187,36 @@ int countWords(DocIterator const & from, DocIterator const & to) } +int countChars(DocIterator const & from, DocIterator const & to, bool with_blanks) +{ + int chars = 0; + int blanks = 0; + for (DocIterator dit = from ; dit != to ; dit.forwardPos()) { + + if (!dit.inTexted()) continue; + Paragraph const & par = dit.paragraph(); + pos_type const pos = dit.pos(); + + if (pos != dit.lastpos() && !par.isDeleted(pos)) { + if (Inset const * ins = par.getInset(pos)) { + if (ins->isLetter()) + ++chars; + else if (with_blanks && ins->isSpace()) + ++blanks; + } else { + char_type const c = par.getChar(pos); + if (isPrintableNonspace(c)) + ++chars; + else if (isSpace(c) && with_blanks) + ++blanks; + } + } + } + + return chars + blanks; +} + + namespace { depth_type getDepth(DocIterator const & it) @@ -293,7 +232,7 @@ depth_type getDepth(DocIterator const & it) depth_type getItemDepth(ParIterator const & it) { Paragraph const & par = *it; - LYX_LABEL_TYPES const labeltype = par.layout()->labeltype; + LabelType const labeltype = par.layout().labeltype; if (labeltype != LABEL_ENUMERATE && labeltype != LABEL_ITEMIZE) return 0; @@ -317,20 +256,17 @@ depth_type getItemDepth(ParIterator const & it) // 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) { + if (labeltype == prev_par.layout().labeltype) { + if (prev_depth < min_depth) return prev_par.itemdepth + 1; - } - else if (prev_depth == min_depth) { + if (prev_depth == min_depth) return prev_par.itemdepth; - } } - min_depth = std::min(min_depth, prev_depth); + 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) { + if (prev_depth == 0) return 0; - } } } @@ -338,140 +274,66 @@ depth_type getItemDepth(ParIterator const & it) bool needEnumCounterReset(ParIterator const & it) { Paragraph const & par = *it; - BOOST_ASSERT(par.layout()->labeltype == LABEL_ENUMERATE); + 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; + return prev_par.layout().labeltype != LABEL_ENUMERATE; } // start of nested inset: reset return true; } -void setCaptionLabels(Inset & inset, string const & type, - docstring const label, Counters & counters) -{ - Text * text = inset.getText(0); - if (!text) - return; - - ParagraphList & pars = text->paragraphs(); - if (pars.empty()) - return; - - docstring const counter = from_ascii(type); - - ParagraphList::iterator p = pars.begin(); - for (; p != pars.end(); ++p) { - InsetList::iterator it2 = p->insetlist.begin(); - InsetList::iterator end2 = p->insetlist.end(); - // Any caption within this float should have the same - // label prefix but different numbers. - for (; it2 != end2; ++it2) { - Inset & icap = *it2->inset; - // Look deeper just in case. - setCaptionLabels(icap, type, label, counters); - if (icap.lyxCode() == Inset::CAPTION_CODE) { - // We found a caption! - counters.step(counter); - int number = counters.value(counter); - InsetCaption & ic = static_cast(icap); - ic.setType(type); - ic.setCount(number); - ic.setCustomLabel(label); - } - } - } -} - - -void setCaptions(Paragraph & par, TextClass const & textclass) -{ - if (par.insetlist.empty()) - return; - - Counters & counters = textclass.counters(); - - InsetList::iterator it = par.insetlist.begin(); - InsetList::iterator end = par.insetlist.end(); - for (; it != end; ++it) { - Inset & inset = *it->inset; - if (inset.lyxCode() == Inset::FLOAT_CODE - || inset.lyxCode() == Inset::WRAP_CODE) { - docstring const name = inset.name(); - if (name.empty()) - continue; - - Floating const & fl = textclass.floats().getType(to_ascii(name)); - // FIXME UNICODE - string const & type = fl.type(); - docstring const label = from_utf8(fl.name()); - setCaptionLabels(inset, type, label, counters); - } - else if (inset.lyxCode() == Inset::TABULAR_CODE - && static_cast(inset).tabular.isLongTabular()) { - // FIXME: are "table" and "Table" the correct type and label? - setCaptionLabels(inset, "table", from_ascii("Table"), counters); - } - else if (inset.lyxCode() == Inset::LISTINGS_CODE) - setCaptionLabels(inset, "listing", from_ascii("Listing"), counters); - } -} - // set the label of a paragraph. This includes the counters. -void setLabel(Buffer const & buf, ParIterator & it, TextClass const & textclass) +void setLabel(Buffer const & buf, ParIterator & it) { - Paragraph & par = *it; - Layout_ptr const & layout = par.layout(); + 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 (it.pit() == 0) { - par.params().appendix(par.params().startOfAppendix()); - } else { - par.params().appendix(it.plist()[it.pit() - 1].params().appendix()); - if (!par.params().appendix() && - par.params().startOfAppendix()) { - par.params().appendix(true); - textclass.counters().reset(); - } + 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) { + if (layout.margintype == MARGIN_MANUAL) { if (par.params().labelWidthString().empty()) - par.params().labelWidthString(par.translateIfPossible(layout->labelstring(), buf.params())); + par.params().labelWidthString(par.translateIfPossible(layout.labelstring(), bp)); } else { par.params().labelWidthString(docstring()); } - // Optimisation: setLabel() can be called for each for each - // paragraph of the document. So we make the string static to - // avoid the repeated instanciation. - static docstring itemlabel; - - // is it a layout that has an automatic label? - if (layout->labeltype == LABEL_COUNTER) { - if (layout->toclevel <= buf.params().secnumdepth - && (layout->latextype != LATEX_ENVIRONMENT + switch(layout.labeltype) { + case LABEL_COUNTER: + if (layout.toclevel <= bp.secnumdepth + && (layout.latextype != LATEX_ENVIRONMENT || isFirstInSequence(it.pit(), it.plist()))) { - counters.step(layout->counter); + counters.step(layout.counter); par.params().labelString( - par.expandLabel(layout, buf.params())); + par.expandLabel(layout, bp)); } else par.params().labelString(docstring()); + break; - } else if (layout->labeltype == LABEL_ITEMIZE) { + case LABEL_ITEMIZE: { // At some point of time we should do something more // clever here, like: // par.params().labelString( - // buf.params().user_defined_bullet(par.itemdepth).getText()); + // 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); @@ -487,10 +349,11 @@ void setLabel(Buffer const & buf, ParIterator & it, TextClass const & textclass) break; } par.params().labelString(itemlabel); + break; + } - } else if (layout->labeltype == LABEL_ENUMERATE) { - // FIXME - // Yes I know this is a really, really! bad solution + case LABEL_ENUMERATE: { + // FIXME: Yes I know this is a really, really! bad solution // (Lgb) docstring enumcounter = from_ascii("enum"); @@ -537,127 +400,88 @@ void setLabel(Buffer const & buf, ParIterator & it, TextClass const & textclass) } par.params().labelString(counters.counterLabel( - par.translateIfPossible(from_ascii(format), buf.params()))); + par.translateIfPossible(from_ascii(format), bp))); - } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302 - counters.step(from_ascii("bibitem")); - int number = counters.value(from_ascii("bibitem")); - if (par.bibitem()) - par.bibitem()->setCounter(number); + break; + } - par.params().labelString( - par.translateIfPossible(layout->labelstring(), buf.params())); - // In biblio shouldn't be following counters but... - } else if (layout->labeltype == LABEL_SENSITIVE) { - // Search for the first float or wrap inset in the iterator - size_t i = it.depth(); - Inset * in = 0; - while (i > 0) { - --i; - Inset::Code const code = it[i].inset().lyxCode(); - if (code == Inset::FLOAT_CODE || - code == Inset::WRAP_CODE) { - in = &it[i].inset(); - break; - } - } - // FIXME Can Inset::name() return an empty name for wide or - // float insets? If not we can put the definition of type - // inside the if (in) clause and use that instead of - // if (!type.empty()). - docstring type; - if (in) - type = in->name(); - - if (!type.empty()) { - Floating const & fl = textclass.floats().getType(to_ascii(type)); - // FIXME UNICODE - counters.step(from_ascii(fl.type())); - - // Doesn't work... yet. - par.params().labelString(par.translateIfPossible( - bformat(from_ascii("%1$s #:"), from_utf8(fl.name())), - buf.params())); - } else { - // par->SetLayout(0); - par.params().labelString(par.translateIfPossible( - layout->labelstring(), buf.params())); + 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; + } - } else if (layout->labeltype == LABEL_NO_LABEL) + case LABEL_NO_LABEL: par.params().labelString(docstring()); - else - par.params().labelString( - par.translateIfPossible(layout->labelstring(), buf.params())); -} - -} // anon namespace - + break; -bool updateCurrentLabel(Buffer const & buf, - ParIterator & it) -{ - if (it == par_iterator_end(buf.inset())) - return false; - -// if (it.lastpit == 0 && Text::isMainText(buf)) -// return false; - - switch (it->layout()->labeltype) { - - case LABEL_NO_LABEL: case LABEL_MANUAL: - case LABEL_BIBLIO: case LABEL_TOP_ENVIRONMENT: case LABEL_CENTERED_TOP_ENVIRONMENT: - case LABEL_STATIC: - case LABEL_ITEMIZE: - setLabel(buf, it, buf.params().getTextClass()); - return true; - - case LABEL_SENSITIVE: - case LABEL_COUNTER: - // do more things with enumerate later - case LABEL_ENUMERATE: - return false; + case LABEL_STATIC: + case LABEL_BIBLIO: + par.params().labelString( + par.translateIfPossible(layout.labelstring(), bp)); + break; } - - // This is dead code which get rid of a warning: - return true; } +} // anon namespace -void updateLabels(Buffer const & buf, - ParIterator & from, ParIterator & to, bool childonly) +void updateLabels(Buffer const & buf, ParIterator & parit) { - for (ParIterator it = from; it != to; ++it) { - if (it.pit() > it.lastpit()) - return; - if (!updateCurrentLabel (buf, it)) { - updateLabels(buf, childonly); - return; - } - } -} + 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); -void updateLabels(Buffer const & buf, ParIterator & iter, bool childonly) -{ - if (updateCurrentLabel(buf, iter)) - return; + 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); - updateLabels(buf, childonly); + // 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); + } + } } +// FIXME: buf should should be const because updateLabels() modifies +// the contents of the paragraphs. void updateLabels(Buffer const & buf, bool childonly) { + Buffer const * const master = buf.masterBuffer(); // Use the master text class also for child documents - TextClass const & textclass = buf.params().getTextClass(); + DocumentClass const & textclass = master->params().documentClass(); if (!childonly) { // If this is a child document start with the master - Buffer const * const master = buf.getMasterBuffer(); if (master != &buf) { updateLabels(*master); return; @@ -665,52 +489,27 @@ void updateLabels(Buffer const & buf, bool childonly) // start over the counters textclass.counters().reset(); + buf.clearReferenceCache(); + buf.inset().setBuffer(const_cast(buf)); + buf.updateMacros(); } - ParIterator const end = par_iterator_end(buf.inset()); - - for (ParIterator it = par_iterator_begin(buf.inset()); it != end; ++it) { - // reduce depth if necessary - if (it.pit()) { - Paragraph const & prevpar = it.plist()[it.pit() - 1]; - it->params().depth(min(it->params().depth(), - prevpar.getMaxDepthAfter())); - } else - it->params().depth(0); + Buffer & cbuf = const_cast(buf); - // set the counter for this paragraph - setLabel(buf, it, textclass); + BOOST_ASSERT(!buf.text().paragraphs().empty()); - // It is better to set the captions after setLabel because - // the caption number might need the section number in the - // future. - setCaptions(*it, textclass); + // do the real work + ParIterator parit = par_iterator_begin(buf.inset()); + updateLabels(buf, parit); - // Now included docs - InsetList::const_iterator iit = it->insetlist.begin(); - InsetList::const_iterator end = it->insetlist.end(); - for (; iit != end; ++iit) { - if (iit->inset->lyxCode() == Inset::INCLUDE_CODE) - static_cast(iit->inset) - ->updateLabels(buf); - } - } + if (master != &buf) + // TocBackend update will be done later. + return; - Buffer & cbuf = const_cast(buf); cbuf.tocBackend().update(); if (!childonly) cbuf.structureChanged(); } -void checkBufferStructure(Buffer & buffer, ParIterator const & par_it) -{ - if (par_it->layout()->labeltype == LABEL_COUNTER - && par_it->layout()->toclevel != Layout::NOT_IN_TOC) { - Buffer * master = buffer.getMasterBuffer(); - master->tocBackend().updateItem(par_it); - master->structureChanged(); - } -} - } // namespace lyx