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));
164 void rescanTexStyles()
166 // Run rescan in user lyx directory
167 PathChanger p(package().user_support());
168 FileName const command = support::libFileSearch("scripts", "TeXFiles.py");
170 int const status = one.startscript(Systemcall::Wait,
172 quoteName(command.toFilesystemEncoding()));
176 frontend::Alert::error(_("Could not update TeX information"),
177 bformat(_("The script `%s' failed."), from_utf8(command.absFilename())));
181 QStringList texFileList(QString const & filename)
184 FileName const file = libFileSearch(QString(), filename);
189 vector<docstring> doclist =
190 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
192 // Normalise paths like /foo//bar ==> /foo/bar
194 for (size_t i = 0; i != doclist.size(); ++i) {
195 QString file = toqstr(doclist[i]);
196 file.replace("\r", "");
197 while (file.contains("//"))
198 file.replace("//", "/");
204 return QList<QString>::fromSet(set);
208 QString internalPath(const QString & str)
210 return toqstr(os::internal_path(fromqstr(str)));
214 QString onlyFilename(const QString & str)
216 return toqstr(support::onlyFilename(fromqstr(str)));
220 QString onlyPath(const QString & str)
222 return toqstr(support::onlyPath(fromqstr(str)));
226 QString changeExtension(QString const & oldname, QString const & ext)
228 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
231 /// Remove the extension from \p name
232 QString removeExtension(QString const & name)
234 return toqstr(support::removeExtension(fromqstr(name)));
237 /** Add the extension \p ext to \p name.
238 Use this instead of changeExtension if you know that \p name is without
239 extension, because changeExtension would wrongly interpret \p name if it
242 QString addExtension(QString const & name, QString const & ext)
244 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
247 /// Return the extension of the file (not including the .)
248 QString getExtension(QString const & name)
250 return toqstr(support::getExtension(fromqstr(name)));
254 /** Convert relative path into absolute path based on a basepath.
255 If relpath is absolute, just use that.
256 If basepath doesn't exist use CWD.
258 QString makeAbsPath(QString const & relpath, QString const & base)
260 return toqstr(support::makeAbsPath(fromqstr(relpath),
261 fromqstr(base)).absFilename());
265 /////////////////////////////////////////////////////////////////////////
269 /////////////////////////////////////////////////////////////////////////
271 /** Given a string such as
272 * "<glob> <glob> ... *.{abc,def} <glob>",
273 * convert the csh-style brace expresions:
274 * "<glob> <glob> ... *.abc *.def <glob>".
275 * Requires no system support, so should work equally on Unix, Mac, Win32.
277 static string const convert_brace_glob(string const & glob)
279 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
280 // "abc,def,ghi" as group 2.
281 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
282 // Matches "abc" and "abc,", storing "abc" as group 1.
283 static boost::regex const block_re("([^,}]+),?");
287 string::const_iterator it = glob.begin();
288 string::const_iterator const end = glob.end();
290 boost::match_results<string::const_iterator> what;
291 if (!boost::regex_search(it, end, what, glob_re)) {
292 // Ensure that no information is lost.
293 pattern += string(it, end);
297 // Everything from the start of the input to
298 // the start of the match.
299 pattern += string(what[-1].first, what[-1].second);
301 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
302 string const head = string(what[1].first, what[1].second);
303 string const tail = string(what[2].first, what[2].second);
305 // Split the ','-separated chunks of tail so that
306 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
307 string const fmt = " " + head + "$1";
308 pattern += boost::regex_merge(tail, block_re, fmt);
310 // Increment the iterator to the end of the match.
311 it += distance(it, what[0].second);
320 /* \param description text describing the filters.
321 * \param one or more wildcard patterns, separated by
324 Filter(docstring const & description, std::string const & globs);
326 docstring const & description() const { return desc_; }
328 QString toString() const;
331 std::vector<std::string> globs_;
335 Filter::Filter(docstring const & description, string const & globs)
338 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
339 boost::char_separator<char> const separator(" ");
341 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
342 // "<glob> <glob> ... *.abc *.def <glob>"
343 string const expanded_globs = convert_brace_glob(globs);
345 // Split into individual globs.
346 vector<string> matches;
347 Tokenizer const tokens(expanded_globs, separator);
348 globs_ = vector<string>(tokens.begin(), tokens.end());
352 QString Filter::toString() const
356 bool const has_description = desc_.empty();
358 if (has_description) {
363 for (size_t i = 0; i != globs_.size(); ++i) {
366 s += toqstr(globs_[i]);
375 /** \c FileFilterList parses a Qt-style list of available file filters
376 * to generate the corresponding vector.
377 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
378 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
379 * will result in a vector of size 1 in which the description field is empty.
381 struct FileFilterList
383 // FIXME UNICODE: globs_ should be unicode...
384 /** \param qt_style_filter a list of available file filters.
385 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
386 * The "All files (*)" filter is always added to the list.
388 explicit FileFilterList(docstring const & qt_style_filter =
391 typedef std::vector<Filter>::size_type size_type;
393 bool empty() const { return filters_.empty(); }
394 size_type size() const { return filters_.size(); }
395 Filter & operator[](size_type i) { return filters_[i]; }
396 Filter const & operator[](size_type i) const { return filters_[i]; }
398 void parse_filter(std::string const & filter);
399 std::vector<Filter> filters_;
403 FileFilterList::FileFilterList(docstring const & qt_style_filter)
406 string const filter = to_utf8(qt_style_filter)
407 + (qt_style_filter.empty() ? string() : ";;")
408 + to_utf8(_("All Files "))
415 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
416 // into individual filters.
417 static boost::regex const separator_re(";;");
419 string::const_iterator it = filter.begin();
420 string::const_iterator const end = filter.end();
422 boost::match_results<string::const_iterator> what;
424 if (!boost::regex_search(it, end, what, separator_re)) {
425 parse_filter(string(it, end));
429 // Everything from the start of the input to
430 // the start of the match.
431 parse_filter(string(what[-1].first, what[-1].second));
433 // Increment the iterator to the end of the match.
434 it += distance(it, what[0].second);
439 void FileFilterList::parse_filter(string const & filter)
441 // Matches "TeX documents (*.tex)",
442 // storing "TeX documents " as group 1 and "*.tex" as group 2.
443 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
445 boost::match_results<string::const_iterator> what;
446 if (!boost::regex_search(filter, what, filter_re)) {
447 // Just a glob, no description.
448 filters_.push_back(Filter(docstring(), trim(filter)));
451 docstring const desc = from_utf8(string(what[1].first, what[1].second));
452 string const globs = string(what[2].first, what[2].second);
453 filters_.push_back(Filter(trim(desc), trim(globs)));
458 /** \returns the equivalent of the string passed in
459 * although any brace expressions are expanded.
460 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
462 QStringList fileFilters(QString const & desc)
464 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
465 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
466 FileFilterList filters(qstring_to_ucs4(desc));
467 LYXERR0("DESC: " << desc);
469 for (size_t i = 0; i != filters.filters_.size(); ++i) {
470 QString f = filters.filters_[i].toString();
471 LYXERR0("FILTER: " << f);