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 <LocaleRoster.h> 23 #include <Window.h> 24 25 26 #define MAX_DRAG_HEIGHT 200.0 27 #define ALPHA 170 28 29 #undef B_TRANSLATE_CONTEXT 30 #define B_TRANSLATE_CONTEXT "LanguageListView" 31 32 33 static const float kFlagWidth = 17.0; 34 35 LanguageListItem::LanguageListItem(const char* text, const char* id, 36 const char* code, const char* countryCode) 37 : 38 BStringItem(text), 39 fID(id), 40 fCode(code) 41 { 42 fIcon = new(std::nothrow) BBitmap(BRect(0, 0, 15, 15), B_RGBA32); 43 if (fIcon != NULL && BLocaleRoster::Default()->GetFlagIconForCountry(fIcon, 44 countryCode) != B_OK) { 45 delete fIcon; 46 fIcon = NULL; 47 } 48 } 49 50 51 LanguageListItem::LanguageListItem(const LanguageListItem& other) 52 : 53 BStringItem(other.Text()), 54 fID(other.ID()), 55 fCode(other.Code()), 56 fIcon(NULL) 57 { 58 if (other.fIcon != NULL) 59 fIcon = new BBitmap(*other.fIcon); 60 } 61 62 63 LanguageListItem::~LanguageListItem() 64 { 65 delete fIcon; 66 } 67 68 69 void 70 LanguageListItem::DrawItem(BView* owner, BRect frame, bool complete) 71 { 72 rgb_color kHighlight = {140, 140, 140, 0}; 73 rgb_color kBlack = {0, 0, 0, 0}; 74 75 if (IsSelected() || complete) { 76 rgb_color color; 77 if (IsSelected()) 78 color = kHighlight; 79 else 80 color = owner->ViewColor(); 81 owner->SetHighColor(color); 82 owner->SetLowColor(color); 83 owner->FillRect(frame); 84 owner->SetHighColor(kBlack); 85 } else 86 owner->SetLowColor(owner->ViewColor()); 87 88 BString text = Text(); 89 if (IsEnabled()) 90 owner->SetHighColor(kBlack); 91 else { 92 owner->SetHighColor(tint_color(owner->LowColor(), B_DARKEN_3_TINT)); 93 text += " ["; 94 text += B_TRANSLATE("already chosen"); 95 text += "]"; 96 } 97 98 BFont font = be_plain_font; 99 font_height finfo; 100 font.GetHeight(&finfo); 101 owner->SetFont(&font); 102 // TODO: the position is unnecessarily complicated, and not correct either 103 owner->MovePenTo(frame.left + 8, frame.top 104 + (frame.Height() - (finfo.ascent + finfo.descent + finfo.leading)) / 2 105 + (finfo.ascent + finfo.descent) - 1); 106 owner->DrawString(text.String()); 107 108 // Draw the icon 109 frame.left = frame.right - kFlagWidth; 110 BRect iconFrame(frame); 111 iconFrame.Set(iconFrame.left, iconFrame.top + 1, iconFrame.left + kFlagWidth - 2, 112 iconFrame.top + kFlagWidth - 1); 113 114 if (fIcon != NULL && fIcon->IsValid()) { 115 owner->SetDrawingMode(B_OP_OVER); 116 owner->DrawBitmap(fIcon, iconFrame); 117 owner->SetDrawingMode(B_OP_COPY); 118 } 119 120 } 121 122 123 void 124 LanguageListItem::Update(BView* owner, const BFont* font) 125 { 126 BStringItem::Update(owner, font); 127 SetWidth(Width() + kFlagWidth); 128 } 129 130 131 // #pragma mark - 132 133 134 LanguageListView::LanguageListView(const char* name, list_view_type type) 135 : 136 BOutlineListView(name, type), 137 fDropIndex(-1), 138 fDeleteMessage(NULL), 139 fDragMessage(NULL) 140 { 141 } 142 143 144 LanguageListView::~LanguageListView() 145 { 146 } 147 148 149 LanguageListItem* 150 LanguageListView::ItemForLanguageID(const char* id, int32* _index) const 151 { 152 for (int32 index = 0; index < FullListCountItems(); index++) { 153 LanguageListItem* item 154 = static_cast<LanguageListItem*>(FullListItemAt(index)); 155 156 if (item->ID() == id) { 157 if (_index != NULL) 158 *_index = index; 159 return item; 160 } 161 } 162 163 return NULL; 164 } 165 166 167 LanguageListItem* 168 LanguageListView::ItemForLanguageCode(const char* code, int32* _index) const 169 { 170 for (int32 index = 0; index < FullListCountItems(); index++) { 171 LanguageListItem* item 172 = static_cast<LanguageListItem*>(FullListItemAt(index)); 173 174 if (item->Code() == code) { 175 if (_index != NULL) 176 *_index = index; 177 return item; 178 } 179 } 180 181 return NULL; 182 } 183 184 185 void 186 LanguageListView::SetDeleteMessage(BMessage* message) 187 { 188 delete fDeleteMessage; 189 fDeleteMessage = message; 190 } 191 192 193 void 194 LanguageListView::SetDragMessage(BMessage* message) 195 { 196 delete fDragMessage; 197 fDragMessage = message; 198 } 199 200 201 void 202 LanguageListView::AttachedToWindow() 203 { 204 BOutlineListView::AttachedToWindow(); 205 ScrollToSelection(); 206 } 207 208 209 void 210 LanguageListView::MessageReceived(BMessage* message) 211 { 212 if (message->WasDropped() && _AcceptsDragMessage(message)) { 213 // Someone just dropped something on us 214 BMessage dragMessage(*message); 215 dragMessage.AddInt32("drop_index", fDropIndex); 216 dragMessage.AddPointer("drop_target", this); 217 218 Invoke(&dragMessage); 219 } else 220 BOutlineListView::MessageReceived(message); 221 } 222 223 224 bool 225 LanguageListView::InitiateDrag(BPoint point, int32 dragIndex, 226 bool /*wasSelected*/) 227 { 228 if (fDragMessage == NULL) 229 return false; 230 231 BListItem* item = FullListItemAt(CurrentSelection(0)); 232 if (item == NULL) { 233 // workaround for a timing problem 234 // TODO: this should support extending the selection 235 item = ItemAt(dragIndex); 236 Select(dragIndex); 237 } 238 if (item == NULL) 239 return false; 240 241 // create drag message 242 BMessage message = *fDragMessage; 243 message.AddPointer("listview", this); 244 245 for (int32 i = 0;; i++) { 246 int32 index = FullListCurrentSelection(i); 247 if (index < 0) 248 break; 249 250 message.AddInt32("index", index); 251 } 252 253 // figure out drag rect 254 255 BRect dragRect(0.0, 0.0, Bounds().Width(), -1.0); 256 int32 numItems = 0; 257 bool fade = false; 258 259 // figure out, how many items fit into our bitmap 260 for (int32 i = 0, index; message.FindInt32("index", i, &index) == B_OK; 261 i++) { 262 BListItem* item = FullListItemAt(index); 263 if (item == NULL) 264 break; 265 266 dragRect.bottom += ceilf(item->Height()) + 1.0; 267 numItems++; 268 269 if (dragRect.Height() > MAX_DRAG_HEIGHT) { 270 dragRect.bottom = MAX_DRAG_HEIGHT; 271 fade = true; 272 break; 273 } 274 } 275 276 BBitmap* dragBitmap = new BBitmap(dragRect, B_RGB32, true); 277 if (dragBitmap->IsValid()) { 278 BView* view = new BView(dragBitmap->Bounds(), "helper", B_FOLLOW_NONE, 279 B_WILL_DRAW); 280 dragBitmap->AddChild(view); 281 dragBitmap->Lock(); 282 BRect itemBounds(dragRect) ; 283 itemBounds.bottom = 0.0; 284 // let all selected items, that fit into our drag_bitmap, draw 285 for (int32 i = 0; i < numItems; i++) { 286 int32 index = message.FindInt32("index", i); 287 LanguageListItem* item 288 = static_cast<LanguageListItem*>(FullListItemAt(index)); 289 itemBounds.bottom = itemBounds.top + ceilf(item->Height()); 290 if (itemBounds.bottom > dragRect.bottom) 291 itemBounds.bottom = dragRect.bottom; 292 item->DrawItem(view, itemBounds); 293 itemBounds.top = itemBounds.bottom + 1.0; 294 } 295 // make a black frame arround the edge 296 view->SetHighColor(0, 0, 0, 255); 297 view->StrokeRect(view->Bounds()); 298 view->Sync(); 299 300 uint8* bits = (uint8*)dragBitmap->Bits(); 301 int32 height = (int32)dragBitmap->Bounds().Height() + 1; 302 int32 width = (int32)dragBitmap->Bounds().Width() + 1; 303 int32 bpr = dragBitmap->BytesPerRow(); 304 305 if (fade) { 306 for (int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr) { 307 uint8* line = bits + 3; 308 for (uint8* end = line + 4 * width; line < end; line += 4) 309 *line = ALPHA; 310 } 311 for (int32 y = height - ALPHA / 2; y < height; 312 y++, bits += bpr) { 313 uint8* line = bits + 3; 314 for (uint8* end = line + 4 * width; line < end; line += 4) 315 *line = (height - y) << 1; 316 } 317 } else { 318 for (int32 y = 0; y < height; y++, bits += bpr) { 319 uint8* line = bits + 3; 320 for (uint8* end = line + 4 * width; line < end; line += 4) 321 *line = ALPHA; 322 } 323 } 324 dragBitmap->Unlock(); 325 } else { 326 delete dragBitmap; 327 dragBitmap = NULL; 328 } 329 330 if (dragBitmap != NULL) 331 DragMessage(&message, dragBitmap, B_OP_ALPHA, BPoint(0.0, 0.0)); 332 else 333 DragMessage(&message, dragRect.OffsetToCopy(point), this); 334 335 return true; 336 } 337 338 339 void 340 LanguageListView::MouseMoved(BPoint where, uint32 transit, 341 const BMessage* dragMessage) 342 { 343 if (dragMessage != NULL && _AcceptsDragMessage(dragMessage)) { 344 switch (transit) { 345 case B_ENTERED_VIEW: 346 case B_INSIDE_VIEW: 347 { 348 // set drop target through virtual function 349 // offset where by half of item height 350 BRect r = ItemFrame(0); 351 where.y += r.Height() / 2.0; 352 353 int32 index = FullListIndexOf(where); 354 if (index < 0) 355 index = FullListCountItems(); 356 if (fDropIndex != index) { 357 fDropIndex = index; 358 if (fDropIndex >= 0) { 359 // TODO: find out what this was intended for (as it doesn't have any effect) 360 // int32 count = FullListCountItems(); 361 // if (fDropIndex == count) { 362 // BRect r; 363 // if (FullListItemAt(count - 1)) { 364 // r = ItemFrame(count - 1); 365 // r.top = r.bottom; 366 // r.bottom = r.top + 1.0; 367 // } else { 368 // r = Bounds(); 369 // r.bottom--; 370 // // compensate for scrollbars moved slightly 371 // // out of window 372 // } 373 // } else { 374 // BRect r = ItemFrame(fDropIndex); 375 // r.top--; 376 // r.bottom = r.top + 1.0; 377 // } 378 } 379 } 380 break; 381 } 382 } 383 } else 384 BOutlineListView::MouseMoved(where, transit, dragMessage); 385 } 386 387 388 void 389 LanguageListView::KeyDown(const char* bytes, int32 numBytes) 390 { 391 if (bytes[0] == B_DELETE && fDeleteMessage != NULL) { 392 Invoke(fDeleteMessage); 393 return; 394 } 395 396 BOutlineListView::KeyDown(bytes, numBytes); 397 } 398 399 400 bool 401 LanguageListView::_AcceptsDragMessage(const BMessage* message) const 402 { 403 LanguageListView* sourceView = NULL; 404 return message != NULL 405 && message->FindPointer("listview", (void**)&sourceView) == B_OK; 406 } 407