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 prefer them.
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 // First, query the current language subdir (except for English)
60 QString const lang = languageCO->itemData(languageCO->currentIndex()).toString();
61 if (!lang.startsWith("en")) {
62 // First try with the full code
63 dirs << toqstr(addPath(user, fromqstr(lang)));
64 dirs << toqstr(addPath(build, fromqstr(lang)));
65 dirs << toqstr(addPath(system, fromqstr(lang)));
66 // Then the name without country code
67 QString const shortl = lang.left(lang.indexOf('_'));
69 dirs << toqstr(addPath(user, fromqstr(shortl)));
70 dirs << toqstr(addPath(build, fromqstr(shortl)));
71 dirs << toqstr(addPath(system, fromqstr(shortl)));
75 // Next, search in the base path
80 for (int i = 0; i < dirs.size(); ++i) {
81 QString const dir = dirs.at(i);
82 QDirIterator it(dir, QDir::Files, QDirIterator::Subdirectories);
83 while (it.hasNext()) {
84 QString fn(QFile(it.next()).fileName());
85 if (!fn.endsWith(getSuffix()))
87 QString const relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
88 qstring_to_ucs4(dir)));
90 int s = relpath.indexOf('/', 0);
91 QString cat = qt_("General");
94 cat = relpath.left(s);
95 int sc = relpath.indexOf('/', s + 1);
96 QString const subcat = (sc == -1) ?
97 QString() : relpath.mid(s + 1, sc - s - 1);
98 if (langcodes_.contains(cat)
99 && !langcodes_.contains(dir.right(dir.lastIndexOf('/'))))
102 if (!subcat.isEmpty())
105 if (!relpaths.contains(relpath)) {
106 relpaths.append(relpath);
114 GuiLyXFiles::GuiLyXFiles(GuiView & lv)
115 : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
119 // Get all supported languages (by code) in order to exclude those
121 QAbstractItemModel * language_model = guiApp->languageModel();
122 language_model->sort(0);
123 for (int i = 0; i != language_model->rowCount(); ++i) {
124 QModelIndex index = language_model->index(i, 0);
125 Language const * lang =
126 languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
129 string const code = lang->code();
130 languageCO->addItem(qt_(lang->display()), toqstr(code));
131 langcodes_ << toqstr(code);
132 // Also store code without country code
133 string const shortcode = token(code, '_', 0);
134 if (shortcode != code)
135 langcodes_ << toqstr(shortcode);
137 // Preset to GUI language
138 string lang = getGuiMessages().language();
139 string const language = getEnv("LANGUAGE");
140 if (!language.empty())
141 lang += ":" + language;
143 for (auto const & l : getVectorFromString(lang, ":")) {
144 // First try with the full name
145 // `en' files are not in a subdirectory
146 int i = languageCO->findData(toqstr(l));
148 languageCO->setCurrentIndex(i);
151 // Then the name without country code
152 string const shortl = token(l, '_', 0);
153 i = languageCO->findData(toqstr(l));
155 languageCO->setCurrentIndex(i);
161 filter_ = new FancyLineEdit(this);
162 filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
163 filter_->setButtonVisible(FancyLineEdit::Right, true);
164 filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
165 filter_->setAutoHideButton(FancyLineEdit::Right, true);
166 filter_->setPlaceholderText(qt_("All available files"));
167 filter_->setToolTip(qt_("Enter string to filter the list of available files"));
168 #if (QT_VERSION < 0x050000)
169 connect(filter_, SIGNAL(downPressed()),
170 filesLW, SLOT(setFocus()));
172 connect(filter_, &FancyLineEdit::downPressed,
173 filesLW, [=](){ focusAndHighlight(filesLW); });
176 filterBarL->addWidget(filter_, 0);
177 findKeysLA->setBuddy(filter_);
179 connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
180 this, SLOT(slotButtonBox(QAbstractButton *)));
182 connect(filesLW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
183 this, SLOT(changed_adaptor()));
184 connect(filesLW, SIGNAL(itemSelectionChanged()),
185 this, SLOT(changed_adaptor()));
186 connect(filter_, SIGNAL(textEdited(QString)),
187 this, SLOT(filterLabels()));
188 connect(filter_, SIGNAL(rightButtonClicked()),
189 this, SLOT(resetFilter()));
191 bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
192 bc().setOK(buttonBox->button(QDialogButtonBox::Open));
193 bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
195 //filesLW->setViewMode(QListView::ListMode);
196 filesLW->setIconSize(QSize(22, 22));
198 setFocusProxy(filter_);
202 QString const GuiLyXFiles::getSuffix()
204 if (type_ == "bind" || type_ == "ui")
205 return toqstr(".") + type_;
211 bool GuiLyXFiles::translateName() const
213 return (type_ == "templates" || type_ == "examples");
217 void GuiLyXFiles::changed_adaptor()
223 void GuiLyXFiles::on_fileTypeCO_activated(int)
229 void GuiLyXFiles::on_languageCO_activated(int)
235 void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem * item, int)
237 if (!item->data(0, Qt::UserRole).toString().endsWith(getSuffix()))
238 // not a file (probably a header)
247 void GuiLyXFiles::on_browsePB_pressed()
249 bool const examples = (type_ == "examples");
250 FileDialog dlg(qt_("Select template file"));
251 dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
253 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
255 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
257 FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
258 : toqstr(lyxrc.template_path),
259 QStringList(qt_("LyX Documents (*.lyx)")));
261 if (result.first != FileDialog::Later && !result.second.isEmpty()) {
262 file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
269 void GuiLyXFiles::updateContents()
271 QString type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
272 QMap<QString, QString> files;
273 getFiles(files, type);
276 QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
277 QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
279 QMap<QString, QString>::const_iterator it = files.constBegin();
281 capfont.setBold(true);
282 while (it != files.constEnd()) {
283 QFileInfo const info = QFileInfo(it.key());
284 QString cat = it.value();
287 if (cat.contains('/')) {
289 cat = catsave.left(catsave.indexOf('/'));
290 subcat = toqstr(translateIfPossible(
291 qstring_to_ucs4(catsave.mid(
292 catsave.indexOf('/') + 1).replace('_', ' '))));
294 cat = toqstr(translateIfPossible(qstring_to_ucs4(cat.replace('_', ' '))));
295 QTreeWidgetItem * catItem = new QTreeWidgetItem();
296 if (!cats.contains(cat)) {
297 catItem->setText(0, cat);
298 catItem->setFont(0, capfont);
299 filesLW->insertTopLevelItem(0, catItem);
300 catItem->setExpanded(true);
303 catItem = filesLW->findItems(cat, Qt::MatchExactly).first();
304 QTreeWidgetItem * item = new QTreeWidgetItem();
305 QString const filename = info.fileName();
306 QString guiname = filename.left(filename.lastIndexOf(getSuffix())).replace('_', ' ');
308 guiname = toqstr(translateIfPossible(qstring_to_ucs4(guiname)));
309 QIcon file_icon = (info.filePath().startsWith(toqstr(package().user_support().absFileName()))) ?
310 user_icon : system_icon;
311 item->setIcon(0, file_icon);
312 item->setData(0, Qt::UserRole, info.filePath());
313 item->setData(0, Qt::DisplayRole, guiname);
314 item->setData(0, Qt::ToolTipRole, info.filePath());
315 if (subcat.isEmpty())
316 catItem->addChild(item);
318 QTreeWidgetItem * subcatItem = new QTreeWidgetItem();
319 if (cats.contains(catsave)) {
320 QList<QTreeWidgetItem *> pcats = filesLW->findItems(cat, Qt::MatchExactly);
321 for (int iit = 0; iit < pcats.size(); ++iit) {
322 for (int cit = 0; cit < pcats.at(iit)->childCount(); ++cit) {
323 if (pcats.at(iit)->child(cit)->text(0) == subcat) {
324 subcatItem = pcats.at(iit)->child(cit);
330 subcatItem->setText(0, subcat);
331 subcatItem->setIcon(0, file_icon);
334 subcatItem->addChild(item);
335 catItem->addChild(subcatItem);
339 filesLW->sortItems(0, Qt::AscendingOrder);
345 void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
347 switch (buttonBox->standardButton(button)) {
348 case QDialogButtonBox::Open:
351 case QDialogButtonBox::Cancel:
360 void GuiLyXFiles::filterLabels()
362 Qt::CaseSensitivity cs = csFindCB->isChecked() ?
363 Qt::CaseSensitive : Qt::CaseInsensitive;
364 QTreeWidgetItemIterator it(filesLW);
367 (*it)->childCount() == 0
368 && !(*it)->text(0).contains(filter_->text(), cs)
375 void GuiLyXFiles::resetFilter()
377 filter_->setText(QString());
382 void GuiLyXFiles::applyView()
384 file_ = filesLW->currentItem()->data(0, Qt::UserRole).toString();
388 bool GuiLyXFiles::isValid()
390 return filesLW->currentItem() && filesLW->currentItem()->isSelected();
394 bool GuiLyXFiles::initialiseParams(string const & type)
396 type_ = type.empty() ? toqstr("templates") : toqstr(type);
402 void GuiLyXFiles::paramsToDialog()
405 if (type_ == "examples" || type_ == "templates") {
406 fileTypeCO->addItem(qt_("Templates"), toqstr("templates"));
407 fileTypeCO->addItem(qt_("Examples"), toqstr("examples"));
408 } else if (type_ == "ui")
409 fileTypeCO->addItem(qt_("User Interface Files"), toqstr("ui"));
410 else if (type_ == "bind")
411 fileTypeCO->addItem(qt_("Key Binding Files"), toqstr("bind"));
413 if (!type_.isEmpty()) {
414 int i = fileTypeCO->findData(type_);
416 fileTypeCO->setCurrentIndex(i);
418 if (type_ == "examples")
419 setTitle(qt_("Open Example File"));
420 else if (type_ == "templates")
421 setTitle(qt_("New File From Template"));
423 setTitle(qt_("Open File"));
425 bc().setValid(isValid());
429 void GuiLyXFiles::dispatchParams()
435 if (type_ == "templates")
437 arg += fromqstr(file_);
438 FuncCode const lfun = getLfun();
440 dispatch(FuncRequest(lfun, arg));
444 FuncCode GuiLyXFiles::getLfun() const
446 if (type_ == "examples")
447 return LFUN_FILE_OPEN;
448 else if (type_ == "templates")
449 return LFUN_BUFFER_NEW_TEMPLATE;
450 return LFUN_NOACTION;
453 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
456 } // namespace frontend
459 #include "moc_GuiLyXFiles.cpp"