X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2Fqt_helpers.cpp;h=7f4c5d8fda1f8e1cccb18cc18553b97d3d58643c;hb=ee7dd4a11ea21851e7e32164c66b37d3bc8ac31d;hp=7fe70acea35f00c4826bb9293cb04361fd63d57e;hpb=8d06b3049b268a6002b803f458948108a3d6e94b;p=lyx.git diff --git a/src/frontends/qt4/qt_helpers.cpp b/src/frontends/qt4/qt_helpers.cpp index 7fe70acea3..7f4c5d8fda 100644 --- a/src/frontends/qt4/qt_helpers.cpp +++ b/src/frontends/qt4/qt_helpers.cpp @@ -4,7 +4,7 @@ * Licence details can be found in the file COPYING. * * \author Dekel Tsur - * \author Jürgen Spitzmüller + * \author Jürgen Spitzmüller * \author Richard Heck * * Full author contact details are available in file CREDITS. @@ -14,17 +14,22 @@ #include "qt_helpers.h" +#include "FileDialog.h" #include "LengthCombo.h" -#include "debug.h" -#include "gettext.h" +#include "frontends/alert.h" + +#include "BufferParams.h" +#include "FloatList.h" #include "Language.h" #include "Length.h" +#include "TextClass.h" -#include "frontends/FileDialog.h" -#include "frontends/alert.h" - +#include "support/convert.h" +#include "support/debug.h" #include "support/filetools.h" +#include "support/foreach.h" +#include "support/gettext.h" #include "support/lstrings.h" #include "support/lyxalgo.h" #include "support/os.h" @@ -32,40 +37,34 @@ #include "support/Path.h" #include "support/Systemcall.h" -#include #include -#include +#include #include - -#include +#include +#include +#include #include #include +#include + +// for FileFilter. +// FIXME: Remove +#include "support/regex.h" +#include + -using std::string; -using std::vector; -using std::endl; +using namespace std; +using namespace lyx::support; namespace lyx { -using support::addName; -using support::bformat; -using support::FileFilterList; -using support::FileName; -using support::getExtension; -using support::getVectorFromString; -using support::libFileSearch; -using support::makeAbsPath; -using support::makeRelPath; -using support::onlyFilename; -using support::onlyPath; -using support::package; -using support::prefixIs; -using support::quoteName; -using support::removeExtension; -using support::Systemcall; -using support::token; -using support::isStrDbl; +FileName libFileSearch(QString const & dir, QString const & name, + QString const & ext) +{ + return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext)); +} + namespace frontend { @@ -81,7 +80,7 @@ string widgetsToLength(QLineEdit const * input, LengthCombo const * combo) Length::UNIT const unit = combo->currentLengthItem(); - return Length(length.toDouble(), unit).asString(); + return Length(length.trimmed().toDouble(), unit).asString(); } @@ -95,9 +94,16 @@ Length widgetsToLength(QLineEdit const * input, QComboBox const * combo) if (isValidGlueLength(fromqstr(length))) return Length(fromqstr(length)); - Length::UNIT const unit = unitFromString(fromqstr(combo->currentText())); + Length::UNIT unit = Length::UNIT_NONE; + QString const item = combo->currentText(); + for (int i = 0; i < num_units; i++) { + if (qt_(lyx::unit_name_gui[i]) == item) { + unit = unitFromString(unit_name[i]); + break; + } + } - return Length(length.toDouble(), unit); + return Length(length.trimmed().toDouble(), unit); } @@ -105,7 +111,9 @@ void lengthToWidgets(QLineEdit * input, LengthCombo * combo, Length const & len, Length::UNIT /*defaultUnit*/) { combo->setCurrentItem(len.unit()); - input->setText(QString::number(Length(len).value())); + QLocale loc; + loc.setNumberOptions(QLocale::OmitGroupSeparator); + input->setText(loc.toString(Length(len).value())); } @@ -126,13 +134,44 @@ void lengthToWidgets(QLineEdit * input, LengthCombo * combo, } -void lengthAutoToWidgets(QLineEdit * input, LengthCombo * combo, - Length const & len, Length::UNIT defaultUnit) +void lengthToWidgets(QLineEdit * input, LengthCombo * combo, + docstring const & len, Length::UNIT defaultUnit) +{ + lengthToWidgets(input, combo, to_utf8(len), defaultUnit); +} + + +double widgetToDouble(QLineEdit const * input) +{ + QString const text = input->text(); + if (text.isEmpty()) + return 0.0; + + return text.trimmed().toDouble(); +} + + +string widgetToDoubleStr(QLineEdit const * input) +{ + QString const text = input->text(); + if (text.isEmpty()) + return string(); + + return convert(text.trimmed().toDouble()); +} + + +void doubleToWidget(QLineEdit * input, double const & value, char f, int prec) { - if (len.value() == 0) - lengthToWidgets(input, combo, "auto", defaultUnit); - else - lengthToWidgets(input, combo, len, defaultUnit); + QLocale loc; + loc.setNumberOptions(QLocale::OmitGroupSeparator); + input->setText(loc.toString(value, f, prec)); +} + + +void doubleToWidget(QLineEdit * input, string const & value, char f, int prec) +{ + doubleToWidget(input, convert(value), f, prec); } @@ -160,183 +199,377 @@ QString const qt_(string const & str) return toqstr(_(str)); } -namespace { -struct Sorter +void rescanTexStyles() +{ + // Run rescan in user lyx directory + PathChanger p(package().user_support()); + FileName const command = support::libFileSearch("scripts", "TeXFiles.py"); + Systemcall one; + int const status = one.startscript(Systemcall::Wait, + os::python() + ' ' + + quoteName(command.toFilesystemEncoding())); + if (status == 0) + return; + // FIXME UNICODE + frontend::Alert::error(_("Could not update TeX information"), + bformat(_("The script `%1$s' failed."), from_utf8(command.absFileName()))); +} + + +QStringList texFileList(QString const & filename) { - bool operator()(LanguagePair const & lhs, LanguagePair const & rhs) const { - return lhs.first < rhs.first; + QStringList list; + FileName const file = libFileSearch(QString(), filename); + if (file.empty()) + return list; + + // FIXME Unicode. + vector doclist = + getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n")); + + // Normalise paths like /foo//bar ==> /foo/bar + QSet set; + for (size_t i = 0; i != doclist.size(); ++i) { + QString file = toqstr(doclist[i]); + file.replace("\r", ""); + while (file.contains("//")) + file.replace("//", "/"); + if (!file.isEmpty()) + set.insert(file); } -}; + // remove duplicates + return QList::fromSet(set); +} -} // namespace anon +QString const externalLineEnding(docstring const & str) +{ +#ifdef Q_WS_MACX + // The MAC clipboard uses \r for lineendings, and we use \n + return toqstr(subst(str, '\n', '\r')); +#elif defined(Q_WS_WIN) + // Windows clipboard uses \r\n for lineendings, and we use \n + return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n"))); +#else + return toqstr(str); +#endif +} -vector const getLanguageData(bool character_dlg) +docstring const internalLineEnding(QString const & str) { - vector::size_type const size = character_dlg ? - languages.size() + 2 : languages.size(); + docstring const s = subst(qstring_to_ucs4(str), + from_ascii("\r\n"), from_ascii("\n")); + return subst(s, '\r', '\n'); +} - vector langs(size); - if (character_dlg) { - langs[0].first = _("No change"); - langs[0].second = "ignore"; - langs[1].first = _("Reset"); - langs[1].second = "reset"; - } +QString internalPath(const QString & str) +{ + return toqstr(os::internal_path(fromqstr(str))); +} - vector::size_type i = character_dlg ? 2 : 0; - for (Languages::const_iterator cit = languages.begin(); - cit != languages.end(); ++cit) { - langs[i].first = _(cit->second.display()); - langs[i].second = cit->second.lang(); - ++i; - } - // Don't sort "ignore" and "reset" - vector::iterator begin = character_dlg ? - langs.begin() + 2 : langs.begin(); +QString onlyFileName(const QString & str) +{ + return toqstr(support::onlyFileName(fromqstr(str))); +} - std::sort(begin, langs.end(), Sorter()); - return langs; +QString onlyPath(const QString & str) +{ + return toqstr(support::onlyPath(fromqstr(str))); } -docstring browseFile(docstring const & filename, docstring const & title, - FileFilterList const & filters, bool save, - docstring const & label1, docstring const & dir1, - docstring const & label2, docstring const & dir2) +QString changeExtension(QString const & oldname, QString const & ext) +{ + return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext))); +} + +/// Remove the extension from \p name +QString removeExtension(QString const & name) { - docstring lastPath = from_ascii("."); - if (!filename.empty()) - lastPath = from_utf8(onlyPath(to_utf8(filename))); + return toqstr(support::removeExtension(fromqstr(name))); +} - FileDialog dlg(title, LFUN_SELECT_FILE_SYNC); - dlg.setButton1(label1, dir1); - dlg.setButton2(label2, dir2); +/** Add the extension \p ext to \p name. + Use this instead of changeExtension if you know that \p name is without + extension, because changeExtension would wrongly interpret \p name if it + contains a dot. + */ +QString addExtension(QString const & name, QString const & ext) +{ + return toqstr(support::addExtension(fromqstr(name), fromqstr(ext))); +} - FileDialog::Result result; +/// Return the extension of the file (not including the .) +QString getExtension(QString const & name) +{ + return toqstr(support::getExtension(fromqstr(name))); +} - if (save) - result = dlg.save(lastPath, filters, - from_utf8(onlyFilename(to_utf8(filename)))); - else - result = dlg.open(lastPath, filters, - from_utf8(onlyFilename(to_utf8(filename)))); - return result.second; +/** Convert relative path into absolute path based on a basepath. + If relpath is absolute, just use that. + If basepath doesn't exist use CWD. + */ +QString makeAbsPath(QString const & relpath, QString const & base) +{ + return toqstr(support::makeAbsPath(fromqstr(relpath), + fromqstr(base)).absFileName()); } -docstring browseRelFile(docstring const & filename, docstring const & refpath, - docstring const & title, FileFilterList const & filters, bool save, - docstring const & label1, docstring const & dir1, - docstring const & label2, docstring const & dir2) +///////////////////////////////////////////////////////////////////////// +// +// FileFilterList +// +///////////////////////////////////////////////////////////////////////// + +/** Given a string such as + * " ... *.{abc,def} ", + * convert the csh-style brace expresions: + * " ... *.abc *.def ". + * Requires no system support, so should work equally on Unix, Mac, Win32. + */ +static string const convert_brace_glob(string const & glob) { - docstring const fname = from_utf8(makeAbsPath( - to_utf8(filename), to_utf8(refpath)).absFilename()); + // Matches " *.{abc,def,ghi}", storing "*." as group 1 and + // "abc,def,ghi" as group 2. + static lyx::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}"); + // Matches "abc" and "abc,", storing "abc" as group 1. + static lyx::regex const block_re("([^,}]+),?"); + + string pattern; + + string::const_iterator it = glob.begin(); + string::const_iterator const end = glob.end(); + while (true) { + match_results what; + if (!regex_search(it, end, what, glob_re)) { + // Ensure that no information is lost. + pattern += string(it, end); + break; + } + + // Everything from the start of the input to + // the start of the match. + pattern += string(what[-1].first, what[-1].second); + + // Given " *.{abc,def}", head == "*." and tail == "abc,def". + string const head = string(what[1].first, what[1].second); + string const tail = string(what[2].first, what[2].second); + + // Split the ','-separated chunks of tail so that + // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2". + string const fmt = " " + head + "$1"; + pattern += regex_replace(tail, block_re, fmt); + + // Increment the iterator to the end of the match. + it += distance(it, what[0].second); + } - docstring const outname = browseFile(fname, title, filters, save, - label1, dir1, label2, dir2); - docstring const reloutname = makeRelPath(outname, refpath); - if (prefixIs(reloutname, from_ascii("../"))) - return outname; - else - return reloutname; + return pattern; } -docstring browseLibFile(docstring const & dir, docstring const & name, - docstring const & ext, docstring const & title, - FileFilterList const & filters) +struct Filter { - // FIXME UNICODE - docstring const label1 = _("System files|#S#s"); - docstring const dir1 = - from_utf8(addName(package().system_support().absFilename(), to_utf8(dir))); + /* \param description text describing the filters. + * \param one or more wildcard patterns, separated by + * whitespace. + */ + Filter(docstring const & description, std::string const & globs); + + docstring const & description() const { return desc_; } - docstring const label2 = _("User files|#U#u"); - docstring const dir2 = - from_utf8(addName(package().user_support().absFilename(), to_utf8(dir))); + QString toString() const; - docstring const result = browseFile(from_utf8( - libFileSearch(to_utf8(dir), to_utf8(name), to_utf8(ext)).absFilename()), - title, filters, false, dir1, dir2); + docstring desc_; + std::vector globs_; +}; + + +Filter::Filter(docstring const & description, string const & globs) + : desc_(description) +{ + typedef boost::tokenizer > Tokenizer; + boost::char_separator const separator(" "); - // remove the extension if it is the default one - docstring noextresult; - if (from_utf8(getExtension(to_utf8(result))) == ext) - noextresult = from_utf8(removeExtension(to_utf8(result))); - else - noextresult = result; + // Given " ... *.{abc,def} ", expand to + // " ... *.abc *.def " + string const expanded_globs = convert_brace_glob(globs); - // remove the directory, if it is the default one - docstring const file = from_utf8(onlyFilename(to_utf8(noextresult))); - if (from_utf8(libFileSearch(to_utf8(dir), to_utf8(file), to_utf8(ext)).absFilename()) == result) - return file; - else - return noextresult; + // Split into individual globs. + vector matches; + Tokenizer const tokens(expanded_globs, separator); + globs_ = vector(tokens.begin(), tokens.end()); } -docstring browseDir(docstring const & pathname, docstring const & title, - docstring const & label1, docstring const & dir1, - docstring const & label2, docstring const & dir2) +QString Filter::toString() const { - docstring lastPath = from_ascii("."); - if (!pathname.empty()) - lastPath = from_utf8(onlyPath(to_utf8(pathname))); + QString s; - FileDialog dlg(title, LFUN_SELECT_FILE_SYNC); - dlg.setButton1(label1, dir1); - dlg.setButton2(label2, dir2); + bool const has_description = desc_.empty(); - FileDialog::Result const result = - dlg.opendir(lastPath, from_utf8(onlyFilename(to_utf8(pathname)))); + if (has_description) { + s += toqstr(desc_); + s += " ("; + } + + for (size_t i = 0; i != globs_.size(); ++i) { + if (i > 0) + s += ' '; + s += toqstr(globs_[i]); + } - return result.second; + if (has_description) + s += ')'; + return s; } -void rescanTexStyles() +/** \c FileFilterList parses a Qt-style list of available file filters + * to generate the corresponding vector. + * For example "TeX documents (*.tex);;LyX Documents (*.lyx)" + * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf" + * will result in a vector of size 1 in which the description field is empty. + */ +struct FileFilterList +{ + // FIXME UNICODE: globs_ should be unicode... + /** \param qt_style_filter a list of available file filters. + * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)". + * The "All files (*)" filter is always added to the list. + */ + explicit FileFilterList(docstring const & qt_style_filter = + docstring()); + + typedef std::vector::size_type size_type; + + bool empty() const { return filters_.empty(); } + size_type size() const { return filters_.size(); } + Filter & operator[](size_type i) { return filters_[i]; } + Filter const & operator[](size_type i) const { return filters_[i]; } + + void parse_filter(std::string const & filter); + std::vector filters_; +}; + + +FileFilterList::FileFilterList(docstring const & qt_style_filter) { - // Run rescan in user lyx directory - support::PathChanger p(package().user_support()); - FileName const command = libFileSearch("scripts", "TeXFiles.py"); - Systemcall one; - int const status = one.startscript(Systemcall::Wait, - support::os::python() + ' ' + - quoteName(command.toFilesystemEncoding())); - if (status == 0) - return; // FIXME UNICODE - frontend::Alert::error(_("Could not update TeX information"), - bformat(_("The script `%s' failed."), from_utf8(command.absFilename()))); + string const filter = to_utf8(qt_style_filter) + + (qt_style_filter.empty() ? string() : ";;") + + to_utf8(_("All Files ")) +#if defined(_WIN32) + + ("(*.*)"); +#else + + ("(*)"); +#endif + + // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)" + // into individual filters. + static lyx::regex const separator_re(";;"); + + string::const_iterator it = filter.begin(); + string::const_iterator const end = filter.end(); + while (true) { + match_results what; + + if (!lyx::regex_search(it, end, what, separator_re)) { + parse_filter(string(it, end)); + break; + } + + // Everything from the start of the input to + // the start of the match. + parse_filter(string(what[-1].first, what[-1].second)); + + // Increment the iterator to the end of the match. + it += distance(it, what[0].second); + } } -void getTexFileList(string const & filename, std::vector & list) +void FileFilterList::parse_filter(string const & filter) { - list.clear(); - FileName const file = libFileSearch("", filename); - if (file.empty()) - return; + // Matches "TeX documents (*.tex)", + // storing "TeX documents " as group 1 and "*.tex" as group 2. + static lyx::regex const filter_re("([^(]*)\\(([^)]+)\\) *$"); + + match_results what; + if (!lyx::regex_search(filter, what, filter_re)) { + // Just a glob, no description. + filters_.push_back(Filter(docstring(), trim(filter))); + } else { + // FIXME UNICODE + docstring const desc = from_utf8(string(what[1].first, what[1].second)); + string const globs = string(what[2].first, what[2].second); + filters_.push_back(Filter(trim(desc), trim(globs))); + } +} - list = getVectorFromString(file.fileContents(), "\n"); - // Normalise paths like /foo//bar ==> /foo/bar - boost::RegEx regex("/{2,}"); - std::vector::iterator it = list.begin(); - std::vector::iterator end = list.end(); - for (; it != end; ++it) - *it = regex.Merge((*it), "/"); +/** \returns the equivalent of the string passed in + * although any brace expressions are expanded. + * (E.g. "*.{png,jpg}" -> "*.png *.jpg") + */ +QStringList fileFilters(QString const & desc) +{ + // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}" + // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++" + FileFilterList filters(qstring_to_ucs4(desc)); + //LYXERR0("DESC: " << desc); + QStringList list; + for (size_t i = 0; i != filters.filters_.size(); ++i) { + QString f = filters.filters_[i].toString(); + //LYXERR0("FILTER: " << f); + list.append(f); + } + return list; +} - // remove empty items and duplicates - list.erase(std::remove(list.begin(), list.end(), ""), list.end()); - eliminate_duplicates(list); + +QString guiName(string const & type, BufferParams const & bp) +{ + if (type == "tableofcontents") + return qt_("Table of Contents"); + if (type == "child") + return qt_("Child Documents"); + if (type == "graphics") + return qt_("List of Graphics"); + if (type == "equation") + return qt_("List of Equations"); + if (type == "footnote") + return qt_("List of Footnotes"); + if (type == "listing") + return qt_("List of Listings"); + if (type == "index") + return qt_("List of Indexes"); + if (type == "marginalnote") + return qt_("List of Marginal notes"); + if (type == "note") + return qt_("List of Notes"); + if (type == "citation") + return qt_("List of Citations"); + if (type == "label") + return qt_("Labels and References"); + if (type == "branch") + return qt_("List of Branches"); + if (type == "change") + return qt_("List of Changes"); + + FloatList const & floats = bp.documentClass().floats(); + if (floats.typeExist(type)) + return qt_(floats.getType(type).listName()); + + return qt_(type); } + } // namespace lyx