/* * Copyright 2004-2018, Haiku Inc. All Rights Reserved. * Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved. * * Distributed under the terms of the MIT License. */ //! mail_daemon's deskbar menu and view #include "DeskbarView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DeskbarViewIcons.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "DeskbarView" const char* kTrackerSignature = "application/x-vnd.Be-TRAK"; extern "C" _EXPORT BView* instantiate_deskbar_item(float maxWidth, float maxHeight); static status_t our_image(image_info& image) { int32 cookie = 0; while (get_next_image_info(B_CURRENT_TEAM, &cookie, &image) == B_OK) { if ((char *)our_image >= (char *)image.text && (char *)our_image <= (char *)image.text + image.text_size) return B_OK; } return B_ERROR; } BView* instantiate_deskbar_item(float maxWidth, float maxHeight) { return new DeskbarView(BRect(0, 0, maxHeight - 1, maxHeight - 1)); } // #pragma mark - DeskbarView::DeskbarView(BRect frame) : BView(frame, "mail_daemon", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED), fStatus(kStatusNoMail), fLastButtons(0) { _InitBitmaps(); } DeskbarView::DeskbarView(BMessage *message) : BView(message), fStatus(kStatusNoMail), fLastButtons(0) { _InitBitmaps(); } DeskbarView::~DeskbarView() { for (int i = 0; i < kStatusCount; i++) delete fBitmaps[i]; for (int32 i = 0; i < fNewMailQueries.CountItems(); i++) delete ((BQuery *)(fNewMailQueries.ItemAt(i))); } void DeskbarView::AttachedToWindow() { BView::AttachedToWindow(); AdoptParentColors(); if (ViewUIColor() == B_NO_COLOR) SetLowColor(ViewColor()); else SetLowUIColor(ViewUIColor()); if (be_roster->IsRunning(B_MAIL_DAEMON_SIGNATURE)) { _RefreshMailQuery(); } else { BDeskbar deskbar; deskbar.RemoveItem("mail_daemon"); } } bool DeskbarView::_EntryInTrash(const entry_ref* ref) { BEntry entry(ref); BVolume volume(ref->device); BPath path; if (volume.InitCheck() != B_OK || find_directory(B_TRASH_DIRECTORY, &path, false, &volume) != B_OK) return false; BDirectory trash(path.Path()); return trash.Contains(&entry); } void DeskbarView::_RefreshMailQuery() { for (int32 i = 0; i < fNewMailQueries.CountItems(); i++) delete ((BQuery *)(fNewMailQueries.ItemAt(i))); fNewMailQueries.MakeEmpty(); BVolumeRoster volumes; BVolume volume; fNewMessages = 0; while (volumes.GetNextVolume(&volume) == B_OK) { BQuery *newMailQuery = new BQuery; newMailQuery->SetTarget(this); newMailQuery->SetVolume(&volume); newMailQuery->PushAttr(B_MAIL_ATTR_STATUS); newMailQuery->PushString("New"); newMailQuery->PushOp(B_EQ); newMailQuery->Fetch(); BEntry entry; while (newMailQuery->GetNextEntry(&entry) == B_OK) { if (entry.InitCheck() == B_OK) { entry_ref ref; entry.GetRef(&ref); if (!_EntryInTrash(&ref)) fNewMessages++; } } fNewMailQueries.AddItem(newMailQuery); } fStatus = (fNewMessages > 0) ? kStatusNewMail : kStatusNoMail; Invalidate(); } DeskbarView* DeskbarView::Instantiate(BMessage *data) { if (!validate_instantiation(data, "DeskbarView")) return NULL; return new DeskbarView(data); } status_t DeskbarView::Archive(BMessage *data,bool deep) const { BView::Archive(data, deep); data->AddString("add_on", B_MAIL_DAEMON_SIGNATURE); return B_NO_ERROR; } void DeskbarView::Draw(BRect /*updateRect*/) { if (fBitmaps[fStatus] == NULL) return; SetDrawingMode(B_OP_ALPHA); DrawBitmap(fBitmaps[fStatus]); SetDrawingMode(B_OP_COPY); } void DeskbarView::MessageReceived(BMessage* message) { switch (message->what) { case MD_CHECK_SEND_NOW: // also happens in DeskbarView::MouseUp() with // B_TERTIARY_MOUSE_BUTTON pressed BMailDaemon().CheckAndSendQueuedMail(); break; case MD_CHECK_FOR_MAILS: BMailDaemon().CheckMail(message->FindInt32("account")); break; case MD_SEND_MAILS: BMailDaemon().SendQueuedMail(); break; case MD_OPEN_NEW: { char* argv[] = {(char *)"New Message", (char *)"mailto:"}; be_roster->Launch("text/x-email", 2, argv); break; } case MD_OPEN_PREFS: be_roster->Launch("application/x-vnd.Haiku-Mail"); break; case MD_REFRESH_QUERY: _RefreshMailQuery(); break; case B_QUERY_UPDATE: { int32 opcode; message->FindInt32("opcode", &opcode); switch (opcode) { case B_ENTRY_CREATED: case B_ENTRY_REMOVED: { entry_ref ref; message->FindInt32("device", &ref.device); message->FindInt64("directory", &ref.directory); if (!_EntryInTrash(&ref)) { if (opcode == B_ENTRY_CREATED) fNewMessages++; else fNewMessages--; } break; } } fStatus = fNewMessages > 0 ? kStatusNewMail : kStatusNoMail; Invalidate(); break; } case B_QUIT_REQUESTED: BMailDaemon().Quit(); break; // open received files in the standard mail application case B_REFS_RECEIVED: { BMessage argv(B_ARGV_RECEIVED); argv.AddString("argv", "E-mail"); entry_ref ref; BPath path; int i = 0; while (message->FindRef("refs", i++, &ref) == B_OK && path.SetTo(&ref) == B_OK) { //fprintf(stderr,"got %s\n", path.Path()); argv.AddString("argv", path.Path()); } if (i > 1) { argv.AddInt32("argc", i); be_roster->Launch("text/x-email", &argv); } break; } default: BView::MessageReceived(message); } } void DeskbarView::_InitBitmaps() { for (int i = 0; i < kStatusCount; i++) fBitmaps[i] = NULL; image_info info; if (our_image(info) != B_OK) return; BFile file(info.name, B_READ_ONLY); if (file.InitCheck() != B_OK) return; BResources resources(&file); if (resources.InitCheck() != B_OK) return; for (int i = 0; i < kStatusCount; i++) { const void* data = NULL; size_t size; data = resources.LoadResource(B_VECTOR_ICON_TYPE, kIconNoMail + i, &size); if (data != NULL) { BBitmap* icon = new BBitmap(Bounds(), B_RGBA32); if (icon->InitCheck() == B_OK && BIconUtils::GetVectorIcon((const uint8 *)data, size, icon) == B_OK) { fBitmaps[i] = icon; } else delete icon; } } } void DeskbarView::Pulse() { // TODO: Check if mail_daemon is still running } void DeskbarView::MouseUp(BPoint pos) { if ((fLastButtons & B_PRIMARY_MOUSE_BUTTON) !=0 && OpenWithTracker(B_USER_SETTINGS_DIRECTORY, "Mail/mailbox") != B_OK) { entry_ref ref; _GetNewQueryRef(ref); BMessenger trackerMessenger(kTrackerSignature); BMessage message(B_REFS_RECEIVED); message.AddRef("refs", &ref); trackerMessenger.SendMessage(&message); } if ((fLastButtons & B_TERTIARY_MOUSE_BUTTON) != 0) BMailDaemon().CheckMail(); } void DeskbarView::MouseDown(BPoint pos) { Looper()->CurrentMessage()->FindInt32("buttons", &fLastButtons); if ((fLastButtons & B_SECONDARY_MOUSE_BUTTON) != 0) { ConvertToScreen(&pos); BPopUpMenu* menu = _BuildMenu(); menu->Go(pos, true, true, BRect(pos.x - 2, pos.y - 2, pos.x + 2, pos.y + 2), true); } } bool DeskbarView::_CreateMenuLinks(BDirectory& directory, BPath& path) { status_t status = directory.SetTo(path.Path()); if (status == B_OK) return true; // Check if the directory has to be created (and do it in this case, // filling it with some standard links). Normally the installer will // create the directory and fill it with links, so normally this doesn't // get used. BEntry entry(path.Path()); if (status != B_ENTRY_NOT_FOUND || entry.GetParent(&directory) < B_OK || directory.CreateDirectory(path.Leaf(), NULL) < B_OK || directory.SetTo(path.Path()) < B_OK) return false; BPath targetPath; find_directory(B_USER_DIRECTORY, &targetPath); targetPath.Append("mail/in"); directory.CreateSymLink("Open Inbox Folder", targetPath.Path(), NULL); targetPath.GetParent(&targetPath); directory.CreateSymLink("Open Mail Folder", targetPath.Path(), NULL); // create the draft query BFile file; if (directory.CreateFile("Open Draft", &file) < B_OK) return true; BString string("MAIL:draft==1"); file.WriteAttrString("_trk/qrystr", &string); string = "E-mail"; file.WriteAttrString("_trk/qryinitmime", &string); BNodeInfo(&file).SetType("application/x-vnd.Be-query"); return true; } void DeskbarView::_CreateNewMailQuery(BEntry& query) { BFile file(&query, B_READ_WRITE | B_CREATE_FILE); if (file.InitCheck() != B_OK) return; BString string(B_MAIL_ATTR_STATUS "==\"New\""); file.WriteAttrString("_trk/qrystr", &string); file.WriteAttrString("_trk/qryinitstr", &string); int32 mode = 'Fbyq'; file.WriteAttr("_trk/qryinitmode", B_INT32_TYPE, 0, &mode, sizeof(int32)); string = "E-mail"; file.WriteAttrString("_trk/qryinitmime", &string); BNodeInfo(&file).SetType("application/x-vnd.Be-query"); } BPopUpMenu* DeskbarView::_BuildMenu() { BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false); menu->SetFont(be_plain_font); menu->AddItem(new BMenuItem(B_TRANSLATE("Create new message" B_UTF8_ELLIPSIS), new BMessage(MD_OPEN_NEW))); menu->AddSeparatorItem(); BMessenger tracker(kTrackerSignature); BNavMenu* navMenu; BMenuItem* item; BMessage* msg; entry_ref ref; BPath path; find_directory(B_USER_SETTINGS_DIRECTORY, &path); path.Append("Mail/Menu Links"); BDirectory directory; if (_CreateMenuLinks(directory, path)) { int32 count = 0; while (directory.GetNextRef(&ref) == B_OK) { count++; path.SetTo(&ref); // the true here dereferences the symlinks all the way :) BEntry entry(&ref, true); // do we want to use the NavMenu, or just an ordinary BMenuItem? // we are using the NavMenu only for directories and queries bool useNavMenu = false; if (entry.InitCheck() == B_OK) { if (entry.IsDirectory()) useNavMenu = true; else if (entry.IsFile()) { // Files should use the BMenuItem unless they are queries char mimeString[B_MIME_TYPE_LENGTH]; BNode node(&entry); BNodeInfo info(&node); if (info.GetType(mimeString) == B_OK && strcmp(mimeString, "application/x-vnd.Be-query") == 0) useNavMenu = true; } // clobber the existing ref only if the symlink derefernces // completely, otherwise we'll stick with what we have entry.GetRef(&ref); } msg = new BMessage(B_REFS_RECEIVED); msg->AddRef("refs", &ref); if (useNavMenu) { item = new BMenuItem(navMenu = new BNavMenu(path.Leaf(), B_REFS_RECEIVED, tracker), msg); navMenu->SetNavDir(&ref); } else item = new BMenuItem(path.Leaf(), msg); menu->AddItem(item); if (entry.InitCheck() != B_OK) item->SetEnabled(false); } if (count > 0) menu->AddSeparatorItem(); } // Hack for R5's buggy Query Notification #ifdef HAIKU_TARGET_PLATFORM_BEOS menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh New Mail Count"), new BMessage(MD_REFRESH_QUERY))); #endif // The New E-mail query if (fNewMessages > 0) { static BStringFormat format(B_TRANSLATE( "{0, plural, one{# new message} other{# new messages}}")); BString string; format.Format(string, fNewMessages); _GetNewQueryRef(ref); item = new BMenuItem(navMenu = new BNavMenu(string.String(), B_REFS_RECEIVED, BMessenger(kTrackerSignature)), msg = new BMessage(B_REFS_RECEIVED)); msg->AddRef("refs", &ref); navMenu->SetNavDir(&ref); menu->AddItem(item); } else { menu->AddItem(item = new BMenuItem(B_TRANSLATE("No new messages"), NULL)); item->SetEnabled(false); } BMailAccounts accounts; if ((modifiers() & B_SHIFT_KEY) != 0) { BMenu *accountMenu = new BMenu(B_TRANSLATE("Check for mails only")); BFont font; menu->GetFont(&font); accountMenu->SetFont(&font); for (int32 i = 0; i < accounts.CountAccounts(); i++) { BMailAccountSettings* account = accounts.AccountAt(i); BMessage* message = new BMessage(MD_CHECK_FOR_MAILS); message->AddInt32("account", account->AccountID()); accountMenu->AddItem(new BMenuItem(account->Name(), message)); } if (accounts.CountAccounts() == 0) { item = new BMenuItem(B_TRANSLATE(""), NULL); item->SetEnabled(false); accountMenu->AddItem(item); } accountMenu->SetTargetForItems(this); menu->AddItem(new BMenuItem(accountMenu, new BMessage(MD_CHECK_FOR_MAILS))); // Not used: // menu->AddItem(new BMenuItem(B_TRANSLATE("Check For Mails Only"), // new BMessage(MD_CHECK_FOR_MAILS))); menu->AddItem(new BMenuItem(B_TRANSLATE("Send pending mails"), new BMessage(MD_SEND_MAILS))); } else { menu->AddItem(item = new BMenuItem(B_TRANSLATE("Check for mail now"), new BMessage(MD_CHECK_SEND_NOW))); if (accounts.CountAccounts() == 0) item->SetEnabled(false); } menu->AddSeparatorItem(); menu->AddItem(new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), new BMessage(MD_OPEN_PREFS))); if (modifiers() & B_SHIFT_KEY) { menu->AddItem(new BMenuItem(B_TRANSLATE("Shutdown mail services"), new BMessage(B_QUIT_REQUESTED))); } // Reset Item Targets (only those which aren't already set) for (int32 i = menu->CountItems(); i-- > 0;) { item = menu->ItemAt(i); if (item != NULL && (msg = item->Message()) != NULL) { if (msg->what == B_REFS_RECEIVED) item->SetTarget(tracker); else item->SetTarget(this); } } return menu; } status_t DeskbarView::_GetNewQueryRef(entry_ref& ref) { BPath path; find_directory(B_USER_SETTINGS_DIRECTORY, &path); path.Append("Mail/New E-mail"); BEntry query(path.Path()); if (!query.Exists()) _CreateNewMailQuery(query); return query.GetRef(&ref); }