X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fbuffer_funcs.cpp;h=9e1b048cc01785799fe1b7fe0b7867abcd0110bd;hb=c7d29be153debac82e3d2e8865fcc849f0a5f40d;hp=05727eafc978b9f5db602b9ac148400ac74972ed;hpb=1447a7bf98fb691e9979fd3c7689adba4ed3f421;p=lyx.git diff --git a/src/buffer_funcs.cpp b/src/buffer_funcs.cpp index 05727eafc9..9e1b048cc0 100644 --- a/src/buffer_funcs.cpp +++ b/src/buffer_funcs.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 Alfredo Braunstein * * Full author contact details are available in file CREDITS. @@ -22,16 +22,13 @@ #include "Floating.h" #include "FloatList.h" #include "InsetList.h" -#include "InsetIterator.h" #include "Language.h" #include "LaTeX.h" #include "Layout.h" -#include "LayoutPtr.h" #include "LyX.h" +#include "LyXVC.h" #include "TextClass.h" -#include "TextClassList.h" #include "Paragraph.h" -#include "paragraph_funcs.h" #include "ParagraphList.h" #include "ParagraphParameters.h" #include "ParIterator.h" @@ -44,11 +41,14 @@ #include "insets/InsetBibitem.h" #include "insets/InsetInclude.h" +#include "support/lassert.h" #include "support/convert.h" #include "support/debug.h" #include "support/filetools.h" #include "support/gettext.h" #include "support/lstrings.h" +#include "support/mutex.h" +#include "support/textutils.h" using namespace std; using namespace lyx::support; @@ -58,33 +58,64 @@ namespace lyx { namespace Alert = frontend::Alert; -Buffer * checkAndLoadLyXFile(FileName const & filename) +Buffer * checkAndLoadLyXFile(FileName const & filename, bool const acceptDirty) { // File already open? - Buffer * checkBuffer = theBufferList().getBuffer(filename.absFilename()); + Buffer * checkBuffer = theBufferList().getBuffer(filename); if (checkBuffer) { - if (checkBuffer->isClean()) + // Sometimes (when setting the master buffer from a child) + // we accept a dirty buffer right away (otherwise we'd get + // an infinite loop (bug 5514). + // We also accept a dirty buffer when the document has not + // yet been saved to disk. + if (checkBuffer->isClean() || acceptDirty || !filename.exists()) return checkBuffer; - docstring const file = makeDisplayPath(filename.absFilename(), 20); - docstring text = bformat(_( + docstring const file = makeDisplayPath(filename.absFileName(), 20); + docstring const 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); + int res = Alert::prompt(_("Reload saved document?"), + text, 2, 2, _("Yes, &Reload"), _("No, &Keep Changes"), _("&Cancel")); + switch (res) { + case 0: { + // reload the document + if (checkBuffer->reload() != Buffer::ReadSuccess) + return 0; + return checkBuffer; + } + case 1: + // keep changes + return checkBuffer; + case 2: + // cancel + return 0; + } } - if (filename.isReadableFile()) { - Buffer * b = theBufferList().newBuffer(filename.absFilename()); - if (!b) + bool const exists = filename.exists(); + bool const tryVC = exists ? false : LyXVC::fileInVC(filename); + if (exists || tryVC) { + if (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; + } + if (filename.extension() == "lyx" && filename.isFileEmpty()) { + // Makes it possible to open an empty (0 bytes) .lyx file + return newFile(filename.absFileName(), "", true); + } + } + Buffer * b = theBufferList().newBuffer(filename.absFileName()); + if (!b) { // Buffer creation is not possible. return 0; - if (!b->loadLyXFile(filename)) { + } + if (b->loadLyXFile() != Buffer::ReadSuccess) { + // do not save an emergency file when releasing the buffer + b->markClean(); theBufferList().release(b); return 0; } @@ -93,10 +124,10 @@ Buffer * checkAndLoadLyXFile(FileName const & filename) docstring text = bformat(_("The document %1$s does not yet " "exist.\n\nDo you want to create a new document?"), - from_utf8(filename.absFilename())); + from_utf8(filename.absFileName())); if (!Alert::prompt(_("Create new document?"), - text, 0, 1, _("&Create"), _("Cancel"))) - return newFile(filename.absFilename(), string(), true); + text, 0, 1, _("&Yes, Create New Document"), _("&No, Do Not Create"))) + return newFile(filename.absFileName(), string(), true); return 0; } @@ -104,7 +135,7 @@ Buffer * checkAndLoadLyXFile(FileName const & filename) // FIXME newFile() should probably be a member method of Application... Buffer * newFile(string const & filename, string const & templatename, - bool const isNamed) + bool is_named) { // get a free buffer Buffer * b = theBufferList().newBuffer(filename); @@ -120,8 +151,8 @@ Buffer * newFile(string const & filename, string const & templatename, tname = makeAbsPath(templatename); if (!tname.empty()) { - if (!b->readFile(tname)) { - docstring const file = makeDisplayPath(tname.absFilename(), 50); + if (b->loadThisLyXFile(tname) != Buffer::ReadSuccess) { + docstring const file = makeDisplayPath(tname.absFileName(), 50); docstring const text = bformat( _("The specified document template\n%1$s\ncould not be read."), file); @@ -131,10 +162,12 @@ Buffer * newFile(string const & filename, string const & templatename, } } - if (!isNamed) { + if (is_named) + // in this case, the user chose the filename, so we + // assume that she really does want this file. + b->markDirty(); + else b->setUnnamed(); - b->setFileName(filename); - } b->setReadonly(false); b->setFullyLoaded(true); @@ -143,342 +176,44 @@ Buffer * newFile(string const & filename, string const & templatename, } -Buffer * newUnnamedFile(string const & templatename, FileName const & path) +Buffer * newUnnamedFile(FileName const & path, string const & prefix, + string const & templatename) { - static int newfile_number; - - string document_path = path.absFilename(); - string filename = addName(document_path, - "newfile" + convert(++newfile_number) + ".lyx"); - while (theBufferList().exists(filename) - || FileName(filename).isReadableFile()) { - ++newfile_number; - filename = addName(document_path, - "newfile" + convert(newfile_number) + ".lyx"); - } - return newFile(filename, templatename, false); -} + static map file_number; + static Mutex mutex; + Mutex::Locker locker(&mutex); + FileName filename; -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()) - && !dit.paragraph().isDeleted(dit.pos())) { - if (!inword) { - ++count; - inword = true; - } - } else if (inword) - inword = false; + do { + filename.set(path, + prefix + convert(++file_number[prefix]) + ".lyx"); } + while (theBufferList().exists(filename) || filename.isReadableFile()); - return count; + return newFile(filename.absFileName(), templatename, false); } -namespace { - -depth_type getDepth(DocIterator const & it) +Buffer * loadIfNeeded(FileName const & fname) { - 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; -} - -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) + Buffer * buffer = theBufferList().getBuffer(fname); + if (!buffer) { + if (!fname.exists() && !LyXVC::fileInVC(fname)) return 0; - } -} - -bool needEnumCounterReset(ParIterator const & it) -{ - Paragraph const & par = *it; - BOOST_ASSERT(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. -void setLabel(Buffer const & buf, ParIterator & it) -{ - TextClass const & textclass = buf.params().getTextClass(); - Paragraph & par = it.paragraph(); - LayoutPtr 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) { - if (par.params().labelWidthString().empty()) - par.params().labelWidthString(par.translateIfPossible(layout->labelstring(), buf.params())); - } else { - par.params().labelWidthString(docstring()); - } - - switch(layout->labeltype) { - case LABEL_COUNTER: - if (layout->toclevel <= buf.params().secnumdepth - && (layout->latextype != LATEX_ENVIRONMENT - || isFirstInSequence(it.pit(), it.plist()))) { - counters.step(layout->counter); - par.params().labelString( - par.expandLabel(layout, buf.params())); - } 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( - // buf.params().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: { - // FIXME: Yes I know this is a really, really! bad solution - // (Lgb) - docstring enumcounter = from_ascii("enum"); - - 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); - - 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; - } - - par.params().labelString(counters.counterLabel( - par.translateIfPossible(from_ascii(format), buf.params()))); - - 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(), - buf.params())); - break; - } -} - -} // anon namespace - -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); - } - } - -} - - -// 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 = master->params().getTextClass(); + buffer = theBufferList().newBuffer(fname.absFileName()); + if (!buffer) + // Buffer creation is not possible. + return 0; - if (!childonly) { - // If this is a child document start with the master - if (master != &buf) { - updateLabels(*master); - return; + if (buffer->loadLyXFile() != Buffer::ReadSuccess) { + //close the buffer we just opened + theBufferList().release(buffer); + return 0; } - - // start over the counters - textclass.counters().reset(); - } - - Buffer & cbuf = const_cast(buf); - - if (buf.text().empty()) { - // 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. - cbuf.tocBackend().update(); - return; - } - - // do the real work - ParIterator parit = par_iterator_begin(buf.inset()); - updateLabels(buf, parit); - - cbuf.tocBackend().update(); - if (!childonly) - cbuf.structureChanged(); -} - - -void checkBufferStructure(Buffer & buffer, ParIterator const & par_it) -{ - if (par_it->layout()->toclevel != Layout::NOT_IN_TOC) { - Buffer const * master = buffer.masterBuffer(); - master->tocBackend().updateItem(par_it); - master->structureChanged(); } + return buffer; }