1/*
2 * Copyright 2004-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Alexander von Gluck IV, kallisti5@unixzen.com
7 *		Philippe Houdoin
8 * 		Fredrik Mod��en
9 *		John Scipione, jscipione@gmail.com
10 */
11
12
13#include "InterfaceListItem.h"
14
15#include <algorithm>
16
17#include <Application.h>
18#include <Bitmap.h>
19#include <Catalog.h>
20#include <ControlLook.h>
21#include <IconUtils.h>
22#include <OutlineListView.h>
23#include <Resources.h>
24#include <String.h>
25
26
27#undef B_TRANSLATION_CONTEXT
28#define B_TRANSLATION_CONTEXT "InterfaceListItem"
29
30
31InterfaceListItem::InterfaceListItem(const char* name,
32	BNetworkInterfaceType type)
33	:
34	BListItem(0, false),
35	fType(type),
36	fIcon(NULL),
37	fIconOffline(NULL),
38	fIconPending(NULL),
39	fIconOnline(NULL),
40	fFirstLineOffset(0),
41	fLineOffset(0),
42	fDisabled(false),
43	fHasLink(false),
44	fConnecting(false)
45{
46	fInterface.SetTo(name);
47	_Init();
48}
49
50
51InterfaceListItem::~InterfaceListItem()
52{
53	delete fIcon;
54	delete fIconOffline;
55	delete fIconPending;
56	delete fIconOnline;
57}
58
59
60// #pragma mark - InterfaceListItem public methods
61
62
63void
64InterfaceListItem::DrawItem(BView* owner, BRect bounds, bool complete)
65{
66	owner->PushState();
67
68	rgb_color lowColor = owner->LowColor();
69
70	if (IsSelected() || complete) {
71		if (IsSelected()) {
72			owner->SetHighColor(ui_color(B_LIST_SELECTED_BACKGROUND_COLOR));
73			owner->SetLowColor(owner->HighColor());
74		} else
75			owner->SetHighColor(lowColor);
76
77		owner->FillRect(bounds);
78	}
79
80	BBitmap* stateIcon = _StateIcon();
81	const int32 stateIconWidth = stateIcon->Bounds().IntegerWidth() + 1;
82	const char* stateText = _StateText();
83
84	// Set the initial bounds of item contents
85	BPoint iconPoint = bounds.LeftTop()
86		+ BPoint(be_control_look->DefaultLabelSpacing(), 2);
87	BPoint statePoint = bounds.RightTop() + BPoint(0, fFirstLineOffset)
88		- BPoint(be_plain_font->StringWidth(stateText)
89			+ be_control_look->DefaultLabelSpacing(), 0);
90	BPoint namePoint = bounds.LeftTop()
91		+ BPoint(stateIconWidth	+ (be_control_look->DefaultLabelSpacing() * 2),
92			fFirstLineOffset);
93
94	if (fDisabled) {
95		owner->SetDrawingMode(B_OP_ALPHA);
96		owner->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
97		owner->SetHighColor(0, 0, 0, 32);
98	} else
99		owner->SetDrawingMode(B_OP_OVER);
100
101	owner->DrawBitmapAsync(fIcon, iconPoint);
102	owner->DrawBitmapAsync(stateIcon, iconPoint);
103
104	if (fDisabled) {
105		rgb_color textColor;
106		if (IsSelected())
107			textColor = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR);
108		else
109			textColor = ui_color(B_LIST_ITEM_TEXT_COLOR);
110
111		if (textColor.red + textColor.green + textColor.blue > 128 * 3)
112			owner->SetHighColor(tint_color(textColor, B_DARKEN_1_TINT));
113		else
114			owner->SetHighColor(tint_color(textColor, B_LIGHTEN_1_TINT));
115	} else {
116		if (IsSelected())
117			owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
118		else
119			owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
120	}
121
122	owner->SetFont(be_bold_font);
123
124	owner->DrawString(fDeviceName, namePoint);
125	owner->SetFont(be_plain_font);
126	owner->DrawString(stateText, statePoint);
127
128	BPoint linePoint = bounds.LeftTop()
129		+ BPoint(stateIconWidth + (be_control_look->DefaultLabelSpacing() * 2),
130		fFirstLineOffset + fLineOffset);
131	owner->DrawString(fSubtitle, linePoint);
132
133	owner->PopState();
134}
135
136
137void
138InterfaceListItem::Update(BView* owner, const BFont* font)
139{
140	BListItem::Update(owner, font);
141	font_height height;
142	font->GetHeight(&height);
143
144	float lineHeight = ceilf(height.ascent) + ceilf(height.descent)
145		+ ceilf(height.leading);
146
147	fFirstLineOffset = 2 + ceilf(height.ascent + height.leading / 2);
148	fLineOffset = lineHeight;
149
150	_UpdateState();
151
152	SetWidth(fIcon->Bounds().Width() + 36
153		+ be_control_look->DefaultLabelSpacing()
154		+ be_bold_font->StringWidth(fDeviceName.String())
155		+ owner->StringWidth(_StateText()));
156	SetHeight(std::max(2 * lineHeight + 4, fIcon->Bounds().Height() + 4));
157		// either to the text height or icon height, whichever is taller
158}
159
160
161void
162InterfaceListItem::ConfigurationUpdated(const BMessage& message)
163{
164	_UpdateState();
165}
166
167
168// #pragma mark - InterfaceListItem private methods
169
170
171void
172InterfaceListItem::_Init()
173{
174	switch (fType) {
175		case B_NETWORK_INTERFACE_TYPE_WIFI:
176			_PopulateBitmaps("wifi");
177			break;
178		case B_NETWORK_INTERFACE_TYPE_ETHERNET:
179			_PopulateBitmaps("ether");
180			break;
181		case B_NETWORK_INTERFACE_TYPE_VPN:
182			_PopulateBitmaps("vpn");
183			break;
184		case B_NETWORK_INTERFACE_TYPE_DIAL_UP:
185			_PopulateBitmaps("dialup");
186			break;
187		default:
188			_PopulateBitmaps(NULL);
189			break;
190	}
191}
192
193
194void
195InterfaceListItem::_PopulateBitmaps(const char* mediaType)
196{
197	const uint8* interfaceHVIF;
198	const uint8* offlineHVIF;
199	const uint8* pendingHVIF;
200	const uint8* onlineHVIF;
201
202	BBitmap* interfaceBitmap = NULL;
203	BBitmap* offlineBitmap = NULL;
204	BBitmap* pendingBitmap = NULL;
205	BBitmap* onlineBitmap = NULL;
206
207	BResources* resources = BApplication::AppResources();
208
209	size_t iconDataSize;
210
211	// Try specific interface icon?
212	interfaceHVIF = (const uint8*)resources->LoadResource(
213		B_VECTOR_ICON_TYPE, Name(), &iconDataSize);
214
215	if (interfaceHVIF == NULL && mediaType != NULL)
216		// Not found, try interface media type?
217		interfaceHVIF = (const uint8*)resources->LoadResource(
218			B_VECTOR_ICON_TYPE, mediaType, &iconDataSize);
219	if (interfaceHVIF == NULL)
220		// Not found, try default interface icon?
221		interfaceHVIF = (const uint8*)resources->LoadResource(
222			B_VECTOR_ICON_TYPE, "ether", &iconDataSize);
223
224	const BSize iconSize = be_control_look->ComposeIconSize(B_LARGE_ICON);
225	if (interfaceHVIF != NULL) {
226		// Now build the bitmap
227		interfaceBitmap = new(std::nothrow) BBitmap(
228			BRect(BPoint(0, 0), iconSize), 0, B_RGBA32);
229		if (BIconUtils::GetVectorIcon(interfaceHVIF,
230				iconDataSize, interfaceBitmap) == B_OK)
231			fIcon = interfaceBitmap;
232		else
233			delete interfaceBitmap;
234	}
235
236	// Load possible state icons
237	offlineHVIF = (const uint8*)resources->LoadResource(
238		B_VECTOR_ICON_TYPE, "offline", &iconDataSize);
239
240	if (offlineHVIF != NULL) {
241		offlineBitmap = new(std::nothrow) BBitmap(
242			BRect(BPoint(0, 0), iconSize), 0, B_RGBA32);
243		if (BIconUtils::GetVectorIcon(offlineHVIF,
244				iconDataSize, offlineBitmap) == B_OK)
245			fIconOffline = offlineBitmap;
246		else
247			delete offlineBitmap;
248	}
249
250	pendingHVIF = (const uint8*)resources->LoadResource(
251		B_VECTOR_ICON_TYPE, "pending", &iconDataSize);
252
253	if (pendingHVIF != NULL) {
254		pendingBitmap = new(std::nothrow) BBitmap(
255			BRect(BPoint(0, 0), iconSize), 0, B_RGBA32);
256		if (BIconUtils::GetVectorIcon(pendingHVIF,
257				iconDataSize, pendingBitmap) == B_OK)
258			fIconPending = pendingBitmap;
259		else
260			delete pendingBitmap;
261	}
262
263	onlineHVIF = (const uint8*)resources->LoadResource(
264		B_VECTOR_ICON_TYPE, "online", &iconDataSize);
265
266	if (onlineHVIF != NULL) {
267		onlineBitmap = new(std::nothrow) BBitmap(
268			BRect(BPoint(0, 0), iconSize), 0, B_RGBA32);
269		if (BIconUtils::GetVectorIcon(onlineHVIF,
270				iconDataSize, onlineBitmap) == B_OK)
271			fIconOnline = onlineBitmap;
272		else
273			delete onlineBitmap;
274	}
275}
276
277
278void
279InterfaceListItem::_UpdateState()
280{
281	fDeviceName = Name();
282	fDeviceName.RemoveFirst("/dev/net/");
283
284	fDisabled = (fInterface.Flags() & IFF_UP) == 0;
285	fHasLink = fInterface.HasLink();
286	fConnecting = (fInterface.Flags() & IFF_CONFIGURING) != 0;
287
288	switch (fType) {
289		case B_NETWORK_INTERFACE_TYPE_WIFI:
290			fSubtitle = B_TRANSLATE("Wireless device");
291			break;
292		case B_NETWORK_INTERFACE_TYPE_ETHERNET:
293			fSubtitle = B_TRANSLATE("Ethernet device");
294			break;
295		case B_NETWORK_INTERFACE_TYPE_DIAL_UP:
296			fSubtitle = B_TRANSLATE("Dial-up connection");
297			fDisabled = false;
298			break;
299		case B_NETWORK_INTERFACE_TYPE_VPN:
300			fSubtitle = B_TRANSLATE("VPN connection");
301			fDisabled = false;
302			break;
303		default:
304			fSubtitle = "";
305	}
306}
307
308
309BBitmap*
310InterfaceListItem::_StateIcon() const
311{
312	if (fDisabled)
313		return fIconOffline;
314	if (!fHasLink)
315		return fIconOffline;
316	// TODO!
317//	} else if ((fSettings->IPAddr(AF_INET).IsEmpty()
318//		&& fSettings->IPAddr(AF_INET6).IsEmpty())
319//		&& (fSettings->AutoConfigure(AF_INET)
320//		|| fSettings->AutoConfigure(AF_INET6))) {
321//		interfaceState = "connecting" B_UTF8_ELLIPSIS;
322//		stateIcon = fIconPending;
323
324	return fIconOnline;
325}
326
327
328const char*
329InterfaceListItem::_StateText() const
330{
331	if (fDisabled)
332		return B_TRANSLATE("disabled");
333
334	if (!fInterface.HasLink()) {
335		switch (fType) {
336			case B_NETWORK_INTERFACE_TYPE_VPN:
337			case B_NETWORK_INTERFACE_TYPE_DIAL_UP:
338				return B_TRANSLATE("disconnected");
339			default:
340				return B_TRANSLATE("no link");
341		}
342	}
343
344	// TODO!
345//	} else if ((fSettings->IPAddr(AF_INET).IsEmpty()
346//		&& fSettings->IPAddr(AF_INET6).IsEmpty())
347//		&& (fSettings->AutoConfigure(AF_INET)
348//		|| fSettings->AutoConfigure(AF_INET6))) {
349//		interfaceState = "connecting" B_UTF8_ELLIPSIS;
350//		stateIcon = fIconPending;
351
352	return B_TRANSLATE("connected");
353}
354