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/convert.h"
29 #include "support/debug.h"
30 #include "support/filetools.h"
31 #include "support/foreach.h"
32 #include "support/gettext.h"
33 #include "support/lstrings.h"
34 #include "support/lyxalgo.h"
35 #include "support/os.h"
36 #include "support/Package.h"
37 #include "support/Path.h"
38 #include "support/Systemcall.h"
53 #include "support/regex.h"
54 #include <boost/tokenizer.hpp>
58 using namespace lyx::support;
62 FileName libFileSearch(QString const & dir, QString const & name,
65 return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext));
69 FileName imageLibFileSearch(QString & dir, QString const & name,
72 string tmp = fromqstr(dir);
73 FileName fn = support::imageLibFileSearch(tmp, fromqstr(name), fromqstr(ext));
81 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
83 QString const length = input->text();
87 // Don't return unit-from-choice if the input(field) contains a unit
88 if (isValidGlueLength(fromqstr(length)))
89 return fromqstr(length);
91 Length::UNIT const unit = combo->currentLengthItem();
93 return Length(length.trimmed().toDouble(), unit).asString();
97 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
99 QString const length = input->text();
100 if (length.isEmpty())
103 // don't return unit-from-choice if the input(field) contains a unit
104 if (isValidGlueLength(fromqstr(length)))
105 return Length(fromqstr(length));
107 Length::UNIT unit = Length::UNIT_NONE;
108 QString const item = combo->currentText();
109 for (int i = 0; i < num_units; i++) {
110 if (qt_(lyx::unit_name_gui[i]) == item) {
111 unit = unitFromString(unit_name[i]);
116 return Length(length.trimmed().toDouble(), unit);
120 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
121 Length const & len, Length::UNIT /*defaultUnit*/)
123 combo->setCurrentItem(len.unit());
125 loc.setNumberOptions(QLocale::OmitGroupSeparator);
126 input->setText(loc.toString(Length(len).value()));
130 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
131 string const & len, Length::UNIT defaultUnit)
134 // no length (UNIT_NONE)
135 combo->setCurrentItem(defaultUnit);
137 } else if (!isValidLength(len) && !isStrDbl(len)) {
138 // use input field only for gluelengths
139 combo->setCurrentItem(defaultUnit);
140 input->setText(toqstr(len));
142 lengthToWidgets(input, combo, Length(len), defaultUnit);
147 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
148 docstring const & len, Length::UNIT defaultUnit)
150 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
154 double widgetToDouble(QLineEdit const * input)
156 QString const text = input->text();
160 return text.trimmed().toDouble();
164 string widgetToDoubleStr(QLineEdit const * input)
166 QString const text = input->text();
170 return convert<string>(text.trimmed().toDouble());
174 void doubleToWidget(QLineEdit * input, double const & value, char f, int prec)
177 loc.setNumberOptions(QLocale::OmitGroupSeparator);
178 input->setText(loc.toString(value, f, prec));
182 void doubleToWidget(QLineEdit * input, string const & value, char f, int prec)
184 doubleToWidget(input, convert<double>(value), f, prec);
188 void setValid(QWidget * widget, bool valid)
191 widget->setPalette(QPalette());
193 QPalette pal = widget->palette();
194 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
195 widget->setPalette(pal);
199 } // namespace frontend
201 QString const qt_(char const * str, const char *)
203 return toqstr(_(str));
207 QString const qt_(string const & str)
209 return toqstr(_(str));
213 void rescanTexStyles(string const & arg)
215 // Run rescan in user lyx directory
216 PathChanger p(package().user_support());
217 FileName const prog = support::libFileSearch("scripts", "TeXFiles.py");
219 string const command = os::python() + ' ' +
220 quoteName(prog.toFilesystemEncoding()) + ' ' +
222 int const status = one.startscript(Systemcall::Wait, command);
226 frontend::Alert::error(_("Could not update TeX information"),
227 bformat(_("The script `%1$s' failed."), from_utf8(prog.absFileName())));
231 QStringList texFileList(QString const & filename)
234 FileName const file = libFileSearch(QString(), filename);
239 vector<docstring> doclist =
240 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
242 // Normalise paths like /foo//bar ==> /foo/bar
244 for (size_t i = 0; i != doclist.size(); ++i) {
245 QString file = toqstr(doclist[i]);
246 file.replace("\r", "");
247 while (file.contains("//"))
248 file.replace("//", "/");
254 return QList<QString>::fromSet(set);
257 QString const externalLineEnding(docstring const & str)
260 // The MAC clipboard uses \r for lineendings, and we use \n
261 return toqstr(subst(str, '\n', '\r'));
262 #elif defined(Q_WS_WIN)
263 // Windows clipboard uses \r\n for lineendings, and we use \n
264 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
271 docstring const internalLineEnding(QString const & str)
273 docstring const s = subst(qstring_to_ucs4(str),
274 from_ascii("\r\n"), from_ascii("\n"));
275 return subst(s, '\r', '\n');
279 QString internalPath(const QString & str)
281 return toqstr(os::internal_path(fromqstr(str)));
285 QString onlyFileName(const QString & str)
287 return toqstr(support::onlyFileName(fromqstr(str)));
291 QString onlyPath(const QString & str)
293 return toqstr(support::onlyPath(fromqstr(str)));
297 QString changeExtension(QString const & oldname, QString const & ext)
299 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
302 /// Remove the extension from \p name
303 QString removeExtension(QString const & name)
305 return toqstr(support::removeExtension(fromqstr(name)));
308 /** Add the extension \p ext to \p name.
309 Use this instead of changeExtension if you know that \p name is without
310 extension, because changeExtension would wrongly interpret \p name if it
313 QString addExtension(QString const & name, QString const & ext)
315 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
318 /// Return the extension of the file (not including the .)
319 QString getExtension(QString const & name)
321 return toqstr(support::getExtension(fromqstr(name)));
325 /** Convert relative path into absolute path based on a basepath.
326 If relpath is absolute, just use that.
327 If basepath doesn't exist use CWD.
329 QString makeAbsPath(QString const & relpath, QString const & base)
331 return toqstr(support::makeAbsPath(fromqstr(relpath),
332 fromqstr(base)).absFileName());
336 /////////////////////////////////////////////////////////////////////////
340 /////////////////////////////////////////////////////////////////////////
342 /** Given a string such as
343 * "<glob> <glob> ... *.{abc,def} <glob>",
344 * convert the csh-style brace expresions:
345 * "<glob> <glob> ... *.abc *.def <glob>".
346 * Requires no system support, so should work equally on Unix, Mac, Win32.
348 static string const convert_brace_glob(string const & glob)
350 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
351 // "abc,def,ghi" as group 2, while allowing spaces in group 2.
352 static lyx::regex const glob_re(" *([^ {]*)\\{([^}]+)\\}");
353 // Matches "abc" and "abc,", storing "abc" as group 1,
354 // while ignoring surrounding spaces.
355 static lyx::regex const block_re(" *([^ ,}]+) *,? *");
359 string::const_iterator it = glob.begin();
360 string::const_iterator const end = glob.end();
362 match_results<string::const_iterator> what;
363 if (!regex_search(it, end, what, glob_re)) {
364 // Ensure that no information is lost.
365 pattern += string(it, end);
369 // Everything from the start of the input to
370 // the start of the match.
371 pattern += string(what[-1].first, what[-1].second);
373 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
374 string const head = string(what[1].first, what[1].second);
375 string const tail = string(what[2].first, what[2].second);
377 // Split the ','-separated chunks of tail so that
378 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
379 string const fmt = " " + head + "$1";
380 pattern += regex_replace(tail, block_re, fmt);
382 // Increment the iterator to the end of the match.
383 it += distance(it, what[0].second);
392 /* \param description text describing the filters.
393 * \param one or more wildcard patterns, separated by
396 Filter(docstring const & description, std::string const & globs);
398 docstring const & description() const { return desc_; }
400 QString toString() const;
403 std::vector<std::string> globs_;
407 Filter::Filter(docstring const & description, string const & globs)
410 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
411 boost::char_separator<char> const separator(" ");
413 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
414 // "<glob> <glob> ... *.abc *.def <glob>"
415 string const expanded_globs = convert_brace_glob(globs);
417 // Split into individual globs.
418 vector<string> matches;
419 Tokenizer const tokens(expanded_globs, separator);
420 globs_ = vector<string>(tokens.begin(), tokens.end());
424 QString Filter::toString() const
428 bool const has_description = !desc_.empty();
430 if (has_description) {
435 for (size_t i = 0; i != globs_.size(); ++i) {
438 s += toqstr(globs_[i]);
447 /** \c FileFilterList parses a Qt-style list of available file filters
448 * to generate the corresponding vector.
449 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
450 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
451 * will result in a vector of size 1 in which the description field is empty.
453 struct FileFilterList
455 // FIXME UNICODE: globs_ should be unicode...
456 /** \param qt_style_filter a list of available file filters.
457 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
458 * The "All files (*)" filter is always added to the list.
460 explicit FileFilterList(docstring const & qt_style_filter =
463 typedef std::vector<Filter>::size_type size_type;
465 bool empty() const { return filters_.empty(); }
466 size_type size() const { return filters_.size(); }
467 Filter & operator[](size_type i) { return filters_[i]; }
468 Filter const & operator[](size_type i) const { return filters_[i]; }
470 void parse_filter(std::string const & filter);
471 std::vector<Filter> filters_;
475 FileFilterList::FileFilterList(docstring const & qt_style_filter)
478 string const filter = to_utf8(qt_style_filter)
479 + (qt_style_filter.empty() ? string() : ";;")
480 + to_utf8(_("All Files "))
487 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
488 // into individual filters.
489 static lyx::regex const separator_re(";;");
491 string::const_iterator it = filter.begin();
492 string::const_iterator const end = filter.end();
494 match_results<string::const_iterator> what;
496 if (!lyx::regex_search(it, end, what, separator_re)) {
497 parse_filter(string(it, end));
501 // Everything from the start of the input to
502 // the start of the match.
503 parse_filter(string(what[-1].first, what[-1].second));
505 // Increment the iterator to the end of the match.
506 it += distance(it, what[0].second);
511 void FileFilterList::parse_filter(string const & filter)
513 // Matches "TeX documents (plain) (*.tex)",
514 // storing "TeX documents (plain) " as group 1 and "*.tex" as group 2.
515 static lyx::regex const filter_re("(.*)\\(([^()]+)\\) *$");
517 match_results<string::const_iterator> what;
518 if (!lyx::regex_search(filter, what, filter_re)) {
519 // Just a glob, no description.
520 filters_.push_back(Filter(docstring(), trim(filter)));
523 docstring const desc = from_utf8(string(what[1].first, what[1].second));
524 string const globs = string(what[2].first, what[2].second);
525 filters_.push_back(Filter(trim(desc), trim(globs)));
530 /** \returns the equivalent of the string passed in
531 * although any brace expressions are expanded.
532 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
534 QStringList fileFilters(QString const & desc)
536 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
537 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
538 FileFilterList filters(qstring_to_ucs4(desc));
539 //LYXERR0("DESC: " << desc);
541 for (size_t i = 0; i != filters.filters_.size(); ++i) {
542 QString f = filters.filters_[i].toString();
543 //LYXERR0("FILTER: " << f);
550 QString guiName(string const & type, BufferParams const & bp)
552 if (type == "tableofcontents")
553 return qt_("Table of Contents");
555 return qt_("Child Documents");
556 if (type == "graphics")
557 return qt_("List of Graphics");
558 if (type == "equation")
559 return qt_("List of Equations");
560 if (type == "footnote")
561 return qt_("List of Footnotes");
562 if (type == "listing")
563 return qt_("List of Listings");
565 return qt_("List of Index Entries");
566 if (type == "marginalnote")
567 return qt_("List of Marginal notes");
569 return qt_("List of Notes");
570 if (type == "citation")
571 return qt_("List of Citations");
573 return qt_("Labels and References");
574 if (type == "branch")
575 return qt_("List of Branches");
576 if (type == "change")
577 return qt_("List of Changes");
579 FloatList const & floats = bp.documentClass().floats();
580 if (floats.typeExist(type))
581 return qt_(floats.getType(type).listName());