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