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/debug.h"
29 #include "support/filetools.h"
30 #include "support/foreach.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/Path.h"
37 #include "support/Systemcall.h"
51 #include <boost/regex.hpp>
52 #include <boost/tokenizer.hpp>
56 using namespace lyx::support;
60 FileName libFileSearch(QString const & dir, QString const & name,
63 return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext));
69 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
71 QString const length = input->text();
75 // Don't return unit-from-choice if the input(field) contains a unit
76 if (isValidGlueLength(fromqstr(length)))
77 return fromqstr(length);
79 Length::UNIT const unit = combo->currentLengthItem();
81 return Length(length.toDouble(), unit).asString();
85 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
87 QString const length = input->text();
91 // don't return unit-from-choice if the input(field) contains a unit
92 if (isValidGlueLength(fromqstr(length)))
93 return Length(fromqstr(length));
96 QString const item = combo->currentText();
97 for (int i = 0; i < num_units; i++) {
98 if (qt_(lyx::unit_name_gui[i]) == item) {
99 unit = unitFromString(unit_name[i]);
104 return Length(length.toDouble(), unit);
108 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
109 Length const & len, Length::UNIT /*defaultUnit*/)
111 combo->setCurrentItem(len.unit());
112 input->setText(QString::number(Length(len).value()));
116 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
117 string const & len, Length::UNIT defaultUnit)
120 // no length (UNIT_NONE)
121 combo->setCurrentItem(defaultUnit);
123 } else if (!isValidLength(len) && !isStrDbl(len)) {
124 // use input field only for gluelengths
125 combo->setCurrentItem(defaultUnit);
126 input->setText(toqstr(len));
128 lengthToWidgets(input, combo, Length(len), defaultUnit);
133 void lengthAutoToWidgets(QLineEdit * input, LengthCombo * combo,
134 Length const & len, Length::UNIT defaultUnit)
136 if (len.value() == 0)
137 lengthToWidgets(input, combo, "auto", defaultUnit);
139 lengthToWidgets(input, combo, len, defaultUnit);
143 void setValid(QWidget * widget, bool valid)
146 widget->setPalette(QPalette());
148 QPalette pal = widget->palette();
149 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
150 widget->setPalette(pal);
154 } // namespace frontend
156 QString const qt_(char const * str, const char *)
158 return toqstr(_(str));
162 QString const qt_(string const & str)
164 return toqstr(_(str));
168 void rescanTexStyles()
170 // Run rescan in user lyx directory
171 PathChanger p(package().user_support());
172 FileName const command = support::libFileSearch("scripts", "TeXFiles.py");
174 int const status = one.startscript(Systemcall::Wait,
176 quoteName(command.toFilesystemEncoding()));
180 frontend::Alert::error(_("Could not update TeX information"),
181 bformat(_("The script `%s' failed."), from_utf8(command.absFilename())));
185 QStringList texFileList(QString const & filename)
188 FileName const file = libFileSearch(QString(), filename);
193 vector<docstring> doclist =
194 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
196 // Normalise paths like /foo//bar ==> /foo/bar
198 for (size_t i = 0; i != doclist.size(); ++i) {
199 QString file = toqstr(doclist[i]);
200 file.replace("\r", "");
201 while (file.contains("//"))
202 file.replace("//", "/");
208 return QList<QString>::fromSet(set);
211 QString const externalLineEnding(docstring const & str)
214 // The MAC clipboard uses \r for lineendings, and we use \n
215 return toqstr(subst(str, '\n', '\r'));
216 #elif defined(Q_WS_WIN)
217 // Windows clipboard uses \r\n for lineendings, and we use \n
218 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
225 docstring const internalLineEnding(QString const & str)
227 docstring const s = subst(qstring_to_ucs4(str),
228 from_ascii("\r\n"), from_ascii("\n"));
229 return subst(s, '\r', '\n');
233 QString internalPath(const QString & str)
235 return toqstr(os::internal_path(fromqstr(str)));
239 QString onlyFilename(const QString & str)
241 return toqstr(support::onlyFilename(fromqstr(str)));
245 QString onlyPath(const QString & str)
247 return toqstr(support::onlyPath(fromqstr(str)));
251 QString changeExtension(QString const & oldname, QString const & ext)
253 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
256 /// Remove the extension from \p name
257 QString removeExtension(QString const & name)
259 return toqstr(support::removeExtension(fromqstr(name)));
262 /** Add the extension \p ext to \p name.
263 Use this instead of changeExtension if you know that \p name is without
264 extension, because changeExtension would wrongly interpret \p name if it
267 QString addExtension(QString const & name, QString const & ext)
269 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
272 /// Return the extension of the file (not including the .)
273 QString getExtension(QString const & name)
275 return toqstr(support::getExtension(fromqstr(name)));
279 /** Convert relative path into absolute path based on a basepath.
280 If relpath is absolute, just use that.
281 If basepath doesn't exist use CWD.
283 QString makeAbsPath(QString const & relpath, QString const & base)
285 return toqstr(support::makeAbsPath(fromqstr(relpath),
286 fromqstr(base)).absFilename());
290 /////////////////////////////////////////////////////////////////////////
294 /////////////////////////////////////////////////////////////////////////
296 /** Given a string such as
297 * "<glob> <glob> ... *.{abc,def} <glob>",
298 * convert the csh-style brace expresions:
299 * "<glob> <glob> ... *.abc *.def <glob>".
300 * Requires no system support, so should work equally on Unix, Mac, Win32.
302 static string const convert_brace_glob(string const & glob)
304 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
305 // "abc,def,ghi" as group 2.
306 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
307 // Matches "abc" and "abc,", storing "abc" as group 1.
308 static boost::regex const block_re("([^,}]+),?");
312 string::const_iterator it = glob.begin();
313 string::const_iterator const end = glob.end();
315 boost::match_results<string::const_iterator> what;
316 if (!boost::regex_search(it, end, what, glob_re)) {
317 // Ensure that no information is lost.
318 pattern += string(it, end);
322 // Everything from the start of the input to
323 // the start of the match.
324 pattern += string(what[-1].first, what[-1].second);
326 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
327 string const head = string(what[1].first, what[1].second);
328 string const tail = string(what[2].first, what[2].second);
330 // Split the ','-separated chunks of tail so that
331 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
332 string const fmt = " " + head + "$1";
333 pattern += boost::regex_merge(tail, block_re, fmt);
335 // Increment the iterator to the end of the match.
336 it += distance(it, what[0].second);
345 /* \param description text describing the filters.
346 * \param one or more wildcard patterns, separated by
349 Filter(docstring const & description, std::string const & globs);
351 docstring const & description() const { return desc_; }
353 QString toString() const;
356 std::vector<std::string> globs_;
360 Filter::Filter(docstring const & description, string const & globs)
363 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
364 boost::char_separator<char> const separator(" ");
366 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
367 // "<glob> <glob> ... *.abc *.def <glob>"
368 string const expanded_globs = convert_brace_glob(globs);
370 // Split into individual globs.
371 vector<string> matches;
372 Tokenizer const tokens(expanded_globs, separator);
373 globs_ = vector<string>(tokens.begin(), tokens.end());
377 QString Filter::toString() const
381 bool const has_description = desc_.empty();
383 if (has_description) {
388 for (size_t i = 0; i != globs_.size(); ++i) {
391 s += toqstr(globs_[i]);
400 /** \c FileFilterList parses a Qt-style list of available file filters
401 * to generate the corresponding vector.
402 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
403 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
404 * will result in a vector of size 1 in which the description field is empty.
406 struct FileFilterList
408 // FIXME UNICODE: globs_ should be unicode...
409 /** \param qt_style_filter a list of available file filters.
410 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
411 * The "All files (*)" filter is always added to the list.
413 explicit FileFilterList(docstring const & qt_style_filter =
416 typedef std::vector<Filter>::size_type size_type;
418 bool empty() const { return filters_.empty(); }
419 size_type size() const { return filters_.size(); }
420 Filter & operator[](size_type i) { return filters_[i]; }
421 Filter const & operator[](size_type i) const { return filters_[i]; }
423 void parse_filter(std::string const & filter);
424 std::vector<Filter> filters_;
428 FileFilterList::FileFilterList(docstring const & qt_style_filter)
431 string const filter = to_utf8(qt_style_filter)
432 + (qt_style_filter.empty() ? string() : ";;")
433 + to_utf8(_("All Files "))
440 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
441 // into individual filters.
442 static boost::regex const separator_re(";;");
444 string::const_iterator it = filter.begin();
445 string::const_iterator const end = filter.end();
447 boost::match_results<string::const_iterator> what;
449 if (!boost::regex_search(it, end, what, separator_re)) {
450 parse_filter(string(it, end));
454 // Everything from the start of the input to
455 // the start of the match.
456 parse_filter(string(what[-1].first, what[-1].second));
458 // Increment the iterator to the end of the match.
459 it += distance(it, what[0].second);
464 void FileFilterList::parse_filter(string const & filter)
466 // Matches "TeX documents (*.tex)",
467 // storing "TeX documents " as group 1 and "*.tex" as group 2.
468 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
470 boost::match_results<string::const_iterator> what;
471 if (!boost::regex_search(filter, what, filter_re)) {
472 // Just a glob, no description.
473 filters_.push_back(Filter(docstring(), trim(filter)));
476 docstring const desc = from_utf8(string(what[1].first, what[1].second));
477 string const globs = string(what[2].first, what[2].second);
478 filters_.push_back(Filter(trim(desc), trim(globs)));
483 /** \returns the equivalent of the string passed in
484 * although any brace expressions are expanded.
485 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
487 QStringList fileFilters(QString const & desc)
489 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
490 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
491 FileFilterList filters(qstring_to_ucs4(desc));
492 //LYXERR0("DESC: " << desc);
494 for (size_t i = 0; i != filters.filters_.size(); ++i) {
495 QString f = filters.filters_[i].toString();
496 //LYXERR0("FILTER: " << f);
503 QString guiName(string const & type, BufferParams const & bp)
505 if (type == "tableofcontents")
506 return qt_("Table of Contents");
508 return qt_("Child Documents");
509 if (type == "graphics")
510 return qt_("List of Graphics");
511 if (type == "equation")
512 return qt_("List of Equations");
513 if (type == "footnote")
514 return qt_("List of Footnotes");
515 if (type == "listing")
516 return qt_("List of Listings");
518 return qt_("List of Indexes");
519 if (type == "marginalnote")
520 return qt_("List of Marginal notes");
522 return qt_("List of Notes");
523 if (type == "citation")
524 return qt_("List of Citations");
526 return qt_("Labels and References");
527 if (type == "branch")
528 return qt_("List of Branches");
529 if (type == "change")
530 return qt_("List of Changes");
532 FloatList const & floats = bp.documentClass().floats();
533 if (floats.typeExist(type))
534 return qt_(floats.getType(type).listName());