1 /* DeskbarView - mail_daemon's deskbar menu and view 2 * 3 * Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved. 4 */ 5 6 7 #include "DeskbarView.h" 8 9 #include <stdio.h> 10 #include <malloc.h> 11 12 #include <Bitmap.h> 13 #include <Deskbar.h> 14 #include <Directory.h> 15 #include <Entry.h> 16 #include <FindDirectory.h> 17 #include <IconUtils.h> 18 #include <kernel/fs_info.h> 19 #include <kernel/fs_index.h> 20 #include <MenuItem.h> 21 #include <Messenger.h> 22 #include <NodeInfo.h> 23 #include <NodeMonitor.h> 24 #include <OpenWithTracker.h> 25 #include <Path.h> 26 #include <PopUpMenu.h> 27 #include <Query.h> 28 #include <Rect.h> 29 #include <Resources.h> 30 #include <Roster.h> 31 #include <String.h> 32 #include <SymLink.h> 33 #include <VolumeRoster.h> 34 #include <Window.h> 35 36 #include <E-mail.h> 37 #include <MailDaemon.h> 38 #include <MailSettings.h> 39 #include <MDRLanguage.h> 40 41 #include "DeskbarViewIcons.h" 42 43 const char* kTrackerSignature = "application/x-vnd.Be-TRAK"; 44 45 46 //-----The following #defines get around a bug in get_image_info on ppc--- 47 #if __INTEL__ 48 #define text_part text 49 #define text_part_size text_size 50 #else 51 #define text_part data 52 #define text_part_size data_size 53 #endif 54 55 extern "C" _EXPORT BView* instantiate_deskbar_item(); 56 57 58 status_t our_image(image_info* image) 59 { 60 int32 cookie = 0; 61 status_t ret; 62 while ((ret = get_next_image_info(0,&cookie,image)) == B_OK) 63 { 64 if ((char*)our_image >= (char*)image->text_part && 65 (char*)our_image <= (char*)image->text_part + image->text_part_size) 66 break; 67 } 68 69 return ret; 70 } 71 72 BView* instantiate_deskbar_item(void) 73 { 74 return new DeskbarView(BRect(0, 0, 15, 15)); 75 } 76 77 78 //------------------------------------------------------------------------------- 79 // #pragma mark - 80 81 82 DeskbarView::DeskbarView(BRect frame) 83 : 84 BView(frame, "mail_daemon", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED), 85 fStatus(kStatusNoMail) 86 { 87 _InitBitmaps(); 88 } 89 90 91 DeskbarView::DeskbarView(BMessage *message) 92 : 93 BView(message), 94 fStatus(kStatusNoMail) 95 { 96 _InitBitmaps(); 97 } 98 99 100 DeskbarView::~DeskbarView() 101 { 102 for (int i = 0; i < kStatusCount; i++) 103 delete fBitmaps[i]; 104 105 for (int32 i = 0; i < fNewMailQueries.CountItems(); i++) 106 delete ((BQuery *)(fNewMailQueries.ItemAt(i))); 107 } 108 109 110 void DeskbarView::AttachedToWindow() 111 { 112 BView::AttachedToWindow(); 113 if (Parent()) 114 SetViewColor(Parent()->ViewColor()); 115 else 116 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 117 118 SetLowColor(ViewColor()); 119 120 if (be_roster->IsRunning("application/x-vnd.Be-POST")) { 121 _RefreshMailQuery(); 122 } else { 123 BDeskbar deskbar; 124 deskbar.RemoveItem("mail_daemon"); 125 } 126 } 127 128 129 bool DeskbarView::_EntryInTrash(const entry_ref* ref) 130 { 131 BEntry entry(ref); 132 BVolume volume(ref->device); 133 BPath path; 134 if (volume.InitCheck() != B_OK 135 || find_directory(B_TRASH_DIRECTORY, &path, false, &volume) != B_OK) 136 return false; 137 138 BDirectory trash(path.Path()); 139 return trash.Contains(&entry); 140 } 141 142 143 void DeskbarView::_RefreshMailQuery() 144 { 145 for (int32 i = 0; i < fNewMailQueries.CountItems(); i++) 146 delete ((BQuery *)(fNewMailQueries.ItemAt(i))); 147 fNewMailQueries.MakeEmpty(); 148 149 BVolumeRoster volumes; 150 BVolume volume; 151 fNewMessages = 0; 152 153 while (volumes.GetNextVolume(&volume) == B_OK) { 154 BQuery *newMailQuery = new BQuery; 155 newMailQuery->SetTarget(this); 156 newMailQuery->SetVolume(&volume); 157 newMailQuery->PushAttr(B_MAIL_ATTR_READ); 158 newMailQuery->PushInt32(B_UNREAD); 159 newMailQuery->PushOp(B_EQ); 160 newMailQuery->PushAttr("BEOS:TYPE"); 161 newMailQuery->PushString("text/x-email"); 162 newMailQuery->PushOp(B_EQ); 163 newMailQuery->PushAttr("BEOS:TYPE"); 164 newMailQuery->PushString("text/x-partial-email"); 165 newMailQuery->PushOp(B_EQ); 166 newMailQuery->PushOp(B_OR); 167 newMailQuery->PushOp(B_AND); 168 newMailQuery->Fetch(); 169 170 BEntry entry; 171 while (newMailQuery->GetNextEntry(&entry) == B_OK) { 172 if (entry.InitCheck() == B_OK) { 173 entry_ref ref; 174 entry.GetRef(&ref); 175 if (!_EntryInTrash(&ref)) 176 fNewMessages++; 177 } 178 } 179 180 fNewMailQueries.AddItem(newMailQuery); 181 } 182 183 fStatus = (fNewMessages > 0) ? kStatusNewMail : kStatusNoMail; 184 Invalidate(); 185 } 186 187 188 DeskbarView* DeskbarView::Instantiate(BMessage *data) 189 { 190 if (!validate_instantiation(data, "DeskbarView")) 191 return NULL; 192 193 return new DeskbarView(data); 194 } 195 196 197 status_t DeskbarView::Archive(BMessage *data,bool deep) const 198 { 199 BView::Archive(data, deep); 200 201 data->AddString("add_on", "application/x-vnd.Be-POST"); 202 return B_NO_ERROR; 203 } 204 205 206 void DeskbarView::Draw(BRect /*updateRect*/) 207 { 208 if (fBitmaps[fStatus] == NULL) 209 return; 210 211 SetDrawingMode(B_OP_ALPHA); 212 DrawBitmap(fBitmaps[fStatus]); 213 SetDrawingMode(B_OP_COPY); 214 } 215 216 217 void 218 DeskbarView::MessageReceived(BMessage* message) 219 { 220 switch(message->what) 221 { 222 case MD_CHECK_SEND_NOW: 223 // also happens in DeskbarView::MouseUp() with 224 // B_TERTIARY_MOUSE_BUTTON pressed 225 BMailDaemon::CheckAndSendQueuedMail(); 226 break; 227 case MD_CHECK_FOR_MAILS: 228 BMailDaemon::CheckMail(message->FindInt32("account")); 229 break; 230 case MD_SEND_MAILS: 231 BMailDaemon::SendQueuedMail(); 232 break; 233 234 case MD_OPEN_NEW: 235 { 236 char* argv[] = {(char *)"New Message", (char *)"mailto:"}; 237 be_roster->Launch("text/x-email", 2, argv); 238 break; 239 } 240 case MD_OPEN_PREFS: 241 be_roster->Launch("application/x-vnd.Haiku-Mail"); 242 break; 243 244 case MD_REFRESH_QUERY: 245 _RefreshMailQuery(); 246 break; 247 248 case B_QUERY_UPDATE: 249 { 250 int32 what; 251 dev_t device; 252 ino_t directory; 253 const char *name; 254 entry_ref ref; 255 message->FindInt32("opcode", &what); 256 message->FindInt32("device", &device); 257 message->FindInt64("directory", &directory); 258 switch (what) { 259 case B_ENTRY_CREATED: 260 if (message->FindString("name", &name) == B_OK) { 261 ref.device = device; 262 ref.directory = directory; 263 ref.set_name(name); 264 if (!_EntryInTrash(&ref)) 265 fNewMessages++; 266 } 267 break; 268 case B_ENTRY_REMOVED: 269 node_ref node; 270 node.device = device; 271 node.node = directory; 272 BDirectory dir(&node); 273 BEntry entry(&dir, NULL); 274 entry.GetRef(&ref); 275 if (!_EntryInTrash(&ref)) 276 fNewMessages--; 277 break; 278 } 279 fStatus = (fNewMessages > 0) ? kStatusNewMail : kStatusNoMail; 280 Invalidate(); 281 break; 282 } 283 case B_QUIT_REQUESTED: 284 BMailDaemon::Quit(); 285 break; 286 287 // open received files in the standard mail application 288 case B_REFS_RECEIVED: 289 { 290 BMessage argv(B_ARGV_RECEIVED); 291 argv.AddString("argv", "E-mail"); 292 293 entry_ref ref; 294 BPath path; 295 int i = 0; 296 297 while (message->FindRef("refs", i++, &ref) == B_OK 298 && path.SetTo(&ref) == B_OK) { 299 //fprintf(stderr,"got %s\n", path.Path()); 300 argv.AddString("argv", path.Path()); 301 } 302 303 if (i > 1) { 304 argv.AddInt32("argc", i); 305 be_roster->Launch("text/x-email", &argv); 306 } 307 break; 308 } 309 default: 310 BView::MessageReceived(message); 311 } 312 } 313 314 315 void 316 DeskbarView::_InitBitmaps() 317 { 318 for (int i = 0; i < kStatusCount; i++) 319 fBitmaps[i] = NULL; 320 321 image_info info; 322 if (our_image(&info) != B_OK) 323 return; 324 325 BFile file(info.name, B_READ_ONLY); 326 if (file.InitCheck() != B_OK) 327 return; 328 329 BResources resources(&file); 330 if (resources.InitCheck() != B_OK) 331 return; 332 333 for (int i = 0; i < kStatusCount; i++) { 334 const void* data = NULL; 335 size_t size; 336 data = resources.LoadResource(B_VECTOR_ICON_TYPE, 337 kIconNoMail + i, &size); 338 if (data != NULL) { 339 BBitmap* icon = new BBitmap(Bounds(), B_RGBA32); 340 if (icon->InitCheck() == B_OK 341 && BIconUtils::GetVectorIcon((const uint8 *)data, 342 size, icon) == B_OK) { 343 fBitmaps[i] = icon; 344 } else 345 delete icon; 346 } 347 } 348 } 349 350 351 void 352 DeskbarView::Pulse() 353 { 354 // TODO: Check if mail_daemon is still running 355 } 356 357 358 void 359 DeskbarView::MouseUp(BPoint pos) 360 { 361 if (fLastButtons & B_PRIMARY_MOUSE_BUTTON) { 362 if (OpenWithTracker(B_USER_SETTINGS_DIRECTORY, "Mail/mailbox") 363 != B_OK) { 364 entry_ref ref; 365 _GetNewQueryRef(ref); 366 367 BMessenger trackerMessenger(kTrackerSignature); 368 BMessage message(B_REFS_RECEIVED); 369 message.AddRef("refs", &ref); 370 371 trackerMessenger.SendMessage(&message); 372 } 373 } 374 375 if (fLastButtons & B_TERTIARY_MOUSE_BUTTON) 376 BMailDaemon::CheckMail(); 377 } 378 379 380 void 381 DeskbarView::MouseDown(BPoint pos) 382 { 383 Looper()->CurrentMessage()->FindInt32("buttons",&fLastButtons); 384 Looper()->CurrentMessage()->PrintToStream(); 385 386 if (fLastButtons & B_SECONDARY_MOUSE_BUTTON) { 387 ConvertToScreen(&pos); 388 389 BPopUpMenu* menu = _BuildMenu(); 390 if (menu) { 391 menu->Go(pos, true, true, BRect(pos.x - 2, pos.y - 2, 392 pos.x + 2, pos.y + 2), true); 393 } 394 } 395 } 396 397 398 bool 399 DeskbarView::_CreateMenuLinks(BDirectory& directory, BPath& path) 400 { 401 status_t status = directory.SetTo(path.Path()); 402 if (status == B_OK) 403 return true; 404 405 // Check if the directory has to be created (and do it in this case, 406 // filling it with some standard links). Normally the installer will 407 // create the directory and fill it with links, so normally this doesn't 408 // get used. 409 410 BEntry entry(path.Path()); 411 if (status != B_ENTRY_NOT_FOUND 412 || entry.GetParent(&directory) < B_OK 413 || directory.CreateDirectory(path.Leaf(), NULL) < B_OK 414 || directory.SetTo(path.Path()) < B_OK) 415 return false; 416 417 BPath targetPath; 418 find_directory(B_USER_DIRECTORY, &targetPath); 419 targetPath.Append("mail/in"); 420 421 directory.CreateSymLink("Open Inbox Folder", targetPath.Path(), NULL); 422 targetPath.GetParent(&targetPath); 423 directory.CreateSymLink("Open Mail Folder", targetPath.Path(), NULL); 424 425 // create the draft query 426 427 BFile file; 428 if (directory.CreateFile("Open Draft", &file) < B_OK) 429 return true; 430 431 BString string("MAIL:draft==1"); 432 file.WriteAttrString("_trk/qrystr", &string); 433 string = "E-mail"; 434 file.WriteAttrString("_trk/qryinitmime", &string); 435 BNodeInfo(&file).SetType("application/x-vnd.Be-query"); 436 437 return true; 438 } 439 440 441 void 442 DeskbarView::_CreateNewMailQuery(BEntry& query) 443 { 444 BFile file(&query, B_READ_WRITE | B_CREATE_FILE); 445 if (file.InitCheck() != B_OK) 446 return; 447 448 BString string("((" B_MAIL_ATTR_READ "<2)&&((BEOS:TYPE==" 449 "\"text/x-email\")||(BEOS:TYPE==\"text/x-partial-email\")))"); 450 file.WriteAttrString("_trk/qrystr", &string); 451 file.WriteAttrString("_trk/qryinitstr", &string); 452 int32 mode = 'Fbyq'; 453 file.WriteAttr("_trk/qryinitmode", B_INT32_TYPE, 0, &mode, sizeof(int32)); 454 string = "E-mail"; 455 file.WriteAttrString("_trk/qryinitmime", &string); 456 BNodeInfo(&file).SetType("application/x-vnd.Be-query"); 457 } 458 459 460 BPopUpMenu* 461 DeskbarView::_BuildMenu() 462 { 463 BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false); 464 menu->SetFont(be_plain_font); 465 466 menu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE ( 467 "Create new message", "N) 新規メッセージ作成")B_UTF8_ELLIPSIS, 468 new BMessage(MD_OPEN_NEW))); 469 menu->AddSeparatorItem(); 470 471 BMessenger tracker(kTrackerSignature); 472 BNavMenu* navMenu; 473 BMenuItem* item; 474 BMessage* msg; 475 entry_ref ref; 476 477 BPath path; 478 find_directory(B_USER_SETTINGS_DIRECTORY, &path); 479 path.Append("Mail/Menu Links"); 480 481 BDirectory directory; 482 if (_CreateMenuLinks(directory, path)) { 483 int32 count = 0; 484 485 while (directory.GetNextRef(&ref) == B_OK) { 486 count++; 487 488 path.SetTo(&ref); 489 // the true here dereferences the symlinks all the way :) 490 BEntry entry(&ref, true); 491 492 // do we want to use the NavMenu, or just an ordinary BMenuItem? 493 // we are using the NavMenu only for directories and queries 494 bool useNavMenu = false; 495 496 if (entry.InitCheck() == B_OK) { 497 if (entry.IsDirectory()) 498 useNavMenu = true; 499 else if (entry.IsFile()) { 500 // Files should use the BMenuItem unless they are queries 501 char mimeString[B_MIME_TYPE_LENGTH]; 502 BNode node(&entry); 503 BNodeInfo info(&node); 504 if (info.GetType(mimeString) == B_OK 505 && strcmp(mimeString, "application/x-vnd.Be-query") 506 == 0) 507 useNavMenu = true; 508 } 509 // clobber the existing ref only if the symlink derefernces 510 // completely, otherwise we'll stick with what we have 511 entry.GetRef(&ref); 512 } 513 514 msg = new BMessage(B_REFS_RECEIVED); 515 msg->AddRef("refs", &ref); 516 517 if (useNavMenu) { 518 item = new BMenuItem(navMenu = new BNavMenu(path.Leaf(), 519 B_REFS_RECEIVED, tracker), msg); 520 navMenu->SetNavDir(&ref); 521 } else 522 item = new BMenuItem(path.Leaf(), msg); 523 524 menu->AddItem(item); 525 if(entry.InitCheck() != B_OK) 526 item->SetEnabled(false); 527 } 528 if (count > 0) 529 menu->AddSeparatorItem(); 530 } 531 532 // Hack for R5's buggy Query Notification 533 #ifdef HAIKU_TARGET_PLATFORM_BEOS 534 menu->AddItem(new BMenuItem( 535 MDR_DIALECT_CHOICE("Refresh New Mail Count", 536 "未読メールカウントを更新"), 537 new BMessage(MD_REFRESH_QUERY))); 538 #endif 539 540 // The New E-mail query 541 542 if (fNewMessages > 0) { 543 BString string; 544 MDR_DIALECT_CHOICE( 545 string << fNewMessages << " new message" 546 << (fNewMessages != 1 ? "s" : B_EMPTY_STRING), 547 string << fNewMessages << " 通の未読メッセージ"); 548 549 _GetNewQueryRef(ref); 550 551 item = new BMenuItem(navMenu = new BNavMenu(string.String(), 552 B_REFS_RECEIVED, BMessenger(kTrackerSignature)), 553 msg = new BMessage(B_REFS_RECEIVED)); 554 msg->AddRef("refs", &ref); 555 navMenu->SetNavDir(&ref); 556 557 menu->AddItem(item); 558 } else { 559 menu->AddItem(item = new BMenuItem( 560 MDR_DIALECT_CHOICE ("No new messages","未読メッセージなし"), NULL)); 561 item->SetEnabled(false); 562 } 563 564 BMailAccounts accounts; 565 if (modifiers() & B_SHIFT_KEY) { 566 BMenu *accountMenu = new BMenu( 567 MDR_DIALECT_CHOICE ("Check for mails only","R) メール受信のみ")); 568 BFont font; 569 menu->GetFont(&font); 570 accountMenu->SetFont(&font); 571 572 for (int32 i = 0; i < accounts.CountAccounts(); i++) { 573 BMailAccountSettings* account = accounts.AccountAt(i); 574 575 BMessage* message = new BMessage(MD_CHECK_FOR_MAILS); 576 message->AddInt32("account", account->AccountID()); 577 578 accountMenu->AddItem(new BMenuItem(account->Name(), message)); 579 } 580 if (accounts.CountAccounts() == 0) { 581 item = new BMenuItem("<no accounts>", NULL); 582 item->SetEnabled(false); 583 accountMenu->AddItem(item); 584 } 585 accountMenu->SetTargetForItems(this); 586 menu->AddItem(new BMenuItem(accountMenu, 587 new BMessage(MD_CHECK_FOR_MAILS))); 588 589 // Not used: 590 // menu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE ( 591 // "Check For Mails Only","メール受信のみ"), new BMessage(MD_CHECK_FOR_MAILS))); 592 menu->AddItem(new BMenuItem( 593 MDR_DIALECT_CHOICE ("Send pending mails", "M) 保留メールを送信"), 594 new BMessage(MD_SEND_MAILS))); 595 } else { 596 menu->AddItem(item = new BMenuItem( 597 MDR_DIALECT_CHOICE ("Check for mail now", "C) メールチェック"), 598 new BMessage(MD_CHECK_SEND_NOW))); 599 if (accounts.CountAccounts() == 0) 600 item->SetEnabled(false); 601 } 602 603 menu->AddSeparatorItem(); 604 menu->AddItem(new BMenuItem( 605 MDR_DIALECT_CHOICE ("Preferences", "P) メール環境設定") B_UTF8_ELLIPSIS, 606 new BMessage(MD_OPEN_PREFS))); 607 608 if (modifiers() & B_SHIFT_KEY) { 609 menu->AddItem(new BMenuItem( 610 MDR_DIALECT_CHOICE ("Shutdown mail services", "Q) 終了"), 611 new BMessage(B_QUIT_REQUESTED))); 612 } 613 614 // Reset Item Targets (only those which aren't already set) 615 616 for (int32 i = menu->CountItems(); i-- > 0;) { 617 item = menu->ItemAt(i); 618 if (item && (msg = item->Message()) != NULL) { 619 if (msg->what == B_REFS_RECEIVED) 620 item->SetTarget(tracker); 621 else 622 item->SetTarget(this); 623 } 624 } 625 return menu; 626 } 627 628 629 status_t 630 DeskbarView::_GetNewQueryRef(entry_ref& ref) 631 { 632 BPath path; 633 find_directory(B_USER_SETTINGS_DIRECTORY, &path); 634 path.Append("Mail/New E-mail"); 635 BEntry query(path.Path()); 636 if (!query.Exists()) 637 _CreateNewMailQuery(query); 638 return query.GetRef(&ref); 639 } 640