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);
204 QString const externalLineEnding(docstring const & str)
207 // The MAC clipboard uses \r for lineendings, and we use \n
208 return toqstr(subst(str, '\n', '\r'));
209 #elif defined(Q_WS_WIN)
210 // Windows clipboard uses \r\n for lineendings, and we use \n
211 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
218 docstring const internalLineEnding(QString const & str)
220 docstring const s = subst(qstring_to_ucs4(str),
221 from_ascii("\r\n"), from_ascii("\n"));
222 return subst(s, '\r', '\n');
226 QString internalPath(const QString & str)
228 return toqstr(os::internal_path(fromqstr(str)));
232 QString onlyFilename(const QString & str)
234 return toqstr(support::onlyFilename(fromqstr(str)));
238 QString onlyPath(const QString & str)
240 return toqstr(support::onlyPath(fromqstr(str)));
244 QString changeExtension(QString const & oldname, QString const & ext)
246 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
249 /// Remove the extension from \p name
250 QString removeExtension(QString const & name)
252 return toqstr(support::removeExtension(fromqstr(name)));
255 /** Add the extension \p ext to \p name.
256 Use this instead of changeExtension if you know that \p name is without
257 extension, because changeExtension would wrongly interpret \p name if it
260 QString addExtension(QString const & name, QString const & ext)
262 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
265 /// Return the extension of the file (not including the .)
266 QString getExtension(QString const & name)
268 return toqstr(support::getExtension(fromqstr(name)));
272 /** Convert relative path into absolute path based on a basepath.
273 If relpath is absolute, just use that.
274 If basepath doesn't exist use CWD.
276 QString makeAbsPath(QString const & relpath, QString const & base)
278 return toqstr(support::makeAbsPath(fromqstr(relpath),
279 fromqstr(base)).absFilename());
283 /////////////////////////////////////////////////////////////////////////
287 /////////////////////////////////////////////////////////////////////////
289 /** Given a string such as
290 * "<glob> <glob> ... *.{abc,def} <glob>",
291 * convert the csh-style brace expresions:
292 * "<glob> <glob> ... *.abc *.def <glob>".
293 * Requires no system support, so should work equally on Unix, Mac, Win32.
295 static string const convert_brace_glob(string const & glob)
297 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
298 // "abc,def,ghi" as group 2.
299 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
300 // Matches "abc" and "abc,", storing "abc" as group 1.
301 static boost::regex const block_re("([^,}]+),?");
305 string::const_iterator it = glob.begin();
306 string::const_iterator const end = glob.end();
308 boost::match_results<string::const_iterator> what;
309 if (!boost::regex_search(it, end, what, glob_re)) {
310 // Ensure that no information is lost.
311 pattern += string(it, end);
315 // Everything from the start of the input to
316 // the start of the match.
317 pattern += string(what[-1].first, what[-1].second);
319 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
320 string const head = string(what[1].first, what[1].second);
321 string const tail = string(what[2].first, what[2].second);
323 // Split the ','-separated chunks of tail so that
324 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
325 string const fmt = " " + head + "$1";
326 pattern += boost::regex_merge(tail, block_re, fmt);
328 // Increment the iterator to the end of the match.
329 it += distance(it, what[0].second);
338 /* \param description text describing the filters.
339 * \param one or more wildcard patterns, separated by
342 Filter(docstring const & description, std::string const & globs);
344 docstring const & description() const { return desc_; }
346 QString toString() const;
349 std::vector<std::string> globs_;
353 Filter::Filter(docstring const & description, string const & globs)
356 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
357 boost::char_separator<char> const separator(" ");
359 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
360 // "<glob> <glob> ... *.abc *.def <glob>"
361 string const expanded_globs = convert_brace_glob(globs);
363 // Split into individual globs.
364 vector<string> matches;
365 Tokenizer const tokens(expanded_globs, separator);
366 globs_ = vector<string>(tokens.begin(), tokens.end());
370 QString Filter::toString() const
374 bool const has_description = desc_.empty();
376 if (has_description) {
381 for (size_t i = 0; i != globs_.size(); ++i) {
384 s += toqstr(globs_[i]);
393 /** \c FileFilterList parses a Qt-style list of available file filters
394 * to generate the corresponding vector.
395 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
396 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
397 * will result in a vector of size 1 in which the description field is empty.
399 struct FileFilterList
401 // FIXME UNICODE: globs_ should be unicode...
402 /** \param qt_style_filter a list of available file filters.
403 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
404 * The "All files (*)" filter is always added to the list.
406 explicit FileFilterList(docstring const & qt_style_filter =
409 typedef std::vector<Filter>::size_type size_type;
411 bool empty() const { return filters_.empty(); }
412 size_type size() const { return filters_.size(); }
413 Filter & operator[](size_type i) { return filters_[i]; }
414 Filter const & operator[](size_type i) const { return filters_[i]; }
416 void parse_filter(std::string const & filter);
417 std::vector<Filter> filters_;
421 FileFilterList::FileFilterList(docstring const & qt_style_filter)
424 string const filter = to_utf8(qt_style_filter)
425 + (qt_style_filter.empty() ? string() : ";;")
426 + to_utf8(_("All Files "))
433 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
434 // into individual filters.
435 static boost::regex const separator_re(";;");
437 string::const_iterator it = filter.begin();
438 string::const_iterator const end = filter.end();
440 boost::match_results<string::const_iterator> what;
442 if (!boost::regex_search(it, end, what, separator_re)) {
443 parse_filter(string(it, end));
447 // Everything from the start of the input to
448 // the start of the match.
449 parse_filter(string(what[-1].first, what[-1].second));
451 // Increment the iterator to the end of the match.
452 it += distance(it, what[0].second);
457 void FileFilterList::parse_filter(string const & filter)
459 // Matches "TeX documents (*.tex)",
460 // storing "TeX documents " as group 1 and "*.tex" as group 2.
461 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
463 boost::match_results<string::const_iterator> what;
464 if (!boost::regex_search(filter, what, filter_re)) {
465 // Just a glob, no description.
466 filters_.push_back(Filter(docstring(), trim(filter)));
469 docstring const desc = from_utf8(string(what[1].first, what[1].second));
470 string const globs = string(what[2].first, what[2].second);
471 filters_.push_back(Filter(trim(desc), trim(globs)));
476 /** \returns the equivalent of the string passed in
477 * although any brace expressions are expanded.
478 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
480 QStringList fileFilters(QString const & desc)
482 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
483 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
484 FileFilterList filters(qstring_to_ucs4(desc));
485 //LYXERR0("DESC: " << desc);
487 for (size_t i = 0; i != filters.filters_.size(); ++i) {
488 QString f = filters.filters_[i].toString();
489 //LYXERR0("FILTER: " << f);
496 QString guiName(string const & type, BufferParams const & bp)
498 if (type == "tableofcontents")
499 return qt_("Table of Contents");
501 return qt_("Child Documents");
502 if (type == "graphics")
503 return qt_("List of Graphics");
504 if (type == "equation")
505 return qt_("List of Equations");
506 if (type == "footnote")
507 return qt_("List of Footnotes");
508 if (type == "listing")
509 return qt_("List of Listings");
511 return qt_("List of Indexes");
512 if (type == "marginalnote")
513 return qt_("List of Marginal notes");
515 return qt_("List of Notes");
516 if (type == "citation")
517 return qt_("List of Citations");
519 return qt_("Labels and References");
520 if (type == "branch")
521 return qt_("List of Branches");
522 if (type == "change")
523 return qt_("List of Changes");
525 FloatList const & floats = bp.documentClass().floats();
526 if (floats.typeExist(type))
527 return qt_(floats.getType(type).listName());