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 36 LanguageListItem::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 46 LanguageListItem::LanguageListItem(const LanguageListItem& other) 47 : 48 BStringItem(other.Text()), 49 fID(other.fID), 50 fCode(other.fCode) 51 { 52 } 53 54 55 void 56 LanguageListItem::DrawItem(BView* owner, BRect frame, bool complete) 57 { 58 DrawItemWithTextOffset(owner, frame, complete, 0); 59 } 60 61 62 void 63 LanguageListItem::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 111 LanguageListItemWithFlag::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 121 LanguageListItemWithFlag::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 131 LanguageListItemWithFlag::~LanguageListItemWithFlag() 132 { 133 delete fIcon; 134 } 135 136 137 void 138 LanguageListItemWithFlag::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 158 void 159 LanguageListItemWithFlag::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 183 LanguageListView::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 195 LanguageListView::~LanguageListView() 196 { 197 } 198 199 200 LanguageListItem* 201 LanguageListView::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 218 LanguageListItem* 219 LanguageListView::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 236 void 237 LanguageListView::SetDeleteMessage(BMessage* message) 238 { 239 delete fDeleteMessage; 240 fDeleteMessage = message; 241 } 242 243 244 void 245 LanguageListView::SetDragMessage(BMessage* message) 246 { 247 delete fDragMessage; 248 fDragMessage = message; 249 } 250 251 252 void 253 LanguageListView::SetGlobalDropTargetIndicator(bool isGlobal) 254 { 255 fGlobalDropTargetIndicator = isGlobal; 256 } 257 258 259 void 260 LanguageListView::AttachedToWindow() 261 { 262 BOutlineListView::AttachedToWindow(); 263 ScrollToSelection(); 264 } 265 266 267 void 268 LanguageListView::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 281 void 282 LanguageListView::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 309 bool 310 LanguageListView::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 424 void 425 LanguageListView::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 482 void 483 LanguageListView::MouseUp(BPoint point) 484 { 485 BOutlineListView::MouseUp(point); 486 if (fDropTargetHighlightFrame.IsValid()) { 487 Invalidate(fDropTargetHighlightFrame); 488 fDropTargetHighlightFrame = BRect(); 489 } 490 } 491 492 493 void 494 LanguageListView::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 505 bool 506 LanguageListView::_AcceptsDragMessage(const BMessage* message) const 507 { 508 LanguageListView* sourceView = NULL; 509 return message != NULL 510 && message->FindPointer("listview", (void**)&sourceView) == B_OK; 511 } 512