1/* 2 * Copyright 2006-2010, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan A��mus <superstippi@gmx.de> 7 * Adrien Destugues <pulkomandy@gmail.com> 8 * Axel D��rfler, axeld@pinc-software.de 9 * Oliver Tappe <zooey@hirschkaefer.de> 10 */ 11 12 13#include "LanguageListView.h" 14 15#include <stdio.h> 16 17#include <new> 18 19#include <Bitmap.h> 20#include <Catalog.h> 21#include <ControlLook.h> 22#include <FormattingConventions.h> 23#include <GradientLinear.h> 24#include <LocaleRoster.h> 25#include <Region.h> 26#include <Window.h> 27 28 29#define MAX_DRAG_HEIGHT 200.0 30#define ALPHA 170 31 32#undef B_TRANSLATION_CONTEXT 33#define B_TRANSLATION_CONTEXT "LanguageListView" 34 35 36LanguageListItem::LanguageListItem(const char* text, const char* id, 37 const char* languageCode) 38 : 39 BStringItem(text), 40 fID(id), 41 fCode(languageCode) 42{ 43} 44 45 46LanguageListItem::LanguageListItem(const LanguageListItem& other) 47 : 48 BStringItem(other.Text()), 49 fID(other.fID), 50 fCode(other.fCode) 51{ 52} 53 54 55void 56LanguageListItem::DrawItem(BView* owner, BRect frame, bool complete) 57{ 58 DrawItemWithTextOffset(owner, frame, complete, 0); 59} 60 61 62void 63LanguageListItem::DrawItemWithTextOffset(BView* owner, BRect frame, 64 bool complete, float textOffset) 65{ 66 rgb_color highColor = owner->HighColor(); 67 rgb_color lowColor = owner->LowColor(); 68 69 if (IsSelected() || complete) { 70 rgb_color color; 71 if (IsSelected()) 72 color = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR); 73 else 74 color = owner->ViewColor(); 75 76 owner->SetHighColor(color); 77 owner->SetLowColor(color); 78 owner->FillRect(frame); 79 } else 80 owner->SetLowColor(owner->ViewColor()); 81 82 BString text = Text(); 83 if (!IsEnabled()) { 84 rgb_color textColor = ui_color(B_LIST_ITEM_TEXT_COLOR); 85 if (textColor.red + textColor.green + textColor.blue > 128 * 3) 86 owner->SetHighColor(tint_color(textColor, B_DARKEN_2_TINT)); 87 else 88 owner->SetHighColor(tint_color(textColor, B_LIGHTEN_2_TINT)); 89 90 text << " [" << B_TRANSLATE("already chosen") << "]"; 91 } else { 92 if (IsSelected()) 93 owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR)); 94 else 95 owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR)); 96 } 97 98 owner->MovePenTo( 99 frame.left + be_control_look->DefaultLabelSpacing() + textOffset, 100 frame.top + BaselineOffset()); 101 owner->DrawString(text.String()); 102 103 owner->SetHighColor(highColor); 104 owner->SetLowColor(lowColor); 105} 106 107 108// #pragma mark - 109 110 111LanguageListItemWithFlag::LanguageListItemWithFlag(const char* text, 112 const char* id, const char* languageCode, const char* countryCode) 113 : 114 LanguageListItem(text, id, languageCode), 115 fCountryCode(countryCode), 116 fIcon(NULL) 117{ 118} 119 120 121LanguageListItemWithFlag::LanguageListItemWithFlag( 122 const LanguageListItemWithFlag& other) 123 : 124 LanguageListItem(other), 125 fCountryCode(other.fCountryCode), 126 fIcon(other.fIcon != NULL ? new BBitmap(*other.fIcon) : NULL) 127{ 128} 129 130 131LanguageListItemWithFlag::~LanguageListItemWithFlag() 132{ 133 delete fIcon; 134} 135 136 137void 138LanguageListItemWithFlag::Update(BView* owner, const BFont* font) 139{ 140 LanguageListItem::Update(owner, font); 141 142 float iconSize = Height(); 143 SetWidth(Width() + iconSize + be_control_look->DefaultLabelSpacing()); 144 145 if (fCountryCode.IsEmpty()) 146 return; 147 148 fIcon = new(std::nothrow) BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1), 149 B_RGBA32); 150 if (fIcon != NULL && BLocaleRoster::Default()->GetFlagIconForCountry(fIcon, 151 fCountryCode.String()) != B_OK) { 152 delete fIcon; 153 fIcon = NULL; 154 } 155} 156 157 158void 159LanguageListItemWithFlag::DrawItem(BView* owner, BRect frame, bool complete) 160{ 161 if (fIcon == NULL || !fIcon->IsValid()) { 162 DrawItemWithTextOffset(owner, frame, complete, 0); 163 return; 164 } 165 166 float iconSize = fIcon->Bounds().Width(); 167 DrawItemWithTextOffset(owner, frame, complete, 168 iconSize + be_control_look->DefaultLabelSpacing()); 169 170 BRect iconFrame(frame.left + be_control_look->DefaultLabelSpacing(), 171 frame.top, 172 frame.left + iconSize - 1 + be_control_look->DefaultLabelSpacing(), 173 frame.top + iconSize - 1); 174 owner->SetDrawingMode(B_OP_OVER); 175 owner->DrawBitmap(fIcon, iconFrame); 176 owner->SetDrawingMode(B_OP_COPY); 177} 178 179 180// #pragma mark - 181 182 183LanguageListView::LanguageListView(const char* name, list_view_type type) 184 : 185 BOutlineListView(name, type), 186 fDropIndex(-1), 187 fDropTargetHighlightFrame(), 188 fGlobalDropTargetIndicator(false), 189 fDeleteMessage(NULL), 190 fDragMessage(NULL) 191{ 192} 193 194 195LanguageListView::~LanguageListView() 196{ 197} 198 199 200LanguageListItem* 201LanguageListView::ItemForLanguageID(const char* id, int32* _index) const 202{ 203 for (int32 index = 0; index < FullListCountItems(); index++) { 204 LanguageListItem* item 205 = static_cast<LanguageListItem*>(FullListItemAt(index)); 206 207 if (item->ID() == id) { 208 if (_index != NULL) 209 *_index = index; 210 return item; 211 } 212 } 213 214 return NULL; 215} 216 217 218LanguageListItem* 219LanguageListView::ItemForLanguageCode(const char* code, int32* _index) const 220{ 221 for (int32 index = 0; index < FullListCountItems(); index++) { 222 LanguageListItem* item 223 = static_cast<LanguageListItem*>(FullListItemAt(index)); 224 225 if (item->Code() == code) { 226 if (_index != NULL) 227 *_index = index; 228 return item; 229 } 230 } 231 232 return NULL; 233} 234 235 236void 237LanguageListView::SetDeleteMessage(BMessage* message) 238{ 239 delete fDeleteMessage; 240 fDeleteMessage = message; 241} 242 243 244void 245LanguageListView::SetDragMessage(BMessage* message) 246{ 247 delete fDragMessage; 248 fDragMessage = message; 249} 250 251 252void 253LanguageListView::SetGlobalDropTargetIndicator(bool isGlobal) 254{ 255 fGlobalDropTargetIndicator = isGlobal; 256} 257 258 259void 260LanguageListView::AttachedToWindow() 261{ 262 BOutlineListView::AttachedToWindow(); 263 ScrollToSelection(); 264} 265 266 267void 268LanguageListView::MessageReceived(BMessage* message) 269{ 270 if (message->WasDropped() && _AcceptsDragMessage(message)) { 271 // Someone just dropped something on us 272 BMessage dragMessage(*message); 273 dragMessage.AddInt32("drop_index", fDropIndex); 274 dragMessage.AddPointer("drop_target", this); 275 Messenger().SendMessage(&dragMessage); 276 } else 277 BOutlineListView::MessageReceived(message); 278} 279 280 281void 282LanguageListView::Draw(BRect updateRect) 283{ 284 BOutlineListView::Draw(updateRect); 285 286 if (fDropIndex >= 0 && fDropTargetHighlightFrame.IsValid()) { 287 // TODO: decide if drawing of a drop target indicator should be moved 288 // into ControlLook 289 BGradientLinear gradient; 290 int step = fGlobalDropTargetIndicator ? 64 : 128; 291 for (int i = 0; i < 256; i += step) 292 gradient.AddColor(i % (step * 2) == 0 293 ? ViewColor() : ui_color(B_CONTROL_HIGHLIGHT_COLOR), i); 294 gradient.AddColor(ViewColor(), 255); 295 gradient.SetStart(fDropTargetHighlightFrame.LeftTop()); 296 gradient.SetEnd(fDropTargetHighlightFrame.RightBottom()); 297 if (fGlobalDropTargetIndicator) { 298 BRegion region(fDropTargetHighlightFrame); 299 region.Exclude(fDropTargetHighlightFrame.InsetByCopy(2.0, 2.0)); 300 ConstrainClippingRegion(®ion); 301 FillRect(fDropTargetHighlightFrame, gradient); 302 ConstrainClippingRegion(NULL); 303 } else 304 FillRect(fDropTargetHighlightFrame, gradient); 305 } 306} 307 308 309bool 310LanguageListView::InitiateDrag(BPoint point, int32 dragIndex, 311 bool /*wasSelected*/) 312{ 313 if (fDragMessage == NULL) 314 return false; 315 316 BListItem* item = ItemAt(CurrentSelection(0)); 317 if (item == NULL) { 318 // workaround for a timing problem 319 // TODO: this should support extending the selection 320 item = ItemAt(dragIndex); 321 Select(dragIndex); 322 } 323 if (item == NULL) 324 return false; 325 326 // create drag message 327 BMessage message = *fDragMessage; 328 message.AddPointer("listview", this); 329 330 for (int32 i = 0;; i++) { 331 int32 index = CurrentSelection(i); 332 if (index < 0) 333 break; 334 335 message.AddInt32("index", index); 336 } 337 338 // figure out drag rect 339 340 BRect dragRect(0.0, 0.0, Bounds().Width(), -1.0); 341 int32 numItems = 0; 342 bool fade = false; 343 344 // figure out, how many items fit into our bitmap 345 for (int32 i = 0, index; message.FindInt32("index", i, &index) == B_OK; 346 i++) { 347 BListItem* item = ItemAt(index); 348 if (item == NULL) 349 break; 350 351 dragRect.bottom += ceilf(item->Height()) + 1.0; 352 numItems++; 353 354 if (dragRect.Height() > MAX_DRAG_HEIGHT) { 355 dragRect.bottom = MAX_DRAG_HEIGHT; 356 fade = true; 357 break; 358 } 359 } 360 361 BBitmap* dragBitmap = new BBitmap(dragRect, B_RGB32, true); 362 if (dragBitmap->IsValid()) { 363 BView* view = new BView(dragBitmap->Bounds(), "helper", B_FOLLOW_NONE, 364 B_WILL_DRAW); 365 dragBitmap->AddChild(view); 366 dragBitmap->Lock(); 367 BRect itemBounds(dragRect) ; 368 itemBounds.bottom = 0.0; 369 // let all selected items, that fit into our drag_bitmap, draw 370 for (int32 i = 0; i < numItems; i++) { 371 int32 index = message.FindInt32("index", i); 372 LanguageListItem* item 373 = static_cast<LanguageListItem*>(ItemAt(index)); 374 itemBounds.bottom = itemBounds.top + ceilf(item->Height()); 375 if (itemBounds.bottom > dragRect.bottom) 376 itemBounds.bottom = dragRect.bottom; 377 item->DrawItem(view, itemBounds); 378 itemBounds.top = itemBounds.bottom + 1.0; 379 } 380 // make a black frame around the edge 381 view->SetHighColor(0, 0, 0, 255); 382 view->StrokeRect(view->Bounds()); 383 view->Sync(); 384 385 uint8* bits = (uint8*)dragBitmap->Bits(); 386 int32 height = (int32)dragBitmap->Bounds().Height() + 1; 387 int32 width = (int32)dragBitmap->Bounds().Width() + 1; 388 int32 bpr = dragBitmap->BytesPerRow(); 389 390 if (fade) { 391 for (int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr) { 392 uint8* line = bits + 3; 393 for (uint8* end = line + 4 * width; line < end; line += 4) 394 *line = ALPHA; 395 } 396 for (int32 y = height - ALPHA / 2; y < height; 397 y++, bits += bpr) { 398 uint8* line = bits + 3; 399 for (uint8* end = line + 4 * width; line < end; line += 4) 400 *line = (height - y) << 1; 401 } 402 } else { 403 for (int32 y = 0; y < height; y++, bits += bpr) { 404 uint8* line = bits + 3; 405 for (uint8* end = line + 4 * width; line < end; line += 4) 406 *line = ALPHA; 407 } 408 } 409 dragBitmap->Unlock(); 410 } else { 411 delete dragBitmap; 412 dragBitmap = NULL; 413 } 414 415 if (dragBitmap != NULL) 416 DragMessage(&message, dragBitmap, B_OP_ALPHA, BPoint(0.0, 0.0)); 417 else 418 DragMessage(&message, dragRect.OffsetToCopy(point), this); 419 420 return true; 421} 422 423 424void 425LanguageListView::MouseMoved(BPoint where, uint32 transit, 426 const BMessage* dragMessage) 427{ 428 if (dragMessage != NULL && _AcceptsDragMessage(dragMessage)) { 429 switch (transit) { 430 case B_ENTERED_VIEW: 431 case B_INSIDE_VIEW: 432 { 433 BRect highlightFrame; 434 435 if (fGlobalDropTargetIndicator) { 436 highlightFrame = Bounds(); 437 fDropIndex = 0; 438 } else { 439 // offset where by half of item height 440 BRect r = ItemFrame(0); 441 where.y += r.Height() / 2.0; 442 443 int32 index = IndexOf(where); 444 if (index < 0) 445 index = CountItems(); 446 highlightFrame = ItemFrame(index); 447 if (highlightFrame.IsValid()) 448 highlightFrame.bottom = highlightFrame.top; 449 else { 450 highlightFrame = ItemFrame(index - 1); 451 if (highlightFrame.IsValid()) 452 highlightFrame.top = highlightFrame.bottom; 453 else { 454 // empty view, show indicator at top 455 highlightFrame = Bounds(); 456 highlightFrame.bottom = highlightFrame.top; 457 } 458 } 459 fDropIndex = index; 460 } 461 462 if (fDropTargetHighlightFrame != highlightFrame) { 463 Invalidate(fDropTargetHighlightFrame); 464 fDropTargetHighlightFrame = highlightFrame; 465 Invalidate(fDropTargetHighlightFrame); 466 } 467 468 BOutlineListView::MouseMoved(where, transit, dragMessage); 469 return; 470 } 471 } 472 } 473 474 if (fDropTargetHighlightFrame.IsValid()) { 475 Invalidate(fDropTargetHighlightFrame); 476 fDropTargetHighlightFrame = BRect(); 477 } 478 BOutlineListView::MouseMoved(where, transit, dragMessage); 479} 480 481 482void 483LanguageListView::MouseUp(BPoint point) 484{ 485 BOutlineListView::MouseUp(point); 486 if (fDropTargetHighlightFrame.IsValid()) { 487 Invalidate(fDropTargetHighlightFrame); 488 fDropTargetHighlightFrame = BRect(); 489 } 490} 491 492 493void 494LanguageListView::KeyDown(const char* bytes, int32 numBytes) 495{ 496 if (bytes[0] == B_DELETE && fDeleteMessage != NULL) { 497 Invoke(fDeleteMessage); 498 return; 499 } 500 501 BOutlineListView::KeyDown(bytes, numBytes); 502} 503 504 505bool 506LanguageListView::_AcceptsDragMessage(const BMessage* message) const 507{ 508 LanguageListView* sourceView = NULL; 509 return message != NULL 510 && message->FindPointer("listview", (void**)&sourceView) == B_OK; 511} 512