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 29const uint32 kMsgFilterMoved = 'flmv'; 30const uint32 kMsgChainSelected = 'chsl'; 31const uint32 kMsgAddFilter = 'addf'; 32const uint32 kMsgRemoveFilter = 'rmfi'; 33const uint32 kMsgFilterSelected = 'fsel'; 34 35const uint32 kMsgItemDragged = 'itdr'; 36 37 38class DragListView : public BListView { 39public: 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 172private: 173 bool fDragging; 174 int32 fLastDragTarget,fDragIndex; 175 BMessage *fItemMovedMessage; 176}; 177 178 179// #pragma mark - 180 181 182class FilterSettingsView : public BBox { 183public: 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 204private: 205 BMailSettingsView* fSettingsView; 206}; 207 208 209// #pragma mark - 210 211 212FiltersConfigView::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 268FiltersConfigView::~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 279void 280FiltersConfigView::_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 314void 315FiltersConfigView::_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 364void 365FiltersConfigView::AttachedToWindow() 366{ 367 fChainsField->Menu()->SetTargetForItems(this); 368 fListView->SetTarget(this); 369 fAddField->Menu()->SetTargetForItems(this); 370 fRemoveButton->SetTarget(this); 371} 372 373 374void 375FiltersConfigView::DetachedFromWindow() 376{ 377 _SaveConfig(fCurrentIndex); 378} 379 380 381void 382FiltersConfigView::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 458BMailProtocolSettings* 459FiltersConfigView::_MailSettings() 460{ 461 return fDirection == kIncoming 462 ? &fAccount.InboundSettings() : &fAccount.OutboundSettings(); 463} 464 465 466FilterList* 467FiltersConfigView::_FilterList() 468{ 469 return fDirection == kIncoming ? &fInboundFilters : &fOutboundFilters; 470} 471 472 473void 474FiltersConfigView::_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