Files
haiku-beta6/src/kits/interface/MenuItem.cpp
T
Stephan Aßmus 306e92084c * small cleanup of Draw (less usage of ui_color() which currently
takes a trip to the server!)
* changed look of mark and submenu symbol to better fit the rest
  of the already changed controls
* the actual background color is passed to DrawMarkSymbol() and
  DrawSubmenuSymbol()


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16896 a95241bf-73f2-0310-859d-f6bbb57e9c96
2006-03-27 18:20:06 +00:00

795 lines
19 KiB
C++

/*
* Copyright 2001-2006, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Marc Flerackers ([email protected])
* Bill Hayden ([email protected])
* Stefano Ceccherini ([email protected])
* Olivier Milla
*/
//! Display item for BMenu class
#include <Bitmap.h>
#include <MenuItem.h>
#include <Shape.h>
#include <String.h>
#include <Window.h>
#include <string.h>
#include <stdlib.h>
const uint32 kCtrlLength = 20*11;
const unsigned char kCtrlBits [] = {
0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x13,0x04,0x04,0x13,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x04,0x04,0x1a,0x04,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x04,0x1a,0x1a,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x04,0x1a,0x1a,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x13,0x04,0x04,0x13,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x04,0x04,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x14,0xff,0xff,0xff,
0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0xff,0xff,0xff
/* 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff*/
};
const uint32 kAltLength = 20*11;
const unsigned char kAltBits [] = {
0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x13,0x04,0x04,0x13,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x1a,0x1a,0x04,0x04,0x04,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x04,0x04,0x04,0x04,0x1a,0x04,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x1a,0x1a,0x1a,0x04,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x04,0x1a,0x1a,0x04,0x1a,0x04,0x04,0x04,0x1a,0x04,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x17,0x14,0xff,0xff,0xff,
0x1d,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x14,0xff,0xff,0xff,
0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0xff,0xff,0xff
/* 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff*/
};
const uint32 kShiftLength = 24*11;
const unsigned char kShiftBits [] = {
0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x1d,0x17,0xff,0xff,
0x1d,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x17,0xff,0xff,
0x1d,0x1b,0x1b,0x17,0x0d,0x0d,0x17,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x17,0xff,0xff,
0x1d,0x1b,0x1b,0x0d,0x19,0x19,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x0d,0x0d,0x0d,0x1b,0x0d,0x0d,0x0d,0x1b,0x19,0x17,0xff,0xff,
0x1d,0x1b,0x1b,0x17,0x0d,0x0d,0x19,0x1b,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x1b,0x1b,0x1b,0x0d,0x1b,0x1b,0x19,0x17,0xff,0xff,
0x1d,0x1b,0x1b,0x1b,0x1b,0x1b,0x0d,0x1b,0x0d,0x0d,0x0d,0x1b,0x0d,0x0d,0x1b,0x1b,0x1b,0x0d,0x1b,0x1b,0x19,0x17,0xff,0xff,
0x1d,0x1b,0x1b,0x0d,0x19,0x19,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x1b,0x1b,0x1b,0x0d,0x1b,0x1b,0x19,0x17,0xff,0xff,
0x1d,0x1b,0x1b,0x17,0x0d,0x0d,0x17,0x1b,0x0d,0x1b,0x0d,0x1b,0x0d,0x1b,0x1b,0x1b,0x1b,0x0d,0x1b,0x1b,0x19,0x17,0xff,0xff,
0x1d,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x1b,0x19,0x17,0xff,0xff,
0x1d,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x17,0xff,0xff,
0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0xff,0xff
/*0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff*/
};
const float kLightBGTint = (B_LIGHTEN_1_TINT + B_LIGHTEN_1_TINT + B_NO_TINT) / 3.0;
BMenuItem::BMenuItem(const char *label, BMessage *message, char shortcut,
uint32 modifiers)
{
InitData();
if (label != NULL)
fLabel = strdup(label);
SetMessage(message);
fShortcutChar = shortcut;
if (shortcut != 0)
fModifiers = modifiers | B_COMMAND_KEY;
else
fModifiers = 0;
}
BMenuItem::BMenuItem(BMenu *menu, BMessage *message)
{
InitData();
SetMessage(message);
InitMenuData(menu);
}
BMenuItem::BMenuItem(BMessage *data)
{
InitData();
if (data->HasString("_label")) {
const char *string;
data->FindString("_label", &string);
SetLabel(string);
}
bool disable;
if (data->FindBool("_disable", &disable) == B_OK)
SetEnabled(!disable);
bool marked;
if (data->FindBool("_marked", &marked) == B_OK)
SetMarked(marked);
if (data->HasInt32("_user_trig")) {
int32 user_trig;
data->FindInt32("_user_trig", &user_trig);
SetTrigger(user_trig);
}
if (data->HasInt32("_shortcut")) {
int32 shortcut, mods;
data->FindInt32("_shortcut", &shortcut);
data->FindInt32("_mods", &mods);
SetShortcut(shortcut, mods);
}
if (data->HasMessage("_msg")) {
BMessage *msg = new BMessage;
data->FindMessage("_msg", msg);
SetMessage(msg);
}
BMessage subMessage;
if (data->FindMessage("_submenu", &subMessage) == B_OK) {
BArchivable *object = instantiate_object(&subMessage);
if (object != NULL) {
BMenu *menu = dynamic_cast<BMenu *>(object);
if (menu != NULL)
InitMenuData(menu);
}
}
}
BArchivable *
BMenuItem::Instantiate(BMessage *data)
{
if (validate_instantiation(data, "BMenuItem"))
return new BMenuItem(data);
else
return NULL;
}
status_t
BMenuItem::Archive(BMessage *data, bool deep) const
{
if (fLabel)
data->AddString("_label", Label());
if (!IsEnabled())
data->AddBool("_disable", true);
if (IsMarked())
data->AddBool("_marked", true);
if (fUserTrigger)
data->AddInt32("_user_trig", fUserTrigger);
if (fShortcutChar) {
data->AddInt32("_shortcut", fShortcutChar);
data->AddInt32("_mods", fModifiers);
}
if (Message())
data->AddMessage("_msg", Message());
if (deep && fSubmenu) {
BMessage submenu;
if (fSubmenu->Archive(&submenu, true) == B_OK)
data->AddMessage("_submenu", &submenu);
}
return B_OK;
}
BMenuItem::~BMenuItem()
{
free(fLabel);
delete fSubmenu;
}
void
BMenuItem::SetLabel(const char *string)
{
if (fLabel != NULL) {
free(fLabel);
fLabel = NULL;
}
if (string != NULL)
fLabel = strdup(string);
if (fSuper != NULL) {
fSuper->InvalidateLayout();
if (fSuper->LockLooper()) {
fSuper->Invalidate();
fSuper->UnlockLooper();
}
}
}
void
BMenuItem::SetEnabled(bool state)
{
if (fEnabled == state)
return;
fEnabled = state;
if (fSubmenu != NULL)
fSubmenu->SetEnabled(state);
BMenu *menu = Menu();
if (menu != NULL && menu->LockLooper()) {
menu->Invalidate(fBounds);
menu->UnlockLooper();
}
}
void
BMenuItem::SetMarked(bool state)
{
fMark = state;
if (state && Menu() != NULL)
Menu()->ItemMarked(this);
}
void
BMenuItem::SetTrigger(char ch)
{
fUserTrigger = ch;
if (strchr(fLabel, ch) != 0)
fSysTrigger = ch;
else
fSysTrigger = -1;
if (fSuper != NULL)
fSuper->InvalidateLayout();
}
void
BMenuItem::SetShortcut(char ch, uint32 modifiers)
{
if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow)
fWindow->RemoveShortcut(fShortcutChar, fModifiers);
fShortcutChar = ch;
if (ch != 0)
fModifiers = modifiers | B_COMMAND_KEY;
else
fModifiers = 0;
if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow)
fWindow->AddShortcut(fShortcutChar, fModifiers, this);
if (fSuper) {
fSuper->InvalidateLayout();
if (fSuper->LockLooper()) {
fSuper->Invalidate();
fSuper->UnlockLooper();
}
}
}
const char *
BMenuItem::Label() const
{
return fLabel;
}
bool
BMenuItem::IsEnabled() const
{
if (fSubmenu)
return fSubmenu->IsEnabled();
if (!fEnabled)
return false;
return fSuper ? fSuper->IsEnabled() : true;
}
bool
BMenuItem::IsMarked() const
{
return fMark;
}
char
BMenuItem::Trigger() const
{
return fUserTrigger;
}
char
BMenuItem::Shortcut(uint32 *modifiers) const
{
if (modifiers)
*modifiers = fModifiers;
return fShortcutChar;
}
BMenu *
BMenuItem::Submenu() const
{
return fSubmenu;
}
BMenu *
BMenuItem::Menu() const
{
return fSuper;
}
BRect
BMenuItem::Frame() const
{
return fBounds;
}
void
BMenuItem::GetContentSize(float *width, float *height)
{
fSuper->CacheFontInfo();
fCachedWidth = fSuper->StringWidth(fLabel);
if (width)
*width = (float)ceil(fCachedWidth);
if (height)
*height = fSuper->fFontHeight;
}
void
BMenuItem::TruncateLabel(float maxWidth, char *newLabel)
{
BFont font;
BString string(fLabel);
font.TruncateString(&string, B_TRUNCATE_MIDDLE, maxWidth);
string.CopyInto(newLabel, 0, string.Length());
newLabel[string.Length()] = '\0';
}
void
BMenuItem::DrawContent()
{
fSuper->CacheFontInfo();
fSuper->MovePenBy(0, fSuper->fAscent);
BPoint lineStart = fSuper->PenLocation();
float labelWidth, labelHeight;
GetContentSize(&labelWidth, &labelHeight);
// truncate if needed
// TODO: Actually, this is still never triggered
if (fBounds.Width() > labelWidth)
fSuper->DrawString(fLabel);
else {
char *truncatedLabel = new char[strlen(fLabel) + 4];
TruncateLabel(fBounds.Width(), truncatedLabel);
fSuper->DrawString(truncatedLabel);
delete[] truncatedLabel;
}
if (fSuper->AreTriggersEnabled() && fTriggerIndex != -1) {
float escapements[fTriggerIndex + 1];
BFont font;
fSuper->GetFont(&font);
font.GetEscapements(fLabel, fTriggerIndex + 1, escapements);
for (int32 i = 0; i < fTriggerIndex; i++)
lineStart.x += escapements[i] * font.Size();
lineStart.x--;
lineStart.y++;
BPoint lineEnd(lineStart);
lineEnd.x += escapements[fTriggerIndex] * font.Size();
fSuper->StrokeLine(lineStart, lineEnd);
}
}
void
BMenuItem::Draw()
{
bool enabled = IsEnabled();
bool selected = IsSelected();
rgb_color noTint = ui_color(B_MENU_BACKGROUND_COLOR);
rgb_color bgColor = noTint;
// set low color and fill background if selected
if (selected && (enabled || Submenu()) /*&& fSuper->fRedrawAfterSticky*/) {
bgColor = tint_color(noTint, B_DARKEN_2_TINT);
fSuper->SetLowColor(bgColor);
fSuper->FillRect(Frame(), B_SOLID_LOW);
} else
fSuper->SetLowColor(bgColor);
// set high color
if (enabled)
fSuper->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
else if (selected)
fSuper->SetHighColor(tint_color(noTint,
B_LIGHTEN_1_TINT));
else
fSuper->SetHighColor(tint_color(noTint,
B_DISABLED_LABEL_TINT));
// draw content
fSuper->MovePenTo(ContentLocation());
DrawContent();
// draw extra symbols
if (fSuper->Layout() == B_ITEMS_IN_COLUMN) {
if (IsMarked())
DrawMarkSymbol(bgColor);
if (fShortcutChar)
DrawShortcutSymbol();
if (Submenu())
DrawSubmenuSymbol(bgColor);
}
}
void
BMenuItem::Highlight(bool flag)
{
Menu()->Invalidate(Frame());
}
bool
BMenuItem::IsSelected() const
{
return fSelected;
}
BPoint
BMenuItem::ContentLocation() const
{
return BPoint(fBounds.left + Menu()->fPad.left,
fBounds.top + Menu()->fPad.top);
}
void BMenuItem::_ReservedMenuItem1() {}
void BMenuItem::_ReservedMenuItem2() {}
void BMenuItem::_ReservedMenuItem3() {}
void BMenuItem::_ReservedMenuItem4() {}
BMenuItem::BMenuItem(const BMenuItem &)
{
}
BMenuItem &
BMenuItem::operator=(const BMenuItem &)
{
return *this;
}
void
BMenuItem::InitData()
{
fLabel = NULL;
fSubmenu = NULL;
fWindow = NULL;
fSuper = NULL;
fModifiers = 0;
fCachedWidth = 0;
fTriggerIndex = -1;
fUserTrigger = 0;
fSysTrigger = 0;
fShortcutChar = 0;
fMark = false;
fEnabled = true;
fSelected = false;
}
void
BMenuItem::InitMenuData(BMenu *menu)
{
fSubmenu = menu;
fSubmenu->fSuperitem = this;
BMenuItem *item = menu->FindMarked();
if (menu->IsRadioMode() && menu->IsLabelFromMarked() && item != NULL)
SetLabel(item->Label());
else
SetLabel(menu->Name());
}
void
BMenuItem::Install(BWindow *window)
{
if (fSubmenu)
fSubmenu->Install(window);
fWindow = window;
if (fShortcutChar != 0 && (fModifiers & B_COMMAND_KEY) && fWindow)
window->AddShortcut(fShortcutChar, fModifiers, this);
if (!Messenger().IsValid())
SetTarget(window);
}
status_t
BMenuItem::Invoke(BMessage *message)
{
if (!IsEnabled())
return B_ERROR;
if (fSuper->IsRadioMode())
SetMarked(true);
bool notify = false;
uint32 kind = InvokeKind(&notify);
BMessage clone(kind);
status_t err = B_BAD_VALUE;
if (!message && !notify)
message = Message();
if (!message) {
if (!fSuper->IsWatched())
return err;
} else
clone = *message;
clone.AddInt32("index", Menu()->IndexOf(this));
clone.AddInt64("when", (int64)system_time());
clone.AddPointer("source", this);
clone.AddMessenger("be:sender", BMessenger(fSuper));
if (message)
err = BInvoker::Invoke(&clone);
// TODO: assynchronous messaging
// SendNotices(kind, &clone);
return err;
}
void
BMenuItem::Uninstall()
{
if (fSubmenu != NULL)
fSubmenu->Uninstall();
if (Target() == fWindow)
SetTarget(BMessenger());
if (fShortcutChar != 0 && fModifiers != 0 && fWindow != NULL)
fWindow->RemoveShortcut(fShortcutChar, fModifiers);
fWindow = NULL;
}
void
BMenuItem::SetSuper(BMenu *super)
{
if (fSuper != NULL && super != NULL)
debugger("Error - can't add menu or menu item to more than 1 container (either menu or menubar).");
fSuper = super;
if (fSubmenu != NULL)
fSubmenu->fSuper = super;
}
void
BMenuItem::Select(bool selected)
{
if (fSelected == selected)
return;
if (Submenu() || IsEnabled()) {
fSelected = selected;
Highlight(selected);
}
}
void
BMenuItem::DrawMarkSymbol(rgb_color bgColor)
{
fSuper->PushState();
BRect r(fBounds);
r.right = r.left + r.Height();
r.top += 2;
r.bottom -= 2;
r.left += 1;
r.right -= 5;
fSuper->SetHighColor(tint_color(bgColor, kLightBGTint));
fSuper->FillRect(r);
BPoint center(floorf((r.left + r.right) / 2.0) + 0.5,
floorf((r.top + r.bottom) / 2.0) + 0.5);
// NOTE: center is on X.5, Y.5 on purpose!
BShape arrowShape;
arrowShape.MoveTo(BPoint(center.x - 4, center.y - 1.5));
arrowShape.LineTo(BPoint(center.x - 1, center.y + 2.0));
arrowShape.LineTo(BPoint(center.x + 4, center.y - 3.5));
fSuper->SetDrawingMode(B_OP_OVER);
fSuper->SetHighColor(tint_color(bgColor, B_DARKEN_MAX_TINT));
fSuper->SetPenSize(2.0);
// NOTE: StrokeShape() offsets the shape by the current pen position,
// it is not documented in the BeBook, but it is true!
fSuper->MovePenTo(B_ORIGIN);
fSuper->StrokeShape(&arrowShape);
fSuper->PopState();
}
void
BMenuItem::DrawShortcutSymbol()
{
// TODO: Review this
BPoint where(ContentLocation() + BPoint(fBounds.Width() - 28.0f, fBounds.Height() - 5.0f));
if (fSubmenu)
where.x -= 12;
// TODO: If the shortcut is one of B_DOWN_ARROW, B_UP_ARROW, B_ENTER, etc.
// we can't just use DrawString(), as those aren't valid ascii/UTF8 charachters.
// In that case we need to build a BBitmap for the given shortcut and draw it using DrawBitmap()
fSuper->DrawChar(fShortcutChar, where);
where -= BPoint(20, 10);
if (fModifiers & B_COMMAND_KEY) {
BRect rect(0,0,16,10);
BBitmap control(rect, B_COLOR_8_BIT);
if (BMenu::sAltAsCommandKey)
control.SetBits(kAltBits, kAltLength, 0, B_COLOR_8_BIT);
else
control.SetBits(kCtrlBits, kCtrlLength, 0, B_COLOR_8_BIT);
fSuper->DrawBitmap(&control, where);
where.x -= rect.Width() + 1;
}
if (fModifiers & B_CONTROL_KEY) {
BRect rect(0,0,16,10);
BBitmap control(rect, B_COLOR_8_BIT);
if (BMenu::sAltAsCommandKey)
control.SetBits(kCtrlBits, kCtrlLength, 0, B_COLOR_8_BIT);
else
control.SetBits(kAltBits, kAltLength, 0, B_COLOR_8_BIT);
fSuper->DrawBitmap(&control, where);
where.x -= rect.Width() + 1;
}
if (fModifiers & B_SHIFT_KEY) {
BRect rect(0,0,21,10);
BBitmap shift(rect, B_COLOR_8_BIT);
shift.SetBits(kShiftBits, kShiftLength, 0, B_COLOR_8_BIT);
fSuper->DrawBitmap(&shift, where - BPoint(6, 0));
}
}
void
BMenuItem::DrawSubmenuSymbol(rgb_color bgColor)
{
fSuper->PushState();
BRect r(fBounds);
r.left = r.right - r.Height();
r.InsetBy(2.0, 2.0);
fSuper->SetHighColor(tint_color(bgColor, kLightBGTint));
fSuper->FillRect(r);
BPoint center(floorf((r.left + r.right) / 2.0) + 0.5,
floorf((r.top + r.bottom) / 2.0) + 0.5);
// NOTE: center is on X.5, Y.5 on purpose!
BShape arrowShape;
arrowShape.MoveTo(BPoint(center.x - 1.5, center.y - 3));
arrowShape.LineTo(BPoint(center.x + 2.0, center.y));
arrowShape.LineTo(BPoint(center.x - 1.5, center.y + 3));
fSuper->SetDrawingMode(B_OP_OVER);
fSuper->SetHighColor(tint_color(bgColor, B_DARKEN_MAX_TINT));
fSuper->SetPenSize(2.0);
// NOTE: StrokeShape() offsets the shape by the current pen position,
// it is not documented in the BeBook, but it is true!
fSuper->MovePenTo(B_ORIGIN);
fSuper->StrokeShape(&arrowShape);
fSuper->PopState();
}
void
BMenuItem::DrawControlChar(const char *control)
{
// TODO: Implement
}
void
BMenuItem::SetSysTrigger(char ch)
{
fSysTrigger = ch;
}