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