--- /dev/null
+/**
+ * \file GuiLyXFiles.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Jürgen Spitzmüller
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "GuiLyXFiles.h"
+#include "GuiApplication.h"
+#include "qt_helpers.h"
+
+#include "FileDialog.h"
+#include "Buffer.h"
+#include "BufferParams.h"
+#include "FuncRequest.h"
+#include "Language.h"
+#include "LyXRC.h"
+
+#include "support/environment.h"
+#include "support/filetools.h"
+#include "support/gettext.h"
+#include "support/lstrings.h"
+#include "support/Messages.h"
+#include "support/qstring_helpers.h"
+#include "support/Package.h"
+
+#include <QDirIterator>
+#include <QFileIconProvider>
+#include <QTreeWidget>
+
+using namespace std;
+using namespace lyx::support;
+
+namespace lyx {
+namespace frontend {
+
+namespace {
+
+void getFiles(QMap<QString, QString> & in, QString const type)
+{
+ // We look for lyx files in the subdirectory dir of
+ // 1) user_lyxdir
+ // 2) build_lyxdir (if not empty)
+ // 3) system_lyxdir
+ // in this order. Files with a given sub-hierarchy will
+ // only be listed once.
+ // We also consider i18n subdirectories and prefer them.
+ QStringList dirs;
+ QStringList relpaths;
+ QStringList langcodes;
+
+ // The three locations to look at.
+ string const user = addPath(package().user_support().absFileName(), fromqstr(type));
+ string const build = addPath(package().build_support().absFileName(), fromqstr(type));
+ string const system = addPath(package().system_support().absFileName(), fromqstr(type));
+
+ // If the LANGUAGE variable is set, use it as a fallback for searching for files.
+ string lang = getGuiMessages().language();
+ string const language = getEnv("LANGUAGE");
+ if (!language.empty())
+ lang += ":" + language;
+
+ // Get all supported languages (by code) in order to exclude those
+ // dirs later.
+ QAbstractItemModel * language_model = guiApp->languageModel();
+ for (int i = 0; i != language_model->rowCount(); ++i) {
+ QModelIndex index = language_model->index(i, 0);
+ Language const * lang =
+ languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
+ if (!lang)
+ continue;
+ string const code = lang->code();
+ langcodes << toqstr(code);
+ // Also store code without country code
+ string const shortcode = token(code, '_', 0);
+ if (shortcode != code)
+ langcodes << toqstr(shortcode);
+ }
+
+ for (auto const & l : getVectorFromString(lang, ":")) {
+ FileName tmp;
+ // First try with the full name
+ // `en' files are not in a subdirectory
+ if (l == "en")
+ break;
+ else {
+ dirs << toqstr(addPath(user, l));
+ dirs << toqstr(addPath(build, l));
+ dirs << toqstr(addPath(system, l));
+ }
+ // Then the name without country code
+ string const shortl = token(l, '_', 0);
+ if (shortl != l) {
+ dirs << toqstr(addPath(user, shortl));
+ dirs << toqstr(addPath(build, shortl));
+ dirs << toqstr(addPath(system, shortl));
+ }
+ }
+
+ // Next, search in the base path
+ dirs << toqstr(user)
+ << toqstr(build)
+ << toqstr(system);
+
+ for (int i = 0; i < dirs.size(); ++i) {
+ QString const dir = dirs.at(i);
+ QDirIterator it(dir, QDir::Files, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ QString fn(QFile(it.next()).fileName());
+ if (!fn.endsWith(".lyx"))
+ continue;
+ QString const relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
+ qstring_to_ucs4(dir)));
+ // <cat>/
+ int s = relpath.indexOf('/', 0);
+ QString cat = qt_("General");
+ if (s != -1) {
+ // <cat>/<subcat>/
+ cat = relpath.left(s);
+ int sc = relpath.indexOf('/', s + 1);
+ QString const subcat = (sc == -1) ?
+ QString() : relpath.mid(s + 1, sc - s - 1);
+ if (langcodes.contains(cat)
+ && !langcodes.contains(dir.right(dir.lastIndexOf('/'))))
+ // Skip i18n dir
+ continue;
+ if (!subcat.isEmpty())
+ cat += '/' + subcat;
+ }
+ if (!relpaths.contains(relpath)) {
+ relpaths.append(relpath);
+ in.insert(fn, cat);
+ }
+ }
+ }
+}
+
+}// namespace anon
+
+
+GuiLyXFiles::GuiLyXFiles(GuiView & lv)
+ : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
+{
+ setupUi(this);
+
+ // The filter bar
+ filter_ = new FancyLineEdit(this);
+ filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
+ filter_->setButtonVisible(FancyLineEdit::Right, true);
+ filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
+ filter_->setAutoHideButton(FancyLineEdit::Right, true);
+ filter_->setPlaceholderText(qt_("All available labels"));
+ filter_->setToolTip(qt_("Enter string to filter the list of available labels"));
+#if (QT_VERSION < 0x050000)
+ connect(filter_, SIGNAL(downPressed()),
+ filesLW, SLOT(setFocus()));
+#else
+ connect(filter_, &FancyLineEdit::downPressed,
+ filesLW, [=](){ focusAndHighlight(filesLW); });
+#endif
+
+ filterBarL->addWidget(filter_, 0);
+ findKeysLA->setBuddy(filter_);
+
+ connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
+ this, SLOT(slotButtonBox(QAbstractButton *)));
+
+ connect(filesLW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
+ this, SLOT(changed_adaptor()));
+ connect(filesLW, SIGNAL(itemSelectionChanged()),
+ this, SLOT(changed_adaptor()));
+ connect(filter_, SIGNAL(textEdited(QString)),
+ this, SLOT(filterLabels()));
+ connect(filter_, SIGNAL(rightButtonClicked()),
+ this, SLOT(resetFilter()));
+
+ bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
+ bc().setOK(buttonBox->button(QDialogButtonBox::Open));
+ bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
+
+ //filesLW->setViewMode(QListView::ListMode);
+ filesLW->setIconSize(QSize(22, 22));
+
+ fileTypeCO->addItem(qt_("Templates"), toqstr("templates"));
+ fileTypeCO->addItem(qt_("Examples"), toqstr("examples"));
+
+ setFocusProxy(filter_);
+}
+
+
+void GuiLyXFiles::changed_adaptor()
+{
+ changed();
+}
+
+
+void GuiLyXFiles::on_fileTypeCO_activated(int)
+{
+ updateContents();
+}
+
+
+void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem *, int)
+{
+ applyView();
+ dispatchParams();
+ close();
+}
+
+
+void GuiLyXFiles::on_browsePB_pressed()
+{
+ bool const examples = (type_ == "examples");
+ FileDialog dlg(qt_("Select template file"));
+ dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
+ if (examples)
+ dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
+ else
+ dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
+
+ FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
+ : toqstr(lyxrc.template_path),
+ QStringList(qt_("LyX Documents (*.lyx)")));
+
+ if (result.first != FileDialog::Later && !result.second.isEmpty()) {
+ file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
+ dispatchParams();
+ close();
+ }
+}
+
+
+void GuiLyXFiles::updateContents()
+{
+ QString type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
+ QMap<QString, QString> files;
+ getFiles(files, type);
+
+ filesLW->clear();
+ QFileIconProvider iconprovider;
+ QStringList cats;
+ QMap<QString, QString>::const_iterator it = files.constBegin();
+ QFont capfont;
+ capfont.setBold(true);
+ while (it != files.constEnd()) {
+ QFileInfo const info = QFileInfo(it.key());
+ QString cat = it.value();
+ QString subcat;
+ QString catsave;
+ if (cat.contains('/')) {
+ catsave = cat;
+ cat = catsave.left(catsave.indexOf('/'));
+ subcat = toqstr(translateIfPossible(
+ qstring_to_ucs4(catsave.mid(
+ catsave.indexOf('/') + 1).replace('_', ' '))));
+ }
+ cat = toqstr(translateIfPossible(qstring_to_ucs4(cat.replace('_', ' '))));
+ QTreeWidgetItem * catItem = new QTreeWidgetItem();
+ if (!cats.contains(cat)) {
+ catItem->setText(0, cat);
+ catItem->setFont(0, capfont);
+ filesLW->insertTopLevelItem(0, catItem);
+ catItem->setExpanded(true);
+ cats << cat;
+ } else
+ catItem = filesLW->findItems(cat, Qt::MatchExactly).first();
+ QTreeWidgetItem * item = new QTreeWidgetItem();
+ QString const filename = info.fileName();
+ QString const guiname =
+ toqstr(translateIfPossible(
+ qstring_to_ucs4(filename.left(filename.lastIndexOf(".lyx")).replace('_', ' '))));
+ item->setIcon(0, iconprovider.icon(info));
+ item->setData(0, Qt::UserRole, info.filePath());
+ item->setData(0, Qt::DisplayRole, guiname);
+ item->setData(0, Qt::ToolTipRole, info.filePath());
+ if (subcat.isEmpty())
+ catItem->addChild(item);
+ else {
+ QTreeWidgetItem * subcatItem = new QTreeWidgetItem();
+ if (cats.contains(catsave)) {
+ QList<QTreeWidgetItem *> pcats = filesLW->findItems(cat, Qt::MatchExactly);
+ for (int iit = 0; iit < pcats.size(); ++iit) {
+ for (int cit = 0; cit < pcats.at(iit)->childCount(); ++cit) {
+ if (pcats.at(iit)->child(cit)->text(0) == subcat) {
+ subcatItem = pcats.at(iit)->child(cit);
+ break;
+ }
+ }
+ }
+ } else {
+ subcatItem->setText(0, subcat);
+ cats << catsave;
+ }
+ subcatItem->addChild(item);
+ catItem->addChild(subcatItem);
+ }
+ ++it;
+ }
+ filesLW->sortItems(0, Qt::AscendingOrder);
+ // redo filter
+ filterLabels();
+}
+
+
+void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
+{
+ switch (buttonBox->standardButton(button)) {
+ case QDialogButtonBox::Open:
+ slotOK();
+ break;
+ case QDialogButtonBox::Cancel:
+ slotClose();
+ break;
+ default:
+ break;
+ }
+}
+
+
+void GuiLyXFiles::filterLabels()
+{
+ Qt::CaseSensitivity cs = csFindCB->isChecked() ?
+ Qt::CaseSensitive : Qt::CaseInsensitive;
+ QTreeWidgetItemIterator it(filesLW);
+ while (*it) {
+ (*it)->setHidden(
+ (*it)->childCount() == 0
+ && !(*it)->text(0).contains(filter_->text(), cs)
+ );
+ ++it;
+ }
+}
+
+
+void GuiLyXFiles::resetFilter()
+{
+ filter_->setText(QString());
+ filterLabels();
+}
+
+
+void GuiLyXFiles::applyView()
+{
+ file_ = filesLW->currentItem()->data(0, Qt::UserRole).toString();
+}
+
+
+bool GuiLyXFiles::isValid()
+{
+ return filesLW->currentItem() && filesLW->currentItem()->isSelected();
+}
+
+
+bool GuiLyXFiles::initialiseParams(string const & type)
+{
+ type_ = type.empty() ? toqstr("templates") : toqstr(type);
+ paramsToDialog(type_);
+ return true;
+}
+
+
+void GuiLyXFiles::paramsToDialog(QString const & command)
+{
+ if (!command.isEmpty()) {
+ int i = fileTypeCO->findData(command);
+ if (i != -1)
+ fileTypeCO->setCurrentIndex(i);
+ }
+ if (command == "examples")
+ setTitle(qt_("Open Example File"));
+ else {
+ setTitle(qt_("New File From Template"));
+ }
+
+ bc().setValid(isValid());
+}
+
+
+void GuiLyXFiles::dispatchParams()
+{
+ if (file_.isEmpty())
+ return;
+
+ string arg;
+ if (type_ == "templates")
+ arg = "newfile ";
+ arg += fromqstr(file_);
+ FuncCode const lfun = (type_ == toqstr("examples")) ?
+ LFUN_FILE_OPEN : getLfun();
+
+ dispatch(FuncRequest(lfun, arg));
+}
+
+Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
+
+
+} // namespace frontend
+} // namespace lyx
+
+#include "moc_GuiLyXFiles.cpp"
if (!doc_buffer)
enable = name == "aboutlyx"
|| name == "file" //FIXME: should be removed.
+ || name == "lyxfiles"
|| name == "prefs"
|| name == "texinfo"
|| name == "progress"
}
-void GuiView::newDocument(string const & filename, bool from_template)
+void GuiView::newDocument(string const & filename, string templatefile,
+ bool from_template)
{
FileName initpath(lyxrc.document_path);
if (documentBufferView()) {
initpath = trypath;
}
- string templatefile;
if (from_template) {
- templatefile = selectTemplateFile().absFileName();
+ if (templatefile.empty())
+ templatefile = selectTemplateFile().absFileName();
if (templatefile.empty())
return;
}
"citation", "compare", "comparehistory", "document", "errorlist", "ert",
"external", "file", "findreplace", "findreplaceadv", "float", "graphics",
"href", "include", "index", "index_print", "info", "listings", "label", "line",
-"log", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
+"log", "lyxfiles", "mathdelimiter", "mathmatrix", "mathspace", "nomenclature",
"nomencl_print", "note", "paragraph", "phantom", "prefs", "ref",
"sendto", "space", "spellchecker", "symbols", "tabular", "tabularcreate",
"thesaurus", "texinfo", "toc", "view-source", "vspace", "wrap", "progress"};
Dialog * createGuiIndex(GuiView & lv);
Dialog * createGuiListings(GuiView & lv);
Dialog * createGuiLog(GuiView & lv);
+Dialog * createGuiLyXFiles(GuiView & lv);
Dialog * createGuiMathMatrix(GuiView & lv);
Dialog * createGuiNote(GuiView & lv);
Dialog * createGuiParagraph(GuiView & lv);
return createGuiListings(*this);
if (name == "log")
return createGuiLog(*this);
+ if (name == "lyxfiles")
+ return createGuiLyXFiles(*this);
if (name == "mathdelimiter")
return createGuiDelimiter(*this);
if (name == "mathmatrix")
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LyXFilesUi</class>
+ <widget class="QDialog" name="LyXFilesUi">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>381</width>
+ <height>474</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string/>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="filesTypeLA">
+ <property name="text">
+ <string>&Type:</string>
+ </property>
+ <property name="buddy">
+ <cstring>fileTypeCO</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2">
+ <widget class="QComboBox" name="fileTypeCO">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="findKeysLA">
+ <property name="text">
+ <string>&Filter:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="filterBarL"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="csFindCB">
+ <property name="toolTip">
+ <string>Filter case-sensitively</string>
+ </property>
+ <property name="text">
+ <string>Case Sensiti&ve</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QTreeWidget" name="filesLW">
+ <property name="rootIsDecorated">
+ <bool>true</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>true</bool>
+ </property>
+ <attribute name="headerVisible">
+ <bool>false</bool>
+ </attribute>
+ <column>
+ <property name="text">
+ <string notr="true">1</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="browsePB">
+ <property name="text">
+ <string>&Browse...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+ <layout class="QHBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Open</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <includes>
+ <include location="local">qt_i18n.h</include>
+ </includes>
+ <resources/>
+ <connections/>
+</ui>