]> git.lyx.org Git - lyx.git/blobdiff - src/Buffer.cpp
* Add ability to rename branches (bug 4128).
[lyx.git] / src / Buffer.cpp
index 30076ff2f21b252af22620979b1f231aac1fed8a..53ade6faf3683655839783effc631bc4f9a0f8f3 100644 (file)
@@ -47,6 +47,7 @@
 #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"
@@ -68,6 +69,7 @@
 
 #include "insets/InsetBibitem.h"
 #include "insets/InsetBibtex.h"
+#include "insets/InsetBranch.h"
 #include "insets/InsetInclude.h"
 #include "insets/InsetText.h"
 
@@ -125,7 +127,7 @@ namespace {
 
 // Do not remove the comment below, so we get merge conflict in
 // independent branches. Instead add your own.
-int const LYX_FORMAT = 357;  // sanda: change latex output for various underline commands
+int const LYX_FORMAT = 362;  // jspitzm: support applemac encoding
 
 typedef map<string, bool> DepClean;
 typedef map<docstring, pair<InsetLabel const *, Buffer::References> > RefCache;
@@ -459,19 +461,38 @@ 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
+       // Also consider the master buffer log file
+       FileName masterfname = fname;
+       LogType mtype;
+       if (masterBuffer() != this) {
+               string const mlogfile = masterBuffer()->logName(&mtype);
+               masterfname = FileName(mlogfile);
+       }
 
+       // If no Latex log or Build log is newer, show Build log
        if (bname.exists() &&
-           (!fname.exists() || fname.lastModified() < bname.lastModified())) {
+           ((!fname.exists() && !masterfname.exists())
+            || (fname.lastModified() < bname.lastModified()
+                && masterfname.lastModified() < bname.lastModified()))) {
                LYXERR(Debug::FILES, "Log name calculated as: " << bname);
                if (type)
                        *type = buildlog;
                return bname.absFilename();
+       // If we have a newer master file log or only a master log, show this
+       } else if (fname != masterfname
+                  && (!fname.exists() && masterfname.exists()
+                  || fname.lastModified() < masterfname.lastModified())) {
+               LYXERR(Debug::FILES, "Log name calculated as: " << masterfname);
+               if (type)
+                       *type = mtype;
+               return masterfname.absFilename();
        }
        LYXERR(Debug::FILES, "Log name calculated as: " << fname);
        if (type)
@@ -1335,6 +1356,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 << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" <<
+                       " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
+               // FIXME Language should be set properly.
+               os << "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
+               // FIXME Header
+               os << "<head>\n";
+               // FIXME Presumably need to set this right
+               os << "<meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" />\n";
+               // FIXME Get this during validation? What about other meta-data?
+               os << "<title>TBA</title>\n";
+
+               os << features.getTClassHTMLPreamble();
+
+               os << '\n';
+
+               docstring const styleinfo = features.getTClassHTMLStyles();
+               if (!styleinfo.empty()) {
+                       os << "<style type='text/css'>\n";
+                       os << styleinfo;
+                       os << "</style>\n";
+               }
+               os << "</head>\n<body>\n";
+       }
+
+       params().documentClass().counters().reset();
+       xhtmlParagraphs(paragraphs(), *this, os, runparams);
+       if (!only_body)
+               os << "</body>\n</html>\n";
+}
+
+
 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
 // Other flags: -wall -v0 -x
 int Buffer::runChktex()
@@ -1542,6 +1622,8 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                        break;
                }
 
+               case LFUN_BRANCH_ADD:
+               case LFUN_BRANCHES_RENAME:
                case LFUN_BUFFER_PRINT:
                        // if no Buffer is present, then of course we won't be called!
                        flag.setEnabled(true);
@@ -1578,6 +1660,28 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
                break;
        }
 
+       case LFUN_BRANCH_ADD: {
+               BranchList & branchList = params().branchlist();
+               docstring const branchName = func.argument();
+               if (branchName.empty()) {
+                       dispatched = false;
+                       break;
+               }
+               Branch * branch = branchList.find(branchName);
+               if (branch) {
+                       LYXERR0("Branch " << branchName << " does already exist.");
+                       dr.setError(true);
+                       docstring const msg = 
+                               bformat(_("Branch \"%1$s\" does already exist."), branchName);
+                       dr.setMessage(msg);
+               } else {
+                       branchList.add(branchName);
+                       dr.setError(false);
+                       dr.update(Update::Force);
+               }
+               break;
+       }
+
        case LFUN_BRANCH_ACTIVATE:
        case LFUN_BRANCH_DEACTIVATE: {
                BranchList & branchList = params().branchlist();
@@ -1602,6 +1706,13 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
                break;
        }
 
