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
795 lines
19 KiB
C++
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(¬ify);
|
|
|
|
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;
|
|
}
|