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 input->setText(loc.toString(Length(len).value()));
119 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
120 string const & len, Length::UNIT defaultUnit)
123 // no length (UNIT_NONE)
124 combo->setCurrentItem(defaultUnit);
126 } else if (!isValidLength(len) && !isStrDbl(len)) {
127 // use input field only for gluelengths
128 combo->setCurrentItem(defaultUnit);
129 input->setText(toqstr(len));
131 lengthToWidgets(input, combo, Length(len), defaultUnit);
136 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
137 docstring const & len, Length::UNIT defaultUnit)
139 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
143 double widgetToDouble(QLineEdit const * input)
145 QString const text = input->text();
149 return text.trimmed().toDouble();
153 string widgetToDoubleStr(QLineEdit const * input)
155 QString const text = input->text();
159 return convert<string>(text.trimmed().toDouble());
163 void doubleToWidget(QLineEdit * input, double const & value, char f, int prec)
166 input->setText(loc.toString(value, f, prec));
170 void doubleToWidget(QLineEdit * input, string const & value, char f, int prec)
172 doubleToWidget(input, convert<double>(value), f, prec);
176 void setValid(QWidget * widget, bool valid)
179 widget->setPalette(QPalette());
181 QPalette pal = widget->palette();
182 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
183 widget->setPalette(pal);
187 } // namespace frontend
189 QString const qt_(char const * str, const char *)
191 return toqstr(_(str));
195 QString const qt_(string const & str)
197 return toqstr(_(str));
201 void rescanTexStyles()
203 // Run rescan in user lyx directory
204 PathChanger p(package().user_support());
205 FileName const command = support::libFileSearch("scripts", "TeXFiles.py");
207 int const status = one.startscript(Systemcall::Wait,
209 quoteName(command.toFilesystemEncoding()));
213 frontend::Alert::error(_("Could not update TeX information"),
214 bformat(_("The script `%s' failed."), from_utf8(command.absFilename())));
218 QStringList texFileList(QString const & filename)
221 FileName const file = libFileSearch(QString(), filename);
226 vector<docstring> doclist =
227 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
229 // Normalise paths like /foo//bar ==> /foo/bar
231 for (size_t i = 0; i != doclist.size(); ++i) {
232 QString file = toqstr(doclist[i]);
233 file.replace("\r", "");
234 while (file.contains("//"))
235 file.replace("//", "/");
241 return QList<QString>::fromSet(set);
244 QString const externalLineEnding(docstring const & str)
247 // The MAC clipboard uses \r for lineendings, and we use \n
248 return toqstr(subst(str, '\n', '\r'));
249 #elif defined(Q_WS_WIN)
250 // Windows clipboard uses \r\n for lineendings, and we use \n
251 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
258 docstring const internalLineEnding(QString const & str)
260 docstring const s = subst(qstring_to_ucs4(str),
261 from_ascii("\r\n"), from_ascii("\n"));
262 return subst(s, '\r', '\n');
266 QString internalPath(const QString & str)
268 return toqstr(os::internal_path(fromqstr(str)));
272 QString onlyFilename(const QString & str)
274 return toqstr(support::onlyFilename(fromqstr(str)));
278 QString onlyPath(const QString & str)
280 return toqstr(support::onlyPath(fromqstr(str)));
284 QString changeExtension(QString const & oldname, QString const & ext)
286 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
289 /// Remove the extension from \p name
290 QString removeExtension(QString const & name)
292 return toqstr(support::removeExtension(fromqstr(name)));
295 /** Add the extension \p ext to \p name.
296 Use this instead of changeExtension if you know that \p name is without
297 extension, because changeExtension would wrongly interpret \p name if it
300 QString addExtension(QString const & name, QString const & ext)
302 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
305 /// Return the extension of the file (not including the .)
306 QString getExtension(QString const & name)
308 return toqstr(support::getExtension(fromqstr(name)));
312 /** Convert relative path into absolute path based on a basepath.
313 If relpath is absolute, just use that.
314 If basepath doesn't exist use CWD.
316 QString makeAbsPath(QString const & relpath, QString const & base)
318 return toqstr(support::makeAbsPath(fromqstr(relpath),
319 fromqstr(base)).absFilename());
323 /////////////////////////////////////////////////////////////////////////
327 /////////////////////////////////////////////////////////////////////////
329 /** Given a string such as
330 * "<glob> <glob> ... *.{abc,def} <glob>",
331 * convert the csh-style brace expresions:
332 * "<glob> <glob> ... *.abc *.def <glob>".
333 * Requires no system support, so should work equally on Unix, Mac, Win32.
335 static string const convert_brace_glob(string const & glob)
337 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
338 // "abc,def,ghi" as group 2.
339 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
340 // Matches "abc" and "abc,", storing "abc" as group 1.
341 static boost::regex const block_re("([^,}]+),?");
345 string::const_iterator it = glob.begin();
346 string::const_iterator const end = glob.end();
348 boost::match_results<string::const_iterator> what;
349 if (!boost::regex_search(it, end, what, glob_re)) {
350 // Ensure that no information is lost.
351 pattern += string(it, end);
355 // Everything from the start of the input to
356 // the start of the match.
357 pattern += string(what[-1].first, what[-1].second);
359 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
360 string const head = string(what[1].first, what[1].second);
361 string const tail = string(what[2].first, what[2].second);
363 // Split the ','-separated chunks of tail so that
364 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
365 string const fmt = " " + head + "$1";
366 pattern += boost::regex_merge(tail, block_re, fmt);
368 // Increment the iterator to the end of the match.
369 it += distance(it, what[0].second);
378 /* \param description text describing the filters.
379 * \param one or more wildcard patterns, separated by
382 Filter(docstring const & description, std::string const & globs);
384 docstring const & description() const { return desc_; }
386 QString toString() const;
389 std::vector<std::string> globs_;
393 Filter::Filter(docstring const & description, string const & globs)
396 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
397 boost::char_separator<char> const separator(" ");
399 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
400 // "<glob> <glob> ... *.abc *.def <glob>"
401 string const expanded_globs = convert_brace_glob(globs);
403 // Split into individual globs.
404 vector<string> matches;
405 Tokenizer const tokens(expanded_globs, separator);
406 globs_ = vector<string>(tokens.begin(), tokens.end());
410 QString Filter::toString() const
414 bool const has_description = desc_.empty();
416 if (has_description) {
421 for (size_t i = 0; i != globs_.size(); ++i) {
424 s += toqstr(globs_[i]);
433 /** \c FileFilterList parses a Qt-style list of available file filters
434 * to generate the corresponding vector.
435 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
436 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
437 * will result in a vector of size 1 in which the description field is empty.
439 struct FileFilterList
441 // FIXME UNICODE: globs_ should be unicode...
442 /** \param qt_style_filter a list of available file filters.
443 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
444 * The "All files (*)" filter is always added to the list.
446 explicit FileFilterList(docstring const & qt_style_filter =
449 typedef std::vector<Filter>::size_type size_type;
451 bool empty() const { return filters_.empty(); }
452 size_type size() const { return filters_.size(); }
453 Filter & operator[](size_type i) { return filters_[i]; }
454 Filter const & operator[](size_type i) const { return filters_[i]; }
456 void parse_filter(std::string const & filter);
457 std::vector<Filter> filters_;
461 FileFilterList::FileFilterList(docstring const & qt_style_filter)
464 string const filter = to_utf8(qt_style_filter)
465 + (qt_style_filter.empty() ? string() : ";;")
466 + to_utf8(_("All Files "))
473 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
474 // into individual filters.
475 static boost::regex const separator_re(";;");
477 string::const_iterator it = filter.begin();
478 string::const_iterator const end = filter.end();
480 boost::match_results<string::const_iterator> what;
482 if (!boost::regex_search(it, end, what, separator_re)) {
483 parse_filter(string(it, end));
487 // Everything from the start of the input to
488 // the start of the match.
489 parse_filter(string(what[-1].first, what[-1].second));
491 // Increment the iterator to the end of the match.
492 it += distance(it, what[0].second);
497 void FileFilterList::parse_filter(string const & filter)
499 // Matches "TeX documents (*.tex)",
500 // storing "TeX documents " as group 1 and "*.tex" as group 2.
501 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
503 boost::match_results<string::const_iterator> what;
504 if (!boost::regex_search(filter, what, filter_re)) {
505 // Just a glob, no description.
506 filters_.push_back(Filter(docstring(), trim(filter)));
509 docstring const desc = from_utf8(string(what[1].first, what[1].second));
510 string const globs = string(what[2].first, what[2].second);
511 filters_.push_back(Filter(trim(desc), trim(globs)));
516 /** \returns the equivalent of the string passed in
517 * although any brace expressions are expanded.
518 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
520 QStringList fileFilters(QString const & desc)
522 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
523 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
524 FileFilterList filters(qstring_to_ucs4(desc));
525 //LYXERR0("DESC: " << desc);
527 for (size_t i = 0; i != filters.filters_.size(); ++i) {
528 QString f = filters.filters_[i].toString();
529 //LYXERR0("FILTER: " << f);
536 QString guiName(string const & type, BufferParams const & bp)
538 if (type == "tableofcontents")
539 return qt_("Table of Contents");
541 return qt_("Child Documents");
542 if (type == "graphics")
543 return qt_("List of Graphics");
544 if (type == "equation")
545 return qt_("List of Equations");
546 if (type == "footnote")
547 return qt_("List of Footnotes");
548 if (type == "listing")
549 return qt_("List of Listings");
551 return qt_("List of Indexes");
552 if (type == "marginalnote")
553 return qt_("List of Marginal notes");
555 return qt_("List of Notes");
556 if (type == "citation")
557 return qt_("List of Citations");
559 return qt_("Labels and References");
560 if (type == "branch")
561 return qt_("List of Branches");
562 if (type == "change")
563 return qt_("List of Changes");
565 FloatList const & floats = bp.documentClass().floats();
566 if (floats.typeExist(type))
567 return qt_(floats.getType(type).listName());