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 "Mail.h" 43 #include "Enclosures.h" 44 45 #include <Debug.h> 46 #include <Beep.h> 47 #include <Bitmap.h> 48 #include <MenuItem.h> 49 #include <Alert.h> 50 #include <NodeMonitor.h> 51 52 #include <MailAttachment.h> 53 #include <MailMessage.h> 54 55 #include <MDRLanguage.h> 56 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 61 62 //==================================================================== 63 64 65 static status_t 66 GetTrackerIcon(BMimeType &type, BBitmap *icon, icon_size iconSize) 67 { 68 // set some icon size related variables 69 status_t error = B_OK; 70 BRect bounds; 71 switch (iconSize) { 72 case B_MINI_ICON: 73 bounds.Set(0, 0, 15, 15); 74 break; 75 case B_LARGE_ICON: 76 bounds.Set(0, 0, 31, 31); 77 break; 78 default: 79 error = B_BAD_VALUE; 80 break; 81 } 82 // check parameters and initialization 83 if (error == B_OK 84 && (!icon || icon->InitCheck() != B_OK || icon->Bounds() != bounds)) 85 return B_BAD_VALUE; 86 87 bool success = false; 88 89 // Ask the MIME database for the preferred application for the file type 90 // and whether this application has a special icon for the type. 91 char signature[B_MIME_TYPE_LENGTH]; 92 if (type.GetPreferredApp(signature) == B_OK) { 93 BMimeType type(signature); 94 success = (type.GetIconForType(type.Type(), icon, iconSize) == B_OK); 95 } 96 97 // Ask the MIME database whether there is an icon for the node's file type. 98 if (error == B_OK && !success) 99 success = (type.GetIcon(icon, iconSize) == B_OK); 100 101 // Ask the MIME database for the super type and start all over 102 if (error == B_OK && !success) { 103 BMimeType super; 104 if (type.GetSupertype(&super) == B_OK) 105 return GetTrackerIcon(super, icon, iconSize); 106 } 107 108 // Return the icon for "application/octet-stream" from the MIME database. 109 if (error == B_OK && !success) { 110 // get the "application/octet-stream" icon 111 BMimeType type("application/octet-stream"); 112 error = type.GetIcon(icon, iconSize); 113 } 114 115 return error; 116 } 117 118 119 // #pragma mark - 120 121 122 TEnclosuresView::TEnclosuresView(BRect rect, BRect wind_rect) 123 : BView(rect, "m_enclosures", B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW), 124 fFocus(false) 125 { 126 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 127 128 BFont font = *be_plain_font; 129 font.SetSize(FONT_SIZE); 130 font_height fHeight; 131 font.GetHeight(&fHeight); 132 fOffset = 12; 133 134 BRect r; 135 r.left = ENCLOSE_TEXT_H + font.StringWidth( 136 MDR_DIALECT_CHOICE ("Enclosures: ","添付ファイル")) + 5; 137 r.top = ENCLOSE_FIELD_V; 138 r.right = wind_rect.right - wind_rect.left - B_V_SCROLL_BAR_WIDTH - 9; 139 r.bottom = Frame().Height() - 8; 140 fList = new TListView(r, this); 141 fList->SetInvocationMessage(new BMessage(LIST_INVOKED)); 142 143 BScrollView *scroll = new BScrollView("", fList, B_FOLLOW_LEFT_RIGHT | 144 B_FOLLOW_TOP, 0, false, true); 145 AddChild(scroll); 146 scroll->ScrollBar(B_VERTICAL)->SetRange(0, 0); 147 } 148 149 150 TEnclosuresView::~TEnclosuresView() 151 { 152 for (int32 index = fList->CountItems();index-- > 0;) 153 { 154 TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); 155 fList->RemoveItem(index); 156 157 if (item->Component() == NULL) 158 watch_node(item->NodeRef(), B_STOP_WATCHING, this); 159 delete item; 160 } 161 } 162 163 164 void 165 TEnclosuresView::Draw(BRect where) 166 { 167 BView::Draw(where); 168 169 BFont font = *be_plain_font; 170 font.SetSize(FONT_SIZE); 171 SetFont(&font); 172 SetHighColor(0, 0, 0); 173 SetLowColor(ViewColor()); 174 175 font_height fHeight; 176 font.GetHeight(&fHeight); 177 178 MovePenTo(ENCLOSE_TEXT_H, ENCLOSE_TEXT_V + fHeight.ascent); 179 DrawString(ENCLOSE_TEXT); 180 } 181 182 183 void 184 TEnclosuresView::MessageReceived(BMessage *msg) 185 { 186 switch (msg->what) 187 { 188 case LIST_INVOKED: 189 { 190 BListView *list; 191 msg->FindPointer("source", (void **)&list); 192 if (list) 193 { 194 TListItem *item = (TListItem *) (list->ItemAt(msg->FindInt32("index"))); 195 if (item) 196 { 197 BMessenger tracker("application/x-vnd.Be-TRAK"); 198 if (tracker.IsValid()) 199 { 200 BMessage message(B_REFS_RECEIVED); 201 message.AddRef("refs", item->Ref()); 202 203 tracker.SendMessage(&message); 204 } 205 } 206 } 207 break; 208 } 209 210 case M_REMOVE: 211 { 212 int32 index; 213 while ((index = fList->CurrentSelection()) >= 0) 214 { 215 TListItem *item = (TListItem *) fList->ItemAt(index); 216 fList->RemoveItem(index); 217 218 if (item->Component()) 219 { 220 // remove the component from the mail 221 TMailWindow *window = dynamic_cast<TMailWindow *>(Window()); 222 if (window && window->Mail()) 223 window->Mail()->RemoveComponent(item->Component()); 224 225 (new BAlert("", MDR_DIALECT_CHOICE ( 226 "Removing enclosures from a forwarded mail is not yet implemented!\n" 227 "It will not yet work correctly.", 228 "転送メールから添付ファイルを削除する機能はまだ実装されていません。"), 229 MDR_DIALECT_CHOICE ("OK","了解")))->Go(); 230 } 231 else 232 watch_node(item->NodeRef(), B_STOP_WATCHING, this); 233 delete item; 234 } 235 break; 236 } 237 238 case M_SELECT: 239 fList->Select(0, fList->CountItems() - 1, true); 240 break; 241 242 case B_SIMPLE_DATA: 243 case B_REFS_RECEIVED: 244 case REFS_RECEIVED: 245 if (msg->HasRef("refs")) 246 { 247 bool badType = false; 248 249 int32 index = 0; 250 entry_ref ref; 251 while (msg->FindRef("refs", index++, &ref) == B_NO_ERROR) 252 { 253 BFile file(&ref, O_RDONLY); 254 if (file.InitCheck() == B_OK && file.IsFile()) 255 { 256 TListItem *item; 257 for (int16 loop = 0; loop < fList->CountItems(); loop++) 258 { 259 item = (TListItem *) fList->ItemAt(loop); 260 if (ref == *(item->Ref())) 261 { 262 fList->Select(loop); 263 fList->ScrollToSelection(); 264 continue; 265 } 266 } 267 fList->AddItem(item = new TListItem(&ref)); 268 fList->Select(fList->CountItems() - 1); 269 fList->ScrollToSelection(); 270 271 watch_node(item->NodeRef(), B_WATCH_NAME, this); 272 } 273 else 274 badType = true; 275 } 276 if (badType) 277 { 278 beep(); 279 (new BAlert("", MDR_DIALECT_CHOICE ( 280 "Only files can be added as enclosures.", 281 "添付できるのは、ファイルのみです。"), 282 MDR_DIALECT_CHOICE ("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 enclosure> 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); 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( 427 MDR_DIALECT_CHOICE ("Open Enclosure","添付ファイルを開く"), 428 new BMessage(LIST_INVOKED))); 429 menu.AddItem(new BMenuItem( 430 MDR_DIALECT_CHOICE ("Remove Enclosure","添付ファイルを削除"), 431 new BMessage(M_REMOVE))); 432 433 BPoint menuStart = ConvertToScreen(point); 434 435 BMenuItem *item; 436 if ((item = menu.Go(menuStart)) != NULL) 437 { 438 if (item->Command() == LIST_INVOKED) 439 { 440 BMessage msg(LIST_INVOKED); 441 msg.AddPointer("source",this); 442 msg.AddInt32("index",IndexOf(point)); 443 Window()->PostMessage(&msg,fParent); 444 } 445 else 446 { 447 Select(IndexOf(point)); 448 Window()->PostMessage(item->Command(),fParent); 449 } 450 } 451 } 452 else 453 BListView::MouseDown(point); 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 //==================================================================== 468 // #pragma mark - 469 470 471 TListItem::TListItem(entry_ref *ref) 472 { 473 fComponent = NULL; 474 fRef = *ref; 475 476 BEntry entry(ref); 477 entry.GetNodeRef(&fNodeRef); 478 } 479 480 481 TListItem::TListItem(BMailComponent *component) 482 : 483 fComponent(component) 484 { 485 } 486 487 488 void 489 TListItem::Update(BView *owner, const BFont *font) 490 { 491 BListItem::Update(owner, font); 492 493 if (Height() < 17) // mini icon height + 1 494 SetHeight(17); 495 } 496 497 498 void 499 TListItem::DrawItem(BView *owner, BRect r, bool /* complete */) 500 { 501 if (IsSelected()) { 502 owner->SetHighColor(180, 180, 180); 503 owner->SetLowColor(180, 180, 180); 504 } else { 505 owner->SetHighColor(255, 255, 255); 506 owner->SetLowColor(255, 255, 255); 507 } 508 owner->FillRect(r); 509 owner->SetHighColor(0, 0, 0); 510 511 BFont font = *be_plain_font; 512 font.SetSize(FONT_SIZE); 513 owner->SetFont(&font); 514 owner->MovePenTo(r.left + 24, r.bottom - 4); 515 516 if (fComponent) { 517 // if it's already a mail component, we don't have an icon to 518 // draw, and the entry_ref is invalid 519 BMailAttachment *attachment = dynamic_cast<BMailAttachment *>(fComponent); 520 521 char name[B_FILE_NAME_LENGTH * 2]; 522 if ((attachment == NULL) || (attachment->FileName(name) < B_OK)) 523 strcpy(name, "unnamed"); 524 525 BMimeType type; 526 if (fComponent->MIMEType(&type) == B_OK) 527 sprintf(name + strlen(name), ", Type: %s", type.Type()); 528 529 owner->DrawString(name); 530 531 BRect iconRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 532 533 BBitmap bitmap(iconRect, B_COLOR_8_BIT); 534 if (GetTrackerIcon(type, &bitmap, B_MINI_ICON) == B_NO_ERROR) { 535 BRect rect(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 536 owner->SetDrawingMode(B_OP_OVER); 537 owner->DrawBitmap(&bitmap, iconRect, rect); 538 owner->SetDrawingMode(B_OP_COPY); 539 } else { 540 // ToDo: find some nicer image for this :-) 541 owner->SetHighColor(150, 150, 150); 542 owner->FillEllipse(BRect(r.left + 8, r.top + 4, r.left + 16, r.top + 13)); 543 } 544 return; 545 } 546 547 BFile file(&fRef, O_RDONLY); 548 BEntry entry(&fRef); 549 BPath path; 550 if (entry.GetPath(&path) == B_OK && file.InitCheck() == B_OK) { 551 owner->DrawString(path.Path()); 552 553 BNodeInfo info(&file); 554 BRect sr(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 555 556 BBitmap bitmap(sr, B_COLOR_8_BIT); 557 if (info.GetTrackerIcon(&bitmap, B_MINI_ICON) == B_NO_ERROR) { 558 BRect dr(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); 559 owner->SetDrawingMode(B_OP_OVER); 560 owner->DrawBitmap(&bitmap, sr, dr); 561 owner->SetDrawingMode(B_OP_COPY); 562 } 563 } else 564 owner->DrawString("<missing enclosure>"); 565 } 566 567