* Copyright 2006-2010, Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Adrien Destugues <pulkomandy@gmail.com>
* Axel Dörfler, axeld@pinc-software.de
* Oliver Tappe <zooey@hirschkaefer.de>
*/
#include "LanguageListView.h"
#include <stdio.h>
#include <new>
#include <Bitmap.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <FormattingConventions.h>
#include <GradientLinear.h>
#include <LocaleRoster.h>
#include <Region.h>
#include <Window.h>
#define MAX_DRAG_HEIGHT 200.0
#define ALPHA 170
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "LanguageListView"
LanguageListItem::LanguageListItem(const char* text, const char* id,
const char* languageCode)
:
BStringItem(text),
fID(id),
fCode(languageCode)
{
}
LanguageListItem::LanguageListItem(const LanguageListItem& other)
:
BStringItem(other.Text()),
fID(other.fID),
fCode(other.fCode)
{
}
void
LanguageListItem::DrawItem(BView* owner, BRect frame, bool complete)
{
DrawItemWithTextOffset(owner, frame, complete, 0);
}
void
LanguageListItem::DrawItemWithTextOffset(BView* owner, BRect frame,
bool complete, float textOffset)
{
rgb_color highColor = owner->HighColor();
rgb_color lowColor = owner->LowColor();
if (IsSelected() || complete) {
rgb_color color;
if (IsSelected())
color = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR);
else
color = owner->ViewColor();
owner->SetHighColor(color);
owner->SetLowColor(color);
owner->FillRect(frame);
} else
owner->SetLowColor(owner->ViewColor());
BString text = Text();
if (!IsEnabled()) {
rgb_color textColor = ui_color(B_LIST_ITEM_TEXT_COLOR);
if (textColor.red + textColor.green + textColor.blue > 128 * 3)
owner->SetHighColor(tint_color(textColor, B_DARKEN_2_TINT));
else
owner->SetHighColor(tint_color(textColor, B_LIGHTEN_2_TINT));
text << " [" << B_TRANSLATE("already chosen") << "]";
} else {
if (IsSelected())
owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
else
owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
}
owner->MovePenTo(
frame.left + be_control_look->DefaultLabelSpacing() + textOffset,
frame.top + BaselineOffset());
owner->DrawString(text.String());
owner->SetHighColor(highColor);
owner->SetLowColor(lowColor);
}
LanguageListItemWithFlag::LanguageListItemWithFlag(const char* text,
const char* id, const char* languageCode, const char* countryCode)
:
LanguageListItem(text, id, languageCode),
fCountryCode(countryCode),
fIcon(NULL)
{
}
LanguageListItemWithFlag::LanguageListItemWithFlag(
const LanguageListItemWithFlag& other)
:
LanguageListItem(other),
fCountryCode(other.fCountryCode),
fIcon(other.fIcon != NULL ? new BBitmap(*other.fIcon) : NULL)
{
}
LanguageListItemWithFlag::~LanguageListItemWithFlag()
{
delete fIcon;
}
void
LanguageListItemWithFlag::Update(BView* owner, const BFont* font)
{
LanguageListItem::Update(owner, font);
float iconSize = Height();
SetWidth(Width() + iconSize + be_control_look->DefaultLabelSpacing());
if (fCountryCode.IsEmpty())
return;
fIcon = new(std::nothrow) BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1),
B_RGBA32);
if (fIcon != NULL && BLocaleRoster::Default()->GetFlagIconForCountry(fIcon,
fCountryCode.String()) != B_OK) {
delete fIcon;
fIcon = NULL;
}
}
void
LanguageListItemWithFlag::DrawItem(BView* owner, BRect frame, bool complete)
{
if (fIcon == NULL || !fIcon->IsValid()) {
DrawItemWithTextOffset(owner, frame, complete, 0);
return;
}
float iconSize = fIcon->Bounds().Width();
DrawItemWithTextOffset(owner, frame, complete,
iconSize + be_control_look->DefaultLabelSpacing());
BRect iconFrame(frame.left + be_control_look->DefaultLabelSpacing(),
frame.top,
frame.left + iconSize - 1 + be_control_look->DefaultLabelSpacing(),
frame.top + iconSize - 1);
owner->SetDrawingMode(B_OP_OVER);
owner->DrawBitmap(fIcon, iconFrame);
owner->SetDrawingMode(B_OP_COPY);
}
LanguageListView::LanguageListView(const char* name, list_view_type type)
:
BOutlineListView(name, type),
fDropIndex(-1),
fDropTargetHighlightFrame(),
fGlobalDropTargetIndicator(false),
fDeleteMessage(NULL),
fDragMessage(NULL)
{
}
LanguageListView::~LanguageListView()
{
}
LanguageListItem*
LanguageListView::ItemForLanguageID(const char* id, int32* _index) const
{
for (int32 index = 0; index < FullListCountItems(); index++) {
LanguageListItem* item
= static_cast<LanguageListItem*>(FullListItemAt(index));
if (item->ID() == id) {
if (_index != NULL)
*_index = index;
return item;
}
}
return NULL;
}
LanguageListItem*
LanguageListView::ItemForLanguageCode(const char* code, int32* _index) const
{
for (int32 index = 0; index < FullListCountItems(); index++) {
LanguageListItem* item
= static_cast<LanguageListItem*>(FullListItemAt(index));
if (item->Code() == code) {
if (_index != NULL)
*_index = index;
return item;
}
}
return NULL;
}
void
LanguageListView::SetDeleteMessage(BMessage* message)
{
delete fDeleteMessage;
fDeleteMessage = message;
}
void
LanguageListView::SetDragMessage(BMessage* message)
{
delete fDragMessage;
fDragMessage = message;
}
void
LanguageListView::SetGlobalDropTargetIndicator(bool isGlobal)
{
fGlobalDropTargetIndicator = isGlobal;
}
void
LanguageListView::AttachedToWindow()
{
BOutlineListView::AttachedToWindow();
ScrollToSelection();
}
void
LanguageListView::MessageReceived(BMessage* message)
{
if (message->WasDropped() && _AcceptsDragMessage(message)) {
BMessage dragMessage(*message);
dragMessage.AddInt32("drop_index", fDropIndex);
dragMessage.AddPointer("drop_target", this);
Messenger().SendMessage(&dragMessage);
} else
BOutlineListView::MessageReceived(message);
}
void
LanguageListView::Draw(BRect updateRect)
{
BOutlineListView::Draw(updateRect);
if (fDropIndex >= 0 && fDropTargetHighlightFrame.IsValid()) {
BGradientLinear gradient;
int step = fGlobalDropTargetIndicator ? 64 : 128;
for (int i = 0; i < 256; i += step)
gradient.AddColor(i % (step * 2) == 0
? ViewColor() : ui_color(B_CONTROL_HIGHLIGHT_COLOR), i);
gradient.AddColor(ViewColor(), 255);
gradient.SetStart(fDropTargetHighlightFrame.LeftTop());
gradient.SetEnd(fDropTargetHighlightFrame.RightBottom());
if (fGlobalDropTargetIndicator) {
BRegion region(fDropTargetHighlightFrame);
region.Exclude(fDropTargetHighlightFrame.InsetByCopy(2.0, 2.0));
ConstrainClippingRegion(®ion);
FillRect(fDropTargetHighlightFrame, gradient);
ConstrainClippingRegion(NULL);
} else
FillRect(fDropTargetHighlightFrame, gradient);
}
}
bool
LanguageListView::InitiateDrag(BPoint point, int32 dragIndex,
bool )
{
if (fDragMessage == NULL)
return false;
BListItem* item = ItemAt(CurrentSelection(0));
if (item == NULL) {
item = ItemAt(dragIndex);
Select(dragIndex);
}
if (item == NULL)
return false;
BMessage message = *fDragMessage;
message.AddPointer("listview", this);
for (int32 i = 0;; i++) {
int32 index = CurrentSelection(i);
if (index < 0)
break;
message.AddInt32("index", index);
}
BRect dragRect(0.0, 0.0, Bounds().Width(), -1.0);
int32 numItems = 0;
bool fade = false;
for (int32 i = 0, index; message.FindInt32("index", i, &index) == B_OK;
i++) {
BListItem* item = ItemAt(index);
if (item == NULL)
break;
dragRect.bottom += ceilf(item->Height()) + 1.0;
numItems++;
if (dragRect.Height() > MAX_DRAG_HEIGHT) {
dragRect.bottom = MAX_DRAG_HEIGHT;
fade = true;
break;
}
}
BBitmap* dragBitmap = new BBitmap(dragRect, B_RGB32, true);
if (dragBitmap->IsValid()) {
BView* view = new BView(dragBitmap->Bounds(), "helper", B_FOLLOW_NONE,
B_WILL_DRAW);
dragBitmap->AddChild(view);
dragBitmap->Lock();
BRect itemBounds(dragRect) ;
itemBounds.bottom = 0.0;
for (int32 i = 0; i < numItems; i++) {
int32 index = message.FindInt32("index", i);
LanguageListItem* item
= static_cast<LanguageListItem*>(ItemAt(index));
itemBounds.bottom = itemBounds.top + ceilf(item->Height());
if (itemBounds.bottom > dragRect.bottom)
itemBounds.bottom = dragRect.bottom;
item->DrawItem(view, itemBounds);
itemBounds.top = itemBounds.bottom + 1.0;
}
view->SetHighColor(0, 0, 0, 255);
view->StrokeRect(view->Bounds());
view->Sync();
uint8* bits = (uint8*)dragBitmap->Bits();
int32 height = (int32)dragBitmap->Bounds().Height() + 1;
int32 width = (int32)dragBitmap->Bounds().Width() + 1;
int32 bpr = dragBitmap->BytesPerRow();
if (fade) {
for (int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr) {
uint8* line = bits + 3;
for (uint8* end = line + 4 * width; line < end; line += 4)
*line = ALPHA;
}
for (int32 y = height - ALPHA / 2; y < height;
y++, bits += bpr) {
uint8* line = bits + 3;
for (uint8* end = line + 4 * width; line < end; line += 4)
*line = (height - y) << 1;
}
} else {
for (int32 y = 0; y < height; y++, bits += bpr) {
uint8* line = bits + 3;
for (uint8* end = line + 4 * width; line < end; line += 4)
*line = ALPHA;
}
}
dragBitmap->Unlock();
} else {
delete dragBitmap;
dragBitmap = NULL;
}
if (dragBitmap != NULL)
DragMessage(&message, dragBitmap, B_OP_ALPHA, BPoint(0.0, 0.0));
else
DragMessage(&message, dragRect.OffsetToCopy(point), this);
return true;
}
void
LanguageListView::MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage)
{
if (dragMessage != NULL && _AcceptsDragMessage(dragMessage)) {
switch (transit) {
case B_ENTERED_VIEW:
case B_INSIDE_VIEW:
{
BRect highlightFrame;
if (fGlobalDropTargetIndicator) {
highlightFrame = Bounds();
fDropIndex = 0;
} else {
BRect r = ItemFrame(0);
where.y += r.Height() / 2.0;
int32 index = IndexOf(where);
if (index < 0)
index = CountItems();
highlightFrame = ItemFrame(index);
if (highlightFrame.IsValid())
highlightFrame.bottom = highlightFrame.top;
else {
highlightFrame = ItemFrame(index - 1);
if (highlightFrame.IsValid())
highlightFrame.top = highlightFrame.bottom;
else {
highlightFrame = Bounds();
highlightFrame.bottom = highlightFrame.top;
}
}
fDropIndex = index;
}
if (fDropTargetHighlightFrame != highlightFrame) {
Invalidate(fDropTargetHighlightFrame);
fDropTargetHighlightFrame = highlightFrame;
Invalidate(fDropTargetHighlightFrame);
}
BOutlineListView::MouseMoved(where, transit, dragMessage);
return;
}
}
}
if (fDropTargetHighlightFrame.IsValid()) {
Invalidate(fDropTargetHighlightFrame);
fDropTargetHighlightFrame = BRect();
}
BOutlineListView::MouseMoved(where, transit, dragMessage);
}
void
LanguageListView::MouseUp(BPoint point)
{
BOutlineListView::MouseUp(point);
if (fDropTargetHighlightFrame.IsValid()) {
Invalidate(fDropTargetHighlightFrame);
fDropTargetHighlightFrame = BRect();
}
}
void
LanguageListView::KeyDown(const char* bytes, int32 numBytes)
{
if (bytes[0] == B_DELETE && fDeleteMessage != NULL) {
Invoke(fDeleteMessage);
return;
}
BOutlineListView::KeyDown(bytes, numBytes);
}
bool
LanguageListView::_AcceptsDragMessage(const BMessage* message) const
{
LanguageListView* sourceView = NULL;
return message != NULL
&& message->FindPointer("listview", (void**)&sourceView) == B_OK;
}