+       case LFUN_BRANCHES_RENAME: {
+               docstring const oldname = from_utf8(func.getArg(0));
+               docstring const newname = from_utf8(func.getArg(1));
+               renameBranches(oldname, newname);
+               break;
+       }
+
        case LFUN_BUFFER_PRINT: {
                // we'll assume there's a problem until we succeed
                dr.setError(true); 
@@ -2249,6 +2360,107 @@ void Buffer::updateMacros() const
 }
 
 
+void Buffer::getUsedBranches(std::list<docstring> & result, bool const from_master) const
+{
+       // Iterate over buffer, starting with first paragraph
+       // The scope must be bigger than any lookup DocIterator
+       // later. For the global lookup, lastpit+1 is used, hence
+       // we use lastpit+2 here.
+       DocIterator it = par_iterator_begin();
+       DocIterator scope = it;
+       scope.pit() = scope.lastpit() + 2;
+       pit_type lastpit = it.lastpit();
+
+       while (it.pit() <= lastpit) {
+               Paragraph & par = it.paragraph();
+
+               // iterate over the insets of the current paragraph
+               InsetList const & insets = par.insetList();
+               InsetList::const_iterator iit = insets.begin();
+               InsetList::const_iterator end = insets.end();
+               for (; iit != end; ++iit) {
+                       it.pos() = iit->pos;
+
+                       if (iit->inset->lyxCode() == BRANCH_CODE) {
+                               // get buffer of external file
+                               InsetBranch const & br =
+                                       static_cast<InsetBranch const &>(*iit->inset);
+                               docstring const name = br.branch();
+                               if (!from_master && !params().branchlist().find(name))
+                                       result.push_back(name);
+                               else if (from_master && !masterBuffer()->params().branchlist().find(name))
+                                       result.push_back(name);
+                               continue;
+                       }
+
+                       // is it an external file?
+                       if (iit->inset->lyxCode() == INCLUDE_CODE) {
+                               // get buffer of external file
+                               InsetInclude const & inset =
+                                       static_cast<InsetInclude const &>(*iit->inset);
+                               Buffer * child = inset.getChildBuffer();
+                               if (!child)
+                                       continue;
+                               child->getUsedBranches(result, true);
+                       }
+               }
+               // next paragraph
+               it.pit()++;
+               it.pos() = 0;
+       }
+       // remove duplicates
+       result.unique();
+}
+
+
+void Buffer::renameBranches(docstring const & oldname, docstring const & newname)
+{
+       // Iterate over buffer, starting with first paragraph
+       // The scope must be bigger than any lookup DocIterator
+       // later. For the global lookup, lastpit+1 is used, hence
+       // we use lastpit+2 here.
+       DocIterator it = par_iterator_begin();
+       DocIterator scope = it;
+       scope.pit() = scope.lastpit() + 2;
+       pit_type lastpit = it.lastpit();
+
+       while (it.pit() <= lastpit) {
+               Paragraph & par = it.paragraph();
+
+               // iterate over the insets of the current paragraph
+               InsetList const & insets = par.insetList();
+               InsetList::const_iterator iit = insets.begin();
+               InsetList::const_iterator end = insets.end();
+               for (; iit != end; ++iit) {
+                       it.pos() = iit->pos;
+
+                       if (iit->inset->lyxCode() == BRANCH_CODE) {
+                               // get buffer of external file
+                               InsetBranch & br =
+                                       static_cast<InsetBranch &>(*iit->inset);
+                               if (br.branch() == oldname)
+                                       br.rename(newname);
+                               continue;
+                       }
+
+                       // is it an external file?
+                       if (iit->inset->lyxCode() == INCLUDE_CODE) {
+                               // get buffer of external file
+                               InsetInclude const & inset =
+                                       static_cast<InsetInclude const &>(*iit->inset);
+                               Buffer * child = inset.getChildBuffer();
+                               if (!child)
+                                       continue;
+                               child->renameBranches(oldname, newname);
+                       }
+               }
+               // next paragraph
+               it.pit()++;
+               it.pos() = 0;
+       }
+}
+
+
 void Buffer::updateMacroInstances() const
 {
        LYXERR(Debug::MACROS, "updateMacroInstances for "
@@ -2417,9 +2629,9 @@ void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
        // 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 (isDocBook())
@@ -2441,14 +2653,16 @@ void Buffer::getSourceCode(odocstream & os, pit_type par_begin,
                                        convert<docstring>(par_end - 1))
                           << "\n\n";
                }
-               d->texrow.newline();
-               d->texrow.newline();
+               TexRow texrow;
+               texrow.reset();
+               texrow.newline();
+               texrow.newline();
                // output paragraphs
                if (isDocBook())
                        docbookParagraphs(paragraphs(), *this, os, runparams);
                else 
                        // latex or literate
-                       latexParagraphs(*this, text(), os, d->texrow, runparams);
+                       latexParagraphs(*this, text(), os, texrow, runparams);
        }
 }
 
