1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3 * Pan - A Newsreader for Gtk+
4 * Copyright (C) 2002-2006 Charles Kerr <charles@rebelbase.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include <config.h>
21 #include <glib.h>
22 #include <glib/gi18n.h>
23 #include <pan/general/macros.h>
24 #include "filter-info.h"
25
26 using namespace pan;
27
28 /***
29 ****
30 ***/
31
32 /*
33 * Copy-and-swap idiom according to
34 * http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
35 */
36
FilterInfo(const FilterInfo & that)37 FilterInfo :: FilterInfo (const FilterInfo &that)
38 : _type(that._type)
39 , _ge(that._ge)
40 , _header(that._header)
41 , _text(that._text)
42 , _negate(that._negate)
43 , _needs_body(that._needs_body)
44 {
45 foreach_const (aggregatesp_t, that._aggregates, it) {
46 _aggregates.push_back (new FilterInfo(**it));
47 }
48 }
49
50 void
swap(FilterInfo & first,FilterInfo & second)51 swap (FilterInfo &first, FilterInfo &second)
52 {
53 using std::swap;
54
55 swap (first._type, second._type);
56 swap (first._ge, second._ge);
57 swap (first._header, second._header);
58 swap (first._text, second._text);
59 swap (first._aggregates, second._aggregates);
60 swap (first._negate, second._negate);
61 swap (first._needs_body, second._needs_body);
62 }
63
operator =(FilterInfo other)64 FilterInfo &FilterInfo::operator = (FilterInfo other)
65 {
66 swap (*this, other);
67
68 return *this;
69 }
70
~FilterInfo()71 FilterInfo :: ~FilterInfo ()
72 {
73 foreach (aggregatesp_t, _aggregates, it) {
74 delete *it;
75 }
76 }
77
78 void
clear()79 FilterInfo :: clear ()
80 {
81 _type = FilterInfo::TYPE_ERR;
82 _ge = 0;
83 _header.clear ();
84 _text.clear ();
85 foreach (aggregatesp_t, _aggregates, it) {
86 delete *it;
87 }
88 _aggregates.clear ();
89 _negate = false;
90 _needs_body = false;
91 }
92
93 void
set_type_is(Type type)94 FilterInfo :: set_type_is (Type type) {
95 clear ();
96 _type = type;
97 }
98 void
set_type_ge(Type type,unsigned long ge)99 FilterInfo :: set_type_ge (Type type, unsigned long ge) {
100 clear ();
101 _type = type;
102 _ge = ge;
103 }
104
105 void
set_type_le(Type type,unsigned long le)106 FilterInfo :: set_type_le (Type type, unsigned long le) {
107 clear ();
108 _type = type;
109 _negate = true;
110 _ge = le+1; // le N == !ge N+1
111 }
112
113 void
set_type_aggregate_and()114 FilterInfo :: set_type_aggregate_and () {
115 clear ();
116 _type = AGGREGATE_AND;
117 }
118 void
set_type_aggregate_or()119 FilterInfo :: set_type_aggregate_or () {
120 clear ();
121 _type = AGGREGATE_OR;
122 }
123 void
set_type_text(const Quark & header,const TextMatch::Description & text)124 FilterInfo :: set_type_text (const Quark & header,
125 const TextMatch::Description & text)
126 {
127 static const Quark subject("Subject"), from("From"),
128 xref("Xref"), references("References"), newsgroups("Newsgroups"),
129 message_Id("Message-Id"), message_ID("Message-ID");
130
131 clear ();
132 _type = TEXT;
133 _header = header;
134 _text.set (text);
135
136 if( !(header == subject || header == from || header == message_Id ||
137 header == message_ID || header == newsgroups || header == references ||
138 header == xref) )
139 _needs_body = true;
140 }
141
142 /****
143 *****
144 ****/
145
146 void
set_type_binary()147 FilterInfo :: set_type_binary ()
148 {
149 set_type_is (IS_BINARY);
150 }
151 void
set_type_byte_count_ge(unsigned long ge)152 FilterInfo :: set_type_byte_count_ge (unsigned long ge)
153 {
154 set_type_ge (BYTE_COUNT_GE, ge);
155 }
156 void
set_type_cached()157 FilterInfo :: set_type_cached ()
158 {
159 set_type_is (IS_CACHED);
160 }
161 void
set_type_crosspost_count_ge(unsigned long ge)162 FilterInfo :: set_type_crosspost_count_ge (unsigned long ge)
163 {
164 set_type_ge (CROSSPOST_COUNT_GE, ge);
165 }
166 void
set_type_days_old_ge(unsigned long ge)167 FilterInfo :: set_type_days_old_ge (unsigned long ge)
168 {
169 set_type_ge (DAYS_OLD_GE, ge);
170 }
171 void
set_type_days_old_le(unsigned long le)172 FilterInfo :: set_type_days_old_le (unsigned long le)
173 {
174 set_type_le (DAYS_OLD_GE, le);
175 }
176 void
set_type_line_count_ge(unsigned long ge)177 FilterInfo :: set_type_line_count_ge (unsigned long ge)
178 {
179 set_type_ge (LINE_COUNT_GE, ge);
180 }
181 void
set_type_score_ge(unsigned long ge)182 FilterInfo :: set_type_score_ge (unsigned long ge)
183 {
184 set_type_ge (SCORE_GE, ge);
185 }
186 void
set_type_score_le(unsigned long le)187 FilterInfo :: set_type_score_le (unsigned long le)
188 {
189 set_type_le (SCORE_GE, le);
190 }
191 void
set_type_is_read()192 FilterInfo :: set_type_is_read ()
193 {
194 set_type_is (IS_READ);
195 }
196 void
set_type_is_unread()197 FilterInfo :: set_type_is_unread ()
198 {
199 set_type_is (IS_UNREAD);
200 }
201 void
set_type_posted_by_me()202 FilterInfo :: set_type_posted_by_me ()
203 {
204 set_type_is (IS_POSTED_BY_ME);
205 }
206
207 std::string
describe() const208 FilterInfo :: describe () const
209 {
210 std::string ret;
211 char buf[4096];
212
213 if (_type==IS_BINARY && _negate)
214 {
215 ret = _("article doesn’t have attachments");
216 }
217 else if (_type==IS_BINARY)
218 {
219 ret = _("the article has attachments");
220 }
221 else if (_type==IS_CACHED && _negate)
222 {
223 ret = _("the article isn’t cached locally");
224 }
225 else if (_type==IS_CACHED)
226 {
227 ret = _("the article is cached locally");
228 }
229 else if (_type==IS_POSTED_BY_ME && _negate)
230 {
231 ret = _("the article wasn’t posted by you");
232 }
233 else if (_type==IS_POSTED_BY_ME)
234 {
235 ret = _("the article was posted by you");
236 }
237 else if (_type==IS_READ)
238 {
239 ret = _("the article has been read");
240 }
241 else if (_type==IS_UNREAD)
242 {
243 ret = _("the article hasn’t been read");
244 }
245 else if (_type==BYTE_COUNT_GE && _negate)
246 {
247 g_snprintf (buf, sizeof(buf), _("the article is less than %ld bytes long"), _ge);
248 ret = buf;
249 }
250 else if (_type==BYTE_COUNT_GE)
251 {
252 g_snprintf (buf, sizeof(buf), _("the article is at least %ld bytes long"), _ge);
253 ret = buf;
254 }
255 else if (_type==LINE_COUNT_GE && _negate)
256 {
257 g_snprintf (buf, sizeof(buf), _("the article is less than %ld lines long"), _ge);
258 ret = buf;
259 }
260 else if (_type==LINE_COUNT_GE)
261 {
262 g_snprintf (buf, sizeof(buf), _("the article is at least %ld lines long"), _ge);
263 ret = buf;
264 }
265 else if (_type==DAYS_OLD_GE && _negate)
266 {
267 g_snprintf (buf, sizeof(buf), _("the article is less than %ld days old"), _ge);
268 ret = buf;
269 }
270 else if (_type==DAYS_OLD_GE)
271 {
272 g_snprintf (buf, sizeof(buf), _("the article is at least %ld days old"), _ge);
273 ret = buf;
274 }
275 else if (_type==CROSSPOST_COUNT_GE && _negate)
276 {
277 g_snprintf (buf, sizeof(buf), _("the article was posted to less than %ld groups"), _ge);
278 ret = buf;
279 }
280 else if (_type==CROSSPOST_COUNT_GE)
281 {
282 g_snprintf (buf, sizeof(buf), _("the article was posted to at least %ld groups"), _ge);
283 ret = buf;
284 }
285 else if (_type==SCORE_GE && _negate)
286 {
287 g_snprintf (buf, sizeof(buf), _("the article’s score is less than %ld"), _ge);
288 ret = buf;
289 }
290 else if (_type==SCORE_GE)
291 {
292 g_snprintf (buf, sizeof(buf), _("the article’s score is %ld or higher"), _ge);
293 ret = buf;
294 }
295 else if (_type==TEXT && _negate)
296 {
297 #if 0
298 const char * h (_header.c_str());
299 const char * t (_text.get_state().text.c_str());
300 switch (_text.get_state().type) {
301 case TextMatch::CONTAINS: g_snprintf (buf, sizeof(buf), _("%s doesn’t contain “%s”"), h, t); break;
302 case TextMatch::IS: g_snprintf (buf, sizeof(buf), _("%s isn’t “%s”"), h, t); break;
303 case TextMatch::BEGINS_WITH: g_snprintf (buf, sizeof(buf), _("%s doesn’t begin with “%s”"), h, t); break;
304 case TextMatch::ENDS_WITH: g_snprintf (buf, sizeof(buf), _("%s doesn’t end with “%s”"), h, t); break;
305 case TextMatch::REGEX: g_snprintf (buf, sizeof(buf), _("%s doesn’t match the regex “%s”"), h, t); break;
306 }
307 #else
308 const char * h (_header.c_str());
309 const char * t (_text._impl_text.c_str());
310 switch (_text._impl_type) {
311 case TextMatch::CONTAINS: g_snprintf (buf, sizeof(buf), _("%s doesn’t contain “%s”"), h, t); break;
312 case TextMatch::IS: g_snprintf (buf, sizeof(buf), _("%s isn’t “%s”"), h, t); break;
313 case TextMatch::BEGINS_WITH: g_snprintf (buf, sizeof(buf), _("%s doesn’t begin with “%s”"), h, t); break;
314 case TextMatch::ENDS_WITH: g_snprintf (buf, sizeof(buf), _("%s doesn’t end with “%s”"), h, t); break;
315 case TextMatch::REGEX: g_snprintf (buf, sizeof(buf), _("%s doesn’t match the regex “%s”"), h, t); break;
316 }
317 #endif
318 ret = buf;
319 }
320 else if (_type==TEXT)
321 {
322 const char * h (_header.c_str());
323 //const char * t (_text.get_state().text.c_str());
324 //switch (_text.get_state().type) {
325 const char * t (_text._impl_text.c_str());
326 switch (_text._impl_type) {
327 case TextMatch::CONTAINS: g_snprintf (buf, sizeof(buf), _("%s contains “%s”"), h, t); break;
328 case TextMatch::IS: g_snprintf (buf, sizeof(buf), _("%s is “%s”"), h, t); break;
329 case TextMatch::BEGINS_WITH: g_snprintf (buf, sizeof(buf), _("%s begins with “%s”"), h, t); break;
330 case TextMatch::ENDS_WITH: g_snprintf (buf, sizeof(buf), _("%s ends with “%s”"), h, t); break;
331 case TextMatch::REGEX: g_snprintf (buf, sizeof(buf), _("%s matches the regex “%s”"), h, t); break;
332 }
333 ret = buf;
334 }
335 else if (_type==AGGREGATE_AND && _negate)
336 {
337 ret = _("Any of these tests fail:");
338 ret += "\n";
339 foreach_const (aggregatesp_t, _aggregates, it)
340 ret += " " + (*it)->describe() + "\n";
341 }
342 else if (_type==AGGREGATE_AND)
343 {
344 ret = _("All of these tests pass:");
345 ret += "\n";
346 foreach_const (aggregatesp_t, _aggregates, it)
347 ret += " " + (*it)->describe() + "\n";
348 }
349 else if (_type==AGGREGATE_OR && _negate)
350 {
351 ret = _("None of these tests pass:");
352 ret += "\n";
353 foreach_const (aggregatesp_t, _aggregates, it)
354 ret += " " + (*it)->describe() + "\n";
355 }
356 else if (_type==AGGREGATE_OR)
357 {
358 ret = _("Any of these tests pass:");
359 ret += "\n";
360 foreach_const (aggregatesp_t, _aggregates, it)
361 ret += " " + (*it)->describe() + "\n";
362 }
363
364 return ret;
365 }
366