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 <boost/regex.hpp>
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));
71 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
73 QString const length = input->text();
77 // Don't return unit-from-choice if the input(field) contains a unit
78 if (isValidGlueLength(fromqstr(length)))
79 return fromqstr(length);
81 Length::UNIT const unit = combo->currentLengthItem();
83 return Length(length.trimmed().toDouble(), unit).asString();
87 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
89 QString const length = input->text();
93 // don't return unit-from-choice if the input(field) contains a unit
94 if (isValidGlueLength(fromqstr(length)))
95 return Length(fromqstr(length));
97 Length::UNIT unit = Length::UNIT_NONE;
98 QString const item = combo->currentText();
99 for (int i = 0; i < num_units; i++) {
100 if (qt_(lyx::unit_name_gui[i]) == item) {
101 unit = unitFromString(unit_name[i]);
106 return Length(length.trimmed().toDouble(), unit);
110 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
111 Length const & len, Length::UNIT /*defaultUnit*/)
113 combo->setCurrentItem(len.unit());
115 loc.setNumberOptions(QLocale::OmitGroupSeparator);
116 input->setText(loc.toString(Length(len).value()));
120 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
121 string const & len, Length::UNIT defaultUnit)
124 // no length (UNIT_NONE)
125 combo->setCurrentItem(defaultUnit);
127 } else if (!isValidLength(len) && !isStrDbl(len)) {
128 // use input field only for gluelengths
129 combo->setCurrentItem(defaultUnit);
130 input->setText(toqstr(len));
132 lengthToWidgets(input, combo, Length(len), defaultUnit);
137 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
138 docstring const & len, Length::UNIT defaultUnit)
140 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
144 double widgetToDouble(QLineEdit const * input)
146 QString const text = input->text();
150 return text.trimmed().toDouble();
154 string widgetToDoubleStr(QLineEdit const * input)
156 QString const text = input->text();
160 return convert<string>(text.trimmed().toDouble());
164 void doubleToWidget(QLineEdit * input, double const & value, char f, int prec)
167 loc.setNumberOptions(QLocale::OmitGroupSeparator);
168 input->setText(loc.toString(value, f, prec));
172 void doubleToWidget(QLineEdit * input, string const & value, char f, int prec)
174 doubleToWidget(input, convert<double>(value), f, prec);
178 void setValid(QWidget * widget, bool valid)
181 widget->setPalette(QPalette());
183 QPalette pal = widget->palette();
184 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
185 widget->setPalette(pal);
189 } // namespace frontend
191 QString const qt_(char const * str, const char *)
193 return toqstr(_(str));
197 QString const qt_(string const & str)
199 return toqstr(_(str));
203 void rescanTexStyles()
205 // Run rescan in user lyx directory
206 PathChanger p(package().user_support());
207 FileName const command = support::libFileSearch("scripts", "TeXFiles.py");
209 int const status = one.startscript(Systemcall::Wait,
211 quoteName(command.toFilesystemEncoding()));
215 frontend::Alert::error(_("Could not update TeX information"),
216 bformat(_("The script `%1$s' failed."), from_utf8(command.absFilename())));
220 QStringList texFileList(QString const & filename)
223 FileName const file = libFileSearch(QString(), filename);
228 vector<docstring> doclist =
229 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
231 // Normalise paths like /foo//bar ==> /foo/bar
233 for (size_t i = 0; i != doclist.size(); ++i) {
234 QString file = toqstr(doclist[i]);
235 file.replace("\r", "");
236 while (file.contains("//"))
237 file.replace("//", "/");
243 return QList<QString>::fromSet(set);
246 QString const externalLineEnding(docstring const & str)
249 // The MAC clipboard uses \r for lineendings, and we use \n
250 return toqstr(subst(str, '\n', '\r'));
251 #elif defined(Q_WS_WIN)
252 // Windows clipboard uses \r\n for lineendings, and we use \n
253 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
260 docstring const internalLineEnding(QString const & str)
262 docstring const s = subst(qstring_to_ucs4(str),
263 from_ascii("\r\n"), from_ascii("\n"));
264 return subst(s, '\r', '\n');
268 QString internalPath(const QString & str)
270 return toqstr(os::internal_path(fromqstr(str)));
274 QString onlyFilename(const QString & str)
276 return toqstr(support::onlyFilename(fromqstr(str)));
280 QString onlyPath(const QString & str)
282 return toqstr(support::onlyPath(fromqstr(str)));
286 QString changeExtension(QString const & oldname, QString const & ext)
288 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
291 /// Remove the extension from \p name
292 QString removeExtension(QString const & name)
294 return toqstr(support::removeExtension(fromqstr(name)));
297 /** Add the extension \p ext to \p name.
298 Use this instead of changeExtension if you know that \p name is without
299 extension, because changeExtension would wrongly interpret \p name if it
302 QString addExtension(QString const & name, QString const & ext)
304 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
307 /// Return the extension of the file (not including the .)
308 QString getExtension(QString const & name)
310 return toqstr(support::getExtension(fromqstr(name)));
314 /** Convert relative path into absolute path based on a basepath.
315 If relpath is absolute, just use that.
316 If basepath doesn't exist use CWD.
318 QString makeAbsPath(QString const & relpath, QString const & base)
320 return toqstr(support::makeAbsPath(fromqstr(relpath),
321 fromqstr(base)).absFilename());
325 /////////////////////////////////////////////////////////////////////////
329 /////////////////////////////////////////////////////////////////////////
331 /** Given a string such as
332 * "<glob> <glob> ... *.{abc,def} <glob>",
333 * convert the csh-style brace expresions:
334 * "<glob> <glob> ... *.abc *.def <glob>".
335 * Requires no system support, so should work equally on Unix, Mac, Win32.
337 static string const convert_brace_glob(string const & glob)
339 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
340 // "abc,def,ghi" as group 2.
341 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
342 // Matches "abc" and "abc,", storing "abc" as group 1.
343 static boost::regex const block_re("([^,}]+),?");
347 string::const_iterator it = glob.begin();
348 string::const_iterator const end = glob.end();
350 boost::match_results<string::const_iterator> what;
351 if (!boost::regex_search(it, end, what, glob_re)) {
352 // Ensure that no information is lost.
353 pattern += string(it, end);
357 // Everything from the start of the input to
358 // the start of the match.
359 pattern += string(what[-1].first, what[-1].second);
361 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
362 string const head = string(what[1].first, what[1].second);
363 string const tail = string(what[2].first, what[2].second);
365 // Split the ','-separated chunks of tail so that
366 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
367 string const fmt = " " + head + "$1";
368 pattern += boost::regex_merge(tail, block_re, fmt);
370 // Increment the iterator to the end of the match.
371 it += distance(it, what[0].second);
380 /* \param description text describing the filters.
381 * \param one or more wildcard patterns, separated by
384 Filter(docstring const & description, std::string const & globs);
386 docstring const & description() const { return desc_; }
388 QString toString() const;
391 std::vector<std::string> globs_;
395 Filter::Filter(docstring const & description, string const & globs)
398 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
399 boost::char_separator<char> const separator(" ");
401 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
402 // "<glob> <glob> ... *.abc *.def <glob>"
403 string const expanded_globs = convert_brace_glob(globs);
405 // Split into individual globs.
406 vector<string> matches;
407 Tokenizer const tokens(expanded_globs, separator);
408 globs_ = vector<string>(tokens.begin(), tokens.end());
412 QString Filter::toString() const
416 bool const has_description = desc_.empty();
418 if (has_description) {
423 for (size_t i = 0; i != globs_.size(); ++i) {
426 s += toqstr(globs_[i]);
435 /** \c FileFilterList parses a Qt-style list of available file filters
436 * to generate the corresponding vector.
437 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
438 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
439 * will result in a vector of size 1 in which the description field is empty.
441 struct FileFilterList
443 // FIXME UNICODE: globs_ should be unicode...
444 /** \param qt_style_filter a list of available file filters.
445 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
446 * The "All files (*)" filter is always added to the list.
448 explicit FileFilterList(docstring const & qt_style_filter =
451 typedef std::vector<Filter>::size_type size_type;
453 bool empty() const { return filters_.empty(); }
454 size_type size() const { return filters_.size(); }
455 Filter & operator[](size_type i) { return filters_[i]; }
456 Filter const & operator[](size_type i) const { return filters_[i]; }
458 void parse_filter(std::string const & filter);
459 std::vector<Filter> filters_;
463 FileFilterList::FileFilterList(docstring const & qt_style_filter)
466 string const filter = to_utf8(qt_style_filter)
467 + (qt_style_filter.empty() ? string() : ";;")
468 + to_utf8(_("All Files "))
475 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
476 // into individual filters.
477 static boost::regex const separator_re(";;");
479 string::const_iterator it = filter.begin();
480 string::const_iterator const end = filter.end();
482 boost::match_results<string::const_iterator> what;
484 if (!boost::regex_search(it, end, what, separator_re)) {
485 parse_filter(string(it, end));
489 // Everything from the start of the input to
490 // the start of the match.
491 parse_filter(string(what[-1].first, what[-1].second));
493 // Increment the iterator to the end of the match.
494 it += distance(it, what[0].second);
499 void FileFilterList::parse_filter(string const & filter)
501 // Matches "TeX documents (*.tex)",
502 // storing "TeX documents " as group 1 and "*.tex" as group 2.
503 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
505 boost::match_results<string::const_iterator> what;
506 if (!boost::regex_search(filter, what, filter_re)) {
507 // Just a glob, no description.
508 filters_.push_back(Filter(docstring(), trim(filter)));
511 docstring const desc = from_utf8(string(what[1].first, what[1].second));
512 string const globs = string(what[2].first, what[2].second);
513 filters_.push_back(Filter(trim(desc), trim(globs)));
518 /** \returns the equivalent of the string passed in
519 * although any brace expressions are expanded.
520 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
522 QStringList fileFilters(QString const & desc)
524 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
525 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
526 FileFilterList filters(qstring_to_ucs4(desc));
527 //LYXERR0("DESC: " << desc);
529 for (size_t i = 0; i != filters.filters_.size(); ++i) {
530 QString f = filters.filters_[i].toString();
531 //LYXERR0("FILTER: " << f);
538 QString guiName(string const & type, BufferParams const & bp)
540 if (type == "tableofcontents")
541 return qt_("Table of Contents");
543 return qt_("Child Documents");
544 if (type == "graphics")
545 return qt_("List of Graphics");
546 if (type == "equation")
547 return qt_("List of Equations");
548 if (type == "footnote")
549 return qt_("List of Footnotes");
550 if (type == "listing")
551 return qt_("List of Listings");
553 return qt_("List of Indexes");
554 if (type == "marginalnote")
555 return qt_("List of Marginal notes");
557 return qt_("List of Notes");
558 if (type == "citation")
559 return qt_("List of Citations");
561 return qt_("Labels and References");
562 if (type == "branch")
563 return qt_("List of Branches");
564 if (type == "change")
565 return qt_("List of Changes");
567 FloatList const & floats = bp.documentClass().floats();
568 if (floats.typeExist(type))
569 return qt_(floats.getType(type).listName());