X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2Fqt_helpers.cpp;h=7f4c5d8fda1f8e1cccb18cc18553b97d3d58643c;hb=8e7d7e42952f0119db1c192386a63d1d9ede3b7f;hp=2c14d42149a32545d8bb9302fda74bb5d6603b95;hpb=cc4b9be8930debb4ba91d2b859539197c5306551;p=lyx.git diff --git a/src/frontends/qt4/qt_helpers.cpp b/src/frontends/qt4/qt_helpers.cpp index 2c14d42149..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. @@ -12,53 +12,61 @@ #include -#include "LengthCombo.h" #include "qt_helpers.h" -#include "lengthcommon.h" -#include "gettext.h" +#include "FileDialog.h" +#include "LengthCombo.h" -#include "support/os.h" -#include "support/lstrings.h" +#include "frontends/alert.h" -#include "debug.h" +#include "BufferParams.h" +#include "FloatList.h" +#include "Language.h" +#include "Length.h" +#include "TextClass.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" +#include "support/Package.h" +#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 -namespace lyx { - -using support::isStrDbl; -using std::vector; -using std::make_pair; -using std::string; -using std::pair; -using std::endl; +using namespace std; +using namespace lyx::support; +namespace lyx { -string makeFontName(string const & family, string const & foundry) +FileName libFileSearch(QString const & dir, QString const & name, + QString const & ext) { - if (foundry.empty()) - return family; - return family + " [" + foundry + ']'; + return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext)); } -pair parseFontName(string const & name) -{ - string::size_type const idx = name.find('['); - if (idx == string::npos || idx == 0) - return make_pair(name, string()); - return make_pair(name.substr(0, idx - 1), - name.substr(idx + 1, name.size() - idx - 2)); -} - +namespace frontend { string widgetsToLength(QLineEdit const * input, LengthCombo const * combo) { @@ -72,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(); } @@ -86,17 +94,26 @@ 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); } void lengthToWidgets(QLineEdit * input, LengthCombo * combo, Length const & len, Length::UNIT /*defaultUnit*/) { - combo->setCurrentItem(Length(len).unit()); - input->setText(QString::number(Length(len).value())); + combo->setCurrentItem(len.unit()); + QLocale loc; + loc.setNumberOptions(QLocale::OmitGroupSeparator); + input->setText(loc.toString(Length(len).value())); } @@ -117,30 +134,60 @@ 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) { - if (len.value() == 0) - lengthToWidgets(input, combo, "auto", defaultUnit); - else - lengthToWidgets(input, combo, len, defaultUnit); + lengthToWidgets(input, combo, to_utf8(len), defaultUnit); } -//NOTE "CB" here because we probably will want one of these -//for labeled sets, as well. -void setAutoTextCB(QCheckBox * checkBox, QLineEdit * lineEdit, - LengthCombo * lengthCombo) +double widgetToDouble(QLineEdit const * input) { - if (!checkBox->isChecked()) - lengthToWidgets(lineEdit, lengthCombo, - "auto", lengthCombo->currentLengthItem()); - else if (lineEdit->text() == "auto") - lengthToWidgets(lineEdit, lengthCombo, string(), - lengthCombo->currentLengthItem()); + 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) +{ + 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); +} + + +void setValid(QWidget * widget, bool valid) +{ + if (valid) { + widget->setPalette(QPalette()); + } else { + QPalette pal = widget->palette(); + pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0)); + widget->setPalette(pal); + } +} + +} // namespace frontend + QString const qt_(char const * str, const char *) { return toqstr(_(str)); @@ -153,62 +200,376 @@ QString const qt_(string const & str) } -docstring const formatted(docstring const & text, int w) +void rescanTexStyles() { - docstring sout; + // 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) +{ + 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); + } - if (text.empty()) - return sout; + // remove duplicates + return QList::fromSet(set); +} + +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 +} + + +docstring const internalLineEnding(QString const & str) +{ + docstring const s = subst(qstring_to_ucs4(str), + from_ascii("\r\n"), from_ascii("\n")); + return subst(s, '\r', '\n'); +} + + +QString internalPath(const QString & str) +{ + return toqstr(os::internal_path(fromqstr(str))); +} + + +QString onlyFileName(const QString & str) +{ + return toqstr(support::onlyFileName(fromqstr(str))); +} + + +QString onlyPath(const QString & str) +{ + return toqstr(support::onlyPath(fromqstr(str))); +} + + +QString changeExtension(QString const & oldname, QString const & ext) +{ + return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext))); +} - docstring::size_type curpos = 0; - docstring line; +/// Remove the extension from \p name +QString removeExtension(QString const & name) +{ + return toqstr(support::removeExtension(fromqstr(name))); +} - for (;;) { - docstring::size_type const nxtpos1 = text.find(' ', curpos); - docstring::size_type const nxtpos2 = text.find('\n', curpos); - docstring::size_type const nxtpos = std::min(nxtpos1, nxtpos2); +/** 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))); +} - docstring const word = - nxtpos == docstring::npos ? - text.substr(curpos) : - text.substr(curpos, nxtpos - curpos); +/// Return the extension of the file (not including the .) +QString getExtension(QString const & name) +{ + return toqstr(support::getExtension(fromqstr(name))); +} - bool const newline = (nxtpos2 != docstring::npos && - nxtpos2 < nxtpos1); - docstring const line_plus_word = - line.empty() ? word : line + char_type(' ') + word; +/** 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()); +} - // FIXME: make w be size_t - if (int(line_plus_word.length()) >= w) { - sout += line + char_type('\n'); - if (newline) { - sout += word + char_type('\n'); - line.erase(); - } else { - line = word; - } - } else if (newline) { - sout += line_plus_word + char_type('\n'); - line.erase(); +///////////////////////////////////////////////////////////////////////// +// +// FileFilterList +// +///////////////////////////////////////////////////////////////////////// - } else { - if (!line.empty()) - line += char_type(' '); - line += word; +/** 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) +{ + // 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; } - if (nxtpos == docstring::npos) { - if (!line.empty()) - sout += line; + // 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); + } + + return pattern; +} + + +struct Filter +{ + /* \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_; } + + QString toString() const; + + docstring desc_; + std::vector globs_; +}; + + +Filter::Filter(docstring const & description, string const & globs) + : desc_(description) +{ + typedef boost::tokenizer > Tokenizer; + boost::char_separator const separator(" "); + + // Given " ... *.{abc,def} ", expand to + // " ... *.abc *.def " + string const expanded_globs = convert_brace_glob(globs); + + // Split into individual globs. + vector matches; + Tokenizer const tokens(expanded_globs, separator); + globs_ = vector(tokens.begin(), tokens.end()); +} + + +QString Filter::toString() const +{ + QString s; + + bool const has_description = desc_.empty(); + + if (has_description) { + s += toqstr(desc_); + s += " ("; + } + + for (size_t i = 0; i != globs_.size(); ++i) { + if (i > 0) + s += ' '; + s += toqstr(globs_[i]); + } + + if (has_description) + s += ')'; + return s; +} + + +/** \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) +{ + // FIXME UNICODE + 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; } - curpos = nxtpos + 1; + // 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 FileFilterList::parse_filter(string const & filter) +{ + // 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))); + } +} + - return sout; +/** \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; +} + + +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