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 QString const qt_(QString const & qstr)
232 return toqstr(_(fromqstr(qstr)));
236 void rescanTexStyles(string const & arg)
238 // Run rescan in user lyx directory
239 PathChanger p(package().user_support());
240 FileName const prog = support::libFileSearch("scripts", "TeXFiles.py");
242 string const command = os::python() + ' ' +
243 quoteName(prog.toFilesystemEncoding()) + ' ' +
245 int const status = one.startscript(Systemcall::Wait, command);
249 frontend::Alert::error(_("Could not update TeX information"),
250 bformat(_("The script `%1$s' failed."), from_utf8(prog.absFileName())));
254 QStringList texFileList(QString const & filename)
257 FileName const file = libFileSearch(QString(), filename);
262 vector<docstring> doclist =
263 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
265 // Normalise paths like /foo//bar ==> /foo/bar
267 for (size_t i = 0; i != doclist.size(); ++i) {
268 QString file = toqstr(doclist[i]);
269 file.replace("\r", "");
270 while (file.contains("//"))
271 file.replace("//", "/");
277 return QList<QString>::fromSet(set);
280 QString const externalLineEnding(docstring const & str)
283 // The MAC clipboard uses \r for lineendings, and we use \n
284 return toqstr(subst(str, '\n', '\r'));
285 #elif defined(Q_WS_WIN)
286 // Windows clipboard uses \r\n for lineendings, and we use \n
287 return toqstr(subst(str, from_ascii("\n"), from_ascii("\r\n")));
294 docstring const internalLineEnding(QString const & str)
296 docstring const s = subst(qstring_to_ucs4(str),
297 from_ascii("\r\n"), from_ascii("\n"));
298 return subst(s, '\r', '\n');
302 QString internalPath(const QString & str)
304 return toqstr(os::internal_path(fromqstr(str)));
308 QString onlyFileName(const QString & str)
310 return toqstr(support::onlyFileName(fromqstr(str)));
314 QString onlyPath(const QString & str)
316 return toqstr(support::onlyPath(fromqstr(str)));
320 QString changeExtension(QString const & oldname, QString const & ext)
322 return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
325 /// Remove the extension from \p name
326 QString removeExtension(QString const & name)
328 return toqstr(support::removeExtension(fromqstr(name)));
331 /** Add the extension \p ext to \p name.
332 Use this instead of changeExtension if you know that \p name is without
333 extension, because changeExtension would wrongly interpret \p name if it
336 QString addExtension(QString const & name, QString const & ext)
338 return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
341 /// Return the extension of the file (not including the .)
342 QString getExtension(QString const & name)
344 return toqstr(support::getExtension(fromqstr(name)));
348 /** Convert relative path into absolute path based on a basepath.
349 If relpath is absolute, just use that.
350 If basepath doesn't exist use CWD.
352 QString makeAbsPath(QString const & relpath, QString const & base)
354 return toqstr(support::makeAbsPath(fromqstr(relpath),
355 fromqstr(base)).absFileName());
359 /////////////////////////////////////////////////////////////////////////
363 /////////////////////////////////////////////////////////////////////////
365 /** Given a string such as
366 * "<glob> <glob> ... *.{abc,def} <glob>",
367 * convert the csh-style brace expresions:
368 * "<glob> <glob> ... *.abc *.def <glob>".
369 * Requires no system support, so should work equally on Unix, Mac, Win32.
371 static string const convert_brace_glob(string const & glob)
373 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
374 // "abc,def,ghi" as group 2, while allowing spaces in group 2.
375 static lyx::regex const glob_re(" *([^ {]*)\\{([^}]+)\\}");
376 // Matches "abc" and "abc,", storing "abc" as group 1,
377 // while ignoring surrounding spaces.
378 static lyx::regex const block_re(" *([^ ,}]+) *,? *");
382 string::const_iterator it = glob.begin();
383 string::const_iterator const end = glob.end();
385 match_results<string::const_iterator> what;
386 if (!regex_search(it, end, what, glob_re)) {
387 // Ensure that no information is lost.
388 pattern += string(it, end);
392 // Everything from the start of the input to
393 // the start of the match.
394 pattern += string(what[-1].first, what[-1].second);
396 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
397 string const head = string(what[1].first, what[1].second);
398 string const tail = string(what[2].first, what[2].second);
400 // Split the ','-separated chunks of tail so that
401 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
402 string const fmt = " " + head + "$1";
403 pattern += regex_replace(tail, block_re, fmt);
405 // Increment the iterator to the end of the match.
406 it += distance(it, what[0].second);
415 /* \param description text describing the filters.
416 * \param one or more wildcard patterns, separated by
419 Filter(docstring const & description, std::string const & globs);
421 docstring const & description() const { return desc_; }
423 QString toString() const;
426 std::vector<std::string> globs_;
430 Filter::Filter(docstring const & description, string const & globs)
433 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
434 boost::char_separator<char> const separator(" ");
436 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
437 // "<glob> <glob> ... *.abc *.def <glob>"
438 string const expanded_globs = convert_brace_glob(globs);
440 // Split into individual globs.
441 vector<string> matches;
442 Tokenizer const tokens(expanded_globs, separator);
443 globs_ = vector<string>(tokens.begin(), tokens.end());
447 QString Filter::toString() const
451 bool const has_description = !desc_.empty();
453 if (has_description) {
458 for (size_t i = 0; i != globs_.size(); ++i) {
461 s += toqstr(globs_[i]);
470 /** \c FileFilterList parses a Qt-style list of available file filters
471 * to generate the corresponding vector.
472 * For example "TeX documents (*.tex);;LyX Documents (*.lyx)"
473 * will be parsed to fill a vector of size 2, whilst "*.{p[bgp]m} *.pdf"
474 * will result in a vector of size 1 in which the description field is empty.
476 struct FileFilterList
478 // FIXME UNICODE: globs_ should be unicode...
479 /** \param qt_style_filter a list of available file filters.
480 * Eg. "TeX documents (*.tex);;LyX Documents (*.lyx)".
481 * The "All files (*)" filter is always added to the list.
483 explicit FileFilterList(docstring const & qt_style_filter =
486 typedef std::vector<Filter>::size_type size_type;
488 bool empty() const { return filters_.empty(); }
489 size_type size() const { return filters_.size(); }
490 Filter & operator[](size_type i) { return filters_[i]; }
491 Filter const & operator[](size_type i) const { return filters_[i]; }
493 void parse_filter(std::string const & filter);
494 std::vector<Filter> filters_;
498 FileFilterList::FileFilterList(docstring const & qt_style_filter)
501 string const filter = to_utf8(qt_style_filter)
502 + (qt_style_filter.empty() ? string() : ";;")
503 + to_utf8(_("All Files "))
510 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
511 // into individual filters.
512 static lyx::regex const separator_re(";;");
514 string::const_iterator it = filter.begin();
515 string::const_iterator const end = filter.end();
517 match_results<string::const_iterator> what;
519 if (!lyx::regex_search(it, end, what, separator_re)) {
520 parse_filter(string(it, end));
524 // Everything from the start of the input to
525 // the start of the match.
526 parse_filter(string(what[-1].first, what[-1].second));
528 // Increment the iterator to the end of the match.
529 it += distance(it, what[0].second);
534 void FileFilterList::parse_filter(string const & filter)
536 // Matches "TeX documents (plain) (*.tex)",
537 // storing "TeX documents (plain) " as group 1 and "*.tex" as group 2.
538 static lyx::regex const filter_re("(.*)\\(([^()]+)\\) *$");
540 match_results<string::const_iterator> what;
541 if (!lyx::regex_search(filter, what, filter_re)) {
542 // Just a glob, no description.
543 filters_.push_back(Filter(docstring(), trim(filter)));
546 docstring const desc = from_utf8(string(what[1].first, what[1].second));
547 string const globs = string(what[2].first, what[2].second);
548 filters_.push_back(Filter(trim(desc), trim(globs)));
553 /** \returns the equivalent of the string passed in
554 * although any brace expressions are expanded.
555 * (E.g. "*.{png,jpg}" -> "*.png *.jpg")
557 QStringList fileFilters(QString const & desc)
559 // we have: "*.{gif,png,jpg,bmp,pbm,ppm,tga,tif,xpm,xbm}"
560 // but need: "*.cpp;*.cc;*.C;*.cxx;*.c++"
561 FileFilterList filters(qstring_to_ucs4(desc));
562 //LYXERR0("DESC: " << desc);
564 for (size_t i = 0; i != filters.filters_.size(); ++i) {
565 QString f = filters.filters_[i].toString();
566 //LYXERR0("FILTER: " << f);
573 QString guiName(string const & type, BufferParams const & bp)
575 if (type == "tableofcontents")
576 return qt_("Table of Contents");
578 return qt_("Child Documents");
579 if (type == "graphics")
580 return qt_("Graphics");
581 if (type == "equation")
582 return qt_("Equations");
583 if (type == "footnote")
584 return qt_("Footnotes");
585 if (type == "listing")
586 return qt_("Listings");
588 return qt_("Index Entries");
589 if (type == "marginalnote")
590 return qt_("Marginal notes");
591 if (type == "nomencl")
592 return qt_("Nomenclature Entries");
595 if (type == "citation")
596 return qt_("Citations");
598 return qt_("Labels and References");
599 if (type == "branch")
600 return qt_("Branches");
601 if (type == "change")
602 return qt_("Changes");
604 FloatList const & floats = bp.documentClass().floats();
605 if (floats.typeExist(type))
606 return qt_(floats.getType(type).listName());