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