xref: /haiku/src/preferences/mail/FilterConfigView.cpp (revision bab64f65bb775dc23060e276f1f1c4498ab7af6c)
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