]> 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 95b5ef217142550f8fe42752dfb380bce850c7b4..98c43419fdc29c3e4cfcb5e2e4bbcb4aceb6d907 100644 (file)
@@ -30,7 +30,6 @@
 #include "support/Package.h"
 
 #include <QDirIterator>
-#include <QFileIconProvider>
 #include <QTreeWidget>
 
 using namespace std;
@@ -39,44 +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;
 
        // 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));
-
-       // First, query the current language subdir (except for English)
-       QString const lang = languageCO->itemData(languageCO->currentIndex()).toString();
-       if (!lang.startsWith("en")) {
-               // First try with the full code
-               dirs << toqstr(addPath(user, fromqstr(lang)));
-               dirs << toqstr(addPath(build, fromqstr(lang)));
-               dirs << toqstr(addPath(system, fromqstr(lang)));
-               // Then the name without country code
-               QString const shortl = lang.left(lang.indexOf('_'));
-               if (shortl != lang) {
-                       dirs << toqstr(addPath(user, fromqstr(shortl)));
-                       dirs << toqstr(addPath(build, fromqstr(shortl)));
-                       dirs << toqstr(addPath(system, fromqstr(shortl)));
-               }
-       }
+       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_));
+
+       available_languages_.insert(toqstr("en"), qt_("English"));
+
+       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);
@@ -85,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;
 }
 
 
@@ -127,36 +172,20 @@ GuiLyXFiles::GuiLyXFiles(GuiView & lv)
                        languages.getLanguage(fromqstr(index.data(Qt::UserRole).toString()));
                if (!lang)
                        continue;
-               string const code = lang->code();
-               languageCO->addItem(qt_(lang->display()), toqstr(code));
-               langcodes_ << toqstr(code);
+               QString const code = toqstr(lang->code());
+               if (!all_languages_.contains(code))
+                       all_languages_.insert(code, qt_(lang->display()));
                // Also store code without country code
-               string const shortcode = token(code, '_', 0);
-               if (shortcode != code)
-                       langcodes_ << toqstr(shortcode);
+               QString const shortcode = code.left(code.indexOf('_'));
+               if (shortcode != code && !all_languages_.contains(shortcode))
+                       all_languages_.insert(shortcode, qt_(lang->display()));
        }
-       // Preset to GUI language
+       // Get GUI language
        string lang = getGuiMessages().language();
        string const language = getEnv("LANGUAGE");
        if (!language.empty())
                lang += ":" + language;
-
-       for (auto const & l : getVectorFromString(lang, ":")) {
-               // First try with the full name
-               // `en' files are not in a subdirectory
-               int i = languageCO->findData(toqstr(l));
-               if (i != -1) {
-                       languageCO->setCurrentIndex(i);
-                       break;
-               }
-               // Then the name without country code
-               string const shortl = token(l, '_', 0);
-               i = languageCO->findData(toqstr(l));
-               if (i != -1) {
-                       languageCO->setCurrentIndex(i);
-                       break;
-               }
-       }
+       guilangs_ =  getVectorFromString(lang, ":");
 
        // The filter bar
        filter_ = new FancyLineEdit(this);
@@ -196,6 +225,12 @@ GuiLyXFiles::GuiLyXFiles(GuiView & lv)
        //filesLW->setViewMode(QListView::ListMode);
        filesLW->setIconSize(QSize(22, 22));
 
+       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_);
 }
 
@@ -204,6 +239,8 @@ QString const GuiLyXFiles::getSuffix()
 {
        if (type_ == "bind" || type_ == "ui")
                return toqstr(".") + type_;
+       else if (type_ == "kbd")
+               return ".kmap";
        
        return ".lyx";
 }
@@ -227,9 +264,14 @@ void GuiLyXFiles::on_fileTypeCO_activated(int)
 }
 
 
-void GuiLyXFiles::on_languageCO_activated(int)
+void GuiLyXFiles::on_languageCO_activated(int i)
 {
-       updateContents();
+       savelang_ = languageCO->itemData(i).toString();
+       if (!filesLW->currentItem())
+               return;
+
+       filesLW->currentItem()->setData(0, Qt::ToolTipRole, getRealPath());
+       changed();
 }
 
 
@@ -244,20 +286,103 @@ void GuiLyXFiles::on_filesLW_itemDoubleClicked(QTreeWidgetItem * item, int)
        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());
@@ -269,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;
@@ -288,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);
@@ -304,12 +430,17 @@ void GuiLyXFiles::updateContents()
                QTreeWidgetItem * item = new QTreeWidgetItem();
                QString const filename = info.fileName();
                QString guiname = filename.left(filename.lastIndexOf(getSuffix())).replace('_', ' ');
-               if (translateName())
-                       guiname = toqstr(translateIfPossible(qstring_to_ucs4(guiname)));
-               item->setIcon(0, iconprovider.icon(info));
-               item->setData(0, Qt::UserRole, info.filePath());
+               // 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 {
@@ -326,6 +457,7 @@ void GuiLyXFiles::updateContents()
                                }
                        } else {
                                subcatItem->setText(0, subcat);
+                               subcatItem->setIcon(0, file_icon);
                                cats << catsave;
                        }
                        subcatItem->addChild(item);
@@ -375,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();
 }
 
 
@@ -396,22 +544,28 @@ bool GuiLyXFiles::initialiseParams(string const & type)
 }
 
 
+void GuiLyXFiles::passParams(string const & data)
+{
+       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()
 {
-       fileTypeCO->clear();
-       if (type_ == "examples" || type_ == "templates") {
-               fileTypeCO->addItem(qt_("Templates"), toqstr("templates"));
-               fileTypeCO->addItem(qt_("Examples"), toqstr("examples"));
-       } else if (type_ == "ui")
-               fileTypeCO->addItem(qt_("User Interface Files"), toqstr("ui"));
-       else if (type_ == "bind")
-               fileTypeCO->addItem(qt_("Key Binding Files"), toqstr("bind"));
-
-       if (!type_.isEmpty()) {
-               int i = fileTypeCO->findData(type_);
-               if (i != -1)
-                       fileTypeCO->setCurrentIndex(i);
-       }
        if (type_ == "examples")
                setTitle(qt_("Open Example File"));
        else if (type_ == "templates")
@@ -434,7 +588,11 @@ 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));
 }