X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2FInsetInclude.cpp;h=137c5a17fb8c47548066e0536b1884615a7e1632;hb=62ca7f3ae55ad2e0c395cb554d71afab87de1ee3;hp=20979947762132b9175827507a2ed22c219e41b0;hpb=b3bff4232a9d0410bae21b9d74584ee469c70e19;p=lyx.git diff --git a/src/insets/InsetInclude.cpp b/src/insets/InsetInclude.cpp index 2097994776..6d512b4b7d 100644 --- a/src/insets/InsetInclude.cpp +++ b/src/insets/InsetInclude.cpp @@ -3,7 +3,8 @@ * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * - * \author Lars Gullik Bjønnes + * \author Lars Gullik Bjønnes + * \author Richard Heck (conversion to InsetCommand) * * Full author contact details are available in file CREDITS. */ @@ -18,18 +19,22 @@ #include "BufferParams.h" #include "BufferView.h" #include "Cursor.h" -#include "debug.h" #include "DispatchResult.h" +#include "ErrorList.h" #include "Exporter.h" +#include "Format.h" #include "FuncRequest.h" #include "FuncStatus.h" -#include "gettext.h" #include "LaTeXFeatures.h" +#include "LayoutFile.h" +#include "LayoutModuleList.h" #include "LyX.h" #include "LyXRC.h" #include "Lexer.h" #include "MetricsInfo.h" +#include "output_xhtml.h" #include "OutputParams.h" +#include "TextClass.h" #include "TocBackend.h" #include "frontends/alert.h" @@ -38,57 +43,28 @@ #include "graphics/PreviewImage.h" #include "graphics/PreviewLoader.h" -#include "insets/RenderPreview.h" +#include "insets/InsetLabel.h" #include "insets/InsetListingsParams.h" +#include "insets/RenderPreview.h" +#include "support/convert.h" +#include "support/debug.h" +#include "support/docstream.h" +#include "support/FileNameList.h" #include "support/filetools.h" +#include "support/gettext.h" +#include "support/lassert.h" #include "support/lstrings.h" // contains #include "support/lyxalgo.h" -#include "support/lyxlib.h" -#include "support/convert.h" #include -#include +using namespace std; +using namespace lyx::support; namespace lyx { - -// Implementation is in LyX.cpp -extern void dispatch(FuncRequest const & action); - -using support::addName; -using support::absolutePath; -using support::bformat; -using support::changeExtension; -using support::contains; -using support::copy; -using support::DocFileName; -using support::FileName; -using support::getFileContents; -using support::getVectorFromString; -using support::isFileReadable; -using support::isLyXFilename; -using support::isValidLaTeXFilename; -using support::latex_path; -using support::makeAbsPath; -using support::makeDisplayPath; -using support::makeRelPath; -using support::onlyFilename; -using support::onlyPath; -using support::prefixIs; -using support::subst; -using support::sum; - -using std::endl; -using std::string; -using std::auto_ptr; -using std::istringstream; -using std::ostream; -using std::ostringstream; -using std::vector; namespace Alert = frontend::Alert; -namespace fs = boost::filesystem; namespace { @@ -100,343 +76,401 @@ docstring const uniqueID() } -bool isListings(InsetCommandParams const & params) -{ - return params.getCmdName() == "lstinputlisting"; -} - -} // namespace anon +/// the type of inclusion +enum Types { + INCLUDE, VERB, INPUT, VERBAST, LISTINGS, NONE +}; -InsetInclude::InsetInclude(InsetCommandParams const & p) - : params_(p), include_label(uniqueID()), - preview_(new RenderMonitoredPreview(this)), - set_label_(false) +Types type(string const & s) { - preview_->fileChanged(boost::bind(&InsetInclude::fileChanged, this)); + if (s == "input") + return INPUT; + if (s == "verbatiminput") + return VERB; + if (s == "verbatiminput*") + return VERBAST; + if (s == "lstinputlisting") + return LISTINGS; + if (s == "include") + return INCLUDE; + return NONE; } -InsetInclude::InsetInclude(InsetInclude const & other) - : Inset(other), - params_(other.params_), - include_label(other.include_label), - preview_(new RenderMonitoredPreview(this)), - set_label_(false) +Types type(InsetCommandParams const & params) { - preview_->fileChanged(boost::bind(&InsetInclude::fileChanged, this)); + return type(params.getCmdName()); } -InsetInclude::~InsetInclude() +bool isListings(InsetCommandParams const & params) { - InsetIncludeMailer(*this).hideDialog(); + return type(params) == LISTINGS; } -void InsetInclude::doDispatch(Cursor & cur, FuncRequest & cmd) +bool isVerbatim(InsetCommandParams const & params) { - switch (cmd.action) { + Types const t = type(params); + return t == VERB || t == VERBAST; +} - case LFUN_INSET_MODIFY: { - InsetCommandParams p("include"); - InsetIncludeMailer::string2params(to_utf8(cmd.argument()), p); - if (!p.getCmdName().empty()) { - if (isListings(p)){ - InsetListingsParams par_old(params().getOptions()); - InsetListingsParams par_new(p.getOptions()); - if (par_old.getParamValue("label") != - par_new.getParamValue("label") - && !par_new.getParamValue("label").empty()) - cur.bv().buffer()->changeRefsIfUnique( - from_utf8(par_old.getParamValue("label")), - from_utf8(par_new.getParamValue("label")), - Inset::REF_CODE); - } - set(p, cur.buffer()); - cur.buffer().updateBibfilesCache(); - } else - cur.noUpdate(); - break; - } - case LFUN_INSET_DIALOG_UPDATE: - InsetIncludeMailer(*this).updateDialog(&cur.bv()); - break; +bool isInputOrInclude(InsetCommandParams const & params) +{ + Types const t = type(params); + return t == INPUT || t == INCLUDE; +} - case LFUN_MOUSE_RELEASE: - if (!cur.selection()) - InsetIncludeMailer(*this).showDialog(&cur.bv()); - break; - default: - Inset::doDispatch(cur, cmd); - break; - } +FileName const masterFileName(Buffer const & buffer) +{ + return buffer.masterBuffer()->fileName(); } -bool InsetInclude::getStatus(Cursor & cur, FuncRequest const & cmd, - FuncStatus & flag) const -{ - switch (cmd.action) { +void add_preview(RenderMonitoredPreview &, InsetInclude const &, Buffer const &); - case LFUN_INSET_MODIFY: - case LFUN_INSET_DIALOG_UPDATE: - flag.enabled(true); - return true; - default: - return Inset::getStatus(cur, cmd, flag); - } +string const parentFilename(Buffer const & buffer) +{ + return buffer.absFileName(); } -InsetCommandParams const & InsetInclude::params() const +FileName const includedFilename(Buffer const & buffer, + InsetCommandParams const & params) { - return params_; + return makeAbsPath(to_utf8(params["filename"]), + onlyPath(parentFilename(buffer))); } -namespace { +InsetLabel * createLabel(Buffer * buf, docstring const & label_str) +{ + if (label_str.empty()) + return 0; + InsetCommandParams icp(LABEL_CODE); + icp["name"] = label_str; + return new InsetLabel(buf, icp); +} -/// the type of inclusion -enum Types { - INCLUDE = 0, - VERB = 1, - INPUT = 2, - VERBAST = 3, - LISTINGS = 4, -}; +} // namespace anon -Types type(InsetCommandParams const & params) +InsetInclude::InsetInclude(Buffer * buf, InsetCommandParams const & p) + : InsetCommand(buf, p, "include"), include_label(uniqueID()), + preview_(new RenderMonitoredPreview(this)), failedtoload_(false), + set_label_(false), label_(0), child_buffer_(0) { - string const command_name = params.getCmdName(); + preview_->fileChanged(boost::bind(&InsetInclude::fileChanged, this)); - if (command_name == "input") - return INPUT; - if (command_name == "verbatiminput") - return VERB; - if (command_name == "verbatiminput*") - return VERBAST; - if (command_name == "lstinputlisting") - return LISTINGS; - return INCLUDE; + if (isListings(params())) { + InsetListingsParams listing_params(to_utf8(p["lstparams"])); + label_ = createLabel(buffer_, from_utf8(listing_params.getParamValue("label"))); + } } -bool isVerbatim(InsetCommandParams const & params) +InsetInclude::InsetInclude(InsetInclude const & other) + : InsetCommand(other), include_label(other.include_label), + preview_(new RenderMonitoredPreview(this)), failedtoload_(false), + set_label_(false), label_(0), child_buffer_(0) { - string const command_name = params.getCmdName(); - return command_name == "verbatiminput" || - command_name == "verbatiminput*"; + preview_->fileChanged(boost::bind(&InsetInclude::fileChanged, this)); + + if (other.label_) + label_ = new InsetLabel(*other.label_); } -bool isInputOrInclude(InsetCommandParams const & params) +InsetInclude::~InsetInclude() { - Types const t = type(params); - return (t == INPUT) || (t == INCLUDE); + delete label_; } -string const masterFilename(Buffer const & buffer) +void InsetInclude::setBuffer(Buffer & buffer) { - return buffer.getMasterBuffer()->fileName(); + InsetCommand::setBuffer(buffer); + if (label_) + label_->setBuffer(buffer); } -string const parentFilename(Buffer const & buffer) +void InsetInclude::setChildBuffer(Buffer * buffer) { - return buffer.fileName(); + child_buffer_ = buffer; } -FileName const includedFilename(Buffer const & buffer, - InsetCommandParams const & params) +ParamInfo const & InsetInclude::findInfo(string const & /* cmdName */) { - return makeAbsPath(to_utf8(params["filename"]), - onlyPath(parentFilename(buffer))); + // FIXME + // This is only correct for the case of listings, but it'll do for now. + // In the other cases, this second parameter should just be empty. + static ParamInfo param_info_; + if (param_info_.empty()) { + param_info_.add("filename", ParamInfo::LATEX_REQUIRED); + param_info_.add("lstparams", ParamInfo::LATEX_OPTIONAL); + } + return param_info_; } -void add_preview(RenderMonitoredPreview &, InsetInclude const &, Buffer const &); - -} // namespace anon +bool InsetInclude::isCompatibleCommand(string const & s) +{ + return type(s) != NONE; +} -void InsetInclude::set(InsetCommandParams const & p, Buffer const & buffer) +void InsetInclude::doDispatch(Cursor & cur, FuncRequest & cmd) { - params_ = p; - set_label_ = false; + switch (cmd.action()) { - if (preview_->monitoring()) - preview_->stopMonitoring(); + case LFUN_INSET_EDIT: { + editIncluded(to_utf8(params()["filename"])); + break; + } - if (type(params_) == INPUT) - add_preview(*preview_, *this, buffer); + case LFUN_INSET_MODIFY: { + // It should be OK just to invalidate the cache is setParams() + // If not.... + // child_buffer_ = 0; + InsetCommandParams p(INCLUDE_CODE); + if (cmd.getArg(0) == "changetype") { + InsetCommand::doDispatch(cur, cmd); + p = params(); + } else + InsetCommand::string2params("include", to_utf8(cmd.argument()), p); + if (!p.getCmdName().empty()) { + if (isListings(p)){ + InsetListingsParams new_params(to_utf8(p["lstparams"])); + docstring const new_label = + from_utf8(new_params.getParamValue("label")); + + if (new_label.empty()) { + delete label_; + label_ = 0; + } else { + docstring old_label; + if (label_) + old_label = label_->getParam("name"); + else { + label_ = createLabel(buffer_, new_label); + label_->setBuffer(buffer()); + } + + if (new_label != old_label) { + label_->updateCommand(new_label); + // the label might have been adapted (duplicate) + if (new_label != label_->getParam("name")) { + new_params.addParam("label", "{" + + to_utf8(label_->getParam("name")) + "}", true); + p["lstparams"] = from_utf8(new_params.params()); + } + } + } + } + setParams(p); + } else + cur.noUpdate(); + break; + } + + //pass everything else up the chain + default: + InsetCommand::doDispatch(cur, cmd); + break; + } } -auto_ptr InsetInclude::doClone() const +void InsetInclude::editIncluded(string const & file) { - return auto_ptr(new InsetInclude(*this)); + string const ext = support::getExtension(file); + if (ext == "lyx") { + FuncRequest fr(LFUN_BUFFER_CHILD_OPEN, file); + lyx::dispatch(fr); + } else + // tex file or other text file in verbatim mode + formats.edit(buffer(), + support::makeAbsPath(file, support::onlyPath(buffer().absFileName())), + "text"); } -void InsetInclude::write(Buffer const &, ostream & os) const +bool InsetInclude::getStatus(Cursor & cur, FuncRequest const & cmd, + FuncStatus & flag) const { - write(os); -} + switch (cmd.action()) { + case LFUN_INSET_EDIT: + flag.setEnabled(true); + return true; -void InsetInclude::write(ostream & os) const -{ - os << "Include " << to_utf8(params_.getCommand()) << '\n' - << "preview " << convert(params_.preview()) << '\n'; + case LFUN_INSET_MODIFY: + if (cmd.getArg(0) == "changetype") + return InsetCommand::getStatus(cur, cmd, flag); + else + flag.setEnabled(true); + return true; + + default: + return InsetCommand::getStatus(cur, cmd, flag); + } } -void InsetInclude::read(Buffer const &, Lexer & lex) +void InsetInclude::setParams(InsetCommandParams const & p) { - read(lex); + // invalidate the cache + child_buffer_ = 0; + + InsetCommand::setParams(p); + set_label_ = false; + + if (preview_->monitoring()) + preview_->stopMonitoring(); + + if (type(params()) == INPUT) + add_preview(*preview_, *this, buffer()); + + buffer().updateBibfilesCache(); } -void InsetInclude::read(Lexer & lex) +bool InsetInclude::isChildIncluded() const { - if (lex.isOK()) { - lex.eatLine(); - string const command = lex.getString(); - params_.scanCommand(command); - } - string token; - while (lex.isOK()) { - lex.next(); - token = lex.getString(); - if (token == "\\end_inset") - break; - if (token == "preview") { - lex.next(); - params_.preview(lex.getBool()); - } else - lex.printError("Unknown parameter name `$$Token' for command " + params_.getCmdName()); - } - if (token != "\\end_inset") { - lex.printError("Missing \\end_inset at this point. " - "Read: `$$Token'"); - } + std::list includeonlys = + buffer().params().getIncludedChildren(); + if (includeonlys.empty()) + return true; + return (std::find(includeonlys.begin(), + includeonlys.end(), + to_utf8(params()["filename"])) != includeonlys.end()); } -docstring const InsetInclude::getScreenLabel(Buffer const & buf) const +docstring InsetInclude::screenLabel() const { docstring temp; - switch (type(params_)) { + switch (type(params())) { case INPUT: - temp = buf.B_("Input"); + temp = buffer().B_("Input"); break; case VERB: - temp = buf.B_("Verbatim Input"); + temp = buffer().B_("Verbatim Input"); break; case VERBAST: - temp = buf.B_("Verbatim Input*"); + temp = buffer().B_("Verbatim Input*"); break; case INCLUDE: - temp = buf.B_("Include"); + if (isChildIncluded()) + temp = buffer().B_("Include"); + else + temp += buffer().B_("Include (excluded)"); break; - case LISTINGS: { + case LISTINGS: temp = listings_label_; - } + break; + case NONE: + LASSERT(false, /**/); } temp += ": "; - if (params_["filename"].empty()) + if (params()["filename"].empty()) temp += "???"; else - temp += from_utf8(onlyFilename(to_utf8(params_["filename"]))); + temp += from_utf8(onlyFilename(to_utf8(params()["filename"]))); return temp; } -namespace { - -/// return the child buffer if the file is a LyX doc and is loaded -Buffer * getChildBuffer(Buffer const & buffer, InsetCommandParams const & params) +Buffer * InsetInclude::getChildBuffer() const { - if (isVerbatim(params) || isListings(params)) - return 0; - - string const included_file = includedFilename(buffer, params).absFilename(); - if (!isLyXFilename(included_file)) - return 0; + Buffer * childBuffer = loadIfNeeded(); - Buffer * childBuffer = theBufferList().getBuffer(included_file); - - //FIXME RECURSIVE INCLUDES - if (childBuffer == & buffer) - return 0; - else - return childBuffer; + // FIXME: recursive includes + return (childBuffer == &buffer()) ? 0 : childBuffer; } -} // namespace anon - -Buffer * loadIfNeeded(Buffer const & buffer, InsetCommandParams const & params) +Buffer * InsetInclude::loadIfNeeded() const { - if (isVerbatim(params) || isListings(params)) + // This is for background export and preview. We don't even want to + // try to load the cloned child document again. + if (buffer().isClone()) + return child_buffer_; + + // Don't try to load it again if we failed before. + if (failedtoload_ || isVerbatim(params()) || isListings(params())) return 0; - FileName const included_file = includedFilename(buffer, params); - if (!isLyXFilename(included_file.absFilename())) + FileName const included_file = includedFilename(buffer(), params()); + // Use cached Buffer if possible. + if (child_buffer_ != 0) { + if (theBufferList().isLoaded(child_buffer_) + // additional sanity check: make sure the Buffer really is + // associated with the file we want. + && child_buffer_ == theBufferList().getBuffer(included_file)) + return child_buffer_; + // Buffer vanished, so invalidate cache and try to reload. + child_buffer_ = 0; + } + + if (!isLyXFilename(included_file.absFileName())) return 0; - Buffer * buf = theBufferList().getBuffer(included_file.absFilename()); - if (!buf) { + Buffer * child = theBufferList().getBuffer(included_file); + if (!child) { // the readonly flag can/will be wrong, not anymore I think. - if (!fs::exists(included_file.toFilesystemEncoding())) - return false; - if (use_gui) { - lyx::dispatch(FuncRequest(LFUN_BUFFER_CHILD_OPEN, - included_file.absFilename() + "|true")); - buf = theBufferList().getBuffer(included_file.absFilename()); + if (!included_file.exists()) + return 0; + + child = theBufferList().newBuffer(included_file.absFileName()); + if (!child) + // Buffer creation is not possible. + return 0; + + if (!child->loadLyXFile(included_file)) { + failedtoload_ = true; + //close the buffer we just opened + theBufferList().release(child); + return 0; } - else { - buf = theBufferList().newBuffer(included_file.absFilename()); - if (!loadLyXFile(buf, included_file)) { - //close the buffer we just opened - theBufferList().close(buf, false); - return false; - } + + if (!child->errorList("Parse").empty()) { + // FIXME: Do something. } } - buf->setParentName(parentFilename(buffer)); - return buf; + child->setParent(&buffer()); + // Cache the child buffer. + child_buffer_ = child; + return child; } -int InsetInclude::latex(Buffer const & buffer, odocstream & os, - OutputParams const & runparams) const +int InsetInclude::latex(odocstream & os, OutputParams const & runparams) const { - string incfile(to_utf8(params_["filename"])); + string incfile = to_utf8(params()["filename"]); // Do nothing if no file name has been specified if (incfile.empty()) return 0; - FileName const included_file(includedFilename(buffer, params_)); + FileName const included_file = includedFilename(buffer(), params()); - //Check we're not trying to include ourselves. - //FIXME RECURSIVE INCLUDE - //This isn't sufficient, as the inclusion could be downstream. - //But it'll have to do for now. - if (isInputOrInclude(params_) && - buffer.fileName() == included_file.absFilename()) + // Check we're not trying to include ourselves. + // FIXME RECURSIVE INCLUDE + // This isn't sufficient, as the inclusion could be downstream. + // But it'll have to do for now. + if (isInputOrInclude(params()) && + buffer().absFileName() == included_file.absFileName()) { Alert::error(_("Recursive input"), bformat(_("Attempted to include file %1$s in itself! " @@ -444,89 +478,119 @@ int InsetInclude::latex(Buffer const & buffer, odocstream & os, return 0; } - Buffer const * const m_buffer = buffer.getMasterBuffer(); + Buffer const * const masterBuffer = buffer().masterBuffer(); // if incfile is relative, make it relative to the master // buffer directory. - if (!absolutePath(incfile)) { + if (!FileName::isAbsolute(incfile)) { // FIXME UNICODE - incfile = to_utf8(makeRelPath(from_utf8(included_file.absFilename()), - from_utf8(m_buffer->filePath()))); + incfile = to_utf8(makeRelPath(from_utf8(included_file.absFileName()), + from_utf8(masterBuffer->filePath()))); } // write it to a file (so far the complete file) - string const exportfile = changeExtension(incfile, ".tex"); - string const mangled = - DocFileName(changeExtension(included_file.absFilename(),".tex")). + string exportfile; + string mangled; + // bug 5681 + if (type(params()) == LISTINGS) { + exportfile = incfile; + mangled = DocFileName(included_file).mangledFilename(); + } else { + exportfile = changeExtension(incfile, ".tex"); + mangled = DocFileName(changeExtension(included_file.absFileName(), ".tex")). mangledFilename(); - FileName const writefile(makeAbsPath(mangled, m_buffer->temppath())); + } + + FileName const writefile(makeAbsPath(mangled, masterBuffer->temppath())); if (!runparams.nice) incfile = mangled; else if (!isValidLaTeXFilename(incfile)) { frontend::Alert::warning(_("Invalid filename"), - _("The following filename is likely to cause trouble " + _("The following filename is likely to cause trouble " "when running the exported file through LaTeX: ") + from_utf8(incfile)); } - LYXERR(Debug::LATEX) << "incfile:" << incfile << endl; - LYXERR(Debug::LATEX) << "exportfile:" << exportfile << endl; - LYXERR(Debug::LATEX) << "writefile:" << writefile << endl; + LYXERR(Debug::LATEX, "incfile:" << incfile); + LYXERR(Debug::LATEX, "exportfile:" << exportfile); + LYXERR(Debug::LATEX, "writefile:" << writefile); if (runparams.inComment || runparams.dryrun) { //Don't try to load or copy the file if we're //in a comment or doing a dryrun - } else if (isInputOrInclude(params_) && - isLyXFilename(included_file.absFilename())) { + } else if (isInputOrInclude(params()) && + isLyXFilename(included_file.absFileName())) { //if it's a LyX file and we're inputting or including, //try to load it so we can write the associated latex - if (!loadIfNeeded(buffer, params_)) + if (!loadIfNeeded()) return false; - Buffer * tmp = theBufferList().getBuffer(included_file.absFilename()); + Buffer * tmp = theBufferList().getBuffer(included_file); - if (tmp->params().textclass != m_buffer->params().textclass) { + if (tmp->params().baseClass() != masterBuffer->params().baseClass()) { // FIXME UNICODE docstring text = bformat(_("Included file `%1$s'\n" - "has textclass `%2$s'\n" - "while parent file has textclass `%3$s'."), - makeDisplayPath(included_file.absFilename()), - from_utf8(tmp->params().getTextClass().name()), - from_utf8(m_buffer->params().getTextClass().name())); - Alert::warning(_("Different textclasses"), text); - //return 0; + "has textclass `%2$s'\n" + "while parent file has textclass `%3$s'."), + included_file.displayName(), + from_utf8(tmp->params().documentClass().name()), + from_utf8(masterBuffer->params().documentClass().name())); + Alert::warning(_("Different textclasses"), text, true); + } + + // Make sure modules used in child are all included in master + // FIXME It might be worth loading the children's modules into the master + // over in BufferParams rather than doing this check. + LayoutModuleList const masterModules = masterBuffer->params().getModules(); + LayoutModuleList const childModules = tmp->params().getModules(); + LayoutModuleList::const_iterator it = childModules.begin(); + LayoutModuleList::const_iterator end = childModules.end(); + for (; it != end; ++it) { + string const module = *it; + LayoutModuleList::const_iterator found = + find(masterModules.begin(), masterModules.end(), module); + if (found == masterModules.end()) { + docstring text = bformat(_("Included file `%1$s'\n" + "uses module `%2$s'\n" + "which is not used in parent file."), + included_file.displayName(), from_utf8(module)); + Alert::warning(_("Module not found"), text); + } } - tmp->markDepClean(m_buffer->temppath()); + tmp->markDepClean(masterBuffer->temppath()); -// FIXME: handle non existing files -// FIXME: Second argument is irrelevant! -// since only_body is true, makeLaTeXFile will not look at second -// argument. Should we set it to string(), or should makeLaTeXFile -// make use of it somehow? (JMarc 20031002) + // FIXME: handle non existing files + // FIXME: Second argument is irrelevant! + // since only_body is true, makeLaTeXFile will not look at second + // argument. Should we set it to string(), or should makeLaTeXFile + // make use of it somehow? (JMarc 20031002) // The included file might be written in a different encoding + // and language. Encoding const * const oldEnc = runparams.encoding; + Language const * const oldLang = runparams.master_language; runparams.encoding = &tmp->params().encoding(); + runparams.master_language = buffer().params().language; tmp->makeLaTeXFile(writefile, - onlyPath(masterFilename(buffer)), + masterFileName(buffer()).onlyPath().absFileName(), runparams, false); runparams.encoding = oldEnc; + runparams.master_language = oldLang; } else { // In this case, it's not a LyX file, so we copy the file // to the temp dir, so that .aux files etc. are not created // in the original dir. Files included by this file will be // found via input@path, see ../Buffer.cpp. - unsigned long const checksum_in = sum(included_file); - unsigned long const checksum_out = sum(writefile); + unsigned long const checksum_in = included_file.checksum(); + unsigned long const checksum_out = writefile.checksum(); if (checksum_in != checksum_out) { - if (!copy(included_file, writefile)) { + if (!included_file.copyTo(writefile)) { // FIXME UNICODE - LYXERR(Debug::LATEX) - << to_utf8(bformat(_("Could not copy the file\n%1$s\n" + LYXERR(Debug::LATEX, + to_utf8(bformat(_("Could not copy the file\n%1$s\n" "into the temporary directory."), - from_utf8(included_file.absFilename()))) - << endl; + from_utf8(included_file.absFileName())))); return 0; } } @@ -534,37 +598,45 @@ int InsetInclude::latex(Buffer const & buffer, odocstream & os, string const tex_format = (runparams.flavor == OutputParams::LATEX) ? "latex" : "pdflatex"; - if (isVerbatim(params_)) { + switch (type(params())) { + case VERB: + case VERBAST: { incfile = latex_path(incfile); // FIXME UNICODE - os << '\\' << from_ascii(params_.getCmdName()) << '{' + os << '\\' << from_ascii(params().getCmdName()) << '{' << from_utf8(incfile) << '}'; - } else if (type(params_) == INPUT) { + break; + } + case INPUT: { runparams.exportdata->addExternalFile(tex_format, writefile, exportfile); // \input wants file with extension (default is .tex) - if (!isLyXFilename(included_file.absFilename())) { + if (!isLyXFilename(included_file.absFileName())) { incfile = latex_path(incfile); // FIXME UNICODE - os << '\\' << from_ascii(params_.getCmdName()) + os << '\\' << from_ascii(params().getCmdName()) << '{' << from_utf8(incfile) << '}'; } else { incfile = changeExtension(incfile, ".tex"); incfile = latex_path(incfile); // FIXME UNICODE - os << '\\' << from_ascii(params_.getCmdName()) + os << '\\' << from_ascii(params().getCmdName()) << '{' << from_utf8(incfile) << '}'; } - } else if (type(params_) == LISTINGS) { - os << '\\' << from_ascii(params_.getCmdName()); - string opt = params_.getOptions(); + break; + } + case LISTINGS: { + os << '\\' << from_ascii(params().getCmdName()); + string const opt = to_utf8(params()["lstparams"]); // opt is set in QInclude dialog and should have passed validation. InsetListingsParams params(opt); if (!params.params().empty()) os << "[" << from_utf8(params.params()) << "]"; os << '{' << from_utf8(incfile) << '}'; - } else { + break; + } + case INCLUDE: { runparams.exportdata->addExternalFile(tex_format, writefile, exportfile); @@ -573,49 +645,98 @@ int InsetInclude::latex(Buffer const & buffer, odocstream & os, incfile = changeExtension(incfile, string()); incfile = latex_path(incfile); // FIXME UNICODE - os << '\\' << from_ascii(params_.getCmdName()) << '{' + os << '\\' << from_ascii(params().getCmdName()) << '{' << from_utf8(incfile) << '}'; + break; + } + case NONE: + break; } return 0; } -int InsetInclude::plaintext(Buffer const & buffer, odocstream & os, - OutputParams const &) const +docstring InsetInclude::xhtml(XHTMLStream & xs, OutputParams const &rp) const { - if (isVerbatim(params_) || isListings(params_)) { - os << '[' << getScreenLabel(buffer) << '\n'; - // FIXME: We don't know the encoding of the file - docstring const str = - from_utf8(getFileContents(includedFilename(buffer, params_))); - os << str; + if (rp.inComment) + return docstring(); + + // For verbatim and listings, we just include the contents of the file as-is. + // In the case of listings, we wrap it in
.
+	bool const listing = isListings(params());
+	if (listing || isVerbatim(params())) {
+		if (listing)
+			xs << html::StartTag("pre");
+		// FIXME: We don't know the encoding of the file, default to UTF-8.
+		xs << includedFilename(buffer(), params()).fileContents("UTF-8");
+		if (listing)
+			xs << html::EndTag("pre");
+		return docstring();
+	}
+
+	// We don't (yet) know how to Input or Include non-LyX files.
+	// (If we wanted to get really arcane, we could run some tex2html
+	// converter on the included file. But that's just masochistic.)
+	FileName const included_file = includedFilename(buffer(), params());
+	if (!isLyXFilename(included_file.absFileName())) {
+		frontend::Alert::warning(_("Unsupported Inclusion"),
+					 bformat(_("LyX does not know how to include non-LyX files when "
+					           "generating HTML output. Offending file:\n%1$s"),
+					            params()["filename"]));
+		return docstring();
+	}
+
+	// In the other cases, we will generate the HTML and include it.
+
+	// Check we're not trying to include ourselves.
+	// FIXME RECURSIVE INCLUDE
+	if (buffer().absFileName() == included_file.absFileName()) {
+		Alert::error(_("Recursive input"),
+			       bformat(_("Attempted to include file %1$s in itself! "
+			       "Ignoring inclusion."), params()["filename"]));
+		return docstring();
+	}
+
+	Buffer const * const ibuf = loadIfNeeded();
+	if (!ibuf)
+		return docstring();
+	ibuf->writeLyXHTMLSource(xs.os(), rp, true);
+	return docstring();
+}
+
+
+int InsetInclude::plaintext(odocstream & os, OutputParams const &) const
+{
+	if (isVerbatim(params()) || isListings(params())) {
+		os << '[' << screenLabel() << '\n';
+		// FIXME: We don't know the encoding of the file, default to UTF-8.
+		os << includedFilename(buffer(), params()).fileContents("UTF-8");
 		os << "\n]";
 		return PLAINTEXT_NEWLINE + 1; // one char on a separate line
 	} else {
-		docstring const str = '[' + getScreenLabel(buffer) + ']';
+		docstring const str = '[' + screenLabel() + ']';
 		os << str;
 		return str.size();
 	}
 }
 
 
