624 lines
14 KiB
C++
624 lines
14 KiB
C++
/*
|
|
* 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 <stdio.h>
|
|
#include <malloc.h>
|
|
|
|
#include <Bitmap.h>
|
|
#include <Catalog.h>
|
|
#include <Deskbar.h>
|
|
#include <Directory.h>
|
|
#include <Entry.h>
|
|
#include <FindDirectory.h>
|
|
#include <IconUtils.h>
|
|
#include <kernel/fs_info.h>
|
|
#include <kernel/fs_index.h>
|
|
#include <MenuItem.h>
|
|
#include <Messenger.h>
|
|
#include <NodeInfo.h>
|
|
#include <NodeMonitor.h>
|
|
#include <OpenWithTracker.h>
|
|
#include <Path.h>
|
|
#include <PopUpMenu.h>
|
|
#include <Query.h>
|
|
#include <Rect.h>
|
|
#include <Resources.h>
|
|
#include <Roster.h>
|
|
#include <String.h>
|
|
#include <StringFormat.h>
|
|
#include <SymLink.h>
|
|
#include <VolumeRoster.h>
|
|
#include <Window.h>
|
|
|
|
#include <E-mail.h>
|
|
#include <MailDaemon.h>
|
|
#include <MailSettings.h>
|
|
|
|
#include <MailPrivate.h>
|
|
|
|
#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 what;
|
|
dev_t device;
|
|
ino_t directory;
|
|
const char *name;
|
|
entry_ref ref;
|
|
message->FindInt32("opcode", &what);
|
|
message->FindInt32("device", &device);
|
|
message->FindInt64("directory", &directory);
|
|
switch (what) {
|
|
case B_ENTRY_CREATED:
|
|
if (message->FindString("name", &name) == B_OK) {
|
|
ref.device = device;
|
|
ref.directory = directory;
|
|
ref.set_name(name);
|
|
if (!_EntryInTrash(&ref))
|
|
fNewMessages++;
|
|
}
|
|
break;
|
|
case B_ENTRY_REMOVED:
|
|
node_ref node;
|
|
node.device = device;
|
|
node.node = directory;
|
|
BDirectory dir(&node);
|
|
BEntry entry(&dir, NULL);
|
|
entry.GetRef(&ref);
|
|
if (!_EntryInTrash(&ref))
|
|
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("<no accounts>"), 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);
|
|
}
|