1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2001, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN 23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or 30 registered trademarks of Be Incorporated in the United States and other 31 countries. Other brand product names are registered trademarks or trademarks 32 of their respective holders. All rights reserved. 33 */ 34 35 //-------------------------------------------------------------------- 36 // 37 // Enclosures.cpp 38 // The enclosures list view (TListView), the list items (TListItem), 39 // and the view containing the list 40 // and handling the messages (TEnclosuresView). 41 //-------------------------------------------------------------------- 42 43 #include "Enclosures.h" 44 45 #include <Alert.h> 46 #include <Beep.h> 47 #include <Bitmap.h> 48 #include <Debug.h> 49 #include <Locale.h> 50 #include <MenuItem.h> 51 #include <NodeMonitor.h> 52 #include <PopUpMenu.h> 53 54 #include <MailAttachment.h> 55 #include <MailMessage.h> 56 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 61 #include "MailApp.h" 62 #include "MailSupport.h" 63 #include "MailWindow.h" 64 #include "Messages.h" 65 66 67 #define B_TRANSLATION_CONTEXT "Mail" 68 69 70 static const float kPlainFontSizeScale = 0.9; 71 72 static status_t 73 GetTrackerIcon(BMimeType &type, BBitmap *icon, icon_size iconSize) 74 { 75 // set some icon size related variables 76 status_t error = B_OK; 77 BRect bounds; 78 switch (iconSize) { 79 case B_MINI_ICON: 80 bounds.Set(0, 0, 15, 15); 81 break; 82 case B_LARGE_ICON: 83 bounds.Set(0, 0, 31, 31); 84 break; 85 default: 86 error = B_BAD_VALUE; 87 break; 88 } 89 // check parameters and initialization 90 if (error == B_OK 91 && (!icon || icon->InitCheck() != B_OK || icon->Bounds() != bounds)) 92 return B_BAD_VALUE; 93 94 bool success = false; 95 96 // Ask the MIME database for the preferred application for the file type 97 // and whether this application has a special icon for the type. 98 char signature[B_MIME_TYPE_LENGTH]; 99 if (type.GetPreferredApp(signature) == B_OK) { 100 BMimeType type(signature); 101 success = (type.GetIconForType(type.Type(), icon, iconSize) == B_OK); 102 } 103 104 // Ask the MIME database whether there is an icon for the node's file type. 105 if (error == B_OK && !success) 106 success = (type.GetIcon(icon, iconSize) == B_OK); 107 108 // Ask the MIME database for the super type and start all over 109 if (error == B_OK && !success) { 110 BMimeType super; 111 if (type.GetSupertype(&super) == B_OK) 112 return GetTrackerIcon(super, icon, iconSize); 113 } 114 115 // Return the icon for "application/octet-stream" from the MIME database. 116 if (error == B_OK && !success) { 117 // get the "application/octet-stream" icon 118 BMimeType type("application/octet-stream"); 119 error = type.GetIcon(icon, iconSize); 120 } 121 122 return error; 123 } 124 125 126 static void 127 recursive_attachment_search(TEnclosuresView* us, BMailContainer* mail, 128 BMailComponent *body) 129 { 130 if (mail == NULL) 131 return; 132 133 for (int32 i = 0; i < mail->CountComponents(); i++) { 134 BMailComponent *component = mail->GetComponent(i); 135 if (component == body) 136 continue; 137 138 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) { 139 recursive_attachment_search(us, 140 dynamic_cast<BMIMEMultipartMailContainer *>(component), body); 141 } 142 143 us->fList->AddItem(new TListItem(component)); 144 } 145 } 146 147 148 // #pragma mark - 149 150 151 TEnclosuresView::TEnclosuresView(BRect rect, BRect windowRect) 152 : 153 BView(rect, "m_enclosures", B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT, 154 B_WILL_DRAW), 155 fFocus(false) 156 { 157 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 158 159 BFont font(be_plain_font); 160 font.SetSize(font.Size() * kPlainFontSizeScale); 161 SetFont(&font); 162 163 fOffset = 12; 164 165 BRect r; 166 r.left = ENCLOSE_TEXT_H + font.StringWidth( 167 B_TRANSLATE("Attachments: ")) + 5; 168 r.top = ENCLOSE_FIELD_V; 169 r.right = windowRect.right - windowRect.left - B_V_SCROLL_BAR_WIDTH - 9; 170 r.bottom = Frame().Height() - 8; 171 fList = new TListView(r, this); 172 fList->SetInvocationMessage(new BMessage(LIST_INVOKED)); 173 174 BScrollView *scroll = new BScrollView("", fList, B_FOLLOW_LEFT_RIGHT | 175 B_FOLLOW_TOP, 0, false, true); 176 AddChild(scroll); 177 scroll->ScrollBar(B_VERTICAL)->SetRange(0, 0); 178 } 179 180 181 TEnclosuresView::~TEnclosuresView() 182 { 183 for (int32 index = fList->CountItems();index-- > 0;) 184 { 185 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); 186 fList->RemoveItem(index); 187 188 if (item->Component() == NULL) 189 watch_node(item->NodeRef(), B_STOP_WATCHING, this); 190 delete item; 191 } 192 } 193 194 195 void 196 TEnclosuresView::Draw(BRect where) 197 { 198 BView::Draw(where); 199 200 SetHighColor(0, 0, 0); 201 SetLowColor(ViewColor()); 202 203 font_height fh; 204 GetFontHeight(&fh); 205 206 MovePenTo(ENCLOSE_TEXT_H, ENCLOSE_TEXT_V + fh.ascent); 207 DrawString(ENCLOSE_TEXT); 208 } 209 210 211 void 212 TEnclosuresView::MessageReceived(BMessage *msg) 213 { 214 switch (msg->what) 215 { 216 case LIST_INVOKED: 217 { 218 BListView *list; 219 msg->FindPointer("source", (void **)&list); 220 if (list) 221 { 222 TListItem *item = (TListItem *) (list->ItemAt(msg->FindInt32("index"))); 223 if (item) 224 { 225 BMessenger tracker("application/x-vnd.Be-TRAK"); 226 if (tracker.IsValid()) 227 { 228 BMessage message(B_REFS_RECEIVED); 229 message.AddRef("refs", item->Ref()); 230 231 tracker.SendMessage(&message); 232 } 233 } 234 } 235 break; 236 } 237 238 case M_REMOVE: 239 { 240 int32 index; 241 while ((index = fList->CurrentSelection()) >= 0) 242 { 243 TListItem *item = (TListItem *) fList->ItemAt(index); 244 fList->RemoveItem(index); 245 246 if (item->Component()) 247 { 248 TMailWindow *window = dynamic_cast<TMailWindow *>(Window()); 249 if (window && window->Mail()) 250 window->Mail()->RemoveComponent(item->Component()); 251 252 BAlert* alert = new BAlert("", B_TRANSLATE( 253 "Removing attachments from a forwarded mail is not yet " 254 "implemented!\nIt will not yet work correctly."), 255 B_TRANSLATE("OK")); 256 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 257 alert->Go(); 258 } 259 else 260 watch_node(item->NodeRef(), B_STOP_WATCHING, this); 261 delete item; 262 } 263 break; 264 } 265 266 case M_SELECT: 267 fList->Select(0, fList->CountItems() - 1, true); 268 break; 269 270 case B_SIMPLE_DATA: 271 case B_REFS_RECEIVED: 272 case REFS_RECEIVED: 273 if (msg->HasRef("refs")) 274 { 275 bool badType = false; 276 277 int32 index = 0; 278 entry_ref ref; 279 while (msg->FindRef("refs", index++, &ref) == B_NO_ERROR) 280 { 281 BFile file(&ref, O_RDONLY); 282 if (file.InitCheck() == B_OK && file.IsFile()) 283 { 284 TListItem *item; 285 for (int16 loop = 0; loop < fList->CountItems(); loop++) 286 { 287 item = (TListItem *) fList->ItemAt(loop); 288 if (ref == *(item->Ref())) 289 { 290 fList->Select(loop); 291 fList->ScrollToSelection(); 292 continue; 293 } 294 } 295 fList->AddItem(item = new TListItem(&ref)); 296 fList->Select(fList->CountItems() - 1); 297 fList->ScrollToSelection(); 298 299 watch_node(item->NodeRef(), B_WATCH_NAME, this); 300 } 301 else 302 badType = true; 303 } 304 if (badType) 305 { 306 beep(); 307 BAlert* alert = new BAlert("", 308 B_TRANSLATE("Only files can be added as attachments."), 309 B_TRANSLATE("OK")); 310 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 311 alert->Go(); 312 } 313 } 314 break; 315 316 case B_NODE_MONITOR: 317 { 318 int32 opcode; 319 if (msg->FindInt32("opcode", &opcode) == B_NO_ERROR) 320 { 321 dev_t device; 322 if (msg->FindInt32("device", &device) < B_OK) 323 break; 324 ino_t inode; 325 if (msg->FindInt64("node", &inode) < B_OK) 326 break; 327 328 for (int32 index = fList->CountItems();index-- > 0;) 329 { 330 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); 331 332 if (device == item->NodeRef()->device 333 && inode == item->NodeRef()->node) 334 { 335 if (opcode == B_ENTRY_REMOVED) 336 { 337 // don't hide the <missing attachment> item 338 339 //fList->RemoveItem(index); 340 // 341 //watch_node(item->NodeRef(), B_STOP_WATCHING, this); 342 //delete item; 343 } 344 else if (opcode == B_ENTRY_MOVED) 345 { 346 item->Ref()->device = device; 347 msg->FindInt64("to directory", &item->Ref()->directory); 348 349 const char *name; 350 msg->FindString("name", &name); 351 item->Ref()->set_name(name); 352 } 353 354 fList->InvalidateItem(index); 355 break; 356 } 357 } 358 } 359 break; 360 } 361 362 default: 363 BView::MessageReceived(msg); 364 break; 365 } 366 } 367 368 369 void 370 TEnclosuresView::Focus(bool focus) 371 { 372 if (fFocus != focus) 373 { 374 fFocus = focus; 375 Draw(Frame()); 376 } 377 } 378 379 380 void 381 TEnclosuresView::AddEnclosuresFromMail(BEmailMessage *mail) 382 { 383 for (int32 i = 0; i < mail->CountComponents(); i++) 384 { 385 BMailComponent *component = mail->GetComponent(i); 386 if (component == mail->Body()) 387 continue; 388 389 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) 390 recursive_attachment_search(this,dynamic_cast<BMIMEMultipartMailContainer *>(component),mail->Body()); 391 392 fList->AddItem(new TListItem(component)); 393 } 394 } 395 396 397 // #pragma mark - 398 399 400 TListView::TListView(BRect rect, TEnclosuresView *view) 401 : 402 BListView(rect, "", B_MULTIPLE_SELECTION_LIST, 403 B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT), 404 fParent(view) 405 { 406 } 407 408 409 void 410 TListView::AttachedToWindow() 411 { 412 BListView::AttachedToWindow(); 413 414 BFont font(be_plain_font); 415 font.SetSize(font.Size() * kPlainFontSizeScale); 416 SetFont(&font); 417 } 418 419 420 void 421 TListView::MakeFocus(bool focus) 422 { 423 BListView::MakeFocus(focus); 424 fParent->Focus(focus); 425 } 426 427 428 void 429 TListView::MouseDown(BPoint point) 430 { 431 int32 buttons; 432 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 433 434 BListView::MouseDown(point); 435 436 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0 && IndexOf(point) >= 0) { 437 BPopUpMenu menu("enclosure", false, false); 438 menu.SetFont(be_plain_font); 439 menu.AddItem(new BMenuItem(B_TRANSLATE("Open attachment"), 440 new BMessage(LIST_INVOKED))); 441 menu.AddItem(new BMenuItem(B_TRANSLATE("Remove attachment"), 442 new BMessage(M_REMOVE))); 443 444 BPoint menuStart = ConvertToScreen(point); 445 446 BMenuItem* item = menu.Go(menuStart); 447 if (item != NULL) { 448 if (item->Command() == LIST_INVOKED) { 449 BMessage msg(LIST_INVOKED); 450 msg.AddPointer("source",this); 451 msg.AddInt32("index",IndexOf(point)); 452 Window()->PostMessage(&msg,fParent); 453 } else { 454 Select(IndexOf(point)); 455 Window()->PostMessage(item->Command(),fParent); 456 } 457 } 458 } 459 } 460 461 462 void 463 TListView::KeyDown(const char *bytes, int32 numBytes) 464 { 465 BListView::KeyDown(bytes,numBytes); 466 467 if (numBytes == 1 && *bytes == B_DELETE) 468 Window()->PostMessage(M_REMOVE, fParent); 469 } 470 471 472 // #pragma mark - 473 474 475 TListItem::TListItem(entry_ref *ref) 476 { 477 fComponent = NULL; 478 fRef = *ref; 479 480 BEntry entry(ref); 481 entry.GetNodeRef(&fNodeRef); 482 } 483 484 485 TListItem::TListItem(BMailComponent *component) 486 : 487 fComponent(component) 488 { 489 } 490 491 492 void 493 TListItem::Update(BView *owner, const BFont *font) 494 { 495 BListItem::Update(owner, font); 496 497 if (Height() < 17) // mini icon height + 1 498 SetHeight(17); 499 } 500 501 502 void 503 TListItem::DrawItem(BView *owner, BRect r, bool /* complete */) 504 { 505 if (IsSelected()) { 506 owner->SetHighColor(180, 180, 180); 507 owner->SetLowColor(180, 180, 180); 508 } else { 509 owner->SetHighColor(255, 255, 255); 510 owner->SetLowColor(255, 255, 255); 511 } 512 owner->FillRect(r); 513 owner->SetHighColor(0, 0, 0); 514 515 BFont font = *be_plain_font; 516 font.SetSize(font.Size() * kPlainFontSizeScale); 517 owner->SetFont(&font); 518 owner->MovePenTo(r.left + 24, r.bottom - 4); 519 520 if (fComponent) { 521 // if it's already a mail component, we don't have an icon to 522 // draw, and the entry_ref is invalid 523 BMailAttachment *attachment = dynamic_cast<BMailAttachment *>(fComponent); 524 525 char name[B_FILE_NAME_LENGTH * 2]; 526 if ((attachment == NULL) || (attachment->FileName(name) < B_OK)) 527 strcpy(name, "unnamed"); 528 529 BMimeType type; 530 if (fComponent->MIMEType(&type) == B_OK) 531 sprintf(name + strlen(name), ", Type: %s", type.Type()); 532 533 owner->DrawString(name); 534 535 BRect iconRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 536 537 BBitmap bitmap(iconRect, B_RGBA32); 538 if (GetTrackerIcon(type, &bitmap, B_MINI_ICON) == B_NO_ERROR) { 539 BRect rect(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 540 owner->SetDrawingMode(B_OP_ALPHA); 541 owner->DrawBitmap(&bitmap, iconRect, rect); 542 owner->SetDrawingMode(B_OP_COPY); 543 } else { 544 // ToDo: find some nicer image for this :-) 545 owner->SetHighColor(150, 150, 150); 546 owner->FillEllipse(BRect(r.left + 8, r.top + 4, r.left + 16, r.top + 13)); 547 } 548 return; 549 } 550 551 BFile file(&fRef, O_RDONLY); 552 BEntry entry(&fRef); 553 BPath path; 554 if (entry.GetPath(&path) == B_OK && file.InitCheck() == B_OK) { 555 owner->DrawString(path.Path()); 556 557 BNodeInfo info(&file); 558 BRect sr(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 559 560 BBitmap bitmap(sr, B_RGBA32); 561 if (info.GetTrackerIcon(&bitmap, B_MINI_ICON) == B_NO_ERROR) { 562 BRect dr(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 563 owner->SetDrawingMode(B_OP_ALPHA); 564 owner->DrawBitmap(&bitmap, sr, dr); 565 owner->SetDrawingMode(B_OP_COPY); 566 } 567 } else 568 owner->DrawString("<missing attachment>"); 569 } 570