]> git.lyx.org Git - lyx.git/blob - src/support/FileFilterList.cpp
Use *.* to select all files in the file selection dialog on Windows. Using shortcuts...
[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
15 #include "support/lstrings.h"
16 #include "support/gettext.h"
17
18 #include <boost/regex.hpp>
19 #include <boost/tokenizer.hpp>
20
21 #include <sstream>
22
23 using namespace std;
24
25 namespace {
26
27 /** Given a string such as
28  *      "<glob> <glob> ... *.{abc,def} <glob>",
29  *  convert the csh-style brace expresions:
30  *      "<glob> <glob> ... *.abc *.def <glob>".
31  *  Requires no system support, so should work equally on Unix, Mac, Win32.
32  */
33 string const convert_brace_glob(string const & glob)
34 {
35         // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
36         // "abc,def,ghi" as group 2.
37         static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
38         // Matches "abc" and "abc,", storing "abc" as group 1.
39         static boost::regex const block_re("([^,}]+),?");
40
41         string pattern;
42
43         string::const_iterator it = glob.begin();
44         string::const_iterator const end = glob.end();
45         while (true) {
46                 boost::match_results<string::const_iterator> what;
47                 if (!boost::regex_search(it, end, what, glob_re)) {
48                         // Ensure that no information is lost.
49                         pattern += string(it, end);
50                         break;
51                 }
52
53                 // Everything from the start of the input to
54                 // the start of the match.
55                 pattern += string(what[-1].first, what[-1].second);
56
57                 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
58                 string const head = string(what[1].first, what[1].second);
59                 string const tail = string(what[2].first, what[2].second);
60
61                 // Split the ','-separated chunks of tail so that
62                 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
63                 string const fmt = " " + head + "$1";
64                 pattern += boost::regex_merge(tail, block_re, fmt);
65
66                 // Increment the iterator to the end of the match.
67                 it += distance(it, what[0].second);
68         }
69
70         return pattern;
71 }
72
73 } // namespace anon
74
75
76 namespace lyx {
77 namespace support {
78
79 FileFilterList::Filter::Filter(docstring const & description,
80                                string const & globs)
81         : desc_(description)
82 {
83         typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
84         boost::char_separator<char> const separator(" ");
85
86         // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
87         //       "<glob> <glob> ... *.abc *.def <glob>"
88         string const expanded_globs = convert_brace_glob(globs);
89
90         // Split into individual globs.
91         vector<string> matches;
92         Tokenizer const tokens(expanded_globs, separator);
93         globs_ = vector<string>(tokens.begin(), tokens.end());
94 }
95
96
97 FileFilterList::FileFilterList(docstring const & qt_style_filter)
98 {
99         // FIXME UNICODE
100         string const filter = to_utf8(qt_style_filter)
101                 + (qt_style_filter.empty() ? string() : ";;")
102                 + to_utf8(_("All Files "))
103 #if defined(_WIN32)             
104                 + ("(*.*)");
105 #else
106                 + ("(*)");
107 #endif
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(docstring(), trim(filter)));
143         } else {
144                 // FIXME UNICODE
145                 docstring const desc = from_utf8(string(what[1].first, what[1].second));
146                 string const globs = string(what[2].first, what[2].second);
147                 filters_.push_back(Filter(trim(desc), trim(globs)));
148         }
149 }
150
151
152 docstring const FileFilterList::as_string() const
153 {
154         // FIXME UNICODE
155         ostringstream ss;
156
157         vector<Filter>::const_iterator fit = filters_.begin();
158         vector<Filter>::const_iterator const fend = filters_.end();
159         for (; fit != fend; ++fit) {
160                 Filter::glob_iterator const gbegin = fit->begin();
161                 Filter::glob_iterator const gend = fit->end();
162                 if (gbegin == gend)
163                         continue;
164
165                 if (ss.tellp() > 0)
166                         ss << ";;";
167
168                 bool const has_description = !fit->description().empty();
169                 if (has_description)
170                         ss << to_utf8(fit->description()) << " (";
171
172                 for (Filter::glob_iterator git = gbegin; git != gend; ++git) {
173                         if (git != gbegin)
174                                 ss << ' ';
175                         ss << *git;
176                 }
177
178                 if (has_description)
179                         ss << ')';
180         }
181
182         return from_utf8(ss.str());
183 }
184
185 } // namespace support
186 } // namespace lyx