]> git.lyx.org Git - lyx.git/blob - src/support/FileFilterList.cpp
fix linking error in the frontend
[lyx.git] / src / support / FileFilterList.cpp
1 /**
2  * \file FileFilterList.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "support/FileFilterList.h"
14 #include "support/lstrings.h"
15
16 // FIXME Interface violation
17 #include "gettext.h"
18
19 #include <boost/regex.hpp>
20 #include <boost/tokenizer.hpp>
21
22 #include <sstream>
23
24 using lyx::docstring;
25
26 using std::distance;
27 using std::ostringstream;
28 using std::string;
29 using std::vector;
30
31
32 namespace {
33
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.
39  */
40 string const convert_brace_glob(string const & glob)
41 {
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("([^,}]+),?");
47
48         string pattern;
49
50         string::const_iterator it = glob.begin();
51         string::const_iterator const end = glob.end();
52         while (true) {
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);
57                         break;
58                 }
59
60                 // Everything from the start of the input to
61                 // the start of the match.
62                 pattern += string(what[-1].first, what[-1].second);
63
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);
67
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);
72
73                 // Increment the iterator to the end of the match.
74                 it += distance(it, what[0].second);
75         }
76
77         return pattern;
78 }
79
80 } // namespace anon
81
82
83 namespace lyx {
84 namespace support {
85
86 FileFilterList::Filter::Filter(lyx::docstring const & description,
87                                std::string const & globs)
88         : desc_(description)
89 {
90         typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
91         boost::char_separator<char> const separator(" ");
92
93         // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
94         //       "<glob> <glob> ... *.abc *.def <glob>"
95         string const expanded_globs = convert_brace_glob(globs);
96
97         // Split into individual globs.
98         vector<string> matches;
99         Tokenizer const tokens(expanded_globs, separator);
100         globs_ = vector<string>(tokens.begin(), tokens.end());
101 }
102
103
104 FileFilterList::FileFilterList(docstring const & qt_style_filter)
105 {
106         // FIXME UNICODE
107         string const filter = lyx::to_utf8(qt_style_filter)
108                 + (qt_style_filter.empty() ? string() : ";;")
109                 + lyx::to_utf8(_("All files (*)"));
110
111         // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
112         // into individual filters.
113         static boost::regex const separator_re(";;");
114
115         string::const_iterator it = filter.begin();
116         string::const_iterator const end = filter.end();
117         while (true) {
118                 boost::match_results<string::const_iterator> what;
119
120                 if (!boost::regex_search(it, end, what, separator_re)) {
121                         parse_filter(string(it, end));
122                         break;
123                 }
124
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));
128
129                 // Increment the iterator to the end of the match.
130                 it += distance(it, what[0].second);
131         }
132 }
133
134
135 void FileFilterList::parse_filter(string const & filter)
136 {
137         // Matches "TeX documents (*.tex)",
138         // storing "TeX documents " as group 1 and "*.tex" as group 2.
139         static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
140
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)));
145         } else {
146                 // FIXME UNICODE
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)));
150         }
151 }
152
153
154 docstring const FileFilterList::as_string() const
155 {
156         // FIXME UNICODE
157         ostringstream ss;
158
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();
164                 if (gbegin == gend)
165                         continue;
166
167                 if (ss.tellp() > 0)
168                         ss << ";;";
169
170                 bool const has_description = !fit->description().empty();
171                 if (has_description)
172                         ss << lyx::to_utf8(fit->description()) << " (";
173
174                 for (Filter::glob_iterator git = gbegin; git != gend; ++git) {
175                         if (git != gbegin)
176                                 ss << ' ';
177                         ss << *git;
178                 }
179
180                 if (has_description)
181                         ss << ')';
182         }
183
184         return lyx::from_utf8(ss.str());
185 }
186
187 } // namespace support
188 } // namespace lyx