]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiLyXFiles.cpp
Fix header case
[lyx.git] / src / frontends / qt4 / GuiLyXFiles.cpp
1 /**
2  * \file GuiLyXFiles.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Spitzmüller
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "GuiLyXFiles.h"
14 #include "GuiApplication.h"
15 #include "qt_helpers.h"
16
17 #include "FileDialog.h"
18 #include "Buffer.h"
19 #include "BufferParams.h"
20 #include "FuncRequest.h"
21 #include "Language.h"
22 #include "LyXRC.h"
23
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"
31
32 #include <QDirIterator>
33 #include <QTreeWidget>
34
35 using namespace std;
36 using namespace lyx::support;
37
38 namespace lyx {
39 namespace frontend {
40
41
42 QMap<QString, QString> GuiLyXFiles::getFiles()
43 {
44         QMap<QString, QString> result;
45         // We look for lyx files in the subdirectory dir of
46         //   1) user_lyxdir
47         //   2) build_lyxdir (if not empty)
48         //   3) system_lyxdir
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.
52         QStringList dirs;
53         QStringList relpaths;
54
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_));
59
60         available_languages_.insert(toqstr("en"), qt_("English"));
61
62         QString const type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
63
64         // Search in the base paths
65         if (type == "all" || type == "user")
66                 dirs << toqstr(user);
67         if (type == "all" || type == "system")
68                 dirs << toqstr(build)
69                      << toqstr(system);
70
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()))
77                                 continue;
78                         QString relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
79                                                              qstring_to_ucs4(dir)));
80                         // <cat>/
81                         int s = relpath.indexOf('/', 0);
82                         QString cat = qt_("General");
83                         QString localization = "en";
84                         if (s != -1) {
85                                 // <cat>/<subcat>/
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());
93                                         localization = cat;
94                                         int sc = relpath.indexOf('/', s + 1);
95                                         cat = (sc == -1) ? qt_("General") : relpath.mid(s + 1, sc - s - 1);
96                                         s = sc;
97                                 }
98                                 if (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())
103                                                 cat += '/' + subcat;
104                                 }
105                         }
106                         if (!relpaths.contains(relpath)) {
107                                 relpaths.append(relpath);
108                                 if (localization != "en")
109                                         // strip off lang/
110                                         relpath = relpath.mid(relpath.indexOf('/') + 1);
111                                 result.insert(relpath, cat);
112                                                                         
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);
118                         }
119                 }
120         }
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);
127                         break;
128                 }
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);
133                         break;
134                 }
135         }
136         // pre-fill the language combo (it will be updated once an item 
137         // has been clicked)
138         languageCO->clear();
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());
143                 ++i;
144         }
145         setLanguage();
146         languageLA->setText(qt_("Preferred &Language:"));
147         return result;
148 }
149
150
151 GuiLyXFiles::GuiLyXFiles(GuiView & lv)
152         : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
153 {
154         setupUi(this);
155
156         // Get all supported languages (by code) in order to exclude those
157         // dirs later.
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()));
164                 if (!lang)
165                         continue;
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()));
173         }
174         // Get GUI language
175         string lang = getGuiMessages().language();
176         string const language = getEnv("LANGUAGE");
177         if (!language.empty())
178                 lang += ":" + language;
179         guilangs_ =  getVectorFromString(lang, ":");
180
181         // The filter bar
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()));
192 #else
193         connect(filter_, &FancyLineEdit::downPressed,
194                 filesLW, [=](){ focusAndHighlight(filesLW); });
195 #endif
196
197         filterBarL->addWidget(filter_, 0);
198         findKeysLA->setBuddy(filter_);
199
200         connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
201                 this, SLOT(slotButtonBox(QAbstractButton *)));
202
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()));
211
212         bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
213         bc().setOK(buttonBox->button(QDialogButtonBox::Open));
214         bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
215
216         //filesLW->setViewMode(QListView::ListMode);
217         filesLW->setIconSize(QSize(22, 22));
218
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"));
224
225         setFocusProxy(filter_);
226 }
227
228
229 QString const GuiLyXFiles::getSuffix()
230 {
231         if (type_ == "bind" || type_ == "ui")
232                 return toqstr(".") + type_;
233         
234         return ".lyx";
235 }
236
237
238 bool GuiLyXFiles::translateName() const
239 {
240         return (type_ == "templates" || type_ == "examples");
241 }
242
243
244 void GuiLyXFiles::changed_adaptor()
245 {
246         changed();
247 }
248
249
250 void GuiLyXFiles::on_fileTypeCO_activated(int)
251 {
252         updateContents();
253 }
254
255
256 void GuiLyXFiles::on_languageCO_activated(int i)
257 {
258         savelang_ = languageCO->itemData(i).toString();
259         if (!filesLW->currentItem())
260                 return;
261
262         filesLW->currentItem()->setData(0, Qt::ToolTipRole, getRealPath());
263         changed();
264 }
265
266
267 void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem * item, int)
268 {
269         if (!item->data(0, Qt::UserRole).toString().endsWith(getSuffix()))
270                 // not a file (probably a header)
271                 return;
272
273         applyView();
274         dispatchParams();
275         close();
276 }
277
278 void GuiLyXFiles::on_filesLW_itemClicked(QTreeWidgetItem * item, int)
279 {
280         QString const data = item->data(0, Qt::UserRole).toString();
281         if (!data.endsWith(getSuffix()))
282                 // not a file (probably a header)
283                 return;
284
285         languageCO->clear();
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());
291                 ++i;
292         }
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."));
296         setLanguage();
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);
304 }
305
306
307 void GuiLyXFiles::setLanguage()
308 {
309         // first try last setting
310         if (!savelang_.isEmpty()) {
311                 int index = languageCO->findData(savelang_);
312                 if (index != -1) {
313                         languageCO->setCurrentIndex(index);
314                         return;
315                 }
316         }
317         // next, try GUI lang
318         if (!guilang_.isEmpty()) {
319                 int index = languageCO->findData(guilang_);
320                 if (index != -1) {
321                         languageCO->setCurrentIndex(index);
322                         return;
323                 }
324         }
325         // Finally, fall back to English (which should be always there)
326         int index = languageCO->findData(toqstr("en"));
327         if (index != -1) {
328                 languageCO->setCurrentIndex(index);
329         }
330 }
331
332
333 void GuiLyXFiles::on_browsePB_pressed()
334 {
335         bool const examples = (type_ == "examples");
336         FileDialog dlg(qt_("Select template file"));
337         dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
338         if (examples)
339                 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
340         else
341                 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
342
343         FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
344                                                       : toqstr(lyxrc.template_path),
345                                  QStringList(qt_("LyX Documents (*.lyx)")));
346
347         if (result.first != FileDialog::Later && !result.second.isEmpty()) {
348                 file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
349                 dispatchParams();
350                 close();
351         }
352 }
353
354
355 void GuiLyXFiles::updateContents()
356 {
357         languageCO->clear();
358         QMap<QString, QString> files = getFiles();
359         languageCO->model()->sort(0);
360
361         filesLW->clear();
362         QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
363         QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
364         QStringList cats;
365         QMap<QString, QString>::const_iterator it = files.constBegin();
366         QFont capfont;
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();
372                 QString subcat;
373                 QString catsave;
374                 if (cat.contains('/')) {
375                         catsave = cat;
376                         cat = catsave.left(catsave.indexOf('/'));
377                         subcat = toqstr(translateIfPossible(
378                                         qstring_to_ucs4(catsave.mid(
379                                                 catsave.indexOf('/') + 1).replace('_', ' '))));
380                 }
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);
388                         cats << cat;
389                 } else
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('_', ' ');
394                 if (translateName())
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);
404                 else {
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);
412                                                         break;
413                                                 }
414                                         }
415                                 }
416                         } else {
417                                 subcatItem->setText(0, subcat);
418                                 subcatItem->setIcon(0, file_icon);
419                                 cats << catsave;
420                         }
421                         subcatItem->addChild(item);
422                         catItem->addChild(subcatItem);
423                 }
424                 ++it;
425         }
426         filesLW->sortItems(0, Qt::AscendingOrder);
427         // redo filter
428         filterLabels();
429 }
430
431
432 void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
433 {
434         switch (buttonBox->standardButton(button)) {
435         case QDialogButtonBox::Open:
436                 slotOK();
437                 break;
438         case QDialogButtonBox::Cancel:
439                 slotClose();
440                 break;
441         default:
442                 break;
443         }
444 }
445
446
447 void GuiLyXFiles::filterLabels()
448 {
449         Qt::CaseSensitivity cs = csFindCB->isChecked() ?
450                 Qt::CaseSensitive : Qt::CaseInsensitive;
451         QTreeWidgetItemIterator it(filesLW);
452         while (*it) {
453                 (*it)->setHidden(
454                         (*it)->childCount() == 0
455                         && !(*it)->text(0).contains(filter_->text(), cs)
456                 );
457                 ++it;
458         }
459 }
460
461
462 void GuiLyXFiles::resetFilter()
463 {
464         filter_->setText(QString());
465         filterLabels();
466 }
467
468 QString const GuiLyXFiles::getRealPath(QString relpath)
469 {
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();
480         }
481         return QString();
482 }
483
484
485 void GuiLyXFiles::applyView()
486 {
487         file_ = getRealPath();
488 }
489
490
491 bool GuiLyXFiles::isValid()
492 {
493         return filesLW->currentItem() && filesLW->currentItem()->isSelected();
494 }
495
496
497 bool GuiLyXFiles::initialiseParams(string const & type)
498 {
499         type_ = type.empty() ? toqstr("templates") : toqstr(type);
500         paramsToDialog();
501         return true;
502 }
503
504
505 void GuiLyXFiles::paramsToDialog()
506 {
507         if (type_ == "examples")
508                 setTitle(qt_("Open Example File"));
509         else if (type_ == "templates")
510                 setTitle(qt_("New File From Template"));
511         else
512                 setTitle(qt_("Open File"));
513
514         bc().setValid(isValid());
515 }
516
517
518 void GuiLyXFiles::dispatchParams()
519 {
520         if (file_.isEmpty())
521                 return;
522
523         string arg;
524         if (type_ == "templates")
525                 arg = "newfile ";
526         arg += fromqstr(file_);
527         FuncCode const lfun = getLfun();
528
529         dispatch(FuncRequest(lfun, arg));
530 }
531
532
533 FuncCode GuiLyXFiles::getLfun() const
534 {
535         if (type_ == "examples")
536                 return LFUN_FILE_OPEN;
537         else if (type_ == "templates")
538                 return LFUN_BUFFER_NEW_TEMPLATE;
539         return LFUN_NOACTION;
540 }
541
542 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
543
544
545 } // namespace frontend
546 } // namespace lyx
547
548 #include "moc_GuiLyXFiles.cpp"