]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiLyXFiles.cpp
Use <cstdint> instead of <boost/cstdint.hpp>
[lyx.git] / src / frontends / qt4 / GuiLyXFiles.cpp
index 35d86d222c49d2c90cd785210b5af2d9bccf9714..98c43419fdc29c3e4cfcb5e2e4bbcb4aceb6d907 100644 (file)
@@ -30,7 +30,6 @@
 #include "support/Package.h"
 
 #include <QDirIterator>
-#include <QFileIconProvider>
 #include <QTreeWidget>
 
 using namespace std;
@@ -39,72 +38,45 @@ using namespace lyx::support;
 namespace lyx {
 namespace frontend {
 
+namespace {
 
-void GuiLyXFiles::getFiles(QMap<QString, QString> & in, QString const type)
+QString const guiString(QString in)
 {
+       // recode specially encoded chars in file names (URL encoding and underbar)
+       return QString(QByteArray::fromPercentEncoding(in.toUtf8())).replace('_', ' ');
+}
+
+} // namespace anon
+
+
+QMap<QString, QString> GuiLyXFiles::getFiles()
+{
+       QMap<QString, QString> result;
        // We look for lyx files in the subdirectory dir of
        //   1) user_lyxdir
        //   2) build_lyxdir (if not empty)
        //   3) system_lyxdir
        // in this order. Files with a given sub-hierarchy will
        // only be listed once.
-       // We also consider i18n subdirectories and prefer them.
+       // We also consider i18n subdirectories and store them separately.
        QStringList dirs;
        QStringList relpaths;
-       QStringList langcodes;
 
        // The three locations to look at.
-       string const user = addPath(package().user_support().absFileName(), fromqstr(type));
-       string const build = addPath(package().build_support().absFileName(), fromqstr(type));
-       string const system = addPath(package().system_support().absFileName(), fromqstr(type));
-
-       // If the LANGUAGE variable is set, use it as a fallback for searching for files.
-       string lang = getGuiMessages().language();
-       string const language = getEnv("LANGUAGE");
-       if (!language.empty())
-               lang += ":" + language;
+       string const user = addPath(package().user_support().absFileName(), fromqstr(type_));
+       string const build = addPath(package().build_support().absFileName(), fromqstr(type_));
+       string const system = addPath(package().system_support().absFileName(), fromqstr(type_));
 
-       // Get all supported languages (by code) in order to exclude those
-       // dirs later.
-       QAbstractItemModel * language_model = guiApp->languageModel();
-       for (int i = 0; i != language_model->rowCount(); ++i) {
-               QModelIndex index = language_model->index(i, 0);
-               Language const * lang =
-                       languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
-               if (!lang)
-                       continue;
-               string const code = lang->code();
-               langcodes << toqstr(code);
-               // Also store code without country code
-               string const shortcode = token(code, '_', 0);
-               if (shortcode != code)
-                       langcodes << toqstr(shortcode);
-       }
+       available_languages_.insert(toqstr("en"), qt_("English"));
 
-       for (auto const & l : getVectorFromString(lang, ":")) {
-               FileName tmp;
-               // First try with the full name
-               // `en' files are not in a subdirectory
-               if (l == "en")
-                       break;
-               else {
-                       dirs << toqstr(addPath(user, l));
-                       dirs << toqstr(addPath(build, l));
-                       dirs << toqstr(addPath(system, l));
-               }
-               // Then the name without country code
-               string const shortl = token(l, '_', 0);
-               if (shortl != l) {
-                       dirs << toqstr(addPath(user, shortl));
-                       dirs << toqstr(addPath(build, shortl));
-                       dirs << toqstr(addPath(system, shortl));
-               }
-       }
+       QString const type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
 
-       // Next, search in the base path
-       dirs << toqstr(user)
-            << toqstr(build)
-            << toqstr(system);
+       // Search in the base paths
+       if (type == "all" || type == "user")
+               dirs << toqstr(user);
+       if (type == "all" || type == "system")
+               dirs << toqstr(build)
+                    << toqstr(system);
 
        for (int i = 0; i < dirs.size(); ++i) {
                QString const dir = dirs.at(i);
@@ -113,30 +85,75 @@ void GuiLyXFiles::getFiles(QMap<QString, QString> & in, QString const type)
                        QString fn(QFile(it.next()).fileName());
                        if (!fn.endsWith(getSuffix()))
                                continue;
-                       QString const relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
-                                                                  qstring_to_ucs4(dir)));
+                       QString relpath = toqstr(makeRelPath(qstring_to_ucs4(fn),
+                                                            qstring_to_ucs4(dir)));
                        // <cat>/
                        int s = relpath.indexOf('/', 0);
                        QString cat = qt_("General");
+                       QString localization = "en";
                        if (s != -1) {
                                // <cat>/<subcat>/
                                cat = relpath.left(s);
-                               int sc = relpath.indexOf('/', s + 1);
-                               QString const subcat = (sc == -1) ?
-                                                       QString() : relpath.mid(s + 1, sc - s - 1);
-                               if (langcodes.contains(cat)
-                                   && !langcodes.contains(dir.right(dir.lastIndexOf('/'))))
-                                       // Skip i18n dir
-                                       continue;
-                               if (!subcat.isEmpty())
-                                       cat += '/' + subcat;
+                               if (all_languages_.contains(cat)
+                                   && !all_languages_.contains(dir.right(dir.lastIndexOf('/')))) {
+                                       QMap<QString, QString>::const_iterator li = all_languages_.find(cat);
+                                       // Skip i18n dir, but add language to the combo
+                                       if (!available_languages_.contains(li.key()))
+                                               available_languages_.insert(li.key(), li.value());
+                                       localization = cat;
+                                       int sc = relpath.indexOf('/', s + 1);
+                                       cat = (sc == -1) ? qt_("General") : relpath.mid(s + 1, sc - s - 1);
+                                       s = sc;
+                               }
+                               if (s != -1) {
+                                       int sc = relpath.indexOf('/', s + 1);
+                                       QString const subcat = (sc == -1) ?
+                                                               QString() : relpath.mid(s + 1, sc - s - 1);
+                                       if (!subcat.isEmpty())
+                                               cat += '/' + subcat;
+                               }
                        }
                        if (!relpaths.contains(relpath)) {
                                relpaths.append(relpath);
-                               in.insert(fn, cat);
+                               if (localization != "en")
+                                       // strip off lang/
+                                       relpath = relpath.mid(relpath.indexOf('/') + 1);
+                               result.insert(relpath, cat);
+                                                                       
+                               QMap<QString, QString> lm;
+                               if (localizations_.contains(relpath))
+                                       lm = localizations_.find(relpath).value();
+                               lm.insert(localization, fn);
+                               localizations_.insert(relpath, lm);
                        }
                }
        }
