xref: /haiku/src/preferences/locale/LanguageListView.cpp (revision d374a27286b8a52974a97dba0d5966ea026a665d)
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