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"
24 #include "IndicesList.h"
27 #include "TextClass.h"
29 #include "support/convert.h"
30 #include "support/debug.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"
56 using namespace lyx::support;
60 FileName libFileSearch(QString const & dir, QString const & name,
61 QString const & ext, search_mode mode)
63 return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext), mode);
67 FileName imageLibFileSearch(QString & dir, QString const & name,
68 QString const & ext, search_mode mode)
70 string tmp = fromqstr(dir);
71 FileName fn = support::imageLibFileSearch(tmp, fromqstr(name), fromqstr(ext), mode);
78 double locstringToDouble(QString const & str)
82 double res = loc.toDouble(str, &ok);
85 QLocale c(QLocale::C);
86 res = c.toDouble(str);
96 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
98 QString const length = input->text();
102 // Don't return unit-from-choice if the input(field) contains a unit
103 if (isValidGlueLength(fromqstr(length)))
104 return fromqstr(length);
106 Length::UNIT const unit = combo->currentLengthItem();
108 return Length(locstringToDouble(length.trimmed()), unit).asString();
112 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
114 QString const length = input->text();
115 if (length.isEmpty())
118 // don't return unit-from-choice if the input(field) contains a unit
119 if (isValidGlueLength(fromqstr(length)))
120 return Length(fromqstr(length));
122 Length::UNIT unit = Length::UNIT_NONE;
123 QString const item = combo->currentText();
124 for (int i = 0; i < num_units; i++) {
125 if (qt_(lyx::unit_name_gui[i]) == item) {
126 unit = unitFromString(unit_name[i]);
131 return Length(locstringToDouble(length.trimmed()), unit);
135 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
136 Length const & len, Length::UNIT /*defaultUnit*/)
139 // no length (UNIT_NONE)
140 combo->setCurrentItem(Length::defaultUnit());
143 combo->setCurrentItem(len.unit());
145 loc.setNumberOptions(QLocale::OmitGroupSeparator);
146 input->setText(formatLocFPNumber(Length(len).value()));
151 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
152 string const & len, Length::UNIT defaultUnit)
155 // no length (UNIT_NONE)
156 combo->setCurrentItem(defaultUnit);
158 } else if (!isValidLength(len) && !isStrDbl(len)) {
159 // use input field only for gluelengths
160 combo->setCurrentItem(defaultUnit);
161 input->setText(toqstr(len));
163 lengthToWidgets(input, combo, Length(len), defaultUnit);
168 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
169 docstring const & len, Length::UNIT defaultUnit)
171 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
175 double widgetToDouble(QLineEdit const * input)
177 QString const text = input->text();
181 return locstringToDouble(text.trimmed());
185 string widgetToDoubleStr(QLineEdit const * input)
187 return convert<string>(widgetToDouble(input));
191 void doubleToWidget(QLineEdit * input, double const & value, char f, int prec)
194 loc.setNumberOptions(QLocale::OmitGroupSeparator);
195 input->setText(loc.toString(value, f, prec));
199 void doubleToWidget(QLineEdit * input, string const & value, char f, int prec)
201 doubleToWidget(input, convert<double>(value), f, prec);
205 QString formatLocFPNumber(double d)
207 QString result = toqstr(formatFPNumber(d));
209 result.replace('.', loc.decimalPoint());
214 bool ColorSorter(ColorCode lhs, ColorCode rhs)
216 return compare_no_case(lcolor.getGUIName(lhs), lcolor.getGUIName(rhs)) < 0;
220 void setValid(QWidget * widget, bool valid)
223 widget->setPalette(QPalette());
225 QPalette pal = widget->palette();
226 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
227 widget->setPalette(pal);
231 /// wrapper to hide the change of method name to setSectionResizeMode
232 void setSectionResizeMode(QHeaderView * view,
233 int logicalIndex, QHeaderView::ResizeMode mode) {
234 #if (QT_VERSION >= 0x050000)
235 view->setSectionResizeMode(logicalIndex, mode);
237 view->setResizeMode(logicalIndex, mode);
241 void setSectionResizeMode(QHeaderView * view, QHeaderView::ResizeMode mode) {
242 #if (QT_VERSION >= 0x050000)
243 view->setSectionResizeMode(mode);
245 view->setResizeMode(mode);
248 } // namespace frontend
250 QString const qt_(char const * str, const char *)
252 return toqstr(_(str));
256 QString const qt_(string const & str)
258 return toqstr(_(str));
262 QString const qt_(QString const & qstr)
264 return toqstr(_(fromqstr(qstr)));
268 void rescanTexStyles(string const & arg)
270 // Run rescan in user lyx directory
271 PathChanger p(package().user_support());
272 FileName const prog = support::libFileSearch("scripts", "TeXFiles.py");
274 string const command = os::python() + ' ' +
275 quoteName(prog.toFilesystemEncoding()) + ' ' +
277 int const status = one.startscript(Systemcall::Wait, command);
281 frontend::Alert::error(_("Could not update TeX information"),
282 bformat(_("The script `%1$s' failed."), from_utf8(prog.absFileName())));
286 QStringList texFileList(QString const & filename)
289 FileName const file = libFileSearch(QString(), filename);
294 vector<docstring> doclist =
295 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
297 // Normalise paths like /foo//bar ==> /foo/bar
299 for (size_t i = 0; i != doclist.size(); ++i) {
300 QString file = toqstr(doclist[i]);
301 file.replace("\r", "");
302 while (file.contains("//"))
303 file.replace("//", "/");
309 return QList<QString>::fromSet(set);
312 QString const externalLineEnding(docstring const & str)
315 // The MAC clipboard uses \r for lineendings, and we use \n
316 return toqstr(subst(str, '\n', '\r'));
317 #elif defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
318 // Windows clipboard uses \r\n for lineendings, and we use \n
319 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
326 docstring const internalLineEnding(QString const & str)
328 docstring const s = subst(qstring_to_ucs4(str),
329 from_ascii("\r\n"), from_ascii("\n"));
330 return subst(s, '\r', '\n');
334 QString internalPath(const QString & str)
336 return toqstr(os::internal_path(fromqstr(str)));
340 QString onlyFileName(const QString & str)
342 return toqstr(support::onlyFileName(fromqstr(str)));
346 QString onlyPath(const QString & str)
348 return toqstr(support::onlyPath(fromqstr(str)));
352 QString changeExtension(QString const & oldname, QString const & ext)
354 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
357 /// Remove the extension from \p name
358 QString removeExtension(QString const & name)
360 return toqstr(support::removeExtension(fromqstr(name)));
363 /** Add the extension \p ext to \p name.
364 Use this instead of changeExtension if you know that \p name is without
365 extension, because changeExtension would wrongly interpret \p name if it
368 QString addExtension(QString const & name, QString const & ext)
370 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
373 /// Return the extension of the file (not including the .)
374 QString getExtension(QString const & name)
376 return toqstr(support::getExtension(fromqstr(name)));
380 /** Convert relative path into absolute path based on a basepath.
381 If relpath is absolute, just use that.
382 If basepath doesn't exist use CWD.
384 QString makeAbsPath(QString const & relpath, QString const & base)
386 return toqstr(support::makeAbsPath(fromqstr(relpath),
387 fromqstr(base)).absFileName());
391 /////////////////////////////////////////////////////////////////////////
395 /////////////////////////////////////////////////////////////////////////
397 /** Given a string such as
398 * "<glob> <glob> ... *.{abc,def} <glob>",
399 * convert the csh-style brace expresions:
400 * "<glob> <glob> ... *.abc *.def <glob>".
401 * Requires no system support, so should work equally on Unix, Mac, Win32.
403 static string const convert_brace_glob(string const & glob)
405 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
406 // "abc,def,ghi" as group 2, while allowing spaces in group 2.
407 static lyx::regex const glob_re(" *([^ {]*)\\{([^}]+)\\}");
408 // Matches "abc" and "abc,", storing "abc" as group 1,
409 // while ignoring surrounding spaces.
410 static lyx::regex const block_re(" *([^ ,}]+) *,? *");
414 string::const_iterator it = glob.begin();
415 string::const_iterator const end = glob.end();
417 match_results<string::const_iterator> what;
418 if (!regex_search(it, end, what, glob_re)) {
419 // Ensure that no information is lost.
420 pattern += string(it, end);
424 // Everything from the start of the input to
425 // the start of the match.
426 pattern += string(what[-1].first, what[-1].second);
428 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
429 string const head = string(what[1].first, what[1].second);
430 string const tail = string(what[2].first, what[2].second);
432 // Split the ','-separated chunks of tail so that
433 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
434 string const fmt = " " + head + "$1";
435 pattern += regex_replace(tail, block_re, fmt);
437 // Increment the iterator to the end of the match.
438 it += distance(it, what[0].second);
447 /* \param description text describing the filters.
448 * \param one or more wildcard patterns, separated by
451 Filter(docstring const & description, std::string const & globs);
453 docstring const & description() const { return desc_; }
455 QString toString() const;
458 std::vector<std::string> globs_;
462 Filter::Filter(docstring const & description, string const & globs)
465 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
466 // "<glob> <glob> ... *.abc *.def <glob>"
467 string const expanded_globs = convert_brace_glob(globs);
469 // Split into individual globs.
470 globs_ = getVectorFromString(expanded_globs, " ");
474 QString Filter::toString() const
478 bool const has_description = !desc_.empty();
480 if (has_description) {
485 s += toqstr(getStringFromVector(globs_, " "));
493 /** \c FileFilterList parses a Qt-style list of available file filters
494 * to generate the corresponding vector.
495 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
496 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
497 * will result in a vector of size 1 in which the description field is empty.
499 struct FileFilterList
501 // FIXME UNICODE: globs_ should be unicode...
502 /** \param qt_style_filter a list of available file filters.
503 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
504 * The "All files (*)" filter is always added to the list.
506 explicit FileFilterList(docstring const & qt_style_filter =
509 typedef std::vector<Filter>::size_type size_type;
511 bool empty() const { return filters_.empty(); }
512 size_type size() const { return filters_.size(); }
513 Filter & operator[](size_type i) { return filters_[i]; }
514 Filter const & operator[](size_type i) const { return filters_[i]; }
516 void parse_filter(std::string const & filter);
517 std::vector<Filter> filters_;
521 FileFilterList::FileFilterList(docstring const & qt_style_filter)
524 string const filter = to_utf8(qt_style_filter)
525 + (qt_style_filter.empty() ? string() : ";;")
526 + to_utf8(_("All Files "))
533 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
534 // into individual filters.
535 static lyx::regex const separator_re(";;");
537 string::const_iterator it = filter.begin();
538 string::const_iterator const end = filter.end();
540 match_results<string::const_iterator> what;
542 if (!lyx::regex_search(it, end, what, separator_re)) {
543 parse_filter(string(it, end));
547 // Everything from the start of the input to
548 // the start of the match.
549 parse_filter(string(what[-1].first, what[-1].second));
551 // Increment the iterator to the end of the match.
552 it += distance(it, what[0].second);
557 void FileFilterList::parse_filter(string const & filter)
559 // Matches "TeX documents (plain) (*.tex)",
560 // storing "TeX documents (plain) " as group 1 and "*.tex" as group 2.
561 static lyx::regex const filter_re("(.*)\\(([^()]+)\\) *$");
563 match_results<string::const_iterator> what;
564 if (!lyx::regex_search(filter, what, filter_re)) {
565 // Just a glob, no description.
566 filters_.push_back(Filter(docstring(), trim(filter)));
569 docstring const desc = from_utf8(string(what[1].first, what[1].second));
570 string const globs = string(what[2].first, what[2].second);
571 filters_.push_back(Filter(trim(desc), trim(globs)));
576 /** \returns the equivalent of the string passed in
577 * although any brace expressions are expanded.
578 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
580 QStringList fileFilters(QString const & desc)
582 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
583 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
584 FileFilterList filters(qstring_to_ucs4(desc));
585 //LYXERR0("DESC: " << desc);
587 for (size_t i = 0; i != filters.filters_.size(); ++i) {
588 QString f = filters.filters_[i].toString();
589 //LYXERR0("FILTER: " << f);
596 QString guiName(string const & type, BufferParams const & bp)
598 if (type == "tableofcontents")
599 return qt_("Table of Contents");
601 return qt_("Child Documents");
602 if (type == "graphics")
603 return qt_("Graphics");
604 if (type == "equation")
605 return qt_("Equations");
606 if (type == "external")
607 return qt_("External material");
608 if (type == "footnote")
609 return qt_("Footnotes");
610 if (type == "listing")
611 return qt_("Listings");
613 return qt_("Index Entries");
614 if (type == "marginalnote")
615 return qt_("Marginal notes");
616 if (type == "math-macro")
617 return qt_("Math macros");
618 if (type == "nomencl")
619 return qt_("Nomenclature Entries");
622 if (type == "citation")
623 return qt_("Citations");
625 return qt_("Labels and References");
626 if (type == "branch")
627 return qt_("Branches");
628 if (type == "change")
629 return qt_("Changes");
630 if (type == "senseless")
631 return qt_("Senseless");
632 if (prefixIs(type, "index:")) {
633 string const itype = split(type, ':');
634 IndicesList const & indiceslist = bp.indiceslist();
635 Index const * index = indiceslist.findShortcut(from_utf8(itype));
636 docstring indextype = _("unknown type!");
638 indextype = index->index();
639 return toqstr(bformat(_("Index Entries (%1$s)"), indextype));
642 FloatList const & floats = bp.documentClass().floats();
643 if (floats.typeExist(type))
644 return qt_(floats.getType(type).listName());