+       // Find and store GUI language
+       for (auto const & l : guilangs_) {
+               // First try with the full name
+               // `en' files are not in a subdirectory
+               if (available_languages_.contains(toqstr(l))) {
+                       guilang_ = toqstr(l);
+                       break;
+               }
+               // Then the name without country code
+               string const shortl = token(l, '_', 0);
+               if (available_languages_.contains(toqstr(shortl))) {
+                       guilang_ = toqstr(shortl);
+                       break;
+               }
+       }
+       // pre-fill the language combo (it will be updated once an item 
+       // has been clicked)
+       languageCO->clear();
+       QMap<QString, QString>::const_iterator i =available_languages_.constBegin();
+       while (i != available_languages_.constEnd()) {
+               languageCO->addItem(i.value(), i.key());
+               ++i;
+       }
+       setLanguage();
+       languageLA->setText(qt_("Preferred &Language:"));
+       return result;
 }
 
 
@@ -145,6 +162,31 @@ GuiLyXFiles::GuiLyXFiles(GuiView & lv)
 {
        setupUi(this);
 
+       // Get all supported languages (by code) in order to exclude those
+       // dirs later.
+       QAbstractItemModel * language_model = guiApp->languageModel();
+       language_model->sort(0);
+       for (int i = 0; i != language_model->rowCount(); ++i) {
+               QModelIndex index = language_model->index(i, 0);
+               Language const * lang =
+                       languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
+               if (!lang)
+                       continue;
+               QString const code = toqstr(lang->code());
+               if (!all_languages_.contains(code))
+                       all_languages_.insert(code, qt_(lang->display()));
+               // Also store code without country code
+               QString const shortcode = code.left(code.indexOf('_'));
+               if (shortcode != code && !all_languages_.contains(shortcode))
+                       all_languages_.insert(shortcode, qt_(lang->display()));
+       }
+       // Get GUI language
+       string lang = getGuiMessages().language();
+       string const language = getEnv("LANGUAGE");
+       if (!language.empty())
+               lang += ":" + language;
+       guilangs_ =  getVectorFromString(lang, ":");
+
        // The filter bar
        filter_ = new FancyLineEdit(this);
        filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
@@ -183,8 +225,11 @@ GuiLyXFiles::GuiLyXFiles(GuiView & lv)
        //filesLW->setViewMode(QListView::ListMode);
        filesLW->setIconSize(QSize(22, 22));
 
-       fileTypeCO->addItem(qt_("Templates"), toqstr("templates"));
-       fileTypeCO->addItem(qt_("Examples"), toqstr("examples"));
+       QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
+       QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
+       fileTypeCO->addItem(qt_("User and System Files"), toqstr("all"));
+       fileTypeCO->addItem(user_icon, qt_("User Files Only"), toqstr("user"));
+       fileTypeCO->addItem(system_icon, qt_("System Files Only"), toqstr("system"));
 
        setFocusProxy(filter_);
 }
