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 && be_locale_roster->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 fDeleteMessage(NULL), 138 fDragMessage(NULL) 139 { 140 } 141 142 143 LanguageListView::~LanguageListView() 144 { 145 } 146 147 148 LanguageListItem* 149 LanguageListView::ItemForLanguageID(const char* id, int32* _index) const 150 { 151 for (int32 index = 0; index < FullListCountItems(); index++) { 152 LanguageListItem* item 153 = static_cast<LanguageListItem*>(FullListItemAt(index)); 154 155 if (item->ID() == id) { 156 if (_index != NULL) 157 *_index = index; 158 return item; 159 } 160 } 161 162 return NULL; 163 } 164 165 166 LanguageListItem* 167 LanguageListView::ItemForLanguageCode(const char* code, int32* _index) const 168 { 169 for (int32 index = 0; index < FullListCountItems(); index++) { 170 LanguageListItem* item 171 = static_cast<LanguageListItem*>(FullListItemAt(index)); 172 173 if (item->Code() == code) { 174 if (_index != NULL) 175 *_index = index; 176 return item; 177 } 178 } 179 180 return NULL; 181 } 182 183 184 void 185 LanguageListView::SetDeleteMessage(BMessage* message) 186 { 187 delete fDeleteMessage; 188 fDeleteMessage = message; 189 } 190 191 192 void 193 LanguageListView::SetDragMessage(BMessage* message) 194 { 195 delete fDragMessage; 196 fDragMessage = message; 197 } 198 199 200 void 201 LanguageListView::AttachedToWindow() 202 { 203 BOutlineListView::AttachedToWindow(); 204 ScrollToSelection(); 205 } 206 207 208 void 209 LanguageListView::MessageReceived(BMessage* message) 210 { 211 if (message->WasDropped() && _AcceptsDragMessage(message)) { 212 // Someone just dropped something on us 213 BMessage dragMessage(*message); 214 dragMessage.AddInt32("drop_index", fDropIndex); 215 dragMessage.AddPointer("drop_target", this); 216 217 Invoke(&dragMessage); 218 } else 219 BOutlineListView::MessageReceived(message); 220 } 221 222 223 bool 224 LanguageListView::InitiateDrag(BPoint point, int32 dragIndex, 225 bool /*wasSelected*/) 226 { 227 if (fDragMessage == NULL) 228 return false; 229 230 BListItem* item = FullListItemAt(CurrentSelection(0)); 231 if (item == NULL) { 232 // workaround for a timing problem 233 // TODO: this should support extending the selection 234 item = ItemAt(dragIndex); 235 Select(dragIndex); 236 } 237 if (item == NULL) 238 return false; 239 240 // create drag message 241 BMessage message = *fDragMessage; 242 message.AddPointer("listview", this); 243 244 for (int32 i = 0;; i++) { 245 int32 index = FullListCurrentSelection(i); 246 if (index < 0) 247 break; 248 249 message.AddInt32("index", index); 250 } 251 252 // figure out drag rect 253 254 BRect dragRect(0.0, 0.0, Bounds().Width(), -1.0); 255 int32 numItems = 0; 256 bool fade = false; 257 258 // figure out, how many items fit into our bitmap 259 for (int32 i = 0, index; message.FindInt32("index", i, &index) == B_OK; 260 i++) { 261 BListItem* item = FullListItemAt(index); 262 if (item == NULL) 263 break; 264 265 dragRect.bottom += ceilf(item->Height()) + 1.0; 266 numItems++; 267 268 if (dragRect.Height() > MAX_DRAG_HEIGHT) { 269 dragRect.bottom = MAX_DRAG_HEIGHT; 270 fade = true; 271 break; 272 } 273 } 274 275 BBitmap* dragBitmap = new BBitmap(dragRect, B_RGB32, true); 276 if (dragBitmap->IsValid()) { 277 BView* view = new BView(dragBitmap->Bounds(), "helper", B_FOLLOW_NONE, 278 B_WILL_DRAW); 279 dragBitmap->AddChild(view); 280 dragBitmap->Lock(); 281 BRect itemBounds(dragRect) ; 282 itemBounds.bottom = 0.0; 283 // let all selected items, that fit into our drag_bitmap, draw 284 for (int32 i = 0; i < numItems; i++) { 285 int32 index = message.FindInt32("index", i); 286 LanguageListItem* item 287 = static_cast<LanguageListItem*>(FullListItemAt(index)); 288 itemBounds.bottom = itemBounds.top + ceilf(item->Height()); 289 if (itemBounds.bottom > dragRect.bottom) 290 itemBounds.bottom = dragRect.bottom; 291 item->DrawItem(view, itemBounds); 292 itemBounds.top = itemBounds.bottom + 1.0; 293 } 294 // make a black frame arround the edge 295 view->SetHighColor(0, 0, 0, 255); 296 view->StrokeRect(view->Bounds()); 297 view->Sync(); 298 299 uint8* bits = (uint8*)dragBitmap->Bits(); 300 int32 height = (int32)dragBitmap->Bounds().Height() + 1; 301 int32 width = (int32)dragBitmap->Bounds().Width() + 1; 302 int32 bpr = dragBitmap->BytesPerRow(); 303 304 if (fade) { 305 for (int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr) { 306 uint8* line = bits + 3; 307 for (uint8* end = line + 4 * width; line < end; line += 4) 308 *line = ALPHA; 309 } 310 for (int32 y = height - ALPHA / 2; y < height; 311 y++, bits += bpr) { 312 uint8* line = bits + 3; 313 for (uint8* end = line + 4 * width; line < end; line += 4) 314 *line = (height - y) << 1; 315 } 316 } else { 317 for (int32 y = 0; y < height; y++, bits += bpr) { 318 uint8* line = bits + 3; 319 for (uint8* end = line + 4 * width; line < end; line += 4) 320 *line = ALPHA; 321 } 322 } 323 dragBitmap->Unlock(); 324 } else { 325 delete dragBitmap; 326 dragBitmap = NULL; 327 } 328 329 if (dragBitmap != NULL) 330 DragMessage(&message, dragBitmap, B_OP_ALPHA, BPoint(0.0, 0.0)); 331 else 332 DragMessage(&message, dragRect.OffsetToCopy(point), this); 333 334 return true; 335 } 336 337 338 void 339 LanguageListView::MouseMoved(BPoint where, uint32 transit, 340 const BMessage* dragMessage) 341 { 342 if (dragMessage != NULL && _AcceptsDragMessage(dragMessage)) { 343 switch (transit) { 344 case B_ENTERED_VIEW: 345 case B_INSIDE_VIEW: 346 { 347 // set drop target through virtual function 348 // offset where by half of item height 349 BRect r = ItemFrame(0); 350 where.y += r.Height() / 2.0; 351 352 int32 index = FullListIndexOf(where); 353 if (index < 0) 354 index = FullListCountItems(); 355 if (fDropIndex != index) { 356 fDropIndex = index; 357 if (fDropIndex >= 0) { 358 // TODO: find out what this was intended for (as it doesn't have any effect) 359 // int32 count = FullListCountItems(); 360 // if (fDropIndex == count) { 361 // BRect r; 362 // if (FullListItemAt(count - 1)) { 363 // r = ItemFrame(count - 1); 364 // r.top = r.bottom; 365 // r.bottom = r.top + 1.0; 366 // } else { 367 // r = Bounds(); 368 // r.bottom--; 369 // // compensate for scrollbars moved slightly 370 // // out of window 371 // } 372 // } else { 373 // BRect r = ItemFrame(fDropIndex); 374 // r.top--; 375 // r.bottom = r.top + 1.0; 376 // } 377 } 378 } 379 break; 380 } 381 } 382 } else 383 BOutlineListView::MouseMoved(where, transit, dragMessage); 384 } 385 386 387 void 388 LanguageListView::KeyDown(const char* bytes, int32 numBytes) 389 { 390 if (bytes[0] == B_DELETE && fDeleteMessage != NULL) { 391 Invoke(fDeleteMessage); 392 return; 393 } 394 395 BOutlineListView::KeyDown(bytes, numBytes); 396 } 397 398 399 bool 400 LanguageListView::_AcceptsDragMessage(const BMessage* message) const 401 { 402 LanguageListView* sourceView = NULL; 403 return message != NULL 404 && message->FindPointer("listview", (void**)&sourceView) == B_OK; 405 } 406