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_TRANSLATE_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 SetViewColor(ui_color(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 (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")))->Go(); 256 } 257 else 258 watch_node(item->NodeRef(), B_STOP_WATCHING, this); 259 delete item; 260 } 261 break; 262 } 263 264 case M_SELECT: 265 fList->Select(0, fList->CountItems() - 1, true); 266 break; 267 268 case B_SIMPLE_DATA: 269 case B_REFS_RECEIVED: 270 case REFS_RECEIVED: 271 if (msg->HasRef("refs")) 272 { 273 bool badType = false; 274 275 int32 index = 0; 276 entry_ref ref; 277 while (msg->FindRef("refs", index++, &ref) == B_NO_ERROR) 278 { 279 BFile file(&ref, O_RDONLY); 280 if (file.InitCheck() == B_OK && file.IsFile()) 281 { 282 TListItem *item; 283 for (int16 loop = 0; loop < fList->CountItems(); loop++) 284 { 285 item = (TListItem *) fList->ItemAt(loop); 286 if (ref == *(item->Ref())) 287 { 288 fList->Select(loop); 289 fList->ScrollToSelection(); 290 continue; 291 } 292 } 293 fList->AddItem(item = new TListItem(&ref)); 294 fList->Select(fList->CountItems() - 1); 295 fList->ScrollToSelection(); 296 297 watch_node(item->NodeRef(), B_WATCH_NAME, this); 298 } 299 else 300 badType = true; 301 } 302 if (badType) 303 { 304 beep(); 305 (new BAlert("", 306 B_TRANSLATE("Only files can be added as attachments."), 307 B_TRANSLATE("OK")))->Go(); 308 } 309 } 310 break; 311 312 case B_NODE_MONITOR: 313 { 314 int32 opcode; 315 if (msg->FindInt32("opcode", &opcode) == B_NO_ERROR) 316 { 317 dev_t device; 318 if (msg->FindInt32("device", &device) < B_OK) 319 break; 320 ino_t inode; 321 if (msg->FindInt64("node", &inode) < B_OK) 322 break; 323 324 for (int32 index = fList->CountItems();index-- > 0;) 325 { 326 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); 327 328 if (device == item->NodeRef()->device 329 && inode == item->NodeRef()->node) 330 { 331 if (opcode == B_ENTRY_REMOVED) 332 { 333 // don't hide the <missing attachment> item 334 335 //fList->RemoveItem(index); 336 // 337 //watch_node(item->NodeRef(), B_STOP_WATCHING, this); 338 //delete item; 339 } 340 else if (opcode == B_ENTRY_MOVED) 341 { 342 item->Ref()->device = device; 343 msg->FindInt64("to directory", &item->Ref()->directory); 344 345 const char *name; 346 msg->FindString("name", &name); 347 item->Ref()->set_name(name); 348 } 349 350 fList->InvalidateItem(index); 351 break; 352 } 353 } 354 } 355 break; 356 } 357 358 default: 359 BView::MessageReceived(msg); 360 } 361 } 362 363 364 void 365 TEnclosuresView::Focus(bool focus) 366 { 367 if (fFocus != focus) 368 { 369 fFocus = focus; 370 Draw(Frame()); 371 } 372 } 373 374 375 void 376 TEnclosuresView::AddEnclosuresFromMail(BEmailMessage *mail) 377 { 378 for (int32 i = 0; i < mail->CountComponents(); i++) 379 { 380 BMailComponent *component = mail->GetComponent(i); 381 if (component == mail->Body()) 382 continue; 383 384 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) 385 recursive_attachment_search(this,dynamic_cast<BMIMEMultipartMailContainer *>(component),mail->Body()); 386 387 fList->AddItem(new TListItem(component)); 388 } 389 } 390 391 392 // #pragma mark - 393 394 395 TListView::TListView(BRect rect, TEnclosuresView *view) 396 : 397 BListView(rect, "", B_MULTIPLE_SELECTION_LIST, 398 B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT), 399 fParent(view) 400 { 401 } 402 403 404 void 405 TListView::AttachedToWindow() 406 { 407 BListView::AttachedToWindow(); 408 409 BFont font(be_plain_font); 410 font.SetSize(font.Size() * kPlainFontSizeScale); 411 SetFont(&font); 412 } 413 414 415 void 416 TListView::MakeFocus(bool focus) 417 { 418 BListView::MakeFocus(focus); 419 fParent->Focus(focus); 420 } 421 422 423 void 424 TListView::MouseDown(BPoint point) 425 { 426 int32 buttons; 427 Looper()->CurrentMessage()->FindInt32("buttons", &buttons); 428 429 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { 430 BPopUpMenu menu("enclosure", false, false); 431 menu.SetFont(be_plain_font); 432 menu.AddItem(new BMenuItem(B_TRANSLATE("Open attachment"), 433 new BMessage(LIST_INVOKED))); 434 menu.AddItem(new BMenuItem(B_TRANSLATE("Remove attachment"), 435 new BMessage(M_REMOVE))); 436 437 BPoint menuStart = ConvertToScreen(point); 438 439 BMenuItem* item = menu.Go(menuStart); 440 if (item != NULL) { 441 if (item->Command() == LIST_INVOKED) { 442 BMessage msg(LIST_INVOKED); 443 msg.AddPointer("source",this); 444 msg.AddInt32("index",IndexOf(point)); 445 Window()->PostMessage(&msg,fParent); 446 } else { 447 Select(IndexOf(point)); 448 Window()->PostMessage(item->Command(),fParent); 449 } 450 } 451 } else 452 BListView::MouseDown(point); 453 } 454 455 456 void 457 TListView::KeyDown(const char *bytes, int32 numBytes) 458 { 459 BListView::KeyDown(bytes,numBytes); 460 461 if (numBytes == 1 && *bytes == B_DELETE) 462 Window()->PostMessage(M_REMOVE, fParent); 463 } 464 465 466 // #pragma mark - 467 468 469 TListItem::TListItem(entry_ref *ref) 470 { 471 fComponent = NULL; 472 fRef = *ref; 473 474 BEntry entry(ref); 475 entry.GetNodeRef(&fNodeRef); 476 } 477 478 479 TListItem::TListItem(BMailComponent *component) 480 : 481 fComponent(component) 482 { 483 } 484 485 486 void 487 TListItem::Update(BView *owner, const BFont *font) 488 { 489 BListItem::Update(owner, font); 490 491 if (Height() < 17) // mini icon height + 1 492 SetHeight(17); 493 } 494 495 496 void 497 TListItem::DrawItem(BView *owner, BRect r, bool /* complete */) 498 { 499 if (IsSelected()) { 500 owner->SetHighColor(180, 180, 180); 501 owner->SetLowColor(180, 180, 180); 502 } else { 503 owner->SetHighColor(255, 255, 255); 504 owner->SetLowColor(255, 255, 255); 505 } 506 owner->FillRect(r); 507 owner->SetHighColor(0, 0, 0); 508 509 BFont font = *be_plain_font; 510 font.SetSize(font.Size() * kPlainFontSizeScale); 511 owner->SetFont(&font); 512 owner->MovePenTo(r.left + 24, r.bottom - 4); 513 514 if (fComponent) { 515 // if it's already a mail component, we don't have an icon to 516 // draw, and the entry_ref is invalid 517 BMailAttachment *attachment = dynamic_cast<BMailAttachment *>(fComponent); 518 519 char name[B_FILE_NAME_LENGTH * 2]; 520 if ((attachment == NULL) || (attachment->FileName(name) < B_OK)) 521 strcpy(name, "unnamed"); 522 523 BMimeType type; 524 if (fComponent->MIMEType(&type) == B_OK) 525 sprintf(name + strlen(name), ", Type: %s", type.Type()); 526 527 owner->DrawString(name); 528 529 BRect iconRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 530 531 BBitmap bitmap(iconRect, B_RGBA32); 532 if (GetTrackerIcon(type, &bitmap, B_MINI_ICON) == B_NO_ERROR) { 533 BRect rect(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 534 owner->SetDrawingMode(B_OP_ALPHA); 535 owner->DrawBitmap(&bitmap, iconRect, rect); 536 owner->SetDrawingMode(B_OP_COPY); 537 } else { 538 // ToDo: find some nicer image for this :-) 539 owner->SetHighColor(150, 150, 150); 540 owner->FillEllipse(BRect(r.left + 8, r.top + 4, r.left + 16, r.top + 13)); 541 } 542 return; 543 } 544 545 BFile file(&fRef, O_RDONLY); 546 BEntry entry(&fRef); 547 BPath path; 548 if (entry.GetPath(&path) == B_OK && file.InitCheck() == B_OK) { 549 owner->DrawString(path.Path()); 550 551 BNodeInfo info(&file); 552 BRect sr(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 553 554 BBitmap bitmap(sr, B_RGBA32); 555 if (info.GetTrackerIcon(&bitmap, B_MINI_ICON) == B_NO_ERROR) { 556 BRect dr(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 557 owner->SetDrawingMode(B_OP_ALPHA); 558 owner->DrawBitmap(&bitmap, sr, dr); 559 owner->SetDrawingMode(B_OP_COPY); 560 } 561 } else 562 owner->DrawString("<missing attachment>"); 563 } 564 565