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