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;
43 void GuiLyXFiles::getFiles(QMap<QString, QString> & in, QString const type)
45 // We look for lyx files in the subdirectory dir of
47 // 2) build_lyxdir (if not empty)
49 // in this order. Files with a given sub-hierarchy will
50 // only be listed once.
51 // We also consider i18n subdirectories and prefer them.
54 QStringList langcodes;
56 // The three locations to look at.
57 string const user = addPath(package().user_support().absFileName(), fromqstr(type));
58 string const build = addPath(package().build_support().absFileName(), fromqstr(type));
59 string const system = addPath(package().system_support().absFileName(), fromqstr(type));
61 // If the LANGUAGE variable is set, use it as a fallback for searching for files.
62 string lang = getGuiMessages().language();
63 string const language = getEnv("LANGUAGE");
64 if (!language.empty())
65 lang += ":" + language;
67 // Get all supported languages (by code) in order to exclude those
69 QAbstractItemModel * language_model = guiApp->languageModel();
70 for (int i = 0; i != language_model->rowCount(); ++i) {
71 QModelIndex index = language_model->index(i, 0);
72 Language const * lang =
73 languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
76 string const code = lang->code();
77 langcodes << toqstr(code);
78 // Also store code without country code
79 string const shortcode = token(code, '_', 0);
80 if (shortcode != code)
81 langcodes << toqstr(shortcode);
84 for (auto const & l : getVectorFromString(lang, ":")) {
86 // First try with the full name
87 // `en' files are not in a subdirectory
91 dirs << toqstr(addPath(user, l));
92 dirs << toqstr(addPath(build, l));
93 dirs << toqstr(addPath(system, l));
95 // Then the name without country code
96 string const shortl = token(l, '_', 0);
98 dirs << toqstr(addPath(user, shortl));
99 dirs << toqstr(addPath(build, shortl));
100 dirs << toqstr(addPath(system, shortl));
104 // Next, search in the base path
109 for (int i = 0; i < dirs.size(); ++i) {
110 QString const dir = dirs.at(i);
111 QDirIterator it(dir, QDir::Files, QDirIterator::Subdirectories);
112 while (it.hasNext()) {
113 QString fn(QFile(it.next()).fileName());
114 if (!fn.endsWith(getSuffix()))
116 QString const relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
117 qstring_to_ucs4(dir)));
119 int s = relpath.indexOf('/', 0);
120 QString cat = qt_("General");
123 cat = relpath.left(s);
124 int sc = relpath.indexOf('/', s + 1);
125 QString const subcat = (sc == -1) ?
126 QString() : relpath.mid(s + 1, sc - s - 1);
127 if (langcodes.contains(cat)
128 && !langcodes.contains(dir.right(dir.lastIndexOf('/'))))
131 if (!subcat.isEmpty())
134 if (!relpaths.contains(relpath)) {
135 relpaths.append(relpath);
143 GuiLyXFiles::GuiLyXFiles(GuiView & lv)
144 : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
149 filter_ = new FancyLineEdit(this);
150 filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
151 filter_->setButtonVisible(FancyLineEdit::Right, true);
152 filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
153 filter_->setAutoHideButton(FancyLineEdit::Right, true);
154 filter_->setPlaceholderText(qt_("All available files"));
155 filter_->setToolTip(qt_("Enter string to filter the list of available files"));
156 #if (QT_VERSION < 0x050000)
157 connect(filter_, SIGNAL(downPressed()),
158 filesLW, SLOT(setFocus()));
160 connect(filter_, &FancyLineEdit::downPressed,
161 filesLW, [=](){ focusAndHighlight(filesLW); });
164 filterBarL->addWidget(filter_, 0);
165 findKeysLA->setBuddy(filter_);
167 connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
168 this, SLOT(slotButtonBox(QAbstractButton *)));
170 connect(filesLW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
171 this, SLOT(changed_adaptor()));
172 connect(filesLW, SIGNAL(itemSelectionChanged()),
173 this, SLOT(changed_adaptor()));
174 connect(filter_, SIGNAL(textEdited(QString)),
175 this, SLOT(filterLabels()));
176 connect(filter_, SIGNAL(rightButtonClicked()),
177 this, SLOT(resetFilter()));
179 bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
180 bc().setOK(buttonBox->button(QDialogButtonBox::Open));
181 bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
183 //filesLW->setViewMode(QListView::ListMode);
184 filesLW->setIconSize(QSize(22, 22));
186 fileTypeCO->addItem(qt_("Templates"), toqstr("templates"));
187 fileTypeCO->addItem(qt_("Examples"), toqstr("examples"));
189 setFocusProxy(filter_);
193 QString const GuiLyXFiles::getSuffix()
195 if (type_ == "bind" || type_ == "ui")
196 return toqstr(".") + type_;
202 void GuiLyXFiles::changed_adaptor()
208 void GuiLyXFiles::on_fileTypeCO_activated(int)
214 void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem *, int)
222 void GuiLyXFiles::on_browsePB_pressed()
224 bool const examples = (type_ == "examples");
225 FileDialog dlg(qt_("Select template file"));
226 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
228 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
230 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
232 FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
233 : toqstr(lyxrc.template_path),
234 QStringList(qt_("LyX Documents (*.lyx)")));
236 if (result.first != FileDialog::Later && !result.second.isEmpty()) {
237 file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
244 void GuiLyXFiles::updateContents()
246 QString type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
247 QMap<QString, QString> files;
248 getFiles(files, type);
251 QFileIconProvider iconprovider;
253 QMap<QString, QString>::const_iterator it = files.constBegin();
255 capfont.setBold(true);
256 while (it != files.constEnd()) {
257 QFileInfo const info = QFileInfo(it.key());
258 QString cat = it.value();
261 if (cat.contains('/')) {
263 cat = catsave.left(catsave.indexOf('/'));
264 subcat = toqstr(translateIfPossible(
265 qstring_to_ucs4(catsave.mid(
266 catsave.indexOf('/') + 1).replace('_', ' '))));
268 cat = toqstr(translateIfPossible(qstring_to_ucs4(cat.replace('_', ' '))));
269 QTreeWidgetItem * catItem = new QTreeWidgetItem();
270 if (!cats.contains(cat)) {
271 catItem->setText(0, cat);
272 catItem->setFont(0, capfont);
273 filesLW->insertTopLevelItem(0, catItem);
274 catItem->setExpanded(true);
277 catItem = filesLW->findItems(cat, Qt::MatchExactly).first();
278 QTreeWidgetItem * item = new QTreeWidgetItem();
279 QString const filename = info.fileName();
280 QString const guiname =
281 toqstr(translateIfPossible(
282 qstring_to_ucs4(filename.left(filename.lastIndexOf(getSuffix())).replace('_', ' '))));
283 item->setIcon(0, iconprovider.icon(info));
284 item->setData(0, Qt::UserRole, info.filePath());
285 item->setData(0, Qt::DisplayRole, guiname);
286 item->setData(0, Qt::ToolTipRole, info.filePath());
287 if (subcat.isEmpty())
288 catItem->addChild(item);
290 QTreeWidgetItem * subcatItem = new QTreeWidgetItem();
291 if (cats.contains(catsave)) {
292 QList<QTreeWidgetItem *> pcats = filesLW->findItems(cat, Qt::MatchExactly);
293 for (int iit = 0; iit < pcats.size(); ++iit) {
294 for (int cit = 0; cit < pcats.at(iit)->childCount(); ++cit) {
295 if (pcats.at(iit)->child(cit)->text(0) == subcat) {
296 subcatItem = pcats.at(iit)->child(cit);
302 subcatItem->setText(0, subcat);
305 subcatItem->addChild(item);
306 catItem->addChild(subcatItem);
310 filesLW->sortItems(0, Qt::AscendingOrder);
316 void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
318 switch (buttonBox->standardButton(button)) {
319 case QDialogButtonBox::Open:
322 case QDialogButtonBox::Cancel:
331 void GuiLyXFiles::filterLabels()
333 Qt::CaseSensitivity cs = csFindCB->isChecked() ?
334 Qt::CaseSensitive : Qt::CaseInsensitive;
335 QTreeWidgetItemIterator it(filesLW);
338 (*it)->childCount() == 0
339 && !(*it)->text(0).contains(filter_->text(), cs)
346 void GuiLyXFiles::resetFilter()
348 filter_->setText(QString());
353 void GuiLyXFiles::applyView()
355 file_ = filesLW->currentItem()->data(0, Qt::UserRole).toString();
359 bool GuiLyXFiles::isValid()
361 return filesLW->currentItem() && filesLW->currentItem()->isSelected();
365 bool GuiLyXFiles::initialiseParams(string const & type)
367 type_ = type.empty() ? toqstr("templates") : toqstr(type);
368 paramsToDialog(type_);
373 void GuiLyXFiles::paramsToDialog(QString const & command)
375 if (!command.isEmpty()) {
376 int i = fileTypeCO->findData(command);
378 fileTypeCO->setCurrentIndex(i);
380 if (command == "examples")
381 setTitle(qt_("Open Example File"));
383 setTitle(qt_("New File From Template"));
386 bc().setValid(isValid());
390 void GuiLyXFiles::dispatchParams()
396 if (type_ == "templates")
398 arg += fromqstr(file_);
399 FuncCode const lfun = getLfun();
401 dispatch(FuncRequest(lfun, arg));
404 FuncCode GuiLyXFiles::getLfun() const
406 if (type_ == toqstr("examples"))
407 return LFUN_FILE_OPEN;
408 return LFUN_BUFFER_NEW_TEMPLATE;
411 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
414 } // namespace frontend
417 #include "moc_GuiLyXFiles.cpp"