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 // #pragma mark - 127 128 129 TEnclosuresView::TEnclosuresView(BRect rect, BRect wind_rect) 130 : BView(rect, "m_enclosures", B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW), 131 fFocus(false) 132 { 133 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 134 135 BFont font(be_plain_font); 136 font.SetSize(font.Size() * kPlainFontSizeScale); 137 SetFont(&font); 138 139 fOffset = 12; 140 141 BRect r; 142 r.left = ENCLOSE_TEXT_H + font.StringWidth( 143 B_TRANSLATE("Attachments: ")) + 5; 144 r.top = ENCLOSE_FIELD_V; 145 r.right = wind_rect.right - wind_rect.left - B_V_SCROLL_BAR_WIDTH - 9; 146 r.bottom = Frame().Height() - 8; 147 fList = new TListView(r, this); 148 fList->SetInvocationMessage(new BMessage(LIST_INVOKED)); 149 150 BScrollView *scroll = new BScrollView("", fList, B_FOLLOW_LEFT_RIGHT | 151 B_FOLLOW_TOP, 0, false, true); 152 AddChild(scroll); 153 scroll->ScrollBar(B_VERTICAL)->SetRange(0, 0); 154 } 155 156 157 TEnclosuresView::~TEnclosuresView() 158 { 159 for (int32 index = fList->CountItems();index-- > 0;) 160 { 161 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); 162 fList->RemoveItem(index); 163 164 if (item->Component() == NULL) 165 watch_node(item->NodeRef(), B_STOP_WATCHING, this); 166 delete item; 167 } 168 } 169 170 171 void 172 TEnclosuresView::Draw(BRect where) 173 { 174 BView::Draw(where); 175 176 SetHighColor(0, 0, 0); 177 SetLowColor(ViewColor()); 178 179 font_height fh; 180 GetFontHeight(&fh); 181 182 MovePenTo(ENCLOSE_TEXT_H, ENCLOSE_TEXT_V + fh.ascent); 183 DrawString(ENCLOSE_TEXT); 184 } 185 186 187 void 188 TEnclosuresView::MessageReceived(BMessage *msg) 189 { 190 switch (msg->what) 191 { 192 case LIST_INVOKED: 193 { 194 BListView *list; 195 msg->FindPointer("source", (void **)&list); 196 if (list) 197 { 198 TListItem *item = (TListItem *) (list->ItemAt(msg->FindInt32("index"))); 199 if (item) 200 { 201 BMessenger tracker("application/x-vnd.Be-TRAK"); 202 if (tracker.IsValid()) 203 { 204 BMessage message(B_REFS_RECEIVED); 205 message.AddRef("refs", item->Ref()); 206 207 tracker.SendMessage(&message); 208 } 209 } 210 } 211 break; 212 } 213 214 case M_REMOVE: 215 { 216 int32 index; 217 while ((index = fList->CurrentSelection()) >= 0) 218 { 219 TListItem *item = (TListItem *) fList->ItemAt(index); 220 fList->RemoveItem(index); 221 222 if (item->Component()) 223 { 224 TMailWindow *window = dynamic_cast<TMailWindow *>(Window()); 225 if (window && window->Mail()) 226 window->Mail()->RemoveComponent(item->Component()); 227 228 (new BAlert("", B_TRANSLATE( 229 "Removing attachments from a forwarded mail is not yet " 230 "implemented!\nIt will not yet work correctly."), 231 B_TRANSLATE("OK")))->Go(); 232 } 233 else 234 watch_node(item->NodeRef(), B_STOP_WATCHING, this); 235 delete item; 236 } 237 break; 238 } 239 240 case M_SELECT: 241 fList->Select(0, fList->CountItems() - 1, true); 242 break; 243 244 case B_SIMPLE_DATA: 245 case B_REFS_RECEIVED: 246 case REFS_RECEIVED: 247 if (msg->HasRef("refs")) 248 { 249 bool badType = false; 250 251 int32 index = 0; 252 entry_ref ref; 253 while (msg->FindRef("refs", index++, &ref) == B_NO_ERROR) 254 { 255 BFile file(&ref, O_RDONLY); 256 if (file.InitCheck() == B_OK && file.IsFile()) 257 { 258 TListItem *item; 259 for (int16 loop = 0; loop < fList->CountItems(); loop++) 260 { 261 item = (TListItem *) fList->ItemAt(loop); 262 if (ref == *(item->Ref())) 263 { 264 fList->Select(loop); 265 fList->ScrollToSelection(); 266 continue; 267 } 268 } 269 fList->AddItem(item = new TListItem(&ref)); 270 fList->Select(fList->CountItems() - 1); 271 fList->ScrollToSelection(); 272 273 watch_node(item->NodeRef(), B_WATCH_NAME, this); 274 } 275 else 276 badType = true; 277 } 278 if (badType) 279 { 280 beep(); 281 (new BAlert("", 282 B_TRANSLATE("Only files can be added as attachments."), 283 B_TRANSLATE("OK")))->Go(); 284 } 285 } 286 break; 287 288 case B_NODE_MONITOR: 289 { 290 int32 opcode; 291 if (msg->FindInt32("opcode", &opcode) == B_NO_ERROR) 292 { 293 dev_t device; 294 if (msg->FindInt32("device", &device) < B_OK) 295 break; 296 ino_t inode; 297 if (msg->FindInt64("node", &inode) < B_OK) 298 break; 299 300 for (int32 index = fList->CountItems();index-- > 0;) 301 { 302 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); 303 304 if (device == item->NodeRef()->device 305 && inode == item->NodeRef()->node) 306 { 307 if (opcode == B_ENTRY_REMOVED) 308 { 309 // don't hide the <missing attachment> item 310 311 //fList->RemoveItem(index); 312 // 313 //watch_node(item->NodeRef(), B_STOP_WATCHING, this); 314 //delete item; 315 } 316 else if (opcode == B_ENTRY_MOVED) 317 { 318 item->Ref()->device = device; 319 msg->FindInt64("to directory", &item->Ref()->directory); 320 321 const char *name; 322 msg->FindString("name", &name); 323 item->Ref()->set_name(name); 324 } 325 326 fList->InvalidateItem(index); 327 break; 328 } 329 } 330 } 331 break; 332 } 333 334 default: 335 BView::MessageReceived(msg); 336 } 337 } 338 339 340 void 341 TEnclosuresView::Focus(bool focus) 342 { 343 if (fFocus != focus) 344 { 345 fFocus = focus; 346 Draw(Frame()); 347 } 348 } 349 350 351 static void recursive_attachment_search(TEnclosuresView *us,BMailContainer *mail,BMailComponent *body) { 352 if (mail == NULL) 353 return; 354 for (int32 i = 0; i < mail->CountComponents(); i++) 355 { 356 BMailComponent *component = mail->GetComponent(i); 357 if (component == body) 358 continue; 359 360 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) 361 recursive_attachment_search(us,dynamic_cast<BMIMEMultipartMailContainer *>(component),body); 362 363 us->fList->AddItem(new TListItem(component)); 364 } 365 } 366 367 void 368 TEnclosuresView::AddEnclosuresFromMail(BEmailMessage *mail) 369 { 370 for (int32 i = 0; i < mail->CountComponents(); i++) 371 { 372 BMailComponent *component = mail->GetComponent(i); 373 if (component == mail->Body()) 374 continue; 375 376 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) 377 recursive_attachment_search(this,dynamic_cast<BMIMEMultipartMailContainer *>(component),mail->Body()); 378 379 fList->AddItem(new TListItem(component)); 380 } 381 } 382 383 384 //==================================================================== 385 // #pragma mark - 386 387 388 TListView::TListView(BRect rect, TEnclosuresView *view) 389 : BListView(rect, "", B_MULTIPLE_SELECTION_LIST, B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT), 390 fParent(view) 391 { 392 } 393 394 395 void 396 TListView::AttachedToWindow() 397 { 398 BListView::AttachedToWindow(); 399 400 BFont font(be_plain_font); 401 font.SetSize(font.Size() * kPlainFontSizeScale); 402 SetFont(&font); 403 } 404 405 406 void 407 TListView::MakeFocus(bool focus) 408 { 409 BListView::MakeFocus(focus); 410 fParent->Focus(focus); 411 } 412 413 414 void 415 TListView::MouseDown(BPoint point) 416 { 417 int32 buttons; 418 Looper()->CurrentMessage()->FindInt32("buttons",&buttons); 419 420 if (buttons & B_SECONDARY_MOUSE_BUTTON) 421 { 422 BFont font = *be_plain_font; 423 font.SetSize(10); 424 425 BPopUpMenu menu("enclosure", false, false); 426 menu.SetFont(&font); 427 menu.AddItem(new BMenuItem(B_TRANSLATE("Open attachment"), 428 new BMessage(LIST_INVOKED))); 429 menu.AddItem(new BMenuItem(B_TRANSLATE("Remove attachment"), 430 new BMessage(M_REMOVE))); 431 432 BPoint menuStart = ConvertToScreen(point); 433 434 BMenuItem *item; 435 if ((item = menu.Go(menuStart)) != NULL) 436 { 437 if (item->Command() == LIST_INVOKED) 438 { 439 BMessage msg(LIST_INVOKED); 440 msg.AddPointer("source",this); 441 msg.AddInt32("index",IndexOf(point)); 442 Window()->PostMessage(&msg,fParent); 443 } 444 else 445 { 446 Select(IndexOf(point)); 447 Window()->PostMessage(item->Command(),fParent); 448 } 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 //==================================================================== 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_COLOR_8_BIT); 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_OVER); 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_COLOR_8_BIT); 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_OVER); 559 owner->DrawBitmap(&bitmap, sr, dr); 560 owner->SetDrawingMode(B_OP_COPY); 561 } 562 } else 563 owner->DrawString("<missing attachment>"); 564 } 565 566