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