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