@@ -194,11 +239,19 @@ QString const GuiLyXFiles::getSuffix()
 {
        if (type_ == "bind" || type_ == "ui")
                return toqstr(".") + type_;
+       else if (type_ == "kbd")
+               return ".kmap";
        
        return ".lyx";
 }
 
 
+bool GuiLyXFiles::translateName() const
+{
+       return (type_ == "templates" || type_ == "examples");
+}
+
+
 void GuiLyXFiles::changed_adaptor()
 {
        changed();
@@ -211,27 +264,125 @@ void GuiLyXFiles::on_fileTypeCO_activated(int)
 }
 
 
-void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem *, int)
+void GuiLyXFiles::on_languageCO_activated(int i)
+{
+       savelang_ = languageCO->itemData(i).toString();
+       if (!filesLW->currentItem())
+               return;
+
+       filesLW->currentItem()->setData(0, Qt::ToolTipRole, getRealPath());
+       changed();
+}
+
+
+void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem * item, int)
 {
+       if (!item->data(0, Qt::UserRole).toString().endsWith(getSuffix()))
+               // not a file (probably a header)
+               return;
+
        applyView();
        dispatchParams();
        close();
 }
 
+void GuiLyXFiles::on_filesLW_itemClicked(QTreeWidgetItem * item, int)
+{
+       QString const data = item->data(0, Qt::UserRole).toString();
+       if (!data.endsWith(getSuffix()))
+               // not a file (probably a header)
+               return;
+
+       languageCO->clear();
+       QMap<QString, QString>::const_iterator i =available_languages_.constBegin();
+       while (i != available_languages_.constEnd()) {
+               if (localizations_.contains(data)
+                   && localizations_.find(data).value().contains(i.key()))
+                       languageCO->addItem(i.value(), i.key());
+               ++i;
+       }
+       languageLA->setText(qt_("File &Language:"));
+       languageCO->setToolTip(qt_("All available languages of the selected file are displayed here.\n"
+                                  "The selected language version will be opened."));
+       setLanguage();
+       QString const realpath = getRealPath();
+       filesLW->currentItem()->setData(0, Qt::ToolTipRole, realpath);
+       QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
+       QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
+       QIcon file_icon = (realpath.startsWith(toqstr(package().user_support().absFileName()))) ?
+                       user_icon : system_icon;
+       item->setIcon(0, file_icon);
+}
+
+
+void GuiLyXFiles::setLanguage()
+{
+       // Enable language selection only if there is a selection.
+       languageCO->setEnabled(languageCO->count() > 1);
+       languageLA->setEnabled(languageCO->count() > 1);
+       // first try last setting
+       if (!savelang_.isEmpty()) {
+               int index = languageCO->findData(savelang_);
+               if (index != -1) {
+                       languageCO->setCurrentIndex(index);
+                       return;
+               }
+       }
+       // next, try GUI lang
+       if (!guilang_.isEmpty()) {
+               int index = languageCO->findData(guilang_);
+               if (index != -1) {
+                       languageCO->setCurrentIndex(index);
+                       return;
+               }
+       }
+       // Finally, fall back to English (which should be always there)
+       int index = languageCO->findData(toqstr("en"));
+       if (index != -1) {
+               languageCO->setCurrentIndex(index);
+       }
+}
+
 
 void GuiLyXFiles::on_browsePB_pressed()
 {
-       bool const examples = (type_ == "examples");
-       FileDialog dlg(qt_("Select template file"));
-       dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path));
-       if (examples)
-               dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path));
-       else
-               dlg.setButton2(qt_("&Templates"), toqstr(lyxrc.template_path));
+       QString path1 = toqstr(lyxrc.document_path);
+       QString path2 = toqstr(lyxrc.example_path);
+       QString title = qt_("Select example file");
+       QString filter = qt_("LyX Documents (*.lyx)");
+       QString b1 = qt_("D&ocuments");
+       QString b2 = qt_("&Examples");
+
+       if (type_ == "templates") {
+               path2 = toqstr(lyxrc.template_path);
+               title = qt_("Select template file");
+               b1 = qt_("D&ocuments");
+               b2 = qt_("&Templates");
+       }
+       else if (type_ != "examples") {
+               path1 = toqstr(addName(package().user_support().absFileName(), fromqstr(type_)));
+               path2 = toqstr(addName(package().system_support().absFileName(), fromqstr(type_)));
+               b1 = qt_("&User files");
+               b2 = qt_("&System files");
+       }
+       if (type_ == "ui") {
+               title = qt_("Chose UI file");
+               filter = qt_("LyX UI Files (*.ui)");
+       }
+       if (type_ == "bind") {
+               title = qt_("Chose bind file");
+               filter = qt_("LyX Bind Files (*.bind)");
+       }
+       if (type_ == "kbd") {
+               title = qt_("Chose keyboard map");
+               filter = qt_("LyX Keymap Files (*.kmap)");
+       }
+
+       FileDialog dlg(title);
+       dlg.setButton1(b1, path1);
+       dlg.setButton2(b2, path2);
 
