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 <QTreeWidget>
36 using namespace lyx::support;
42 QMap<QString, QString> GuiLyXFiles::getFiles()
44 QMap<QString, QString> result;
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 store them separately.
55 // The three locations to look at.
56 string const user = addPath(package().user_support().absFileName(), fromqstr(type_));
57 string const build = addPath(package().build_support().absFileName(), fromqstr(type_));
58 string const system = addPath(package().system_support().absFileName(), fromqstr(type_));
60 available_languages_.insert(toqstr("en"), qt_("English"));
62 QString const type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
64 // Search in the base paths
65 if (type == "all" || type == "user")
67 if (type == "all" || type == "system")
71 for (int i = 0; i < dirs.size(); ++i) {
72 QString const dir = dirs.at(i);
73 QDirIterator it(dir, QDir::Files, QDirIterator::Subdirectories);
74 while (it.hasNext()) {
75 QString fn(QFile(it.next()).fileName());
76 if (!fn.endsWith(getSuffix()))
78 QString relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
79 qstring_to_ucs4(dir)));
81 int s = relpath.indexOf('/', 0);
82 QString cat = qt_("General");
83 QString localization = "en";
86 cat = relpath.left(s);
87 if (all_languages_.contains(cat)
88 && !all_languages_.contains(dir.right(dir.lastIndexOf('/')))) {
89 QMap<QString, QString>::const_iterator li = all_languages_.find(cat);
90 // Skip i18n dir, but add language to the combo
91 if (!available_languages_.contains(li.key()))
92 available_languages_.insert(li.key(), li.value());
94 int sc = relpath.indexOf('/', s + 1);
95 cat = (sc == -1) ? QString() : relpath.mid(s + 1, sc - s - 1);
99 int sc = relpath.indexOf('/', s + 1);
100 QString const subcat = (sc == -1) ?
101 QString() : relpath.mid(s + 1, sc - s - 1);
102 if (!subcat.isEmpty())
106 if (!relpaths.contains(relpath)) {
107 relpaths.append(relpath);
108 if (localization != "en")
110 relpath = relpath.mid(relpath.indexOf('/') + 1);
111 result.insert(relpath, cat);
113 QMap<QString, QString> lm;
114 if (localizations_.contains(relpath))
115 lm = localizations_.find(relpath).value();
116 lm.insert(localization, fn);
117 localizations_.insert(relpath, lm);
121 // Find and store GUI language
122 for (auto const & l : guilangs_) {
123 // First try with the full name
124 // `en' files are not in a subdirectory
125 if (available_languages_.contains(toqstr(l))) {
126 guilang_ = toqstr(l);
129 // Then the name without country code
130 string const shortl = token(l, '_', 0);
131 if (available_languages_.contains(toqstr(shortl))) {
132 guilang_ = toqstr(shortl);
136 // pre-fill the language combo (it will be updated once an item
139 languageCO->addItem(qt_("English"), toqstr("en"));
140 QMap<QString, QString>::const_iterator i =available_languages_.constBegin();
141 while (i != available_languages_.constEnd()) {
142 languageCO->addItem(i.value(), i.key());
146 languageLA->setText(qt_("Preferred &Language:"));
151 GuiLyXFiles::GuiLyXFiles(GuiView & lv)
152 : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
156 // Get all supported languages (by code) in order to exclude those
158 QAbstractItemModel * language_model = guiApp->languageModel();
159 language_model->sort(0);
160 for (int i = 0; i != language_model->rowCount(); ++i) {
161 QModelIndex index = language_model->index(i, 0);
162 Language const * lang =
163 languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
166 QString const code = toqstr(lang->code());
167 if (!all_languages_.contains(code))
168 all_languages_.insert(code, qt_(lang->display()));
169 // Also store code without country code
170 QString const shortcode = code.left(code.indexOf('_'));
171 if (shortcode != code && !all_languages_.contains(shortcode))
172 all_languages_.insert(shortcode, qt_(lang->display()));
175 string lang = getGuiMessages().language();
176 string const language = getEnv("LANGUAGE");
177 if (!language.empty())
178 lang += ":" + language;
179 guilangs_ = getVectorFromString(lang, ":");
182 filter_ = new FancyLineEdit(this);
183 filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
184 filter_->setButtonVisible(FancyLineEdit::Right, true);
185 filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
186 filter_->setAutoHideButton(FancyLineEdit::Right, true);
187 filter_->setPlaceholderText(qt_("All available files"));
188 filter_->setToolTip(qt_("Enter string to filter the list of available files"));
189 #if (QT_VERSION < 0x050000)
190 connect(filter_, SIGNAL(downPressed()),
191 filesLW, SLOT(setFocus()));
193 connect(filter_, &FancyLineEdit::downPressed,
194 filesLW, [=](){ focusAndHighlight(filesLW); });
197 filterBarL->addWidget(filter_, 0);
198 findKeysLA->setBuddy(filter_);
200 connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
201 this, SLOT(slotButtonBox(QAbstractButton *)));
203 connect(filesLW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
204 this, SLOT(changed_adaptor()));
205 connect(filesLW, SIGNAL(itemSelectionChanged()),
206 this, SLOT(changed_adaptor()));
207 connect(filter_, SIGNAL(textEdited(QString)),
208 this, SLOT(filterLabels()));
209 connect(filter_, SIGNAL(rightButtonClicked()),
210 this, SLOT(resetFilter()));
212 bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
213 bc().setOK(buttonBox->button(QDialogButtonBox::Open));
214 bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
216 //filesLW->setViewMode(QListView::ListMode);
217 filesLW->setIconSize(QSize(22, 22));
219 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
220 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
221 fileTypeCO->addItem(qt_("User and System Files"), toqstr("all"));
222 fileTypeCO->addItem(user_icon, qt_("User Files Only"), toqstr("user"));
223 fileTypeCO->addItem(system_icon, qt_("System Files Only"), toqstr("system"));
225 setFocusProxy(filter_);
229 QString const GuiLyXFiles::getSuffix()
231 if (type_ == "bind" || type_ == "ui")
232 return toqstr(".") + type_;
238 bool GuiLyXFiles::translateName() const
240 return (type_ == "templates" || type_ == "examples");
244 void GuiLyXFiles::changed_adaptor()
250 void GuiLyXFiles::on_fileTypeCO_activated(int)
256 void GuiLyXFiles::on_languageCO_activated(int i)
258 savelang_ = languageCO->itemData(i).toString();
259 if (!filesLW->currentItem())
262 filesLW->currentItem()->setData(0, Qt::ToolTipRole, getRealPath());
267 void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem * item, int)
269 if (!item->data(0, Qt::UserRole).toString().endsWith(getSuffix()))
270 // not a file (probably a header)
278 void GuiLyXFiles::on_filesLW_itemClicked(QTreeWidgetItem * item, int)
280 QString const data = item->data(0, Qt::UserRole).toString();
281 if (!data.endsWith(getSuffix()))
282 // not a file (probably a header)
286 QMap<QString, QString>::const_iterator i =available_languages_.constBegin();
287 while (i != available_languages_.constEnd()) {
288 if (localizations_.contains(data)
289 && localizations_.find(data).value().contains(i.key()))
290 languageCO->addItem(i.value(), i.key());
293 languageLA->setText(qt_("File &Language:"));
294 languageCO->setToolTip(qt_("All available languages of the selected file are displayed here.\n"
295 "The selected language version will be opened."));
297 QString const realpath = getRealPath();
298 filesLW->currentItem()->setData(0, Qt::ToolTipRole, realpath);
299 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
300 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
301 QIcon file_icon = (realpath.startsWith(toqstr(package().user_support().absFileName()))) ?
302 user_icon : system_icon;
303 item->setIcon(0, file_icon);
307 void GuiLyXFiles::setLanguage()
309 // first try last setting
310 if (!savelang_.isEmpty()) {
311 int index = languageCO->findData(savelang_);
313 languageCO->setCurrentIndex(index);
317 // next, try GUI lang
318 if (!guilang_.isEmpty()) {
319 int index = languageCO->findData(guilang_);
321 languageCO->setCurrentIndex(index);
325 // Finally, fall back to English (which should be always there)
326 int index = languageCO->findData(toqstr("en"));
328 languageCO->setCurrentIndex(index);
333 void GuiLyXFiles::on_browsePB_pressed()
335 bool const examples = (type_ == "examples");
336 FileDialog dlg(qt_("Select template file"));
337 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
339 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
341 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
343 FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
344 : toqstr(lyxrc.template_path),
345 QStringList(qt_("LyX Documents (*.lyx)")));
347 if (result.first != FileDialog::Later && !result.second.isEmpty()) {
348 file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
355 void GuiLyXFiles::updateContents()
358 QMap<QString, QString> files = getFiles();
359 languageCO->model()->sort(0);
362 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
363 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
365 QMap<QString, QString>::const_iterator it = files.constBegin();
367 capfont.setBold(true);
368 while (it != files.constEnd()) {
369 QFileInfo const info = QFileInfo(it.key());
370 QString const realpath = getRealPath(it.key());
371 QString cat = it.value();
374 if (cat.contains('/')) {
376 cat = catsave.left(catsave.indexOf('/'));
377 subcat = toqstr(translateIfPossible(
378 qstring_to_ucs4(catsave.mid(
379 catsave.indexOf('/') + 1).replace('_', ' '))));
381 cat = toqstr(translateIfPossible(qstring_to_ucs4(cat.replace('_', ' '))));
382 QTreeWidgetItem * catItem = new QTreeWidgetItem();
383 if (!cats.contains(cat)) {
384 catItem->setText(0, cat);
385 catItem->setFont(0, capfont);
386 filesLW->insertTopLevelItem(0, catItem);
387 catItem->setExpanded(true);
390 catItem = filesLW->findItems(cat, Qt::MatchExactly).first();
391 QTreeWidgetItem * item = new QTreeWidgetItem();
392 QString const filename = info.fileName();
393 QString guiname = filename.left(filename.lastIndexOf(getSuffix())).replace('_', ' ');
395 guiname = toqstr(translateIfPossible(qstring_to_ucs4(guiname)));
396 QIcon file_icon = (realpath.startsWith(toqstr(package().user_support().absFileName()))) ?
397 user_icon : system_icon;
398 item->setIcon(0, file_icon);
399 item->setData(0, Qt::UserRole, it.key());
400 item->setData(0, Qt::DisplayRole, guiname);
401 item->setData(0, Qt::ToolTipRole, realpath);
402 if (subcat.isEmpty())
403 catItem->addChild(item);
405 QTreeWidgetItem * subcatItem = new QTreeWidgetItem();
406 if (cats.contains(catsave)) {
407 QList<QTreeWidgetItem *> pcats = filesLW->findItems(cat, Qt::MatchExactly);
408 for (int iit = 0; iit < pcats.size(); ++iit) {
409 for (int cit = 0; cit < pcats.at(iit)->childCount(); ++cit) {
410 if (pcats.at(iit)->child(cit)->text(0) == subcat) {
411 subcatItem = pcats.at(iit)->child(cit);
417 subcatItem->setText(0, subcat);
418 subcatItem->setIcon(0, file_icon);
421 subcatItem->addChild(item);
422 catItem->addChild(subcatItem);
426 filesLW->sortItems(0, Qt::AscendingOrder);
432 void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
434 switch (buttonBox->standardButton(button)) {
435 case QDialogButtonBox::Open:
438 case QDialogButtonBox::Cancel:
447 void GuiLyXFiles::filterLabels()
449 Qt::CaseSensitivity cs = csFindCB->isChecked() ?
450 Qt::CaseSensitive : Qt::CaseInsensitive;
451 QTreeWidgetItemIterator it(filesLW);
454 (*it)->childCount() == 0
455 && !(*it)->text(0).contains(filter_->text(), cs)
462 void GuiLyXFiles::resetFilter()
464 filter_->setText(QString());
468 QString const GuiLyXFiles::getRealPath(QString relpath)
470 if (relpath.isEmpty())
471 relpath = filesLW->currentItem()->data(0, Qt::UserRole).toString();
472 QString const language = languageCO->itemData(languageCO->currentIndex()).toString();
473 if (localizations_.contains(relpath)) {
474 if (localizations_.find(relpath).value().contains(language))
475 return localizations_.find(relpath).value().find(language).value();
476 else if (localizations_.find(relpath).value().contains(guilang_))
477 return localizations_.find(relpath).value().find(guilang_).value();
478 else if (localizations_.find(relpath).value().contains(toqstr("en")))
479 return localizations_.find(relpath).value().find(toqstr("en")).value();
485 void GuiLyXFiles::applyView()
487 file_ = getRealPath();
491 bool GuiLyXFiles::isValid()
493 return filesLW->currentItem() && filesLW->currentItem()->isSelected();
497 bool GuiLyXFiles::initialiseParams(string const & type)
499 type_ = type.empty() ? toqstr("templates") : toqstr(type);
505 void GuiLyXFiles::paramsToDialog()
507 if (type_ == "examples")
508 setTitle(qt_("Open Example File"));
509 else if (type_ == "templates")
510 setTitle(qt_("New File From Template"));
512 setTitle(qt_("Open File"));
514 bc().setValid(isValid());
518 void GuiLyXFiles::dispatchParams()
524 if (type_ == "templates")
526 arg += fromqstr(file_);
527 FuncCode const lfun = getLfun();
529 dispatch(FuncRequest(lfun, arg));
533 FuncCode GuiLyXFiles::getLfun() const
535 if (type_ == "examples")
536 return LFUN_FILE_OPEN;
537 else if (type_ == "templates")
538 return LFUN_BUFFER_NEW_TEMPLATE;
539 return LFUN_NOACTION;
542 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
545 } // namespace frontend
548 #include "moc_GuiLyXFiles.cpp"