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