xref: /haiku/src/preferences/locale/LanguageListView.cpp (revision a7dde370f552f5376edbf25046ec9cf2ba8bbd1a)
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 LanguageListItem::LanguageListItem(const char* text, const char* id,
34 	const char* code, const char* countryCode)
35 	:
36 	BStringItem(text),
37 	fID(id),
38 	fCode(code)
39 {
40 	fIcon = new(std::nothrow) BBitmap(BRect(0, 0, 15, 15), B_RGBA32);
41 	if (fIcon != NULL && be_locale_roster->GetFlagIconForCountry(fIcon,
42 			countryCode) != B_OK) {
43 		delete fIcon;
44 		fIcon = NULL;
45 	}
46 }
47 
48 
49 LanguageListItem::LanguageListItem(const LanguageListItem& other)
50 	:
51 	BStringItem(other.Text()),
52 	fID(other.ID()),
53 	fCode(other.Code()),
54 	fIcon(NULL)
55 {
56 	if (other.fIcon != NULL)
57 		fIcon = new BBitmap(*other.fIcon);
58 }
59 
60 
61 LanguageListItem::~LanguageListItem()
62 {
63 	delete fIcon;
64 }
65 
66 
67 void
68 LanguageListItem::DrawItem(BView* owner, BRect frame, bool complete)
69 {
70 	rgb_color kHighlight = {140, 140, 140, 0};
71 	rgb_color kBlack = {0, 0, 0, 0};
72 
73 	if (IsSelected() || complete) {
74 		rgb_color color;
75 		if (IsSelected())
76 			color = kHighlight;
77 		else
78 			color = owner->ViewColor();
79 		owner->SetHighColor(color);
80 		owner->SetLowColor(color);
81 		owner->FillRect(frame);
82 		owner->SetHighColor(kBlack);
83 	} else
84 		owner->SetLowColor(owner->ViewColor());
85 
86 	BString text = Text();
87 	if (IsEnabled())
88 		owner->SetHighColor(kBlack);
89 	else {
90 		owner->SetHighColor(tint_color(owner->LowColor(), B_DARKEN_3_TINT));
91 		text += "   [";
92 		text += B_TRANSLATE("already chosen");
93 		text += "]";
94 	}
95 
96 	BFont font = be_plain_font;
97 	font_height	finfo;
98 	font.GetHeight(&finfo);
99 	owner->SetFont(&font);
100 	// TODO: the position is unnecessarily complicated, and not correct either
101 	owner->MovePenTo(frame.left + 8, frame.top
102 		+ (frame.Height() - (finfo.ascent + finfo.descent + finfo.leading)) / 2
103 		+ (finfo.ascent + finfo.descent) - 1);
104 	owner->DrawString(text.String());
105 
106 	// Draw the icon
107 	frame.left = frame.right - 17;
108 	BRect iconFrame(frame);
109 	iconFrame.Set(iconFrame.left, iconFrame.top + 1, iconFrame.left + 15,
110 		iconFrame.top + 16);
111 
112 	if (fIcon != NULL && fIcon->IsValid()) {
113 		owner->SetDrawingMode(B_OP_OVER);
114 		owner->DrawBitmap(fIcon, iconFrame);
115 		owner->SetDrawingMode(B_OP_COPY);
116 	}
117 
118 }
119 
120 
121 // #pragma mark -
122 
123 
124 LanguageListView::LanguageListView(const char* name, list_view_type type)
125 	:
126 	BOutlineListView(name, type),
127 	fDeleteMessage(NULL),
128 	fDragMessage(NULL)
129 {
130 }
131 
132 
133 LanguageListView::~LanguageListView()
134 {
135 }
136 
137 
138 LanguageListItem*
139 LanguageListView::ItemForLanguageID(const char* id, int32* _index) const
140 {
141 	for (int32 index = 0; index < FullListCountItems(); index++) {
142 		LanguageListItem* item
143 			= static_cast<LanguageListItem*>(FullListItemAt(index));
144 
145 		if (item->ID() == id) {
146 			if (_index != NULL)
147 				*_index = index;
148 			return item;
149 		}
150 	}
151 
152 	return NULL;
153 }
154 
155 
156 LanguageListItem*
157 LanguageListView::ItemForLanguageCode(const char* code, int32* _index) const
158 {
159 	for (int32 index = 0; index < FullListCountItems(); index++) {
160 		LanguageListItem* item
161 			= static_cast<LanguageListItem*>(FullListItemAt(index));
162 
163 		if (item->Code() == code) {
164 			if (_index != NULL)
165 				*_index = index;
166 			return item;
167 		}
168 	}
169 
170 	return NULL;
171 }
172 
173 
174 void
175 LanguageListView::SetDeleteMessage(BMessage* message)
176 {
177 	delete fDeleteMessage;
178 	fDeleteMessage = message;
179 }
180 
181 
182 void
183 LanguageListView::SetDragMessage(BMessage* message)
184 {
185 	delete fDragMessage;
186 	fDragMessage = message;
187 }
188 
189 
190 void
191 LanguageListView::AttachedToWindow()
192 {
193 	BOutlineListView::AttachedToWindow();
194 	ScrollToSelection();
195 }
196 
197 
198 void
199 LanguageListView::MessageReceived(BMessage* message)
200 {
201 	if (message->WasDropped() && _AcceptsDragMessage(message)) {
202 		// Someone just dropped something on us
203 		BMessage dragMessage(*message);
204 		dragMessage.AddInt32("drop_index", fDropIndex);
205 		dragMessage.AddPointer("drop_target", this);
206 
207 		Invoke(&dragMessage);
208 	} else
209 		BOutlineListView::MessageReceived(message);
210 }
211 
212 
213 bool
214 LanguageListView::InitiateDrag(BPoint point, int32 dragIndex,
215 	bool /*wasSelected*/)
216 {
217 	if (fDragMessage == NULL)
218 		return false;
219 
220 	BListItem* item = FullListItemAt(CurrentSelection(0));
221 	if (item == NULL) {
222 		// workaround for a timing problem
223 		// TODO: this should support extending the selection
224 		item = ItemAt(dragIndex);
225 		Select(dragIndex);
226 	}
227 	if (item == NULL)
228 		return false;
229 
230 	// create drag message
231 	BMessage message = *fDragMessage;
232 	message.AddPointer("listview", this);
233 
234 	for (int32 i = 0;; i++) {
235 		int32 index = FullListCurrentSelection(i);
236 		if (index < 0)
237 			break;
238 
239 		message.AddInt32("index", index);
240 	}
241 
242 	// figure out drag rect
243 
244 	BRect dragRect(0.0, 0.0, Bounds().Width(), -1.0);
245 	int32 numItems = 0;
246 	bool fade = false;
247 
248 	// figure out, how many items fit into our bitmap
249 	for (int32 i = 0, index; message.FindInt32("index", i, &index) == B_OK;
250 			i++) {
251 		BListItem* item = FullListItemAt(index);
252 		if (item == NULL)
253 			break;
254 
255 		dragRect.bottom += ceilf(item->Height()) + 1.0;
256 		numItems++;
257 
258 		if (dragRect.Height() > MAX_DRAG_HEIGHT) {
259 			dragRect.bottom = MAX_DRAG_HEIGHT;
260 			fade = true;
261 			break;
262 		}
263 	}
264 
265 	BBitmap* dragBitmap = new BBitmap(dragRect, B_RGB32, true);
266 	if (dragBitmap->IsValid()) {
267 		BView* view = new BView(dragBitmap->Bounds(), "helper", B_FOLLOW_NONE,
268 			B_WILL_DRAW);
269 		dragBitmap->AddChild(view);
270 		dragBitmap->Lock();
271 		BRect itemBounds(dragRect) ;
272 		itemBounds.bottom = 0.0;
273 		// let all selected items, that fit into our drag_bitmap, draw
274 		for (int32 i = 0; i < numItems; i++) {
275 			int32 index = message.FindInt32("index", i);
276 			LanguageListItem* item
277 				= static_cast<LanguageListItem*>(FullListItemAt(index));
278 			itemBounds.bottom = itemBounds.top + ceilf(item->Height());
279 			if (itemBounds.bottom > dragRect.bottom)
280 				itemBounds.bottom = dragRect.bottom;
281 			item->DrawItem(view, itemBounds);
282 			itemBounds.top = itemBounds.bottom + 1.0;
283 		}
284 		// make a black frame arround the edge
285 		view->SetHighColor(0, 0, 0, 255);
286 		view->StrokeRect(view->Bounds());
287 		view->Sync();
288 
289 		uint8* bits = (uint8*)dragBitmap->Bits();
290 		int32 height = (int32)dragBitmap->Bounds().Height() + 1;
291 		int32 width = (int32)dragBitmap->Bounds().Width() + 1;
292 		int32 bpr = dragBitmap->BytesPerRow();
293 
294 		if (fade) {
295 			for (int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr) {
296 				uint8* line = bits + 3;
297 				for (uint8* end = line + 4 * width; line < end; line += 4)
298 					*line = ALPHA;
299 			}
300 			for (int32 y = height - ALPHA / 2; y < height;
301 				y++, bits += bpr) {
302 				uint8* line = bits + 3;
303 				for (uint8* end = line + 4 * width; line < end; line += 4)
304 					*line = (height - y) << 1;
305 			}
306 		} else {
307 			for (int32 y = 0; y < height; y++, bits += bpr) {
308 				uint8* line = bits + 3;
309 				for (uint8* end = line + 4 * width; line < end; line += 4)
310 					*line = ALPHA;
311 			}
312 		}
313 		dragBitmap->Unlock();
314 	} else {
315 		delete dragBitmap;
316 		dragBitmap = NULL;
317 	}
318 
319 	if (dragBitmap != NULL)
320 		DragMessage(&message, dragBitmap, B_OP_ALPHA, BPoint(0.0, 0.0));
321 	else
322 		DragMessage(&message, dragRect.OffsetToCopy(point), this);
323 
324 	return true;
325 }
326 
327 
328 void
329 LanguageListView::MouseMoved(BPoint where, uint32 transit,
330 	const BMessage* dragMessage)
331 {
332 	if (dragMessage != NULL && _AcceptsDragMessage(dragMessage)) {
333 		switch (transit) {
334 			case B_ENTERED_VIEW:
335 			case B_INSIDE_VIEW:
336 			{
337 				// set drop target through virtual function
338 				// offset where by half of item height
339 				BRect r = ItemFrame(0);
340 				where.y += r.Height() / 2.0;
341 
342 				int32 index = FullListIndexOf(where);
343 				if (index < 0)
344 					index = FullListCountItems();
345 				if (fDropIndex != index) {
346 					fDropIndex = index;
347 					if (fDropIndex >= 0) {
348 // TODO: find out what this was intended for (as it doesn't have any effect)
349 //						int32 count = FullListCountItems();
350 //						if (fDropIndex == count) {
351 //							BRect r;
352 //							if (FullListItemAt(count - 1)) {
353 //								r = ItemFrame(count - 1);
354 //								r.top = r.bottom;
355 //								r.bottom = r.top + 1.0;
356 //							} else {
357 //								r = Bounds();
358 //								r.bottom--;
359 //									// compensate for scrollbars moved slightly
360 //									// out of window
361 //							}
362 //						} else {
363 //							BRect r = ItemFrame(fDropIndex);
364 //							r.top--;
365 //							r.bottom = r.top + 1.0;
366 //						}
367 					}
368 				}
369 				break;
370 			}
371 		}
372 	} else
373 		BOutlineListView::MouseMoved(where, transit, dragMessage);
374 }
375 
376 
377 void
378 LanguageListView::KeyDown(const char* bytes, int32 numBytes)
379 {
380 	if (bytes[0] == B_DELETE && fDeleteMessage != NULL) {
381 		Invoke(fDeleteMessage);
382 		return;
383 	}
384 
385 	BOutlineListView::KeyDown(bytes, numBytes);
386 }
387 
388 
389 bool
390 LanguageListView::_AcceptsDragMessage(const BMessage* message) const
391 {
392 	LanguageListView* sourceView = NULL;
393 	return message != NULL
394 		&& message->FindPointer("listview", (void**)&sourceView) == B_OK;
395 }
396