-       FileDialog::Result result = dlg.open(examples ? toqstr(lyxrc.example_path)
-                                                     : toqstr(lyxrc.template_path),
-                                QStringList(qt_("LyX Documents (*.lyx)")));
+       FileDialog::Result result = dlg.open(path2, QStringList(filter));
 
        if (result.first != FileDialog::Later && !result.second.isEmpty()) {
                file_ = toqstr(FileName(fromqstr(result.second)).absFileName());
@@ -243,18 +394,20 @@ void GuiLyXFiles::on_browsePB_pressed()
 
 void GuiLyXFiles::updateContents()
 {
-       QString type = fileTypeCO->itemData(fileTypeCO->currentIndex()).toString();
-       QMap<QString, QString> files;
-       getFiles(files, type);
+       languageCO->clear();
+       QMap<QString, QString> files = getFiles();
+       languageCO->model()->sort(0);
 
        filesLW->clear();
-       QFileIconProvider iconprovider;
+       QIcon user_icon(getPixmap("images/", "lyxfiles-user", "svgz,png"));
+       QIcon system_icon(getPixmap("images/", "lyxfiles-system", "svgz,png"));
        QStringList cats;
        QMap<QString, QString>::const_iterator it = files.constBegin();
        QFont capfont;
        capfont.setBold(true);
        while (it != files.constEnd()) {
                QFileInfo const info = QFileInfo(it.key());
+               QString const realpath = getRealPath(it.key());
                QString cat = it.value();
                QString subcat;
                QString catsave;
@@ -262,10 +415,9 @@ void GuiLyXFiles::updateContents()
                        catsave = cat;
                        cat = catsave.left(catsave.indexOf('/'));
                        subcat = toqstr(translateIfPossible(
-                                       qstring_to_ucs4(catsave.mid(
-                                               catsave.indexOf('/') + 1).replace('_', ' '))));
+                                       qstring_to_ucs4(guiString(catsave.mid(catsave.indexOf('/') + 1)))));
                }
-               cat =  toqstr(translateIfPossible(qstring_to_ucs4(cat.replace('_', ' '))));
+               cat =  toqstr(translateIfPossible(qstring_to_ucs4(guiString(cat))));
                QTreeWidgetItem * catItem = new QTreeWidgetItem();
                if (!cats.contains(cat)) {
                        catItem->setText(0, cat);
@@ -277,13 +429,18 @@ void GuiLyXFiles::updateContents()
                        catItem = filesLW->findItems(cat, Qt::MatchExactly).first();
                QTreeWidgetItem * item = new QTreeWidgetItem();
                QString const filename = info.fileName();
-               QString const guiname =
-                               toqstr(translateIfPossible(
-                                              qstring_to_ucs4(filename.left(filename.lastIndexOf(getSuffix())).replace('_', ' '))));
-               item->setIcon(0, iconprovider.icon(info));
-               item->setData(0, Qt::UserRole, info.filePath());
+               QString guiname = filename.left(filename.lastIndexOf(getSuffix())).replace('_', ' ');
+               // Special case: defaults.lyx
+               if (type_ == "templates" && guiname == "defaults")
+                       guiname = qt_("Default Template");
+               else if (translateName())
+                       guiname = toqstr(translateIfPossible(qstring_to_ucs4(guiString(guiname))));
+               QIcon file_icon = (realpath.startsWith(toqstr(package().user_support().absFileName()))) ?
+                               user_icon : system_icon;
+               item->setIcon(0, file_icon);
+               item->setData(0, Qt::UserRole, it.key());
                item->setData(0, Qt::DisplayRole, guiname);
-               item->setData(0, Qt::ToolTipRole, info.filePath());
+               item->setData(0, Qt::ToolTipRole, realpath);
                if (subcat.isEmpty())
                        catItem->addChild(item);
                else {
@@ -300,6 +457,7 @@ void GuiLyXFiles::updateContents()
                                }
                        } else {
                                subcatItem->setText(0, subcat);
+                               subcatItem->setIcon(0, file_icon);
                                cats << catsave;
                        }
                        subcatItem->addChild(item);
