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