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: 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 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 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 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 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 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: 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 199 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 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 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 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 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 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 375 FiltersConfigView::DetachedFromWindow() 376 { 377 _SaveConfig(fCurrentIndex); 378 } 379 380 381 void 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* 459 FiltersConfigView::_MailSettings() 460 { 461 return fDirection == kIncoming 462 ? &fAccount.InboundSettings() : &fAccount.OutboundSettings(); 463 } 464 465 466 FilterList* 467 FiltersConfigView::_FilterList() 468 { 469 return fDirection == kIncoming ? &fInboundFilters : &fOutboundFilters; 470 } 471 472 473 void 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