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"
22 #include "BufferParams.h"
23 #include "FloatList.h"
26 #include "TextClass.h"
28 #include "support/debug.h"
29 #include "support/filetools.h"
30 #include "support/foreach.h"
31 #include "support/gettext.h"
32 #include "support/lstrings.h"
33 #include "support/lyxalgo.h"
34 #include "support/os.h"
35 #include "support/Package.h"
36 #include "support/Path.h"
37 #include "support/Systemcall.h"
51 #include <boost/regex.hpp>
52 #include <boost/tokenizer.hpp>
56 using namespace lyx::support;
60 LyXErr & operator<<(LyXErr & err, QString const & str)
62 return err << fromqstr(str);
66 FileName libFileSearch(QString const & dir, QString const & name,
69 return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext));
75 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
77 QString const length = input->text();
81 // Don't return unit-from-choice if the input(field) contains a unit
82 if (isValidGlueLength(fromqstr(length)))
83 return fromqstr(length);
85 Length::UNIT const unit = combo->currentLengthItem();
87 return Length(length.toDouble(), unit).asString();
91 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
93 QString const length = input->text();
97 // don't return unit-from-choice if the input(field) contains a unit
98 if (isValidGlueLength(fromqstr(length)))
99 return Length(fromqstr(length));
101 Length::UNIT const unit = unitFromString(fromqstr(combo->currentText()));
103 return Length(length.toDouble(), unit);
107 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
108 Length const & len, Length::UNIT /*defaultUnit*/)
110 combo->setCurrentItem(len.unit());
111 input->setText(QString::number(Length(len).value()));
115 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
116 string const & len, Length::UNIT defaultUnit)
119 // no length (UNIT_NONE)
120 combo->setCurrentItem(defaultUnit);
122 } else if (!isValidLength(len) && !isStrDbl(len)) {
123 // use input field only for gluelengths
124 combo->setCurrentItem(defaultUnit);
125 input->setText(toqstr(len));
127 lengthToWidgets(input, combo, Length(len), defaultUnit);
132 void lengthAutoToWidgets(QLineEdit * input, LengthCombo * combo,
133 Length const & len, Length::UNIT defaultUnit)
135 if (len.value() == 0)
136 lengthToWidgets(input, combo, "auto", defaultUnit);
138 lengthToWidgets(input, combo, len, defaultUnit);
142 void setValid(QWidget * widget, bool valid)
145 widget->setPalette(QPalette());
147 QPalette pal = widget->palette();
148 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
149 widget->setPalette(pal);
153 } // namespace frontend
155 QString const qt_(char const * str, const char *)
157 return toqstr(_(str));
161 QString const qt_(string const & str)
163 return toqstr(_(str));
167 void rescanTexStyles()
169 // Run rescan in user lyx directory
170 PathChanger p(package().user_support());
171 FileName const command = support::libFileSearch("scripts", "TeXFiles.py");
173 int const status = one.startscript(Systemcall::Wait,
175 quoteName(command.toFilesystemEncoding()));
179 frontend::Alert::error(_("Could not update TeX information"),
180 bformat(_("The script `%s' failed."), from_utf8(command.absFilename())));
184 QStringList texFileList(QString const & filename)
187 FileName const file = libFileSearch(QString(), filename);
192 vector<docstring> doclist =
193 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
195 // Normalise paths like /foo//bar ==> /foo/bar
197 for (size_t i = 0; i != doclist.size(); ++i) {
198 QString file = toqstr(doclist[i]);
199 file.replace("\r", "");
200 while (file.contains("//"))
201 file.replace("//", "/");
207 return QList<QString>::fromSet(set);
211 QString internalPath(const QString & str)
213 return toqstr(os::internal_path(fromqstr(str)));
217 QString onlyFilename(const QString & str)
219 return toqstr(support::onlyFilename(fromqstr(str)));
223 QString onlyPath(const QString & str)
225 return toqstr(support::onlyPath(fromqstr(str)));
229 QString changeExtension(QString const & oldname, QString const & ext)
231 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
234 /// Remove the extension from \p name
235 QString removeExtension(QString const & name)
237 return toqstr(support::removeExtension(fromqstr(name)));
240 /** Add the extension \p ext to \p name.
241 Use this instead of changeExtension if you know that \p name is without
242 extension, because changeExtension would wrongly interpret \p name if it
245 QString addExtension(QString const & name, QString const & ext)
247 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
250 /// Return the extension of the file (not including the .)
251 QString getExtension(QString const & name)
253 return toqstr(support::getExtension(fromqstr(name)));
257 /** Convert relative path into absolute path based on a basepath.
258 If relpath is absolute, just use that.
259 If basepath doesn't exist use CWD.
261 QString makeAbsPath(QString const & relpath, QString const & base)
263 return toqstr(support::makeAbsPath(fromqstr(relpath),
264 fromqstr(base)).absFilename());
268 /////////////////////////////////////////////////////////////////////////
272 /////////////////////////////////////////////////////////////////////////
274 /** Given a string such as
275 * "<glob> <glob> ... *.{abc,def} <glob>",
276 * convert the csh-style brace expresions:
277 * "<glob> <glob> ... *.abc *.def <glob>".
278 * Requires no system support, so should work equally on Unix, Mac, Win32.
280 static string const convert_brace_glob(string const & glob)
282 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
283 // "abc,def,ghi" as group 2.
284 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
285 // Matches "abc" and "abc,", storing "abc" as group 1.
286 static boost::regex const block_re("([^,}]+),?");
290 string::const_iterator it = glob.begin();
291 string::const_iterator const end = glob.end();
293 boost::match_results<string::const_iterator> what;
294 if (!boost::regex_search(it, end, what, glob_re)) {
295 // Ensure that no information is lost.
296 pattern += string(it, end);
300 // Everything from the start of the input to
301 // the start of the match.
302 pattern += string(what[-1].first, what[-1].second);
304 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
305 string const head = string(what[1].first, what[1].second);
306 string const tail = string(what[2].first, what[2].second);
308 // Split the ','-separated chunks of tail so that
309 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
310 string const fmt = " " + head + "$1";
311 pattern += boost::regex_merge(tail, block_re, fmt);
313 // Increment the iterator to the end of the match.
314 it += distance(it, what[0].second);
323 /* \param description text describing the filters.
324 * \param one or more wildcard patterns, separated by
327 Filter(docstring const & description, std::string const & globs);
329 docstring const & description() const { return desc_; }
331 QString toString() const;
334 std::vector<std::string> globs_;
338 Filter::Filter(docstring const & description, string const & globs)
341 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
342 boost::char_separator<char> const separator(" ");
344 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
345 // "<glob> <glob> ... *.abc *.def <glob>"
346 string const expanded_globs = convert_brace_glob(globs);
348 // Split into individual globs.
349 vector<string> matches;
350 Tokenizer const tokens(expanded_globs, separator);
351 globs_ = vector<string>(tokens.begin(), tokens.end());
355 QString Filter::toString() const
359 bool const has_description = desc_.empty();
361 if (has_description) {
366 for (size_t i = 0; i != globs_.size(); ++i) {
369 s += toqstr(globs_[i]);
378 /** \c FileFilterList parses a Qt-style list of available file filters
379 * to generate the corresponding vector.
380 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
381 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
382 * will result in a vector of size 1 in which the description field is empty.
384 struct FileFilterList
386 // FIXME UNICODE: globs_ should be unicode...
387 /** \param qt_style_filter a list of available file filters.
388 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
389 * The "All files (*)" filter is always added to the list.
391 explicit FileFilterList(docstring const & qt_style_filter =
394 typedef std::vector<Filter>::size_type size_type;
396 bool empty() const { return filters_.empty(); }
397 size_type size() const { return filters_.size(); }
398 Filter & operator[](size_type i) { return filters_[i]; }
399 Filter const & operator[](size_type i) const { return filters_[i]; }
401 void parse_filter(std::string const & filter);
402 std::vector<Filter> filters_;
406 FileFilterList::FileFilterList(docstring const & qt_style_filter)
409 string const filter = to_utf8(qt_style_filter)
410 + (qt_style_filter.empty() ? string() : ";;")
411 + to_utf8(_("All Files "))
418 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
419 // into individual filters.
420 static boost::regex const separator_re(";;");
422 string::const_iterator it = filter.begin();
423 string::const_iterator const end = filter.end();
425 boost::match_results<string::const_iterator> what;
427 if (!boost::regex_search(it, end, what, separator_re)) {
428 parse_filter(string(it, end));
432 // Everything from the start of the input to
433 // the start of the match.
434 parse_filter(string(what[-1].first, what[-1].second));
436 // Increment the iterator to the end of the match.
437 it += distance(it, what[0].second);
442 void FileFilterList::parse_filter(string const & filter)
444 // Matches "TeX documents (*.tex)",
445 // storing "TeX documents " as group 1 and "*.tex" as group 2.
446 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
448 boost::match_results<string::const_iterator> what;
449 if (!boost::regex_search(filter, what, filter_re)) {
450 // Just a glob, no description.
451 filters_.push_back(Filter(docstring(), trim(filter)));
454 docstring const desc = from_utf8(string(what[1].first, what[1].second));
455 string const globs = string(what[2].first, what[2].second);
456 filters_.push_back(Filter(trim(desc), trim(globs)));
461 /** \returns the equivalent of the string passed in
462 * although any brace expressions are expanded.
463 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
465 QStringList fileFilters(QString const & desc)
467 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
468 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
469 FileFilterList filters(qstring_to_ucs4(desc));
470 //LYXERR0("DESC: " << desc);
472 for (size_t i = 0; i != filters.filters_.size(); ++i) {
473 QString f = filters.filters_[i].toString();
474 //LYXERR0("FILTER: " << f);
481 QString guiName(string const & type, BufferParams const & bp)
483 if (type == "tableofcontents")
484 return qt_("Table of Contents");
486 return qt_("Child Documents");
487 if (type == "graphics")
488 return qt_("List of Graphics");
489 if (type == "equation")
490 return qt_("List of Equations");
491 if (type == "footnote")
492 return qt_("List of Footnotes");
493 if (type == "listing")
494 return qt_("List of Listings");
496 return qt_("List of Indexes");
497 if (type == "marginalnote")
498 return qt_("List of Marginal notes");
500 return qt_("List of Notes");
501 if (type == "citation")
502 return qt_("List of Citations");
504 return qt_("Labels and References");
505 if (type == "branch")
506 return qt_("List of Branches");
508 FloatList const & floats = bp.documentClass().floats();
509 if (floats.typeExist(type))
510 return qt_(floats.getType(type).listName());