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;
43 QString const guiString(QString in)
45 // recode specially encoded chars in file names
46 return in.replace('_', ' ').replace("%26", "&").replace("%28", "(").replace("%29", ")");
52 QMap<QString, QString> GuiLyXFiles::getFiles()
54 QMap<QString, QString> result;
55 // We look for lyx files in the subdirectory dir of
57 // 2) build_lyxdir (if not empty)
59 // in this order. Files with a given sub-hierarchy will
60 // only be listed once.
61 // We also consider i18n subdirectories and store them separately.
65 // The three locations to look at.
66 string const user = addPath(package().user_support().absFileName(), fromqstr(type_));
67 string const build = addPath(package().build_support().absFileName(), fromqstr(type_));
68 string const system = addPath(package().system_support().absFileName(), fromqstr(type_));
70 available_languages_.insert(toqstr("en"), qt_("English"));
72 QString const type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
74 // Search in the base paths
75 if (type == "all" || type == "user")
77 if (type == "all" || type == "system")
81 for (int i = 0; i < dirs.size(); ++i) {
82 QString const dir = dirs.at(i);
83 QDirIterator it(dir, QDir::Files, QDirIterator::Subdirectories);
84 while (it.hasNext()) {
85 QString fn(QFile(it.next()).fileName());
86 if (!fn.endsWith(getSuffix()))
88 QString relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
89 qstring_to_ucs4(dir)));
91 int s = relpath.indexOf('/', 0);
92 QString cat = qt_("General");
93 QString localization = "en";
96 cat = relpath.left(s);
97 if (all_languages_.contains(cat)
98 && !all_languages_.contains(dir.right(dir.lastIndexOf('/')))) {
99 QMap<QString, QString>::const_iterator li = all_languages_.find(cat);
100 // Skip i18n dir, but add language to the combo
101 if (!available_languages_.contains(li.key()))
102 available_languages_.insert(li.key(), li.value());
104 int sc = relpath.indexOf('/', s + 1);
105 cat = (sc == -1) ? qt_("General") : relpath.mid(s + 1, sc - s - 1);
109 int sc = relpath.indexOf('/', s + 1);
110 QString const subcat = (sc == -1) ?
111 QString() : relpath.mid(s + 1, sc - s - 1);
112 if (!subcat.isEmpty())
116 if (!relpaths.contains(relpath)) {
117 relpaths.append(relpath);
118 if (localization != "en")
120 relpath = relpath.mid(relpath.indexOf('/') + 1);
121 result.insert(relpath, cat);
123 QMap<QString, QString> lm;
124 if (localizations_.contains(relpath))
125 lm = localizations_.find(relpath).value();
126 lm.insert(localization, fn);
127 localizations_.insert(relpath, lm);
131 // Find and store GUI language
132 for (auto const & l : guilangs_) {
133 // First try with the full name
134 // `en' files are not in a subdirectory
135 if (available_languages_.contains(toqstr(l))) {
136 guilang_ = toqstr(l);
139 // Then the name without country code
140 string const shortl = token(l, '_', 0);
141 if (available_languages_.contains(toqstr(shortl))) {
142 guilang_ = toqstr(shortl);
146 // pre-fill the language combo (it will be updated once an item
149 QMap<QString, QString>::const_iterator i =available_languages_.constBegin();
150 while (i != available_languages_.constEnd()) {
151 languageCO->addItem(i.value(), i.key());
155 languageLA->setText(qt_("Preferred &Language:"));
160 GuiLyXFiles::GuiLyXFiles(GuiView & lv)
161 : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
165 // Get all supported languages (by code) in order to exclude those
167 QAbstractItemModel * language_model = guiApp->languageModel();
168 language_model->sort(0);
169 for (int i = 0; i != language_model->rowCount(); ++i) {
170 QModelIndex index = language_model->index(i, 0);
171 Language const * lang =
172 languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
175 QString const code = toqstr(lang->code());
176 if (!all_languages_.contains(code))
177 all_languages_.insert(code, qt_(lang->display()));
178 // Also store code without country code
179 QString const shortcode = code.left(code.indexOf('_'));
180 if (shortcode != code && !all_languages_.contains(shortcode))
181 all_languages_.insert(shortcode, qt_(lang->display()));
184 string lang = getGuiMessages().language();
185 string const language = getEnv("LANGUAGE");
186 if (!language.empty())
187 lang += ":" + language;
188 guilangs_ = getVectorFromString(lang, ":");
191 filter_ = new FancyLineEdit(this);
192 filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
193 filter_->setButtonVisible(FancyLineEdit::Right, true);
194 filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
195 filter_->setAutoHideButton(FancyLineEdit::Right, true);
196 filter_->setPlaceholderText(qt_("All available files"));
197 filter_->setToolTip(qt_("Enter string to filter the list of available files"));
198 #if (QT_VERSION < 0x050000)
199 connect(filter_, SIGNAL(downPressed()),
200 filesLW, SLOT(setFocus()));
202 connect(filter_, &FancyLineEdit::downPressed,
203 filesLW, [=](){ focusAndHighlight(filesLW); });
206 filterBarL->addWidget(filter_, 0);
207 findKeysLA->setBuddy(filter_);
209 connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
210 this, SLOT(slotButtonBox(QAbstractButton *)));
212 connect(filesLW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
213 this, SLOT(changed_adaptor()));
214 connect(filesLW, SIGNAL(itemSelectionChanged()),
215 this, SLOT(changed_adaptor()));
216 connect(filter_, SIGNAL(textEdited(QString)),
217 this, SLOT(filterLabels()));
218 connect(filter_, SIGNAL(rightButtonClicked()),
219 this, SLOT(resetFilter()));
221 bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
222 bc().setOK(buttonBox->button(QDialogButtonBox::Open));
223 bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
225 //filesLW->setViewMode(QListView::ListMode);
226 filesLW->setIconSize(QSize(22, 22));
228 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
229 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
230 fileTypeCO->addItem(qt_("User and System Files"), toqstr("all"));
231 fileTypeCO->addItem(user_icon, qt_("User Files Only"), toqstr("user"));
232 fileTypeCO->addItem(system_icon, qt_("System Files Only"), toqstr("system"));
234 setFocusProxy(filter_);
238 QString const GuiLyXFiles::getSuffix()
240 if (type_ == "bind" || type_ == "ui")
241 return toqstr(".") + type_;
242 else if (type_ == "kbd")
249 bool GuiLyXFiles::translateName() const
251 return (type_ == "templates" || type_ == "examples");
255 void GuiLyXFiles::changed_adaptor()
261 void GuiLyXFiles::on_fileTypeCO_activated(int)
267 void GuiLyXFiles::on_languageCO_activated(int i)
269 savelang_ = languageCO->itemData(i).toString();
270 if (!filesLW->currentItem())
273 filesLW->currentItem()->setData(0, Qt::ToolTipRole, getRealPath());
278 void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem * item, int)
280 if (!item->data(0, Qt::UserRole).toString().endsWith(getSuffix()))
281 // not a file (probably a header)
289 void GuiLyXFiles::on_filesLW_itemClicked(QTreeWidgetItem * item, int)
291 QString const data = item->data(0, Qt::UserRole).toString();
292 if (!data.endsWith(getSuffix()))
293 // not a file (probably a header)
297 QMap<QString, QString>::const_iterator i =available_languages_.constBegin();
298 while (i != available_languages_.constEnd()) {
299 if (localizations_.contains(data)
300 && localizations_.find(data).value().contains(i.key()))
301 languageCO->addItem(i.value(), i.key());
304 languageLA->setText(qt_("File &Language:"));
305 languageCO->setToolTip(qt_("All available languages of the selected file are displayed here.\n"
306 "The selected language version will be opened."));
308 QString const realpath = getRealPath();
309 filesLW->currentItem()->setData(0, Qt::ToolTipRole, realpath);
310 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
311 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
312 QIcon file_icon = (realpath.startsWith(toqstr(package().user_support().absFileName()))) ?
313 user_icon : system_icon;
314 item->setIcon(0, file_icon);
318 void GuiLyXFiles::setLanguage()
320 // Enable language selection only if there is a selection.
321 languageCO->setEnabled(languageCO->count() > 1);
322 languageLA->setEnabled(languageCO->count() > 1);
323 // first try last setting
324 if (!savelang_.isEmpty()) {
325 int index = languageCO->findData(savelang_);
327 languageCO->setCurrentIndex(index);
331 // next, try GUI lang
332 if (!guilang_.isEmpty()) {
333 int index = languageCO->findData(guilang_);
335 languageCO->setCurrentIndex(index);
339 // Finally, fall back to English (which should be always there)
340 int index = languageCO->findData(toqstr("en"));
342 languageCO->setCurrentIndex(index);
347 void GuiLyXFiles::on_browsePB_pressed()
349 QString path1 = toqstr(lyxrc.document_path);
350 QString path2 = toqstr(lyxrc.example_path);
351 QString title = qt_("Select example file");
352 QString filter = qt_("LyX Documents (*.lyx)");
353 QString b1 = qt_("D&ocuments");
354 QString b2 = qt_("&Examples");
356 if (type_ == "templates") {
357 path2 = toqstr(lyxrc.template_path);
358 title = qt_("Select template file");
359 b1 = qt_("D&ocuments");
360 b2 = qt_("&Templates");
362 else if (type_ != "examples") {
363 path1 = toqstr(addName(package().user_support().absFileName(), fromqstr(type_)));
364 path2 = toqstr(addName(package().system_support().absFileName(), fromqstr(type_)));
365 b1 = qt_("&User files");
366 b2 = qt_("&System files");
369 title = qt_("Chose UI file");
370 filter = qt_("LyX UI Files (*.ui)");
372 if (type_ == "bind") {
373 title = qt_("Chose bind file");
374 filter = qt_("LyX Bind Files (*.bind)");
376 if (type_ == "kbd") {
377 title = qt_("Chose keyboard map");
378 filter = qt_("LyX Keymap Files (*.kmap)");
381 FileDialog dlg(title);
382 dlg.setButton1(b1, path1);
383 dlg.setButton2(b2, path2);
385 FileDialog::Result result = dlg.open(path2, QStringList(filter));
387 if (result.first != FileDialog::Later && !result.second.isEmpty()) {
388 file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
395 void GuiLyXFiles::updateContents()
398 QMap<QString, QString> files = getFiles();
399 languageCO->model()->sort(0);
402 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
403 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
405 QMap<QString, QString>::const_iterator it = files.constBegin();
407 capfont.setBold(true);
408 while (it != files.constEnd()) {
409 QFileInfo const info = QFileInfo(it.key());
410 QString const realpath = getRealPath(it.key());
411 QString cat = it.value();
414 if (cat.contains('/')) {
416 cat = catsave.left(catsave.indexOf('/'));
417 subcat = toqstr(translateIfPossible(
418 qstring_to_ucs4(guiString(catsave.mid(catsave.indexOf('/') + 1)))));
420 cat = toqstr(translateIfPossible(qstring_to_ucs4(guiString(cat))));
421 QTreeWidgetItem * catItem = new QTreeWidgetItem();
422 if (!cats.contains(cat)) {
423 catItem->setText(0, cat);
424 catItem->setFont(0, capfont);
425 filesLW->insertTopLevelItem(0, catItem);
426 catItem->setExpanded(true);
429 catItem = filesLW->findItems(cat, Qt::MatchExactly).first();
430 QTreeWidgetItem * item = new QTreeWidgetItem();
431 QString const filename = info.fileName();
432 QString guiname = filename.left(filename.lastIndexOf(getSuffix())).replace('_', ' ');
434 guiname = toqstr(translateIfPossible(qstring_to_ucs4(guiString(guiname))));
435 QIcon file_icon = (realpath.startsWith(toqstr(package().user_support().absFileName()))) ?
436 user_icon : system_icon;
437 item->setIcon(0, file_icon);
438 item->setData(0, Qt::UserRole, it.key());
439 item->setData(0, Qt::DisplayRole, guiname);
440 item->setData(0, Qt::ToolTipRole, realpath);
441 if (subcat.isEmpty())
442 catItem->addChild(item);
444 QTreeWidgetItem * subcatItem = new QTreeWidgetItem();
445 if (cats.contains(catsave)) {
446 QList<QTreeWidgetItem *> pcats = filesLW->findItems(cat, Qt::MatchExactly);
447 for (int iit = 0; iit < pcats.size(); ++iit) {
448 for (int cit = 0; cit < pcats.at(iit)->childCount(); ++cit) {
449 if (pcats.at(iit)->child(cit)->text(0) == subcat) {
450 subcatItem = pcats.at(iit)->child(cit);
456 subcatItem->setText(0, subcat);
457 subcatItem->setIcon(0, file_icon);
460 subcatItem->addChild(item);
461 catItem->addChild(subcatItem);
465 filesLW->sortItems(0, Qt::AscendingOrder);
471 void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
473 switch (buttonBox->standardButton(button)) {
474 case QDialogButtonBox::Open:
477 case QDialogButtonBox::Cancel:
486 void GuiLyXFiles::filterLabels()
488 Qt::CaseSensitivity cs = csFindCB->isChecked() ?
489 Qt::CaseSensitive : Qt::CaseInsensitive;
490 QTreeWidgetItemIterator it(filesLW);
493 (*it)->childCount() == 0
494 && !(*it)->text(0).contains(filter_->text(), cs)
501 void GuiLyXFiles::resetFilter()
503 filter_->setText(QString());
507 QString const GuiLyXFiles::getRealPath(QString relpath)
509 if (relpath.isEmpty())
510 relpath = filesLW->currentItem()->data(0, Qt::UserRole).toString();
511 QString const language = languageCO->itemData(languageCO->currentIndex()).toString();
512 if (localizations_.contains(relpath)) {
513 if (localizations_.find(relpath).value().contains(language))
514 return localizations_.find(relpath).value().find(language).value();
515 else if (localizations_.find(relpath).value().contains(guilang_))
516 return localizations_.find(relpath).value().find(guilang_).value();
517 else if (localizations_.find(relpath).value().contains(toqstr("en")))
518 return localizations_.find(relpath).value().find(toqstr("en")).value();
524 void GuiLyXFiles::applyView()
526 file_ = getRealPath();
530 bool GuiLyXFiles::isValid()
532 return filesLW->currentItem() && filesLW->currentItem()->isSelected();
536 bool GuiLyXFiles::initialiseParams(string const & type)
538 type_ = type.empty() ? toqstr("templates") : toqstr(type);
544 void GuiLyXFiles::passParams(string const & data)
546 initialiseParams(data);
551 void GuiLyXFiles::selectItem(QString const item)
553 QList<QTreeWidgetItem *> twi = filesLW->findItems(item, Qt::MatchExactly|Qt::MatchRecursive);
555 twi.first()->setSelected(true);
559 void GuiLyXFiles::paramsToDialog()
561 if (type_ == "examples")
562 setTitle(qt_("Open Example File"));
563 else if (type_ == "templates")
564 setTitle(qt_("New File From Template"));
566 setTitle(qt_("Open File"));
568 bc().setValid(isValid());
572 void GuiLyXFiles::dispatchParams()
578 if (type_ == "templates")
580 arg += fromqstr(file_);
581 FuncCode const lfun = getLfun();
583 if (lfun == LFUN_NOACTION)
587 dispatch(FuncRequest(lfun, arg));
591 FuncCode GuiLyXFiles::getLfun() const
593 if (type_ == "examples")
594 return LFUN_FILE_OPEN;
595 else if (type_ == "templates")
596 return LFUN_BUFFER_NEW_TEMPLATE;
597 return LFUN_NOACTION;
600 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
603 } // namespace frontend
606 #include "moc_GuiLyXFiles.cpp"