]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiLyXFiles.cpp
Compilation fix for Qt < 5.2.
[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 <QFileIconProvider>
34 #include <QTreeWidget>
35
36 using namespace std;
37 using namespace lyx::support;
38
39 namespace lyx {
40 namespace frontend {
41
42
43 void GuiLyXFiles::getFiles(QMap<QString, QString> & in, QString const type)
44 {
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 prefer them.
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         // First, query the current language subdir (except for English)
61 #if QT_VERSION >= 0x050200
62         QString const lang = languageCO->currentData().toString();
63 #else
64         QString const lang = languageCO->currentText();
65 #endif
66         if (!lang.startsWith("en")) {
67                 // First try with the full code
68                 dirs << toqstr(addPath(user, fromqstr(lang)));
69                 dirs << toqstr(addPath(build, fromqstr(lang)));
70                 dirs << toqstr(addPath(system, fromqstr(lang)));
71                 // Then the name without country code
72                 QString const shortl = lang.left(lang.indexOf('_'));
73                 if (shortl != lang) {
74                         dirs << toqstr(addPath(user, fromqstr(shortl)));
75                         dirs << toqstr(addPath(build, fromqstr(shortl)));
76                         dirs << toqstr(addPath(system, fromqstr(shortl)));
77                 }
78         }
79
80         // Next, search in the base path
81         dirs << toqstr(user)
82              << toqstr(build)
83              << toqstr(system);
84
85         for (int i = 0; i < dirs.size(); ++i) {
86                 QString const dir = dirs.at(i);
87                 QDirIterator it(dir, QDir::Files, QDirIterator::Subdirectories);
88                 while (it.hasNext()) {
89                         QString fn(QFile(it.next()).fileName());
90                         if (!fn.endsWith(getSuffix()))
91                                 continue;
92                         QString const relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
93                                                                    qstring_to_ucs4(dir)));
94                         // <cat>/
95                         int s = relpath.indexOf('/', 0);
96                         QString cat = qt_("General");
97                         if (s != -1) {
98                                 // <cat>/<subcat>/
99                                 cat = relpath.left(s);
100                                 int sc = relpath.indexOf('/', s + 1);
101                                 QString const subcat = (sc == -1) ?
102                                                         QString() : relpath.mid(s + 1, sc - s - 1);
103                                 if (langcodes_.contains(cat)
104                                     && !langcodes_.contains(dir.right(dir.lastIndexOf('/'))))
105                                         // Skip i18n dir
106                                         continue;
107                                 if (!subcat.isEmpty())
108                                         cat += '/' + subcat;
109                         }
110                         if (!relpaths.contains(relpath)) {
111                                 relpaths.append(relpath);
112                                 in.insert(fn, cat);
113                         }
114                 }
115         }
116 }
117
118
119 GuiLyXFiles::GuiLyXFiles(GuiView & lv)
120         : GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
121 {
122         setupUi(this);
123
124         // Get all supported languages (by code) in order to exclude those
125         // dirs later.
126         QAbstractItemModel * language_model = guiApp->languageModel();
127         language_model->sort(0);
128         for (int i = 0; i != language_model->rowCount(); ++i) {
129                 QModelIndex index = language_model->index(i, 0);
130                 Language const * lang =
131                         languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
132                 if (!lang)
133                         continue;
134                 string const code = lang->code();
135                 languageCO->addItem(qt_(lang->display()), toqstr(code));
136                 langcodes_ << toqstr(code);
137                 // Also store code without country code
138                 string const shortcode = token(code, '_', 0);
139                 if (shortcode != code)
140                         langcodes_ << toqstr(shortcode);
141         }
142         // Preset to GUI language
143         string lang = getGuiMessages().language();
144         string const language = getEnv("LANGUAGE");
145         if (!language.empty())
146                 lang += ":" + language;
147
148         for (auto const & l : getVectorFromString(lang, ":")) {
149                 // First try with the full name
150                 // `en' files are not in a subdirectory
151                 int i = languageCO->findData(toqstr(l));
152                 if (i != -1) {
153                         languageCO->setCurrentIndex(i);
154                         break;
155                 }
156                 // Then the name without country code
157                 string const shortl = token(l, '_', 0);
158                 i = languageCO->findData(toqstr(l));
159                 if (i != -1) {
160                         languageCO->setCurrentIndex(i);
161                         break;
162                 }
163         }
164
165         // The filter bar
166         filter_ = new FancyLineEdit(this);
167         filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
168         filter_->setButtonVisible(FancyLineEdit::Right, true);
169         filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
170         filter_->setAutoHideButton(FancyLineEdit::Right, true);
171         filter_->setPlaceholderText(qt_("All available files"));
172         filter_->setToolTip(qt_("Enter string to filter the list of available files"));
173 #if (QT_VERSION < 0x050000)
174         connect(filter_, SIGNAL(downPressed()),
175                 filesLW, SLOT(setFocus()));
176 #else
177         connect(filter_, &FancyLineEdit::downPressed,
178                 filesLW, [=](){ focusAndHighlight(filesLW); });
179 #endif
180
181         filterBarL->addWidget(filter_, 0);
182         findKeysLA->setBuddy(filter_);
183
184         connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
185                 this, SLOT(slotButtonBox(QAbstractButton *)));
186
187         connect(filesLW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
188                 this, SLOT(changed_adaptor()));
189         connect(filesLW, SIGNAL(itemSelectionChanged()),
190                 this, SLOT(changed_adaptor()));
191         connect(filter_, SIGNAL(textEdited(QString)),
192                 this, SLOT(filterLabels()));
193         connect(filter_, SIGNAL(rightButtonClicked()),
194                 this, SLOT(resetFilter()));
195
196         bc().setPolicy(ButtonPolicy::OkApplyCancelPolicy);
197         bc().setOK(buttonBox->button(QDialogButtonBox::Open));
198         bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
199
200         //filesLW->setViewMode(QListView::ListMode);
201         filesLW->setIconSize(QSize(22, 22));
202
203         setFocusProxy(filter_);
204 }
205
206
207 QString const GuiLyXFiles::getSuffix()
208 {
209         if (type_ == "bind" || type_ == "ui")
210                 return toqstr(".") + type_;
211         
212         return ".lyx";
213 }
214
215
216 bool GuiLyXFiles::translateName() const
217 {
218         return (type_ == "templates" || type_ == "examples");
219 }
220
221
222 void GuiLyXFiles::changed_adaptor()
223 {
224         changed();
225 }
226
227
228 void GuiLyXFiles::on_fileTypeCO_activated(int)
229 {
230         updateContents();
231 }
232
233
234 void GuiLyXFiles::on_languageCO_activated(int)
235 {
236         updateContents();
237 }
238
239
240 void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem *, int)
241 {
242         applyView();
243         dispatchParams();
244         close();
245 }
246
247
248 void GuiLyXFiles::on_browsePB_pressed()
249 {
250         bool const examples = (type_ == "examples");
251         FileDialog dlg(qt_("Select template file"));
252         dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
253         if (examples)
254                 dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
255         else
256                 dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
257
258         FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
259                                                       : toqstr(lyxrc.template_path),
260                                  QStringList(qt_("LyX Documents (*.lyx)")));
261
262         if (result.first != FileDialog::Later && !result.second.isEmpty()) {
263                 file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
264                 dispatchParams();
265                 close();
266         }
267 }
268
269
270 void GuiLyXFiles::updateContents()
271 {
272         QString type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
273         QMap<QString, QString> files;
274         getFiles(files, type);
275
276         filesLW->clear();
277         QFileIconProvider iconprovider;
278         QStringList cats;
279         QMap<QString, QString>::const_iterator it = files.constBegin();
280         QFont capfont;
281         capfont.setBold(true);
282         while (it != files.constEnd()) {
283                 QFileInfo const info = QFileInfo(it.key());
284                 QString cat = it.value();
285                 QString subcat;
286                 QString catsave;
287                 if (cat.contains('/')) {
288                         catsave = cat;
289                         cat = catsave.left(catsave.indexOf('/'));
290                         subcat = toqstr(translateIfPossible(
291                                         qstring_to_ucs4(catsave.mid(
292                                                 catsave.indexOf('/') + 1).replace('_', ' '))));
293                 }
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);
301                         cats << cat;
302                 } else
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('_', ' ');
307                 if (translateName())
308                         guiname = toqstr(translateIfPossible(qstring_to_ucs4(guiname)));
309                 item->setIcon(0, iconprovider.icon(info));
310                 item->setData(0, Qt::UserRole, info.filePath());
311                 item->setData(0, Qt::DisplayRole, guiname);
312                 item->setData(0, Qt::ToolTipRole, info.filePath());
313                 if (subcat.isEmpty())
314                         catItem->addChild(item);
315                 else {
316                         QTreeWidgetItem * subcatItem = new QTreeWidgetItem();
317                         if (cats.contains(catsave)) {
318                                 QList<QTreeWidgetItem *> pcats = filesLW->findItems(cat, Qt::MatchExactly);
319                                 for (int iit = 0; iit < pcats.size(); ++iit) {
320                                         for (int cit = 0; cit < pcats.at(iit)->childCount(); ++cit) {
321                                                 if (pcats.at(iit)->child(cit)->text(0) == subcat) {
322                                                         subcatItem = pcats.at(iit)->child(cit);
323                                                         break;
324                                                 }
325                                         }
326                                 }
327                         } else {
328                                 subcatItem->setText(0, subcat);
329                                 cats << catsave;
330                         }
331                         subcatItem->addChild(item);
332                         catItem->addChild(subcatItem);
333                 }
334                 ++it;
335         }
336         filesLW->sortItems(0, Qt::AscendingOrder);
337         // redo filter
338         filterLabels();
339 }
340
341
342 void GuiLyXFiles::slotButtonBox(QAbstractButton * button)
343 {
344         switch (buttonBox->standardButton(button)) {
345         case QDialogButtonBox::Open:
346                 slotOK();
347                 break;
348         case QDialogButtonBox::Cancel:
349                 slotClose();
350                 break;
351         default:
352                 break;
353         }
354 }
355
356
357 void GuiLyXFiles::filterLabels()
358 {
359         Qt::CaseSensitivity cs = csFindCB->isChecked() ?
360                 Qt::CaseSensitive : Qt::CaseInsensitive;
361         QTreeWidgetItemIterator it(filesLW);
362         while (*it) {
363                 (*it)->setHidden(
364                         (*it)->childCount() == 0
365                         && !(*it)->text(0).contains(filter_->text(), cs)
366                 );
367                 ++it;
368         }
369 }
370
371
372 void GuiLyXFiles::resetFilter()
373 {
374         filter_->setText(QString());
375         filterLabels();
376 }
377
378
379 void GuiLyXFiles::applyView()
380 {
381         file_ = filesLW->currentItem()->data(0, Qt::UserRole).toString();
382 }
383
384
385 bool GuiLyXFiles::isValid()
386 {
387         return filesLW->currentItem() && filesLW->currentItem()->isSelected();
388 }
389
390
391 bool GuiLyXFiles::initialiseParams(string const & type)
392 {
393         type_ = type.empty() ? toqstr("templates") : toqstr(type);
394         paramsToDialog();
395         return true;
396 }
397
398
399 void GuiLyXFiles::paramsToDialog()
400 {
401         fileTypeCO->clear();
402         if (type_ == "examples" || type_ == "templates") {
403                 fileTypeCO->addItem(qt_("Templates"), toqstr("templates"));
404                 fileTypeCO->addItem(qt_("Examples"), toqstr("examples"));
405         } else if (type_ == "ui")
406                 fileTypeCO->addItem(qt_("User Interface Files"), toqstr("ui"));
407         else if (type_ == "bind")
408                 fileTypeCO->addItem(qt_("Key Binding Files"), toqstr("bind"));
409
410         if (!type_.isEmpty()) {
411                 int i = fileTypeCO->findData(type_);
412                 if (i != -1)
413                         fileTypeCO->setCurrentIndex(i);
414         }
415         if (type_ == "examples")
416                 setTitle(qt_("Open Example File"));
417         else if (type_ == "templates")
418                 setTitle(qt_("New File From Template"));
419         else
420                 setTitle(qt_("Open File"));
421
422         bc().setValid(isValid());
423 }
424
425
426 void GuiLyXFiles::dispatchParams()
427 {
428         if (file_.isEmpty())
429                 return;
430
431         string arg;
432         if (type_ == "templates")
433                 arg = "newfile ";
434         arg += fromqstr(file_);
435         FuncCode const lfun = getLfun();
436
437         dispatch(FuncRequest(lfun, arg));
438 }
439
440
441 FuncCode GuiLyXFiles::getLfun() const
442 {
443         if (type_ == "examples")
444                 return LFUN_FILE_OPEN;
445         else if (type_ == "templates")
446                 return LFUN_BUFFER_NEW_TEMPLATE;
447         return LFUN_NOACTION;
448 }
449
450 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }
451
452
453 } // namespace frontend
454 } // namespace lyx
455
456 #include "moc_GuiLyXFiles.cpp"