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