xref: /haiku/src/preferences/mail/ConfigViews.cpp (revision 37c7d5d83a2372a6971e383411d5bacbeef0ebdc)
1 /* ConfigViews - config views for the account, protocols, and filters
2 **
3 ** Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved.
4 */
5 
6 
7 #include "ConfigViews.h"
8 #include "Account.h"
9 #include "CenterContainer.h"
10 
11 #include <TextControl.h>
12 #include <ListView.h>
13 #include <ScrollView.h>
14 #include <PopUpMenu.h>
15 #include <MenuField.h>
16 #include <MenuItem.h>
17 #include <Button.h>
18 #include <Bitmap.h>
19 #include <Looper.h>
20 #include <Path.h>
21 #include <Alert.h>
22 #include <Entry.h>
23 #include <FindDirectory.h>
24 #include <Directory.h>
25 
26 #include <Catalog.h>
27 #include <Locale.h>
28 
29 #include <string.h>
30 
31 #include <MailSettings.h>
32 
33 #include <MDRLanguage.h>
34 
35 
36 #undef TR_CONTEXT
37 #define TR_CONTEXT "Config Views"
38 
39 
40 // AccountConfigView
41 const uint32 kMsgAccountTypeChanged = 'atch';
42 const uint32 kMsgAccountNameChanged = 'anmc';
43 
44 // ProtocolsConfigView
45 const uint32 kMsgProtocolChanged = 'prch';
46 
47 // FiltersConfigView
48 const uint32 kMsgItemDragged = 'itdr';
49 const uint32 kMsgFilterMoved = 'flmv';
50 const uint32 kMsgChainSelected = 'chsl';
51 const uint32 kMsgAddFilter = 'addf';
52 const uint32 kMsgRemoveFilter = 'rmfi';
53 const uint32 kMsgFilterSelected = 'fsel';
54 
55 
56 AccountConfigView::AccountConfigView(BRect rect,Account *account)
57 	:	BBox(rect),
58 		fAccount(account)
59 {
60 	SetLabel(TR ("Account settings"));
61 
62 	rect = Bounds().InsetByCopy(8,8);
63 	rect.top += 10;
64 	CenterContainer *view = new CenterContainer(rect,false);
65 	view->SetSpacing(5);
66 
67 	// determine font height
68 	font_height fontHeight;
69 	view->GetFontHeight(&fontHeight);
70 	int32 height = (int32)(fontHeight.ascent + fontHeight.descent + fontHeight.leading) + 5;
71 
72 	rect = view->Bounds();
73 	rect.bottom = height + 5;
74 
75 	float labelWidth = view->StringWidth(TR ("Account name:")) + 6;
76 
77 	view->AddChild(fNameControl = new BTextControl(rect,NULL,TR ("Account name:"),NULL,new BMessage(kMsgAccountNameChanged)));
78 	fNameControl->SetDivider(labelWidth);
79 	view->AddChild(fRealNameControl = new BTextControl(rect,NULL,TR ("Real name:"),NULL,NULL));
80 	fRealNameControl->SetDivider(labelWidth);
81 	view->AddChild(fReturnAddressControl = new BTextControl(rect,NULL,TR ("Return address:"),NULL,NULL));
82 	fReturnAddressControl->SetDivider(labelWidth);
83 //			control->TextView()->HideTyping(true);
84 
85 	BPopUpMenu *chainsPopUp = new BPopUpMenu(B_EMPTY_STRING);
86 	const char *chainModes[] = {
87 		TR ("Receive mail only"),
88 		TR ("Send mail only"),
89 		TR ("Send and receive mail")};
90 	BMenuItem *item;
91 	for (int32 i = 0;i < 3;i++)
92 		chainsPopUp->AddItem(item = new BMenuItem(chainModes[i],new BMessage(kMsgAccountTypeChanged)));
93 
94 	fTypeField = new BMenuField(rect,NULL,TR ("Account type:"),chainsPopUp);
95 	fTypeField->SetDivider(labelWidth + 3);
96 	view->AddChild(fTypeField);
97 
98 	float w,h;
99 	view->GetPreferredSize(&w,&h);
100 	ResizeTo(w + 15,h + 22);
101 	view->ResizeTo(w,h);
102 
103 	AddChild(view);
104 }
105 
106 
107 void AccountConfigView::DetachedFromWindow()
108 {
109 	fAccount->SetName(fNameControl->Text());
110 	fAccount->SetRealName(fRealNameControl->Text());
111 	fAccount->SetReturnAddress(fReturnAddressControl->Text());
112 }
113 
114 
115 void AccountConfigView::AttachedToWindow()
116 {
117 	UpdateViews();
118 	fNameControl->SetTarget(this);
119 	fTypeField->Menu()->SetTargetForItems(this);
120 }
121 
122 
123 void AccountConfigView::MessageReceived(BMessage *msg)
124 {
125 	switch (msg->what)
126 	{
127 		case kMsgAccountTypeChanged:
128 		{
129 			int32 index;
130 			if (msg->FindInt32("index",&index) < B_OK)
131 				break;
132 
133 			if (fAccount->Type() < 0)
134 			{
135 				fNameControl->SetEnabled(true);
136 				fRealNameControl->SetEnabled(true);
137 				fReturnAddressControl->SetEnabled(true);
138 			}
139 			fAccount->SetType(index);
140 			UpdateViews();
141 			break;
142 		}
143 		case kMsgAccountNameChanged:
144 			fAccount->SetName(fNameControl->Text());
145 			break;
146 
147 		default:
148 			BView::MessageReceived(msg);
149 	}
150 }
151 
152 
153 void AccountConfigView::UpdateViews()
154 {
155 	if (!fAccount->Inbound() && !fAccount->Outbound())
156 	{
157 		if (BMenuItem *item = fTypeField->Menu()->FindMarked())
158 			item->SetMarked(false);
159 		fTypeField->Menu()->Superitem()->SetLabel(TR ("Select account type"));
160 
161 		fNameControl->SetEnabled(false);
162 		fRealNameControl->SetEnabled(false);
163 		fReturnAddressControl->SetEnabled(false);
164 		return;
165 	}
166 	fNameControl->SetText(fAccount->Name());
167 	fRealNameControl->SetText(fAccount->RealName());
168 	fReturnAddressControl->SetText(fAccount->ReturnAddress());
169 
170 	if (BMenuItem *item = fTypeField->Menu()->ItemAt(fAccount->Type()))
171 		item->SetMarked(true);
172 }
173 
174 
175 //---------------------------------------------------------------------------------------
176 //	#pragma mark -
177 
178 #include <stdio.h>
179 FilterConfigView::FilterConfigView(BMailChain *chain,int32 index,BMessage *msg,entry_ref *ref)
180 	:	BBox(BRect(0,0,100,100)),
181 		fConfigView(NULL),
182 		fChain(chain),
183 		fIndex(index),
184 		fMessage(msg),
185 		fEntryRef(ref)
186 {
187 	Load(msg,ref);
188 	BPath addon(ref);
189 	SetLabel(addon.Leaf());
190 }
191 
192 
193 FilterConfigView::~FilterConfigView()
194 {
195 	Remove();
196 }
197 
198 
199 void FilterConfigView::Load(BMessage *msg,entry_ref *ref)
200 {
201 	ResizeTo(264,30);
202 
203 	BView *(* instantiate_config)(BMessage *,BMessage *);
204 	BPath addon(ref);
205 	fImage = load_add_on(addon.Path());
206 	if (fImage < B_OK)
207 		return;
208 
209 	if (get_image_symbol(fImage,"instantiate_config_panel",B_SYMBOL_TYPE_TEXT,(void **)&instantiate_config) < B_OK)
210 	{
211 		unload_add_on(fImage);
212 		fImage = B_MISSING_SYMBOL;
213 		return;
214 	}
215 
216 	fConfigView = (*instantiate_config)(msg,fChain->MetaData());
217 
218 	float w = fConfigView->Bounds().Width();
219 	float h = fConfigView->Bounds().Height();
220 	fConfigView->MoveTo(3,13);
221 	ResizeTo(w + 6,h + 16);
222 	AddChild(fConfigView);
223 }
224 
225 
226 void FilterConfigView::Remove(bool deleteMessage)
227 {
228 	// remove config view here, because they may not be available
229 	// anymore, if the add-on is unloaded
230 	if (fConfigView && RemoveChild(fConfigView))
231 	{
232 		delete fConfigView;
233 		fConfigView = NULL;
234 	}
235 	unload_add_on(fImage);
236 
237 	if (deleteMessage)
238 	{
239 		delete fMessage;
240 		fMessage = NULL;
241 	}
242 	delete fEntryRef;
243 	fEntryRef = NULL;
244 }
245 
246 
247 status_t FilterConfigView::InitCheck()
248 {
249 	return fImage;
250 }
251 
252 
253 void FilterConfigView::DetachedFromWindow()
254 {
255 	if (fConfigView == NULL)
256 		return;
257 
258 	if (fConfigView->Archive(fMessage) >= B_OK)
259 		fChain->SetFilter(fIndex,*fMessage,*fEntryRef);
260 }
261 
262 
263 void FilterConfigView::AttachedToWindow()
264 {
265 }
266 
267 
268 //---------------------------------------------------------------------------------------
269 //	#pragma mark -
270 
271 
272 ProtocolsConfigView::ProtocolsConfigView(BMailChain *chain,int32 index,BMessage *msg,entry_ref *ref)
273 	:	FilterConfigView(chain,index,msg,ref)
274 {
275 	BPopUpMenu *menu = new BPopUpMenu("Choose Protocol");
276 
277 	for (int i = 0; i < 2; i++) {
278 		BPath path;
279 		status_t status = find_directory((i == 0) ? B_USER_ADDONS_DIRECTORY : B_BEOS_ADDONS_DIRECTORY,&path);
280 		if (status != B_OK)
281 		{
282 			fImage = status;
283 			return;
284 		}
285 
286 		path.Append("mail_daemon");
287 
288 		if (chain->ChainDirection() == inbound)
289 			path.Append("inbound_protocols");
290 		else
291 			path.Append("outbound_protocols");
292 
293 		BDirectory dir(path.Path());
294 		entry_ref protocolRef;
295 		while (dir.GetNextRef(&protocolRef) == B_OK)
296 		{
297 			char name[B_FILE_NAME_LENGTH];
298 			BEntry entry(&protocolRef);
299 			entry.GetName(name);
300 
301 			BMenuItem *item;
302 			BMessage *msg;
303 			menu->AddItem(item = new BMenuItem(name,msg = new BMessage(kMsgProtocolChanged)));
304 			msg->AddRef("protocol",&protocolRef);
305 
306 			if (*ref == protocolRef)
307 				item->SetMarked(true);
308 		}
309 	}
310 
311 	fProtocolsMenuField = new BMenuField(BRect(0,0,200,40),NULL,NULL,menu);
312 	fProtocolsMenuField->ResizeToPreferred();
313 	SetLabel(fProtocolsMenuField);
314 
315 	if (fConfigView)
316 	{
317 		fConfigView->MoveTo(3,21);
318 		ResizeBy(0,8);
319 	}
320 	else
321 		fImage = B_OK;
322 }
323 
324 
325 void ProtocolsConfigView::AttachedToWindow()
326 {
327 	FilterConfigView::AttachedToWindow();
328 	fProtocolsMenuField->Menu()->SetTargetForItems(this);
329 }
330 
331 
332 void ProtocolsConfigView::MessageReceived(BMessage *msg)
333 {
334 	switch (msg->what)
335 	{
336 		case kMsgProtocolChanged:
337 		{
338 			entry_ref ref;
339 			if (msg->FindRef("protocol",&ref) < B_OK)
340 				break;
341 
342 			DetachedFromWindow();
343 			Remove(false);
344 
345 			fEntryRef = new entry_ref(ref);
346 			Load(fMessage,fEntryRef);
347 			fChain->SetFilter(fIndex,*fMessage,*fEntryRef);
348 
349 			// resize view
350 			if (LockLooperWithTimeout(1000000L) == B_OK)
351 			{
352 				if (fConfigView)
353 				{
354 					fConfigView->MoveTo(3,21);
355 					ResizeBy(0,8);
356 				}
357 				UnlockLooper();
358 
359 				if (CenterContainer *container = dynamic_cast<CenterContainer *>(Parent()))
360 					container->Layout();
361 			}
362 			break;
363 		}
364 		default:
365 			BView::MessageReceived(msg);
366 			break;
367 	}
368 }
369 
370 
371 //---------------------------------------------------------------------------------------
372 //	#pragma mark -
373 
374 #include <stdio.h>
375 class DragListView : public BListView
376 {
377 	public:
378 		DragListView(BRect frame,const char *name,list_view_type type = B_SINGLE_SELECTION_LIST,
379 					 uint32 resizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP,BMessage *itemMovedMsg = NULL)
380 			: BListView(frame,name,type,resizingMode),
381 			fDragging(false),
382 			fItemMovedMessage(itemMovedMsg)
383 		{
384 		}
385 
386 		virtual bool InitiateDrag(BPoint point,int32 index,bool wasSelected)
387 		{
388 			BRect frame(ItemFrame(index));
389 			BBitmap *bitmap = new BBitmap(frame.OffsetToCopy(B_ORIGIN),B_RGBA32,true);
390 			BView *view = new BView(bitmap->Bounds(),NULL,0,0);
391 			bitmap->AddChild(view);
392 
393 			if (view->LockLooper())
394 			{
395 				BListItem *item = ItemAt(index);
396 				bool selected = item->IsSelected();
397 
398 				view->SetLowColor(225,225,225,128);
399 				view->FillRect(view->Bounds());
400 
401 				if (selected)
402 					item->Deselect();
403 				ItemAt(index)->DrawItem(view,view->Bounds(),true);
404 				if (selected)
405 					item->Select();
406 
407 				view->UnlockLooper();
408 			}
409 			fLastDragTarget = -1;
410 			fDragIndex = index;
411 			fDragging = true;
412 
413 			BMessage drag(kMsgItemDragged);
414 			drag.AddInt32("index",index);
415 			DragMessage(&drag,bitmap,B_OP_ALPHA,point - frame.LeftTop(),this);
416 
417 			return true;
418 		}
419 
420 		void DrawDragTargetIndicator(int32 target)
421 		{
422 			PushState();
423 			SetDrawingMode(B_OP_INVERT);
424 
425 			bool last = false;
426 			if (target >= CountItems())
427 				target = CountItems() - 1, last = true;
428 
429 			BRect frame = ItemFrame(target);
430 			if (last)
431 				frame.OffsetBy(0,frame.Height());
432 			frame.bottom = frame.top + 1;
433 
434 			FillRect(frame);
435 
436 			PopState();
437 		}
438 
439 		virtual void MouseMoved(BPoint point,uint32 transit,const BMessage *msg)
440 		{
441 			BListView::MouseMoved(point,transit,msg);
442 
443 			if ((transit != B_ENTERED_VIEW && transit != B_INSIDE_VIEW) || !fDragging)
444 				return;
445 
446 			int32 target = IndexOf(point);
447 			if (target == -1)
448 				target = CountItems();
449 
450 			// correct the target insertion index
451 			if (target == fDragIndex || target == fDragIndex + 1)
452 				target = -1;
453 
454 			if (target == fLastDragTarget)
455 				return;
456 
457 			// remove old target indicator
458 			if (fLastDragTarget != -1)
459 				DrawDragTargetIndicator(fLastDragTarget);
460 
461 			// draw new one
462 			fLastDragTarget = target;
463 			if (target != -1)
464 				DrawDragTargetIndicator(target);
465 		}
466 
467 		virtual void MouseUp(BPoint point)
468 		{
469 			if (fDragging)
470 			{
471 				fDragging = false;
472 				if (fLastDragTarget != -1)
473 					DrawDragTargetIndicator(fLastDragTarget);
474 			}
475 			BListView::MouseUp(point);
476 		}
477 
478 		virtual void MessageReceived(BMessage *msg)
479 		{
480 			switch(msg->what)
481 			{
482 				case kMsgItemDragged:
483 				{
484 					int32 source = msg->FindInt32("index");
485 					BPoint point = msg->FindPoint("_drop_point_");
486 					ConvertFromScreen(&point);
487 					int32 to = IndexOf(point);
488 					if (to > fDragIndex)
489 						to--;
490 					if (to == -1)
491 						to = CountItems() - 1;
492 
493 					if (source != to)
494 					{
495 						MoveItem(source,to);
496 
497 						if (fItemMovedMessage != NULL)
498 						{
499 							BMessage msg(fItemMovedMessage->what);
500 							msg.AddInt32("from",source);
501 							msg.AddInt32("to",to);
502 							Messenger().SendMessage(&msg);
503 						}
504 					}
505 					break;
506 				}
507 			}
508 			BListView::MessageReceived(msg);
509 		}
510 
511 	private:
512 		bool		fDragging;
513 		int32		fLastDragTarget,fDragIndex;
514 		BMessage	*fItemMovedMessage;
515 };
516 
517 
518 void GetPrettyDescriptiveName(BPath &path, char *name, BMessage *msg = NULL)
519 {
520 	strcpy(name, path.Leaf());
521 
522 	image_id image = load_add_on(path.Path());
523 	if (image < B_OK)
524 		return;
525 
526 	if (msg)
527 	{
528 		status_t (* descriptive_name)(BMessage *,char *);
529 		if (get_image_symbol(image,"descriptive_name",B_SYMBOL_TYPE_TEXT,(void **)&descriptive_name) == B_OK)
530 			(*descriptive_name)(msg,name);
531 	}
532 	unload_add_on(image);
533 }
534 
535 
536 //	#pragma mark -
537 
538 
539 FiltersConfigView::FiltersConfigView(BRect rect,Account *account)
540 	:	BBox(rect),
541 		fAccount(account),
542 		fFilterView(NULL)
543 {
544 	BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING);
545 
546 	BMenuItem *item;
547 	BMessage *msg;
548 	if ((fChain = fAccount->Inbound()))
549 	{
550 		menu->AddItem(item = new BMenuItem(TR ("Incoming mail filters"),msg = new BMessage(kMsgChainSelected)));
551 		msg->AddPointer("chain",fChain);
552 		item->SetMarked(true);
553 	}
554 	if (BMailChain *chain = fAccount->Outbound())
555 	{
556 		menu->AddItem(item = new BMenuItem(TR ("Outgoing mail filters"),msg = new BMessage(kMsgChainSelected)));
557 		msg->AddPointer("chain",chain);
558 		if (fChain == NULL)
559 		{
560 			item->SetMarked(true);
561 			fChain = chain;
562 		}
563 	}
564 
565 	fChainsField = new BMenuField(BRect(0,0,200,40),NULL,NULL,menu);
566 	fChainsField->ResizeToPreferred();
567 	SetLabel(fChainsField);
568 
569 	// determine font height
570 	font_height fontHeight;
571 	fChainsField->GetFontHeight(&fontHeight);
572 	int32 height = (int32)(fontHeight.ascent + fontHeight.descent + fontHeight.leading) + 5;
573 
574 	rect = Bounds().InsetByCopy(10,10);
575 	rect.top += 18;
576 	rect.right -= B_V_SCROLL_BAR_WIDTH;
577 	rect.bottom = rect.top + 4 * height + 2;
578 	fListView = new DragListView(rect,NULL,B_SINGLE_SELECTION_LIST,B_FOLLOW_ALL,new BMessage(kMsgFilterMoved));
579 	AddChild(new BScrollView(NULL,fListView,B_FOLLOW_ALL,0,false,true));
580 	rect.right += B_V_SCROLL_BAR_WIDTH;
581 
582 //	fListView->Select(gSettings.formats.IndexOf(format));
583 	fListView->SetSelectionMessage(new BMessage(kMsgFilterSelected));
584 
585 	rect.top = rect.bottom + 8;  rect.bottom = rect.top + height;
586 	BRect sizeRect = rect;	sizeRect.right = sizeRect.left + 30 + fChainsField->StringWidth(TR ("Add filter"));
587 
588 	menu = new BPopUpMenu(TR ("Add filter"));
589 	menu->SetRadioMode(false);
590 
591 	fAddField = new BMenuField(rect,NULL,NULL,menu);
592 	fAddField->ResizeToPreferred();
593 	AddChild(fAddField);
594 
595 	sizeRect.left = sizeRect.right + 5;	sizeRect.right = sizeRect.left + 30 + fChainsField->StringWidth(TR ("Remove"));
596 	sizeRect.top--;
597 	AddChild(fRemoveButton = new BButton(sizeRect,NULL,TR ("Remove"),new BMessage(kMsgRemoveFilter),B_FOLLOW_BOTTOM));
598 
599 	ResizeTo(Bounds().Width(),sizeRect.bottom + 10);
600 	SetTo(fChain);
601 }
602 
603 
604 FiltersConfigView::~FiltersConfigView()
605 {
606 }
607 
608 
609 void FiltersConfigView::SelectFilter(int32 index)
610 {
611 	if (Parent())
612 		Parent()->Hide();
613 
614 	// remove old config view
615 	if (fFilterView)
616 	{
617 		Parent()->RemoveChild(fFilterView);
618 
619 		// update the name in the list
620 		BStringItem *item = (BStringItem *)fListView->ItemAt(fFilterView->fIndex - fFirst);
621 
622 		char name[B_FILE_NAME_LENGTH];
623 		BPath path(fFilterView->fEntryRef);
624 		GetPrettyDescriptiveName(path, name, fFilterView->fMessage);
625 		item->SetText(name);
626 
627 		delete fFilterView;
628 		fFilterView = NULL;
629 	}
630 
631 	if (index >= 0)
632 	{
633 		// add new config view
634 		BMessage *msg = new BMessage();
635 		entry_ref *ref = new entry_ref();
636 		if (fChain->GetFilter(index + fFirst,msg,ref) >= B_OK && Parent())
637 		{
638 			fFilterView = new FilterConfigView(fChain,index + fFirst,msg,ref);
639 			if (fFilterView->InitCheck() >= B_OK)
640 				Parent()->AddChild(fFilterView);
641 			else
642 			{
643 				delete fFilterView;
644 				fFilterView = NULL;
645 			}
646 		}
647 		else
648 		{
649 			delete msg;
650 			delete ref;
651 		}
652 	}
653 
654 	// re-layout the view containing the config view
655 	if (CenterContainer *container = dynamic_cast<CenterContainer *>(Parent()))
656 		container->Layout();
657 
658 	if (Parent())
659 		Parent()->Show();
660 }
661 
662 
663 void FiltersConfigView::SetTo(BMailChain *chain)
664 {
665 	// remove the filter config view
666 	SelectFilter(-1);
667 
668 	for (int32 i = fListView->CountItems();i-- > 0;)
669 	{
670 		BStringItem *item = (BStringItem *)fListView->RemoveItem(i);
671 		delete item;
672 	}
673 
674 	if (chain->ChainDirection() == inbound)
675 	{
676 		fFirst = 2;		// skip protocol (e.g. POP3), and Parser
677 		fLast = 2;		// skip Notifier, and Folder
678 	}
679 	else
680 	{
681 		fFirst = 1;		// skip Producer
682 		fLast = 1;		// skip protocol (e.g. SMTP)
683 	}
684 	int32 last = chain->CountFilters() - fLast;
685 	for (int32 i = fFirst;i < last;i++)
686 	{
687 		BMessage msg;
688 		entry_ref ref;
689 		if (chain->GetFilter(i,&msg,&ref) == B_OK)
690 		{
691 			char name[B_FILE_NAME_LENGTH];
692 			BPath addon(&ref);
693 			GetPrettyDescriptiveName(addon, name, &msg);
694 
695 			fListView->AddItem(new BStringItem(name));
696 		}
697 	}
698 	fChain = chain;
699 
700 	/*** search inbound/outbound filters ***/
701 
702 	// remove old filter items
703 	BMenu *menu = fAddField->Menu();
704 	for (int32 i = menu->CountItems();i-- > 0;)
705 	{
706 		BMenuItem *item = menu->RemoveItem(i);
707 		delete item;
708 	}
709 
710 	for (int i = 0; i < 2; i++) {
711 		BPath path;
712 		status_t status = find_directory((i == 0) ? B_USER_ADDONS_DIRECTORY : B_BEOS_ADDONS_DIRECTORY,&path);
713 		if (status != B_OK)
714 			return;
715 
716 		path.Append("mail_daemon");
717 
718 		if (fChain->ChainDirection() == inbound)
719 			path.Append("inbound_filters");
720 		else
721 			path.Append("outbound_filters");
722 
723 		BDirectory dir(path.Path());
724 		entry_ref ref;
725 		while (dir.GetNextRef(&ref) == B_OK)
726 		{
727 			char name[B_FILE_NAME_LENGTH];
728 			BPath path(&ref);
729 			GetPrettyDescriptiveName(path, name);
730 
731 			BMenuItem *item;
732 			BMessage *msg;
733 			menu->AddItem(item = new BMenuItem(name,msg = new BMessage(kMsgAddFilter)));
734 			msg->AddRef("filter",&ref);
735 		}
736 	}
737 	menu->SetTargetForItems(this);
738 }
739 
740 
741 void FiltersConfigView::AttachedToWindow()
742 {
743 	fChainsField->Menu()->SetTargetForItems(this);
744 	fListView->SetTarget(this);
745 	fAddField->Menu()->SetTargetForItems(this);
746 	fRemoveButton->SetTarget(this);
747 }
748 
749 
750 void FiltersConfigView::MessageReceived(BMessage *msg)
751 {
752 	switch (msg->what)
753 	{
754 		case kMsgChainSelected:
755 		{
756 			BMailChain *chain;
757 			if (msg->FindPointer("chain",(void **)&chain) < B_OK)
758 				break;
759 
760 			SetTo(chain);
761 			break;
762 		}
763 		case kMsgAddFilter:
764 		{
765 			entry_ref ref;
766 			if (msg->FindRef("filter",&ref) < B_OK)
767 				break;
768 
769 			BMessage msg;
770 			if (fChain->AddFilter(fChain->CountFilters() - fLast, msg, ref) >= B_OK)
771 			{
772 				char name[B_FILE_NAME_LENGTH];
773 				BPath path(&ref);
774 				GetPrettyDescriptiveName(path, name, &msg);
775 				fListView->AddItem(new BStringItem(name));
776 			}
777 			break;
778 		}
779 		case kMsgRemoveFilter:
780 		{
781 			int32 index = fListView->CurrentSelection();
782 			if (index < 0)
783 				break;
784 
785 			SelectFilter(-1);
786 			if (BStringItem *item = (BStringItem *)fListView->RemoveItem(index))
787 			{
788 				fChain->RemoveFilter(index + fFirst);
789 				delete item;
790 			}
791 			break;
792 		}
793 		case kMsgFilterSelected:
794 		{
795 			int32 index;
796 			if (msg->FindInt32("index",&index) < B_OK)
797 				break;
798 
799 			SelectFilter(index);
800 			break;
801 		}
802 		case kMsgFilterMoved:
803 		{
804 			int32 from = msg->FindInt32("from");
805 			int32 to = msg->FindInt32("to");
806 			if (from == to)
807 				break;
808 
809 			from += fFirst;
810 			to += fFirst;
811 
812 			entry_ref ref;
813 			BMessage settings;
814 			if (fChain->GetFilter(from,&settings,&ref) == B_OK)
815 			{
816 				// disable filter view saving
817 				if (fFilterView && fFilterView->fIndex == from)
818 					fFilterView->fIndex = -1;
819 
820 				fChain->RemoveFilter(from);
821 
822 				if (fChain->AddFilter(to,settings,ref) < B_OK)
823 				{
824 					(new BAlert("E-mail",TR (
825 					"The filter could not be moved. Deleting filter."
826 					),TR("OK")))->Go();
827 
828 					// the filter view belongs to the moved filter
829 					if (fFilterView && fFilterView->fIndex == -1)
830 						SelectFilter(-1);
831 
832 					fListView->RemoveItem(msg->FindInt32("to"));
833 				}
834 				else if (fFilterView)
835 				{
836 					int32 index = fFilterView->fIndex;
837 					if (index == -1)
838 						// the view belongs to the moved filter
839 						fFilterView->fIndex = to;
840 					else if (index > from && index < to)
841 						// the view belongs to another filter (between the
842 						// 'from' & 'to' positions) - all others can keep
843 						// their index value
844 						fFilterView->fIndex--;
845 				}
846 			}
847 			break;
848 		}
849 		default:
850 			BView::MessageReceived(msg);
851 			break;
852 	}
853 }
854 
855