3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Jürgen Spitzmüller
10 * Full author contact details are available in file CREDITS.
15 #include "qt_helpers.h"
17 #include "FileDialog.h"
18 #include "LengthCombo.h"
20 #include "frontends/alert.h"
25 #include "support/debug.h"
26 #include "support/filetools.h"
27 #include "support/foreach.h"
28 #include "support/gettext.h"
29 #include "support/lstrings.h"
30 #include "support/lyxalgo.h"
31 #include "support/os.h"
32 #include "support/Package.h"
33 #include "support/Path.h"
34 #include "support/Systemcall.h"
48 #include <boost/regex.hpp>
49 #include <boost/tokenizer.hpp>
53 using namespace lyx::support;
57 LyXErr & operator<<(LyXErr & err, QString const & str)
59 return err << fromqstr(str);
63 FileName libFileSearch(QString const & dir, QString const & name,
66 return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext));
72 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
74 QString const length = input->text();
78 // Don't return unit-from-choice if the input(field) contains a unit
79 if (isValidGlueLength(fromqstr(length)))
80 return fromqstr(length);
82 Length::UNIT const unit = combo->currentLengthItem();
84 return Length(length.toDouble(), unit).asString();
88 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
90 QString const length = input->text();
94 // don't return unit-from-choice if the input(field) contains a unit
95 if (isValidGlueLength(fromqstr(length)))
96 return Length(fromqstr(length));
98 Length::UNIT const unit = unitFromString(fromqstr(combo->currentText()));
100 return Length(length.toDouble(), unit);
104 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
105 Length const & len, Length::UNIT /*defaultUnit*/)
107 combo->setCurrentItem(len.unit());
108 input->setText(QString::number(Length(len).value()));
112 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
113 string const & len, Length::UNIT defaultUnit)
116 // no length (UNIT_NONE)
117 combo->setCurrentItem(defaultUnit);
119 } else if (!isValidLength(len) && !isStrDbl(len)) {
120 // use input field only for gluelengths
121 combo->setCurrentItem(defaultUnit);
122 input->setText(toqstr(len));
124 lengthToWidgets(input, combo, Length(len), defaultUnit);
129 void lengthAutoToWidgets(QLineEdit * input, LengthCombo * combo,
130 Length const & len, Length::UNIT defaultUnit)
132 if (len.value() == 0)
133 lengthToWidgets(input, combo, "auto", defaultUnit);
135 lengthToWidgets(input, combo, len, defaultUnit);
139 void setValid(QWidget * widget, bool valid)
142 widget->setPalette(QPalette());
144 QPalette pal = widget->palette();
145 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
146 widget->setPalette(pal);
150 } // namespace frontend
152 QString const qt_(char const * str, const char *)
154 return toqstr(_(str));
158 QString const qt_(string const & str)
160 return toqstr(_(str));
168 #if !defined(USE_WCHAR_T) && defined(__GNUC__)
169 bool operator()(LanguagePair const & lhs, LanguagePair const & rhs) const
171 return lhs.first < rhs.first;
174 Sorter() : loc_ok(true)
183 bool operator()(LanguagePair const & lhs, LanguagePair const & rhs) const
185 // FIXME: would that be "QString::localeAwareCompare()"?
187 return loc_(fromqstr(lhs.first), fromqstr(rhs.first));
189 return lhs.first < rhs.first;
201 QList<LanguagePair> languageData(bool character_dlg)
203 size_t const offset = character_dlg ? 2 : 0;
204 vector<LanguagePair> langs(languages.size() + offset);
207 langs[0].first = qt_("No change");
208 langs[0].second = "ignore";
209 langs[1].first = qt_("Reset");
210 langs[1].second = "reset";
213 Languages::const_iterator it = languages.begin();
214 for (size_t i = 0; i != languages.size(); ++i, ++it) {
215 langs[i + offset].first = qt_(it->second.display());
216 langs[i + offset].second = toqstr(it->second.lang());
219 // Don't sort "ignore" and "reset"
220 vector<LanguagePair>::iterator begin = langs.begin() + offset;
221 sort(begin, langs.end(), Sorter());
223 QList<LanguagePair> list;
224 foreach (LanguagePair const & l, langs)
230 void rescanTexStyles()
232 // Run rescan in user lyx directory
233 PathChanger p(package().user_support());
234 FileName const command = libFileSearch("scripts", "TeXFiles.py");
236 int const status = one.startscript(Systemcall::Wait,
238 quoteName(command.toFilesystemEncoding()));
242 frontend::Alert::error(_("Could not update TeX information"),
243 bformat(_("The script `%s' failed."), from_utf8(command.absFilename())));
247 QStringList texFileList(QString const & filename)
250 FileName const file = libFileSearch(QString(), filename);
255 vector<docstring> doclist =
256 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
258 // Normalise paths like /foo//bar ==> /foo/bar
260 for (size_t i = 0; i != doclist.size(); ++i) {
261 QString file = toqstr(doclist[i]);
262 file.replace("\r", "");
263 while (file.contains("//"))
264 file.replace("//", "/");
270 return QList<QString>::fromSet(set);
274 QString internalPath(const QString & str)
276 return toqstr(os::internal_path(fromqstr(str)));
280 QString onlyFilename(const QString & str)
282 return toqstr(support::onlyFilename(fromqstr(str)));
286 QString onlyPath(const QString & str)
288 return toqstr(support::onlyPath(fromqstr(str)));
292 QString changeExtension(QString const & oldname, QString const & ext)
294 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
297 /// Remove the extension from \p name
298 QString removeExtension(QString const & name)
300 return toqstr(support::removeExtension(fromqstr(name)));
303 /** Add the extension \p ext to \p name.
304 Use this instead of changeExtension if you know that \p name is without
305 extension, because changeExtension would wrongly interpret \p name if it
308 QString addExtension(QString const & name, QString const & ext)
310 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
313 /// Return the extension of the file (not including the .)
314 QString getExtension(QString const & name)
316 return toqstr(support::getExtension(fromqstr(name)));
320 /** Convert relative path into absolute path based on a basepath.
321 If relpath is absolute, just use that.
322 If basepath doesn't exist use CWD.
324 QString makeAbsPath(QString const & relpath, QString const & base)
326 return toqstr(support::makeAbsPath(fromqstr(relpath),
327 fromqstr(base)).absFilename());
331 /////////////////////////////////////////////////////////////////////////
335 /////////////////////////////////////////////////////////////////////////
337 /** Given a string such as
338 * "<glob> <glob> ... *.{abc,def} <glob>",
339 * convert the csh-style brace expresions:
340 * "<glob> <glob> ... *.abc *.def <glob>".
341 * Requires no system support, so should work equally on Unix, Mac, Win32.
343 static string const convert_brace_glob(string const & glob)
345 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
346 // "abc,def,ghi" as group 2.
347 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
348 // Matches "abc" and "abc,", storing "abc" as group 1.
349 static boost::regex const block_re("([^,}]+),?");
353 string::const_iterator it = glob.begin();
354 string::const_iterator const end = glob.end();
356 boost::match_results<string::const_iterator> what;
357 if (!boost::regex_search(it, end, what, glob_re)) {
358 // Ensure that no information is lost.
359 pattern += string(it, end);
363 // Everything from the start of the input to
364 // the start of the match.
365 pattern += string(what[-1].first, what[-1].second);
367 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
368 string const head = string(what[1].first, what[1].second);
369 string const tail = string(what[2].first, what[2].second);
371 // Split the ','-separated chunks of tail so that
372 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
373 string const fmt = " " + head + "$1";
374 pattern += boost::regex_merge(tail, block_re, fmt);
376 // Increment the iterator to the end of the match.
377 it += distance(it, what[0].second);
386 /* \param description text describing the filters.
387 * \param one or more wildcard patterns, separated by
390 Filter(docstring const & description, std::string const & globs);
392 docstring const & description() const { return desc_; }
394 QString toString() const;
397 std::vector<std::string> globs_;
401 Filter::Filter(docstring const & description, string const & globs)
404 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
405 boost::char_separator<char> const separator(" ");
407 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
408 // "<glob> <glob> ... *.abc *.def <glob>"
409 string const expanded_globs = convert_brace_glob(globs);
411 // Split into individual globs.
412 vector<string> matches;
413 Tokenizer const tokens(expanded_globs, separator);
414 globs_ = vector<string>(tokens.begin(), tokens.end());
418 QString Filter::toString() const
422 bool const has_description = desc_.empty();
424 if (has_description) {
429 for (size_t i = 0; i != globs_.size(); ++i) {
432 s += toqstr(globs_[i]);
441 /** \c FileFilterList parses a Qt-style list of available file filters
442 * to generate the corresponding vector.
443 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
444 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
445 * will result in a vector of size 1 in which the description field is empty.
447 struct FileFilterList
449 // FIXME UNICODE: globs_ should be unicode...
450 /** \param qt_style_filter a list of available file filters.
451 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
452 * The "All files (*)" filter is always added to the list.
454 explicit FileFilterList(docstring const & qt_style_filter =
457 typedef std::vector<Filter>::size_type size_type;
459 bool empty() const { return filters_.empty(); }
460 size_type size() const { return filters_.size(); }
461 Filter & operator[](size_type i) { return filters_[i]; }
462 Filter const & operator[](size_type i) const { return filters_[i]; }
464 void parse_filter(std::string const & filter);
465 std::vector<Filter> filters_;
469 FileFilterList::FileFilterList(docstring const & qt_style_filter)
472 string const filter = to_utf8(qt_style_filter)
473 + (qt_style_filter.empty() ? string() : ";;")
474 + to_utf8(_("All Files "))
481 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
482 // into individual filters.
483 static boost::regex const separator_re(";;");
485 string::const_iterator it = filter.begin();
486 string::const_iterator const end = filter.end();
488 boost::match_results<string::const_iterator> what;
490 if (!boost::regex_search(it, end, what, separator_re)) {
491 parse_filter(string(it, end));
495 // Everything from the start of the input to
496 // the start of the match.
497 parse_filter(string(what[-1].first, what[-1].second));
499 // Increment the iterator to the end of the match.
500 it += distance(it, what[0].second);
505 void FileFilterList::parse_filter(string const & filter)
507 // Matches "TeX documents (*.tex)",
508 // storing "TeX documents " as group 1 and "*.tex" as group 2.
509 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
511 boost::match_results<string::const_iterator> what;
512 if (!boost::regex_search(filter, what, filter_re)) {
513 // Just a glob, no description.
514 filters_.push_back(Filter(docstring(), trim(filter)));
517 docstring const desc = from_utf8(string(what[1].first, what[1].second));
518 string const globs = string(what[2].first, what[2].second);
519 filters_.push_back(Filter(trim(desc), trim(globs)));
524 /** \returns the equivalent of the string passed in
525 * although any brace expressions are expanded.
526 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
528 QStringList fileFilters(QString const & desc)
530 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
531 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
532 FileFilterList filters(qstring_to_ucs4(desc));
533 LYXERR0("DESC: " << fromqstr(desc));
535 for (size_t i = 0; i != filters.filters_.size(); ++i) {
536 QString f = filters.filters_[i].toString();
537 LYXERR0("FILTER: " << fromqstr(f));