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/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/PathChanger.h"
37 #include "support/Systemcall.h"
52 #include "support/regex.h"
53 #include <boost/tokenizer.hpp>
57 using namespace lyx::support;
61 FileName libFileSearch(QString const & dir, QString const & name,
62 QString const & ext, search_mode mode)
64 return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext), mode);
68 FileName imageLibFileSearch(QString & dir, QString const & name,
69 QString const & ext, search_mode mode)
71 string tmp = fromqstr(dir);
72 FileName fn = support::imageLibFileSearch(tmp, fromqstr(name), fromqstr(ext), mode);
79 double locstringToDouble(QString const & str)
83 double res = loc.toDouble(str, &ok);
86 QLocale c(QLocale::C);
87 res = c.toDouble(str);
97 string widgetsToLength(QLineEdit const * input, LengthCombo 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 fromqstr(length);
107 Length::UNIT const unit = combo->currentLengthItem();
109 return Length(locstringToDouble(length.trimmed()), unit).asString();
113 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
115 QString const length = input->text();
116 if (length.isEmpty())
119 // don't return unit-from-choice if the input(field) contains a unit
120 if (isValidGlueLength(fromqstr(length)))
121 return Length(fromqstr(length));
123 Length::UNIT unit = Length::UNIT_NONE;
124 QString const item = combo->currentText();
125 for (int i = 0; i < num_units; i++) {
126 if (qt_(lyx::unit_name_gui[i]) == item) {
127 unit = unitFromString(unit_name[i]);
132 return Length(locstringToDouble(length.trimmed()), unit);
136 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
137 Length const & len, Length::UNIT /*defaultUnit*/)
140 // no length (UNIT_NONE)
141 combo->setCurrentItem(Length::defaultUnit());
144 combo->setCurrentItem(len.unit());
146 loc.setNumberOptions(QLocale::OmitGroupSeparator);
147 input->setText(loc.toString(Length(len).value()));
152 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
153 string const & len, Length::UNIT defaultUnit)
156 // no length (UNIT_NONE)
157 combo->setCurrentItem(defaultUnit);
159 } else if (!isValidLength(len) && !isStrDbl(len)) {
160 // use input field only for gluelengths
161 combo->setCurrentItem(defaultUnit);
162 input->setText(toqstr(len));
164 lengthToWidgets(input, combo, Length(len), defaultUnit);
169 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
170 docstring const & len, Length::UNIT defaultUnit)
172 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
176 double widgetToDouble(QLineEdit const * input)
178 QString const text = input->text();
182 return locstringToDouble(text.trimmed());
186 string widgetToDoubleStr(QLineEdit const * input)
188 return convert<string>(widgetToDouble(input));
192 void doubleToWidget(QLineEdit * input, double const & value, char f, int prec)
195 loc.setNumberOptions(QLocale::OmitGroupSeparator);
196 input->setText(loc.toString(value, f, prec));
200 void doubleToWidget(QLineEdit * input, string const & value, char f, int prec)
202 doubleToWidget(input, convert<double>(value), f, prec);
206 void setValid(QWidget * widget, bool valid)
209 widget->setPalette(QPalette());
211 QPalette pal = widget->palette();
212 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
213 widget->setPalette(pal);
217 /// wrapper to hide the change of method name to setSectionResizeMode
218 void setSectionResizeMode(QHeaderView * view,
219 int logicalIndex, QHeaderView::ResizeMode mode) {
220 #if (QT_VERSION >= 0x050000)
221 view->setSectionResizeMode(logicalIndex, mode);
223 view->setResizeMode(logicalIndex, mode);
227 void setSectionResizeMode(QHeaderView * view, QHeaderView::ResizeMode mode) {
228 #if (QT_VERSION >= 0x050000)
229 view->setSectionResizeMode(mode);
231 view->setResizeMode(mode);
234 } // namespace frontend
236 QString const qt_(char const * str, const char *)
238 return toqstr(_(str));
242 QString const qt_(string const & str)
244 return toqstr(_(str));
248 QString const qt_(QString const & qstr)
250 return toqstr(_(fromqstr(qstr)));
254 void rescanTexStyles(string const & arg)
256 // Run rescan in user lyx directory
257 PathChanger p(package().user_support());
258 FileName const prog = support::libFileSearch("scripts", "TeXFiles.py");
260 string const command = os::python() + ' ' +
261 quoteName(prog.toFilesystemEncoding()) + ' ' +
263 int const status = one.startscript(Systemcall::Wait, command);
267 frontend::Alert::error(_("Could not update TeX information"),
268 bformat(_("The script `%1$s' failed."), from_utf8(prog.absFileName())));
272 QStringList texFileList(QString const & filename)
275 FileName const file = libFileSearch(QString(), filename);
280 vector<docstring> doclist =
281 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
283 // Normalise paths like /foo//bar ==> /foo/bar
285 for (size_t i = 0; i != doclist.size(); ++i) {
286 QString file = toqstr(doclist[i]);
287 file.replace("\r", "");
288 while (file.contains("//"))
289 file.replace("//", "/");
295 return QList<QString>::fromSet(set);
298 QString const externalLineEnding(docstring const & str)
301 // The MAC clipboard uses \r for lineendings, and we use \n
302 return toqstr(subst(str, '\n', '\r'));
303 #elif defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
304 // Windows clipboard uses \r\n for lineendings, and we use \n
305 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
312 docstring const internalLineEnding(QString const & str)
314 docstring const s = subst(qstring_to_ucs4(str),
315 from_ascii("\r\n"), from_ascii("\n"));
316 return subst(s, '\r', '\n');
320 QString internalPath(const QString & str)
322 return toqstr(os::internal_path(fromqstr(str)));
326 QString onlyFileName(const QString & str)
328 return toqstr(support::onlyFileName(fromqstr(str)));
332 QString onlyPath(const QString & str)
334 return toqstr(support::onlyPath(fromqstr(str)));
338 QString changeExtension(QString const & oldname, QString const & ext)
340 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
343 /// Remove the extension from \p name
344 QString removeExtension(QString const & name)
346 return toqstr(support::removeExtension(fromqstr(name)));
349 /** Add the extension \p ext to \p name.
350 Use this instead of changeExtension if you know that \p name is without
351 extension, because changeExtension would wrongly interpret \p name if it
354 QString addExtension(QString const & name, QString const & ext)
356 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
359 /// Return the extension of the file (not including the .)
360 QString getExtension(QString const & name)
362 return toqstr(support::getExtension(fromqstr(name)));
366 /** Convert relative path into absolute path based on a basepath.
367 If relpath is absolute, just use that.
368 If basepath doesn't exist use CWD.
370 QString makeAbsPath(QString const & relpath, QString const & base)
372 return toqstr(support::makeAbsPath(fromqstr(relpath),
373 fromqstr(base)).absFileName());
377 /////////////////////////////////////////////////////////////////////////
381 /////////////////////////////////////////////////////////////////////////
383 /** Given a string such as
384 * "<glob> <glob> ... *.{abc,def} <glob>",
385 * convert the csh-style brace expresions:
386 * "<glob> <glob> ... *.abc *.def <glob>".
387 * Requires no system support, so should work equally on Unix, Mac, Win32.
389 static string const convert_brace_glob(string const & glob)
391 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
392 // "abc,def,ghi" as group 2, while allowing spaces in group 2.
393 static lyx::regex const glob_re(" *([^ {]*)\\{([^}]+)\\}");
394 // Matches "abc" and "abc,", storing "abc" as group 1,
395 // while ignoring surrounding spaces.
396 static lyx::regex const block_re(" *([^ ,}]+) *,? *");
400 string::const_iterator it = glob.begin();
401 string::const_iterator const end = glob.end();
403 match_results<string::const_iterator> what;
404 if (!regex_search(it, end, what, glob_re)) {
405 // Ensure that no information is lost.
406 pattern += string(it, end);
410 // Everything from the start of the input to
411 // the start of the match.
412 pattern += string(what[-1].first, what[-1].second);
414 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
415 string const head = string(what[1].first, what[1].second);
416 string const tail = string(what[2].first, what[2].second);
418 // Split the ','-separated chunks of tail so that
419 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
420 string const fmt = " " + head + "$1";
421 pattern += regex_replace(tail, block_re, fmt);
423 // Increment the iterator to the end of the match.
424 it += distance(it, what[0].second);
433 /* \param description text describing the filters.
434 * \param one or more wildcard patterns, separated by
437 Filter(docstring const & description, std::string const & globs);
439 docstring const & description() const { return desc_; }
441 QString toString() const;
444 std::vector<std::string> globs_;
448 Filter::Filter(docstring const & description, string const & globs)
451 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
452 boost::char_separator<char> const separator(" ");
454 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
455 // "<glob> <glob> ... *.abc *.def <glob>"
456 string const expanded_globs = convert_brace_glob(globs);
458 // Split into individual globs.
459 vector<string> matches;
460 Tokenizer const tokens(expanded_globs, separator);
461 globs_ = vector<string>(tokens.begin(), tokens.end());
465 QString Filter::toString() const
469 bool const has_description = !desc_.empty();
471 if (has_description) {
476 for (size_t i = 0; i != globs_.size(); ++i) {
479 s += toqstr(globs_[i]);
488 /** \c FileFilterList parses a Qt-style list of available file filters
489 * to generate the corresponding vector.
490 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
491 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
492 * will result in a vector of size 1 in which the description field is empty.
494 struct FileFilterList
496 // FIXME UNICODE: globs_ should be unicode...
497 /** \param qt_style_filter a list of available file filters.
498 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
499 * The "All files (*)" filter is always added to the list.
501 explicit FileFilterList(docstring const & qt_style_filter =
504 typedef std::vector<Filter>::size_type size_type;
506 bool empty() const { return filters_.empty(); }
507 size_type size() const { return filters_.size(); }
508 Filter & operator[](size_type i) { return filters_[i]; }
509 Filter const & operator[](size_type i) const { return filters_[i]; }
511 void parse_filter(std::string const & filter);
512 std::vector<Filter> filters_;
516 FileFilterList::FileFilterList(docstring const & qt_style_filter)
519 string const filter = to_utf8(qt_style_filter)
520 + (qt_style_filter.empty() ? string() : ";;")
521 + to_utf8(_("All Files "))
528 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
529 // into individual filters.
530 static lyx::regex const separator_re(";;");
532 string::const_iterator it = filter.begin();
533 string::const_iterator const end = filter.end();
535 match_results<string::const_iterator> what;
537 if (!lyx::regex_search(it, end, what, separator_re)) {
538 parse_filter(string(it, end));
542 // Everything from the start of the input to
543 // the start of the match.
544 parse_filter(string(what[-1].first, what[-1].second));
546 // Increment the iterator to the end of the match.
547 it += distance(it, what[0].second);
552 void FileFilterList::parse_filter(string const & filter)
554 // Matches "TeX documents (plain) (*.tex)",
555 // storing "TeX documents (plain) " as group 1 and "*.tex" as group 2.
556 static lyx::regex const filter_re("(.*)\\(([^()]+)\\) *$");
558 match_results<string::const_iterator> what;
559 if (!lyx::regex_search(filter, what, filter_re)) {
560 // Just a glob, no description.
561 filters_.push_back(Filter(docstring(), trim(filter)));
564 docstring const desc = from_utf8(string(what[1].first, what[1].second));
565 string const globs = string(what[2].first, what[2].second);
566 filters_.push_back(Filter(trim(desc), trim(globs)));
571 /** \returns the equivalent of the string passed in
572 * although any brace expressions are expanded.
573 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
575 QStringList fileFilters(QString const & desc)
577 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
578 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
579 FileFilterList filters(qstring_to_ucs4(desc));
580 //LYXERR0("DESC: " << desc);
582 for (size_t i = 0; i != filters.filters_.size(); ++i) {
583 QString f = filters.filters_[i].toString();
584 //LYXERR0("FILTER: " << f);
591 QString guiName(string const & type, BufferParams const & bp)
593 if (type == "tableofcontents")
594 return qt_("Table of Contents");
596 return qt_("Child Documents");
597 if (type == "graphics")
598 return qt_("Graphics");
599 if (type == "equation")
600 return qt_("Equations");
601 if (type == "footnote")
602 return qt_("Footnotes");
603 if (type == "listing")
604 return qt_("Listings");
606 return qt_("Index Entries");
607 if (type == "marginalnote")
608 return qt_("Marginal notes");
609 if (type == "nomencl")
610 return qt_("Nomenclature Entries");
613 if (type == "citation")
614 return qt_("Citations");
616 return qt_("Labels and References");
617 if (type == "branch")
618 return qt_("Branches");
619 if (type == "change")
620 return qt_("Changes");
622 FloatList const & floats = bp.documentClass().floats();
623 if (floats.typeExist(type))
624 return qt_(floats.getType(type).listName());