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));
95 Length::UNIT unit = Length::UNIT_NONE;
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 lengthToWidgets(QLineEdit * input, LengthCombo * combo,
134 docstring const & len, Length::UNIT defaultUnit)
136 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
140 void setValid(QWidget * widget, bool valid)
143 widget->setPalette(QPalette());
145 QPalette pal = widget->palette();
146 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
147 widget->setPalette(pal);
151 } // namespace frontend
153 QString const qt_(char const * str, const char *)
155 return toqstr(_(str));
159 QString const qt_(string const & str)
161 return toqstr(_(str));
165 void rescanTexStyles()
167 // Run rescan in user lyx directory
168 PathChanger p(package().user_support());
169 FileName const command = support::libFileSearch("scripts", "TeXFiles.py");
171 int const status = one.startscript(Systemcall::Wait,
173 quoteName(command.toFilesystemEncoding()));
177 frontend::Alert::error(_("Could not update TeX information"),
178 bformat(_("The script `%s' failed."), from_utf8(command.absFilename())));
182 QStringList texFileList(QString const & filename)
185 FileName const file = libFileSearch(QString(), filename);
190 vector<docstring> doclist =
191 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
193 // Normalise paths like /foo//bar ==> /foo/bar
195 for (size_t i = 0; i != doclist.size(); ++i) {
196 QString file = toqstr(doclist[i]);
197 file.replace("\r", "");
198 while (file.contains("//"))
199 file.replace("//", "/");
205 return QList<QString>::fromSet(set);
208 QString const externalLineEnding(docstring const & str)
211 // The MAC clipboard uses \r for lineendings, and we use \n
212 return toqstr(subst(str, '\n', '\r'));
213 #elif defined(Q_WS_WIN)
214 // Windows clipboard uses \r\n for lineendings, and we use \n
215 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
222 docstring const internalLineEnding(QString const & str)
224 docstring const s = subst(qstring_to_ucs4(str),
225 from_ascii("\r\n"), from_ascii("\n"));
226 return subst(s, '\r', '\n');
230 QString internalPath(const QString & str)
232 return toqstr(os::internal_path(fromqstr(str)));
236 QString onlyFilename(const QString & str)
238 return toqstr(support::onlyFilename(fromqstr(str)));
242 QString onlyPath(const QString & str)
244 return toqstr(support::onlyPath(fromqstr(str)));
248 QString changeExtension(QString const & oldname, QString const & ext)
250 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
253 /// Remove the extension from \p name
254 QString removeExtension(QString const & name)
256 return toqstr(support::removeExtension(fromqstr(name)));
259 /** Add the extension \p ext to \p name.
260 Use this instead of changeExtension if you know that \p name is without
261 extension, because changeExtension would wrongly interpret \p name if it
264 QString addExtension(QString const & name, QString const & ext)
266 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
269 /// Return the extension of the file (not including the .)
270 QString getExtension(QString const & name)
272 return toqstr(support::getExtension(fromqstr(name)));
276 /** Convert relative path into absolute path based on a basepath.
277 If relpath is absolute, just use that.
278 If basepath doesn't exist use CWD.
280 QString makeAbsPath(QString const & relpath, QString const & base)
282 return toqstr(support::makeAbsPath(fromqstr(relpath),
283 fromqstr(base)).absFilename());
287 /////////////////////////////////////////////////////////////////////////
291 /////////////////////////////////////////////////////////////////////////
293 /** Given a string such as
294 * "<glob> <glob> ... *.{abc,def} <glob>",
295 * convert the csh-style brace expresions:
296 * "<glob> <glob> ... *.abc *.def <glob>".
297 * Requires no system support, so should work equally on Unix, Mac, Win32.
299 static string const convert_brace_glob(string const & glob)
301 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
302 // "abc,def,ghi" as group 2.
303 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
304 // Matches "abc" and "abc,", storing "abc" as group 1.
305 static boost::regex const block_re("([^,}]+),?");
309 string::const_iterator it = glob.begin();
310 string::const_iterator const end = glob.end();
312 boost::match_results<string::const_iterator> what;
313 if (!boost::regex_search(it, end, what, glob_re)) {
314 // Ensure that no information is lost.
315 pattern += string(it, end);
319 // Everything from the start of the input to
320 // the start of the match.
321 pattern += string(what[-1].first, what[-1].second);
323 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
324 string const head = string(what[1].first, what[1].second);
325 string const tail = string(what[2].first, what[2].second);
327 // Split the ','-separated chunks of tail so that
328 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
329 string const fmt = " " + head + "$1";
330 pattern += boost::regex_merge(tail, block_re, fmt);
332 // Increment the iterator to the end of the match.
333 it += distance(it, what[0].second);
342 /* \param description text describing the filters.
343 * \param one or more wildcard patterns, separated by
346 Filter(docstring const & description, std::string const & globs);
348 docstring const & description() const { return desc_; }
350 QString toString() const;
353 std::vector<std::string> globs_;
357 Filter::Filter(docstring const & description, string const & globs)
360 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
361 boost::char_separator<char> const separator(" ");
363 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
364 // "<glob> <glob> ... *.abc *.def <glob>"
365 string const expanded_globs = convert_brace_glob(globs);
367 // Split into individual globs.
368 vector<string> matches;
369 Tokenizer const tokens(expanded_globs, separator);
370 globs_ = vector<string>(tokens.begin(), tokens.end());
374 QString Filter::toString() const
378 bool const has_description = desc_.empty();
380 if (has_description) {
385 for (size_t i = 0; i != globs_.size(); ++i) {
388 s += toqstr(globs_[i]);
397 /** \c FileFilterList parses a Qt-style list of available file filters
398 * to generate the corresponding vector.
399 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
400 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
401 * will result in a vector of size 1 in which the description field is empty.
403 struct FileFilterList
405 // FIXME UNICODE: globs_ should be unicode...
406 /** \param qt_style_filter a list of available file filters.
407 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
408 * The "All files (*)" filter is always added to the list.
410 explicit FileFilterList(docstring const & qt_style_filter =
413 typedef std::vector<Filter>::size_type size_type;
415 bool empty() const { return filters_.empty(); }
416 size_type size() const { return filters_.size(); }
417 Filter & operator[](size_type i) { return filters_[i]; }
418 Filter const & operator[](size_type i) const { return filters_[i]; }
420 void parse_filter(std::string const & filter);
421 std::vector<Filter> filters_;
425 FileFilterList::FileFilterList(docstring const & qt_style_filter)
428 string const filter = to_utf8(qt_style_filter)
429 + (qt_style_filter.empty() ? string() : ";;")
430 + to_utf8(_("All Files "))
437 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
438 // into individual filters.
439 static boost::regex const separator_re(";;");
441 string::const_iterator it = filter.begin();
442 string::const_iterator const end = filter.end();
444 boost::match_results<string::const_iterator> what;
446 if (!boost::regex_search(it, end, what, separator_re)) {
447 parse_filter(string(it, end));
451 // Everything from the start of the input to
452 // the start of the match.
453 parse_filter(string(what[-1].first, what[-1].second));
455 // Increment the iterator to the end of the match.
456 it += distance(it, what[0].second);
461 void FileFilterList::parse_filter(string const & filter)
463 // Matches "TeX documents (*.tex)",
464 // storing "TeX documents " as group 1 and "*.tex" as group 2.
465 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
467 boost::match_results<string::const_iterator> what;
468 if (!boost::regex_search(filter, what, filter_re)) {
469 // Just a glob, no description.
470 filters_.push_back(Filter(docstring(), trim(filter)));
473 docstring const desc = from_utf8(string(what[1].first, what[1].second));
474 string const globs = string(what[2].first, what[2].second);
475 filters_.push_back(Filter(trim(desc), trim(globs)));
480 /** \returns the equivalent of the string passed in
481 * although any brace expressions are expanded.
482 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
484 QStringList fileFilters(QString const & desc)
486 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
487 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
488 FileFilterList filters(qstring_to_ucs4(desc));
489 //LYXERR0("DESC: " << desc);
491 for (size_t i = 0; i != filters.filters_.size(); ++i) {
492 QString f = filters.filters_[i].toString();
493 //LYXERR0("FILTER: " << f);
500 QString guiName(string const & type, BufferParams const & bp)
502 if (type == "tableofcontents")
503 return qt_("Table of Contents");
505 return qt_("Child Documents");
506 if (type == "graphics")
507 return qt_("List of Graphics");
508 if (type == "equation")
509 return qt_("List of Equations");
510 if (type == "footnote")
511 return qt_("List of Footnotes");
512 if (type == "listing")
513 return qt_("List of Listings");
515 return qt_("List of Indexes");
516 if (type == "marginalnote")
517 return qt_("List of Marginal notes");
519 return qt_("List of Notes");
520 if (type == "citation")
521 return qt_("List of Citations");
523 return qt_("Labels and References");
524 if (type == "branch")
525 return qt_("List of Branches");
526 if (type == "change")
527 return qt_("List of Changes");
529 FloatList const & floats = bp.documentClass().floats();
530 if (floats.typeExist(type))
531 return qt_(floats.getType(type).listName());