1 /*
2 * Copyright 2007-2016, Haiku, Inc. All rights reserved.
3 * Copyright 2001-2002 Dr. Zoidberg Enterprises. All rights reserved.
4 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
5 * Distributed under the terms of the MIT License.
6 */
7
8
9 #include "FilterConfigView.h"
10
11 #include <stdio.h>
12
13 #include <Alert.h>
14 #include <Bitmap.h>
15 #include <Box.h>
16 #include <Catalog.h>
17 #include <LayoutBuilder.h>
18 #include <Locale.h>
19 #include <MenuItem.h>
20 #include <PopUpMenu.h>
21 #include <ScrollView.h>
22
23
24 #undef B_TRANSLATION_CONTEXT
25 #define B_TRANSLATION_CONTEXT "Config Views"
26
27
28 // FiltersConfigView
29 const uint32 kMsgFilterMoved = 'flmv';
30 const uint32 kMsgChainSelected = 'chsl';
31 const uint32 kMsgAddFilter = 'addf';
32 const uint32 kMsgRemoveFilter = 'rmfi';
33 const uint32 kMsgFilterSelected = 'fsel';
34
35 const uint32 kMsgItemDragged = 'itdr';
36
37
38 class DragListView : public BListView {
39 public:
DragListView(const char * name,list_view_type type=B_SINGLE_SELECTION_LIST,BMessage * itemMovedMsg=NULL)40 DragListView(const char* name,
41 list_view_type type = B_SINGLE_SELECTION_LIST,
42 BMessage* itemMovedMsg = NULL)
43 :
44 BListView(name, type),
45 fDragging(false),
46 fItemMovedMessage(itemMovedMsg)
47 {
48 }
49
InitiateDrag(BPoint point,int32 index,bool wasSelected)50 virtual bool InitiateDrag(BPoint point, int32 index, bool wasSelected)
51 {
52 BRect frame(ItemFrame(index));
53 BBitmap *bitmap = new BBitmap(frame.OffsetToCopy(B_ORIGIN), B_RGBA32,
54 true);
55 BView *view = new BView(bitmap->Bounds(), NULL, 0, 0);
56 bitmap->AddChild(view);
57
58 if (view->LockLooper()) {
59 BListItem *item = ItemAt(index);
60 bool selected = item->IsSelected();
61
62 view->SetLowColor(225, 225, 225, 128);
63 view->FillRect(view->Bounds());
64
65 if (selected)
66 item->Deselect();
67 ItemAt(index)->DrawItem(view, view->Bounds(), true);
68 if (selected)
69 item->Select();
70
71 view->UnlockLooper();
72 }
73 fLastDragTarget = -1;
74 fDragIndex = index;
75 fDragging = true;
76
77 BMessage drag(kMsgItemDragged);
78 drag.AddInt32("index", index);
79 DragMessage(&drag, bitmap, B_OP_ALPHA, point - frame.LeftTop(), this);
80
81 return true;
82 }
83
DrawDragTargetIndicator(int32 target)84 void DrawDragTargetIndicator(int32 target)
85 {
86 PushState();
87 SetDrawingMode(B_OP_INVERT);
88
89 bool last = false;
90 if (target >= CountItems())
91 target = CountItems() - 1, last = true;
92
93 BRect frame = ItemFrame(target);
94 if (last)
95 frame.OffsetBy(0,frame.Height());
96 frame.bottom = frame.top + 1;
97
98 FillRect(frame);
99
100 PopState();
101 }
102
MouseMoved(BPoint point,uint32 transit,const BMessage * msg)103 virtual void MouseMoved(BPoint point, uint32 transit, const BMessage *msg)
104 {
105 BListView::MouseMoved(point, transit, msg);
106
107 if ((transit != B_ENTERED_VIEW && transit != B_INSIDE_VIEW)
108 || !fDragging)
109 return;
110
111 int32 target = IndexOf(point);
112 if (target == -1)
113 target = CountItems();
114
115 // correct the target insertion index
116 if (target == fDragIndex || target == fDragIndex + 1)
117 target = -1;
118
119 if (target == fLastDragTarget)
120 return;
121
122 // remove old target indicator
123 if (fLastDragTarget != -1)
124 DrawDragTargetIndicator(fLastDragTarget);
125
126 // draw new one
127 fLastDragTarget = target;
128 if (target != -1)
129 DrawDragTargetIndicator(target);
130 }
131
MouseUp(BPoint point)132 virtual void MouseUp(BPoint point)
133 {
134 if (fDragging) {
135 fDragging = false;
136 if (fLastDragTarget != -1)
137 DrawDragTargetIndicator(fLastDragTarget);
138 }
139 BListView::MouseUp(point);
140 }
141
MessageReceived(BMessage * msg)142 virtual void MessageReceived(BMessage *msg)
143 {
144 switch (msg->what) {
145 case kMsgItemDragged:
146 {
147 int32 source = msg->FindInt32("index");
148 BPoint point = msg->FindPoint("_drop_point_");
149 ConvertFromScreen(&point);
150 int32 to = IndexOf(point);
151 if (to > fDragIndex)
152 to--;
153 if (to == -1)
154 to = CountItems() - 1;
155
156 if (source != to) {
157 MoveItem(source,to);
158
159 if (fItemMovedMessage != NULL) {
160 BMessage msg(fItemMovedMessage->what);
161 msg.AddInt32("from",source);
162 msg.AddInt32("to",to);
163 Messenger().SendMessage(&msg);
164 }
165 }
166 break;
167 }
168 }
169 BListView::MessageReceived(msg);
170 }
171
172 private:
173 bool fDragging;
174 int32 fLastDragTarget,fDragIndex;
175 BMessage *fItemMovedMessage;
176 };
177
178
179 // #pragma mark -
180
181
182 class FilterSettingsView : public BBox {
183 public:
FilterSettingsView(const BString & label,BMailSettingsView * settingsView)184 FilterSettingsView(const BString& label, BMailSettingsView* settingsView)
185 :
186 BBox("filter"),
187 fSettingsView(settingsView)
188 {
189 SetLabel(label);
190
191 BView* contents = new BView("contents", 0);
192 AddChild(contents);
193
194 BLayoutBuilder::Group<>(contents, B_VERTICAL)
195 .SetInsets(B_USE_DEFAULT_SPACING)
196 .Add(fSettingsView);
197 }
198
SaveInto(BMailAddOnSettings & settings) const199 status_t SaveInto(BMailAddOnSettings& settings) const
200 {
201 return fSettingsView->SaveInto(settings);
202 }
203
204 private:
205 BMailSettingsView* fSettingsView;
206 };
207
208
209 // #pragma mark -
210
211
FiltersConfigView(BMailAccountSettings & account)212 FiltersConfigView::FiltersConfigView(BMailAccountSettings& account)
213 :
214 BGroupView(B_VERTICAL),
215 fAccount(account),
216 fDirection(kIncoming),
217 fInboundFilters(kIncoming),
218 fOutboundFilters(kOutgoing),
219 fFilterView(NULL),
220 fCurrentIndex(-1)
221 {
222 BBox* box = new BBox("filters");
223 AddChild(box);
224
225 BView* contents = new BView(NULL, 0);
226 box->AddChild(contents);
227
228 BMessage* msg = new BMessage(kMsgChainSelected);
229 msg->AddInt32("direction", kIncoming);
230 BMenuItem* item = new BMenuItem(B_TRANSLATE("Incoming mail filters"), msg);
231 item->SetMarked(true);
232 BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING);
233 menu->AddItem(item);
234
235 msg = new BMessage(kMsgChainSelected);
236 msg->AddInt32("direction", kOutgoing);
237 item = new BMenuItem(B_TRANSLATE("Outgoing mail filters"), msg);
238 menu->AddItem(item);
239
240 fChainsField = new BMenuField(NULL, NULL, menu);
241 fChainsField->ResizeToPreferred();
242 box->SetLabel(fChainsField);
243
244 fListView = new DragListView(NULL, B_SINGLE_SELECTION_LIST,
245 new BMessage(kMsgFilterMoved));
246 fListView->SetSelectionMessage(new BMessage(kMsgFilterSelected));
247
248 menu = new BPopUpMenu(B_TRANSLATE("Add filter"));
249 menu->SetRadioMode(false);
250
251 fAddField = new BMenuField(NULL, NULL, menu);
252
253 fRemoveButton = new BButton(NULL, B_TRANSLATE("Remove"),
254 new BMessage(kMsgRemoveFilter));
255
256 BLayoutBuilder::Group<>(contents, B_VERTICAL)
257 .SetInsets(B_USE_DEFAULT_SPACING)
258 .Add(new BScrollView(NULL, fListView, 0, false, true))
259 .AddGroup(B_HORIZONTAL)
260 .Add(fAddField)
261 .Add(fRemoveButton)
262 .AddGlue();
263
264 _SetDirection(fDirection);
265 }
266
267
~FiltersConfigView()268 FiltersConfigView::~FiltersConfigView()
269 {
270 // We need to remove the filter manually, as their add-on
271 // is not available anymore in the parent destructor.
272 if (fFilterView != NULL) {
273 RemoveChild(fFilterView);
274 delete fFilterView;
275 }
276 }
277
278
279 void
_SelectFilter(int32 index)280 FiltersConfigView::_SelectFilter(int32 index)
281 {
282 Hide();
283
284 // remove old config view
285 if (fFilterView != NULL) {
286 RemoveChild(fFilterView);
287 _SaveConfig(fCurrentIndex);
288 delete fFilterView;
289 fFilterView = NULL;
290 }
291
292 if (index >= 0) {
293 // add new config view
294 BMailAddOnSettings* filterSettings
295 = _MailSettings()->FilterSettingsAt(index);
296 if (filterSettings != NULL) {
297 ::FilterList* filters = _FilterList();
298 BMailSettingsView* view = filters->CreateSettingsView(fAccount,
299 *filterSettings);
300 if (view != NULL) {
301 fFilterView = new FilterSettingsView(
302 filters->DescriptiveName(filterSettings->AddOnRef(),
303 fAccount, NULL), view);
304 AddChild(fFilterView);
305 }
306 }
307 }
308
309 fCurrentIndex = index;
310 Show();
311 }
312
313
314 void
_SetDirection(direction direction)315 FiltersConfigView::_SetDirection(direction direction)
316 {
317 // remove the filter config view
318 _SelectFilter(-1);
319
320 for (int32 i = fListView->CountItems(); i-- > 0;) {
321 BStringItem *item = (BStringItem *)fListView->RemoveItem(i);
322 delete item;
323 }
324
325 fDirection = direction;
326 BMailProtocolSettings* protocolSettings = _MailSettings();
327 ::FilterList* filters = _FilterList();
328 filters->Reload();
329
330 for (int32 i = 0; i < protocolSettings->CountFilterSettings(); i++) {
331 BMailAddOnSettings* settings = protocolSettings->FilterSettingsAt(i);
332 if (filters->InfoIndexFor(settings->AddOnRef()) < 0) {
333 fprintf(stderr, "Removed missing filter: %s\n",
334 settings->AddOnRef().name);
335 protocolSettings->RemoveFilterSettings(i);
336 i--;
337 continue;
338 }
339
340 fListView->AddItem(new BStringItem(filters->DescriptiveName(
341 settings->AddOnRef(), fAccount, settings)));
342 }
343
344 // remove old filter items
345 BMenu* menu = fAddField->Menu();
346 for (int32 i = menu->CountItems(); i-- > 0;) {
347 BMenuItem *item = menu->RemoveItem(i);
348 delete item;
349 }
350
351 for (int32 i = 0; i < filters->CountInfos(); i++) {
352 const FilterInfo& info = filters->InfoAt(i);
353
354 BMessage* msg = new BMessage(kMsgAddFilter);
355 msg->AddRef("filter", &info.ref);
356 BMenuItem* item = new BMenuItem(filters->SimpleName(i, fAccount), msg);
357 menu->AddItem(item);
358 }
359
360 menu->SetTargetForItems(this);
361 }
362
363
364 void
AttachedToWindow()365 FiltersConfigView::AttachedToWindow()
366 {
367 fChainsField->Menu()->SetTargetForItems(this);
368 fListView->SetTarget(this);
369 fAddField->Menu()->SetTargetForItems(this);
370 fRemoveButton->SetTarget(this);
371 }
372
373
374 void
DetachedFromWindow()375 FiltersConfigView::DetachedFromWindow()
376 {
377 _SaveConfig(fCurrentIndex);
378 }
379
380
381 void
MessageReceived(BMessage * msg)382 FiltersConfigView::MessageReceived(BMessage *msg)
383 {
384 switch (msg->what) {
385 case kMsgChainSelected:
386 {
387 direction dir;
388 if (msg->FindInt32("direction", (int32*)&dir) != B_OK)
389 break;
390
391 if (fDirection == dir)
392 break;
393
394 _SetDirection(dir);
395 break;
396 }
397 case kMsgAddFilter:
398 {
399 entry_ref ref;
400 if (msg->FindRef("filter", &ref) != B_OK)
401 break;
402
403 int32 index = _MailSettings()->AddFilterSettings(&ref);
404 if (index < 0)
405 break;
406
407 fListView->AddItem(new BStringItem(_FilterList()->DescriptiveName(
408 ref, fAccount, _MailSettings()->FilterSettingsAt(index))));
409 break;
410 }
411 case kMsgRemoveFilter:
412 {
413 int32 index = fListView->CurrentSelection();
414 if (index < 0)
415 break;
416 BStringItem* item = (BStringItem*)fListView->RemoveItem(index);
417 delete item;
418
419 _SelectFilter(-1);
420 _MailSettings()->RemoveFilterSettings(index);
421 break;
422 }
423 case kMsgFilterSelected:
424 {
425 int32 index = -1;
426 if (msg->FindInt32("index",&index) != B_OK)
427 break;
428
429 _SelectFilter(index);
430 break;
431 }
432 case kMsgFilterMoved:
433 {
434 int32 from = msg->FindInt32("from");
435 int32 to = msg->FindInt32("to");
436 if (from == to)
437 break;
438
439 if (!_MailSettings()->MoveFilterSettings(from, to)) {
440 BAlert* alert = new BAlert("E-mail",
441 B_TRANSLATE("The filter could not be moved. Deleting "
442 "filter."), B_TRANSLATE("OK"));
443 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
444 alert->Go();
445 fListView->RemoveItem(to);
446 break;
447 }
448
449 break;
450 }
451 default:
452 BView::MessageReceived(msg);
453 break;
454 }
455 }
456
457
458 BMailProtocolSettings*
_MailSettings()459 FiltersConfigView::_MailSettings()
460 {
461 return fDirection == kIncoming
462 ? &fAccount.InboundSettings() : &fAccount.OutboundSettings();
463 }
464
465
466 FilterList*
_FilterList()467 FiltersConfigView::_FilterList()
468 {
469 return fDirection == kIncoming ? &fInboundFilters : &fOutboundFilters;
470 }
471
472
473 void
_SaveConfig(int32 index)474 FiltersConfigView::_SaveConfig(int32 index)
475 {
476 if (fFilterView != NULL) {
477 BMailAddOnSettings* settings = _MailSettings()->FilterSettingsAt(index);
478 if (settings != NULL)
479 fFilterView->SaveInto(*settings);
480 }
481 }
482