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 const unit = unitFromString(fromqstr(combo->currentText()));
97 return Length(length.toDouble(), unit);
101 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
102 Length const & len, Length::UNIT /*defaultUnit*/)
104 combo->setCurrentItem(len.unit());
105 input->setText(QString::number(Length(len).value()));
109 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
110 string const & len, Length::UNIT defaultUnit)
113 // no length (UNIT_NONE)
114 combo->setCurrentItem(defaultUnit);
116 } else if (!isValidLength(len) && !isStrDbl(len)) {
117 // use input field only for gluelengths
118 combo->setCurrentItem(defaultUnit);
119 input->setText(toqstr(len));
121 lengthToWidgets(input, combo, Length(len), defaultUnit);
126 void lengthAutoToWidgets(QLineEdit * input, LengthCombo * combo,
127 Length const & len, Length::UNIT defaultUnit)
129 if (len.value() == 0)
130 lengthToWidgets(input, combo, "auto", defaultUnit);
132 lengthToWidgets(input, combo, len, defaultUnit);
136 void setValid(QWidget * widget, bool valid)
139 widget->setPalette(QPalette());
141 QPalette pal = widget->palette();
142 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
143 widget->setPalette(pal);
147 } // namespace frontend
149 QString const qt_(char const * str, const char *)
151 return toqstr(_(str));
155 QString const qt_(string const & str)
157 return toqstr(_(str));
161 void rescanTexStyles()
163 // Run rescan in user lyx directory
164 PathChanger p(package().user_support());
165 FileName const command = support::libFileSearch("scripts", "TeXFiles.py");
167 int const status = one.startscript(Systemcall::Wait,
169 quoteName(command.toFilesystemEncoding()));
173 frontend::Alert::error(_("Could not update TeX information"),
174 bformat(_("The script `%s' failed."), from_utf8(command.absFilename())));
178 QStringList texFileList(QString const & filename)
181 FileName const file = libFileSearch(QString(), filename);
186 vector<docstring> doclist =
187 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
189 // Normalise paths like /foo//bar ==> /foo/bar
191 for (size_t i = 0; i != doclist.size(); ++i) {
192 QString file = toqstr(doclist[i]);
193 file.replace("\r", "");
194 while (file.contains("//"))
195 file.replace("//", "/");
201 return QList<QString>::fromSet(set);
205 QString internalPath(const QString & str)
207 return toqstr(os::internal_path(fromqstr(str)));
211 QString onlyFilename(const QString & str)
213 return toqstr(support::onlyFilename(fromqstr(str)));
217 QString onlyPath(const QString & str)
219 return toqstr(support::onlyPath(fromqstr(str)));
223 QString changeExtension(QString const & oldname, QString const & ext)
225 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
228 /// Remove the extension from \p name
229 QString removeExtension(QString const & name)
231 return toqstr(support::removeExtension(fromqstr(name)));
234 /** Add the extension \p ext to \p name.
235 Use this instead of changeExtension if you know that \p name is without
236 extension, because changeExtension would wrongly interpret \p name if it
239 QString addExtension(QString const & name, QString const & ext)
241 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
244 /// Return the extension of the file (not including the .)
245 QString getExtension(QString const & name)
247 return toqstr(support::getExtension(fromqstr(name)));
251 /** Convert relative path into absolute path based on a basepath.
252 If relpath is absolute, just use that.
253 If basepath doesn't exist use CWD.
255 QString makeAbsPath(QString const & relpath, QString const & base)
257 return toqstr(support::makeAbsPath(fromqstr(relpath),
258 fromqstr(base)).absFilename());
262 /////////////////////////////////////////////////////////////////////////
266 /////////////////////////////////////////////////////////////////////////
268 /** Given a string such as
269 * "<glob> <glob> ... *.{abc,def} <glob>",
270 * convert the csh-style brace expresions:
271 * "<glob> <glob> ... *.abc *.def <glob>".
272 * Requires no system support, so should work equally on Unix, Mac, Win32.
274 static string const convert_brace_glob(string const & glob)
276 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
277 // "abc,def,ghi" as group 2.
278 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
279 // Matches "abc" and "abc,", storing "abc" as group 1.
280 static boost::regex const block_re("([^,}]+),?");
284 string::const_iterator it = glob.begin();
285 string::const_iterator const end = glob.end();
287 boost::match_results<string::const_iterator> what;
288 if (!boost::regex_search(it, end, what, glob_re)) {
289 // Ensure that no information is lost.
290 pattern += string(it, end);
294 // Everything from the start of the input to
295 // the start of the match.
296 pattern += string(what[-1].first, what[-1].second);
298 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
299 string const head = string(what[1].first, what[1].second);
300 string const tail = string(what[2].first, what[2].second);
302 // Split the ','-separated chunks of tail so that
303 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
304 string const fmt = " " + head + "$1";
305 pattern += boost::regex_merge(tail, block_re, fmt);
307 // Increment the iterator to the end of the match.
308 it += distance(it, what[0].second);
317 /* \param description text describing the filters.
318 * \param one or more wildcard patterns, separated by
321 Filter(docstring const & description, std::string const & globs);
323 docstring const & description() const { return desc_; }
325 QString toString() const;
328 std::vector<std::string> globs_;
332 Filter::Filter(docstring const & description, string const & globs)
335 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
336 boost::char_separator<char> const separator(" ");
338 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
339 // "<glob> <glob> ... *.abc *.def <glob>"
340 string const expanded_globs = convert_brace_glob(globs);
342 // Split into individual globs.
343 vector<string> matches;
344 Tokenizer const tokens(expanded_globs, separator);
345 globs_ = vector<string>(tokens.begin(), tokens.end());
349 QString Filter::toString() const
353 bool const has_description = desc_.empty();
355 if (has_description) {
360 for (size_t i = 0; i != globs_.size(); ++i) {
363 s += toqstr(globs_[i]);
372 /** \c FileFilterList parses a Qt-style list of available file filters
373 * to generate the corresponding vector.
374 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
375 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
376 * will result in a vector of size 1 in which the description field is empty.
378 struct FileFilterList
380 // FIXME UNICODE: globs_ should be unicode...
381 /** \param qt_style_filter a list of available file filters.
382 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
383 * The "All files (*)" filter is always added to the list.
385 explicit FileFilterList(docstring const & qt_style_filter =
388 typedef std::vector<Filter>::size_type size_type;
390 bool empty() const { return filters_.empty(); }
391 size_type size() const { return filters_.size(); }
392 Filter & operator[](size_type i) { return filters_[i]; }
393 Filter const & operator[](size_type i) const { return filters_[i]; }
395 void parse_filter(std::string const & filter);
396 std::vector<Filter> filters_;
400 FileFilterList::FileFilterList(docstring const & qt_style_filter)
403 string const filter = to_utf8(qt_style_filter)
404 + (qt_style_filter.empty() ? string() : ";;")
405 + to_utf8(_("All Files "))
412 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
413 // into individual filters.
414 static boost::regex const separator_re(";;");
416 string::const_iterator it = filter.begin();
417 string::const_iterator const end = filter.end();
419 boost::match_results<string::const_iterator> what;
421 if (!boost::regex_search(it, end, what, separator_re)) {
422 parse_filter(string(it, end));
426 // Everything from the start of the input to
427 // the start of the match.
428 parse_filter(string(what[-1].first, what[-1].second));
430 // Increment the iterator to the end of the match.
431 it += distance(it, what[0].second);
436 void FileFilterList::parse_filter(string const & filter)
438 // Matches "TeX documents (*.tex)",
439 // storing "TeX documents " as group 1 and "*.tex" as group 2.
440 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
442 boost::match_results<string::const_iterator> what;
443 if (!boost::regex_search(filter, what, filter_re)) {
444 // Just a glob, no description.
445 filters_.push_back(Filter(docstring(), trim(filter)));
448 docstring const desc = from_utf8(string(what[1].first, what[1].second));
449 string const globs = string(what[2].first, what[2].second);
450 filters_.push_back(Filter(trim(desc), trim(globs)));
455 /** \returns the equivalent of the string passed in
456 * although any brace expressions are expanded.
457 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
459 QStringList fileFilters(QString const & desc)
461 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
462 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
463 FileFilterList filters(qstring_to_ucs4(desc));
464 //LYXERR0("DESC: " << desc);
466 for (size_t i = 0; i != filters.filters_.size(); ++i) {
467 QString f = filters.filters_[i].toString();
468 //LYXERR0("FILTER: " << f);
475 QString guiName(string const & type, BufferParams const & bp)
477 if (type == "tableofcontents")
478 return qt_("Table of Contents");
480 return qt_("Child Documents");
481 if (type == "graphics")
482 return qt_("List of Graphics");
483 if (type == "equation")
484 return qt_("List of Equations");
485 if (type == "footnote")
486 return qt_("List of Footnotes");
487 if (type == "listing")
488 return qt_("List of Listings");
490 return qt_("List of Indexes");
491 if (type == "marginalnote")
492 return qt_("List of Marginal notes");
494 return qt_("List of Notes");
495 if (type == "citation")
496 return qt_("List of Citations");
498 return qt_("Labels and References");
499 if (type == "branch")
500 return qt_("List of Branches");
502 FloatList const & floats = bp.documentClass().floats();
503 if (floats.typeExist(type))
504 return qt_(floats.getType(type).listName());