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());
139 GuiLyXFiles::GuiLyXFiles(GuiView & lv)
140 : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
144 // Get all supported languages (by code) in order to exclude those
146 QAbstractItemModel * language_model = guiApp->languageModel();
147 language_model->sort(0);
148 for (int i = 0; i != language_model->rowCount(); ++i) {
149 QModelIndex index = language_model->index(i, 0);
150 Language const * lang =
151 languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
154 QString const code = toqstr(lang->code());
155 if (!all_languages_.contains(code))
156 all_languages_.insert(code, qt_(lang->display()));
157 // Also store code without country code
158 QString const shortcode = code.left(code.indexOf('_'));
159 if (shortcode != code && !all_languages_.contains(shortcode))
160 all_languages_.insert(shortcode, qt_(lang->display()));
163 string lang = getGuiMessages().language();
164 string const language = getEnv("LANGUAGE");
165 if (!language.empty())
166 lang += ":" + language;
167 guilangs_ = getVectorFromString(lang, ":");
170 filter_ = new FancyLineEdit(this);
171 filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
172 filter_->setButtonVisible(FancyLineEdit::Right, true);
173 filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
174 filter_->setAutoHideButton(FancyLineEdit::Right, true);
175 filter_->setPlaceholderText(qt_("All available files"));
176 filter_->setToolTip(qt_("Enter string to filter the list of available files"));
177 #if (QT_VERSION < 0x050000)
178 connect(filter_, SIGNAL(downPressed()),
179 filesLW, SLOT(setFocus()));
181 connect(filter_, &FancyLineEdit::downPressed,
182 filesLW, [=](){ focusAndHighlight(filesLW); });
185 filterBarL->addWidget(filter_, 0);
186 findKeysLA->setBuddy(filter_);
188 connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
189 this, SLOT(slotButtonBox(QAbstractButton *)));
191 connect(filesLW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
192 this, SLOT(changed_adaptor()));
193 connect(filesLW, SIGNAL(itemSelectionChanged()),
194 this, SLOT(changed_adaptor()));
195 connect(filter_, SIGNAL(textEdited(QString)),
196 this, SLOT(filterLabels()));
197 connect(filter_, SIGNAL(rightButtonClicked()),
198 this, SLOT(resetFilter()));
200 bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
201 bc().setOK(buttonBox->button(QDialogButtonBox::Open));
202 bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
204 //filesLW->setViewMode(QListView::ListMode);
205 filesLW->setIconSize(QSize(22, 22));
207 setFocusProxy(filter_);
211 QString const GuiLyXFiles::getSuffix()
213 if (type_ == "bind" || type_ == "ui")
214 return toqstr(".") + type_;
220 bool GuiLyXFiles::translateName() const
222 return (type_ == "templates" || type_ == "examples");
226 void GuiLyXFiles::changed_adaptor()
232 void GuiLyXFiles::on_fileTypeCO_activated(int)
238 void GuiLyXFiles::on_languageCO_activated(int i)
240 savelang_ = languageCO->itemData(i).toString();
241 filesLW->currentItem()->setData(0, Qt::ToolTipRole, getRealPath());
246 void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem * item, int)
248 if (!item->data(0, Qt::UserRole).toString().endsWith(getSuffix()))
249 // not a file (probably a header)
257 void GuiLyXFiles::on_filesLW_itemClicked(QTreeWidgetItem * item, int)
259 QString const data = item->data(0, Qt::UserRole).toString();
260 if (!data.endsWith(getSuffix()))
261 // not a file (probably a header)
265 QMap<QString, QString>::const_iterator i =available_languages_.constBegin();
266 while (i != available_languages_.constEnd()) {
267 if (localizations_.contains(data)
268 && localizations_.find(data).value().contains(i.key()))
269 languageCO->addItem(i.value(), i.key());
272 languageCO->setToolTip(qt_("All available languages of the selected file are displayed here.\n"
273 "The selected language version will be opened."));
275 filesLW->currentItem()->setData(0, Qt::ToolTipRole, getRealPath());
279 void GuiLyXFiles::setLanguage()
281 // first try last setting
282 if (!savelang_.isEmpty()) {
283 int index = languageCO->findData(savelang_);
285 languageCO->setCurrentIndex(index);
289 // next, try GUI lang
290 if (!guilang_.isEmpty()) {
291 int index = languageCO->findData(guilang_);
293 languageCO->setCurrentIndex(index);
297 // Finally, fall back to English (which should be always there)
298 int index = languageCO->findData(toqstr("en"));
300 languageCO->setCurrentIndex(index);
305 void GuiLyXFiles::on_browsePB_pressed()
307 bool const examples = (type_ == "examples");
308 FileDialog dlg(qt_("Select template file"));
309 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
311 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
313 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
315 FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
316 : toqstr(lyxrc.template_path),
317 QStringList(qt_("LyX Documents (*.lyx)")));
319 if (result.first != FileDialog::Later && !result.second.isEmpty()) {
320 file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
327 void GuiLyXFiles::updateContents()
329 QString type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
330 QMap<QString, QString> files;
332 getFiles(files, type);
333 languageCO->model()->sort(0);
336 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
337 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
339 QMap<QString, QString>::const_iterator it = files.constBegin();
341 capfont.setBold(true);
342 while (it != files.constEnd()) {
343 QFileInfo const info = QFileInfo(it.key());
344 QString cat = it.value();
347 if (cat.contains('/')) {
349 cat = catsave.left(catsave.indexOf('/'));
350 subcat = toqstr(translateIfPossible(
351 qstring_to_ucs4(catsave.mid(
352 catsave.indexOf('/') + 1).replace('_', ' '))));
354 cat = toqstr(translateIfPossible(qstring_to_ucs4(cat.replace('_', ' '))));
355 QTreeWidgetItem * catItem = new QTreeWidgetItem();
356 if (!cats.contains(cat)) {
357 catItem->setText(0, cat);
358 catItem->setFont(0, capfont);
359 filesLW->insertTopLevelItem(0, catItem);
360 catItem->setExpanded(true);
363 catItem = filesLW->findItems(cat, Qt::MatchExactly).first();
364 QTreeWidgetItem * item = new QTreeWidgetItem();
365 QString const filename = info.fileName();
366 QString guiname = filename.left(filename.lastIndexOf(getSuffix())).replace('_', ' ');
368 guiname = toqstr(translateIfPossible(qstring_to_ucs4(guiname)));
369 QIcon file_icon = (info.filePath().startsWith(toqstr(package().user_support().absFileName()))) ?
370 user_icon : system_icon;
371 item->setIcon(0, file_icon);
372 item->setData(0, Qt::UserRole, info.filePath());
373 item->setData(0, Qt::DisplayRole, guiname);
374 item->setData(0, Qt::ToolTipRole, info.filePath());
375 if (subcat.isEmpty())
376 catItem->addChild(item);
378 QTreeWidgetItem * subcatItem = new QTreeWidgetItem();
379 if (cats.contains(catsave)) {
380 QList<QTreeWidgetItem *> pcats = filesLW->findItems(cat, Qt::MatchExactly);
381 for (int iit = 0; iit < pcats.size(); ++iit) {
382 for (int cit = 0; cit < pcats.at(iit)->childCount(); ++cit) {
383 if (pcats.at(iit)->child(cit)->text(0) == subcat) {
384 subcatItem = pcats.at(iit)->child(cit);
390 subcatItem->setText(0, subcat);
391 subcatItem->setIcon(0, file_icon);
394 subcatItem->addChild(item);
395 catItem->addChild(subcatItem);
399 filesLW->sortItems(0, Qt::AscendingOrder);
405 void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
407 switch (buttonBox->standardButton(button)) {
408 case QDialogButtonBox::Open:
411 case QDialogButtonBox::Cancel:
420 void GuiLyXFiles::filterLabels()
422 Qt::CaseSensitivity cs = csFindCB->isChecked() ?
423 Qt::CaseSensitive : Qt::CaseInsensitive;
424 QTreeWidgetItemIterator it(filesLW);
427 (*it)->childCount() == 0
428 && !(*it)->text(0).contains(filter_->text(), cs)
435 void GuiLyXFiles::resetFilter()
437 filter_->setText(QString());
441 QString const GuiLyXFiles::getRealPath()
443 QString const relpath = filesLW->currentItem()->data(0, Qt::UserRole).toString();
444 QString const language = languageCO->itemData(languageCO->currentIndex()).toString();
445 if (localizations_.contains(relpath)
446 && localizations_.find(relpath).value().contains(language))
447 return localizations_.find(relpath).value().find(language).value();
452 void GuiLyXFiles::applyView()
454 file_ = getRealPath();
458 bool GuiLyXFiles::isValid()
460 return filesLW->currentItem() && filesLW->currentItem()->isSelected();
464 bool GuiLyXFiles::initialiseParams(string const & type)
466 type_ = type.empty() ? toqstr("templates") : toqstr(type);
472 void GuiLyXFiles::paramsToDialog()
475 if (type_ == "examples" || type_ == "templates") {
476 fileTypeCO->addItem(qt_("Templates"), toqstr("templates"));
477 fileTypeCO->addItem(qt_("Examples"), toqstr("examples"));
478 } else if (type_ == "ui")
479 fileTypeCO->addItem(qt_("User Interface Files"), toqstr("ui"));
480 else if (type_ == "bind")
481 fileTypeCO->addItem(qt_("Key Binding Files"), toqstr("bind"));
483 if (!type_.isEmpty()) {
484 int i = fileTypeCO->findData(type_);
486 fileTypeCO->setCurrentIndex(i);
488 if (type_ == "examples")
489 setTitle(qt_("Open Example File"));
490 else if (type_ == "templates")
491 setTitle(qt_("New File From Template"));
493 setTitle(qt_("Open File"));
495 bc().setValid(isValid());
499 void GuiLyXFiles::dispatchParams()
505 if (type_ == "templates")
507 arg += fromqstr(file_);
508 FuncCode const lfun = getLfun();
510 dispatch(FuncRequest(lfun, arg));
514 FuncCode GuiLyXFiles::getLfun() const
516 if (type_ == "examples")
517 return LFUN_FILE_OPEN;
518 else if (type_ == "templates")
519 return LFUN_BUFFER_NEW_TEMPLATE;
520 return LFUN_NOACTION;
523 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
526 } // namespace frontend
529 #include "moc_GuiLyXFiles.cpp"