#include "support/Package.h"
#include <QDirIterator>
-#include <QFileIconProvider>
#include <QTreeWidget>
using namespace std;
namespace {
-void 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));
+ 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;
-
- // 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);
QDirIterator it(dir, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString fn(QFile(it.next()).fileName());
- if (!fn.endsWith(".lyx"))
+ 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;
}
-}// namespace anon
-
GuiLyXFiles::GuiLyXFiles(GuiView & lv)
: GuiDialog(lv, "lyxfiles", qt_("New File From Template"))
{
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"));
//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_);
}
+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();
}
-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());
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;
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);
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(".lyx")).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 {
}
} else {
subcatItem->setText(0, subcat);
+ subcatItem->setIcon(0, file_icon);
cats << catsave;
}
subcatItem->addChild(item);
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();
}
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());
}
if (type_ == "templates")
arg = "newfile ";
arg += fromqstr(file_);
- FuncCode const lfun = (type_ == toqstr("examples")) ?
- LFUN_FILE_OPEN : getLfun();
+ 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_ == "examples")
+ return LFUN_FILE_OPEN;
+ else if (type_ == "templates")
+ return LFUN_BUFFER_NEW_TEMPLATE;
+ return LFUN_NOACTION;
}
Dialog * createGuiLyXFiles(GuiView & lv) { return new GuiLyXFiles(lv); }