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 BListView::MouseDown(point); 430 431 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0 && IndexOf(point) >= 0) { 432 BPopUpMenu menu("enclosure", false, false); 433 menu.SetFont(be_plain_font); 434 menu.AddItem(new BMenuItem(B_TRANSLATE("Open attachment"), 435 new BMessage(LIST_INVOKED))); 436 menu.AddItem(new BMenuItem(B_TRANSLATE("Remove attachment"), 437 new BMessage(M_REMOVE))); 438 439 BPoint menuStart = ConvertToScreen(point); 440 441 BMenuItem* item = menu.Go(menuStart); 442 if (item != NULL) { 443 if (item->Command() == LIST_INVOKED) { 444 BMessage msg(LIST_INVOKED); 445 msg.AddPointer("source",this); 446 msg.AddInt32("index",IndexOf(point)); 447 Window()->PostMessage(&msg,fParent); 448 } else { 449 Select(IndexOf(point)); 450 Window()->PostMessage(item->Command(),fParent); 451 } 452 } 453 } 454 } 455 456 457 void 458 TListView::KeyDown(const char *bytes, int32 numBytes) 459 { 460 BListView::KeyDown(bytes,numBytes); 461 462 if (numBytes == 1 && *bytes == B_DELETE) 463 Window()->PostMessage(M_REMOVE, fParent); 464 } 465 466 467 // #pragma mark - 468 469 470 TListItem::TListItem(entry_ref *ref) 471 { 472 fComponent = NULL; 473 fRef = *ref; 474 475 BEntry entry(ref); 476 entry.GetNodeRef(&fNodeRef); 477 } 478 479 480 TListItem::TListItem(BMailComponent *component) 481 : 482 fComponent(component) 483 { 484 } 485 486 487 void 488 TListItem::Update(BView *owner, const BFont *font) 489 { 490 BListItem::Update(owner, font); 491 492 if (Height() < 17) // mini icon height + 1 493 SetHeight(17); 494 } 495 496 497 void 498 TListItem::DrawItem(BView *owner, BRect r, bool /* complete */) 499 { 500 if (IsSelected()) { 501 owner->SetHighColor(180, 180, 180); 502 owner->SetLowColor(180, 180, 180); 503 } else { 504 owner->SetHighColor(255, 255, 255); 505 owner->SetLowColor(255, 255, 255); 506 } 507 owner->FillRect(r); 508 owner->SetHighColor(0, 0, 0); 509 510 BFont font = *be_plain_font; 511 font.SetSize(font.Size() * kPlainFontSizeScale); 512 owner->SetFont(&font); 513 owner->MovePenTo(r.left + 24, r.bottom - 4); 514 515 if (fComponent) { 516 // if it's already a mail component, we don't have an icon to 517 // draw, and the entry_ref is invalid 518 BMailAttachment *attachment = dynamic_cast<BMailAttachment *>(fComponent); 519 520 char name[B_FILE_NAME_LENGTH * 2]; 521 if ((attachment == NULL) || (attachment->FileName(name) < B_OK)) 522 strcpy(name, "unnamed"); 523 524 BMimeType type; 525 if (fComponent->MIMEType(&type) == B_OK) 526 sprintf(name + strlen(name), ", Type: %s", type.Type()); 527 528 owner->DrawString(name); 529 530 BRect iconRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 531 532 BBitmap bitmap(iconRect, B_RGBA32); 533 if (GetTrackerIcon(type, &bitmap, B_MINI_ICON) == B_NO_ERROR) { 534 BRect rect(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 535 owner->SetDrawingMode(B_OP_ALPHA); 536 owner->DrawBitmap(&bitmap, iconRect, rect); 537 owner->SetDrawingMode(B_OP_COPY); 538 } else { 539 // ToDo: find some nicer image for this :-) 540 owner->SetHighColor(150, 150, 150); 541 owner->FillEllipse(BRect(r.left + 8, r.top + 4, r.left + 16, r.top + 13)); 542 } 543 return; 544 } 545 546 BFile file(&fRef, O_RDONLY); 547 BEntry entry(&fRef); 548 BPath path; 549 if (entry.GetPath(&path) == B_OK && file.InitCheck() == B_OK) { 550 owner->DrawString(path.Path()); 551 552 BNodeInfo info(&file); 553 BRect sr(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 554 555 BBitmap bitmap(sr, B_RGBA32); 556 if (info.GetTrackerIcon(&bitmap, B_MINI_ICON) == B_NO_ERROR) { 557 BRect dr(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 558 owner->SetDrawingMode(B_OP_ALPHA); 559 owner->DrawBitmap(&bitmap, sr, dr); 560 owner->SetDrawingMode(B_OP_COPY); 561 } 562 } else 563 owner->DrawString("<missing attachment>"); 564 } 565 566