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/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,
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*/)
124 // no length (UNIT_NONE)
125 combo->setCurrentItem(Length::defaultUnit());
128 combo->setCurrentItem(len.unit());
130 loc.setNumberOptions(QLocale::OmitGroupSeparator);
131 input->setText(loc.toString(Length(len).value()));
136 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
137 string const & len, Length::UNIT defaultUnit)
140 // no length (UNIT_NONE)
141 combo->setCurrentItem(defaultUnit);
143 } else if (!isValidLength(len) && !isStrDbl(len)) {
144 // use input field only for gluelengths
145 combo->setCurrentItem(defaultUnit);
146 input->setText(toqstr(len));
148 lengthToWidgets(input, combo, Length(len), defaultUnit);
153 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
154 docstring const & len, Length::UNIT defaultUnit)
156 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
160 double widgetToDouble(QLineEdit const * input)
162 QString const text = input->text();
166 return text.trimmed().toDouble();
170 string widgetToDoubleStr(QLineEdit const * input)
172 QString const text = input->text();
176 return convert<string>(text.trimmed().toDouble());
180 void doubleToWidget(QLineEdit * input, double const & value, char f, int prec)
183 loc.setNumberOptions(QLocale::OmitGroupSeparator);
184 input->setText(loc.toString(value, f, prec));
188 void doubleToWidget(QLineEdit * input, string const & value, char f, int prec)
190 doubleToWidget(input, convert<double>(value), f, prec);
194 void setValid(QWidget * widget, bool valid)
197 widget->setPalette(QPalette());
199 QPalette pal = widget->palette();
200 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
201 widget->setPalette(pal);
205 /// wrapper to hide the change of method name to setSectionResizeMode
206 void setSectionResizeMode(QHeaderView * view,
207 int logicalIndex, QHeaderView::ResizeMode mode) {
208 #if (QT_VERSION >= 0x050000)
209 view->setSectionResizeMode(logicalIndex, mode);
211 view->setResizeMode(logicalIndex, mode);
215 void setSectionResizeMode(QHeaderView * view, QHeaderView::ResizeMode mode) {
216 #if (QT_VERSION >= 0x050000)
217 view->setSectionResizeMode(mode);
219 view->setResizeMode(mode);
222 } // namespace frontend
224 QString const qt_(char const * str, const char *)
226 return toqstr(_(str));
230 QString const qt_(string const & str)
232 return toqstr(_(str));
236 QString const qt_(QString const & qstr)
238 return toqstr(_(fromqstr(qstr)));
242 void rescanTexStyles(string const & arg)
244 // Run rescan in user lyx directory
245 PathChanger p(package().user_support());
246 FileName const prog = support::libFileSearch("scripts", "TeXFiles.py");
248 string const command = os::python() + ' ' +
249 quoteName(prog.toFilesystemEncoding()) + ' ' +
251 int const status = one.startscript(Systemcall::Wait, command);
255 frontend::Alert::error(_("Could not update TeX information"),
256 bformat(_("The script `%1$s' failed."), from_utf8(prog.absFileName())));
260 QStringList texFileList(QString const & filename)
263 FileName const file = libFileSearch(QString(), filename);
268 vector<docstring> doclist =
269 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
271 // Normalise paths like /foo//bar ==> /foo/bar
273 for (size_t i = 0; i != doclist.size(); ++i) {
274 QString file = toqstr(doclist[i]);
275 file.replace("\r", "");
276 while (file.contains("//"))
277 file.replace("//", "/");
283 return QList<QString>::fromSet(set);
286 QString const externalLineEnding(docstring const & str)
289 // The MAC clipboard uses \r for lineendings, and we use \n
290 return toqstr(subst(str, '\n', '\r'));
291 #elif defined(Q_WS_WIN)
292 // Windows clipboard uses \r\n for lineendings, and we use \n
293 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
300 docstring const internalLineEnding(QString const & str)
302 docstring const s = subst(qstring_to_ucs4(str),
303 from_ascii("\r\n"), from_ascii("\n"));
304 return subst(s, '\r', '\n');
308 QString internalPath(const QString & str)
310 return toqstr(os::internal_path(fromqstr(str)));
314 QString onlyFileName(const QString & str)
316 return toqstr(support::onlyFileName(fromqstr(str)));
320 QString onlyPath(const QString & str)
322 return toqstr(support::onlyPath(fromqstr(str)));
326 QString changeExtension(QString const & oldname, QString const & ext)
328 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
331 /// Remove the extension from \p name
332 QString removeExtension(QString const & name)
334 return toqstr(support::removeExtension(fromqstr(name)));
337 /** Add the extension \p ext to \p name.
338 Use this instead of changeExtension if you know that \p name is without
339 extension, because changeExtension would wrongly interpret \p name if it
342 QString addExtension(QString const & name, QString const & ext)
344 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
347 /// Return the extension of the file (not including the .)
348 QString getExtension(QString const & name)
350 return toqstr(support::getExtension(fromqstr(name)));
354 /** Convert relative path into absolute path based on a basepath.
355 If relpath is absolute, just use that.
356 If basepath doesn't exist use CWD.
358 QString makeAbsPath(QString const & relpath, QString const & base)
360 return toqstr(support::makeAbsPath(fromqstr(relpath),
361 fromqstr(base)).absFileName());
365 /////////////////////////////////////////////////////////////////////////
369 /////////////////////////////////////////////////////////////////////////
371 /** Given a string such as
372 * "<glob> <glob> ... *.{abc,def} <glob>",
373 * convert the csh-style brace expresions:
374 * "<glob> <glob> ... *.abc *.def <glob>".
375 * Requires no system support, so should work equally on Unix, Mac, Win32.
377 static string const convert_brace_glob(string const & glob)
379 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
380 // "abc,def,ghi" as group 2, while allowing spaces in group 2.
381 static lyx::regex const glob_re(" *([^ {]*)\\{([^}]+)\\}");
382 // Matches "abc" and "abc,", storing "abc" as group 1,
383 // while ignoring surrounding spaces.
384 static lyx::regex const block_re(" *([^ ,}]+) *,? *");
388 string::const_iterator it = glob.begin();
389 string::const_iterator const end = glob.end();
391 match_results<string::const_iterator> what;
392 if (!regex_search(it, end, what, glob_re)) {
393 // Ensure that no information is lost.
394 pattern += string(it, end);
398 // Everything from the start of the input to
399 // the start of the match.
400 pattern += string(what[-1].first, what[-1].second);
402 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
403 string const head = string(what[1].first, what[1].second);
404 string const tail = string(what[2].first, what[2].second);
406 // Split the ','-separated chunks of tail so that
407 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
408 string const fmt = " " + head + "$1";
409 pattern += regex_replace(tail, block_re, fmt);
411 // Increment the iterator to the end of the match.
412 it += distance(it, what[0].second);
421 /* \param description text describing the filters.
422 * \param one or more wildcard patterns, separated by
425 Filter(docstring const & description, std::string const & globs);
427 docstring const & description() const { return desc_; }
429 QString toString() const;
432 std::vector<std::string> globs_;
436 Filter::Filter(docstring const & description, string const & globs)
439 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
440 boost::char_separator<char> const separator(" ");
442 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
443 // "<glob> <glob> ... *.abc *.def <glob>"
444 string const expanded_globs = convert_brace_glob(globs);
446 // Split into individual globs.
447 vector<string> matches;
448 Tokenizer const tokens(expanded_globs, separator);
449 globs_ = vector<string>(tokens.begin(), tokens.end());
453 QString Filter::toString() const
457 bool const has_description = !desc_.empty();
459 if (has_description) {
464 for (size_t i = 0; i != globs_.size(); ++i) {
467 s += toqstr(globs_[i]);
476 /** \c FileFilterList parses a Qt-style list of available file filters
477 * to generate the corresponding vector.
478 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
479 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
480 * will result in a vector of size 1 in which the description field is empty.
482 struct FileFilterList
484 // FIXME UNICODE: globs_ should be unicode...
485 /** \param qt_style_filter a list of available file filters.
486 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
487 * The "All files (*)" filter is always added to the list.
489 explicit FileFilterList(docstring const & qt_style_filter =
492 typedef std::vector<Filter>::size_type size_type;
494 bool empty() const { return filters_.empty(); }
495 size_type size() const { return filters_.size(); }
496 Filter & operator[](size_type i) { return filters_[i]; }
497 Filter const & operator[](size_type i) const { return filters_[i]; }
499 void parse_filter(std::string const & filter);
500 std::vector<Filter> filters_;
504 FileFilterList::FileFilterList(docstring const & qt_style_filter)
507 string const filter = to_utf8(qt_style_filter)
508 + (qt_style_filter.empty() ? string() : ";;")
509 + to_utf8(_("All Files "))
516 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
517 // into individual filters.
518 static lyx::regex const separator_re(";;");
520 string::const_iterator it = filter.begin();
521 string::const_iterator const end = filter.end();
523 match_results<string::const_iterator> what;
525 if (!lyx::regex_search(it, end, what, separator_re)) {
526 parse_filter(string(it, end));
530 // Everything from the start of the input to
531 // the start of the match.
532 parse_filter(string(what[-1].first, what[-1].second));
534 // Increment the iterator to the end of the match.
535 it += distance(it, what[0].second);
540 void FileFilterList::parse_filter(string const & filter)
542 // Matches "TeX documents (plain) (*.tex)",
543 // storing "TeX documents (plain) " as group 1 and "*.tex" as group 2.
544 static lyx::regex const filter_re("(.*)\\(([^()]+)\\) *$");
546 match_results<string::const_iterator> what;
547 if (!lyx::regex_search(filter, what, filter_re)) {
548 // Just a glob, no description.
549 filters_.push_back(Filter(docstring(), trim(filter)));
552 docstring const desc = from_utf8(string(what[1].first, what[1].second));
553 string const globs = string(what[2].first, what[2].second);
554 filters_.push_back(Filter(trim(desc), trim(globs)));
559 /** \returns the equivalent of the string passed in
560 * although any brace expressions are expanded.
561 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
563 QStringList fileFilters(QString const & desc)
565 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
566 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
567 FileFilterList filters(qstring_to_ucs4(desc));
568 //LYXERR0("DESC: " << desc);
570 for (size_t i = 0; i != filters.filters_.size(); ++i) {
571 QString f = filters.filters_[i].toString();
572 //LYXERR0("FILTER: " << f);
579 QString guiName(string const & type, BufferParams const & bp)
581 if (type == "tableofcontents")
582 return qt_("Table of Contents");
584 return qt_("Child Documents");
585 if (type == "graphics")
586 return qt_("Graphics");
587 if (type == "equation")
588 return qt_("Equations");
589 if (type == "footnote")
590 return qt_("Footnotes");
591 if (type == "listing")
592 return qt_("Listings");
594 return qt_("Index Entries");
595 if (type == "marginalnote")
596 return qt_("Marginal notes");
597 if (type == "nomencl")
598 return qt_("Nomenclature Entries");
601 if (type == "citation")
602 return qt_("Citations");
604 return qt_("Labels and References");
605 if (type == "branch")
606 return qt_("Branches");
607 if (type == "change")
608 return qt_("Changes");
610 FloatList const & floats = bp.documentClass().floats();
611 if (floats.typeExist(type))
612 return qt_(floats.getType(type).listName());