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 CONNECTION 23 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 registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 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 and handling the messages (TEnclosuresView). 40 //-------------------------------------------------------------------- 41 42 #include "Enclosures.h" 43 44 #include <Alert.h> 45 #include <Beep.h> 46 #include <Bitmap.h> 47 #include <Debug.h> 48 #include <Locale.h> 49 #include <MenuItem.h> 50 #include <NodeMonitor.h> 51 #include <PopUpMenu.h> 52 53 #include <MailAttachment.h> 54 #include <MailMessage.h> 55 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 60 #include "MailApp.h" 61 #include "MailSupport.h" 62 #include "MailWindow.h" 63 #include "Messages.h" 64 65 66 #define TR_CONTEXT "Mail" 67 68 69 static const float kPlainFontSizeScale = 0.9; 70 71 static status_t 72 GetTrackerIcon(BMimeType &type, BBitmap *icon, icon_size iconSize) 73 { 74 // set some icon size related variables 75 status_t error = B_OK; 76 BRect bounds; 77 switch (iconSize) { 78 case B_MINI_ICON: 79 bounds.Set(0, 0, 15, 15); 80 break; 81 case B_LARGE_ICON: 82 bounds.Set(0, 0, 31, 31); 83 break; 84 default: 85 error = B_BAD_VALUE; 86 break; 87 } 88 // check parameters and initialization 89 if (error == B_OK 90 && (!icon || icon->InitCheck() != B_OK || icon->Bounds() != bounds)) 91 return B_BAD_VALUE; 92 93 bool success = false; 94 95 // Ask the MIME database for the preferred application for the file type 96 // and whether this application has a special icon for the type. 97 char signature[B_MIME_TYPE_LENGTH]; 98 if (type.GetPreferredApp(signature) == B_OK) { 99 BMimeType type(signature); 100 success = (type.GetIconForType(type.Type(), icon, iconSize) == B_OK); 101 } 102 103 // Ask the MIME database whether there is an icon for the node's file type. 104 if (error == B_OK && !success) 105 success = (type.GetIcon(icon, iconSize) == B_OK); 106 107 // Ask the MIME database for the super type and start all over 108 if (error == B_OK && !success) { 109 BMimeType super; 110 if (type.GetSupertype(&super) == B_OK) 111 return GetTrackerIcon(super, icon, iconSize); 112 } 113 114 // Return the icon for "application/octet-stream" from the MIME database. 115 if (error == B_OK && !success) { 116 // get the "application/octet-stream" icon 117 BMimeType type("application/octet-stream"); 118 error = type.GetIcon(icon, iconSize); 119 } 120 121 return error; 122 } 123 124 125 // #pragma mark - 126 127 128 TEnclosuresView::TEnclosuresView(BRect rect, BRect wind_rect) 129 : BView(rect, "m_enclosures", B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW), 130 fFocus(false) 131 { 132 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 133 134 BFont font(be_plain_font); 135 font.SetSize(font.Size() * kPlainFontSizeScale); 136 SetFont(&font); 137 138 fOffset = 12; 139 140 BRect r; 141 r.left = ENCLOSE_TEXT_H + font.StringWidth( 142 TR("Attachments: ")) + 5; 143 r.top = ENCLOSE_FIELD_V; 144 r.right = wind_rect.right - wind_rect.left - B_V_SCROLL_BAR_WIDTH - 9; 145 r.bottom = Frame().Height() - 8; 146 fList = new TListView(r, this); 147 fList->SetInvocationMessage(new BMessage(LIST_INVOKED)); 148 149 BScrollView *scroll = new BScrollView("", fList, B_FOLLOW_LEFT_RIGHT | 150 B_FOLLOW_TOP, 0, false, true); 151 AddChild(scroll); 152 scroll->ScrollBar(B_VERTICAL)->SetRange(0, 0); 153 } 154 155 156 TEnclosuresView::~TEnclosuresView() 157 { 158 for (int32 index = fList->CountItems();index-- > 0;) 159 { 160 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); 161 fList->RemoveItem(index); 162 163 if (item->Component() == NULL) 164 watch_node(item->NodeRef(), B_STOP_WATCHING, this); 165 delete item; 166 } 167 } 168 169 170 void 171 TEnclosuresView::Draw(BRect where) 172 { 173 BView::Draw(where); 174 175 SetHighColor(0, 0, 0); 176 SetLowColor(ViewColor()); 177 178 font_height fh; 179 GetFontHeight(&fh); 180 181 MovePenTo(ENCLOSE_TEXT_H, ENCLOSE_TEXT_V + fh.ascent); 182 DrawString(ENCLOSE_TEXT); 183 } 184 185 186 void 187 TEnclosuresView::MessageReceived(BMessage *msg) 188 { 189 switch (msg->what) 190 { 191 case LIST_INVOKED: 192 { 193 BListView *list; 194 msg->FindPointer("source", (void **)&list); 195 if (list) 196 { 197 TListItem *item = (TListItem *) (list->ItemAt(msg->FindInt32("index"))); 198 if (item) 199 { 200 BMessenger tracker("application/x-vnd.Be-TRAK"); 201 if (tracker.IsValid()) 202 { 203 BMessage message(B_REFS_RECEIVED); 204 message.AddRef("refs", item->Ref()); 205 206 tracker.SendMessage(&message); 207 } 208 } 209 } 210 break; 211 } 212 213 case M_REMOVE: 214 { 215 int32 index; 216 while ((index = fList->CurrentSelection()) >= 0) 217 { 218 TListItem *item = (TListItem *) fList->ItemAt(index); 219 fList->RemoveItem(index); 220 221 if (item->Component()) 222 { 223 TMailWindow *window = dynamic_cast<TMailWindow *>(Window()); 224 if (window && window->Mail()) 225 window->Mail()->RemoveComponent(item->Component()); 226 227 (new BAlert("", TR( 228 "Removing attachments from a forwarded mail is not yet " 229 "implemented!\nIt will not yet work correctly."), 230 TR("OK")))->Go(); 231 } 232 else 233 watch_node(item->NodeRef(), B_STOP_WATCHING, this); 234 delete item; 235 } 236 break; 237 } 238 239 case M_SELECT: 240 fList->Select(0, fList->CountItems() - 1, true); 241 break; 242 243 case B_SIMPLE_DATA: 244 case B_REFS_RECEIVED: 245 case REFS_RECEIVED: 246 if (msg->HasRef("refs")) 247 { 248 bool badType = false; 249 250 int32 index = 0; 251 entry_ref ref; 252 while (msg->FindRef("refs", index++, &ref) == B_NO_ERROR) 253 { 254 BFile file(&ref, O_RDONLY); 255 if (file.InitCheck() == B_OK && file.IsFile()) 256 { 257 TListItem *item; 258 for (int16 loop = 0; loop < fList->CountItems(); loop++) 259 { 260 item = (TListItem *) fList->ItemAt(loop); 261 if (ref == *(item->Ref())) 262 { 263 fList->Select(loop); 264 fList->ScrollToSelection(); 265 continue; 266 } 267 } 268 fList->AddItem(item = new TListItem(&ref)); 269 fList->Select(fList->CountItems() - 1); 270 fList->ScrollToSelection(); 271 272 watch_node(item->NodeRef(), B_WATCH_NAME, this); 273 } 274 else 275 badType = true; 276 } 277 if (badType) 278 { 279 beep(); 280 (new BAlert("", 281 TR("Only files can be added as attachments."), 282 TR("OK")))->Go(); 283 } 284 } 285 break; 286 287 case B_NODE_MONITOR: 288 { 289 int32 opcode; 290 if (msg->FindInt32("opcode", &opcode) == B_NO_ERROR) 291 { 292 dev_t device; 293 if (msg->FindInt32("device", &device) < B_OK) 294 break; 295 ino_t inode; 296 if (msg->FindInt64("node", &inode) < B_OK) 297 break; 298 299 for (int32 index = fList->CountItems();index-- > 0;) 300 { 301 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); 302 303 if (device == item->NodeRef()->device 304 && inode == item->NodeRef()->node) 305 { 306 if (opcode == B_ENTRY_REMOVED) 307 { 308 // don't hide the <missing attachment> item 309 310 //fList->RemoveItem(index); 311 // 312 //watch_node(item->NodeRef(), B_STOP_WATCHING, this); 313 //delete item; 314 } 315 else if (opcode == B_ENTRY_MOVED) 316 { 317 item->Ref()->device = device; 318 msg->FindInt64("to directory", &item->Ref()->directory); 319 320 const char *name; 321 msg->FindString("name", &name); 322 item->Ref()->set_name(name); 323 } 324 325 fList->InvalidateItem(index); 326 break; 327 } 328 } 329 } 330 break; 331 } 332 333 default: 334 BView::MessageReceived(msg); 335 } 336 } 337 338 339 void 340 TEnclosuresView::Focus(bool focus) 341 { 342 if (fFocus != focus) 343 { 344 fFocus = focus; 345 Draw(Frame()); 346 } 347 } 348 349 350 static void recursive_attachment_search(TEnclosuresView *us,BMailContainer *mail,BMailComponent *body) { 351 if (mail == NULL) 352 return; 353 for (int32 i = 0; i < mail->CountComponents(); i++) 354 { 355 BMailComponent *component = mail->GetComponent(i); 356 if (component == body) 357 continue; 358 359 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) 360 recursive_attachment_search(us,dynamic_cast<BMIMEMultipartMailContainer *>(component),body); 361 362 us->fList->AddItem(new TListItem(component)); 363 } 364 } 365 366 void 367 TEnclosuresView::AddEnclosuresFromMail(BEmailMessage *mail) 368 { 369 for (int32 i = 0; i < mail->CountComponents(); i++) 370 { 371 BMailComponent *component = mail->GetComponent(i); 372 if (component == mail->Body()) 373 continue; 374 375 if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) 376 recursive_attachment_search(this,dynamic_cast<BMIMEMultipartMailContainer *>(component),mail->Body()); 377 378 fList->AddItem(new TListItem(component)); 379 } 380 } 381 382 383 //==================================================================== 384 // #pragma mark - 385 386 387 TListView::TListView(BRect rect, TEnclosuresView *view) 388 : BListView(rect, "", B_MULTIPLE_SELECTION_LIST, B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT), 389 fParent(view) 390 { 391 } 392 393 394 void 395 TListView::AttachedToWindow() 396 { 397 BListView::AttachedToWindow(); 398 399 BFont font(be_plain_font); 400 font.SetSize(font.Size() * kPlainFontSizeScale); 401 SetFont(&font); 402 } 403 404 405 void 406 TListView::MakeFocus(bool focus) 407 { 408 BListView::MakeFocus(focus); 409 fParent->Focus(focus); 410 } 411 412 413 void 414 TListView::MouseDown(BPoint point) 415 { 416 int32 buttons; 417 Looper()->CurrentMessage()->FindInt32("buttons",&buttons); 418 419 if (buttons & B_SECONDARY_MOUSE_BUTTON) 420 { 421 BFont font = *be_plain_font; 422 font.SetSize(10); 423 424 BPopUpMenu menu("enclosure", false, false); 425 menu.SetFont(&font); 426 menu.AddItem(new BMenuItem(TR("Open attachment"), 427 new BMessage(LIST_INVOKED))); 428 menu.AddItem(new BMenuItem(TR("Remove attachment"), 429 new BMessage(M_REMOVE))); 430 431 BPoint menuStart = ConvertToScreen(point); 432 433 BMenuItem *item; 434 if ((item = menu.Go(menuStart)) != NULL) 435 { 436 if (item->Command() == LIST_INVOKED) 437 { 438 BMessage msg(LIST_INVOKED); 439 msg.AddPointer("source",this); 440 msg.AddInt32("index",IndexOf(point)); 441 Window()->PostMessage(&msg,fParent); 442 } 443 else 444 { 445 Select(IndexOf(point)); 446 Window()->PostMessage(item->Command(),fParent); 447 } 448 } 449 } 450 else 451 BListView::MouseDown(point); 452 } 453 454 455 void 456 TListView::KeyDown(const char *bytes, int32 numBytes) 457 { 458 BListView::KeyDown(bytes,numBytes); 459 460 if (numBytes == 1 && *bytes == B_DELETE) 461 Window()->PostMessage(M_REMOVE, fParent); 462 } 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_COLOR_8_BIT); 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_OVER); 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_COLOR_8_BIT); 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_OVER); 558 owner->DrawBitmap(&bitmap, sr, dr); 559 owner->SetDrawingMode(B_OP_COPY); 560 } 561 } else 562 owner->DrawString("<missing attachment>"); 563 } 564 565