2 * \file GuiLyXFiles.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Jürgen Spitzmüller
8 * Full author contact details are available in file CREDITS.
13 #include "GuiLyXFiles.h"
14 #include "GuiApplication.h"
15 #include "qt_helpers.h"
17 #include "FileDialog.h"
19 #include "BufferParams.h"
20 #include "FuncRequest.h"
24 #include "support/environment.h"
25 #include "support/filetools.h"
26 #include "support/gettext.h"
27 #include "support/lstrings.h"
28 #include "support/Messages.h"
29 #include "support/qstring_helpers.h"
30 #include "support/Package.h"
32 #include <QDirIterator>
33 #include <QFileIconProvider>
34 #include <QTreeWidget>
37 using namespace lyx::support;
44 void getFiles(QMap<QString, QString> & in, QString const type)
46 // We look for lyx files in the subdirectory dir of
48 // 2) build_lyxdir (if not empty)
50 // in this order. Files with a given sub-hierarchy will
51 // only be listed once.
52 // We also consider i18n subdirectories and prefer them.
55 QStringList langcodes;
57 // The three locations to look at.
58 string const user = addPath(package().user_support().absFileName(), fromqstr(type));
59 string const build = addPath(package().build_support().absFileName(), fromqstr(type));
60 string const system = addPath(package().system_support().absFileName(), fromqstr(type));
62 // If the LANGUAGE variable is set, use it as a fallback for searching for files.
63 string lang = getGuiMessages().language();
64 string const language = getEnv("LANGUAGE");
65 if (!language.empty())
66 lang += ":" + language;
68 // Get all supported languages (by code) in order to exclude those
70 QAbstractItemModel * language_model = guiApp->languageModel();
71 for (int i = 0; i != language_model->rowCount(); ++i) {
72 QModelIndex index = language_model->index(i, 0);
73 Language const * lang =
74 languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
77 string const code = lang->code();
78 langcodes << toqstr(code);
79 // Also store code without country code
80 string const shortcode = token(code, '_', 0);
81 if (shortcode != code)
82 langcodes << toqstr(shortcode);
85 for (auto const & l : getVectorFromString(lang, ":")) {
87 // First try with the full name
88 // `en' files are not in a subdirectory
92 dirs << toqstr(addPath(user, l));
93 dirs << toqstr(addPath(build, l));
94 dirs << toqstr(addPath(system, l));
96 // Then the name without country code
97 string const shortl = token(l, '_', 0);
99 dirs << toqstr(addPath(user, shortl));
100 dirs << toqstr(addPath(build, shortl));
101 dirs << toqstr(addPath(system, shortl));
105 // Next, search in the base path
110 for (int i = 0; i < dirs.size(); ++i) {
111 QString const dir = dirs.at(i);
112 QDirIterator it(dir, QDir::Files, QDirIterator::Subdirectories);
113 while (it.hasNext()) {
114 QString fn(QFile(it.next()).fileName());
115 if (!fn.endsWith(".lyx"))
117 QString const relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
118 qstring_to_ucs4(dir)));
120 int s = relpath.indexOf('/', 0);
121 QString cat = qt_("General");
124 cat = relpath.left(s);
125 int sc = relpath.indexOf('/', s + 1);
126 QString const subcat = (sc == -1) ?
127 QString() : relpath.mid(s + 1, sc - s - 1);
128 if (langcodes.contains(cat)
129 && !langcodes.contains(dir.right(dir.lastIndexOf('/'))))
132 if (!subcat.isEmpty())
135 if (!relpaths.contains(relpath)) {
136 relpaths.append(relpath);
146 GuiLyXFiles::GuiLyXFiles(GuiView & lv)
147 : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
152 filter_ = new FancyLineEdit(this);
153 filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
154 filter_->setButtonVisible(FancyLineEdit::Right, true);
155 filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
156 filter_->setAutoHideButton(FancyLineEdit::Right, true);
157 filter_->setPlaceholderText(qt_("All available labels"));
158 filter_->setToolTip(qt_("Enter string to filter the list of available labels"));
159 #if (QT_VERSION < 0x050000)
160 connect(filter_, SIGNAL(downPressed()),
161 filesLW, SLOT(setFocus()));
163 connect(filter_, &FancyLineEdit::downPressed,
164 filesLW, [=](){ focusAndHighlight(filesLW); });
167 filterBarL->addWidget(filter_, 0);
168 findKeysLA->setBuddy(filter_);
170 connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
171 this, SLOT(slotButtonBox(QAbstractButton *)));
173 connect(filesLW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
174 this, SLOT(changed_adaptor()));
175 connect(filesLW, SIGNAL(itemSelectionChanged()),
176 this, SLOT(changed_adaptor()));
177 connect(filter_, SIGNAL(textEdited(QString)),
178 this, SLOT(filterLabels()));
179 connect(filter_, SIGNAL(rightButtonClicked()),
180 this, SLOT(resetFilter()));
182 bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
183 bc().setOK(buttonBox->button(QDialogButtonBox::Open));
184 bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
186 //filesLW->setViewMode(QListView::ListMode);
187 filesLW->setIconSize(QSize(22, 22));
189 fileTypeCO->addItem(qt_("Templates"), toqstr("templates"));
190 fileTypeCO->addItem(qt_("Examples"), toqstr("examples"));
192 setFocusProxy(filter_);
196 void GuiLyXFiles::changed_adaptor()
202 void GuiLyXFiles::on_fileTypeCO_activated(int)
208 void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem *, int)
216 void GuiLyXFiles::on_browsePB_pressed()
218 bool const examples = (type_ == "examples");
219 FileDialog dlg(qt_("Select template file"));
220 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
222 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
224 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
226 FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
227 : toqstr(lyxrc.template_path),
228 QStringList(qt_("LyX Documents (*.lyx)")));
230 if (result.first != FileDialog::Later && !result.second.isEmpty()) {
231 file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
238 void GuiLyXFiles::updateContents()
240 QString type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
241 QMap<QString, QString> files;
242 getFiles(files, type);
245 QFileIconProvider iconprovider;
247 QMap<QString, QString>::const_iterator it = files.constBegin();
249 capfont.setBold(true);
250 while (it != files.constEnd()) {
251 QFileInfo const info = QFileInfo(it.key());
252 QString cat = it.value();
255 if (cat.contains('/')) {
257 cat = catsave.left(catsave.indexOf('/'));
258 subcat = toqstr(translateIfPossible(
259 qstring_to_ucs4(catsave.mid(
260 catsave.indexOf('/') + 1).replace('_', ' '))));
262 cat = toqstr(translateIfPossible(qstring_to_ucs4(cat.replace('_', ' '))));
263 QTreeWidgetItem * catItem = new QTreeWidgetItem();
264 if (!cats.contains(cat)) {
265 catItem->setText(0, cat);
266 catItem->setFont(0, capfont);
267 filesLW->insertTopLevelItem(0, catItem);
268 catItem->setExpanded(true);
271 catItem = filesLW->findItems(cat, Qt::MatchExactly).first();
272 QTreeWidgetItem * item = new QTreeWidgetItem();
273 QString const filename = info.fileName();
274 QString const guiname =
275 toqstr(translateIfPossible(
276 qstring_to_ucs4(filename.left(filename.lastIndexOf(".lyx")).replace('_', ' '))));
277 item->setIcon(0, iconprovider.icon(info));
278 item->setData(0, Qt::UserRole, info.filePath());
279 item->setData(0, Qt::DisplayRole, guiname);
280 item->setData(0, Qt::ToolTipRole, info.filePath());
281 if (subcat.isEmpty())
282 catItem->addChild(item);
284 QTreeWidgetItem * subcatItem = new QTreeWidgetItem();
285 if (cats.contains(catsave)) {
286 QList<QTreeWidgetItem *> pcats = filesLW->findItems(cat, Qt::MatchExactly);
287 for (int iit = 0; iit < pcats.size(); ++iit) {
288 for (int cit = 0; cit < pcats.at(iit)->childCount(); ++cit) {
289 if (pcats.at(iit)->child(cit)->text(0) == subcat) {
290 subcatItem = pcats.at(iit)->child(cit);
296 subcatItem->setText(0, subcat);
299 subcatItem->addChild(item);
300 catItem->addChild(subcatItem);
304 filesLW->sortItems(0, Qt::AscendingOrder);
310 void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
312 switch (buttonBox->standardButton(button)) {
313 case QDialogButtonBox::Open:
316 case QDialogButtonBox::Cancel:
325 void GuiLyXFiles::filterLabels()
327 Qt::CaseSensitivity cs = csFindCB->isChecked() ?
328 Qt::CaseSensitive : Qt::CaseInsensitive;
329 QTreeWidgetItemIterator it(filesLW);
332 (*it)->childCount() == 0
333 && !(*it)->text(0).contains(filter_->text(), cs)
340 void GuiLyXFiles::resetFilter()
342 filter_->setText(QString());
347 void GuiLyXFiles::applyView()
349 file_ = filesLW->currentItem()->data(0, Qt::UserRole).toString();
353 bool GuiLyXFiles::isValid()
355 return filesLW->currentItem() && filesLW->currentItem()->isSelected();
359 bool GuiLyXFiles::initialiseParams(string const & type)
361 type_ = type.empty() ? toqstr("templates") : toqstr(type);
362 paramsToDialog(type_);
367 void GuiLyXFiles::paramsToDialog(QString const & command)
369 if (!command.isEmpty()) {
370 int i = fileTypeCO->findData(command);
372 fileTypeCO->setCurrentIndex(i);
374 if (command == "examples")
375 setTitle(qt_("Open Example File"));
377 setTitle(qt_("New File From Template"));
380 bc().setValid(isValid());
384 void GuiLyXFiles::dispatchParams()
390 if (type_ == "templates")
392 arg += fromqstr(file_);
393 FuncCode const lfun = (type_ == toqstr("examples")) ?
394 LFUN_FILE_OPEN : getLfun();
396 dispatch(FuncRequest(lfun, arg));
399 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
402 } // namespace frontend
405 #include "moc_GuiLyXFiles.cpp"