RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / preferences / locale / LanguageListView.cpp
blob0d80e6df3c4681c23d333ebbf26d45e8dba564a7
1 /*
2 * Copyright 2006-2010, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
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>
13 #include "LanguageListView.h"
15 #include <stdio.h>
17 #include <new>
19 #include <Bitmap.h>
20 #include <Catalog.h>
21 #include <ControlLook.h>
22 #include <FormattingConventions.h>
23 #include <GradientLinear.h>
24 #include <LocaleRoster.h>
25 #include <Region.h>
26 #include <Window.h>
29 #define MAX_DRAG_HEIGHT 200.0
30 #define ALPHA 170
32 #undef B_TRANSLATION_CONTEXT
33 #define B_TRANSLATION_CONTEXT "LanguageListView"
36 LanguageListItem::LanguageListItem(const char* text, const char* id,
37 const char* languageCode)
39 BStringItem(text),
40 fID(id),
41 fCode(languageCode)
46 LanguageListItem::LanguageListItem(const LanguageListItem& other)
48 BStringItem(other.Text()),
49 fID(other.fID),
50 fCode(other.fCode)
55 void
56 LanguageListItem::DrawItem(BView* owner, BRect frame, bool complete)
58 DrawItemWithTextOffset(owner, frame, complete, 0);
62 void
63 LanguageListItem::DrawItemWithTextOffset(BView* owner, BRect frame,
64 bool complete, float textOffset)
66 rgb_color highColor = owner->HighColor();
67 rgb_color lowColor = owner->LowColor();
69 if (IsSelected() || complete) {
70 rgb_color color;
71 if (IsSelected())
72 color = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR);
73 else
74 color = owner->ViewColor();
76 owner->SetHighColor(color);
77 owner->SetLowColor(color);
78 owner->FillRect(frame);
79 } else
80 owner->SetLowColor(owner->ViewColor());
82 BString text = Text();
83 if (!IsEnabled()) {
84 rgb_color textColor = ui_color(B_LIST_ITEM_TEXT_COLOR);
85 if (textColor.red + textColor.green + textColor.blue > 128 * 3)
86 owner->SetHighColor(tint_color(textColor, B_DARKEN_2_TINT));
87 else
88 owner->SetHighColor(tint_color(textColor, B_LIGHTEN_2_TINT));
90 text << " [" << B_TRANSLATE("already chosen") << "]";
91 } else {
92 if (IsSelected())
93 owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
94 else
95 owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
98 owner->MovePenTo(
99 frame.left + be_control_look->DefaultLabelSpacing() + textOffset,
100 frame.top + BaselineOffset());
101 owner->DrawString(text.String());
103 owner->SetHighColor(highColor);
104 owner->SetLowColor(lowColor);
108 // #pragma mark -
111 LanguageListItemWithFlag::LanguageListItemWithFlag(const char* text,
112 const char* id, const char* languageCode, const char* countryCode)
114 LanguageListItem(text, id, languageCode),
115 fCountryCode(countryCode),
116 fIcon(NULL)
121 LanguageListItemWithFlag::LanguageListItemWithFlag(
122 const LanguageListItemWithFlag& other)
124 LanguageListItem(other),
125 fCountryCode(other.fCountryCode),
126 fIcon(other.fIcon != NULL ? new BBitmap(*other.fIcon) : NULL)
131 LanguageListItemWithFlag::~LanguageListItemWithFlag()
133 delete fIcon;
137 void
138 LanguageListItemWithFlag::Update(BView* owner, const BFont* font)
140 LanguageListItem::Update(owner, font);
142 float iconSize = Height();
143 SetWidth(Width() + iconSize + be_control_look->DefaultLabelSpacing());
145 if (fCountryCode.IsEmpty())
146 return;
148 fIcon = new(std::nothrow) BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1),
149 B_RGBA32);
150 if (fIcon != NULL && BLocaleRoster::Default()->GetFlagIconForCountry(fIcon,
151 fCountryCode.String()) != B_OK) {
152 delete fIcon;
153 fIcon = NULL;
158 void
159 LanguageListItemWithFlag::DrawItem(BView* owner, BRect frame, bool complete)
161 if (fIcon == NULL || !fIcon->IsValid()) {
162 DrawItemWithTextOffset(owner, frame, complete, 0);
163 return;
166 float iconSize = fIcon->Bounds().Width();
167 DrawItemWithTextOffset(owner, frame, complete,
168 iconSize + be_control_look->DefaultLabelSpacing());
170 BRect iconFrame(frame.left + be_control_look->DefaultLabelSpacing(),
171 frame.top,
172 frame.left + iconSize - 1 + be_control_look->DefaultLabelSpacing(),
173 frame.top + iconSize - 1);
174 owner->SetDrawingMode(B_OP_OVER);
175 owner->DrawBitmap(fIcon, iconFrame);
176 owner->SetDrawingMode(B_OP_COPY);
180 // #pragma mark -
183 LanguageListView::LanguageListView(const char* name, list_view_type type)
185 BOutlineListView(name, type),
186 fDropIndex(-1),
187 fDropTargetHighlightFrame(),
188 fGlobalDropTargetIndicator(false),
189 fDeleteMessage(NULL),
190 fDragMessage(NULL)
195 LanguageListView::~LanguageListView()
200 LanguageListItem*
201 LanguageListView::ItemForLanguageID(const char* id, int32* _index) const
203 for (int32 index = 0; index < FullListCountItems(); index++) {
204 LanguageListItem* item
205 = static_cast<LanguageListItem*>(FullListItemAt(index));
207 if (item->ID() == id) {
208 if (_index != NULL)
209 *_index = index;
210 return item;
214 return NULL;
218 LanguageListItem*
219 LanguageListView::ItemForLanguageCode(const char* code, int32* _index) const
221 for (int32 index = 0; index < FullListCountItems(); index++) {
222 LanguageListItem* item
223 = static_cast<LanguageListItem*>(FullListItemAt(index));
225 if (item->Code() == code) {
226 if (_index != NULL)
227 *_index = index;
228 return item;
232 return NULL;
236 void
237 LanguageListView::SetDeleteMessage(BMessage* message)
239 delete fDeleteMessage;
240 fDeleteMessage = message;
244 void
245 LanguageListView::SetDragMessage(BMessage* message)
247 delete fDragMessage;
248 fDragMessage = message;
252 void
253 LanguageListView::SetGlobalDropTargetIndicator(bool isGlobal)
255 fGlobalDropTargetIndicator = isGlobal;
259 void
260 LanguageListView::AttachedToWindow()
262 BOutlineListView::AttachedToWindow();
263 ScrollToSelection();
267 void
268 LanguageListView::MessageReceived(BMessage* message)
270 if (message->WasDropped() && _AcceptsDragMessage(message)) {
271 // Someone just dropped something on us
272 BMessage dragMessage(*message);
273 dragMessage.AddInt32("drop_index", fDropIndex);
274 dragMessage.AddPointer("drop_target", this);
275 Messenger().SendMessage(&dragMessage);
276 } else
277 BOutlineListView::MessageReceived(message);
281 void
282 LanguageListView::Draw(BRect updateRect)
284 BOutlineListView::Draw(updateRect);
286 if (fDropIndex >= 0 && fDropTargetHighlightFrame.IsValid()) {
287 // TODO: decide if drawing of a drop target indicator should be moved
288 // into ControlLook
289 BGradientLinear gradient;
290 int step = fGlobalDropTargetIndicator ? 64 : 128;
291 for (int i = 0; i < 256; i += step)
292 gradient.AddColor(i % (step * 2) == 0
293 ? ViewColor() : ui_color(B_CONTROL_HIGHLIGHT_COLOR), i);
294 gradient.AddColor(ViewColor(), 255);
295 gradient.SetStart(fDropTargetHighlightFrame.LeftTop());
296 gradient.SetEnd(fDropTargetHighlightFrame.RightBottom());
297 if (fGlobalDropTargetIndicator) {
298 BRegion region(fDropTargetHighlightFrame);
299 region.Exclude(fDropTargetHighlightFrame.InsetByCopy(2.0, 2.0));
300 ConstrainClippingRegion(&region);
301 FillRect(fDropTargetHighlightFrame, gradient);
302 ConstrainClippingRegion(NULL);
303 } else
304 FillRect(fDropTargetHighlightFrame, gradient);
309 bool
310 LanguageListView::InitiateDrag(BPoint point, int32 dragIndex,
311 bool /*wasSelected*/)
313 if (fDragMessage == NULL)
314 return false;
316 BListItem* item = ItemAt(CurrentSelection(0));
317 if (item == NULL) {
318 // workaround for a timing problem
319 // TODO: this should support extending the selection
320 item = ItemAt(dragIndex);
321 Select(dragIndex);
323 if (item == NULL)
324 return false;
326 // create drag message
327 BMessage message = *fDragMessage;
328 message.AddPointer("listview", this);
330 for (int32 i = 0;; i++) {
331 int32 index = CurrentSelection(i);
332 if (index < 0)
333 break;
335 message.AddInt32("index", index);
338 // figure out drag rect
340 BRect dragRect(0.0, 0.0, Bounds().Width(), -1.0);
341 int32 numItems = 0;
342 bool fade = false;
344 // figure out, how many items fit into our bitmap
345 for (int32 i = 0, index; message.FindInt32("index", i, &index) == B_OK;
346 i++) {
347 BListItem* item = ItemAt(index);
348 if (item == NULL)
349 break;
351 dragRect.bottom += ceilf(item->Height()) + 1.0;
352 numItems++;
354 if (dragRect.Height() > MAX_DRAG_HEIGHT) {
355 dragRect.bottom = MAX_DRAG_HEIGHT;
356 fade = true;
357 break;
361 BBitmap* dragBitmap = new BBitmap(dragRect, B_RGB32, true);
362 if (dragBitmap->IsValid()) {
363 BView* view = new BView(dragBitmap->Bounds(), "helper", B_FOLLOW_NONE,
364 B_WILL_DRAW);
365 dragBitmap->AddChild(view);
366 dragBitmap->Lock();
367 BRect itemBounds(dragRect) ;
368 itemBounds.bottom = 0.0;
369 // let all selected items, that fit into our drag_bitmap, draw
370 for (int32 i = 0; i < numItems; i++) {
371 int32 index = message.FindInt32("index", i);
372 LanguageListItem* item
373 = static_cast<LanguageListItem*>(ItemAt(index));
374 itemBounds.bottom = itemBounds.top + ceilf(item->Height());
375 if (itemBounds.bottom > dragRect.bottom)
376 itemBounds.bottom = dragRect.bottom;
377 item->DrawItem(view, itemBounds);
378 itemBounds.top = itemBounds.bottom + 1.0;
380 // make a black frame around the edge
381 view->SetHighColor(0, 0, 0, 255);
382 view->StrokeRect(view->Bounds());
383 view->Sync();
385 uint8* bits = (uint8*)dragBitmap->Bits();
386 int32 height = (int32)dragBitmap->Bounds().Height() + 1;
387 int32 width = (int32)dragBitmap->Bounds().Width() + 1;
388 int32 bpr = dragBitmap->BytesPerRow();
390 if (fade) {
391 for (int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr) {
392 uint8* line = bits + 3;
393 for (uint8* end = line + 4 * width; line < end; line += 4)
394 *line = ALPHA;
396 for (int32 y = height - ALPHA / 2; y < height;
397 y++, bits += bpr) {
398 uint8* line = bits + 3;
399 for (uint8* end = line + 4 * width; line < end; line += 4)
400 *line = (height - y) << 1;
402 } else {
403 for (int32 y = 0; y < height; y++, bits += bpr) {
404 uint8* line = bits + 3;
405 for (uint8* end = line + 4 * width; line < end; line += 4)
406 *line = ALPHA;
409 dragBitmap->Unlock();
410 } else {
411 delete dragBitmap;
412 dragBitmap = NULL;
415 if (dragBitmap != NULL)
416 DragMessage(&message, dragBitmap, B_OP_ALPHA, BPoint(0.0, 0.0));
417 else
418 DragMessage(&message, dragRect.OffsetToCopy(point), this);
420 return true;
424 void
425 LanguageListView::MouseMoved(BPoint where, uint32 transit,
426 const BMessage* dragMessage)
428 if (dragMessage != NULL && _AcceptsDragMessage(dragMessage)) {
429 switch (transit) {
430 case B_ENTERED_VIEW:
431 case B_INSIDE_VIEW:
433 BRect highlightFrame;
435 if (fGlobalDropTargetIndicator) {
436 highlightFrame = Bounds();
437 fDropIndex = 0;
438 } else {
439 // offset where by half of item height
440 BRect r = ItemFrame(0);
441 where.y += r.Height() / 2.0;
443 int32 index = IndexOf(where);
444 if (index < 0)
445 index = CountItems();
446 highlightFrame = ItemFrame(index);
447 if (highlightFrame.IsValid())
448 highlightFrame.bottom = highlightFrame.top;
449 else {
450 highlightFrame = ItemFrame(index - 1);
451 if (highlightFrame.IsValid())
452 highlightFrame.top = highlightFrame.bottom;
453 else {
454 // empty view, show indicator at top
455 highlightFrame = Bounds();
456 highlightFrame.bottom = highlightFrame.top;
459 fDropIndex = index;
462 if (fDropTargetHighlightFrame != highlightFrame) {
463 Invalidate(fDropTargetHighlightFrame);
464 fDropTargetHighlightFrame = highlightFrame;
465 Invalidate(fDropTargetHighlightFrame);
468 BOutlineListView::MouseMoved(where, transit, dragMessage);
469 return;
474 if (fDropTargetHighlightFrame.IsValid()) {
475 Invalidate(fDropTargetHighlightFrame);
476 fDropTargetHighlightFrame = BRect();
478 BOutlineListView::MouseMoved(where, transit, dragMessage);
482 void
483 LanguageListView::MouseUp(BPoint point)
485 BOutlineListView::MouseUp(point);
486 if (fDropTargetHighlightFrame.IsValid()) {
487 Invalidate(fDropTargetHighlightFrame);
488 fDropTargetHighlightFrame = BRect();
493 void
494 LanguageListView::KeyDown(const char* bytes, int32 numBytes)
496 if (bytes[0] == B_DELETE && fDeleteMessage != NULL) {
497 Invoke(fDeleteMessage);
498 return;
501 BOutlineListView::KeyDown(bytes, numBytes);
505 bool
506 LanguageListView::_AcceptsDragMessage(const BMessage* message) const
508 LanguageListView* sourceView = NULL;
509 return message != NULL
510 && message->FindPointer("listview", (void**)&sourceView) == B_OK;