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/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/PathChanger.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,
63 QString const & ext, search_mode mode)
65 return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext), mode);
69 FileName imageLibFileSearch(QString & dir, QString const & name,
70 QString const & ext, search_mode mode)
72 string tmp = fromqstr(dir);
73 FileName fn = support::imageLibFileSearch(tmp, fromqstr(name), fromqstr(ext), mode);
80 double locstringToDouble(QString const & str)
84 double res = loc.toDouble(str, &ok);
87 QLocale c(QLocale::C);
88 res = c.toDouble(str);
98 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
100 QString const length = input->text();
101 if (length.isEmpty())
104 // Don't return unit-from-choice if the input(field) contains a unit
105 if (isValidGlueLength(fromqstr(length)))
106 return fromqstr(length);
108 Length::UNIT const unit = combo->currentLengthItem();
110 return Length(locstringToDouble(length.trimmed()), unit).asString();
114 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
116 QString const length = input->text();
117 if (length.isEmpty())
120 // don't return unit-from-choice if the input(field) contains a unit
121 if (isValidGlueLength(fromqstr(length)))
122 return Length(fromqstr(length));
124 Length::UNIT unit = Length::UNIT_NONE;
125 QString const item = combo->currentText();
126 for (int i = 0; i < num_units; i++) {
127 if (qt_(lyx::unit_name_gui[i]) == item) {
128 unit = unitFromString(unit_name[i]);
133 return Length(locstringToDouble(length.trimmed()), unit);
137 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
138 Length const & len, Length::UNIT /*defaultUnit*/)
141 // no length (UNIT_NONE)
142 combo->setCurrentItem(Length::defaultUnit());
145 combo->setCurrentItem(len.unit());
147 loc.setNumberOptions(QLocale::OmitGroupSeparator);
148 input->setText(formatLocFPNumber(Length(len).value()));
153 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
154 string const & len, Length::UNIT defaultUnit)
157 // no length (UNIT_NONE)
158 combo->setCurrentItem(defaultUnit);
160 } else if (!isValidLength(len) && !isStrDbl(len)) {
161 // use input field only for gluelengths
162 combo->setCurrentItem(defaultUnit);
163 input->setText(toqstr(len));
165 lengthToWidgets(input, combo, Length(len), defaultUnit);
170 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
171 docstring const & len, Length::UNIT defaultUnit)
173 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
177 double widgetToDouble(QLineEdit const * input)
179 QString const text = input->text();
183 return locstringToDouble(text.trimmed());
187 string widgetToDoubleStr(QLineEdit const * input)
189 return convert<string>(widgetToDouble(input));
193 void doubleToWidget(QLineEdit * input, double const & value, char f, int prec)
196 loc.setNumberOptions(QLocale::OmitGroupSeparator);
197 input->setText(loc.toString(value, f, prec));
201 void doubleToWidget(QLineEdit * input, string const & value, char f, int prec)
203 doubleToWidget(input, convert<double>(value), f, prec);
207 QString formatLocFPNumber(double d)
209 QString result = toqstr(formatFPNumber(d));
211 result.replace('.', loc.decimalPoint());
216 void setValid(QWidget * widget, bool valid)
219 widget->setPalette(QPalette());
221 QPalette pal = widget->palette();
222 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
223 widget->setPalette(pal);
227 /// wrapper to hide the change of method name to setSectionResizeMode
228 void setSectionResizeMode(QHeaderView * view,
229 int logicalIndex, QHeaderView::ResizeMode mode) {
230 #if (QT_VERSION >= 0x050000)
231 view->setSectionResizeMode(logicalIndex, mode);
233 view->setResizeMode(logicalIndex, mode);
237 void setSectionResizeMode(QHeaderView * view, QHeaderView::ResizeMode mode) {
238 #if (QT_VERSION >= 0x050000)
239 view->setSectionResizeMode(mode);
241 view->setResizeMode(mode);
244 } // namespace frontend
246 QString const qt_(char const * str, const char *)
248 return toqstr(_(str));
252 QString const qt_(string const & str)
254 return toqstr(_(str));
258 QString const qt_(QString const & qstr)
260 return toqstr(_(fromqstr(qstr)));
264 void rescanTexStyles(string const & arg)
266 // Run rescan in user lyx directory
267 PathChanger p(package().user_support());
268 FileName const prog = support::libFileSearch("scripts", "TeXFiles.py");
270 string const command = os::python() + ' ' +
271 quoteName(prog.toFilesystemEncoding()) + ' ' +
273 int const status = one.startscript(Systemcall::Wait, command);
277 frontend::Alert::error(_("Could not update TeX information"),
278 bformat(_("The script `%1$s' failed."), from_utf8(prog.absFileName())));
282 QStringList texFileList(QString const & filename)
285 FileName const file = libFileSearch(QString(), filename);
290 vector<docstring> doclist =
291 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
293 // Normalise paths like /foo//bar ==> /foo/bar
295 for (size_t i = 0; i != doclist.size(); ++i) {
296 QString file = toqstr(doclist[i]);
297 file.replace("\r", "");
298 while (file.contains("//"))
299 file.replace("//", "/");
305 return QList<QString>::fromSet(set);
308 QString const externalLineEnding(docstring const & str)
311 // The MAC clipboard uses \r for lineendings, and we use \n
312 return toqstr(subst(str, '\n', '\r'));
313 #elif defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
314 // Windows clipboard uses \r\n for lineendings, and we use \n
315 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
322 docstring const internalLineEnding(QString const & str)
324 docstring const s = subst(qstring_to_ucs4(str),
325 from_ascii("\r\n"), from_ascii("\n"));
326 return subst(s, '\r', '\n');
330 QString internalPath(const QString & str)
332 return toqstr(os::internal_path(fromqstr(str)));
336 QString onlyFileName(const QString & str)
338 return toqstr(support::onlyFileName(fromqstr(str)));
342 QString onlyPath(const QString & str)
344 return toqstr(support::onlyPath(fromqstr(str)));
348 QString changeExtension(QString const & oldname, QString const & ext)
350 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
353 /// Remove the extension from \p name
354 QString removeExtension(QString const & name)
356 return toqstr(support::removeExtension(fromqstr(name)));
359 /** Add the extension \p ext to \p name.
360 Use this instead of changeExtension if you know that \p name is without
361 extension, because changeExtension would wrongly interpret \p name if it
364 QString addExtension(QString const & name, QString const & ext)
366 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
369 /// Return the extension of the file (not including the .)
370 QString getExtension(QString const & name)
372 return toqstr(support::getExtension(fromqstr(name)));
376 /** Convert relative path into absolute path based on a basepath.
377 If relpath is absolute, just use that.
378 If basepath doesn't exist use CWD.
380 QString makeAbsPath(QString const & relpath, QString const & base)
382 return toqstr(support::makeAbsPath(fromqstr(relpath),
383 fromqstr(base)).absFileName());
387 /////////////////////////////////////////////////////////////////////////
391 /////////////////////////////////////////////////////////////////////////
393 /** Given a string such as
394 * "<glob> <glob> ... *.{abc,def} <glob>",
395 * convert the csh-style brace expresions:
396 * "<glob> <glob> ... *.abc *.def <glob>".
397 * Requires no system support, so should work equally on Unix, Mac, Win32.
399 static string const convert_brace_glob(string const & glob)
401 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
402 // "abc,def,ghi" as group 2, while allowing spaces in group 2.
403 static lyx::regex const glob_re(" *([^ {]*)\\{([^}]+)\\}");
404 // Matches "abc" and "abc,", storing "abc" as group 1,
405 // while ignoring surrounding spaces.
406 static lyx::regex const block_re(" *([^ ,}]+) *,? *");
410 string::const_iterator it = glob.begin();
411 string::const_iterator const end = glob.end();
413 match_results<string::const_iterator> what;
414 if (!regex_search(it, end, what, glob_re)) {
415 // Ensure that no information is lost.
416 pattern += string(it, end);
420 // Everything from the start of the input to
421 // the start of the match.
422 pattern += string(what[-1].first, what[-1].second);
424 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
425 string const head = string(what[1].first, what[1].second);
426 string const tail = string(what[2].first, what[2].second);
428 // Split the ','-separated chunks of tail so that
429 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
430 string const fmt = " " + head + "$1";
431 pattern += regex_replace(tail, block_re, fmt);
433 // Increment the iterator to the end of the match.
434 it += distance(it, what[0].second);
443 /* \param description text describing the filters.
444 * \param one or more wildcard patterns, separated by
447 Filter(docstring const & description, std::string const & globs);
449 docstring const & description() const { return desc_; }
451 QString toString() const;
454 std::vector<std::string> globs_;
458 Filter::Filter(docstring const & description, string const & globs)
461 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
462 boost::char_separator<char> const separator(" ");
464 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
465 // "<glob> <glob> ... *.abc *.def <glob>"
466 string const expanded_globs = convert_brace_glob(globs);
468 // Split into individual globs.
469 Tokenizer const tokens(expanded_globs, separator);
470 globs_ = vector<string>(tokens.begin(), tokens.end());
474 QString Filter::toString() const
478 bool const has_description = !desc_.empty();
480 if (has_description) {
485 for (size_t i = 0; i != globs_.size(); ++i) {
488 s += toqstr(globs_[i]);
497 /** \c FileFilterList parses a Qt-style list of available file filters
498 * to generate the corresponding vector.
499 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
500 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
501 * will result in a vector of size 1 in which the description field is empty.
503 struct FileFilterList
505 // FIXME UNICODE: globs_ should be unicode...
506 /** \param qt_style_filter a list of available file filters.
507 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
508 * The "All files (*)" filter is always added to the list.
510 explicit FileFilterList(docstring const & qt_style_filter =
513 typedef std::vector<Filter>::size_type size_type;
515 bool empty() const { return filters_.empty(); }
516 size_type size() const { return filters_.size(); }
517 Filter & operator[](size_type i) { return filters_[i]; }
518 Filter const & operator[](size_type i) const { return filters_[i]; }
520 void parse_filter(std::string const & filter);
521 std::vector<Filter> filters_;
525 FileFilterList::FileFilterList(docstring const & qt_style_filter)
528 string const filter = to_utf8(qt_style_filter)
529 + (qt_style_filter.empty() ? string() : ";;")
530 + to_utf8(_("All Files "))
537 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
538 // into individual filters.
539 static lyx::regex const separator_re(";;");
541 string::const_iterator it = filter.begin();
542 string::const_iterator const end = filter.end();
544 match_results<string::const_iterator> what;
546 if (!lyx::regex_search(it, end, what, separator_re)) {
547 parse_filter(string(it, end));
551 // Everything from the start of the input to
552 // the start of the match.
553 parse_filter(string(what[-1].first, what[-1].second));
555 // Increment the iterator to the end of the match.
556 it += distance(it, what[0].second);
561 void FileFilterList::parse_filter(string const & filter)
563 // Matches "TeX documents (plain) (*.tex)",
564 // storing "TeX documents (plain) " as group 1 and "*.tex" as group 2.
565 static lyx::regex const filter_re("(.*)\\(([^()]+)\\) *$");
567 match_results<string::const_iterator> what;
568 if (!lyx::regex_search(filter, what, filter_re)) {
569 // Just a glob, no description.
570 filters_.push_back(Filter(docstring(), trim(filter)));
573 docstring const desc = from_utf8(string(what[1].first, what[1].second));
574 string const globs = string(what[2].first, what[2].second);
575 filters_.push_back(Filter(trim(desc), trim(globs)));
580 /** \returns the equivalent of the string passed in
581 * although any brace expressions are expanded.
582 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
584 QStringList fileFilters(QString const & desc)
586 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
587 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
588 FileFilterList filters(qstring_to_ucs4(desc));
589 //LYXERR0("DESC: " << desc);
591 for (size_t i = 0; i != filters.filters_.size(); ++i) {
592 QString f = filters.filters_[i].toString();
593 //LYXERR0("FILTER: " << f);
600 QString guiName(string const & type, BufferParams const & bp)
602 if (type == "tableofcontents")
603 return qt_("Table of Contents");
605 return qt_("Child Documents");
606 if (type == "graphics")
607 return qt_("Graphics");
608 if (type == "equation")
609 return qt_("Equations");
610 if (type == "external")
611 return qt_("External material");
612 if (type == "footnote")
613 return qt_("Footnotes");
614 if (type == "listing")
615 return qt_("Listings");
617 return qt_("Index Entries");
618 if (type == "marginalnote")
619 return qt_("Marginal notes");
620 if (type == "math-macro")
621 return qt_("Math macros");
622 if (type == "nomencl")
623 return qt_("Nomenclature Entries");
626 if (type == "citation")
627 return qt_("Citations");
629 return qt_("Labels and References");
630 if (type == "branch")
631 return qt_("Branches");
632 if (type == "change")
633 return qt_("Changes");
634 if (type == "senseless")
635 return qt_("Senseless");
636 if (prefixIs(type, "index:")) {
637 string const itype = split(type, ':');
638 IndicesList const & indiceslist = bp.indiceslist();
639 Index const * index = indiceslist.findShortcut(from_utf8(itype));
640 docstring indextype = _("unknown type!");
642 indextype = index->index();
643 return toqstr(bformat(_("Index Entries (%1$s)"), indextype));
646 FloatList const & floats = bp.documentClass().floats();
647 if (floats.typeExist(type))
648 return qt_(floats.getType(type).listName());