]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/qt_helpers.cpp
Typos.
[lyx.git] / src / frontends / qt4 / qt_helpers.cpp
1 /**
2  * \file qt_helpers.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Dekel Tsur
7  * \author Jürgen Spitzmüller
8  * \author Richard Heck
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "qt_helpers.h"
16
17 #include "FileDialog.h"
18 #include "LengthCombo.h"
19
20 #include "frontends/alert.h"
21
22 #include "BufferParams.h"
23 #include "FloatList.h"
24 #include "Language.h"
25 #include "Length.h"
26 #include "TextClass.h"
27
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"
38
39 #include <QCheckBox>
40 #include <QComboBox>
41 #include <QLineEdit>
42 #include <QPalette>
43 #include <QSet>
44
45 #include <algorithm>
46 #include <fstream>
47 #include <locale>
48
49 // for FileFilter.
50 // FIXME: Remove
51 #include <boost/regex.hpp>
52 #include <boost/tokenizer.hpp>
53
54
55 using namespace std;
56 using namespace lyx::support;
57
58 namespace lyx {
59
60 FileName libFileSearch(QString const & dir, QString const & name,
61                                 QString const & ext)
62 {
63         return support::libFileSearch(fromqstr(dir), fromqstr(name), fromqstr(ext));
64 }
65
66
67 namespace frontend {
68
69 string widgetsToLength(QLineEdit const * input, LengthCombo const * combo)
70 {
71         QString const length = input->text();
72         if (length.isEmpty())
73                 return string();
74
75         // Don't return unit-from-choice if the input(field) contains a unit
76         if (isValidGlueLength(fromqstr(length)))
77                 return fromqstr(length);
78
79         Length::UNIT const unit = combo->currentLengthItem();
80
81         return Length(length.toDouble(), unit).asString();
82 }
83
84
85 Length widgetsToLength(QLineEdit const * input, QComboBox const * combo)
86 {
87         QString const length = input->text();
88         if (length.isEmpty())
89                 return Length();
90
91         // don't return unit-from-choice if the input(field) contains a unit
92         if (isValidGlueLength(fromqstr(length)))
93                 return Length(fromqstr(length));
94
95         Length::UNIT const unit = unitFromString(fromqstr(combo->currentText()));
96
97         return Length(length.toDouble(), unit);
98 }
99
100
101 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
102                      Length const & len, Length::UNIT /*defaultUnit*/)
103 {
104         combo->setCurrentItem(len.unit());
105         input->setText(QString::number(Length(len).value()));
106 }
107
108
109 void lengthToWidgets(QLineEdit * input, LengthCombo * combo,
110         string const & len, Length::UNIT defaultUnit)
111 {
112         if (len.empty()) {
113                 // no length (UNIT_NONE)
114                 combo->setCurrentItem(defaultUnit);
115                 input->setText("");
116         } else if (!isValidLength(len) && !isStrDbl(len)) {
117                 // use input field only for gluelengths
118                 combo->setCurrentItem(defaultUnit);
119                 input->setText(toqstr(len));
120         } else {
121                 lengthToWidgets(input, combo, Length(len), defaultUnit);
122         }
123 }
124
125
126 void lengthAutoToWidgets(QLineEdit * input, LengthCombo * combo,
127         Length const & len, Length::UNIT defaultUnit)
128 {
129         if (len.value() == 0)
130                 lengthToWidgets(input, combo, "auto", defaultUnit);
131         else
132                 lengthToWidgets(input, combo, len, defaultUnit);
133 }
134
135
136 void setValid(QWidget * widget, bool valid)
137 {
138         if (valid) {
139                 widget->setPalette(QPalette());
140         } else {
141                 QPalette pal = widget->palette();
142                 pal.setColor(QPalette::Active, QPalette::Foreground, QColor(255, 0, 0));
143                 widget->setPalette(pal);
144         }
145 }
146
147 } // namespace frontend
148
149 QString const qt_(char const * str, const char *)
150 {
151         return toqstr(_(str));
152 }
153
154
155 QString const qt_(string const & str)
156 {
157         return toqstr(_(str));
158 }
159
160
161 void rescanTexStyles()
162 {
163         // Run rescan in user lyx directory
164         PathChanger p(package().user_support());
165         FileName const command = support::libFileSearch("scripts", "TeXFiles.py");
166         Systemcall one;
167         int const status = one.startscript(Systemcall::Wait,
168                         os::python() + ' ' +
169                         quoteName(command.toFilesystemEncoding()));
170         if (status == 0)
171                 return;
172         // FIXME UNICODE
173         frontend::Alert::error(_("Could not update TeX information"),
174                 bformat(_("The script `%s' failed."), from_utf8(command.absFilename())));
175 }
176
177
178 QStringList texFileList(QString const & filename)
179 {
180         QStringList list;
181         FileName const file = libFileSearch(QString(), filename);
182         if (file.empty())
183                 return list;
184
185         // FIXME Unicode.
186         vector<docstring> doclist = 
187                 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
188
189         // Normalise paths like /foo//bar ==> /foo/bar
190         QSet<QString> set;
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("//", "/");
196                 if (!file.isEmpty())
197                         set.insert(file);
198         }
199
200         // remove duplicates
201         return QList<QString>::fromSet(set);
202 }
203
204 QString const externalLineEnding(docstring const & str)
205 {
206 #ifdef Q_WS_MACX
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")));
212 #else
213         return toqstr(str);
214 #endif
215 }
216
217
218 docstring const internalLineEnding(QString const & str)
219 {
220         docstring const s = subst(qstring_to_ucs4(str), 
221                                   from_ascii("\r\n"), from_ascii("\n"));
222         return subst(s, '\r', '\n');
223 }
224
225
226 QString internalPath(const QString & str)
227 {
228         return toqstr(os::internal_path(fromqstr(str)));
229 }
230
231
232 QString onlyFilename(const QString & str)
233 {
234         return toqstr(support::onlyFilename(fromqstr(str)));
235 }
236
237
238 QString onlyPath(const QString & str)
239 {
240         return toqstr(support::onlyPath(fromqstr(str)));
241 }
242
243
244 QString changeExtension(QString const & oldname, QString const & ext)
245 {
246         return toqstr(support::changeExtension(fromqstr(oldname), fromqstr(ext)));
247 }
248
249 /// Remove the extension from \p name
250 QString removeExtension(QString const & name)
251 {
252         return toqstr(support::removeExtension(fromqstr(name)));
253 }
254
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
258  contains a dot.
259  */
260 QString addExtension(QString const & name, QString const & ext)
261 {
262         return toqstr(support::addExtension(fromqstr(name), fromqstr(ext)));
263 }
264
265 /// Return the extension of the file (not including the .)
266 QString getExtension(QString const & name)
267 {
268         return toqstr(support::getExtension(fromqstr(name)));
269 }
270
271
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.
275   */
276 QString makeAbsPath(QString const & relpath, QString const & base)
277 {
278         return toqstr(support::makeAbsPath(fromqstr(relpath),
279                 fromqstr(base)).absFilename());
280 }
281
282
283 /////////////////////////////////////////////////////////////////////////
284 //
285 // FileFilterList
286 //
287 /////////////////////////////////////////////////////////////////////////
288
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.
294  */
295 static string const convert_brace_glob(string const & glob)
296 {
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("([^,}]+),?");
302
303         string pattern;
304
305         string::const_iterator it = glob.begin();
306         string::const_iterator const end = glob.end();
307         while (true) {
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);
312                         break;
313                 }
314
315                 // Everything from the start of the input to
316                 // the start of the match.
317                 pattern += string(what[-1].first, what[-1].second);
318
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);
322
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);
327
328                 // Increment the iterator to the end of the match.
329                 it += distance(it, what[0].second);
330         }
331
332         return pattern;
333 }
334
335
336 struct Filter
337 {
338         /* \param description text describing the filters.
339          * \param one or more wildcard patterns, separated by
340          * whitespace.
341          */
342         Filter(docstring const & description, std::string const & globs);
343
344         docstring const & description() const { return desc_; }
345
346         QString toString() const;
347
348         docstring desc_;
349         std::vector<std::string> globs_;
350 };
351
352
353 Filter::Filter(docstring const & description, string const & globs)
354         : desc_(description)
355 {
356         typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
357         boost::char_separator<char> const separator(" ");
358
359         // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
360         //       "<glob> <glob> ... *.abc *.def <glob>"
361         string const expanded_globs = convert_brace_glob(globs);
362
363         // Split into individual globs.
364         vector<string> matches;
365         Tokenizer const tokens(expanded_globs, separator);
366         globs_ = vector<string>(tokens.begin(), tokens.end());
367 }
368
369
370 QString Filter::toString() const
371 {
372         QString s;
373
374         bool const has_description = desc_.empty();
375
376         if (has_description) {
377                 s += toqstr(desc_);
378                 s += " (";
379         }
380
381         for (size_t i = 0; i != globs_.size(); ++i) {
382                 if (i > 0)
383                         s += ' ';
384                 s += toqstr(globs_[i]);
385         }
386
387         if (has_description)
388                 s += ')';
389         return s;
390 }
391
392
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.
398  */
399 struct FileFilterList
400 {
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.
405          */
406         explicit FileFilterList(docstring const & qt_style_filter =
407                                 docstring());
408
409         typedef std::vector<Filter>::size_type size_type;
410
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]; }
415
416         void parse_filter(std::string const & filter);
417         std::vector<Filter> filters_;
418 };
419
420
421 FileFilterList::FileFilterList(docstring const & qt_style_filter)
422 {
423         // FIXME UNICODE
424         string const filter = to_utf8(qt_style_filter)
425                 + (qt_style_filter.empty() ? string() : ";;")
426                 + to_utf8(_("All Files "))
427 #if defined(_WIN32)             
428                 + ("(*.*)");
429 #else
430                 + ("(*)");
431 #endif
432
433         // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
434         // into individual filters.
435         static boost::regex const separator_re(";;");
436
437         string::const_iterator it = filter.begin();
438         string::const_iterator const end = filter.end();
439         while (true) {
440                 boost::match_results<string::const_iterator> what;
441
442                 if (!boost::regex_search(it, end, what, separator_re)) {
443                         parse_filter(string(it, end));
444                         break;
445                 }
446
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));
450
451                 // Increment the iterator to the end of the match.
452                 it += distance(it, what[0].second);
453         }
454 }
455
456
457 void FileFilterList::parse_filter(string const & filter)
458 {
459         // Matches "TeX documents (*.tex)",
460         // storing "TeX documents " as group 1 and "*.tex" as group 2.
461         static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
462
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)));
467         } else {
468                 // FIXME UNICODE
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)));
472         }
473 }
474
475
476 /** \returns the equivalent of the string passed in
477  *  although any brace expressions are expanded.
478  *  (E.g. "*.{png,jpg}" -> "*.png *.jpg")
479  */
480 QStringList fileFilters(QString const & desc)
481 {
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);
486         QStringList list;
487         for (size_t i = 0; i != filters.filters_.size(); ++i) {
488                 QString f = filters.filters_[i].toString();
489                 //LYXERR0("FILTER: " << f);
490                 list.append(f);
491         }
492         return list;
493 }
494
495
496 QString guiName(string const & type, BufferParams const & bp)
497 {
498         if (type == "tableofcontents")
499                 return qt_("Table of Contents");
500         if (type == "child")
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");
510         if (type == "index")
511                 return qt_("List of Indexes");
512         if (type == "marginalnote")
513                 return qt_("List of Marginal notes");
514         if (type == "note")
515                 return qt_("List of Notes");
516         if (type == "citation")
517                 return qt_("List of Citations");
518         if (type == "label")
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");
524
525         FloatList const & floats = bp.documentClass().floats();
526         if (floats.typeExist(type))
527                 return qt_(floats.getType(type).listName());
528
529         return qt_(type);
530 }
531
532
533 } // namespace lyx