@@ -349,10 +507,26 @@ void GuiLyXFiles::resetFilter()
        filterLabels();
 }
 
+QString const GuiLyXFiles::getRealPath(QString relpath)
+{
+       if (relpath.isEmpty())
+               relpath = filesLW->currentItem()->data(0, Qt::UserRole).toString();
+       QString const language = languageCO->itemData(languageCO->currentIndex()).toString();
+       if (localizations_.contains(relpath)) {
+               if (localizations_.find(relpath).value().contains(language))
+                       return localizations_.find(relpath).value().find(language).value();
+               else if (localizations_.find(relpath).value().contains(guilang_))
+                       return localizations_.find(relpath).value().find(guilang_).value();
+               else if (localizations_.find(relpath).value().contains(toqstr("en")))
+                       return localizations_.find(relpath).value().find(toqstr("en")).value();
+       }
+       return QString();
+}
+
 
 void GuiLyXFiles::applyView()
 {
-       file_ = filesLW->currentItem()->data(0, Qt::UserRole).toString();
+       file_ = getRealPath();
 }
 
 
@@ -365,23 +539,39 @@ bool GuiLyXFiles::isValid()
 bool GuiLyXFiles::initialiseParams(string const & type)
 {
        type_ = type.empty() ? toqstr("templates") : toqstr(type);
-       paramsToDialog(type_);
+       paramsToDialog();
        return true;
 }
 
 
-void GuiLyXFiles::paramsToDialog(QString const & command)
+void GuiLyXFiles::passParams(string const & data)
 {
-       if (!command.isEmpty()) {
-               int i = fileTypeCO->findData(command);
-               if (i != -1)
-                       fileTypeCO->setCurrentIndex(i);
-       }
-       if (command == "examples")
+       initialiseParams(data);
+       updateContents();
+}
+
+
+void GuiLyXFiles::selectItem(QString const item)
+{
+       /* Using an intermediary variable flags is needed up to at least
+        * Qt 5.5 because of a subtle namespace issue. See:
+        *   https://stackoverflow.com/questions/10755058/qflags-enum-type-conversion-fails-all-of-a-sudden
+        * for details.*/
+       Qt::MatchFlags const flags(Qt::MatchExactly|Qt::MatchRecursive);
+       QList<QTreeWidgetItem *> twi = filesLW->findItems(item, flags);
+       if (!twi.isEmpty())
+               twi.first()->setSelected(true);
+}
+
+
+void GuiLyXFiles::paramsToDialog()
+{
+       if (type_ == "examples")
                setTitle(qt_("Open Example File"));
-       else {
+       else if (type_ == "templates")
                setTitle(qt_("New File From Template"));
-       }
+       else
+               setTitle(qt_("Open File"));
 
        bc().setValid(isValid());
 }
@@ -398,14 +588,21 @@ void GuiLyXFiles::dispatchParams()
        arg += fromqstr(file_);
        FuncCode const lfun = getLfun();
 
-       dispatch(FuncRequest(lfun, arg));
+       if (lfun == LFUN_NOACTION)
+               // emit signal
+               fileSelected(file_);
+       else
+               dispatch(FuncRequest(lfun, arg));
 }
 
+
 FuncCode GuiLyXFiles::getLfun() const
 {
-       if (type_ == toqstr("examples"))
+       if (type_ == "examples")
                return LFUN_FILE_OPEN;
-       return LFUN_BUFFER_NEW_TEMPLATE;
+       else if (type_ == "templates")
+               return LFUN_BUFFER_NEW_TEMPLATE;
+       return LFUN_NOACTION;
 }
 
 Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }