3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Stefan Schimanski
9 * Full author contact details are available in file CREDITS.
17 #include "LayoutFile.h"
18 #include "BiblioInfo.h"
19 #include "BranchList.h"
20 #include "buffer_funcs.h"
21 #include "BufferList.h"
22 #include "BufferParams.h"
25 #include "Converter.h"
27 #include "DispatchResult.h"
28 #include "DocIterator.h"
30 #include "ErrorList.h"
33 #include "FuncRequest.h"
34 #include "FuncStatus.h"
35 #include "IndicesList.h"
36 #include "InsetIterator.h"
37 #include "InsetList.h"
39 #include "LaTeXFeatures.h"
43 #include "LyXAction.h"
47 #include "output_docbook.h"
49 #include "output_latex.h"
50 #include "output_plaintext.h"
51 #include "paragraph_funcs.h"
52 #include "Paragraph.h"
53 #include "ParagraphParameters.h"
54 #include "ParIterator.h"
55 #include "PDFOptions.h"
56 #include "SpellChecker.h"
59 #include "TexStream.h"
61 #include "TextClass.h"
62 #include "TocBackend.h"
64 #include "VCBackend.h"
66 #include "WordLangTuple.h"
69 #include "insets/InsetBibitem.h"
70 #include "insets/InsetBibtex.h"
71 #include "insets/InsetInclude.h"
72 #include "insets/InsetText.h"
74 #include "mathed/MacroTable.h"
75 #include "mathed/MathMacroTemplate.h"
76 #include "mathed/MathSupport.h"
78 #include "frontends/alert.h"
79 #include "frontends/Delegates.h"
80 #include "frontends/WorkAreaManager.h"
82 #include "graphics/Previews.h"
84 #include "support/lassert.h"
85 #include "support/convert.h"
86 #include "support/debug.h"
87 #include "support/docstring_list.h"
88 #include "support/ExceptionMessage.h"
89 #include "support/FileName.h"
90 #include "support/FileNameList.h"
91 #include "support/filetools.h"
92 #include "support/ForkedCalls.h"
93 #include "support/gettext.h"
94 #include "support/gzstream.h"
95 #include "support/lstrings.h"
96 #include "support/lyxalgo.h"
97 #include "support/os.h"
98 #include "support/Package.h"
99 #include "support/Path.h"
100 #include "support/Systemcall.h"
101 #include "support/textutils.h"
102 #include "support/types.h"
104 #include <boost/bind.hpp>
105 #include <boost/shared_ptr.hpp>
117 using namespace lyx::support
;
121 namespace Alert
= frontend::Alert
;
122 namespace os
= support::os
;
126 // Do not remove the comment below, so we get merge conflict in
127 // independent branches. Instead add your own.
128 int const LYX_FORMAT
= 360; // jspitzm: nomencl custom width
130 typedef map
<string
, bool> DepClean
;
131 typedef map
<docstring
, pair
<InsetLabel
const *, Buffer::References
> > RefCache
;
133 void showPrintError(string
const & name
)
135 docstring str
= bformat(_("Could not print the document %1$s.\n"
136 "Check that your printer is set up correctly."),
137 makeDisplayPath(name
, 50));
138 Alert::error(_("Print document failed"), str
);
143 class BufferSet
: public std::set
<Buffer
const *> {};
148 Impl(Buffer
& parent
, FileName
const & file
, bool readonly
);
162 mutable TexRow texrow
;
164 /// need to regenerate .tex?
168 mutable bool lyx_clean
;
170 /// is autosave needed?
171 mutable bool bak_clean
;
173 /// is this a unnamed file (New...)?
179 /// name of the file the buffer is associated with.
182 /** Set to true only when the file is fully loaded.
183 * Used to prevent the premature generation of previews
184 * and by the citation inset.
186 bool file_fully_loaded
;
189 mutable TocBackend toc_backend
;
192 typedef pair
<DocIterator
, MacroData
> ScopeMacro
;
193 typedef map
<DocIterator
, ScopeMacro
> PositionScopeMacroMap
;
194 typedef map
<docstring
, PositionScopeMacroMap
> NamePositionScopeMacroMap
;
195 /// map from the macro name to the position map,
196 /// which maps the macro definition position to the scope and the MacroData.
197 NamePositionScopeMacroMap macros
;
200 /// positions of child buffers in the buffer
201 typedef map
<Buffer
const * const, DocIterator
> BufferPositionMap
;
202 typedef pair
<DocIterator
, Buffer
const *> ScopeBuffer
;
203 typedef map
<DocIterator
, ScopeBuffer
> PositionScopeBufferMap
;
204 /// position of children buffers in this buffer
205 BufferPositionMap children_positions
;
206 /// map from children inclusion positions to their scope and their buffer
207 PositionScopeBufferMap position_to_children
;
209 /// Container for all sort of Buffer dependant errors.
210 map
<string
, ErrorList
> errorLists
;
212 /// timestamp and checksum used to test if the file has been externally
213 /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
215 unsigned long checksum_
;
218 frontend::WorkAreaManager
* wa_
;
223 /// A cache for the bibfiles (including bibfiles of loaded child
224 /// documents), needed for appropriate update of natbib labels.
225 mutable support::FileNameList bibfilesCache_
;
227 // FIXME The caching mechanism could be improved. At present, we have a
228 // cache for each Buffer, that caches all the bibliography info for that
229 // Buffer. A more efficient solution would be to have a global cache per
230 // file, and then to construct the Buffer's bibinfo from that.
231 /// A cache for bibliography info
232 mutable BiblioInfo bibinfo_
;
233 /// whether the bibinfo cache is valid
234 bool bibinfoCacheValid_
;
235 /// Cache of timestamps of .bib files
236 map
<FileName
, time_t> bibfileStatus_
;
238 mutable RefCache ref_cache_
;
240 /// our Text that should be wrapped in an InsetText
243 /// This is here to force the test to be done whenever parent_buffer
245 Buffer
const * parent() const {
246 // if parent_buffer is not loaded, then it has been unloaded,
247 // which means that parent_buffer is an invalid pointer. So we
248 // set it to null in that case.
249 if (!theBufferList().isLoaded(parent_buffer
))
251 return parent_buffer
;
254 void setParent(Buffer
const * pb
) { parent_buffer
= pb
; }
256 /// So we can force access via the accessors.
257 mutable Buffer
const * parent_buffer
;
261 /// Creates the per buffer temporary directory
262 static FileName
createBufferTmpDir()
265 // We are in our own directory. Why bother to mangle name?
266 // In fact I wrote this code to circumvent a problematic behaviour
267 // (bug?) of EMX mkstemp().
268 FileName
tmpfl(package().temp_dir().absFilename() + "/lyx_tmpbuf" +
269 convert
<string
>(count
++));
271 if (!tmpfl
.createDirectory(0777)) {
272 throw ExceptionMessage(WarningException
, _("Disk Error: "), bformat(
273 _("LyX could not create the temporary directory '%1$s' (Disk is full maybe?)"),
274 from_utf8(tmpfl
.absFilename())));
280 Buffer::Impl::Impl(Buffer
& parent
, FileName
const & file
, bool readonly_
)
281 : lyx_clean(true), bak_clean(true), unnamed(false),
282 read_only(readonly_
), filename(file
), file_fully_loaded(false),
283 toc_backend(&parent
), macro_lock(false), timestamp_(0),
284 checksum_(0), wa_(0), undo_(parent
), bibinfoCacheValid_(false),
287 temppath
= createBufferTmpDir();
288 lyxvc
.setBuffer(&parent
);
290 wa_
= new frontend::WorkAreaManager
;
294 Buffer::Buffer(string
const & file
, bool readonly
)
295 : d(new Impl(*this, FileName(file
), readonly
)), gui_(0)
297 LYXERR(Debug::INFO
, "Buffer::Buffer()");
299 d
->inset
= new InsetText(*this);
300 d
->inset
->setAutoBreakRows(true);
301 d
->inset
->getText(0)->setMacrocontextPosition(par_iterator_begin());
307 LYXERR(Debug::INFO
, "Buffer::~Buffer()");
308 // here the buffer should take care that it is
309 // saved properly, before it goes into the void.
311 // GuiView already destroyed
314 if (d
->unnamed
&& d
->filename
.extension() == "internal") {
315 // No need to do additional cleanups for internal buffer.
320 // loop over children
321 Impl::BufferPositionMap::iterator it
= d
->children_positions
.begin();
322 Impl::BufferPositionMap::iterator end
= d
->children_positions
.end();
323 for (; it
!= end
; ++it
) {
324 Buffer
* child
= const_cast<Buffer
*>(it
->first
);
325 // The child buffer might have been closed already.
326 if (theBufferList().isLoaded(child
))
327 theBufferList().releaseChild(this, child
);
330 // clear references to children in macro tables
331 d
->children_positions
.clear();
332 d
->position_to_children
.clear();
334 if (!d
->temppath
.destroyDirectory()) {
335 Alert::warning(_("Could not remove temporary directory"),
336 bformat(_("Could not remove the temporary directory %1$s"),
337 from_utf8(d
->temppath
.absFilename())));
340 // Remove any previewed LaTeX snippets associated with this buffer.
341 thePreviews().removeLoader(*this);
347 void Buffer::changed() const
354 frontend::WorkAreaManager
& Buffer::workAreaManager() const
356 LASSERT(d
->wa_
, /**/);
361 Text
& Buffer::text() const
363 return d
->inset
->text();
367 Inset
& Buffer::inset() const
373 BufferParams
& Buffer::params()
379 BufferParams
const & Buffer::params() const
385 ParagraphList
& Buffer::paragraphs()
387 return text().paragraphs();
391 ParagraphList
const & Buffer::paragraphs() const
393 return text().paragraphs();
397 LyXVC
& Buffer::lyxvc()
403 LyXVC
const & Buffer::lyxvc() const
409 string
const Buffer::temppath() const
411 return d
->temppath
.absFilename();
415 TexRow
& Buffer::texrow()
421 TexRow
const & Buffer::texrow() const
427 TocBackend
& Buffer::tocBackend() const
429 return d
->toc_backend
;
433 Undo
& Buffer::undo()
439 string
Buffer::latexName(bool const no_path
) const
441 FileName latex_name
= makeLatexName(d
->filename
);
442 return no_path
? latex_name
.onlyFileName()
443 : latex_name
.absFilename();
447 string
Buffer::logName(LogType
* type
) const
449 string
const filename
= latexName(false);
451 if (filename
.empty()) {
457 string
const path
= temppath();
459 FileName
const fname(addName(temppath(),
460 onlyFilename(changeExtension(filename
,
462 FileName
const bname(
463 addName(path
, onlyFilename(
464 changeExtension(filename
,
465 formats
.extension("literate") + ".out"))));
467 // If no Latex log or Build log is newer, show Build log
469 if (bname
.exists() &&
470 (!fname
.exists() || fname
.lastModified() < bname
.lastModified())) {
471 LYXERR(Debug::FILES
, "Log name calculated as: " << bname
);
474 return bname
.absFilename();
476 LYXERR(Debug::FILES
, "Log name calculated as: " << fname
);
479 return fname
.absFilename();
483 void Buffer::setReadonly(bool const flag
)
485 if (d
->read_only
!= flag
) {
492 void Buffer::setFileName(string
const & newfile
)
494 d
->filename
= makeAbsPath(newfile
);
495 setReadonly(d
->filename
.isReadOnly());
500 int Buffer::readHeader(Lexer
& lex
)
502 int unknown_tokens
= 0;
504 int begin_header_line
= -1;
506 // Initialize parameters that may be/go lacking in header:
507 params().branchlist().clear();
508 params().preamble
.erase();
509 params().options
.erase();
510 params().master
.erase();
511 params().float_placement
.erase();
512 params().paperwidth
.erase();
513 params().paperheight
.erase();
514 params().leftmargin
.erase();
515 params().rightmargin
.erase();
516 params().topmargin
.erase();
517 params().bottommargin
.erase();
518 params().headheight
.erase();
519 params().headsep
.erase();
520 params().footskip
.erase();
521 params().columnsep
.erase();
522 params().fontsCJK
.erase();
523 params().listings_params
.clear();
524 params().clearLayoutModules();
525 params().clearRemovedModules();
526 params().pdfoptions().clear();
527 params().indiceslist().clear();
528 params().backgroundcolor
= lyx::rgbFromHexName("#ffffff");
530 for (int i
= 0; i
< 4; ++i
) {
531 params().user_defined_bullet(i
) = ITEMIZE_DEFAULTS
[i
];
532 params().temp_bullet(i
) = ITEMIZE_DEFAULTS
[i
];
535 ErrorList
& errorList
= d
->errorLists
["Parse"];
544 if (token
== "\\end_header")
548 if (token
== "\\begin_header") {
549 begin_header_line
= line
;
553 LYXERR(Debug::PARSER
, "Handling document header token: `"
556 string unknown
= params().readToken(lex
, token
, d
->filename
.onlyPath());
557 if (!unknown
.empty()) {
558 if (unknown
[0] != '\\' && token
== "\\textclass") {
559 Alert::warning(_("Unknown document class"),
560 bformat(_("Using the default document class, because the "
561 "class %1$s is unknown."), from_utf8(unknown
)));
564 docstring
const s
= bformat(_("Unknown token: "
568 errorList
.push_back(ErrorItem(_("Document header error"),
573 if (begin_header_line
) {
574 docstring
const s
= _("\\begin_header is missing");
575 errorList
.push_back(ErrorItem(_("Document header error"),
579 params().makeDocumentClass();
581 return unknown_tokens
;
586 // changed to be public and have one parameter
587 // Returns true if "\end_document" is not read (Asger)
588 bool Buffer::readDocument(Lexer
& lex
)
590 ErrorList
& errorList
= d
->errorLists
["Parse"];
593 if (!lex
.checkFor("\\begin_document")) {
594 docstring
const s
= _("\\begin_document is missing");
595 errorList
.push_back(ErrorItem(_("Document header error"),
599 // we are reading in a brand new document
600 LASSERT(paragraphs().empty(), /**/);
604 if (params().outputChanges
) {
605 bool dvipost
= LaTeXFeatures::isAvailable("dvipost");
606 bool xcolorulem
= LaTeXFeatures::isAvailable("ulem") &&
607 LaTeXFeatures::isAvailable("xcolor");
609 if (!dvipost
&& !xcolorulem
) {
610 Alert::warning(_("Changes not shown in LaTeX output"),
611 _("Changes will not be highlighted in LaTeX output, "
612 "because neither dvipost nor xcolor/ulem are installed.\n"
613 "Please install these packages or redefine "
614 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
615 } else if (!xcolorulem
) {
616 Alert::warning(_("Changes not shown in LaTeX output"),
617 _("Changes will not be highlighted in LaTeX output "
618 "when using pdflatex, because xcolor and ulem are not installed.\n"
619 "Please install both packages or redefine "
620 "\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
624 if (!params().master
.empty()) {
625 FileName
const master_file
= makeAbsPath(params().master
,
626 onlyPath(absFileName()));
627 if (isLyXFilename(master_file
.absFilename())) {
629 checkAndLoadLyXFile(master_file
, true);
631 // necessary e.g. after a reload
632 // to re-register the child (bug 5873)
633 // FIXME: clean up updateMacros (here, only
634 // child registering is needed).
635 master
->updateMacros();
636 // set master as master buffer, but only
637 // if we are a real child
638 if (master
->isChild(this))
640 // if the master is not fully loaded
641 // it is probably just loading this
642 // child. No warning needed then.
643 else if (master
->isFullyLoaded())
644 LYXERR0("The master '"
646 << "' assigned to this document ("
648 << ") does not include "
649 "this document. Ignoring the master assignment.");
655 bool const res
= text().read(*this, lex
, errorList
, d
->inset
);
658 updateMacroInstances();
663 // needed to insert the selection
664 void Buffer::insertStringAsLines(ParagraphList
& pars
,
665 pit_type
& pit
, pos_type
& pos
,
666 Font
const & fn
, docstring
const & str
, bool autobreakrows
)
670 // insert the string, don't insert doublespace
671 bool space_inserted
= true;
672 for (docstring::const_iterator cit
= str
.begin();
673 cit
!= str
.end(); ++cit
) {
674 Paragraph
& par
= pars
[pit
];
676 if (autobreakrows
&& (!par
.empty() || par
.allowEmpty())) {
677 breakParagraph(params(), pars
, pit
, pos
,
678 par
.layout().isEnvironment());
681 space_inserted
= true;
685 // do not insert consecutive spaces if !free_spacing
686 } else if ((*cit
== ' ' || *cit
== '\t') &&
687 space_inserted
&& !par
.isFreeSpacing()) {
689 } else if (*cit
== '\t') {
690 if (!par
.isFreeSpacing()) {
691 // tabs are like spaces here
692 par
.insertChar(pos
, ' ', font
, params().trackChanges
);
694 space_inserted
= true;
696 par
.insertChar(pos
, *cit
, font
, params().trackChanges
);
698 space_inserted
= true;
700 } else if (!isPrintable(*cit
)) {
701 // Ignore unprintables
704 // just insert the character
705 par
.insertChar(pos
, *cit
, font
, params().trackChanges
);
707 space_inserted
= (*cit
== ' ');
714 bool Buffer::readString(string
const & s
)
716 params().compressed
= false;
718 // remove dummy empty par
719 paragraphs().clear();
723 FileName
const name
= FileName::tempName("Buffer_readString");
724 switch (readFile(lex
, name
, true)) {
728 // We need to call lyx2lyx, so write the input to a file
729 ofstream
os(name
.toFilesystemEncoding().c_str());
732 return readFile(name
);
742 bool Buffer::readFile(FileName
const & filename
)
744 FileName
fname(filename
);
746 params().compressed
= fname
.isZippedFile();
748 // remove dummy empty par
749 paragraphs().clear();
752 if (readFile(lex
, fname
) != success
)
759 bool Buffer::isFullyLoaded() const
761 return d
->file_fully_loaded
;
765 void Buffer::setFullyLoaded(bool value
)
767 d
->file_fully_loaded
= value
;
771 Buffer::ReadStatus
Buffer::readFile(Lexer
& lex
, FileName
const & filename
,
774 LASSERT(!filename
.empty(), /**/);
776 // the first (non-comment) token _must_ be...
777 if (!lex
.checkFor("\\lyxformat")) {
778 Alert::error(_("Document format failure"),
779 bformat(_("%1$s is not a readable LyX document."),
780 from_utf8(filename
.absFilename())));
786 //lyxerr << "LyX Format: `" << tmp_format << '\'' << endl;
787 // if present remove ".," from string.
788 size_t dot
= tmp_format
.find_first_of(".,");
789 //lyxerr << " dot found at " << dot << endl;
790 if (dot
!= string::npos
)
791 tmp_format
.erase(dot
, 1);
792 int const file_format
= convert
<int>(tmp_format
);
793 //lyxerr << "format: " << file_format << endl;
795 // save timestamp and checksum of the original disk file, making sure
796 // to not overwrite them with those of the file created in the tempdir
797 // when it has to be converted to the current format.
799 // Save the timestamp and checksum of disk file. If filename is an
800 // emergency file, save the timestamp and checksum of the original lyx file
801 // because isExternallyModified will check for this file. (BUG4193)
802 string diskfile
= filename
.absFilename();
803 if (suffixIs(diskfile
, ".emergency"))
804 diskfile
= diskfile
.substr(0, diskfile
.size() - 10);
805 saveCheckSum(FileName(diskfile
));
808 if (file_format
!= LYX_FORMAT
) {
811 // lyx2lyx would fail
814 FileName
const tmpfile
= FileName::tempName("Buffer_readFile");
815 if (tmpfile
.empty()) {
816 Alert::error(_("Conversion failed"),
817 bformat(_("%1$s is from a different"
818 " version of LyX, but a temporary"
819 " file for converting it could"
821 from_utf8(filename
.absFilename())));
824 FileName
const lyx2lyx
= libFileSearch("lyx2lyx", "lyx2lyx");
825 if (lyx2lyx
.empty()) {
826 Alert::error(_("Conversion script not found"),
827 bformat(_("%1$s is from a different"
828 " version of LyX, but the"
829 " conversion script lyx2lyx"
830 " could not be found."),
831 from_utf8(filename
.absFilename())));
834 ostringstream command
;
835 command
<< os::python()
836 << ' ' << quoteName(lyx2lyx
.toFilesystemEncoding())
837 << " -t " << convert
<string
>(LYX_FORMAT
)
838 << " -o " << quoteName(tmpfile
.toFilesystemEncoding())
839 << ' ' << quoteName(filename
.toFilesystemEncoding());
840 string
const command_str
= command
.str();
842 LYXERR(Debug::INFO
, "Running '" << command_str
<< '\'');
844 cmd_ret
const ret
= runCommand(command_str
);
845 if (ret
.first
!= 0) {
846 Alert::error(_("Conversion script failed"),
847 bformat(_("%1$s is from a different version"
848 " of LyX, but the lyx2lyx script"
849 " failed to convert it."),
850 from_utf8(filename
.absFilename())));
853 bool const ret
= readFile(tmpfile
);
854 // Do stuff with tmpfile name and buffer name here.
855 return ret
? success
: failure
;
860 if (readDocument(lex
)) {
861 Alert::error(_("Document format failure"),
862 bformat(_("%1$s ended unexpectedly, which means"
863 " that it is probably corrupted."),
864 from_utf8(filename
.absFilename())));
867 d
->file_fully_loaded
= true;
872 // Should probably be moved to somewhere else: BufferView? LyXView?
873 bool Buffer::save() const
875 // We don't need autosaves in the immediate future. (Asger)
876 resetAutosaveTimers();
878 string
const encodedFilename
= d
->filename
.toFilesystemEncoding();
881 bool madeBackup
= false;
883 // make a backup if the file already exists
884 if (lyxrc
.make_backup
&& fileName().exists()) {
885 backupName
= FileName(absFileName() + '~');
886 if (!lyxrc
.backupdir_path
.empty()) {
887 string
const mangledName
=
888 subst(subst(backupName
.absFilename(), '/', '!'), ':', '!');
889 backupName
= FileName(addName(lyxrc
.backupdir_path
,
892 if (fileName().copyTo(backupName
)) {
895 Alert::error(_("Backup failure"),
896 bformat(_("Cannot create backup file %1$s.\n"
897 "Please check whether the directory exists and is writeable."),
898 from_utf8(backupName
.absFilename())));
899 //LYXERR(Debug::DEBUG, "Fs error: " << fe.what());
903 // ask if the disk file has been externally modified (use checksum method)
904 if (fileName().exists() && isExternallyModified(checksum_method
)) {
905 docstring
const file
= makeDisplayPath(absFileName(), 20);
906 docstring text
= bformat(_("Document %1$s has been externally modified. Are you sure "
907 "you want to overwrite this file?"), file
);
908 int const ret
= Alert::prompt(_("Overwrite modified file?"),
909 text
, 1, 1, _("&Overwrite"), _("&Cancel"));
914 if (writeFile(d
->filename
)) {
918 // Saving failed, so backup is not backup
920 backupName
.moveTo(d
->filename
);
926 bool Buffer::writeFile(FileName
const & fname
) const
928 if (d
->read_only
&& fname
== d
->filename
)
933 docstring
const str
= bformat(_("Saving document %1$s..."),
934 makeDisplayPath(fname
.absFilename()));
937 if (params().compressed
) {
938 gz::ogzstream
ofs(fname
.toFilesystemEncoding().c_str(), ios::out
|ios::trunc
);
939 retval
= ofs
&& write(ofs
);
941 ofstream
ofs(fname
.toFilesystemEncoding().c_str(), ios::out
|ios::trunc
);
942 retval
= ofs
&& write(ofs
);
946 message(str
+ _(" could not write file!"));
950 removeAutosaveFile();
952 saveCheckSum(d
->filename
);
953 message(str
+ _(" done."));
959 bool Buffer::write(ostream
& ofs
) const
962 // Use the standard "C" locale for file output.
963 ofs
.imbue(locale::classic());
966 // The top of the file should not be written by params().
968 // write out a comment in the top of the file
969 ofs
<< "#LyX " << lyx_version
970 << " created this file. For more info see http://www.lyx.org/\n"
971 << "\\lyxformat " << LYX_FORMAT
<< "\n"
972 << "\\begin_document\n";
974 /// For each author, set 'used' to true if there is a change
975 /// by this author in the document; otherwise set it to 'false'.
976 AuthorList::Authors::const_iterator a_it
= params().authors().begin();
977 AuthorList::Authors::const_iterator a_end
= params().authors().end();
978 for (; a_it
!= a_end
; ++a_it
)
979 a_it
->second
.setUsed(false);
981 ParIterator
const end
= const_cast<Buffer
*>(this)->par_iterator_end();
982 ParIterator it
= const_cast<Buffer
*>(this)->par_iterator_begin();
983 for ( ; it
!= end
; ++it
)
984 it
->checkAuthors(params().authors());
986 // now write out the buffer parameters.
987 ofs
<< "\\begin_header\n";
988 params().writeFile(ofs
);
989 ofs
<< "\\end_header\n";
992 ofs
<< "\n\\begin_body\n";
993 text().write(*this, ofs
);
994 ofs
<< "\n\\end_body\n";
996 // Write marker that shows file is complete
997 ofs
<< "\\end_document" << endl
;
999 // Shouldn't really be needed....
1002 // how to check if close went ok?
1003 // Following is an attempt... (BE 20001011)
1005 // good() returns false if any error occured, including some
1006 // formatting error.
1007 // bad() returns true if something bad happened in the buffer,
1008 // which should include file system full errors.
1013 lyxerr
<< "File was not closed properly." << endl
;
1020 bool Buffer::makeLaTeXFile(FileName
const & fname
,
1021 string
const & original_path
,
1022 OutputParams
const & runparams_in
,
1023 bool output_preamble
, bool output_body
) const
1025 OutputParams runparams
= runparams_in
;
1026 if (params().useXetex
)
1027 runparams
.flavor
= OutputParams::XETEX
;
1029 string
const encoding
= runparams
.encoding
->iconvName();
1030 LYXERR(Debug::LATEX
, "makeLaTeXFile encoding: " << encoding
<< "...");
1033 try { ofs
.reset(encoding
); }
1034 catch (iconv_codecvt_facet_exception
& e
) {
1035 lyxerr
<< "Caught iconv exception: " << e
.what() << endl
;
1036 Alert::error(_("Iconv software exception Detected"), bformat(_("Please "
1037 "verify that the support software for your encoding (%1$s) is "
1038 "properly installed"), from_ascii(encoding
)));
1041 if (!openFileWrite(ofs
, fname
))
1044 //TexStream ts(ofs.rdbuf(), &texrow());
1045 ErrorList
& errorList
= d
->errorLists
["Export"];
1047 bool failed_export
= false;
1050 writeLaTeXSource(ofs
, original_path
,
1051 runparams
, output_preamble
, output_body
);
1053 catch (EncodingException
& e
) {
1054 odocstringstream ods
;
1055 ods
.put(e
.failed_char
);
1057 oss
<< "0x" << hex
<< e
.failed_char
<< dec
;
1058 docstring msg
= bformat(_("Could not find LaTeX command for character '%1$s'"
1059 " (code point %2$s)"),
1060 ods
.str(), from_utf8(oss
.str()));
1061 errorList
.push_back(ErrorItem(msg
, _("Some characters of your document are probably not "
1062 "representable in the chosen encoding.\n"
1063 "Changing the document encoding to utf8 could help."),
1064 e
.par_id
, e
.pos
, e
.pos
+ 1));
1065 failed_export
= true;
1067 catch (iconv_codecvt_facet_exception
& e
) {
1068 errorList
.push_back(ErrorItem(_("iconv conversion failed"),
1069 _(e
.what()), -1, 0, 0));
1070 failed_export
= true;
1072 catch (exception
const & e
) {
1073 errorList
.push_back(ErrorItem(_("conversion failed"),
1074 _(e
.what()), -1, 0, 0));
1075 failed_export
= true;
1078 lyxerr
<< "Caught some really weird exception..." << endl
;
1084 failed_export
= true;
1085 lyxerr
<< "File '" << fname
<< "' was not closed properly." << endl
;
1089 return !failed_export
;
1093 void Buffer::writeLaTeXSource(odocstream
& os
,
1094 string
const & original_path
,
1095 OutputParams
const & runparams_in
,
1096 bool const output_preamble
, bool const output_body
) const
1098 // The child documents, if any, shall be already loaded at this point.
1100 OutputParams runparams
= runparams_in
;
1102 // Classify the unicode characters appearing in math insets
1103 Encodings::initUnicodeMath(*this);
1105 // validate the buffer.
1106 LYXERR(Debug::LATEX
, " Validating buffer...");
1107 LaTeXFeatures
features(*this, params(), runparams
);
1109 LYXERR(Debug::LATEX
, " Buffer validation done.");
1111 // The starting paragraph of the coming rows is the
1112 // first paragraph of the document. (Asger)
1113 if (output_preamble
&& runparams
.nice
) {
1114 os
<< "%% LyX " << lyx_version
<< " created this file. "
1115 "For more info, see http://www.lyx.org/.\n"
1116 "%% Do not edit unless you really know what "
1118 d
->texrow
.newline();
1119 d
->texrow
.newline();
1121 LYXERR(Debug::INFO
, "lyx document header finished");
1123 // Don't move this behind the parent_buffer=0 code below,
1124 // because then the macros will not get the right "redefinition"
1125 // flag as they don't see the parent macros which are output before.
1128 // fold macros if possible, still with parent buffer as the
1129 // macros will be put in the prefix anyway.
1130 updateMacroInstances();
1132 // There are a few differences between nice LaTeX and usual files:
1133 // usual is \batchmode and has a
1134 // special input@path to allow the including of figures
1135 // with either \input or \includegraphics (what figinsets do).
1136 // input@path is set when the actual parameter
1137 // original_path is set. This is done for usual tex-file, but not
1138 // for nice-latex-file. (Matthias 250696)
1139 // Note that input@path is only needed for something the user does
1140 // in the preamble, included .tex files or ERT, files included by
1141 // LyX work without it.
1142 if (output_preamble
) {
1143 if (!runparams
.nice
) {
1144 // code for usual, NOT nice-latex-file
1145 os
<< "\\batchmode\n"; // changed
1146 // from \nonstopmode
1147 d
->texrow
.newline();
1149 if (!original_path
.empty()) {
1151 // We don't know the encoding of inputpath
1152 docstring
const inputpath
= from_utf8(latex_path(original_path
));
1153 os
<< "\\makeatletter\n"
1154 << "\\def\\input@path{{"
1155 << inputpath
<< "/}}\n"
1156 << "\\makeatother\n";
1157 d
->texrow
.newline();
1158 d
->texrow
.newline();
1159 d
->texrow
.newline();
1162 // get parent macros (if this buffer has a parent) which will be
1163 // written at the document begin further down.
1164 MacroSet parentMacros
;
1165 listParentMacros(parentMacros
, features
);
1167 // Write the preamble
1168 runparams
.use_babel
= params().writeLaTeX(os
, features
, d
->texrow
);
1170 runparams
.use_japanese
= features
.isRequired("japanese");
1176 os
<< "\\begin{document}\n";
1177 d
->texrow
.newline();
1179 // output the parent macros
1180 MacroSet::iterator it
= parentMacros
.begin();
1181 MacroSet::iterator end
= parentMacros
.end();
1182 for (; it
!= end
; ++it
)
1183 (*it
)->write(os
, true);
1184 } // output_preamble
1186 d
->texrow
.start(paragraphs().begin()->id(), 0);
1188 LYXERR(Debug::INFO
, "preamble finished, now the body.");
1190 // if we are doing a real file with body, even if this is the
1191 // child of some other buffer, let's cut the link here.
1192 // This happens for example if only a child document is printed.
1193 Buffer
const * save_parent
= 0;
1194 if (output_preamble
) {
1195 save_parent
= d
->parent();
1200 latexParagraphs(*this, text(), os
, d
->texrow
, runparams
);
1202 // Restore the parenthood if needed
1203 if (output_preamble
)
1204 d
->setParent(save_parent
);
1206 // add this just in case after all the paragraphs
1208 d
->texrow
.newline();
1210 if (output_preamble
) {
1211 os
<< "\\end{document}\n";
1212 d
->texrow
.newline();
1213 LYXERR(Debug::LATEX
, "makeLaTeXFile...done");
1215 LYXERR(Debug::LATEX
, "LaTeXFile for inclusion made.");
1217 runparams_in
.encoding
= runparams
.encoding
;
1219 // Just to be sure. (Asger)
1220 d
->texrow
.newline();
1222 LYXERR(Debug::INFO
, "Finished making LaTeX file.");
1223 LYXERR(Debug::INFO
, "Row count was " << d
->texrow
.rows() - 1 << '.');
1227 bool Buffer::isLatex() const
1229 return params().documentClass().outputType() == LATEX
;
1233 bool Buffer::isLiterate() const
1235 return params().documentClass().outputType() == LITERATE
;
1239 bool Buffer::isDocBook() const
1241 return params().documentClass().outputType() == DOCBOOK
;
1245 void Buffer::makeDocBookFile(FileName
const & fname
,
1246 OutputParams
const & runparams
,
1247 bool const body_only
) const
1249 LYXERR(Debug::LATEX
, "makeDocBookFile...");
1252 if (!openFileWrite(ofs
, fname
))
1255 writeDocBookSource(ofs
, fname
.absFilename(), runparams
, body_only
);
1259 lyxerr
<< "File '" << fname
<< "' was not closed properly." << endl
;
1263 void Buffer::writeDocBookSource(odocstream
& os
, string
const & fname
,
1264 OutputParams
const & runparams
,
1265 bool const only_body
) const
1267 LaTeXFeatures
features(*this, params(), runparams
);
1272 DocumentClass
const & tclass
= params().documentClass();
1273 string
const top_element
= tclass
.latexname();
1276 if (runparams
.flavor
== OutputParams::XML
)
1277 os
<< "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1280 os
<< "<!DOCTYPE " << from_ascii(top_element
) << ' ';
1283 if (! tclass
.class_header().empty())
1284 os
<< from_ascii(tclass
.class_header());
1285 else if (runparams
.flavor
== OutputParams::XML
)
1286 os
<< "PUBLIC \"-//OASIS//DTD DocBook XML//EN\" "
1287 << "\"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd\"";
1289 os
<< " PUBLIC \"-//OASIS//DTD DocBook V4.2//EN\"";
1291 docstring preamble
= from_utf8(params().preamble
);
1292 if (runparams
.flavor
!= OutputParams::XML
) {
1293 preamble
+= "<!ENTITY % output.print.png \"IGNORE\">\n";
1294 preamble
+= "<!ENTITY % output.print.pdf \"IGNORE\">\n";
1295 preamble
+= "<!ENTITY % output.print.eps \"IGNORE\">\n";
1296 preamble
+= "<!ENTITY % output.print.bmp \"IGNORE\">\n";
1299 string
const name
= runparams
.nice
1300 ? changeExtension(absFileName(), ".sgml") : fname
;
1301 preamble
+= features
.getIncludedFiles(name
);
1302 preamble
+= features
.getLyXSGMLEntities();
1304 if (!preamble
.empty()) {
1305 os
<< "\n [ " << preamble
<< " ]";
1310 string top
= top_element
;
1312 if (runparams
.flavor
== OutputParams::XML
)
1313 top
+= params().language
->code();
1315 top
+= params().language
->code().substr(0, 2);
1318 if (!params().options
.empty()) {
1320 top
+= params().options
;
1323 os
<< "<!-- " << ((runparams
.flavor
== OutputParams::XML
)? "XML" : "SGML")
1324 << " file was created by LyX " << lyx_version
1325 << "\n See http://www.lyx.org/ for more information -->\n";
1327 params().documentClass().counters().reset();
1331 sgml::openTag(os
, top
);
1333 docbookParagraphs(paragraphs(), *this, os
, runparams
);
1334 sgml::closeTag(os
, top_element
);
1338 // chktex should be run with these flags disabled: 3, 22, 25, 30, 38(?)
1339 // Other flags: -wall -v0 -x
1340 int Buffer::runChktex()
1344 // get LaTeX-Filename
1345 FileName
const path(temppath());
1346 string
const name
= addName(path
.absFilename(), latexName());
1347 string
const org_path
= filePath();
1349 PathChanger
p(path
); // path to LaTeX file
1350 message(_("Running chktex..."));
1352 // Generate the LaTeX file if neccessary
1353 OutputParams
runparams(¶ms().encoding());
1354 runparams
.flavor
= OutputParams::LATEX
;
1355 runparams
.nice
= false;
1356 makeLaTeXFile(FileName(name
), org_path
, runparams
);
1359 Chktex
chktex(lyxrc
.chktex_command
, onlyFilename(name
), filePath());
1360 int const res
= chktex
.run(terr
); // run chktex
1363 Alert::error(_("chktex failure"),
1364 _("Could not run chktex successfully."));
1365 } else if (res
> 0) {
1366 ErrorList
& errlist
= d
->errorLists
["ChkTeX"];
1368 bufferErrors(terr
, errlist
);
1379 void Buffer::validate(LaTeXFeatures
& features
) const
1381 params().validate(features
);
1385 for_each(paragraphs().begin(), paragraphs().end(),
1386 boost::bind(&Paragraph::validate
, _1
, boost::ref(features
)));
1388 if (lyxerr
.debugging(Debug::LATEX
)) {
1389 features
.showStruct();
1394 void Buffer::getLabelList(vector
<docstring
> & list
) const
1396 // If this is a child document, use the parent's list instead.
1397 Buffer
const * const pbuf
= d
->parent();
1399 pbuf
->getLabelList(list
);
1404 Toc
& toc
= d
->toc_backend
.toc("label");
1405 TocIterator toc_it
= toc
.begin();
1406 TocIterator end
= toc
.end();
1407 for (; toc_it
!= end
; ++toc_it
) {
1408 if (toc_it
->depth() == 0)
1409 list
.push_back(toc_it
->str());
1414 void Buffer::updateBibfilesCache(UpdateScope scope
) const
1416 // If this is a child document, use the parent's cache instead.
1417 Buffer
const * const pbuf
= d
->parent();
1418 if (pbuf
&& scope
!= UpdateChildOnly
) {
1419 pbuf
->updateBibfilesCache();
1423 d
->bibfilesCache_
.clear();
1424 for (InsetIterator it
= inset_iterator_begin(inset()); it
; ++it
) {
1425 if (it
->lyxCode() == BIBTEX_CODE
) {
1426 InsetBibtex
const & inset
=
1427 static_cast<InsetBibtex
const &>(*it
);
1428 support::FileNameList
const bibfiles
= inset
.getBibFiles();
1429 d
->bibfilesCache_
.insert(d
->bibfilesCache_
.end(),
1432 } else if (it
->lyxCode() == INCLUDE_CODE
) {
1433 InsetInclude
& inset
=
1434 static_cast<InsetInclude
&>(*it
);
1435 inset
.updateBibfilesCache();
1436 support::FileNameList
const & bibfiles
=
1437 inset
.getBibfilesCache();
1438 d
->bibfilesCache_
.insert(d
->bibfilesCache_
.end(),
1443 // the bibinfo cache is now invalid
1444 d
->bibinfoCacheValid_
= false;
1448 void Buffer::invalidateBibinfoCache()
1450 d
->bibinfoCacheValid_
= false;
1454 support::FileNameList
const & Buffer::getBibfilesCache(UpdateScope scope
) const
1456 // If this is a child document, use the parent's cache instead.
1457 Buffer
const * const pbuf
= d
->parent();
1458 if (pbuf
&& scope
!= UpdateChildOnly
)
1459 return pbuf
->getBibfilesCache();
1461 // We update the cache when first used instead of at loading time.
1462 if (d
->bibfilesCache_
.empty())
1463 const_cast<Buffer
*>(this)->updateBibfilesCache(scope
);
1465 return d
->bibfilesCache_
;
1469 BiblioInfo
const & Buffer::masterBibInfo() const
1471 // if this is a child document and the parent is already loaded
1472 // use the parent's list instead [ale990412]
1473 Buffer
const * const tmp
= masterBuffer();
1476 return tmp
->masterBibInfo();
1477 return localBibInfo();
1481 BiblioInfo
const & Buffer::localBibInfo() const
1483 if (d
->bibinfoCacheValid_
) {
1484 support::FileNameList
const & bibfilesCache
= getBibfilesCache();
1485 // compare the cached timestamps with the actual ones.
1486 support::FileNameList::const_iterator ei
= bibfilesCache
.begin();
1487 support::FileNameList::const_iterator en
= bibfilesCache
.end();
1488 for (; ei
!= en
; ++ ei
) {
1489 time_t lastw
= ei
->lastModified();
1490 if (lastw
!= d
->bibfileStatus_
[*ei
]) {
1491 d
->bibinfoCacheValid_
= false;
1492 d
->bibfileStatus_
[*ei
] = lastw
;
1498 if (!d
->bibinfoCacheValid_
) {
1499 d
->bibinfo_
.clear();
1500 for (InsetIterator it
= inset_iterator_begin(inset()); it
; ++it
)
1501 it
->fillWithBibKeys(d
->bibinfo_
, it
);
1502 d
->bibinfoCacheValid_
= true;
1508 bool Buffer::isDepClean(string
const & name
) const
1510 DepClean::const_iterator
const it
= d
->dep_clean
.find(name
);
1511 if (it
== d
->dep_clean
.end())
1517 void Buffer::markDepClean(string
const & name
)
1519 d
->dep_clean
[name
] = true;
1523 bool Buffer::getStatus(FuncRequest
const & cmd
, FuncStatus
& flag
)
1525 switch (cmd
.action
) {
1526 case LFUN_BUFFER_EXPORT
: {
1527 docstring
const arg
= cmd
.argument();
1528 bool enable
= arg
== "custom" || isExportable(to_utf8(arg
));
1530 flag
.message(bformat(
1531 _("Don't know how to export to format: %1$s"), arg
));
1532 flag
.setEnabled(enable
);
1536 case LFUN_BRANCH_ACTIVATE
:
1537 case LFUN_BRANCH_DEACTIVATE
: {
1538 BranchList
const & branchList
= params().branchlist();
1539 docstring
const branchName
= cmd
.argument();
1540 flag
.setEnabled(!branchName
.empty()
1541 && branchList
.find(branchName
));
1545 case LFUN_BUFFER_PRINT
:
1546 // if no Buffer is present, then of course we won't be called!
1547 flag
.setEnabled(true);
1557 void Buffer::dispatch(string
const & command
, DispatchResult
& result
)
1559 return dispatch(lyxaction
.lookupFunc(command
), result
);
1563 // NOTE We can end up here even if we have no GUI, because we are called
1564 // by LyX::exec to handled command-line requests. So we may need to check
1565 // whether we have a GUI or not. The boolean use_gui holds this information.
1566 void Buffer::dispatch(FuncRequest
const & func
, DispatchResult
& dr
)
1568 // We'll set this back to false if need be.
1569 bool dispatched
= true;
1571 switch (func
.action
) {
1572 case LFUN_BUFFER_EXPORT
: {
1573 bool success
= doExport(to_utf8(func
.argument()), false);
1574 dr
.setError(success
);
1576 dr
.setMessage(bformat(_("Error exporting to format: %1$s."),
1581 case LFUN_BRANCH_ACTIVATE
:
1582 case LFUN_BRANCH_DEACTIVATE
: {
1583 BranchList
& branchList
= params().branchlist();
1584 docstring
const branchName
= func
.argument();
1585 // the case without a branch name is handled elsewhere
1586 if (branchName
.empty()) {
1590 Branch
* branch
= branchList
.find(branchName
);
1592 LYXERR0("Branch " << branchName
<< " does not exist.");
1594 docstring
const msg
=
1595 bformat(_("Branch \"%1$s\" does not exist."), branchName
);
1598 branch
->setSelected(func
.action
== LFUN_BRANCH_ACTIVATE
);
1600 dr
.update(Update::Force
);
1605 case LFUN_BUFFER_PRINT
: {
1606 // we'll assume there's a problem until we succeed
1608 string target
= func
.getArg(0);
1609 string target_name
= func
.getArg(1);
1610 string command
= func
.getArg(2);
1613 || target_name
.empty()
1614 || command
.empty()) {
1615 LYXERR0("Unable to parse " << func
.argument());
1616 docstring
const msg
=
1617 bformat(_("Unable to parse \"%1$s\""), func
.argument());
1621 if (target
!= "printer" && target
!= "file") {
1622 LYXERR0("Unrecognized target \"" << target
<< '"');
1623 docstring
const msg
=
1624 bformat(_("Unrecognized target \"%1$s\""), from_utf8(target
));
1629 if (!doExport("dvi", true)) {
1630 showPrintError(absFileName());
1631 dr
.setMessage(_("Error exporting to DVI."));
1635 // Push directory path.
1636 string
const path
= temppath();
1637 // Prevent the compiler from optimizing away p
1641 // there are three cases here:
1642 // 1. we print to a file
1643 // 2. we print directly to a printer
1644 // 3. we print using a spool command (print to file first)
1647 string
const dviname
= changeExtension(latexName(true), "dvi");
1649 if (target
== "printer") {
1650 if (!lyxrc
.print_spool_command
.empty()) {
1651 // case 3: print using a spool
1652 string
const psname
= changeExtension(dviname
,".ps");
1653 command
+= ' ' + lyxrc
.print_to_file
1656 + quoteName(dviname
);
1658 string command2
= lyxrc
.print_spool_command
+ ' ';
1659 if (target_name
!= "default") {
1660 command2
+= lyxrc
.print_spool_printerprefix
1664 command2
+= quoteName(psname
);
1666 // If successful, then spool command
1667 res
= one
.startscript(Systemcall::Wait
, command
);
1670 // If there's no GUI, we have to wait on this command. Otherwise,
1671 // LyX deletes the temporary directory, and with it the spooled
1672 // file, before it can be printed!!
1673 Systemcall::Starttype stype
= use_gui
?
1674 Systemcall::DontWait
: Systemcall::Wait
;
1675 res
= one
.startscript(stype
, command2
);
1678 // case 2: print directly to a printer
1679 if (target_name
!= "default")
1680 command
+= ' ' + lyxrc
.print_to_printer
+ target_name
+ ' ';
1682 Systemcall::Starttype stype
= use_gui
?
1683 Systemcall::DontWait
: Systemcall::Wait
;
1684 res
= one
.startscript(stype
, command
+ quoteName(dviname
));
1688 // case 1: print to a file
1689 FileName
const filename(makeAbsPath(target_name
, filePath()));
1690 FileName
const dvifile(makeAbsPath(dviname
, path
));
1691 if (filename
.exists()) {
1692 docstring text
= bformat(
1693 _("The file %1$s already exists.\n\n"
1694 "Do you want to overwrite that file?"),
1695 makeDisplayPath(filename
.absFilename()));
1696 if (Alert::prompt(_("Overwrite file?"),
1697 text
, 0, 1, _("&Overwrite"), _("&Cancel")) != 0)
1700 command
+= ' ' + lyxrc
.print_to_file
1701 + quoteName(filename
.toFilesystemEncoding())
1703 + quoteName(dvifile
.toFilesystemEncoding());
1705 Systemcall::Starttype stype
= use_gui
?
1706 Systemcall::DontWait
: Systemcall::Wait
;
1707 res
= one
.startscript(stype
, command
);
1713 dr
.setMessage(_("Error running external commands."));
1714 showPrintError(absFileName());
1723 dr
.dispatched(dispatched
);
1727 void Buffer::changeLanguage(Language
const * from
, Language
const * to
)
1729 LASSERT(from
, /**/);
1732 for_each(par_iterator_begin(),
1734 bind(&Paragraph::changeLanguage
, _1
, params(), from
, to
));
1738 bool Buffer::isMultiLingual() const
1740 ParConstIterator end
= par_iterator_end();
1741 for (ParConstIterator it
= par_iterator_begin(); it
!= end
; ++it
)
1742 if (it
->isMultiLingual(params()))
1749 DocIterator
Buffer::getParFromID(int const id
) const
1751 Buffer
* buf
= const_cast<Buffer
*>(this);
1753 // John says this is called with id == -1 from undo
1754 lyxerr
<< "getParFromID(), id: " << id
<< endl
;
1755 return doc_iterator_end(buf
);
1758 for (DocIterator it
= doc_iterator_begin(buf
); !it
.atEnd(); it
.forwardPar())
1759 if (it
.paragraph().id() == id
)
1762 return doc_iterator_end(buf
);
1766 bool Buffer::hasParWithID(int const id
) const
1768 return !getParFromID(id
).atEnd();
1772 ParIterator
Buffer::par_iterator_begin()
1774 return ParIterator(doc_iterator_begin(this));
1778 ParIterator
Buffer::par_iterator_end()
1780 return ParIterator(doc_iterator_end(this));
1784 ParConstIterator
Buffer::par_iterator_begin() const
1786 return ParConstIterator(doc_iterator_begin(this));
1790 ParConstIterator
Buffer::par_iterator_end() const
1792 return ParConstIterator(doc_iterator_end(this));
1796 Language
const * Buffer::language() const
1798 return params().language
;
1802 docstring
const Buffer::B_(string
const & l10n
) const
1804 return params().B_(l10n
);
1808 bool Buffer::isClean() const
1810 return d
->lyx_clean
;
1814 bool Buffer::isBakClean() const
1816 return d
->bak_clean
;
1820 bool Buffer::isExternallyModified(CheckMethod method
) const
1822 LASSERT(d
->filename
.exists(), /**/);
1823 // if method == timestamp, check timestamp before checksum
1824 return (method
== checksum_method
1825 || d
->timestamp_
!= d
->filename
.lastModified())
1826 && d
->checksum_
!= d
->filename
.checksum();
1830 void Buffer::saveCheckSum(FileName
const & file
) const
1832 if (file
.exists()) {
1833 d
->timestamp_
= file
.lastModified();
1834 d
->checksum_
= file
.checksum();
1836 // in the case of save to a new file.
1843 void Buffer::markClean() const
1845 if (!d
->lyx_clean
) {
1846 d
->lyx_clean
= true;
1849 // if the .lyx file has been saved, we don't need an
1851 d
->bak_clean
= true;
1855 void Buffer::markBakClean() const
1857 d
->bak_clean
= true;
1861 void Buffer::setUnnamed(bool flag
)
1867 bool Buffer::isUnnamed() const
1873 // FIXME: this function should be moved to buffer_pimpl.C
1874 void Buffer::markDirty()
1877 d
->lyx_clean
= false;
1880 d
->bak_clean
= false;
1882 DepClean::iterator it
= d
->dep_clean
.begin();
1883 DepClean::const_iterator
const end
= d
->dep_clean
.end();
1885 for (; it
!= end
; ++it
)
1890 FileName
Buffer::fileName() const
1896 string
Buffer::absFileName() const
1898 return d
->filename
.absFilename();
1902 string
Buffer::filePath() const
1904 return d
->filename
.onlyPath().absFilename() + "/";
1908 bool Buffer::isReadonly() const
1910 return d
->read_only
;
1914 void Buffer::setParent(Buffer
const * buffer
)
1916 // Avoids recursive include.
1917 d
->setParent(buffer
== this ? 0 : buffer
);
1922 Buffer
const * Buffer::parent() const
1928 void Buffer::collectRelatives(BufferSet
& bufs
) const
1932 parent()->collectRelatives(bufs
);
1934 // loop over children
1935 Impl::BufferPositionMap::iterator it
= d
->children_positions
.begin();
1936 Impl::BufferPositionMap::iterator end
= d
->children_positions
.end();
1937 for (; it
!= end
; ++it
)
1938 bufs
.insert(const_cast<Buffer
*>(it
->first
));
1942 std::vector
<Buffer
const *> Buffer::allRelatives() const
1945 collectRelatives(bufs
);
1946 BufferSet::iterator it
= bufs
.begin();
1947 std::vector
<Buffer
const *> ret
;
1948 for (; it
!= bufs
.end(); ++it
)
1954 Buffer
const * Buffer::masterBuffer() const
1956 Buffer
const * const pbuf
= d
->parent();
1960 return pbuf
->masterBuffer();
1964 bool Buffer::isChild(Buffer
* child
) const
1966 return d
->children_positions
.find(child
) != d
->children_positions
.end();
1970 DocIterator
Buffer::firstChildPosition(Buffer
const * child
)
1972 Impl::BufferPositionMap::iterator it
;
1973 it
= d
->children_positions
.find(child
);
1974 if (it
== d
->children_positions
.end())
1975 return DocIterator(this);
1980 std::vector
<Buffer
*> Buffer::getChildren() const
1982 std::vector
<Buffer
*> clist
;
1983 // loop over children
1984 Impl::BufferPositionMap::iterator it
= d
->children_positions
.begin();
1985 Impl::BufferPositionMap::iterator end
= d
->children_positions
.end();
1986 for (; it
!= end
; ++it
) {
1987 Buffer
* child
= const_cast<Buffer
*>(it
->first
);
1988 clist
.push_back(child
);
1989 // there might be grandchildren
1990 std::vector
<Buffer
*> glist
= child
->getChildren();
1991 for (vector
<Buffer
*>::const_iterator git
= glist
.begin();
1992 git
!= glist
.end(); ++git
)
1993 clist
.push_back(*git
);
1999 template<typename M
>
2000 typename
M::iterator
greatest_below(M
& m
, typename
M::key_type
const & x
)
2005 typename
M::iterator it
= m
.lower_bound(x
);
2006 if (it
== m
.begin())
2014 MacroData
const * Buffer::getBufferMacro(docstring
const & name
,
2015 DocIterator
const & pos
) const
2017 LYXERR(Debug::MACROS
, "Searching for " << to_ascii(name
) << " at " << pos
);
2019 // if paragraphs have no macro context set, pos will be empty
2023 // we haven't found anything yet
2024 DocIterator bestPos
= par_iterator_begin();
2025 MacroData
const * bestData
= 0;
2027 // find macro definitions for name
2028 Impl::NamePositionScopeMacroMap::iterator nameIt
2029 = d
->macros
.find(name
);
2030 if (nameIt
!= d
->macros
.end()) {
2031 // find last definition in front of pos or at pos itself
2032 Impl::PositionScopeMacroMap::const_iterator it
2033 = greatest_below(nameIt
->second
, pos
);
2034 if (it
!= nameIt
->second
.end()) {
2036 // scope ends behind pos?
2037 if (pos
< it
->second
.first
) {
2038 // Looks good, remember this. If there
2039 // is no external macro behind this,
2040 // we found the right one already.
2041 bestPos
= it
->first
;
2042 bestData
= &it
->second
.second
;
2046 // try previous macro if there is one
2047 if (it
== nameIt
->second
.begin())
2054 // find macros in included files
2055 Impl::PositionScopeBufferMap::const_iterator it
2056 = greatest_below(d
->position_to_children
, pos
);
2057 if (it
== d
->position_to_children
.end())
2058 // no children before
2062 // do we know something better (i.e. later) already?
2063 if (it
->first
< bestPos
)
2066 // scope ends behind pos?
2067 if (pos
< it
->second
.first
) {
2068 // look for macro in external file
2069 d
->macro_lock
= true;
2070 MacroData
const * data
2071 = it
->second
.second
->getMacro(name
, false);
2072 d
->macro_lock
= false;
2074 bestPos
= it
->first
;
2080 // try previous file if there is one
2081 if (it
== d
->position_to_children
.begin())
2086 // return the best macro we have found
2091 MacroData
const * Buffer::getMacro(docstring
const & name
,
2092 DocIterator
const & pos
, bool global
) const
2097 // query buffer macros
2098 MacroData
const * data
= getBufferMacro(name
, pos
);
2102 // If there is a master buffer, query that
2103 Buffer
const * const pbuf
= d
->parent();
2105 d
->macro_lock
= true;
2106 MacroData
const * macro
= pbuf
->getMacro(
2107 name
, *this, false);
2108 d
->macro_lock
= false;
2114 data
= MacroTable::globalMacros().get(name
);
2123 MacroData
const * Buffer::getMacro(docstring
const & name
, bool global
) const
2125 // set scope end behind the last paragraph
2126 DocIterator scope
= par_iterator_begin();
2127 scope
.pit() = scope
.lastpit() + 1;
2129 return getMacro(name
, scope
, global
);
2133 MacroData
const * Buffer::getMacro(docstring
const & name
,
2134 Buffer
const & child
, bool global
) const
2136 // look where the child buffer is included first
2137 Impl::BufferPositionMap::iterator it
= d
->children_positions
.find(&child
);
2138 if (it
== d
->children_positions
.end())
2141 // check for macros at the inclusion position
2142 return getMacro(name
, it
->second
, global
);
2146 void Buffer::updateMacros(DocIterator
& it
, DocIterator
& scope
) const
2148 pit_type lastpit
= it
.lastpit();
2150 // look for macros in each paragraph
2151 while (it
.pit() <= lastpit
) {
2152 Paragraph
& par
= it
.paragraph();
2154 // iterate over the insets of the current paragraph
2155 InsetList
const & insets
= par
.insetList();
2156 InsetList::const_iterator iit
= insets
.begin();
2157 InsetList::const_iterator end
= insets
.end();
2158 for (; iit
!= end
; ++iit
) {
2159 it
.pos() = iit
->pos
;
2161 // is it a nested text inset?
2162 if (iit
->inset
->asInsetText()) {
2163 // Inset needs its own scope?
2164 InsetText
const * itext
= iit
->inset
->asInsetText();
2165 bool newScope
= itext
->isMacroScope();
2167 // scope which ends just behind the inset
2168 DocIterator insetScope
= it
;
2171 // collect macros in inset
2172 it
.push_back(CursorSlice(*iit
->inset
));
2173 updateMacros(it
, newScope
? insetScope
: scope
);
2178 // is it an external file?
2179 if (iit
->inset
->lyxCode() == INCLUDE_CODE
) {
2180 // get buffer of external file
2181 InsetInclude
const & inset
=
2182 static_cast<InsetInclude
const &>(*iit
->inset
);
2183 d
->macro_lock
= true;
2184 Buffer
* child
= inset
.getChildBuffer();
2185 d
->macro_lock
= false;
2189 // register its position, but only when it is
2190 // included first in the buffer
2191 if (d
->children_positions
.find(child
) ==
2192 d
->children_positions
.end())
2193 d
->children_positions
[child
] = it
;
2195 // register child with its scope
2196 d
->position_to_children
[it
] = Impl::ScopeBuffer(scope
, child
);
2200 if (iit
->inset
->lyxCode() != MATHMACRO_CODE
)
2204 MathMacroTemplate
& macroTemplate
=
2205 static_cast<MathMacroTemplate
&>(*iit
->inset
);
2206 MacroContext
mc(*this, it
);
2207 macroTemplate
.updateToContext(mc
);
2210 bool valid
= macroTemplate
.validMacro();
2211 // FIXME: Should be fixNameAndCheckIfValid() in fact,
2212 // then the BufferView's cursor will be invalid in
2213 // some cases which leads to crashes.
2218 d
->macros
[macroTemplate
.name()][it
] =
2219 Impl::ScopeMacro(scope
, MacroData(*this, it
));
2229 void Buffer::updateMacros() const
2234 LYXERR(Debug::MACROS
, "updateMacro of " << d
->filename
.onlyFileName());
2236 // start with empty table
2238 d
->children_positions
.clear();
2239 d
->position_to_children
.clear();
2241 // Iterate over buffer, starting with first paragraph
2242 // The scope must be bigger than any lookup DocIterator
2243 // later. For the global lookup, lastpit+1 is used, hence
2244 // we use lastpit+2 here.
2245 DocIterator it
= par_iterator_begin();
2246 DocIterator outerScope
= it
;
2247 outerScope
.pit() = outerScope
.lastpit() + 2;
2248 updateMacros(it
, outerScope
);
2252 void Buffer::updateMacroInstances() const
2254 LYXERR(Debug::MACROS
, "updateMacroInstances for "
2255 << d
->filename
.onlyFileName());
2256 DocIterator it
= doc_iterator_begin(this);
2257 DocIterator end
= doc_iterator_end(this);
2258 for (; it
!= end
; it
.forwardPos()) {
2259 // look for MathData cells in InsetMathNest insets
2260 Inset
* inset
= it
.nextInset();
2264 InsetMath
* minset
= inset
->asInsetMath();
2268 // update macro in all cells of the InsetMathNest
2269 DocIterator::idx_type n
= minset
->nargs();
2270 MacroContext mc
= MacroContext(*this, it
);
2271 for (DocIterator::idx_type i
= 0; i
< n
; ++i
) {
2272 MathData
& data
= minset
->cell(i
);
2273 data
.updateMacros(0, mc
);
2279 void Buffer::listMacroNames(MacroNameSet
& macros
) const
2284 d
->macro_lock
= true;
2286 // loop over macro names
2287 Impl::NamePositionScopeMacroMap::iterator nameIt
= d
->macros
.begin();
2288 Impl::NamePositionScopeMacroMap::iterator nameEnd
= d
->macros
.end();
2289 for (; nameIt
!= nameEnd
; ++nameIt
)
2290 macros
.insert(nameIt
->first
);
2292 // loop over children
2293 Impl::BufferPositionMap::iterator it
= d
->children_positions
.begin();
2294 Impl::BufferPositionMap::iterator end
= d
->children_positions
.end();
2295 for (; it
!= end
; ++it
)
2296 it
->first
->listMacroNames(macros
);
2299 Buffer
const * const pbuf
= d
->parent();
2301 pbuf
->listMacroNames(macros
);
2303 d
->macro_lock
= false;
2307 void Buffer::listParentMacros(MacroSet
& macros
, LaTeXFeatures
& features
) const
2309 Buffer
const * const pbuf
= d
->parent();
2314 pbuf
->listMacroNames(names
);
2317 MacroNameSet::iterator it
= names
.begin();
2318 MacroNameSet::iterator end
= names
.end();
2319 for (; it
!= end
; ++it
) {
2321 MacroData
const * data
=
2322 pbuf
->getMacro(*it
, *this, false);
2324 macros
.insert(data
);
2326 // we cannot access the original MathMacroTemplate anymore
2327 // here to calls validate method. So we do its work here manually.
2328 // FIXME: somehow make the template accessible here.
2329 if (data
->optionals() > 0)
2330 features
.require("xargs");
2336 Buffer::References
& Buffer::references(docstring
const & label
)
2339 return const_cast<Buffer
*>(masterBuffer())->references(label
);
2341 RefCache::iterator it
= d
->ref_cache_
.find(label
);
2342 if (it
!= d
->ref_cache_
.end())
2343 return it
->second
.second
;
2345 static InsetLabel
const * dummy_il
= 0;
2346 static References
const dummy_refs
;
2347 it
= d
->ref_cache_
.insert(
2348 make_pair(label
, make_pair(dummy_il
, dummy_refs
))).first
;
2349 return it
->second
.second
;
2353 Buffer::References
const & Buffer::references(docstring
const & label
) const
2355 return const_cast<Buffer
*>(this)->references(label
);
2359 void Buffer::setInsetLabel(docstring
const & label
, InsetLabel
const * il
)
2361 masterBuffer()->d
->ref_cache_
[label
].first
= il
;
2365 InsetLabel
const * Buffer::insetLabel(docstring
const & label
) const
2367 return masterBuffer()->d
->ref_cache_
[label
].first
;
2371 void Buffer::clearReferenceCache() const
2374 d
->ref_cache_
.clear();
2378 void Buffer::changeRefsIfUnique(docstring
const & from
, docstring
const & to
,
2381 //FIXME: This does not work for child documents yet.
2382 LASSERT(code
== CITE_CODE
, /**/);
2383 // Check if the label 'from' appears more than once
2384 vector
<docstring
> labels
;
2386 BiblioInfo
const & keys
= masterBibInfo();
2387 BiblioInfo::const_iterator bit
= keys
.begin();
2388 BiblioInfo::const_iterator bend
= keys
.end();
2390 for (; bit
!= bend
; ++bit
)
2392 labels
.push_back(bit
->first
);
2395 if (count(labels
.begin(), labels
.end(), from
) > 1)
2398 for (InsetIterator it
= inset_iterator_begin(inset()); it
; ++it
) {
2399 if (it
->lyxCode() == code
) {
2400 InsetCommand
& inset
= static_cast<InsetCommand
&>(*it
);
2401 docstring
const oldValue
= inset
.getParam(paramName
);
2402 if (oldValue
== from
)
2403 inset
.setParam(paramName
, to
);
2409 void Buffer::getSourceCode(odocstream
& os
, pit_type par_begin
,
2410 pit_type par_end
, bool full_source
) const
2412 OutputParams
runparams(¶ms().encoding());
2413 runparams
.nice
= true;
2414 runparams
.flavor
= params().useXetex
?
2415 OutputParams::XETEX
: OutputParams::LATEX
;
2416 runparams
.linelen
= lyxrc
.plaintext_linelen
;
2417 // No side effect of file copying and image conversion
2418 runparams
.dryrun
= true;
2422 os
<< "% " << _("Preview source code") << "\n\n";
2423 d
->texrow
.newline();
2424 d
->texrow
.newline();
2426 writeDocBookSource(os
, absFileName(), runparams
, false);
2428 // latex or literate
2429 writeLaTeXSource(os
, string(), runparams
, true, true);
2431 runparams
.par_begin
= par_begin
;
2432 runparams
.par_end
= par_end
;
2433 if (par_begin
+ 1 == par_end
) {
2435 << bformat(_("Preview source code for paragraph %1$d"), par_begin
)
2439 << bformat(_("Preview source code from paragraph %1$s to %2$s"),
2440 convert
<docstring
>(par_begin
),
2441 convert
<docstring
>(par_end
- 1))
2444 d
->texrow
.newline();
2445 d
->texrow
.newline();
2446 // output paragraphs
2448 docbookParagraphs(paragraphs(), *this, os
, runparams
);
2450 // latex or literate
2451 latexParagraphs(*this, text(), os
, d
->texrow
, runparams
);
2456 ErrorList
& Buffer::errorList(string
const & type
) const
2458 static ErrorList emptyErrorList
;
2459 map
<string
, ErrorList
>::iterator I
= d
->errorLists
.find(type
);
2460 if (I
== d
->errorLists
.end())
2461 return emptyErrorList
;
2467 void Buffer::updateTocItem(std::string
const & type
,
2468 DocIterator
const & dit
) const
2471 gui_
->updateTocItem(type
, dit
);
2475 void Buffer::structureChanged() const
2478 gui_
->structureChanged();
2482 void Buffer::errors(string
const & err
) const
2489 void Buffer::message(docstring
const & msg
) const
2496 void Buffer::setBusy(bool on
) const
2503 void Buffer::setReadOnly(bool on
) const
2506 d
->wa_
->setReadOnly(on
);
2510 void Buffer::updateTitles() const
2513 d
->wa_
->updateTitles();
2517 void Buffer::resetAutosaveTimers() const
2520 gui_
->resetAutosaveTimers();
2524 bool Buffer::hasGuiDelegate() const
2530 void Buffer::setGuiDelegate(frontend::GuiBufferDelegate
* gui
)
2539 class AutoSaveBuffer
: public ForkedProcess
{
2542 AutoSaveBuffer(Buffer
const & buffer
, FileName
const & fname
)
2543 : buffer_(buffer
), fname_(fname
) {}
2545 virtual boost::shared_ptr
<ForkedProcess
> clone() const
2547 return boost::shared_ptr
<ForkedProcess
>(new AutoSaveBuffer(*this));
2552 command_
= to_utf8(bformat(_("Auto-saving %1$s"),
2553 from_utf8(fname_
.absFilename())));
2554 return run(DontWait
);
2558 virtual int generateChild();
2560 Buffer
const & buffer_
;
2565 int AutoSaveBuffer::generateChild()
2567 // tmp_ret will be located (usually) in /tmp
2568 // will that be a problem?
2569 // Note that this calls ForkedCalls::fork(), so it's
2570 // ok cross-platform.
2571 pid_t
const pid
= fork();
2572 // If you want to debug the autosave
2573 // you should set pid to -1, and comment out the fork.
2574 if (pid
!= 0 && pid
!= -1)
2577 // pid = -1 signifies that lyx was unable
2578 // to fork. But we will do the save
2580 bool failed
= false;
2581 FileName
const tmp_ret
= FileName::tempName("lyxauto");
2582 if (!tmp_ret
.empty()) {
2583 buffer_
.writeFile(tmp_ret
);
2584 // assume successful write of tmp_ret
2585 if (!tmp_ret
.moveTo(fname_
))
2591 // failed to write/rename tmp_ret so try writing direct
2592 if (!buffer_
.writeFile(fname_
)) {
2593 // It is dangerous to do this in the child,
2594 // but safe in the parent, so...
2595 if (pid
== -1) // emit message signal.
2596 buffer_
.message(_("Autosave failed!"));
2600 if (pid
== 0) // we are the child so...
2609 FileName
Buffer::getAutosaveFilename() const
2611 // if the document is unnamed try to save in the backup dir, else
2612 // in the default document path, and as a last try in the filePath,
2613 // which will most often be the temporary directory
2616 fpath
= lyxrc
.backupdir_path
.empty() ? lyxrc
.document_path
2617 : lyxrc
.backupdir_path
;
2618 if (!isUnnamed() || fpath
.empty() || !FileName(fpath
).exists())
2621 string
const fname
= "#" + d
->filename
.onlyFileName() + "#";
2622 return makeAbsPath(fname
, fpath
);
2626 void Buffer::removeAutosaveFile() const
2628 FileName
const f
= getAutosaveFilename();
2634 void Buffer::moveAutosaveFile(support::FileName
const & oldauto
) const
2636 FileName
const newauto
= getAutosaveFilename();
2637 if (!(oldauto
== newauto
|| oldauto
.moveTo(newauto
)))
2638 LYXERR0("Unable to remove autosave file `" << oldauto
<< "'!");
2642 // Perfect target for a thread...
2643 void Buffer::autoSave() const
2645 if (isBakClean() || isReadonly()) {
2646 // We don't save now, but we'll try again later
2647 resetAutosaveTimers();
2651 // emit message signal.
2652 message(_("Autosaving current document..."));
2653 AutoSaveBuffer
autosave(*this, getAutosaveFilename());
2657 resetAutosaveTimers();
2661 string
Buffer::bufferFormat() const
2667 if (params().useXetex
)
2669 if (params().encoding().package() == Encoding::japanese
)
2675 string
Buffer::getDefaultOutputFormat() const
2677 if (!params().defaultOutputFormat
.empty()
2678 && params().defaultOutputFormat
!= "default")
2679 return params().defaultOutputFormat
;
2680 typedef vector
<Format
const *> Formats
;
2681 Formats formats
= exportableFormats(true);
2684 || params().useXetex
2685 || params().encoding().package() == Encoding::japanese
) {
2686 if (formats
.empty())
2688 // return the first we find
2689 return formats
.front()->name();
2691 return lyxrc
.default_view_format
;
2696 bool Buffer::doExport(string
const & format
, bool put_in_tempdir
,
2697 string
& result_file
) const
2699 string backend_format
;
2700 OutputParams
runparams(¶ms().encoding());
2701 runparams
.flavor
= OutputParams::LATEX
;
2702 runparams
.linelen
= lyxrc
.plaintext_linelen
;
2703 vector
<string
> backs
= backends();
2704 if (find(backs
.begin(), backs
.end(), format
) == backs
.end()) {
2705 // Get shortest path to format
2706 Graph::EdgePath path
;
2707 for (vector
<string
>::const_iterator it
= backs
.begin();
2708 it
!= backs
.end(); ++it
) {
2709 Graph::EdgePath p
= theConverters().getPath(*it
, format
);
2710 if (!p
.empty() && (path
.empty() || p
.size() < path
.size())) {
2711 backend_format
= *it
;
2716 runparams
.flavor
= theConverters().getFlavor(path
);
2718 Alert::error(_("Couldn't export file"),
2719 bformat(_("No information for exporting the format %1$s."),
2720 formats
.prettyName(format
)));
2724 backend_format
= format
;
2725 // FIXME: Don't hardcode format names here, but use a flag
2726 if (backend_format
== "pdflatex")
2727 runparams
.flavor
= OutputParams::PDFLATEX
;
2730 string filename
= latexName(false);
2731 filename
= addName(temppath(), filename
);
2732 filename
= changeExtension(filename
,
2733 formats
.extension(backend_format
));
2736 updateMacroInstances();
2738 // Plain text backend
2739 if (backend_format
== "text")
2740 writePlaintextFile(*this, FileName(filename
), runparams
);
2742 else if (backend_format
== "lyx")
2743 writeFile(FileName(filename
));
2745 else if (isDocBook()) {
2746 runparams
.nice
= !put_in_tempdir
;
2747 makeDocBookFile(FileName(filename
), runparams
);
2750 else if (backend_format
== format
) {
2751 runparams
.nice
= true;
2752 if (!makeLaTeXFile(FileName(filename
), string(), runparams
))
2754 } else if (!lyxrc
.tex_allows_spaces
2755 && contains(filePath(), ' ')) {
2756 Alert::error(_("File name error"),
2757 _("The directory path to the document cannot contain spaces."));
2760 runparams
.nice
= false;
2761 if (!makeLaTeXFile(FileName(filename
), filePath(), runparams
))
2765 string
const error_type
= (format
== "program")
2766 ? "Build" : bufferFormat();
2767 ErrorList
& error_list
= d
->errorLists
[error_type
];
2768 string
const ext
= formats
.extension(format
);
2769 FileName
const tmp_result_file(changeExtension(filename
, ext
));
2770 bool const success
= theConverters().convert(this, FileName(filename
),
2771 tmp_result_file
, FileName(absFileName()), backend_format
, format
,
2773 // Emit the signal to show the error list.
2774 if (format
!= backend_format
)
2779 if (put_in_tempdir
) {
2780 result_file
= tmp_result_file
.absFilename();
2784 result_file
= changeExtension(absFileName(), ext
);
2785 // We need to copy referenced files (e. g. included graphics
2786 // if format == "dvi") to the result dir.
2787 vector
<ExportedFile
> const files
=
2788 runparams
.exportdata
->externalFiles(format
);
2789 string
const dest
= onlyPath(result_file
);
2790 CopyStatus status
= SUCCESS
;
2791 for (vector
<ExportedFile
>::const_iterator it
= files
.begin();
2792 it
!= files
.end() && status
!= CANCEL
; ++it
) {
2793 string
const fmt
= formats
.getFormatFromFile(it
->sourceName
);
2794 status
= copyFile(fmt
, it
->sourceName
,
2795 makeAbsPath(it
->exportName
, dest
),
2796 it
->exportName
, status
== FORCE
);
2798 if (status
== CANCEL
) {
2799 message(_("Document export cancelled."));
2800 } else if (tmp_result_file
.exists()) {
2801 // Finally copy the main file
2802 status
= copyFile(format
, tmp_result_file
,
2803 FileName(result_file
), result_file
,
2805 message(bformat(_("Document exported as %1$s "
2807 formats
.prettyName(format
),
2808 makeDisplayPath(result_file
)));
2810 // This must be a dummy converter like fax (bug 1888)
2811 message(bformat(_("Document exported as %1$s"),
2812 formats
.prettyName(format
)));
2819 bool Buffer::doExport(string
const & format
, bool put_in_tempdir
) const
2822 return doExport(format
, put_in_tempdir
, result_file
);
2826 bool Buffer::preview(string
const & format
) const
2829 if (!doExport(format
, true, result_file
))
2831 return formats
.view(*this, FileName(result_file
), format
);
2835 bool Buffer::isExportable(string
const & format
) const
2837 vector
<string
> backs
= backends();
2838 for (vector
<string
>::const_iterator it
= backs
.begin();
2839 it
!= backs
.end(); ++it
)
2840 if (theConverters().isReachable(*it
, format
))
2846 vector
<Format
const *> Buffer::exportableFormats(bool only_viewable
) const
2848 vector
<string
> backs
= backends();
2849 vector
<Format
const *> result
=
2850 theConverters().getReachable(backs
[0], only_viewable
, true);
2851 for (vector
<string
>::const_iterator it
= backs
.begin() + 1;
2852 it
!= backs
.end(); ++it
) {
2853 vector
<Format
const *> r
=
2854 theConverters().getReachable(*it
, only_viewable
, false);
2855 result
.insert(result
.end(), r
.begin(), r
.end());
2861 vector
<string
> Buffer::backends() const
2864 if (params().baseClass()->isTeXClassAvailable()) {
2865 v
.push_back(bufferFormat());
2866 // FIXME: Don't hardcode format names here, but use a flag
2867 if (v
.back() == "latex")
2868 v
.push_back("pdflatex");
2870 v
.push_back("text");
2876 bool Buffer::readFileHelper(FileName
const & s
)
2878 // File information about normal file
2880 docstring
const file
= makeDisplayPath(s
.absFilename(), 50);
2881 docstring text
= bformat(_("The specified document\n%1$s"
2882 "\ncould not be read."), file
);
2883 Alert::error(_("Could not read document"), text
);
2887 // Check if emergency save file exists and is newer.
2888 FileName
const e(s
.absFilename() + ".emergency");
2890 if (e
.exists() && s
.exists() && e
.lastModified() > s
.lastModified()) {
2891 docstring
const file
= makeDisplayPath(s
.absFilename(), 20);
2892 docstring
const text
=
2893 bformat(_("An emergency save of the document "
2895 "Recover emergency save?"), file
);
2896 switch (Alert::prompt(_("Load emergency save?"), text
, 0, 2,
2897 _("&Recover"), _("&Load Original"),
2901 // the file is not saved if we load the emergency file.
2911 // Now check if autosave file is newer.
2912 FileName
const a(onlyPath(s
.absFilename()) + '#' + onlyFilename(s
.absFilename()) + '#');
2914 if (a
.exists() && s
.exists() && a
.lastModified() > s
.lastModified()) {
2915 docstring
const file
= makeDisplayPath(s
.absFilename(), 20);
2916 docstring
const text
=
2917 bformat(_("The backup of the document "
2918 "%1$s is newer.\n\nLoad the "
2919 "backup instead?"), file
);
2920 switch (Alert::prompt(_("Load backup?"), text
, 0, 2,
2921 _("&Load backup"), _("Load &original"),
2925 // the file is not saved if we load the autosave file.
2929 // Here we delete the autosave
2940 bool Buffer::loadLyXFile(FileName
const & s
)
2942 if (s
.isReadableFile()) {
2943 if (readFileHelper(s
)) {
2944 lyxvc().file_found_hook(s
);
2945 if (!s
.isWritable())
2950 docstring
const file
= makeDisplayPath(s
.absFilename(), 20);
2951 // Here we probably should run
2952 if (LyXVC::file_not_found_hook(s
)) {
2953 docstring
const text
=
2954 bformat(_("Do you want to retrieve the document"
2955 " %1$s from version control?"), file
);
2956 int const ret
= Alert::prompt(_("Retrieve from version control?"),
2957 text
, 0, 1, _("&Retrieve"), _("&Cancel"));
2960 // How can we know _how_ to do the checkout?
2961 // With the current VC support it has to be,
2962 // a RCS file since CVS do not have special ,v files.
2964 return loadLyXFile(s
);
2972 void Buffer::bufferErrors(TeXErrors
const & terr
, ErrorList
& errorList
) const
2974 TeXErrors::Errors::const_iterator cit
= terr
.begin();
2975 TeXErrors::Errors::const_iterator end
= terr
.end();
2977 for (; cit
!= end
; ++cit
) {
2980 int errorRow
= cit
->error_in_line
;
2981 bool found
= d
->texrow
.getIdFromRow(errorRow
, id_start
,
2987 found
= d
->texrow
.getIdFromRow(errorRow
, id_end
, pos_end
);
2988 } while (found
&& id_start
== id_end
&& pos_start
== pos_end
);
2990 errorList
.push_back(ErrorItem(cit
->error_desc
,
2991 cit
->error_text
, id_start
, pos_start
, pos_end
));
2996 void Buffer::setBuffersForInsets() const
2998 inset().setBuffer(const_cast<Buffer
&>(*this));
3002 void Buffer::updateLabels(UpdateScope scope
) const
3004 // Use the master text class also for child documents
3005 Buffer
const * const master
= masterBuffer();
3006 DocumentClass
const & textclass
= master
->params().documentClass();
3008 // keep the buffers to be children in this set. If the call from the
3009 // master comes back we can see which of them were actually seen (i.e.
3010 // via an InsetInclude). The remaining ones in the set need still be updated.
3011 static std::set
<Buffer
const *> bufToUpdate
;
3012 if (scope
== UpdateMaster
) {
3013 // If this is a child document start with the master
3014 if (master
!= this) {
3015 bufToUpdate
.insert(this);
3016 master
->updateLabels();
3017 // Do this here in case the master has no gui associated with it. Then,
3018 // the TocModel is not updated and TocModel::toc_ is invalid (bug 5699).
3022 // was buf referenced from the master (i.e. not in bufToUpdate anymore)?
3023 if (bufToUpdate
.find(this) == bufToUpdate
.end())
3027 // start over the counters in the master
3028 textclass
.counters().reset();
3031 // update will be done below for this buffer
3032 bufToUpdate
.erase(this);
3034 // update all caches
3035 clearReferenceCache();
3038 Buffer
& cbuf
= const_cast<Buffer
&>(*this);
3040 LASSERT(!text().paragraphs().empty(), /**/);
3043 ParIterator parit
= cbuf
.par_iterator_begin();
3044 updateLabels(parit
);
3047 // TocBackend update will be done later.
3050 cbuf
.tocBackend().update();
3051 if (scope
== UpdateMaster
)
3052 cbuf
.structureChanged();
3056 static depth_type
getDepth(DocIterator
const & it
)
3058 depth_type depth
= 0;
3059 for (size_t i
= 0 ; i
< it
.depth() ; ++i
)
3060 if (!it
[i
].inset().inMathed())
3061 depth
+= it
[i
].paragraph().getDepth() + 1;
3062 // remove 1 since the outer inset does not count
3066 static depth_type
getItemDepth(ParIterator
const & it
)
3068 Paragraph
const & par
= *it
;
3069 LabelType
const labeltype
= par
.layout().labeltype
;
3071 if (labeltype
!= LABEL_ENUMERATE
&& labeltype
!= LABEL_ITEMIZE
)
3074 // this will hold the lowest depth encountered up to now.
3075 depth_type min_depth
= getDepth(it
);
3076 ParIterator prev_it
= it
;
3079 --prev_it
.top().pit();
3081 // start of nested inset: go to outer par
3083 if (prev_it
.empty()) {
3084 // start of document: nothing to do
3089 // We search for the first paragraph with same label
3090 // that is not more deeply nested.
3091 Paragraph
& prev_par
= *prev_it
;
3092 depth_type
const prev_depth
= getDepth(prev_it
);
3093 if (labeltype
== prev_par
.layout().labeltype
) {
3094 if (prev_depth
< min_depth
)
3095 return prev_par
.itemdepth
+ 1;
3096 if (prev_depth
== min_depth
)
3097 return prev_par
.itemdepth
;
3099 min_depth
= min(min_depth
, prev_depth
);
3100 // small optimization: if we are at depth 0, we won't
3101 // find anything else
3102 if (prev_depth
== 0)
3108 static bool needEnumCounterReset(ParIterator
const & it
)
3110 Paragraph
const & par
= *it
;
3111 LASSERT(par
.layout().labeltype
== LABEL_ENUMERATE
, /**/);
3112 depth_type
const cur_depth
= par
.getDepth();
3113 ParIterator prev_it
= it
;
3114 while (prev_it
.pit()) {
3115 --prev_it
.top().pit();
3116 Paragraph
const & prev_par
= *prev_it
;
3117 if (prev_par
.getDepth() <= cur_depth
)
3118 return prev_par
.layout().labeltype
!= LABEL_ENUMERATE
;
3120 // start of nested inset: reset
3125 // set the label of a paragraph. This includes the counters.
3126 static void setLabel(Buffer
const & buf
, ParIterator
& it
)
3128 BufferParams
const & bp
= buf
.masterBuffer()->params();
3129 DocumentClass
const & textclass
= bp
.documentClass();
3130 Paragraph
& par
= it
.paragraph();
3131 Layout
const & layout
= par
.layout();
3132 Counters
& counters
= textclass
.counters();
3134 if (par
.params().startOfAppendix()) {
3135 // FIXME: only the counter corresponding to toplevel
3136 // sectionning should be reset
3138 counters
.appendix(true);
3140 par
.params().appendix(counters
.appendix());
3142 // Compute the item depth of the paragraph
3143 par
.itemdepth
= getItemDepth(it
);
3145 if (layout
.margintype
== MARGIN_MANUAL
) {
3146 if (par
.params().labelWidthString().empty())
3147 par
.params().labelWidthString(par
.translateIfPossible(layout
.labelstring(), bp
));
3149 par
.params().labelWidthString(docstring());
3152 switch(layout
.labeltype
) {
3154 if (layout
.toclevel
<= bp
.secnumdepth
3155 && (layout
.latextype
!= LATEX_ENVIRONMENT
3156 || isFirstInSequence(it
.pit(), it
.plist()))) {
3157 counters
.step(layout
.counter
);
3158 par
.params().labelString(
3159 par
.expandLabel(layout
, bp
));
3161 par
.params().labelString(docstring());
3164 case LABEL_ITEMIZE
: {
3165 // At some point of time we should do something more
3166 // clever here, like:
3167 // par.params().labelString(
3168 // bp.user_defined_bullet(par.itemdepth).getText());
3169 // for now, use a simple hardcoded label
3170 docstring itemlabel
;
3171 switch (par
.itemdepth
) {
3173 itemlabel
= char_type(0x2022);
3176 itemlabel
= char_type(0x2013);
3179 itemlabel
= char_type(0x2217);
3182 itemlabel
= char_type(0x2219); // or 0x00b7
3185 par
.params().labelString(itemlabel
);
3189 case LABEL_ENUMERATE
: {
3190 // FIXME: Yes I know this is a really, really! bad solution
3192 docstring enumcounter
= from_ascii("enum");
3194 switch (par
.itemdepth
) {
3203 enumcounter
+= "iv";
3206 // not a valid enumdepth...
3210 // Maybe we have to reset the enumeration counter.
3211 if (needEnumCounterReset(it
))
3212 counters
.reset(enumcounter
);
3214 counters
.step(enumcounter
);
3218 switch (par
.itemdepth
) {
3220 format
= N_("\\arabic{enumi}.");
3223 format
= N_("(\\alph{enumii})");
3226 format
= N_("\\roman{enumiii}.");
3229 format
= N_("\\Alph{enumiv}.");
3232 // not a valid enumdepth...
3236 par
.params().labelString(counters
.counterLabel(
3237 par
.translateIfPossible(from_ascii(format
), bp
)));
3242 case LABEL_SENSITIVE
: {
3243 string
const & type
= counters
.current_float();
3244 docstring full_label
;
3246 full_label
= buf
.B_("Senseless!!! ");
3248 docstring name
= buf
.B_(textclass
.floats().getType(type
).name());
3249 if (counters
.hasCounter(from_utf8(type
))) {
3250 counters
.step(from_utf8(type
));
3251 full_label
= bformat(from_ascii("%1$s %2$s:"),
3253 counters
.theCounter(from_utf8(type
)));
3255 full_label
= bformat(from_ascii("%1$s #:"), name
);
3257 par
.params().labelString(full_label
);
3261 case LABEL_NO_LABEL
:
3262 par
.params().labelString(docstring());
3266 case LABEL_TOP_ENVIRONMENT
:
3267 case LABEL_CENTERED_TOP_ENVIRONMENT
:
3270 par
.params().labelString(
3271 par
.translateIfPossible(layout
.labelstring(), bp
));
3277 void Buffer::updateLabels(ParIterator
& parit
) const
3279 LASSERT(parit
.pit() == 0, /**/);
3281 // set the position of the text in the buffer to be able
3282 // to resolve macros in it. This has nothing to do with
3283 // labels, but by putting it here we avoid implementing
3284 // a whole bunch of traversal routines just for this call.
3285 parit
.text()->setMacrocontextPosition(parit
);
3287 depth_type maxdepth
= 0;
3288 pit_type
const lastpit
= parit
.lastpit();
3289 for ( ; parit
.pit() <= lastpit
; ++parit
.pit()) {
3290 // reduce depth if necessary
3291 parit
->params().depth(min(parit
->params().depth(), maxdepth
));
3292 maxdepth
= parit
->getMaxDepthAfter();
3294 // set the counter for this paragraph
3295 setLabel(*this, parit
);
3298 InsetList::const_iterator iit
= parit
->insetList().begin();
3299 InsetList::const_iterator end
= parit
->insetList().end();
3300 for (; iit
!= end
; ++iit
) {
3301 parit
.pos() = iit
->pos
;
3302 iit
->inset
->updateLabels(parit
);
3308 bool Buffer::nextWord(DocIterator
& from
, DocIterator
& to
,
3309 docstring
& word
) const
3311 bool inword
= false;
3312 bool ignoreword
= false;
3314 // Go backward a bit if needed in order to return the word currently
3315 // pointed by 'from'.
3316 while (from
&& from
.pos() && isLetter(from
))
3318 // OK, we start from here.
3320 while (to
.depth()) {
3327 lang_code
= to
.paragraph().getFontSettings(params(),
3328 to
.pos()).language()->code();
3330 // Insets like optional hyphens and ligature
3331 // break are part of a word.
3332 if (!to
.paragraph().isInset(to
.pos())) {
3333 char_type
const c
= to
.paragraph().getChar(to
.pos());
3338 } else { // !isLetter(cur)
3339 if (inword
&& !word
.empty() && !ignoreword
)
3351 int Buffer::spellCheck(DocIterator
& from
, DocIterator
& to
,
3352 WordLangTuple
& word_lang
, docstring_list
& suggestions
) const
3355 SpellChecker::Result res
= SpellChecker::OK
;
3356 SpellChecker
* speller
= theSpellChecker();
3357 suggestions
.clear();
3359 while (nextWord(from
, to
, word
)) {
3361 string lang_code
= lyxrc
.spellchecker_use_alt_lang
3362 ? lyxrc
.spellchecker_alt_lang
3363 : from
.paragraph().getFontSettings(params(), from
.pos()).language()->code();
3364 WordLangTuple
wl(word
, lang_code
);
3365 res
= speller
->check(wl
);
3366 // ... just bail out if the spellchecker reports an error.
3367 if (!speller
->error().empty()) {
3368 throw ExceptionMessage(WarningException
,
3369 _("The spellchecker has failed."), speller
->error());
3371 if (res
!= SpellChecker::OK
&& res
!= SpellChecker::IGNORED_WORD
) {
3377 while (!(word
= speller
->nextMiss()).empty())
3378 suggestions
.push_back(word
);