BAlert: use layout API, allow any number of buttons.

* Added default constructor; you can now alter the complete behavior
  via setters, and also add buttons afterwards.
This commit is contained in:
Axel Dörfler
2015-07-30 19:50:03 +02:00
parent e27a53b231
commit 7b0ff5c6e6
2 changed files with 376 additions and 358 deletions
+77 -56
View File
@@ -1,11 +1,13 @@
/*
* Copyright 2001-2014 Haiku, Inc. All rights reserved.
* Copyright 2001-2015 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _ALERT_H
#define _ALERT_H
#include <vector>
#include <Window.h>
@@ -26,78 +28,97 @@ enum button_spacing {
class BBitmap;
class BButton;
class BGroupLayout;
class BInvoker;
class BTextView;
class TAlertView;
class BAlert : public BWindow {
public:
BAlert(const char* title, const char* text,
const char* button1, const char* button2 = NULL,
const char* button3 = NULL,
button_width width = B_WIDTH_AS_USUAL,
alert_type type = B_INFO_ALERT);
BAlert(const char* title, const char* text,
const char* button1, const char* button2,
const char* button3, button_width width,
button_spacing spacing,
alert_type type = B_INFO_ALERT);
virtual ~BAlert();
BAlert();
BAlert(const char* title, const char* text,
const char* button1,
const char* button2 = NULL,
const char* button3 = NULL,
button_width width = B_WIDTH_AS_USUAL,
alert_type type = B_INFO_ALERT);
BAlert(const char* title, const char* text,
const char* button1, const char* button2,
const char* button3, button_width width,
button_spacing spacing,
alert_type type = B_INFO_ALERT);
BAlert(BMessage* data);
virtual ~BAlert();
// Archiving
BAlert(BMessage* data);
static BArchivable *Instantiate(BMessage* data);
virtual status_t Archive(BMessage* data, bool deep = true) const;
// Archiving
static BArchivable* Instantiate(BMessage* data);
virtual status_t Archive(BMessage* data, bool deep = true) const;
// BAlert guts
void SetShortcut(int32 button_index, char key);
char Shortcut(int32 button_index) const;
// BAlert guts
alert_type Type() const;
void SetType(alert_type type);
void SetIcon(BBitmap* bitmap);
void SetText(const char* text);
void SetButtonSpacing(button_spacing spacing);
void SetButtonWidth(button_width width);
int32 Go();
status_t Go(BInvoker* invoker);
void SetShortcut(int32 buttonIndex, char key);
char Shortcut(int32 buttonIndex) const;
virtual void MessageReceived(BMessage* message);
virtual void FrameResized(float new_width, float new_height);
BButton* ButtonAt(int32 index) const;
BTextView* TextView() const;
int32 Go();
status_t Go(BInvoker* invoker);
virtual BHandler* ResolveSpecifier(BMessage* message, int32 index,
BMessage* specifier, int32 what,
const char* property);
virtual status_t GetSupportedSuites(BMessage* data);
virtual void MessageReceived(BMessage* message);
virtual void FrameResized(float newWidth, float newHeight);
virtual void DispatchMessage(BMessage* message, BHandler* handler);
virtual void Quit();
virtual bool QuitRequested();
void AddButton(const char* label, char key = 0);
int32 CountButtons() const;
BButton* ButtonAt(int32 index) const;
static BPoint AlertPosition(float width, float height);
BTextView* TextView() const;
virtual status_t Perform(perform_code d, void* arg);
virtual BHandler* ResolveSpecifier(BMessage* message, int32 index,
BMessage* specifier, int32 form,
const char* property);
virtual status_t GetSupportedSuites(BMessage* data);
virtual void DispatchMessage(BMessage* message,
BHandler* handler);
virtual void Quit();
virtual bool QuitRequested();
virtual status_t Perform(perform_code d, void* arg);
static BPoint AlertPosition(float width, float height);
private:
friend class _BAlertFilter_;
virtual void _ReservedAlert1();
virtual void _ReservedAlert2();
virtual void _ReservedAlert3();
virtual void _ReservedAlert1();
virtual void _ReservedAlert2();
virtual void _ReservedAlert3();
void _Init(const char* text, const char* button1,
const char* button2, const char* button3,
button_width width, button_spacing spacing,
alert_type type);
BBitmap* _CreateTypeIcon();
BButton* _CreateButton(int32 which, const char* label);
void _Prepare();
void _InitObject(const char* text, const char* button1,
const char* button2 = NULL,
const char* button3 = NULL,
button_width width = B_WIDTH_AS_USUAL,
button_spacing spacing = B_EVEN_SPACING,
alert_type type = B_INFO_ALERT);
BBitmap* _InitIcon();
BButton* _CreateButton(int32 which, const char* label);
sem_id fAlertSem;
int32 fAlertValue;
BButton* fButtons[3];
BTextView* fTextView;
char fKeys[3];
alert_type fMsgType;
button_width fButtonWidth;
BInvoker* fInvoker;
uint32 _reserved[4];
private:
sem_id fAlertSem;
int32 fAlertValue;
TAlertView* fIconView;
BTextView* fTextView;
BGroupLayout* fButtonLayout;
std::vector<BButton*> fButtons;
std::vector<char> fKeys;
uint8 fType;
uint8 fButtonSpacing;
uint8 fButtonWidth;
BInvoker* fInvoker;
uint32 _reserved[1];
};
#endif // _ALERT_H
+299 -302
View File
@@ -9,31 +9,28 @@
*/
// BAlert displays a modal alert window.
//! BAlert displays a modal alert window.
#include <new>
#include <stdio.h>
#include <string.h>
#include <Alert.h>
#include <Autolock.h>
#include <Beep.h>
#include <new>
#include <stdio.h>
#include <Bitmap.h>
#include <Button.h>
#include <File.h>
#include <ControlLook.h>
#include <FindDirectory.h>
#include <Font.h>
#include <IconUtils.h>
#include <Invoker.h>
#include <Looper.h>
#include <Message.h>
#include <LayoutBuilder.h>
#include <MenuField.h>
#include <MessageFilter.h>
#include <Path.h>
#include <Resources.h>
#include <Screen.h>
#include <TextView.h>
#include <View.h>
#include <String.h>
#include <Window.h>
#include <binary_compatibility/Interface.h>
@@ -42,111 +39,102 @@
#ifdef DEBUG_ALERT
# define FTRACE(x) fprintf(x)
#else
# define FTRACE(x) /* nothing */
# define FTRACE(x) ;
#endif
// Default size of the Alert window.
#define DEFAULT_RECT BRect(0, 0, 310, 75)
#define max(LHS, RHS) ((LHS) > (RHS) ? (LHS) : (RHS))
class TAlertView : public BView {
public:
TAlertView();
TAlertView(BMessage* archive);
~TAlertView();
static TAlertView* Instantiate(BMessage* archive);
virtual status_t Archive(BMessage* archive,
bool deep = true) const;
virtual void GetPreferredSize(float* _width, float* _height);
virtual BSize MaxSize();
virtual void Draw(BRect updateRect);
void SetBitmap(BBitmap* Icon)
{ fIconBitmap = Icon; }
BBitmap* Bitmap()
{ return fIconBitmap; }
private:
BBitmap* fIconBitmap;
};
class _BAlertFilter_ : public BMessageFilter {
public:
_BAlertFilter_(BAlert* Alert);
~_BAlertFilter_();
virtual filter_result Filter(BMessage* msg, BHandler** target);
private:
BAlert* fAlert;
};
static const unsigned int kAlertButtonMsg = 'ALTB';
static const int kSemTimeOut = 50000;
static const int kLeftOffset = 10;
static const int kTopOffset = 6;
static const int kBottomOffset = 8;
static const int kRightOffset = 8;
static const int kButtonSpacing = 6;
static const int kButtonOffsetSpacing = 62;
static const int kButtonUsualWidth = 75;
static const int kButtonUsualWidth = 55;
static const int kIconStripeWidth = 30;
static const int kWindowIconOffset = 27;
static const int kWindowMinOffset = 12;
static const int kWindowMinWidth = 310;
static const int kWindowOffsetMinWidth = 335;
static const int kIconStripeWidth = 30;
static const int kTextButtonOffset = 10;
static inline int32
icon_layout_scale()
{
#ifdef __HAIKU__
return max_c(1, ((int32)be_plain_font->Size() + 15) / 16);
#endif
return 1;
}
class TAlertView : public BView {
public:
TAlertView(BRect frame);
TAlertView(BMessage* archive);
~TAlertView();
static TAlertView* Instantiate(BMessage* archive);
virtual status_t Archive(BMessage* archive, bool deep = true) const;
virtual void Draw(BRect updateRect);
// These functions (or something analogous) are missing from libbe.so's
// dump. I can only assume that the bitmap is a public var in the
// original implementation -- or BAlert is a friend of TAlertView.
// Neither one is necessary, since I can just add these.
void SetBitmap(BBitmap* Icon) { fIconBitmap = Icon; }
BBitmap* Bitmap() { return fIconBitmap; }
private:
BBitmap* fIconBitmap;
};
class _BAlertFilter_ : public BMessageFilter {
public:
_BAlertFilter_(BAlert* Alert);
~_BAlertFilter_();
virtual filter_result Filter(BMessage* msg, BHandler** target);
private:
BAlert* fAlert;
};
// #pragma mark -
// #pragma mark - BAlert
BAlert::BAlert()
:
BWindow(BRect(0, 0, 100, 100), "", B_MODAL_WINDOW,
B_NOT_CLOSABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS)
{
_Init(NULL, NULL, NULL, NULL, B_WIDTH_FROM_WIDEST, B_EVEN_SPACING,
B_INFO_ALERT);
}
BAlert::BAlert(const char *title, const char *text, const char *button1,
const char *button2, const char *button3, button_width width,
alert_type type)
: BWindow(DEFAULT_RECT, title, B_MODAL_WINDOW,
:
BWindow(BRect(0, 0, 100, 100), title, B_MODAL_WINDOW,
B_NOT_CLOSABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS)
{
_InitObject(text, button1, button2, button3, width, B_EVEN_SPACING, type);
_Init(text, button1, button2, button3, width, B_EVEN_SPACING, type);
}
BAlert::BAlert(const char *title, const char *text, const char *button1,
const char *button2, const char *button3, button_width width,
button_spacing spacing, alert_type type)
: BWindow(DEFAULT_RECT, title, B_MODAL_WINDOW,
:
BWindow(BRect(0, 0, 100, 100), title, B_MODAL_WINDOW,
B_NOT_CLOSABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS)
{
_InitObject(text, button1, button2, button3, width, spacing, type);
}
BAlert::~BAlert()
{
// Probably not necessary, but it makes me feel better.
if (fAlertSem >= B_OK)
delete_sem(fAlertSem);
_Init(text, button1, button2, button3, width, spacing, type);
}
BAlert::BAlert(BMessage* data)
: BWindow(data)
:
BWindow(data)
{
fInvoker = NULL;
fAlertSem = -1;
@@ -154,20 +142,12 @@ BAlert::BAlert(BMessage* data)
fTextView = (BTextView*)FindView("_tv_");
fButtons[0] = (BButton*)FindView("_b0_");
fButtons[1] = (BButton*)FindView("_b1_");
fButtons[2] = (BButton*)FindView("_b2_");
if (fButtons[2])
SetDefaultButton(fButtons[2]);
else if (fButtons[1])
SetDefaultButton(fButtons[1]);
else if (fButtons[0])
SetDefaultButton(fButtons[0]);
// TODO: window loses default button on dearchive!
// TODO: ButtonAt() doesn't work afterwards (also affects shortcuts)
TAlertView* view = (TAlertView*)FindView("_master_");
if (view)
view->SetBitmap(_InitIcon());
view->SetBitmap(_CreateTypeIcon());
// Get keys
char key;
@@ -176,19 +156,18 @@ BAlert::BAlert(BMessage* data)
fKeys[i] = key;
}
int32 temp;
// Get alert type
if (data->FindInt32("_atype", &temp) == B_OK)
fMsgType = (alert_type)temp;
// Get button width
if (data->FindInt32("_but_width", &temp) == B_OK)
fButtonWidth = (button_width)temp;
AddCommonFilter(new(std::nothrow) _BAlertFilter_(this));
}
BAlert::~BAlert()
{
// Probably not necessary, but it makes me feel better.
if (fAlertSem >= B_OK)
delete_sem(fAlertSem);
}
BArchivable*
BAlert::Instantiate(BMessage* data)
{
@@ -210,7 +189,7 @@ BAlert::Archive(BMessage* data, bool deep) const
// Stow the alert type
if (ret == B_OK)
ret = data->AddInt32("_atype", fMsgType);
ret = data->AddInt32("_atype", fType);
// Stow the button width
if (ret == B_OK)
@@ -232,10 +211,52 @@ BAlert::Archive(BMessage* data, bool deep) const
}
alert_type
BAlert::Type() const
{
return (alert_type)fType;
}
void
BAlert::SetType(alert_type type)
{
fType = type;
}
void
BAlert::SetText(const char* text)
{
TextView()->SetText(text);
}
void
BAlert::SetIcon(BBitmap* bitmap)
{
fIconView->SetBitmap(bitmap);
}
void
BAlert::SetButtonSpacing(button_spacing spacing)
{
fButtonSpacing = spacing;
}
void
BAlert::SetButtonWidth(button_width width)
{
fButtonWidth = width;
}
void
BAlert::SetShortcut(int32 index, char key)
{
if (index >= 0 && index < 3)
if (index >= 0 && (size_t)index < fKeys.size())
fKeys[index] = key;
}
@@ -243,7 +264,8 @@ BAlert::SetShortcut(int32 index, char key)
char
BAlert::Shortcut(int32 index) const
{
if (index >= 0 && index < 3)
if (index >= 0 && (size_t)index < fKeys.size())
return fKeys[index];
return 0;
@@ -254,29 +276,28 @@ int32
BAlert::Go()
{
fAlertSem = create_sem(0, "AlertSem");
if (fAlertSem < B_OK) {
if (fAlertSem < 0) {
Quit();
return -1;
}
// Get the originating window, if it exists
BWindow* window =
dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
BWindow* window = dynamic_cast<BWindow*>(
BLooper::LooperForThread(find_thread(NULL)));
_Prepare();
Show();
// Heavily modified from TextEntryAlert code; the original didn't let the
// blocked window ever draw.
if (window) {
status_t err;
if (window != NULL) {
status_t status;
for (;;) {
do {
err = acquire_sem_etc(fAlertSem, 1, B_RELATIVE_TIMEOUT,
kSemTimeOut);
status = acquire_sem_etc(fAlertSem, 1, B_RELATIVE_TIMEOUT,
kSemTimeOut);
// We've (probably) had our time slice taken away from us
} while (err == B_INTERRUPTED);
} while (status == B_INTERRUPTED);
if (err == B_BAD_SEM_ID) {
if (status == B_BAD_SEM_ID) {
// Semaphore was finally nuked in MessageReceived
break;
}
@@ -301,19 +322,20 @@ status_t
BAlert::Go(BInvoker* invoker)
{
fInvoker = invoker;
_Prepare();
Show();
return B_OK;
}
void
BAlert::MessageReceived(BMessage* message)
BAlert::MessageReceived(BMessage* msg)
{
if (message->what != kAlertButtonMsg)
return BWindow::MessageReceived(message);
if (msg->what != kAlertButtonMsg)
return BWindow::MessageReceived(msg);
int32 which;
if (message->FindInt32("which", &which) == B_OK) {
if (msg->FindInt32("which", &which) == B_OK) {
if (fAlertSem < B_OK) {
// Semaphore hasn't been created; we're running asynchronous
if (fInvoker) {
@@ -346,10 +368,31 @@ BAlert::FrameResized(float newWidth, float newHeight)
}
void
BAlert::AddButton(const char* label, char key)
{
if (label == NULL || label[0] == '\0')
return;
BButton* button = _CreateButton(fButtons.size(), label);
fButtons.push_back(button);
fKeys.push_back(key);
fButtonLayout->AddView(button);
}
int32
BAlert::CountButtons() const
{
return (int32)fButtons.size();
}
BButton*
BAlert::ButtonAt(int32 index) const
{
if (index >= 0 && index < 3)
if (index >= 0 && (size_t)index < fButtons.size())
return fButtons[index];
return NULL;
@@ -364,11 +407,10 @@ BAlert::TextView() const
BHandler*
BAlert::ResolveSpecifier(BMessage* message, int32 index,
BMessage* specifier, int32 what, const char* property)
BAlert::ResolveSpecifier(BMessage* msg, int32 index,
BMessage* specifier, int32 form, const char* property)
{
return BWindow::ResolveSpecifier(message, index, specifier, what,
property);
return BWindow::ResolveSpecifier(msg, index, specifier, form, property);
}
@@ -400,7 +442,7 @@ BAlert::QuitRequested()
}
// This method is deprecated, do not use
//! This method is deprecated, do not use - use BWindow::CenterIn() instead.
BPoint
BAlert::AlertPosition(float width, float height)
{
@@ -429,11 +471,9 @@ BAlert::Perform(perform_code code, void* _data)
{
switch (code) {
case PERFORM_CODE_SET_LAYOUT:
{
perform_data_set_layout* data = (perform_data_set_layout*)_data;
BAlert::SetLayout(data->layout);
return B_OK;
}
}
return BWindow::Perform(code, _data);
@@ -446,185 +486,79 @@ void BAlert::_ReservedAlert3() {}
void
BAlert::_InitObject(const char* text, const char* button0, const char* button1,
BAlert::_Init(const char* text, const char* button0, const char* button1,
const char* button2, button_width buttonWidth, button_spacing spacing,
alert_type type)
{
fInvoker = NULL;
fAlertSem = -1;
fAlertValue = -1;
fButtons[0] = fButtons[1] = fButtons[2] = NULL;
fTextView = NULL;
fKeys[0] = fKeys[1] = fKeys[2] = 0;
fMsgType = type;
fButtonWidth = buttonWidth;
// Set up the "_master_" view
TAlertView* view = new(std::nothrow) TAlertView(Bounds());
if (view == NULL)
return;
AddChild(view);
view->SetBitmap(_InitIcon());
// Must have at least one button
if (button0 == NULL) {
debugger("BAlerts must have at least one button.");
button0 = "";
}
// Set up the buttons
int32 buttonCount = 1;
view->AddChild(fButtons[0] = _CreateButton(0, button0));
if (button1 != NULL) {
view->AddChild(fButtons[1] = _CreateButton(1, button1));
buttonCount++;
}
if (button2 != NULL) {
view->AddChild(fButtons[2] = _CreateButton(2, button2));
buttonCount++;
}
// Find the widest button only if the widest value needs to be known.
if (fButtonWidth == B_WIDTH_FROM_WIDEST) {
float maxWidth = 0;
for (int i = 0; i < buttonCount; i++) {
float width = fButtons[i]->Bounds().Width();
if (width > maxWidth)
maxWidth = width;
}
// resize buttons
for (int i = 0; i < buttonCount; i++) {
fButtons[i]->ResizeTo(maxWidth, fButtons[i]->Bounds().Height());
}
}
float defaultButtonFrameWidth
= -fButtons[buttonCount - 1]->Bounds().Width() / 2.0f;
SetDefaultButton(fButtons[buttonCount - 1]);
defaultButtonFrameWidth
+= fButtons[buttonCount - 1]->Bounds().Width() / 2.0f;
// Layout buttons
float fontFactor = be_plain_font->Size() / 11.0f;
for (int i = buttonCount - 1; i >= 0; --i) {
float x = -fButtons[i]->Bounds().Width();
if (i + 1 == buttonCount)
x += Bounds().right - kRightOffset + defaultButtonFrameWidth;
else
x += fButtons[i + 1]->Frame().left - kButtonSpacing;
if (buttonCount > 1 && i == 0 && spacing == B_OFFSET_SPACING)
x -= kButtonOffsetSpacing * fontFactor;
fButtons[i]->MoveTo(x, fButtons[i]->Frame().top);
}
// Adjust the window's width, if necessary
int32 iconLayoutScale = icon_layout_scale();
float totalWidth = kRightOffset + fButtons[buttonCount - 1]->Frame().right
- defaultButtonFrameWidth - fButtons[0]->Frame().left;
if (view->Bitmap())
totalWidth += (kIconStripeWidth + kWindowIconOffset) * iconLayoutScale;
else
totalWidth += kWindowMinOffset;
float width = (spacing == B_OFFSET_SPACING
? kWindowOffsetMinWidth : kWindowMinWidth) * fontFactor;
ResizeTo(max_c(totalWidth, width), Bounds().Height());
// Set up the text view
BRect textViewRect(kLeftOffset, kTopOffset,
Bounds().right - kRightOffset,
fButtons[0]->Frame().top - kTextButtonOffset);
if (view->Bitmap())
textViewRect.left = (kWindowIconOffset
+ kIconStripeWidth) * iconLayoutScale - 2;
fTextView = new(std::nothrow) BTextView(textViewRect, "_tv_",
textViewRect.OffsetByCopy(B_ORIGIN),
B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);
if (fTextView == NULL)
return;
fIconView = new TAlertView();
fTextView = new BTextView("_tv_");
fTextView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
fTextView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
fTextView->SetText(text, strlen(text));
fTextView->MakeEditable(false);
fTextView->MakeSelectable(false);
fTextView->SetWordWrap(true);
view->AddChild(fTextView);
fTextView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
// Now resize the TextView vertically so that all the text is visible
float textHeight = fTextView->TextHeight(0, fTextView->CountLines());
textViewRect.OffsetTo(0, 0);
textHeight -= textViewRect.Height();
ResizeBy(0, textHeight);
fTextView->ResizeBy(0, textHeight);
textViewRect.bottom += textHeight;
fTextView->SetTextRect(textViewRect);
fButtonLayout = new BGroupLayout(B_HORIZONTAL);
AddCommonFilter(new(std::nothrow) _BAlertFilter_(this));
SetType(type);
SetButtonWidth(buttonWidth);
SetButtonSpacing(spacing);
SetText(text);
// Position the alert so that it is centered vertically but offset a bit
// horizontally in the parent window's frame or, if unavailable, the
// screen frame.
BWindow* parent =
dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
const BRect frame = parent != NULL ? parent->Frame() : BScreen(this).Frame();
MoveTo(dynamic_cast<BWindow*>(this)->AlertPosition(frame));
BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0)
.Add(fIconView)
.AddGroup(B_VERTICAL)
.SetInsets(B_USE_DEFAULT_SPACING)
.Add(fTextView)
.AddGroup(B_HORIZONTAL, 0)
.AddGlue()
.Add(fButtonLayout);
AddButton(button0);
AddButton(button1);
AddButton(button2);
}
BBitmap*
BAlert::_InitIcon()
BAlert::_CreateTypeIcon()
{
// Save the desired alert type and set it to "empty" until
// loading the icon was successful
alert_type alertType = fMsgType;
fMsgType = B_EMPTY_ALERT;
if (Type() == B_EMPTY_ALERT)
return NULL;
// After a bit of a search, I found the icons in app_server. =P
// The icons are in the app_server resources
BBitmap* icon = NULL;
BPath path;
status_t status = find_directory(B_BEOS_SERVERS_DIRECTORY, &path);
if (status < B_OK) {
FTRACE((stderr, "BAlert::_InitIcon() - find_directory failed: %s\n",
strerror(status)));
if (status != B_OK) {
FTRACE((stderr, "BAlert::_CreateTypeIcon() - find_directory "
"failed: %s\n", strerror(status)));
return NULL;
}
path.Append("app_server");
BFile file;
status = file.SetTo(path.Path(), B_READ_ONLY);
if (status < B_OK) {
FTRACE((stderr, "BAlert::_InitIcon() - BFile init failed: %s\n",
if (status != B_OK) {
FTRACE((stderr, "BAlert::_CreateTypeIcon() - BFile init failed: %s\n",
strerror(status)));
return NULL;
}
BResources resources;
status = resources.SetTo(&file);
if (status < B_OK) {
FTRACE((stderr, "BAlert::_InitIcon() - BResources init failed: %s\n",
strerror(status)));
if (status != B_OK) {
FTRACE((stderr, "BAlert::_CreateTypeIcon() - BResources init "
"failed: %s\n", strerror(status)));
return NULL;
}
// Which icon are we trying to load?
const char* iconName = ""; // Don't want any seg faults
switch (alertType) {
const char* iconName;
switch (fType) {
case B_INFO_ALERT:
iconName = "info";
break;
@@ -649,7 +583,7 @@ BAlert::_InitIcon()
icon = new(std::nothrow) BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1),
0, B_RGBA32);
if (icon == NULL || icon->InitCheck() < B_OK) {
FTRACE((stderr, "BAlert::_InitIcon() - No memory for bitmap\n"));
FTRACE((stderr, "BAlert::_CreateTypeIcon() - No memory for bitmap\n"));
delete icon;
return NULL;
}
@@ -658,39 +592,28 @@ BAlert::_InitIcon()
size_t size = 0;
const uint8* rawIcon;
#ifdef __HAIKU__
// Try to load vector icon
rawIcon = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE,
iconName, &size);
if (rawIcon != NULL
&& BIconUtils::GetVectorIcon(rawIcon, size, icon) == B_OK) {
// We have an icon, restore the saved alert type
fMsgType = alertType;
return icon;
}
#endif
// Fall back to bitmap icon
rawIcon = (const uint8*)resources.LoadResource(B_LARGE_ICON_TYPE,
iconName, &size);
if (rawIcon == NULL) {
FTRACE((stderr, "BAlert::_InitIcon() - Icon resource not found\n"));
FTRACE((stderr, "BAlert::_CreateTypeIcon() - Icon resource not found\n"));
delete icon;
return NULL;
}
// Handle color space conversion
#ifdef __HAIKU__
if (icon->ColorSpace() != B_CMAP8) {
BIconUtils::ConvertFromCMAP8(rawIcon, iconSize, iconSize,
iconSize, icon);
}
#else
icon->SetBits(rawIcon, iconSize, 0, B_CMAP8);
#endif
// We have an icon, restore the saved alert type
fMsgType = alertType;
return icon;
}
@@ -705,46 +628,95 @@ BAlert::_CreateButton(int32 which, const char* label)
message->AddInt32("which", which);
BRect rect;
rect.top = Bounds().bottom - kBottomOffset;
rect.bottom = rect.top;
char name[32];
snprintf(name, sizeof(name), "_b%" B_PRId32 "_", which);
BButton* button = new(std::nothrow) BButton(rect, name, label, message,
B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
if (button == NULL)
return NULL;
return new(std::nothrow) BButton(name, label, message);
}
float width, height;
button->GetPreferredSize(&width, &height);
/*! Tweaks the layout according to the configuration.
*/
void
BAlert::_Prepare()
{
// Must have at least one button
if (CountButtons() == 0)
debugger("BAlerts must have at least one button.");
SetDefaultButton(ButtonAt(CountButtons() - 1));
float fontFactor = be_plain_font->Size() / 11.0f;
if (fIconView->Bitmap() == NULL)
fIconView->SetBitmap(_CreateTypeIcon());
if (fButtonWidth == B_WIDTH_AS_USUAL) {
float fontFactor = be_plain_font->Size() / 11.0f;
width = max_c(width, kButtonUsualWidth * fontFactor);
float usualWidth = kButtonUsualWidth * fontFactor;
for (int32 index = 0; index < CountButtons(); index++) {
BButton* button = ButtonAt(index);
if (button->MinSize().width < usualWidth)
button->SetExplicitSize(BSize(usualWidth, B_SIZE_UNSET));
}
} else if (fButtonWidth == B_WIDTH_FROM_WIDEST) {
// Get width of widest label
float maxWidth = 0;
for (int32 index = 0; index < CountButtons(); index++) {
BButton* button = ButtonAt(index);
float width;
button->GetPreferredSize(&width, NULL);
if (width > maxWidth)
maxWidth = width;
}
for (int32 index = 0; index < CountButtons(); index++) {
BButton* button = ButtonAt(index);
button->SetExplicitSize(BSize(maxWidth, B_SIZE_UNSET));
}
}
button->ResizeTo(width, height);
button->MoveBy(0.0f, -height);
return button;
if (fButtonSpacing == B_OFFSET_SPACING && CountButtons() > 1) {
// Insert some strut
fButtonLayout->AddItem(1, BSpaceLayoutItem::CreateHorizontalStrut(
kButtonOffsetSpacing * fontFactor));
}
// Position the alert so that it is centered vertically but offset a bit
// horizontally in the parent window's frame or, if unavailable, the
// screen frame.
float minWindowWidth = (fButtonSpacing == B_OFFSET_SPACING
? kWindowOffsetMinWidth : kWindowMinWidth) * fontFactor;
GetLayout()->SetExplicitMinSize(BSize(minWindowWidth, B_SIZE_UNSET));
ResizeToPreferred();
BWindow* parent = dynamic_cast<BWindow*>(BLooper::LooperForThread(
find_thread(NULL)));
const BRect frame = parent != NULL ? parent->Frame()
: BScreen(this).Frame();
MoveTo(static_cast<BWindow*>(this)->AlertPosition(frame));
// Hidden by BAlert::AlertPosition()
}
// #pragma mark - TAlertView
TAlertView::TAlertView(BRect frame)
: BView(frame, "TAlertView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
fIconBitmap(NULL)
TAlertView::TAlertView()
:
BView("TAlertView", B_WILL_DRAW),
fIconBitmap(NULL)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
TAlertView::TAlertView(BMessage* archive)
: BView(archive),
fIconBitmap(NULL)
:
BView(archive),
fIconBitmap(NULL)
{
}
@@ -772,10 +744,37 @@ TAlertView::Archive(BMessage* archive, bool deep) const
}
void
TAlertView::GetPreferredSize(float* _width, float* _height)
{
int32 scale = icon_layout_scale();
if (_width != NULL) {
if (fIconBitmap != NULL)
*_width = 18 * scale + fIconBitmap->Bounds().Width();
else
*_width = 0;
}
if (_height != NULL) {
if (fIconBitmap != NULL)
*_height = 6 * scale + fIconBitmap->Bounds().Height();
else
*_height = 0;
}
}
BSize
TAlertView::MaxSize()
{
return BSize(MinSize().width, B_SIZE_UNLIMITED);
}
void
TAlertView::Draw(BRect updateRect)
{
if (!fIconBitmap)
if (fIconBitmap == NULL)
return;
// Here's the fun stuff
@@ -789,11 +788,9 @@ TAlertView::Draw(BRect updateRect)
SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
DrawBitmapAsync(fIconBitmap, BPoint(18 * iconLayoutScale,
6 * iconLayoutScale));
}
//------------------------------------------------------------------------------
// #pragma mark - _BAlertFilter_