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));
81 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
83 QString const length = input->text();
87 // Don't return unit-from-choice if the input(field) contains a unit
88 if (isValidGlueLength(fromqstr(length)))
89 return fromqstr(length);
91 Length::UNIT const unit = combo->currentLengthItem();
93 return Length(length.trimmed().toDouble(), unit).asString();
97 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
99 QString const length = input->text();
100 if (length.isEmpty())
103 // don't return unit-from-choice if the input(field) contains a unit
104 if (isValidGlueLength(fromqstr(length)))
105 return Length(fromqstr(length));
107 Length::UNIT unit = Length::UNIT_NONE;
108 QString const item = combo->currentText();
109 for (int i = 0; i < num_units; i++) {
110 if (qt_(lyx::unit_name_gui[i]) == item) {
111 unit = unitFromString(unit_name[i]);
116 return Length(length.trimmed().toDouble(), unit);
120 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
121 Length const & len, Length::UNIT /*defaultUnit*/)
123 combo->setCurrentItem(len.unit());
125 loc.setNumberOptions(QLocale::OmitGroupSeparator);
126 input->setText(loc.toString(Length(len).value()));
130 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
131 string const & len, Length::UNIT defaultUnit)
134 // no length (UNIT_NONE)
135 combo->setCurrentItem(defaultUnit);
137 } else if (!isValidLength(len) && !isStrDbl(len)) {
138 // use input field only for gluelengths
139 combo->setCurrentItem(defaultUnit);
140 input->setText(toqstr(len));
142 lengthToWidgets(input, combo, Length(len), defaultUnit);
147 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
148 docstring const & len, Length::UNIT defaultUnit)
150 lengthToWidgets(input, combo, to_utf8(len), defaultUnit);
154 double widgetToDouble(QLineEdit const * input)
156 QString const text = input->text();
160 return text.trimmed().toDouble();
164 string widgetToDoubleStr(QLineEdit const * input)
166 QString const text = input->text();
170 return convert<string>(text.trimmed().toDouble());
174 void doubleToWidget(QLineEdit * input, double const & value, char f, int prec)
177 loc.setNumberOptions(QLocale::OmitGroupSeparator);
178 input->setText(loc.toString(value, f, prec));
182 void doubleToWidget(QLineEdit * input, string const & value, char f, int prec)
184 doubleToWidget(input, convert<double>(value), f, prec);
188 void setValid(QWidget * widget, bool valid)
191 widget->setPalette(QPalette());
193 QPalette pal = widget->palette();
194 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
195 widget->setPalette(pal);
199 /// wrapper to hide the change of method name to setSectionResizeMode
200 void setSectionResizeMode(QHeaderView * view,
201 int logicalIndex, QHeaderView::ResizeMode mode) {
202 #if (QT_VERSION >= 0x050000)
203 view->setSectionResizeMode(logicalIndex, mode);
205 view->setResizeMode(logicalIndex, mode);
209 void setSectionResizeMode(QHeaderView * view, QHeaderView::ResizeMode mode) {
210 #if (QT_VERSION >= 0x050000)
211 view->setSectionResizeMode(mode);
213 view->setResizeMode(mode);
216 } // namespace frontend
218 QString const qt_(char const * str, const char *)
220 return toqstr(_(str));
224 QString const qt_(string const & str)
226 return toqstr(_(str));
230 void rescanTexStyles(string const & arg)
232 // Run rescan in user lyx directory
233 PathChanger p(package().user_support());
234 FileName const prog = support::libFileSearch("scripts", "TeXFiles.py");
236 string const command = os::python() + ' ' +
237 quoteName(prog.toFilesystemEncoding()) + ' ' +
239 int const status = one.startscript(Systemcall::Wait, command);
243 frontend::Alert::error(_("Could not update TeX information"),
244 bformat(_("The script `%1$s' failed."), from_utf8(prog.absFileName())));
248 QStringList texFileList(QString const & filename)
251 FileName const file = libFileSearch(QString(), filename);
256 vector<docstring> doclist =
257 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
259 // Normalise paths like /foo//bar ==> /foo/bar
261 for (size_t i = 0; i != doclist.size(); ++i) {
262 QString file = toqstr(doclist[i]);
263 file.replace("\r", "");
264 while (file.contains("//"))
265 file.replace("//", "/");
271 return QList<QString>::fromSet(set);
274 QString const externalLineEnding(docstring const & str)
277 // The MAC clipboard uses \r for lineendings, and we use \n
278 return toqstr(subst(str, '\n', '\r'));
279 #elif defined(Q_WS_WIN)
280 // Windows clipboard uses \r\n for lineendings, and we use \n
281 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
288 docstring const internalLineEnding(QString const & str)
290 docstring const s = subst(qstring_to_ucs4(str),
291 from_ascii("\r\n"), from_ascii("\n"));
292 return subst(s, '\r', '\n');
296 QString internalPath(const QString & str)
298 return toqstr(os::internal_path(fromqstr(str)));
302 QString onlyFileName(const QString & str)
304 return toqstr(support::onlyFileName(fromqstr(str)));
308 QString onlyPath(const QString & str)
310 return toqstr(support::onlyPath(fromqstr(str)));
314 QString changeExtension(QString const & oldname, QString const & ext)
316 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
319 /// Remove the extension from \p name
320 QString removeExtension(QString const & name)
322 return toqstr(support::removeExtension(fromqstr(name)));
325 /** Add the extension \p ext to \p name.
326 Use this instead of changeExtension if you know that \p name is without
327 extension, because changeExtension would wrongly interpret \p name if it
330 QString addExtension(QString const & name, QString const & ext)
332 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
335 /// Return the extension of the file (not including the .)
336 QString getExtension(QString const & name)
338 return toqstr(support::getExtension(fromqstr(name)));
342 /** Convert relative path into absolute path based on a basepath.
343 If relpath is absolute, just use that.
344 If basepath doesn't exist use CWD.
346 QString makeAbsPath(QString const & relpath, QString const & base)
348 return toqstr(support::makeAbsPath(fromqstr(relpath),
349 fromqstr(base)).absFileName());
353 /////////////////////////////////////////////////////////////////////////
357 /////////////////////////////////////////////////////////////////////////
359 /** Given a string such as
360 * "<glob> <glob> ... *.{abc,def} <glob>",
361 * convert the csh-style brace expresions:
362 * "<glob> <glob> ... *.abc *.def <glob>".
363 * Requires no system support, so should work equally on Unix, Mac, Win32.
365 static string const convert_brace_glob(string const & glob)
367 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
368 // "abc,def,ghi" as group 2, while allowing spaces in group 2.
369 static lyx::regex const glob_re(" *([^ {]*)\\{([^}]+)\\}");
370 // Matches "abc" and "abc,", storing "abc" as group 1,
371 // while ignoring surrounding spaces.
372 static lyx::regex const block_re(" *([^ ,}]+) *,? *");
376 string::const_iterator it = glob.begin();
377 string::const_iterator const end = glob.end();
379 match_results<string::const_iterator> what;
380 if (!regex_search(it, end, what, glob_re)) {
381 // Ensure that no information is lost.
382 pattern += string(it, end);
386 // Everything from the start of the input to
387 // the start of the match.
388 pattern += string(what[-1].first, what[-1].second);
390 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
391 string const head = string(what[1].first, what[1].second);
392 string const tail = string(what[2].first, what[2].second);
394 // Split the ','-separated chunks of tail so that
395 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
396 string const fmt = " " + head + "$1";
397 pattern += regex_replace(tail, block_re, fmt);
399 // Increment the iterator to the end of the match.
400 it += distance(it, what[0].second);
409 /* \param description text describing the filters.
410 * \param one or more wildcard patterns, separated by
413 Filter(docstring const & description, std::string const & globs);
415 docstring const & description() const { return desc_; }
417 QString toString() const;
420 std::vector<std::string> globs_;
424 Filter::Filter(docstring const & description, string const & globs)
427 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
428 boost::char_separator<char> const separator(" ");
430 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
431 // "<glob> <glob> ... *.abc *.def <glob>"
432 string const expanded_globs = convert_brace_glob(globs);
434 // Split into individual globs.
435 vector<string> matches;
436 Tokenizer const tokens(expanded_globs, separator);
437 globs_ = vector<string>(tokens.begin(), tokens.end());
441 QString Filter::toString() const
445 bool const has_description = !desc_.empty();
447 if (has_description) {
452 for (size_t i = 0; i != globs_.size(); ++i) {
455 s += toqstr(globs_[i]);
464 /** \c FileFilterList parses a Qt-style list of available file filters
465 * to generate the corresponding vector.
466 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
467 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
468 * will result in a vector of size 1 in which the description field is empty.
470 struct FileFilterList
472 // FIXME UNICODE: globs_ should be unicode...
473 /** \param qt_style_filter a list of available file filters.
474 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
475 * The "All files (*)" filter is always added to the list.
477 explicit FileFilterList(docstring const & qt_style_filter =
480 typedef std::vector<Filter>::size_type size_type;
482 bool empty() const { return filters_.empty(); }
483 size_type size() const { return filters_.size(); }
484 Filter & operator[](size_type i) { return filters_[i]; }
485 Filter const & operator[](size_type i) const { return filters_[i]; }
487 void parse_filter(std::string const & filter);
488 std::vector<Filter> filters_;
492 FileFilterList::FileFilterList(docstring const & qt_style_filter)
495 string const filter = to_utf8(qt_style_filter)
496 + (qt_style_filter.empty() ? string() : ";;")
497 + to_utf8(_("All Files "))
504 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
505 // into individual filters.
506 static lyx::regex const separator_re(";;");
508 string::const_iterator it = filter.begin();
509 string::const_iterator const end = filter.end();
511 match_results<string::const_iterator> what;
513 if (!lyx::regex_search(it, end, what, separator_re)) {
514 parse_filter(string(it, end));
518 // Everything from the start of the input to
519 // the start of the match.
520 parse_filter(string(what[-1].first, what[-1].second));
522 // Increment the iterator to the end of the match.
523 it += distance(it, what[0].second);
528 void FileFilterList::parse_filter(string const & filter)
530 // Matches "TeX documents (plain) (*.tex)",
531 // storing "TeX documents (plain) " as group 1 and "*.tex" as group 2.
532 static lyx::regex const filter_re("(.*)\\(([^()]+)\\) *$");
534 match_results<string::const_iterator> what;
535 if (!lyx::regex_search(filter, what, filter_re)) {
536 // Just a glob, no description.
537 filters_.push_back(Filter(docstring(), trim(filter)));
540 docstring const desc = from_utf8(string(what[1].first, what[1].second));
541 string const globs = string(what[2].first, what[2].second);
542 filters_.push_back(Filter(trim(desc), trim(globs)));
547 /** \returns the equivalent of the string passed in
548 * although any brace expressions are expanded.
549 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
551 QStringList fileFilters(QString const & desc)
553 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
554 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
555 FileFilterList filters(qstring_to_ucs4(desc));
556 //LYXERR0("DESC: " << desc);
558 for (size_t i = 0; i != filters.filters_.size(); ++i) {
559 QString f = filters.filters_[i].toString();
560 //LYXERR0("FILTER: " << f);
567 QString guiName(string const & type, BufferParams const & bp)
569 if (type == "tableofcontents")
570 return qt_("Table of Contents");
572 return qt_("Child Documents");
573 if (type == "graphics")
574 return qt_("Graphics");
575 if (type == "equation")
576 return qt_("Equations");
577 if (type == "footnote")
578 return qt_("Footnotes");
579 if (type == "listing")
580 return qt_("Listings");
582 return qt_("Index Entries");
583 if (type == "marginalnote")
584 return qt_("Marginal notes");
585 if (type == "nomencl")
586 return qt_("Nomenclature Entries");
589 if (type == "citation")
590 return qt_("Citations");
592 return qt_("Labels and References");
593 if (type == "branch")
594 return qt_("Branches");
595 if (type == "change")
596 return qt_("Changes");
598 FloatList const & floats = bp.documentClass().floats();
599 if (floats.typeExist(type))
600 return qt_(floats.getType(type).listName());