]> git.lyx.org Git - lyx.git/blob - src/support/filefilterlist.C
This commit moves system font initialization and restoration to new support/fontutils...
[lyx.git] / src / support / filefilterlist.C
1 /**
2  * \file filefilterlist.C
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 std::distance;
25 using std::ostringstream;
26 using std::string;
27 using std::vector;
28
29
30 namespace {
31
32 /** Given a string such as
33  *      "<glob> <glob> ... *.{abc,def} <glob>",
34  *  convert the csh-style brace expresions:
35  *      "<glob> <glob> ... *.abc *.def <glob>".
36  *  Requires no system support, so should work equally on Unix, Mac, Win32.
37  */
38 string const convert_brace_glob(string const & glob)
39 {
40         // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
41         // "abc,def,ghi" as group 2.
42         static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
43         // Matches "abc" and "abc,", storing "abc" as group 1.
44         static boost::regex const block_re("([^,}]+),?");
45
46         string pattern;
47
48         string::const_iterator it = glob.begin();
49         string::const_iterator const end = glob.end();
50         while (true) {
51                 boost::match_results<string::const_iterator> what;
52                 if (!boost::regex_search(it, end, what, glob_re)) {
53                         // Ensure that no information is lost.
54                         pattern += string(it, end);
55                         break;
56                 }
57
58                 // Everything from the start of the input to
59                 // the start of the match.
60                 pattern += string(what[-1].first, what[-1].second);
61
62                 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
63                 string const head = string(what[1].first, what[1].second);
64                 string const tail = string(what[2].first, what[2].second);
65
66                 // Split the ','-separated chunks of tail so that
67                 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
68                 string const fmt = " " + head + "$1";
69                 pattern += boost::regex_merge(tail, block_re, fmt);
70
71                 // Increment the iterator to the end of the match.
72                 it += distance(it, what[0].second);
73         }
74
75         return pattern;
76 }
77
78 } // namespace anon
79
80
81 namespace lyx {
82 namespace support {
83
84 FileFilterList::Filter::Filter(std::string const & description,
85                                std::string const & globs)
86         : desc_(description)
87 {
88         typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
89         boost::char_separator<char> const separator(" ");
90
91         // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
92         //       "<glob> <glob> ... *.abc *.def <glob>"
93         string const expanded_globs = convert_brace_glob(globs);
94
95         // Split into individual globs.
96         vector<string> matches;
97         Tokenizer const tokens(expanded_globs, separator);
98         globs_ = vector<string>(tokens.begin(), tokens.end());
99 }
100
101
102 FileFilterList::FileFilterList(string const & qt_style_filter)
103 {
104         // FIXME UNICODE
105         string const filter = qt_style_filter
106                 + (qt_style_filter.empty() ? string() : ";;")
107                 + lyx::to_utf8(_("All files (*)"));
108
109         // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
110         // into individual filters.
111         static boost::regex const separator_re(";;");
112
113         string::const_iterator it = filter.begin();
114         string::const_iterator const end = filter.end();
115         while (true) {
116                 boost::match_results<string::const_iterator> what;
117
118                 if (!boost::regex_search(it, end, what, separator_re)) {
119                         parse_filter(string(it, end));
120                         break;
121                 }
122
123                 // Everything from the start of the input to
124                 // the start of the match.
125                 parse_filter(string(what[-1].first, what[-1].second));
126
127                 // Increment the iterator to the end of the match.
128                 it += distance(it, what[0].second);
129         }
130 }
131
132
133 void FileFilterList::parse_filter(string const & filter)
134 {
135         // Matches "TeX documents (*.tex)",
136         // storing "TeX documents " as group 1 and "*.tex" as group 2.
137         static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
138
139         boost::match_results<string::const_iterator> what;
140         if (!boost::regex_search(filter, what, filter_re)) {
141                 // Just a glob, no description.
142                 filters_.push_back(Filter(string(), trim(filter)));
143         } else {
144                 string const desc = string(what[1].first, what[1].second);
145                 string const globs = string(what[2].first, what[2].second);
146                 filters_.push_back(Filter(trim(desc), trim(globs)));
147         }
148 }
149
150
151 string const FileFilterList::as_string() const
152 {
153         ostringstream ss;
154
155         vector<Filter>::const_iterator fit = filters_.begin();
156         vector<Filter>::const_iterator const fend = filters_.end();
157         for (; fit != fend; ++fit) {
158                 Filter::glob_iterator const gbegin = fit->begin();
159                 Filter::glob_iterator const gend = fit->end();
160                 if (gbegin == gend)
161                         continue;
162
163                 if (ss.tellp() > 0)
164                         ss << ";;";
165
166                 bool const has_description = !fit->description().empty();
167                 if (has_description)
168                         ss << fit->description() << " (";
169
170                 for (Filter::glob_iterator git = gbegin; git != gend; ++git) {
171                         if (git != gbegin)
172                                 ss << ' ';
173                         ss << *git;
174                 }
175
176                 if (has_description)
177                         ss << ')';
178         }
179
180         return ss.str();
181 }
182
183 } // namespace support
184 } // namespace lyx