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/PathChanger.h"
38 #include "support/Systemcall.h"
53 #include "support/regex.h"
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));
69 FileName imageLibFileSearch(QString & dir, QString const & name,
72 string tmp = fromqstr(dir);
73 FileName fn = support::imageLibFileSearch(tmp, fromqstr(name), fromqstr(ext));
80 double locstringToDouble(QString const & str)
84 double res = loc.toDouble(str, &ok);
87 QLocale c(QLocale::C);
88 res = c.toDouble(str);
98 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
100 QString const length = input->text();
101 if (length.isEmpty())
104 // Don't return unit-from-choice if the input(field) contains a unit
105 if (isValidGlueLength(fromqstr(length)))
106 return fromqstr(length);
108 Length::UNIT const unit = combo->currentLengthItem();
110 return Length(locstringToDouble(length.trimmed()), unit).asString();
114 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
116 QString const length = input->text();
117 if (length.isEmpty())
120 // don't return unit-from-choice if the input(field) contains a unit
121 if (isValidGlueLength(fromqstr(length)))
122 return Length(fromqstr(length));
124 Length::UNIT unit = Length::UNIT_NONE;
125 QString const item = combo->currentText();
126 for (int i = 0; i < num_units; i++) {
127 if (qt_(lyx::unit_name_gui[i]) == item) {
128 unit = unitFromString(unit_name[i]);
133 return Length(locstringToDouble(length.trimmed()), unit);
137 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
138 Length const & len, Length::UNIT /*defaultUnit*/)
141 // no length (UNIT_NONE)
142 combo->setCurrentItem(Length::defaultUnit());
145 combo->setCurrentItem(len.unit());
147 loc.setNumberOptions(QLocale::OmitGroupSeparator);
148 input->setText(loc.toString(Length(len).value()));
153 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
154 string const & len, Length::UNIT defaultUnit)
157 // no length (UNIT_NONE)
158 combo->setCurrentItem(defaultUnit);
160 } else if (!isValidLength(len) && !isStrDbl(len)) {
161 // use input field only for gluelengths
162 combo->setCurrentItem(defaultUnit);
163 input->setText(toqstr(len));
165 lengthToWidgets(input, combo, Length(len), defaultUnit);
170 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
171 docstring const & len, Length::UNIT defaultUnit)
173 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
177 double widgetToDouble(QLineEdit const * input)
179 QString const text = input->text();
183 return locstringToDouble(text.trimmed());
187 string widgetToDoubleStr(QLineEdit const * input)
189 return convert<string>(widgetToDouble(input));
193 void doubleToWidget(QLineEdit * input, double const & value, char f, int prec)
196 loc.setNumberOptions(QLocale::OmitGroupSeparator);
197 input->setText(loc.toString(value, f, prec));
201 void doubleToWidget(QLineEdit * input, string const & value, char f, int prec)
203 doubleToWidget(input, convert<double>(value), f, prec);
207 void setValid(QWidget * widget, bool valid)
210 widget->setPalette(QPalette());
212 QPalette pal = widget->palette();
213 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
214 widget->setPalette(pal);
218 /// wrapper to hide the change of method name to setSectionResizeMode
219 void setSectionResizeMode(QHeaderView * view,
220 int logicalIndex, QHeaderView::ResizeMode mode) {
221 #if (QT_VERSION >= 0x050000)
222 view->setSectionResizeMode(logicalIndex, mode);
224 view->setResizeMode(logicalIndex, mode);
228 void setSectionResizeMode(QHeaderView * view, QHeaderView::ResizeMode mode) {
229 #if (QT_VERSION >= 0x050000)
230 view->setSectionResizeMode(mode);
232 view->setResizeMode(mode);
235 } // namespace frontend
237 QString const qt_(char const * str, const char *)
239 return toqstr(_(str));
243 QString const qt_(string const & str)
245 return toqstr(_(str));
249 QString const qt_(QString const & qstr)
251 return toqstr(_(fromqstr(qstr)));
255 void rescanTexStyles(string const & arg)
257 // Run rescan in user lyx directory
258 PathChanger p(package().user_support());
259 FileName const prog = support::libFileSearch("scripts", "TeXFiles.py");
261 string const command = os::python() + ' ' +
262 quoteName(prog.toFilesystemEncoding()) + ' ' +
264 int const status = one.startscript(Systemcall::Wait, command);
268 frontend::Alert::error(_("Could not update TeX information"),
269 bformat(_("The script `%1$s' failed."), from_utf8(prog.absFileName())));
273 QStringList texFileList(QString const & filename)
276 FileName const file = libFileSearch(QString(), filename);
281 vector<docstring> doclist =
282 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
284 // Normalise paths like /foo//bar ==> /foo/bar
286 for (size_t i = 0; i != doclist.size(); ++i) {
287 QString file = toqstr(doclist[i]);
288 file.replace("\r", "");
289 while (file.contains("//"))
290 file.replace("//", "/");
296 return QList<QString>::fromSet(set);
299 QString const externalLineEnding(docstring const & str)
302 // The MAC clipboard uses \r for lineendings, and we use \n
303 return toqstr(subst(str, '\n', '\r'));
304 #elif defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)
305 // Windows clipboard uses \r\n for lineendings, and we use \n
306 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
313 docstring const internalLineEnding(QString const & str)
315 docstring const s = subst(qstring_to_ucs4(str),
316 from_ascii("\r\n"), from_ascii("\n"));
317 return subst(s, '\r', '\n');
321 QString internalPath(const QString & str)
323 return toqstr(os::internal_path(fromqstr(str)));
327 QString onlyFileName(const QString & str)
329 return toqstr(support::onlyFileName(fromqstr(str)));
333 QString onlyPath(const QString & str)
335 return toqstr(support::onlyPath(fromqstr(str)));
339 QString changeExtension(QString const & oldname, QString const & ext)
341 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
344 /// Remove the extension from \p name
345 QString removeExtension(QString const & name)
347 return toqstr(support::removeExtension(fromqstr(name)));
350 /** Add the extension \p ext to \p name.
351 Use this instead of changeExtension if you know that \p name is without
352 extension, because changeExtension would wrongly interpret \p name if it
355 QString addExtension(QString const & name, QString const & ext)
357 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
360 /// Return the extension of the file (not including the .)
361 QString getExtension(QString const & name)
363 return toqstr(support::getExtension(fromqstr(name)));
367 /** Convert relative path into absolute path based on a basepath.
368 If relpath is absolute, just use that.
369 If basepath doesn't exist use CWD.
371 QString makeAbsPath(QString const & relpath, QString const & base)
373 return toqstr(support::makeAbsPath(fromqstr(relpath),
374 fromqstr(base)).absFileName());
378 /////////////////////////////////////////////////////////////////////////
382 /////////////////////////////////////////////////////////////////////////
384 /** Given a string such as
385 * "<glob> <glob> ... *.{abc,def} <glob>",
386 * convert the csh-style brace expresions:
387 * "<glob> <glob> ... *.abc *.def <glob>".
388 * Requires no system support, so should work equally on Unix, Mac, Win32.
390 static string const convert_brace_glob(string const & glob)
392 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
393 // "abc,def,ghi" as group 2, while allowing spaces in group 2.
394 static lyx::regex const glob_re(" *([^ {]*)\\{([^}]+)\\}");
395 // Matches "abc" and "abc,", storing "abc" as group 1,
396 // while ignoring surrounding spaces.
397 static lyx::regex const block_re(" *([^ ,}]+) *,? *");
401 string::const_iterator it = glob.begin();
402 string::const_iterator const end = glob.end();
404 match_results<string::const_iterator> what;
405 if (!regex_search(it, end, what, glob_re)) {
406 // Ensure that no information is lost.
407 pattern += string(it, end);
411 // Everything from the start of the input to
412 // the start of the match.
413 pattern += string(what[-1].first, what[-1].second);
415 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
416 string const head = string(what[1].first, what[1].second);
417 string const tail = string(what[2].first, what[2].second);
419 // Split the ','-separated chunks of tail so that
420 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
421 string const fmt = " " + head + "$1";
422 pattern += regex_replace(tail, block_re, fmt);
424 // Increment the iterator to the end of the match.
425 it += distance(it, what[0].second);
434 /* \param description text describing the filters.
435 * \param one or more wildcard patterns, separated by
438 Filter(docstring const & description, std::string const & globs);
440 docstring const & description() const { return desc_; }
442 QString toString() const;
445 std::vector<std::string> globs_;
449 Filter::Filter(docstring const & description, string const & globs)
452 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
453 boost::char_separator<char> const separator(" ");
455 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
456 // "<glob> <glob> ... *.abc *.def <glob>"
457 string const expanded_globs = convert_brace_glob(globs);
459 // Split into individual globs.
460 vector<string> matches;
461 Tokenizer const tokens(expanded_globs, separator);
462 globs_ = vector<string>(tokens.begin(), tokens.end());
466 QString Filter::toString() const
470 bool const has_description = !desc_.empty();
472 if (has_description) {
477 for (size_t i = 0; i != globs_.size(); ++i) {
480 s += toqstr(globs_[i]);
489 /** \c FileFilterList parses a Qt-style list of available file filters
490 * to generate the corresponding vector.
491 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
492 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
493 * will result in a vector of size 1 in which the description field is empty.
495 struct FileFilterList
497 // FIXME UNICODE: globs_ should be unicode...
498 /** \param qt_style_filter a list of available file filters.
499 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
500 * The "All files (*)" filter is always added to the list.
502 explicit FileFilterList(docstring const & qt_style_filter =
505 typedef std::vector<Filter>::size_type size_type;
507 bool empty() const { return filters_.empty(); }
508 size_type size() const { return filters_.size(); }
509 Filter & operator[](size_type i) { return filters_[i]; }
510 Filter const & operator[](size_type i) const { return filters_[i]; }
512 void parse_filter(std::string const & filter);
513 std::vector<Filter> filters_;
517 FileFilterList::FileFilterList(docstring const & qt_style_filter)
520 string const filter = to_utf8(qt_style_filter)
521 + (qt_style_filter.empty() ? string() : ";;")
522 + to_utf8(_("All Files "))
529 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
530 // into individual filters.
531 static lyx::regex const separator_re(";;");
533 string::const_iterator it = filter.begin();
534 string::const_iterator const end = filter.end();
536 match_results<string::const_iterator> what;
538 if (!lyx::regex_search(it, end, what, separator_re)) {
539 parse_filter(string(it, end));
543 // Everything from the start of the input to
544 // the start of the match.
545 parse_filter(string(what[-1].first, what[-1].second));
547 // Increment the iterator to the end of the match.
548 it += distance(it, what[0].second);
553 void FileFilterList::parse_filter(string const & filter)
555 // Matches "TeX documents (plain) (*.tex)",
556 // storing "TeX documents (plain) " as group 1 and "*.tex" as group 2.
557 static lyx::regex const filter_re("(.*)\\(([^()]+)\\) *$");
559 match_results<string::const_iterator> what;
560 if (!lyx::regex_search(filter, what, filter_re)) {
561 // Just a glob, no description.
562 filters_.push_back(Filter(docstring(), trim(filter)));
565 docstring const desc = from_utf8(string(what[1].first, what[1].second));
566 string const globs = string(what[2].first, what[2].second);
567 filters_.push_back(Filter(trim(desc), trim(globs)));
572 /** \returns the equivalent of the string passed in
573 * although any brace expressions are expanded.
574 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
576 QStringList fileFilters(QString const & desc)
578 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
579 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
580 FileFilterList filters(qstring_to_ucs4(desc));
581 //LYXERR0("DESC: " << desc);
583 for (size_t i = 0; i != filters.filters_.size(); ++i) {
584 QString f = filters.filters_[i].toString();
585 //LYXERR0("FILTER: " << f);
592 QString guiName(string const & type, BufferParams const & bp)
594 if (type == "tableofcontents")
595 return qt_("Table of Contents");
597 return qt_("Child Documents");
598 if (type == "graphics")
599 return qt_("Graphics");
600 if (type == "equation")
601 return qt_("Equations");
602 if (type == "footnote")
603 return qt_("Footnotes");
604 if (type == "listing")
605 return qt_("Listings");
607 return qt_("Index Entries");
608 if (type == "marginalnote")
609 return qt_("Marginal notes");
610 if (type == "nomencl")
611 return qt_("Nomenclature Entries");
614 if (type == "citation")
615 return qt_("Citations");
617 return qt_("Labels and References");
618 if (type == "branch")
619 return qt_("Branches");
620 if (type == "change")
621 return qt_("Changes");
623 FloatList const & floats = bp.documentClass().floats();
624 if (floats.typeExist(type))
625 return qt_(floats.getType(type).listName());