2 * Copyright 2006-2013, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
7 * Axel Dörfler, axeld@pinc-software.de
8 * Rene Gollent, rene@gollent.com
9 * Hugo Santos, hugosantos@gmail.com
13 #include "NetworkStatusView.h"
19 #include <arpa/inet.h>
24 #include <sys/socket.h>
25 #include <sys/sockio.h>
28 #include <AboutWindow.h>
30 #include <Application.h>
36 #include <IconUtils.h>
39 #include <MessageRunner.h>
40 #include <NetworkDevice.h>
41 #include <NetworkInterface.h>
42 #include <NetworkRoster.h>
43 #include <PopUpMenu.h>
44 #include <Resources.h>
49 #include "NetworkStatus.h"
50 #include "NetworkStatusIcons.h"
51 #include "RadioView.h"
52 #include "WirelessNetworkMenuItem.h"
55 #undef B_TRANSLATION_CONTEXT
56 #define B_TRANSLATION_CONTEXT "NetworkStatusView"
59 static const char *kStatusDescriptions
[] = {
60 B_TRANSLATE("Unknown"),
61 B_TRANSLATE("No link"),
62 B_TRANSLATE("No stateful configuration"),
63 B_TRANSLATE("Configuring"),
67 extern "C" _EXPORT BView
*instantiate_deskbar_item(void);
70 const uint32 kMsgShowConfiguration
= 'shcf';
71 const uint32 kMsgOpenNetworkPreferences
= 'onwp';
72 const uint32 kMsgJoinNetwork
= 'join';
74 const uint32 kMinIconWidth
= 16;
75 const uint32 kMinIconHeight
= 16;
82 signal_strength_compare(const wireless_network
&a
,
83 const wireless_network
&b
)
85 if (a
.signal_strength
== b
.signal_strength
)
86 return strcmp(a
.name
, b
.name
) > 0;
87 return a
.signal_strength
> b
.signal_strength
;
94 NetworkStatusView::NetworkStatusView(BRect frame
, int32 resizingMode
,
96 : BView(frame
, kDeskbarItemName
, resizingMode
,
97 B_WILL_DRAW
| B_FRAME_EVENTS
),
103 // we were obviously added to a standard window - let's add a dragger
104 frame
.OffsetTo(B_ORIGIN
);
105 frame
.top
= frame
.bottom
- 7;
106 frame
.left
= frame
.right
- 7;
107 BDragger
* dragger
= new BDragger(frame
, this,
108 B_FOLLOW_RIGHT
| B_FOLLOW_BOTTOM
);
115 NetworkStatusView::NetworkStatusView(BMessage
* archive
)
120 if (be_app
->GetAppInfo(&info
) == B_OK
121 && !strcasecmp(info
.signature
, "application/x-vnd.Be-TSKB"))
128 NetworkStatusView::~NetworkStatusView()
134 NetworkStatusView::_Init()
136 for (int i
= 0; i
< kStatusCount
; i
++) {
137 fTrayIcons
[i
] = NULL
;
138 fNotifyIcons
[i
] = NULL
;
146 NetworkStatusView::_UpdateBitmaps()
148 for (int i
= 0; i
< kStatusCount
; i
++) {
149 delete fTrayIcons
[i
];
150 delete fNotifyIcons
[i
];
151 fTrayIcons
[i
] = NULL
;
152 fNotifyIcons
[i
] = NULL
;
156 if (our_image(info
) != B_OK
)
159 BFile
file(info
.name
, B_READ_ONLY
);
160 if (file
.InitCheck() < B_OK
)
163 BResources
resources(&file
);
164 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
165 if (resources
.InitCheck() < B_OK
)
169 for (int i
= 0; i
< kStatusCount
; i
++) {
170 const void* data
= NULL
;
172 data
= resources
.LoadResource(B_VECTOR_ICON_TYPE
,
173 kNetworkStatusNoDevice
+ i
, &size
);
175 // Scale main tray icon
176 BBitmap
* trayIcon
= new BBitmap(Bounds(), B_RGBA32
);
177 if (trayIcon
->InitCheck() == B_OK
178 && BIconUtils::GetVectorIcon((const uint8
*)data
,
179 size
, trayIcon
) == B_OK
) {
180 fTrayIcons
[i
] = trayIcon
;
184 // Scale notification icon
185 BBitmap
* notifyIcon
= new BBitmap(BRect(0, 0, 31, 31), B_RGBA32
);
186 if (notifyIcon
->InitCheck() == B_OK
187 && BIconUtils::GetVectorIcon((const uint8
*)data
,
188 size
, notifyIcon
) == B_OK
) {
189 fNotifyIcons
[i
] = notifyIcon
;
198 NetworkStatusView::_Quit()
202 deskbar
.RemoveItem(kDeskbarItemName
);
204 be_app
->PostMessage(B_QUIT_REQUESTED
);
209 NetworkStatusView::Instantiate(BMessage
* archive
)
211 if (!validate_instantiation(archive
, "NetworkStatusView"))
214 return new NetworkStatusView(archive
);
219 NetworkStatusView::Archive(BMessage
* archive
, bool deep
) const
221 status_t status
= BView::Archive(archive
, deep
);
223 status
= archive
->AddString("add_on", kSignature
);
225 status
= archive
->AddString("class", "NetworkStatusView");
232 NetworkStatusView::AttachedToWindow()
234 BView::AttachedToWindow();
235 if (Parent() != NULL
) {
236 if ((Parent()->Flags() & B_DRAW_ON_CHILDREN
) != 0)
237 SetViewColor(B_TRANSPARENT_COLOR
);
241 SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
243 if (ViewUIColor() != B_NO_COLOR
)
244 SetLowUIColor(ViewUIColor());
246 SetLowColor(ViewColor());
248 start_watching_network(
249 B_WATCH_NETWORK_INTERFACE_CHANGES
| B_WATCH_NETWORK_LINK_CHANGES
, this);
256 NetworkStatusView::DetachedFromWindow()
258 stop_watching_network(this);
263 NetworkStatusView::MessageReceived(BMessage
* message
)
265 switch (message
->what
) {
266 case B_NETWORK_MONITOR
:
270 case kMsgShowConfiguration
:
271 _ShowConfiguration(message
);
274 case kMsgOpenNetworkPreferences
:
275 _OpenNetworksPreferences();
278 case kMsgJoinNetwork
:
280 const char* deviceName
;
282 BNetworkAddress address
;
283 if (message
->FindString("device", &deviceName
) == B_OK
284 && message
->FindString("name", &name
) == B_OK
285 && message
->FindFlat("address", &address
) == B_OK
) {
286 BNetworkDevice
device(deviceName
);
287 status_t status
= device
.JoinNetwork(address
);
288 if (status
!= B_OK
) {
290 = B_TRANSLATE("Could not join wireless network:\n");
291 text
<< strerror(status
);
292 BAlert
* alert
= new BAlert(name
, text
.String(),
293 B_TRANSLATE("OK"), NULL
, NULL
, B_WIDTH_AS_USUAL
,
295 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
302 case B_ABOUT_REQUESTED
:
306 case B_QUIT_REQUESTED
:
311 BView::MessageReceived(message
);
317 NetworkStatusView::FrameResized(float width
, float height
)
325 NetworkStatusView::Draw(BRect updateRect
)
327 int32 status
= kStatusUnknown
;
328 for (std::map
<BString
, int32
>::const_iterator it
329 = fInterfaceStatuses
.begin(); it
!= fInterfaceStatuses
.end(); ++it
) {
330 if (it
->second
> status
)
334 if (fTrayIcons
[status
] == NULL
)
337 SetDrawingMode(B_OP_ALPHA
);
338 DrawBitmap(fTrayIcons
[status
]);
339 SetDrawingMode(B_OP_COPY
);
344 NetworkStatusView::_ShowConfiguration(BMessage
* message
)
347 if (message
->FindString("interface", &name
) != B_OK
)
350 BNetworkInterface
networkInterface(name
);
351 if (!networkInterface
.Exists())
354 BNetworkInterfaceAddress address
;
355 networkInterface
.GetAddressAt(0, address
);
356 // TODO: We should get all addresses,
357 // not just the first one.
358 BString
text(B_TRANSLATE("%ifaceName information:\n"));
359 text
.ReplaceFirst("%ifaceName", name
);
361 size_t boldLength
= text
.Length();
363 text
<< "\n" << B_TRANSLATE("Address") << ": " << address
.Address().ToString();
364 text
<< "\n" << B_TRANSLATE("Broadcast") << ": " << address
.Broadcast().ToString();
365 text
<< "\n" << B_TRANSLATE("Netmask") << ": " << address
.Mask().ToString();
367 BAlert
* alert
= new BAlert(name
, text
.String(), B_TRANSLATE("OK"));
368 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
369 BTextView
* view
= alert
->TextView();
372 view
->SetStylable(true);
373 view
->GetFont(&font
);
374 font
.SetFace(B_BOLD_FACE
);
375 view
->SetFontAndColor(0, boldLength
, &font
);
382 NetworkStatusView::MouseDown(BPoint point
)
384 BPopUpMenu
* menu
= new BPopUpMenu(B_EMPTY_STRING
, false, false);
385 menu
->SetAsyncAutoDestruct(true);
386 menu
->SetFont(be_plain_font
);
387 BString wifiInterface
;
388 BNetworkDevice wifiDevice
;
392 for (std::map
<BString
, int32
>::const_iterator it
393 = fInterfaceStatuses
.begin(); it
!= fInterfaceStatuses
.end(); ++it
) {
394 const BString
& name
= it
->first
;
396 BString label
= name
;
398 label
+= kStatusDescriptions
[
399 _DetermineInterfaceStatus(name
.String())];
401 BMessage
* info
= new BMessage(kMsgShowConfiguration
);
402 info
->AddString("interface", name
.String());
403 menu
->AddItem(new BMenuItem(label
.String(), info
));
405 // We only show the networks of the first wireless device we find.
406 if (wifiInterface
.IsEmpty()) {
407 wifiDevice
.SetTo(name
);
408 if (wifiDevice
.IsWireless())
409 wifiInterface
= name
;
413 if (!fInterfaceStatuses
.empty())
414 menu
->AddSeparatorItem();
416 // Add wireless networks, if any
418 if (!wifiInterface
.IsEmpty()) {
419 std::set
<BNetworkAddress
> associated
;
420 BNetworkAddress address
;
422 while (wifiDevice
.GetNextAssociatedNetwork(cookie
, address
) == B_OK
)
423 associated
.insert(address
);
426 wireless_network network
;
427 typedef std::vector
<wireless_network
> WirelessNetworkVector
;
428 WirelessNetworkVector wirelessNetworks
;
429 while (wifiDevice
.GetNextNetwork(cookie
, network
) == B_OK
)
430 wirelessNetworks
.push_back(network
);
432 std::sort(wirelessNetworks
.begin(), wirelessNetworks
.end(),
433 signal_strength_compare
);
436 for (WirelessNetworkVector::iterator it
= wirelessNetworks
.begin();
437 it
!= wirelessNetworks
.end(); it
++) {
438 wireless_network
&network
= *it
;
440 BMessage
* message
= new BMessage(kMsgJoinNetwork
);
441 message
->AddString("device", wifiInterface
);
442 message
->AddString("name", network
.name
);
443 message
->AddFlat("address", &network
.address
);
445 BMenuItem
* item
= new WirelessNetworkMenuItem(network
.name
,
446 network
.signal_strength
, network
.authentication_mode
, message
);
448 if (associated
.find(network
.address
) != associated
.end())
449 item
->SetMarked(true);
454 BMenuItem
* item
= new BMenuItem(
455 B_TRANSLATE("<no wireless networks found>"), NULL
);
456 item
->SetEnabled(false);
459 menu
->AddSeparatorItem();
462 menu
->AddItem(new BMenuItem(B_TRANSLATE(
463 "Open network preferences" B_UTF8_ELLIPSIS
),
464 new BMessage(kMsgOpenNetworkPreferences
)));
467 menu
->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
468 new BMessage(B_QUIT_REQUESTED
)));
470 menu
->SetTargetForItems(this);
472 ConvertToScreen(&point
);
473 menu
->Go(point
, true, true, true);
478 NetworkStatusView::_AboutRequested()
480 BAboutWindow
* window
= new BAboutWindow(
481 B_TRANSLATE_SYSTEM_NAME("NetworkStatus"), kSignature
);
483 const char* authors
[] = {
489 window
->AddCopyright(2007, "Haiku, Inc.");
490 window
->AddAuthors(authors
);
497 NetworkStatusView::_DetermineInterfaceStatus(
498 const BNetworkInterface
& interface
)
500 uint32 flags
= interface
.Flags();
502 if ((flags
& IFF_LINK
) == 0)
503 return kStatusNoLink
;
504 if ((flags
& (IFF_UP
| IFF_LINK
| IFF_CONFIGURING
)) == IFF_LINK
)
505 return kStatusLinkNoConfig
;
506 if ((flags
& IFF_CONFIGURING
) == IFF_CONFIGURING
)
507 return kStatusConnecting
;
508 if ((flags
& (IFF_UP
| IFF_LINK
)) == (IFF_UP
| IFF_LINK
))
511 return kStatusUnknown
;
516 NetworkStatusView::_Update(bool force
)
518 BNetworkRoster
& roster
= BNetworkRoster::Default();
519 BNetworkInterface interface
;
522 while (roster
.GetNextInterface(&cookie
, interface
) == B_OK
) {
523 if ((interface
.Flags() & IFF_LOOPBACK
) == 0) {
524 int32 oldStatus
= kStatusUnknown
;
525 if (fInterfaceStatuses
.find(interface
.Name())
526 != fInterfaceStatuses
.end()) {
527 oldStatus
= fInterfaceStatuses
[interface
.Name()];
529 int32 status
= _DetermineInterfaceStatus(interface
);
530 if (oldStatus
!= status
) {
531 BNotification
notification(B_INFORMATION_NOTIFICATION
);
532 notification
.SetGroup(B_TRANSLATE("Network Status"));
533 notification
.SetTitle(interface
.Name());
534 notification
.SetMessageID(interface
.Name());
535 notification
.SetIcon(fNotifyIcons
[status
]);
536 if (status
== kStatusConnecting
537 || (status
== kStatusReady
538 && oldStatus
== kStatusConnecting
)
539 || (status
== kStatusNoLink
540 && oldStatus
== kStatusReady
)
541 || (status
== kStatusNoLink
542 && oldStatus
== kStatusConnecting
)) {
543 // A significant state change, raise notification.
544 notification
.SetContent(kStatusDescriptions
[status
]);
549 fInterfaceStatuses
[interface
.Name()] = status
;
556 NetworkStatusView::_OpenNetworksPreferences()
558 status_t status
= be_roster
->Launch("application/x-vnd.Haiku-Network");
559 if (status
!= B_OK
&& status
!= B_ALREADY_RUNNING
) {
560 BString
errorMessage(B_TRANSLATE("Launching the network preflet "
561 "failed.\n\nError: "));
562 errorMessage
<< strerror(status
);
563 BAlert
* alert
= new BAlert("launch error", errorMessage
.String(),
565 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
567 // asynchronous alert in order to not block replicant host application
576 extern "C" _EXPORT BView
*
577 instantiate_deskbar_item(void)
579 return new NetworkStatusView(BRect(0, 0, 15, 15),
580 B_FOLLOW_LEFT
| B_FOLLOW_TOP
, true);