Mail: Refactor the Attachments (Enclosures) views to use layouts.

This does not fix all the problems with their appearance,
but it does fix some of them, and paves the way for further fixes
to be made.
This commit is contained in:
Augustin Cavalier
2023-03-14 15:00:34 -04:00
parent dd3f595b54
commit e76f360a15
3 changed files with 119 additions and 202 deletions
+115 -181
View File
@@ -45,11 +45,14 @@ of their respective holders. All rights reserved.
#include <Alert.h> #include <Alert.h>
#include <Beep.h> #include <Beep.h>
#include <Bitmap.h> #include <Bitmap.h>
#include <ControlLook.h>
#include <Debug.h> #include <Debug.h>
#include <LayoutBuilder.h>
#include <Locale.h> #include <Locale.h>
#include <MenuItem.h> #include <MenuItem.h>
#include <NodeMonitor.h> #include <NodeMonitor.h>
#include <PopUpMenu.h> #include <PopUpMenu.h>
#include <StringView.h>
#include <MailAttachment.h> #include <MailAttachment.h>
#include <MailMessage.h> #include <MailMessage.h>
@@ -69,59 +72,6 @@ of their respective holders. All rights reserved.
static const float kPlainFontSizeScale = 0.9; static const float kPlainFontSizeScale = 0.9;
static status_t
GetTrackerIcon(BMimeType &type, BBitmap *icon, icon_size iconSize)
{
// set some icon size related variables
status_t error = B_OK;
BRect bounds;
switch (iconSize) {
case B_MINI_ICON:
bounds.Set(0, 0, 15, 15);
break;
case B_LARGE_ICON:
bounds.Set(0, 0, 31, 31);
break;
default:
error = B_BAD_VALUE;
break;
}
// check parameters and initialization
if (error == B_OK
&& (!icon || icon->InitCheck() != B_OK || icon->Bounds() != bounds))
return B_BAD_VALUE;
bool success = false;
// Ask the MIME database for the preferred application for the file type
// and whether this application has a special icon for the type.
char signature[B_MIME_TYPE_LENGTH];
if (type.GetPreferredApp(signature) == B_OK) {
BMimeType type(signature);
success = (type.GetIconForType(type.Type(), icon, iconSize) == B_OK);
}
// Ask the MIME database whether there is an icon for the node's file type.
if (error == B_OK && !success)
success = (type.GetIcon(icon, iconSize) == B_OK);
// Ask the MIME database for the super type and start all over
if (error == B_OK && !success) {
BMimeType super;
if (type.GetSupertype(&super) == B_OK)
return GetTrackerIcon(super, icon, iconSize);
}
// Return the icon for "application/octet-stream" from the MIME database.
if (error == B_OK && !success) {
// get the "application/octet-stream" icon
BMimeType type("application/octet-stream");
error = type.GetIcon(icon, iconSize);
}
return error;
}
static void static void
recursive_attachment_search(TEnclosuresView* us, BMailContainer* mail, recursive_attachment_search(TEnclosuresView* us, BMailContainer* mail,
@@ -148,10 +98,9 @@ recursive_attachment_search(TEnclosuresView* us, BMailContainer* mail,
// #pragma mark - // #pragma mark -
TEnclosuresView::TEnclosuresView(BRect rect, BRect windowRect) TEnclosuresView::TEnclosuresView()
: :
BView(rect, "m_enclosures", B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT, BView("m_enclosures", B_WILL_DRAW),
B_WILL_DRAW),
fFocus(false) fFocus(false)
{ {
SetViewUIColor(B_PANEL_BACKGROUND_COLOR); SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
@@ -160,28 +109,23 @@ TEnclosuresView::TEnclosuresView(BRect rect, BRect windowRect)
font.SetSize(font.Size() * kPlainFontSizeScale); font.SetSize(font.Size() * kPlainFontSizeScale);
SetFont(&font); SetFont(&font);
fOffset = 12; fList = new TListView(this);
BRect r;
r.left = ENCLOSE_TEXT_H + font.StringWidth(
B_TRANSLATE("Attachments: ")) + 5;
r.top = ENCLOSE_FIELD_V;
r.right = windowRect.right - windowRect.left - B_V_SCROLL_BAR_WIDTH - 9;
r.bottom = Frame().Height() - 8;
fList = new TListView(r, this);
fList->SetInvocationMessage(new BMessage(LIST_INVOKED)); fList->SetInvocationMessage(new BMessage(LIST_INVOKED));
BScrollView *scroll = new BScrollView("", fList, B_FOLLOW_LEFT_RIGHT | BStringView* label = new BStringView("label", B_TRANSLATE("Attachments: "));
B_FOLLOW_TOP, 0, false, true); BScrollView* scroll = new BScrollView("", fList, 0, false, true);
AddChild(scroll);
scroll->ScrollBar(B_VERTICAL)->SetRange(0, 0); BLayoutBuilder::Group<>(this, B_HORIZONTAL)
.SetInsets(0, 0, scroll->ScrollBar(B_VERTICAL)->PreferredSize().width - 2, -2)
.Add(label)
.Add(scroll)
.End();
} }
TEnclosuresView::~TEnclosuresView() TEnclosuresView::~TEnclosuresView()
{ {
for (int32 index = fList->CountItems();index-- > 0;) for (int32 index = fList->CountItems();index-- > 0;) {
{
TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); TListItem *item = static_cast<TListItem *>(fList->ItemAt(index));
fList->RemoveItem(index); fList->RemoveItem(index);
@@ -192,22 +136,6 @@ TEnclosuresView::~TEnclosuresView()
} }
void
TEnclosuresView::Draw(BRect where)
{
BView::Draw(where);
SetHighColor(0, 0, 0);
SetLowColor(ViewColor());
font_height fh;
GetFontHeight(&fh);
MovePenTo(ENCLOSE_TEXT_H, ENCLOSE_TEXT_V + fh.ascent);
DrawString(ENCLOSE_TEXT);
}
void void
TEnclosuresView::MessageReceived(BMessage *msg) TEnclosuresView::MessageReceived(BMessage *msg)
{ {
@@ -217,14 +145,11 @@ TEnclosuresView::MessageReceived(BMessage *msg)
{ {
BListView *list; BListView *list;
msg->FindPointer("source", (void **)&list); msg->FindPointer("source", (void **)&list);
if (list) if (list) {
{
TListItem *item = (TListItem *) (list->ItemAt(msg->FindInt32("index"))); TListItem *item = (TListItem *) (list->ItemAt(msg->FindInt32("index")));
if (item) if (item) {
{
BMessenger tracker("application/x-vnd.Be-TRAK"); BMessenger tracker("application/x-vnd.Be-TRAK");
if (tracker.IsValid()) if (tracker.IsValid()) {
{
BMessage message(B_REFS_RECEIVED); BMessage message(B_REFS_RECEIVED);
message.AddRef("refs", item->Ref()); message.AddRef("refs", item->Ref());
@@ -238,13 +163,11 @@ TEnclosuresView::MessageReceived(BMessage *msg)
case M_REMOVE: case M_REMOVE:
{ {
int32 index; int32 index;
while ((index = fList->CurrentSelection()) >= 0) while ((index = fList->CurrentSelection()) >= 0) {
{
TListItem *item = (TListItem *) fList->ItemAt(index); TListItem *item = (TListItem *) fList->ItemAt(index);
fList->RemoveItem(index); fList->RemoveItem(index);
if (item->Component()) if (item->Component()) {
{
TMailWindow *window = dynamic_cast<TMailWindow *>(Window()); TMailWindow *window = dynamic_cast<TMailWindow *>(Window());
if (window && window->Mail()) if (window && window->Mail())
window->Mail()->RemoveComponent(item->Component()); window->Mail()->RemoveComponent(item->Component());
@@ -255,8 +178,7 @@ TEnclosuresView::MessageReceived(BMessage *msg)
B_TRANSLATE("OK")); B_TRANSLATE("OK"));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go(); alert->Go();
} } else
else
watch_node(item->NodeRef(), B_STOP_WATCHING, this); watch_node(item->NodeRef(), B_STOP_WATCHING, this);
delete item; delete item;
} }
@@ -270,23 +192,18 @@ TEnclosuresView::MessageReceived(BMessage *msg)
case B_SIMPLE_DATA: case B_SIMPLE_DATA:
case B_REFS_RECEIVED: case B_REFS_RECEIVED:
case REFS_RECEIVED: case REFS_RECEIVED:
if (msg->HasRef("refs")) if (msg->HasRef("refs")) {
{
bool badType = false; bool badType = false;
int32 index = 0; int32 index = 0;
entry_ref ref; entry_ref ref;
while (msg->FindRef("refs", index++, &ref) == B_NO_ERROR) while (msg->FindRef("refs", index++, &ref) == B_NO_ERROR) {
{
BFile file(&ref, O_RDONLY); BFile file(&ref, O_RDONLY);
if (file.InitCheck() == B_OK && file.IsFile()) if (file.InitCheck() == B_OK && file.IsFile()) {
{
TListItem *item; TListItem *item;
for (int16 loop = 0; loop < fList->CountItems(); loop++) for (int32 loop = 0; loop < fList->CountItems(); loop++) {
{
item = (TListItem *) fList->ItemAt(loop); item = (TListItem *) fList->ItemAt(loop);
if (ref == *(item->Ref())) if (ref == *(item->Ref())) {
{
fList->Select(loop); fList->Select(loop);
fList->ScrollToSelection(); fList->ScrollToSelection();
continue; continue;
@@ -297,12 +214,10 @@ TEnclosuresView::MessageReceived(BMessage *msg)
fList->ScrollToSelection(); fList->ScrollToSelection();
watch_node(item->NodeRef(), B_WATCH_NAME, this); watch_node(item->NodeRef(), B_WATCH_NAME, this);
} } else
else
badType = true; badType = true;
} }
if (badType) if (badType) {
{
beep(); beep();
BAlert* alert = new BAlert("", BAlert* alert = new BAlert("",
B_TRANSLATE("Only files can be added as attachments."), B_TRANSLATE("Only files can be added as attachments."),
@@ -316,8 +231,7 @@ TEnclosuresView::MessageReceived(BMessage *msg)
case B_NODE_MONITOR: case B_NODE_MONITOR:
{ {
int32 opcode; int32 opcode;
if (msg->FindInt32("opcode", &opcode) == B_NO_ERROR) if (msg->FindInt32("opcode", &opcode) == B_NO_ERROR) {
{
dev_t device; dev_t device;
if (msg->FindInt32("device", &device) < B_OK) if (msg->FindInt32("device", &device) < B_OK)
break; break;
@@ -325,24 +239,20 @@ TEnclosuresView::MessageReceived(BMessage *msg)
if (msg->FindInt64("node", &inode) < B_OK) if (msg->FindInt64("node", &inode) < B_OK)
break; break;
for (int32 index = fList->CountItems();index-- > 0;) for (int32 index = fList->CountItems();index-- > 0;) {
{
TListItem *item = static_cast<TListItem *>(fList->ItemAt(index)); TListItem *item = static_cast<TListItem *>(fList->ItemAt(index));
if (device == item->NodeRef()->device if (device == item->NodeRef()->device
&& inode == item->NodeRef()->node) && inode == item->NodeRef()->node)
{ {
if (opcode == B_ENTRY_REMOVED) if (opcode == B_ENTRY_REMOVED) {
{
// don't hide the <missing attachment> item // don't hide the <missing attachment> item
//fList->RemoveItem(index); //fList->RemoveItem(index);
// //
//watch_node(item->NodeRef(), B_STOP_WATCHING, this); //watch_node(item->NodeRef(), B_STOP_WATCHING, this);
//delete item; //delete item;
} } else if (opcode == B_ENTRY_MOVED) {
else if (opcode == B_ENTRY_MOVED)
{
item->Ref()->device = device; item->Ref()->device = device;
msg->FindInt64("to directory", &item->Ref()->directory); msg->FindInt64("to directory", &item->Ref()->directory);
@@ -369,10 +279,9 @@ TEnclosuresView::MessageReceived(BMessage *msg)
void void
TEnclosuresView::Focus(bool focus) TEnclosuresView::Focus(bool focus)
{ {
if (fFocus != focus) if (fFocus != focus) {
{
fFocus = focus; fFocus = focus;
Draw(Frame()); Invalidate();
} }
} }
@@ -380,14 +289,16 @@ TEnclosuresView::Focus(bool focus)
void void
TEnclosuresView::AddEnclosuresFromMail(BEmailMessage *mail) TEnclosuresView::AddEnclosuresFromMail(BEmailMessage *mail)
{ {
for (int32 i = 0; i < mail->CountComponents(); i++) for (int32 i = 0; i < mail->CountComponents(); i++) {
{
BMailComponent *component = mail->GetComponent(i); BMailComponent *component = mail->GetComponent(i);
if (component == mail->Body()) if (component == mail->Body())
continue; continue;
if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) if (component->ComponentType() == B_MAIL_MULTIPART_CONTAINER) {
recursive_attachment_search(this,dynamic_cast<BMIMEMultipartMailContainer *>(component),mail->Body()); recursive_attachment_search(this,
dynamic_cast<BMIMEMultipartMailContainer *>(component),
mail->Body());
}
fList->AddItem(new TListItem(component)); fList->AddItem(new TListItem(component));
} }
@@ -397,10 +308,9 @@ TEnclosuresView::AddEnclosuresFromMail(BEmailMessage *mail)
// #pragma mark - // #pragma mark -
TListView::TListView(BRect rect, TEnclosuresView *view) TListView::TListView(TEnclosuresView *view)
: :
BListView(rect, "", B_MULTIPLE_SELECTION_LIST, BListView("", B_MULTIPLE_SELECTION_LIST),
B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT),
fParent(view) fParent(view)
{ {
} }
@@ -417,6 +327,15 @@ TListView::AttachedToWindow()
} }
BSize
TListView::MinSize()
{
BSize size = BListView::MinSize();
size.height = be_control_look->DefaultLabelSpacing() * 5;
return size;
}
void void
TListView::MakeFocus(bool focus) TListView::MakeFocus(bool focus)
{ {
@@ -490,32 +409,37 @@ TListItem::TListItem(BMailComponent *component)
void void
TListItem::Update(BView *owner, const BFont *font) TListItem::Update(BView* owner, const BFont* font)
{ {
BListItem::Update(owner, font); BListItem::Update(owner, font);
if (Height() < 17) // mini icon height + 1 const float minimalHeight =
SetHeight(17); be_control_look->ComposeIconSize(B_MINI_ICON).Height() +
(be_control_look->DefaultLabelSpacing() / 3.0f);
if (Height() < minimalHeight)
SetHeight(minimalHeight);
} }
void void
TListItem::DrawItem(BView *owner, BRect r, bool /* complete */) TListItem::DrawItem(BView *owner, BRect frame, bool /* complete */)
{ {
if (IsSelected()) { rgb_color kHighlight = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR);
owner->SetHighColor(180, 180, 180); rgb_color kHighlightText = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR);
owner->SetLowColor(180, 180, 180); rgb_color kText = ui_color(B_LIST_ITEM_TEXT_COLOR);
} else {
owner->SetHighColor(255, 255, 255);
owner->SetLowColor(255, 255, 255);
}
owner->FillRect(r);
owner->SetHighColor(0, 0, 0);
BFont font = *be_plain_font; BRect r(frame);
font.SetSize(font.Size() * kPlainFontSizeScale);
owner->SetFont(&font); if (IsSelected()) {
owner->MovePenTo(r.left + 24, r.bottom - 4); owner->SetHighColor(kHighlight);
owner->SetLowColor(kHighlight);
owner->FillRect(r);
}
const BRect iconRect(BPoint(0, 0), be_control_look->ComposeIconSize(B_MINI_ICON));
BBitmap iconBitmap(iconRect, B_RGBA32);
status_t iconStatus = B_NO_INIT;
BString label;
if (fComponent) { if (fComponent) {
// if it's already a mail component, we don't have an icon to // if it's already a mail component, we don't have an icon to
@@ -528,42 +452,52 @@ TListItem::DrawItem(BView *owner, BRect r, bool /* complete */)
BMimeType type; BMimeType type;
if (fComponent->MIMEType(&type) == B_OK) if (fComponent->MIMEType(&type) == B_OK)
sprintf(name + strlen(name), ", Type: %s", type.Type()); label.SetToFormat("%s, Type: %s", name, type.Type());
else
label = name;
owner->DrawString(name); iconStatus = type.GetIcon(&iconBitmap,
(icon_size)(iconRect.IntegerWidth() + 1));
} else {
BFile file(&fRef, O_RDONLY);
BEntry entry(&fRef);
BPath path;
if (entry.GetPath(&path) == B_OK && file.InitCheck() == B_OK) {
label = path.Path();
BRect iconRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); BNodeInfo info(&file);
iconStatus = info.GetTrackerIcon(&iconBitmap,
BBitmap bitmap(iconRect, B_RGBA32); (icon_size)(iconRect.IntegerWidth() + 1));
if (GetTrackerIcon(type, &bitmap, B_MINI_ICON) == B_NO_ERROR) { } else
BRect rect(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); label = "<missing attachment>";
owner->SetDrawingMode(B_OP_ALPHA);
owner->DrawBitmap(&bitmap, iconRect, rect);
owner->SetDrawingMode(B_OP_COPY);
} else {
// ToDo: find some nicer image for this :-)
owner->SetHighColor(150, 150, 150);
owner->FillEllipse(BRect(r.left + 8, r.top + 4, r.left + 16, r.top + 13));
}
return;
} }
BFile file(&fRef, O_RDONLY); BRect iconFrame(frame);
BEntry entry(&fRef); iconFrame.left += be_control_look->DefaultLabelSpacing() / 2;
BPath path; iconFrame.Set(iconFrame.left, iconFrame.top + 1,
if (entry.GetPath(&path) == B_OK && file.InitCheck() == B_OK) { iconFrame.left + iconRect.Width(),
owner->DrawString(path.Path()); iconFrame.top + iconRect.Height() + 1);
BNodeInfo info(&file); if (iconStatus == B_OK) {
BRect sr(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); owner->SetDrawingMode(B_OP_ALPHA);
owner->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
owner->DrawBitmap(&iconBitmap, iconFrame);
owner->SetDrawingMode(B_OP_COPY);
} else {
// ToDo: find some nicer image for this :-)
owner->SetHighColor(150, 150, 150);
owner->FillEllipse(iconFrame);
}
BBitmap bitmap(sr, B_RGBA32); BFont font;
if (info.GetTrackerIcon(&bitmap, B_MINI_ICON) == B_NO_ERROR) { owner->GetFont(&font);
BRect dr(r.left + 4, r.top + 1, r.left + 4 + 15, r.top + 1 + 15); font_height finfo;
owner->SetDrawingMode(B_OP_ALPHA); font.GetHeight(&finfo);
owner->DrawBitmap(&bitmap, sr, dr); owner->MovePenTo(frame.left + (iconFrame.Width() * 1.5f),
owner->SetDrawingMode(B_OP_COPY); frame.top + ((frame.Height()
} - (finfo.ascent + finfo.descent + finfo.leading)) / 2)
} else + finfo.ascent);
owner->DrawString("<missing attachment>");
owner->SetHighColor(IsSelected() ? kHighlightText : kText);
owner->DrawString(label.String());
} }
+3 -12
View File
@@ -49,14 +49,6 @@ All rights reserved.
#include <MailMessage.h> #include <MailMessage.h>
#define ENCLOSURES_HEIGHT 65
#define ENCLOSE_TEXT B_TRANSLATE_CONTEXT("Attachments:", "Mail")
#define ENCLOSE_TEXT_H 7
#define ENCLOSE_TEXT_V 3
#define ENCLOSE_FIELD_V 3
class TListView; class TListView;
class TMailWindow; class TMailWindow;
class TScrollView; class TScrollView;
@@ -64,10 +56,9 @@ class TScrollView;
class TEnclosuresView : public BView { class TEnclosuresView : public BView {
public: public:
TEnclosuresView(BRect rect, BRect windowRect); TEnclosuresView();
virtual ~TEnclosuresView(); virtual ~TEnclosuresView();
virtual void Draw(BRect updateRect);
virtual void MessageReceived(BMessage* message); virtual void MessageReceived(BMessage* message);
void Focus(bool focus); void Focus(bool focus);
void AddEnclosuresFromMail(BEmailMessage* mail); void AddEnclosuresFromMail(BEmailMessage* mail);
@@ -76,16 +67,16 @@ public:
private: private:
bool fFocus; bool fFocus;
float fOffset;
TMailWindow* fWindow; TMailWindow* fWindow;
}; };
class TListView : public BListView { class TListView : public BListView {
public: public:
TListView(BRect rect, TEnclosuresView* view); TListView(TEnclosuresView* view);
virtual void AttachedToWindow(); virtual void AttachedToWindow();
virtual BSize MinSize();
virtual void MakeFocus(bool focus); virtual void MakeFocus(bool focus);
virtual void MouseDown(BPoint point); virtual void MouseDown(BPoint point);
virtual void KeyDown(const char* bytes,int32 numBytes); virtual void KeyDown(const char* bytes,int32 numBytes);
+1 -9
View File
@@ -1772,16 +1772,8 @@ void
TMailWindow::AddEnclosure(BMessage* msg) TMailWindow::AddEnclosure(BMessage* msg)
{ {
if (fEnclosuresView == NULL && !fIncoming) { if (fEnclosuresView == NULL && !fIncoming) {
BRect r; fEnclosuresView = new TEnclosuresView;
r.left = 0;
r.top = fHeaderView->Frame().bottom - 1;
r.right = Frame().Width() + 2;
r.bottom = r.top + ENCLOSURES_HEIGHT;
fEnclosuresView = new TEnclosuresView(r, Frame());
AddChild(fEnclosuresView, fContentView); AddChild(fEnclosuresView, fContentView);
fContentView->ResizeBy(0, -ENCLOSURES_HEIGHT);
fContentView->MoveBy(0, ENCLOSURES_HEIGHT);
} }
if (fEnclosuresView == NULL) if (fEnclosuresView == NULL)