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