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()
215 // Run rescan in user lyx directory
216 PathChanger p(package().user_support());
217 FileName const command = support::libFileSearch("scripts", "TeXFiles.py");
219 int const status = one.startscript(Systemcall::Wait,
221 quoteName(command.toFilesystemEncoding()));
225 frontend::Alert::error(_("Could not update TeX information"),
226 bformat(_("The script `%1$s' failed."), from_utf8(command.absFileName())));
230 QStringList texFileList(QString const & filename)
233 FileName const file = libFileSearch(QString(), filename);
238 vector<docstring> doclist =
239 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
241 // Normalise paths like /foo//bar ==> /foo/bar
243 for (size_t i = 0; i != doclist.size(); ++i) {
244 QString file = toqstr(doclist[i]);
245 file.replace("\r", "");
246 while (file.contains("//"))
247 file.replace("//", "/");
253 return QList<QString>::fromSet(set);
256 QString const externalLineEnding(docstring const & str)
259 // The MAC clipboard uses \r for lineendings, and we use \n
260 return toqstr(subst(str, '\n', '\r'));
261 #elif defined(Q_WS_WIN)
262 // Windows clipboard uses \r\n for lineendings, and we use \n
263 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
270 docstring const internalLineEnding(QString const & str)
272 docstring const s = subst(qstring_to_ucs4(str),
273 from_ascii("\r\n"), from_ascii("\n"));
274 return subst(s, '\r', '\n');
278 QString internalPath(const QString & str)
280 return toqstr(os::internal_path(fromqstr(str)));
284 QString onlyFileName(const QString & str)
286 return toqstr(support::onlyFileName(fromqstr(str)));
290 QString onlyPath(const QString & str)
292 return toqstr(support::onlyPath(fromqstr(str)));
296 QString changeExtension(QString const & oldname, QString const & ext)
298 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
301 /// Remove the extension from \p name
302 QString removeExtension(QString const & name)
304 return toqstr(support::removeExtension(fromqstr(name)));
307 /** Add the extension \p ext to \p name.
308 Use this instead of changeExtension if you know that \p name is without
309 extension, because changeExtension would wrongly interpret \p name if it
312 QString addExtension(QString const & name, QString const & ext)
314 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
317 /// Return the extension of the file (not including the .)
318 QString getExtension(QString const & name)
320 return toqstr(support::getExtension(fromqstr(name)));
324 /** Convert relative path into absolute path based on a basepath.
325 If relpath is absolute, just use that.
326 If basepath doesn't exist use CWD.
328 QString makeAbsPath(QString const & relpath, QString const & base)
330 return toqstr(support::makeAbsPath(fromqstr(relpath),
331 fromqstr(base)).absFileName());
335 /////////////////////////////////////////////////////////////////////////
339 /////////////////////////////////////////////////////////////////////////
341 /** Given a string such as
342 * "<glob> <glob> ... *.{abc,def} <glob>",
343 * convert the csh-style brace expresions:
344 * "<glob> <glob> ... *.abc *.def <glob>".
345 * Requires no system support, so should work equally on Unix, Mac, Win32.
347 static string const convert_brace_glob(string const & glob)
349 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
350 // "abc,def,ghi" as group 2, while allowing spaces in group 2.
351 static lyx::regex const glob_re(" *([^ {]*)\\{([^}]+)\\}");
352 // Matches "abc" and "abc,", storing "abc" as group 1,
353 // while ignoring surrounding spaces.
354 static lyx::regex const block_re(" *([^ ,}]+) *,? *");
358 string::const_iterator it = glob.begin();
359 string::const_iterator const end = glob.end();
361 match_results<string::const_iterator> what;
362 if (!regex_search(it, end, what, glob_re)) {
363 // Ensure that no information is lost.
364 pattern += string(it, end);
368 // Everything from the start of the input to
369 // the start of the match.
370 pattern += string(what[-1].first, what[-1].second);
372 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
373 string const head = string(what[1].first, what[1].second);
374 string const tail = string(what[2].first, what[2].second);
376 // Split the ','-separated chunks of tail so that
377 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
378 string const fmt = " " + head + "$1";
379 pattern += regex_replace(tail, block_re, fmt);
381 // Increment the iterator to the end of the match.
382 it += distance(it, what[0].second);
391 /* \param description text describing the filters.
392 * \param one or more wildcard patterns, separated by
395 Filter(docstring const & description, std::string const & globs);
397 docstring const & description() const { return desc_; }
399 QString toString() const;
402 std::vector<std::string> globs_;
406 Filter::Filter(docstring const & description, string const & globs)
409 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
410 boost::char_separator<char> const separator(" ");
412 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
413 // "<glob> <glob> ... *.abc *.def <glob>"
414 string const expanded_globs = convert_brace_glob(globs);
416 // Split into individual globs.
417 vector<string> matches;
418 Tokenizer const tokens(expanded_globs, separator);
419 globs_ = vector<string>(tokens.begin(), tokens.end());
423 QString Filter::toString() const
427 bool const has_description = !desc_.empty();
429 if (has_description) {
434 for (size_t i = 0; i != globs_.size(); ++i) {
437 s += toqstr(globs_[i]);
446 /** \c FileFilterList parses a Qt-style list of available file filters
447 * to generate the corresponding vector.
448 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
449 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
450 * will result in a vector of size 1 in which the description field is empty.
452 struct FileFilterList
454 // FIXME UNICODE: globs_ should be unicode...
455 /** \param qt_style_filter a list of available file filters.
456 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
457 * The "All files (*)" filter is always added to the list.
459 explicit FileFilterList(docstring const & qt_style_filter =
462 typedef std::vector<Filter>::size_type size_type;
464 bool empty() const { return filters_.empty(); }
465 size_type size() const { return filters_.size(); }
466 Filter & operator[](size_type i) { return filters_[i]; }
467 Filter const & operator[](size_type i) const { return filters_[i]; }
469 void parse_filter(std::string const & filter);
470 std::vector<Filter> filters_;
474 FileFilterList::FileFilterList(docstring const & qt_style_filter)
477 string const filter = to_utf8(qt_style_filter)
478 + (qt_style_filter.empty() ? string() : ";;")
479 + to_utf8(_("All Files "))
486 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
487 // into individual filters.
488 static lyx::regex const separator_re(";;");
490 string::const_iterator it = filter.begin();
491 string::const_iterator const end = filter.end();
493 match_results<string::const_iterator> what;
495 if (!lyx::regex_search(it, end, what, separator_re)) {
496 parse_filter(string(it, end));
500 // Everything from the start of the input to
501 // the start of the match.
502 parse_filter(string(what[-1].first, what[-1].second));
504 // Increment the iterator to the end of the match.
505 it += distance(it, what[0].second);
510 void FileFilterList::parse_filter(string const & filter)
512 // Matches "TeX documents (plain) (*.tex)",
513 // storing "TeX documents (plain) " as group 1 and "*.tex" as group 2.
514 static lyx::regex const filter_re("(.*)\\(([^()]+)\\) *$");
516 match_results<string::const_iterator> what;
517 if (!lyx::regex_search(filter, what, filter_re)) {
518 // Just a glob, no description.
519 filters_.push_back(Filter(docstring(), trim(filter)));
522 docstring const desc = from_utf8(string(what[1].first, what[1].second));
523 string const globs = string(what[2].first, what[2].second);
524 filters_.push_back(Filter(trim(desc), trim(globs)));
529 /** \returns the equivalent of the string passed in
530 * although any brace expressions are expanded.
531 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
533 QStringList fileFilters(QString const & desc)
535 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
536 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
537 FileFilterList filters(qstring_to_ucs4(desc));
538 //LYXERR0("DESC: " << desc);
540 for (size_t i = 0; i != filters.filters_.size(); ++i) {
541 QString f = filters.filters_[i].toString();
542 //LYXERR0("FILTER: " << f);
549 QString guiName(string const & type, BufferParams const & bp)
551 if (type == "tableofcontents")
552 return qt_("Table of Contents");
554 return qt_("Child Documents");
555 if (type == "graphics")
556 return qt_("List of Graphics");
557 if (type == "equation")
558 return qt_("List of Equations");
559 if (type == "footnote")
560 return qt_("List of Footnotes");
561 if (type == "listing")
562 return qt_("List of Listings");
564 return qt_("List of Index Entries");
565 if (type == "marginalnote")
566 return qt_("List of Marginal notes");
568 return qt_("List of Notes");
569 if (type == "citation")
570 return qt_("List of Citations");
572 return qt_("Labels and References");
573 if (type == "branch")
574 return qt_("List of Branches");
575 if (type == "change")
576 return qt_("List of Changes");
578 FloatList const & floats = bp.documentClass().floats();
579 if (floats.typeExist(type))
580 return qt_(floats.getType(type).listName());