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 void GuiLyXFiles::getFiles(QMap<QString, QString> & in, QString const type)
44 // We look for lyx files in the subdirectory dir of
46 // 2) build_lyxdir (if not empty)
48 // in this order. Files with a given sub-hierarchy will
49 // only be listed once.
50 // We also consider i18n subdirectories and store them separately.
54 // The three locations to look at.
55 string const user = addPath(package().user_support().absFileName(), fromqstr(type));
56 string const build = addPath(package().build_support().absFileName(), fromqstr(type));
57 string const system = addPath(package().system_support().absFileName(), fromqstr(type));
59 available_languages_.insert(toqstr("en"), qt_("English"));
61 // Search in the base path
66 for (int i = 0; i < dirs.size(); ++i) {
67 QString const dir = dirs.at(i);
68 QDirIterator it(dir, QDir::Files, QDirIterator::Subdirectories);
69 while (it.hasNext()) {
70 QString fn(QFile(it.next()).fileName());
71 if (!fn.endsWith(getSuffix()))
73 QString relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
74 qstring_to_ucs4(dir)));
76 int s = relpath.indexOf('/', 0);
77 QString cat = qt_("General");
78 QString localization = "en";
81 cat = relpath.left(s);
82 int sc = relpath.indexOf('/', s + 1);
83 QString const subcat = (sc == -1) ?
84 QString() : relpath.mid(s + 1, sc - s - 1);
85 if (all_languages_.contains(cat)
86 && !all_languages_.contains(dir.right(dir.lastIndexOf('/')))) {
87 QMap<QString, QString>::const_iterator li = all_languages_.find(cat);
88 // Skip i18n dir, but add language to the combo
89 if (!available_languages_.contains(li.key()))
90 available_languages_.insert(li.key(), li.value());
93 if (!subcat.isEmpty())
96 if (!relpaths.contains(relpath)) {
97 relpaths.append(relpath);
98 if (localization == "en")
99 in.insert(relpath, cat);
102 relpath = relpath.mid(relpath.indexOf('/') + 1);
103 QMap<QString, QString> lm;
104 if (localizations_.contains(relpath))
105 lm = localizations_.find(relpath).value();
106 lm.insert(localization, fn);
107 localizations_.insert(relpath, lm);
111 // Find and store GUI language
112 for (auto const & l : guilangs_) {
113 // First try with the full name
114 // `en' files are not in a subdirectory
115 if (available_languages_.contains(toqstr(l))) {
116 guilang_ = toqstr(l);
119 // Then the name without country code
120 string const shortl = token(l, '_', 0);
121 if (available_languages_.contains(toqstr(shortl))) {
122 guilang_ = toqstr(shortl);
126 // pre-fill the language combo (it will be updated once an item
129 languageCO->addItem(qt_("English"), toqstr("en"));
130 QMap<QString, QString>::const_iterator i =available_languages_.constBegin();
131 while (i != available_languages_.constEnd()) {
132 languageCO->addItem(i.value(), i.key());
136 languageLA->setText(qt_("Preferred &Language:"));
140 GuiLyXFiles::GuiLyXFiles(GuiView & lv)
141 : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
145 // Get all supported languages (by code) in order to exclude those
147 QAbstractItemModel * language_model = guiApp->languageModel();
148 language_model->sort(0);
149 for (int i = 0; i != language_model->rowCount(); ++i) {
150 QModelIndex index = language_model->index(i, 0);
151 Language const * lang =
152 languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
155 QString const code = toqstr(lang->code());
156 if (!all_languages_.contains(code))
157 all_languages_.insert(code, qt_(lang->display()));
158 // Also store code without country code
159 QString const shortcode = code.left(code.indexOf('_'));
160 if (shortcode != code && !all_languages_.contains(shortcode))
161 all_languages_.insert(shortcode, qt_(lang->display()));
164 string lang = getGuiMessages().language();
165 string const language = getEnv("LANGUAGE");
166 if (!language.empty())
167 lang += ":" + language;
168 guilangs_ = getVectorFromString(lang, ":");
171 filter_ = new FancyLineEdit(this);
172 filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
173 filter_->setButtonVisible(FancyLineEdit::Right, true);
174 filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
175 filter_->setAutoHideButton(FancyLineEdit::Right, true);
176 filter_->setPlaceholderText(qt_("All available files"));
177 filter_->setToolTip(qt_("Enter string to filter the list of available files"));
178 #if (QT_VERSION < 0x050000)
179 connect(filter_, SIGNAL(downPressed()),
180 filesLW, SLOT(setFocus()));
182 connect(filter_, &FancyLineEdit::downPressed,
183 filesLW, [=](){ focusAndHighlight(filesLW); });
186 filterBarL->addWidget(filter_, 0);
187 findKeysLA->setBuddy(filter_);
189 connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
190 this, SLOT(slotButtonBox(QAbstractButton *)));
192 connect(filesLW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
193 this, SLOT(changed_adaptor()));
194 connect(filesLW, SIGNAL(itemSelectionChanged()),
195 this, SLOT(changed_adaptor()));
196 connect(filter_, SIGNAL(textEdited(QString)),
197 this, SLOT(filterLabels()));
198 connect(filter_, SIGNAL(rightButtonClicked()),
199 this, SLOT(resetFilter()));
201 bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
202 bc().setOK(buttonBox->button(QDialogButtonBox::Open));
203 bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
205 //filesLW->setViewMode(QListView::ListMode);
206 filesLW->setIconSize(QSize(22, 22));
208 setFocusProxy(filter_);
212 QString const GuiLyXFiles::getSuffix()
214 if (type_ == "bind" || type_ == "ui")
215 return toqstr(".") + type_;
221 bool GuiLyXFiles::translateName() const
223 return (type_ == "templates" || type_ == "examples");
227 void GuiLyXFiles::changed_adaptor()
233 void GuiLyXFiles::on_fileTypeCO_activated(int)
239 void GuiLyXFiles::on_languageCO_activated(int i)
241 savelang_ = languageCO->itemData(i).toString();
242 if (!filesLW->currentItem())
245 filesLW->currentItem()->setData(0, Qt::ToolTipRole, getRealPath());
250 void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem * item, int)
252 if (!item->data(0, Qt::UserRole).toString().endsWith(getSuffix()))
253 // not a file (probably a header)
261 void GuiLyXFiles::on_filesLW_itemClicked(QTreeWidgetItem * item, int)
263 QString const data = item->data(0, Qt::UserRole).toString();
264 if (!data.endsWith(getSuffix()))
265 // not a file (probably a header)
269 QMap<QString, QString>::const_iterator i =available_languages_.constBegin();
270 while (i != available_languages_.constEnd()) {
271 if (localizations_.contains(data)
272 && localizations_.find(data).value().contains(i.key()))
273 languageCO->addItem(i.value(), i.key());
276 languageLA->setText(qt_("File &Language:"));
277 languageCO->setToolTip(qt_("All available languages of the selected file are displayed here.\n"
278 "The selected language version will be opened."));
280 QString const realpath = getRealPath();
281 filesLW->currentItem()->setData(0, Qt::ToolTipRole, realpath);
282 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
283 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
284 QIcon file_icon = (realpath.startsWith(toqstr(package().user_support().absFileName()))) ?
285 user_icon : system_icon;
286 item->setIcon(0, file_icon);
290 void GuiLyXFiles::setLanguage()
292 // first try last setting
293 if (!savelang_.isEmpty()) {
294 int index = languageCO->findData(savelang_);
296 languageCO->setCurrentIndex(index);
300 // next, try GUI lang
301 if (!guilang_.isEmpty()) {
302 int index = languageCO->findData(guilang_);
304 languageCO->setCurrentIndex(index);
308 // Finally, fall back to English (which should be always there)
309 int index = languageCO->findData(toqstr("en"));
311 languageCO->setCurrentIndex(index);
316 void GuiLyXFiles::on_browsePB_pressed()
318 bool const examples = (type_ == "examples");
319 FileDialog dlg(qt_("Select template file"));
320 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
322 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
324 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
326 FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
327 : toqstr(lyxrc.template_path),
328 QStringList(qt_("LyX Documents (*.lyx)")));
330 if (result.first != FileDialog::Later && !result.second.isEmpty()) {
331 file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
338 void GuiLyXFiles::updateContents()
340 QString type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
341 QMap<QString, QString> files;
343 getFiles(files, type);
344 languageCO->model()->sort(0);
347 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
348 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
350 QMap<QString, QString>::const_iterator it = files.constBegin();
352 capfont.setBold(true);
353 while (it != files.constEnd()) {
354 QFileInfo const info = QFileInfo(it.key());
355 QString const realpath = getRealPath(it.key());
356 QString cat = it.value();
359 if (cat.contains('/')) {
361 cat = catsave.left(catsave.indexOf('/'));
362 subcat = toqstr(translateIfPossible(
363 qstring_to_ucs4(catsave.mid(
364 catsave.indexOf('/') + 1).replace('_', ' '))));
366 cat = toqstr(translateIfPossible(qstring_to_ucs4(cat.replace('_', ' '))));
367 QTreeWidgetItem * catItem = new QTreeWidgetItem();
368 if (!cats.contains(cat)) {
369 catItem->setText(0, cat);
370 catItem->setFont(0, capfont);
371 filesLW->insertTopLevelItem(0, catItem);
372 catItem->setExpanded(true);
375 catItem = filesLW->findItems(cat, Qt::MatchExactly).first();
376 QTreeWidgetItem * item = new QTreeWidgetItem();
377 QString const filename = info.fileName();
378 QString guiname = filename.left(filename.lastIndexOf(getSuffix())).replace('_', ' ');
380 guiname = toqstr(translateIfPossible(qstring_to_ucs4(guiname)));
381 QIcon file_icon = (realpath.startsWith(toqstr(package().user_support().absFileName()))) ?
382 user_icon : system_icon;
383 item->setIcon(0, file_icon);
384 item->setData(0, Qt::UserRole, it.key());
385 item->setData(0, Qt::DisplayRole, guiname);
386 item->setData(0, Qt::ToolTipRole, realpath);
387 if (subcat.isEmpty())
388 catItem->addChild(item);
390 QTreeWidgetItem * subcatItem = new QTreeWidgetItem();
391 if (cats.contains(catsave)) {
392 QList<QTreeWidgetItem *> pcats = filesLW->findItems(cat, Qt::MatchExactly);
393 for (int iit = 0; iit < pcats.size(); ++iit) {
394 for (int cit = 0; cit < pcats.at(iit)->childCount(); ++cit) {
395 if (pcats.at(iit)->child(cit)->text(0) == subcat) {
396 subcatItem = pcats.at(iit)->child(cit);
402 subcatItem->setText(0, subcat);
403 subcatItem->setIcon(0, file_icon);
406 subcatItem->addChild(item);
407 catItem->addChild(subcatItem);
411 filesLW->sortItems(0, Qt::AscendingOrder);
417 void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
419 switch (buttonBox->standardButton(button)) {
420 case QDialogButtonBox::Open:
423 case QDialogButtonBox::Cancel:
432 void GuiLyXFiles::filterLabels()
434 Qt::CaseSensitivity cs = csFindCB->isChecked() ?
435 Qt::CaseSensitive : Qt::CaseInsensitive;
436 QTreeWidgetItemIterator it(filesLW);
439 (*it)->childCount() == 0
440 && !(*it)->text(0).contains(filter_->text(), cs)
447 void GuiLyXFiles::resetFilter()
449 filter_->setText(QString());
453 QString const GuiLyXFiles::getRealPath(QString relpath)
455 if (relpath.isEmpty())
456 relpath = filesLW->currentItem()->data(0, Qt::UserRole).toString();
457 QString const language = languageCO->itemData(languageCO->currentIndex()).toString();
458 if (localizations_.contains(relpath)) {
459 if (localizations_.find(relpath).value().contains(language))
460 return localizations_.find(relpath).value().find(language).value();
461 else if (localizations_.find(relpath).value().contains(guilang_))
462 return localizations_.find(relpath).value().find(guilang_).value();
463 else if (localizations_.find(relpath).value().contains(toqstr("en")))
464 return localizations_.find(relpath).value().find(toqstr("en")).value();
470 void GuiLyXFiles::applyView()
472 file_ = getRealPath();
476 bool GuiLyXFiles::isValid()
478 return filesLW->currentItem() && filesLW->currentItem()->isSelected();
482 bool GuiLyXFiles::initialiseParams(string const & type)
484 type_ = type.empty() ? toqstr("templates") : toqstr(type);
490 void GuiLyXFiles::paramsToDialog()
493 if (type_ == "examples" || type_ == "templates") {
494 fileTypeCO->addItem(qt_("Templates"), toqstr("templates"));
495 fileTypeCO->addItem(qt_("Examples"), toqstr("examples"));
496 } else if (type_ == "ui")
497 fileTypeCO->addItem(qt_("User Interface Files"), toqstr("ui"));
498 else if (type_ == "bind")
499 fileTypeCO->addItem(qt_("Key Binding Files"), toqstr("bind"));
501 if (!type_.isEmpty()) {
502 int i = fileTypeCO->findData(type_);
504 fileTypeCO->setCurrentIndex(i);
506 if (type_ == "examples")
507 setTitle(qt_("Open Example File"));
508 else if (type_ == "templates")
509 setTitle(qt_("New File From Template"));
511 setTitle(qt_("Open File"));
513 bc().setValid(isValid());
517 void GuiLyXFiles::dispatchParams()
523 if (type_ == "templates")
525 arg += fromqstr(file_);
526 FuncCode const lfun = getLfun();
528 dispatch(FuncRequest(lfun, arg));
532 FuncCode GuiLyXFiles::getLfun() const
534 if (type_ == "examples")
535 return LFUN_FILE_OPEN;
536 else if (type_ == "templates")
537 return LFUN_BUFFER_NEW_TEMPLATE;
538 return LFUN_NOACTION;
541 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
544 } // namespace frontend
547 #include "moc_GuiLyXFiles.cpp"