-int InsetInclude::docbook(Buffer const & buffer, odocstream & os,
-			  OutputParams const & runparams) const
+int InsetInclude::docbook(odocstream & os, OutputParams const & runparams) const
 {
-	string incfile = to_utf8(params_["filename"]);
+	string incfile = to_utf8(params()["filename"]);
 
 	// Do nothing if no file name has been specified
 	if (incfile.empty())
 		return 0;
 
-	string const included_file = includedFilename(buffer, params_).absFilename();
+	string const included_file = includedFilename(buffer(), params()).absFileName();
 
-	//Check we're not trying to include ourselves.
-	//FIXME RECURSIVE INCLUDE
-	//This isn't sufficient, as the inclusion could be downstream.
-	//But it'll have to do for now.
-	if (buffer.fileName() == included_file) {
+	// Check we're not trying to include ourselves.
+	// FIXME RECURSIVE INCLUDE
+	// This isn't sufficient, as the inclusion could be downstream.
+	// But it'll have to do for now.
+	if (buffer().absFileName() == included_file) {
 		Alert::error(_("Recursive input"),
 			       bformat(_("Attempted to include file %1$s in itself! "
 			       "Ignoring inclusion."), from_utf8(incfile)));
@@ -626,18 +747,18 @@ int InsetInclude::docbook(Buffer const & buffer, odocstream & os,
 	string const exportfile = changeExtension(incfile, ".sgml");
 	DocFileName writefile(changeExtension(included_file, ".sgml"));
 
-	if (loadIfNeeded(buffer, params_)) {
-		Buffer * tmp = theBufferList().getBuffer(included_file);
+	if (loadIfNeeded()) {
+		Buffer * tmp = theBufferList().getBuffer(FileName(included_file));
 
 		string const mangled = writefile.mangledFilename();
 		writefile = makeAbsPath(mangled,
-					buffer.getMasterBuffer()->temppath());
+					buffer().masterBuffer()->temppath());
 		if (!runparams.nice)
 			incfile = mangled;
 
-		LYXERR(Debug::LATEX) << "incfile:" << incfile << endl;
-		LYXERR(Debug::LATEX) << "exportfile:" << exportfile << endl;
-		LYXERR(Debug::LATEX) << "writefile:" << writefile << endl;
+		LYXERR(Debug::LATEX, "incfile:" << incfile);
+		LYXERR(Debug::LATEX, "exportfile:" << exportfile);
+		LYXERR(Debug::LATEX, "writefile:" << writefile);
 
 		tmp->makeDocBookFile(writefile, runparams, true);
 	}
@@ -647,7 +768,7 @@ int InsetInclude::docbook(Buffer const & buffer, odocstream & os,
 	runparams.exportdata->addExternalFile("docbook-xml", writefile,
 					      exportfile);
 
-	if (isVerbatim(params_) || isListings(params_)) {
+	if (isVerbatim(params()) || isListings(params())) {
 		os << "";
@@ -660,120 +781,94 @@ int InsetInclude::docbook(Buffer const & buffer, odocstream & os,
 
 void InsetInclude::validate(LaTeXFeatures & features) const
 {
-	string incfile(to_utf8(params_["filename"]));
+	string incfile = to_utf8(params()["filename"]);
 	string writefile;
 
-	Buffer const & buffer = features.buffer();
+	LASSERT(&buffer() == &features.buffer(), /**/);
 
-	string const included_file = includedFilename(buffer, params_).absFilename();
+	string const included_file =
+		includedFilename(buffer(), params()).absFileName();
 
 	if (isLyXFilename(included_file))
 		writefile = changeExtension(included_file, ".sgml");
 	else
 		writefile = included_file;
 
-	if (!features.runparams().nice && !isVerbatim(params_) && !isListings(params_)) {
+	if (!features.runparams().nice && !isVerbatim(params()) && !isListings(params())) {
 		incfile = DocFileName(writefile).mangledFilename();
 		writefile = makeAbsPath(incfile,
-					buffer.getMasterBuffer()->temppath()).absFilename();
+					buffer().masterBuffer()->temppath()).absFileName();
 	}
 
 	features.includeFile(include_label, writefile);
 
-	if (isVerbatim(params_))
+	if (isVerbatim(params()))
 		features.require("verbatim");
-	else if (isListings(params_))
+	else if (isListings(params()))
 		features.require("listings");
 
 	// Here we must do the fun stuff...
 	// Load the file in the include if it needs
 	// to be loaded:
-	if (loadIfNeeded(buffer, params_)) {
+	if (loadIfNeeded()) {
 		// a file got loaded
-		Buffer * const tmp = theBufferList().getBuffer(included_file);
+		Buffer * const tmp = theBufferList().getBuffer(FileName(included_file));
 		// make sure the buffer isn't us
 		// FIXME RECURSIVE INCLUDES
 		// This is not sufficient, as recursive includes could be
 		// more than a file away. But it will do for now.
-		if (tmp && tmp != & buffer) {
+		if (tmp && tmp != &buffer()) {
 			// We must temporarily change features.buffer,
 			// otherwise it would always be the master buffer,
 			// and nested includes would not work.
 			features.setBuffer(*tmp);
 			tmp->validate(features);
-			features.setBuffer(buffer);
+			features.setBuffer(buffer());
 		}
 	}
 }
 
 
-void InsetInclude::getLabelList(Buffer const & buffer,
-				std::vector & list) const
+void InsetInclude::fillWithBibKeys(BiblioInfo & keys,
+	InsetIterator const & /*di*/) const
 {
-	if (isListings(params_)) {
-		InsetListingsParams params(params_.getOptions());
-		string label = params.getParamValue("label");
-		if (!label.empty())
-			list.push_back(from_utf8(label));
-	}
-	else if (loadIfNeeded(buffer, params_)) {
-		string const included_file = includedFilename(buffer, params_).absFilename();
-		Buffer * tmp = theBufferList().getBuffer(included_file);
-		tmp->setParentName("");
-		tmp->getLabelList(list);
-		tmp->setParentName(parentFilename(buffer));
+	if (loadIfNeeded()) {
+		string const included_file = includedFilename(buffer(), params()).absFileName();
+		Buffer * tmp = theBufferList().getBuffer(FileName(included_file));
+		BiblioInfo const & newkeys = tmp->localBibInfo();
+		keys.mergeBiblioInfo(newkeys);
 	}
 }
 
 
-void InsetInclude::fillWithBibKeys(Buffer const & buffer,
-		BiblioInfo & keys, InsetIterator const & /*di*/) const
+void InsetInclude::updateBibfilesCache()
 {
-	if (loadIfNeeded(buffer, params_)) {
-		string const included_file = includedFilename(buffer, params_).absFilename();
-		Buffer * tmp = theBufferList().getBuffer(included_file);
-		//FIXME This is kind of a dirty hack and should be made reasonable.
-		tmp->setParentName("");
-		keys.fillWithBibKeys(tmp);
-		tmp->setParentName(parentFilename(buffer));
-	}
+	Buffer const * const child = getChildBuffer();
+	if (child)
+		child->updateBibfilesCache(Buffer::UpdateChildOnly);
 }
 
 
-void InsetInclude::updateBibfilesCache(Buffer const & buffer)
+support::FileNameList const &
+	InsetInclude::getBibfilesCache() const
 {
-	Buffer * const tmp = getChildBuffer(buffer, params_);
-	if (tmp) {
-		tmp->setParentName("");
-		tmp->updateBibfilesCache();
-		tmp->setParentName(parentFilename(buffer));
-	}
-}
-
+	Buffer const * const child = getChildBuffer();
+	if (child)
+		return child->getBibfilesCache(Buffer::UpdateChildOnly);
 
-std::vector const &
-InsetInclude::getBibfilesCache(Buffer const & buffer) const
-{
-	Buffer * const tmp = getChildBuffer(buffer, params_);
-	if (tmp) {
-		tmp->setParentName("");
-		std::vector const & cache = tmp->getBibfilesCache();
-		tmp->setParentName(parentFilename(buffer));
-		return cache;
-	}
-	static std::vector const empty;
+	static support::FileNameList const empty;
 	return empty;
 }
 
 
-bool InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
+void InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
 {
-	BOOST_ASSERT(mi.base.bv && mi.base.bv->buffer());
+	LASSERT(mi.base.bv, /**/);
 
 	bool use_preview = false;
 	if (RenderPreview::status() != LyXRC::PREVIEW_OFF) {
 		graphics::PreviewImage const * pimage =
-			preview_->getPreviewImage(*mi.base.bv->buffer());
+			preview_->getPreviewImage(mi.base.bv->buffer());
 		use_preview = pimage && pimage->image();
 	}
 
@@ -782,31 +877,24 @@ bool InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
 	} else {
 		if (!set_label_) {
 			set_label_ = true;
-			button_.update(getScreenLabel(*mi.base.bv->buffer()),
-				       true);
+			button_.update(screenLabel(), true);
 		}
 		button_.metrics(mi, dim);
 	}
 
 	Box b(0, dim.wid, -dim.asc, dim.des);
 	button_.setBox(b);
-
-	bool const changed = dim_ != dim;
-	dim_ = dim;
-	return changed;
 }
 
 
 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
 {
-	setPosCache(pi, x, y);
-
-	BOOST_ASSERT(pi.base.bv && pi.base.bv->buffer());
+	LASSERT(pi.base.bv, /**/);
 
 	bool use_preview = false;
 	if (RenderPreview::status() != LyXRC::PREVIEW_OFF) {
 		graphics::PreviewImage const * pimage =
-			preview_->getPreviewImage(*pi.base.bv->buffer());
+			preview_->getPreviewImage(pi.base.bv->buffer());
 		use_preview = pimage && pimage->image();
 	}
 
@@ -817,9 +905,15 @@ void InsetInclude::draw(PainterInfo & pi, int x, int y) const
 }
 
 
+docstring InsetInclude::contextMenu(BufferView const &, int, int) const
+{
+	return from_ascii("context-include");
+}
+
+
 Inset::DisplayType InsetInclude::display() const
 {
-	return type(params_) == INPUT ? Inline : AlignCenter;
+	return type(params()) == INPUT ? Inline : AlignCenter;
 }
 
 
@@ -830,14 +924,13 @@ Inset::DisplayType InsetInclude::display() const
 
 void InsetInclude::fileChanged() const
 {
-	Buffer const * const buffer_ptr = LyX::cref().updateInset(this);
-	if (!buffer_ptr)
+	Buffer const * const buffer = updateFrontend();
+	if (!buffer)
 		return;
 
-	Buffer const & buffer = *buffer_ptr;
-	preview_->removePreview(buffer);
-	add_preview(*preview_.get(), *this, buffer);
-	preview_->startLoading(buffer);
+	preview_->removePreview(*buffer);
+	add_preview(*preview_.get(), *this, *buffer);
+	preview_->startLoading(*buffer);
 }
 
 
@@ -848,18 +941,18 @@ bool preview_wanted(InsetCommandParams const & params, Buffer const & buffer)
 	FileName const included_file = includedFilename(buffer, params);
 
 	return type(params) == INPUT && params.preview() &&
-		isFileReadable(included_file);
+		included_file.isReadableFile();
 }
 
 
-docstring const latex_string(InsetInclude const & inset, Buffer const & buffer)
+docstring latexString(InsetInclude const & inset)
 {
 	odocstringstream os;
 	// We don't need to set runparams.encoding since this will be done
 	// by latex() anyway.
 	OutputParams runparams(0);
 	runparams.flavor = OutputParams::LATEX;
-	inset.latex(buffer, os, runparams);
+	inset.latex(os, runparams);
 
 	return os.str();
 }
@@ -872,7 +965,7 @@ void add_preview(RenderMonitoredPreview & renderer, InsetInclude const & inset,
 	if (RenderPreview::status() != LyXRC::PREVIEW_OFF &&
 	    preview_wanted(params, buffer)) {
 		renderer.setAbsFile(includedFilename(buffer, params));
-		docstring const snippet = latex_string(inset, buffer);
+		docstring const snippet = latexString(inset);
 		renderer.addPreview(snippet, buffer);
 	}
 }
@@ -880,119 +973,103 @@ void add_preview(RenderMonitoredPreview & renderer, InsetInclude const & inset,
 } // namespace anon
 
 
-void InsetInclude::addPreview(graphics::PreviewLoader & ploader) const
+void InsetInclude::addPreview(DocIterator const & /*inset_pos*/,
+	graphics::PreviewLoader & ploader) const
 {
 	Buffer const & buffer = ploader.buffer();
-	if (preview_wanted(params(), buffer)) {
-		preview_->setAbsFile(includedFilename(buffer, params()));
-		docstring const snippet = latex_string(*this, buffer);
-		preview_->addPreview(snippet, ploader);
-	}
+	if (!preview_wanted(params(), buffer))
+		return;
+	preview_->setAbsFile(includedFilename(buffer, params()));
+	docstring const snippet = latexString(*this);
+	preview_->addPreview(snippet, ploader);
 }
 
 
-void InsetInclude::addToToc(TocList & toclist, Buffer const & buffer, ParConstIterator const & pit) const
+void InsetInclude::addToToc(DocIterator const & cpit)
 {
-	if (isListings(params_)) {
-		InsetListingsParams params(params_.getOptions());
-		string caption = params.getParamValue("caption");
-		if (!caption.empty()) {
-			Toc & toc = toclist["listing"];
-			docstring const str = convert(toc.size() + 1)
-				+ ". " +  from_utf8(caption);
-			// This inset does not have a valid ParConstIterator 
-			// so it has to use the iterator of its parent paragraph
-			toc.push_back(TocItem(pit, 0, str));
-		}
+	TocBackend & backend = buffer().tocBackend();
+
+	if (isListings(params())) {
+		if (label_)
+			label_->addToToc(cpit);
+
+		InsetListingsParams p(to_utf8(params()["lstparams"]));
+		string caption = p.getParamValue("caption");
+		if (caption.empty())
+			return;
+		Toc & toc = backend.toc("listing");
+		docstring str = convert(toc.size() + 1)
+			+ ". " +  from_utf8(caption);
+		DocIterator pit = cpit;
+		toc.push_back(TocItem(pit, 0, str));
 		return;
 	}
-	Buffer const * const childbuffer = getChildBuffer(buffer, params_);
+	Buffer const * const childbuffer = getChildBuffer();
 	if (!childbuffer)
 		return;
 
+	Toc & toc = backend.toc("child");
+	docstring str = childbuffer->fileName().displayName();
+	toc.push_back(TocItem(cpit, 0, str));
+
+	TocList & toclist = backend.tocs();
+	childbuffer->tocBackend().update();
 	TocList const & childtoclist = childbuffer->tocBackend().tocs();
 	TocList::const_iterator it = childtoclist.begin();
 	TocList::const_iterator const end = childtoclist.end();
 	for(; it != end; ++it)
 		toclist[it->first].insert(toclist[it->first].end(),
-				it->second.begin(), it->second.end());
-}
-
-
-void InsetInclude::updateLabels(Buffer const & buffer, 
-				ParIterator const &)
-{
-	Buffer const * const childbuffer = getChildBuffer(buffer, params_);
-	if (childbuffer)
-		lyx::updateLabels(*childbuffer, true);
-	else if (isListings(params_)) {
-		InsetListingsParams const par = params_.getOptions();
-		if (par.getParamValue("caption").empty())
-			listings_label_.clear();
-		else {
-			Counters & counters = buffer.params().getTextClass().counters();
-			docstring const cnt = from_ascii("listing");
-			if (counters.hasCounter(cnt)) {
-				counters.step(cnt);
-				listings_label_ = buffer.B_("Program Listing ") + convert(counters.value(cnt));
-			} else
-				listings_label_ = buffer.B_("Program Listing");
-		}
-	}
+			it->second.begin(), it->second.end());
 }
 
 
-string const InsetIncludeMailer::name_("include");
-
-InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
-	: inset_(inset)
-{}
+void InsetInclude::updateCommand()
+{
+	if (!label_)
+		return;
 
+	docstring old_label = label_->getParam("name");
+	label_->updateCommand(old_label, false);
+	// the label might have been adapted (duplicate)
+	docstring new_label = label_->getParam("name");
+	if (old_label == new_label)
+		return;
 
-string const InsetIncludeMailer::inset2string(Buffer const &) const
-{
-	return params2string(inset_.params());
+	// update listings parameters...
+	InsetCommandParams p(INCLUDE_CODE);
+	p = params();
+	InsetListingsParams par(to_utf8(params()["lstparams"]));
+	par.addParam("label", "{" + to_utf8(new_label) + "}", true);
+	p["lstparams"] = from_utf8(par.params());
+	setParams(p);	
 }
 
-
-void InsetIncludeMailer::string2params(string const & in,
-				       InsetCommandParams & params)
+void InsetInclude::updateBuffer(ParIterator const & it, UpdateType utype)
 {
-	params.clear();
-	if (in.empty())
+	Buffer const * const childbuffer = getChildBuffer();
+	if (childbuffer) {
+		childbuffer->updateBuffer(Buffer::UpdateChildOnly, utype);
+		return;
+	}
+	if (!isListings(params()))
 		return;
 
-	istringstream data(in);
-	Lexer lex(0,0);
-	lex.setStream(data);
-
-	string name;
-	lex >> name;
-	if (!lex || name != name_)
-		return print_mailer_error("InsetIncludeMailer", in, 1, name_);
-
-	// This is part of the inset proper that is usually swallowed
-	// by Text::readInset
-	string id;
-	lex >> id;
-	if (!lex || id != "Include")
-		return print_mailer_error("InsetIncludeMailer", in, 2, "Include");
-
-	InsetInclude inset(params);
-	inset.read(lex);
-	params = inset.params();
-}
-
+	if (label_)
+		label_->updateBuffer(it, utype);
 
-string const
-InsetIncludeMailer::params2string(InsetCommandParams const & params)
-{
-	InsetInclude inset(params);
-	ostringstream data;
-	data << name_ << ' ';
-	inset.write(data);
-	data << "\\end_inset\n";
-	return data.str();
+	InsetListingsParams const par(to_utf8(params()["lstparams"]));
+	if (par.getParamValue("caption").empty()) {
+		listings_label_ = buffer().B_("Program Listing");
+		return;
+	}
+	Buffer const & master = *buffer().masterBuffer();
+	Counters & counters = master.params().documentClass().counters();
+	docstring const cnt = from_ascii("listing");
+	listings_label_ = master.B_("Program Listing");
+	if (counters.hasCounter(cnt)) {
+		counters.step(cnt, utype);
+		listings_label_ += " " + convert(counters.value(cnt));
+	}
 }