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 } 365 } 366 367 368 void 369 TEnclosuresView::Focus(bool focus) 370 { 371 if (fFocus != focus) 372 { 373 fFocus = focus; 374 Draw(Frame()); 375 } 376 } 377 378 379 void 380 TEnclosuresView::AddEnclosuresFromMail(BEmailMessage *mail) 381 { 382 for (int32 i = 0; i < mail->CountComponents(); i++) 383 { 384 BMailComponent *component = mail->GetComponent(i); 385 if (component == mail->Body()) 386 continue; 387 388 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) 389 recursive_attachment_search(this,dynamic_cast<BMIMEMultipartMailContainer *>(component),mail->Body()); 390 391 fList->AddItem(new TListItem(component)); 392 } 393 } 394 395 396 // #pragma mark - 397 398 399 TListView::TListView(BRect rect, TEnclosuresView *view) 400 : 401 BListView(rect, "", B_MULTIPLE_SELECTION_LIST, 402 B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT), 403 fParent(view) 404 { 405 } 406 407 408 void 409 TListView::AttachedToWindow() 410 { 411 BListView::AttachedToWindow(); 412 413 BFont font(be_plain_font); 414 font.SetSize(font.Size() * kPlainFontSizeScale); 415 SetFont(&font); 416 } 417 418 419 void 420 TListView::MakeFocus(bool focus) 421 { 422 BListView::MakeFocus(focus); 423 fParent->Focus(focus); 424 } 425 426 427 void 428 TListView::MouseDown(BPoint point) 429 { 430 int32 buttons; 431 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 432 433 BListView::MouseDown(point); 434 435 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0 && IndexOf(point) >= 0) { 436 BPopUpMenu menu("enclosure", false, false); 437 menu.SetFont(be_plain_font); 438 menu.AddItem(new BMenuItem(B_TRANSLATE("Open attachment"), 439 new BMessage(LIST_INVOKED))); 440 menu.AddItem(new BMenuItem(B_TRANSLATE("Remove attachment"), 441 new BMessage(M_REMOVE))); 442 443 BPoint menuStart = ConvertToScreen(point); 444 445 BMenuItem* item = menu.Go(menuStart); 446 if (item != NULL) { 447 if (item->Command() == LIST_INVOKED) { 448 BMessage msg(LIST_INVOKED); 449 msg.AddPointer("source",this); 450 msg.AddInt32("index",IndexOf(point)); 451 Window()->PostMessage(&msg,fParent); 452 } else { 453 Select(IndexOf(point)); 454 Window()->PostMessage(item->Command(),fParent); 455 } 456 } 457 } 458 } 459 460 461 void 462 TListView::KeyDown(const char *bytes, int32 numBytes) 463 { 464 BListView::KeyDown(bytes,numBytes); 465 466 if (numBytes == 1 && *bytes == B_DELETE) 467 Window()->PostMessage(M_REMOVE, fParent); 468 } 469 470 471 // #pragma mark - 472 473 474 TListItem::TListItem(entry_ref *ref) 475 { 476 fComponent = NULL; 477 fRef = *ref; 478 479 BEntry entry(ref); 480 entry.GetNodeRef(&fNodeRef); 481 } 482 483 484 TListItem::TListItem(BMailComponent *component) 485 : 486 fComponent(component) 487 { 488 } 489 490 491 void 492 TListItem::Update(BView *owner, const BFont *font) 493 { 494 BListItem::Update(owner, font); 495 496 if (Height() < 17) // mini icon height + 1 497 SetHeight(17); 498 } 499 500 501 void 502 TListItem::DrawItem(BView *owner, BRect r, bool /* complete */) 503 { 504 if (IsSelected()) { 505 owner->SetHighColor(180, 180, 180); 506 owner->SetLowColor(180, 180, 180); 507 } else { 508 owner->SetHighColor(255, 255, 255); 509 owner->SetLowColor(255, 255, 255); 510 } 511 owner->FillRect(r); 512 owner->SetHighColor(0, 0, 0); 513 514 BFont font = *be_plain_font; 515 font.SetSize(font.Size() * kPlainFontSizeScale); 516 owner->SetFont(&font); 517 owner->MovePenTo(r.left + 24, r.bottom - 4); 518 519 if (fComponent) { 520 // if it's already a mail component, we don't have an icon to 521 // draw, and the entry_ref is invalid 522 BMailAttachment *attachment = dynamic_cast<BMailAttachment *>(fComponent); 523 524 char name[B_FILE_NAME_LENGTH * 2]; 525 if ((attachment == NULL) || (attachment->FileName(name) < B_OK)) 526 strcpy(name, "unnamed"); 527 528 BMimeType type; 529 if (fComponent->MIMEType(&type) == B_OK) 530 sprintf(name + strlen(name), ", Type: %s", type.Type()); 531 532 owner->DrawString(name); 533 534 BRect iconRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 535 536 BBitmap bitmap(iconRect, B_RGBA32); 537 if (GetTrackerIcon(type, &bitmap, B_MINI_ICON) == B_NO_ERROR) { 538 BRect rect(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 539 owner->SetDrawingMode(B_OP_ALPHA); 540 owner->DrawBitmap(&bitmap, iconRect, rect); 541 owner->SetDrawingMode(B_OP_COPY); 542 } else { 543 // ToDo: find some nicer image for this :-) 544 owner->SetHighColor(150, 150, 150); 545 owner->FillEllipse(BRect(r.left + 8, r.top + 4, r.left + 16, r.top + 13)); 546 } 547 return; 548 } 549 550 BFile file(&fRef, O_RDONLY); 551 BEntry entry(&fRef); 552 BPath path; 553 if (entry.GetPath(&path) == B_OK && file.InitCheck() == B_OK) { 554 owner->DrawString(path.Path()); 555 556 BNodeInfo info(&file); 557 BRect sr(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 558 559 BBitmap bitmap(sr, B_RGBA32); 560 if (info.GetTrackerIcon(&bitmap, B_MINI_ICON) == B_NO_ERROR) { 561 BRect dr(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 562 owner->SetDrawingMode(B_OP_ALPHA); 563 owner->DrawBitmap(&bitmap, sr, dr); 564 owner->SetDrawingMode(B_OP_COPY); 565 } 566 } else 567 owner->DrawString("<missing attachment>"); 568 } 569 570