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