2 * \file FileFilterList.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Angus Leeming
8 * Full author contact details are available in file CREDITS.
13 #include "support/FileFilterList.h"
14 #include "support/lstrings.h"
16 // FIXME Interface violation
19 #include <boost/regex.hpp>
20 #include <boost/tokenizer.hpp>
27 using std::ostringstream;
34 /** Given a string such as
35 * "<glob> <glob> ... *.{abc,def} <glob>",
36 * convert the csh-style brace expresions:
37 * "<glob> <glob> ... *.abc *.def <glob>".
38 * Requires no system support, so should work equally on Unix, Mac, Win32.
40 string const convert_brace_glob(string const & glob)
42 // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
43 // "abc,def,ghi" as group 2.
44 static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
45 // Matches "abc" and "abc,", storing "abc" as group 1.
46 static boost::regex const block_re("([^,}]+),?");
50 string::const_iterator it = glob.begin();
51 string::const_iterator const end = glob.end();
53 boost::match_results<string::const_iterator> what;
54 if (!boost::regex_search(it, end, what, glob_re)) {
55 // Ensure that no information is lost.
56 pattern += string(it, end);
60 // Everything from the start of the input to
61 // the start of the match.
62 pattern += string(what[-1].first, what[-1].second);
64 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
65 string const head = string(what[1].first, what[1].second);
66 string const tail = string(what[2].first, what[2].second);
68 // Split the ','-separated chunks of tail so that
69 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
70 string const fmt = " " + head + "$1";
71 pattern += boost::regex_merge(tail, block_re, fmt);
73 // Increment the iterator to the end of the match.
74 it += distance(it, what[0].second);
86 FileFilterList::Filter::Filter(lyx::docstring const & description,
87 std::string const & globs)
90 typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
91 boost::char_separator<char> const separator(" ");
93 // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
94 // "<glob> <glob> ... *.abc *.def <glob>"
95 string const expanded_globs = convert_brace_glob(globs);
97 // Split into individual globs.
98 vector<string> matches;
99 Tokenizer const tokens(expanded_globs, separator);
100 globs_ = vector<string>(tokens.begin(), tokens.end());
104 FileFilterList::FileFilterList(docstring const & qt_style_filter)
107 string const filter = lyx::to_utf8(qt_style_filter)
108 + (qt_style_filter.empty() ? string() : ";;")
109 + lyx::to_utf8(_("All files (*)"));
111 // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
112 // into individual filters.
113 static boost::regex const separator_re(";;");
115 string::const_iterator it = filter.begin();
116 string::const_iterator const end = filter.end();
118 boost::match_results<string::const_iterator> what;
120 if (!boost::regex_search(it, end, what, separator_re)) {
121 parse_filter(string(it, end));
125 // Everything from the start of the input to
126 // the start of the match.
127 parse_filter(string(what[-1].first, what[-1].second));
129 // Increment the iterator to the end of the match.
130 it += distance(it, what[0].second);
135 void FileFilterList::parse_filter(string const & filter)
137 // Matches "TeX documents (*.tex)",
138 // storing "TeX documents " as group 1 and "*.tex" as group 2.
139 static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
141 boost::match_results<string::const_iterator> what;
142 if (!boost::regex_search(filter, what, filter_re)) {
143 // Just a glob, no description.
144 filters_.push_back(Filter(docstring(), trim(filter)));
147 docstring const desc = lyx::from_utf8(string(what[1].first, what[1].second));
148 string const globs = string(what[2].first, what[2].second);
149 filters_.push_back(Filter(trim(desc), trim(globs)));
154 docstring const FileFilterList::as_string() const
159 vector<Filter>::const_iterator fit = filters_.begin();
160 vector<Filter>::const_iterator const fend = filters_.end();
161 for (; fit != fend; ++fit) {
162 Filter::glob_iterator const gbegin = fit->begin();
163 Filter::glob_iterator const gend = fit->end();
170 bool const has_description = !fit->description().empty();
172 ss << lyx::to_utf8(fit->description()) << " (";
174 for (Filter::glob_iterator git = gbegin; git != gend; ++git) {
184 return lyx::from_utf8(ss.str());
187 } // namespace support