@@ -2479,10 +2693,10 @@ void Buffer::structureChanged() const
 }
 
 
-void Buffer::errors(string const & err) const
+void Buffer::errors(string const & err, bool from_master) const
 {
        if (gui_)
-               gui_->errors(err);
+               gui_->errors(err, from_master);
 }
 
 
@@ -2660,15 +2874,14 @@ void Buffer::autoSave() const
 
 string Buffer::bufferFormat() const
 {
-       if (isDocBook())
-               return "docbook";
-       if (isLiterate())
-               return "literate";
-       if (params().useXetex)
-               return "xetex";
-       if (params().encoding().package() == Encoding::japanese)
-               return "platex";
-       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;
 }
 
 
@@ -2739,6 +2952,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
@@ -2771,8 +2986,14 @@ bool Buffer::doExport(string const & format, bool put_in_tempdir,
                tmp_result_file, FileName(absFileName()), backend_format, format,
                error_list);
        // Emit the signal to show the error list.
-       if (format != backend_format)
+       if (format != backend_format) {
                errors(error_type);
+               // also to the children, in case of master-buffer-view
+               std::vector<Buffer *> clist = getChildren();
+               for (vector<Buffer *>::const_iterator cit = clist.begin();
+                    cit != clist.end(); ++cit)
+                       (*cit)->errors(error_type, true);
+       }
        if (!success)
                return false;
 
@@ -2868,6 +3089,7 @@ vector<string> Buffer::backends() const
                        v.push_back("pdflatex");
        }
        v.push_back("text");
+       v.push_back("xhtml");
        v.push_back("lyx");
        return v;
 }
@@ -3017,7 +3239,7 @@ void Buffer::updateLabels(UpdateScope scope) const
                        // 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();     
+                               structureChanged();
 
                        // was buf referenced from the master (i.e. not in bufToUpdate anymore)?
                        if (bufToUpdate.find(this) == bufToUpdate.end())
@@ -3142,7 +3364,8 @@ static void setLabel(Buffer const & buf, ParIterator & it)
        // Compute the item depth of the paragraph
        par.itemdepth = getItemDepth(it);
 
-       if (layout.margintype == MARGIN_MANUAL) {
+       if (layout.margintype == MARGIN_MANUAL
+           || layout.latextype == LATEX_BIB_ENVIRONMENT) {
                if (par.params().labelWidthString().empty())
                        par.params().labelWidthString(par.translateIfPossible(layout.labelstring(), bp));
        } else {
@@ -3187,9 +3410,7 @@ static void setLabel(Buffer const & buf, ParIterator & it)
        }
 
        case LABEL_ENUMERATE: {
-               // FIXME: Yes I know this is a really, really! bad solution
-               // (Lgb)
-               docstring enumcounter = from_ascii("enum");
+               docstring enumcounter = layout.counter.empty() ? from_ascii("enum") : layout.counter;
 
                switch (par.itemdepth) {
                case 2:
@@ -3210,31 +3431,9 @@ static void setLabel(Buffer const & buf, ParIterator & it)
                // 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), bp)));
+               par.params().labelString(counters.theCounter(enumcounter));
 
                break;
        }
@@ -3305,77 +3504,27 @@ void Buffer::updateLabels(ParIterator & parit) const
 }
 
 
-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();
+       WordLangTuple wl;
        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 = WordLangTuple();
+       // OK, we start from here.
+       DocIterator const end = doc_iterator_end(this);
+       for (; from != end; from.forwardPos()) {
+               // We are only interested in text so remove the math CursorSlice.
+               while (from.inMathed())
+                       from.forwardInset();
+               to = from;
+               if (from.paragraph().spellCheck(from.pos(), to.pos(), wl, suggestions)) {
                        word_lang = wl;
                        break;
                }
                from = to;
+               ++progress;
        }
-       while (!(word = speller->nextMiss()).empty())
-               suggestions.push_back(word);
        return progress;
 }