BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / networkstatus / NetworkStatusView.cpp
blob9eb567f339aad113a94c49884ba5adbc194bbecd
1 /*
2 * Copyright 2006-2013, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Dario Casalinuovo
7 * Axel Dörfler, axeld@pinc-software.de
8 * Rene Gollent, rene@gollent.com
9 * Hugo Santos, hugosantos@gmail.com
13 #include "NetworkStatusView.h"
15 #include <algorithm>
16 #include <set>
17 #include <vector>
19 #include <arpa/inet.h>
20 #include <net/if.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <strings.h>
24 #include <sys/socket.h>
25 #include <sys/sockio.h>
26 #include <unistd.h>
28 #include <AboutWindow.h>
29 #include <Alert.h>
30 #include <Application.h>
31 #include <Catalog.h>
32 #include <Bitmap.h>
33 #include <Deskbar.h>
34 #include <Dragger.h>
35 #include <Drivers.h>
36 #include <IconUtils.h>
37 #include <Locale.h>
38 #include <MenuItem.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>
45 #include <Roster.h>
46 #include <String.h>
47 #include <TextView.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"),
64 B_TRANSLATE("Ready")
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;
78 // #pragma mark -
81 static bool
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;
91 // #pragma mark -
94 NetworkStatusView::NetworkStatusView(BRect frame, int32 resizingMode,
95 bool inDeskbar)
96 : BView(frame, kDeskbarItemName, resizingMode,
97 B_WILL_DRAW | B_FRAME_EVENTS),
98 fInDeskbar(inDeskbar)
100 _Init();
102 if (!inDeskbar) {
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);
109 AddChild(dragger);
110 } else
111 _Update();
115 NetworkStatusView::NetworkStatusView(BMessage* archive)
116 : BView(archive),
117 fInDeskbar(false)
119 app_info info;
120 if (be_app->GetAppInfo(&info) == B_OK
121 && !strcasecmp(info.signature, "application/x-vnd.Be-TSKB"))
122 fInDeskbar = true;
124 _Init();
128 NetworkStatusView::~NetworkStatusView()
133 void
134 NetworkStatusView::_Init()
136 for (int i = 0; i < kStatusCount; i++) {
137 fTrayIcons[i] = NULL;
138 fNotifyIcons[i] = NULL;
141 _UpdateBitmaps();
145 void
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;
155 image_info info;
156 if (our_image(info) != B_OK)
157 return;
159 BFile file(info.name, B_READ_ONLY);
160 if (file.InitCheck() < B_OK)
161 return;
163 BResources resources(&file);
164 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
165 if (resources.InitCheck() < B_OK)
166 return;
167 #endif
169 for (int i = 0; i < kStatusCount; i++) {
170 const void* data = NULL;
171 size_t size;
172 data = resources.LoadResource(B_VECTOR_ICON_TYPE,
173 kNetworkStatusNoDevice + i, &size);
174 if (data != NULL) {
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;
181 } else
182 delete 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;
190 } else
191 delete notifyIcon;
197 void
198 NetworkStatusView::_Quit()
200 if (fInDeskbar) {
201 BDeskbar deskbar;
202 deskbar.RemoveItem(kDeskbarItemName);
203 } else
204 be_app->PostMessage(B_QUIT_REQUESTED);
208 NetworkStatusView*
209 NetworkStatusView::Instantiate(BMessage* archive)
211 if (!validate_instantiation(archive, "NetworkStatusView"))
212 return NULL;
214 return new NetworkStatusView(archive);
218 status_t
219 NetworkStatusView::Archive(BMessage* archive, bool deep) const
221 status_t status = BView::Archive(archive, deep);
222 if (status == B_OK)
223 status = archive->AddString("add_on", kSignature);
224 if (status == B_OK)
225 status = archive->AddString("class", "NetworkStatusView");
227 return status;
231 void
232 NetworkStatusView::AttachedToWindow()
234 BView::AttachedToWindow();
235 if (Parent() != NULL) {
236 if ((Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0)
237 SetViewColor(B_TRANSPARENT_COLOR);
238 else
239 AdoptParentColors();
240 } else
241 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
243 if (ViewUIColor() != B_NO_COLOR)
244 SetLowUIColor(ViewUIColor());
245 else
246 SetLowColor(ViewColor());
248 start_watching_network(
249 B_WATCH_NETWORK_INTERFACE_CHANGES | B_WATCH_NETWORK_LINK_CHANGES, this);
251 _Update();
255 void
256 NetworkStatusView::DetachedFromWindow()
258 stop_watching_network(this);
262 void
263 NetworkStatusView::MessageReceived(BMessage* message)
265 switch (message->what) {
266 case B_NETWORK_MONITOR:
267 _Update();
268 break;
270 case kMsgShowConfiguration:
271 _ShowConfiguration(message);
272 break;
274 case kMsgOpenNetworkPreferences:
275 _OpenNetworksPreferences();
276 break;
278 case kMsgJoinNetwork:
280 const char* deviceName;
281 const char* name;
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) {
289 BString text
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,
294 B_STOP_ALERT);
295 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
296 alert->Go(NULL);
299 break;
302 case B_ABOUT_REQUESTED:
303 _AboutRequested();
304 break;
306 case B_QUIT_REQUESTED:
307 _Quit();
308 break;
310 default:
311 BView::MessageReceived(message);
316 void
317 NetworkStatusView::FrameResized(float width, float height)
319 _UpdateBitmaps();
320 Invalidate();
324 void
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)
331 status = it->second;
334 if (fTrayIcons[status] == NULL)
335 return;
337 SetDrawingMode(B_OP_ALPHA);
338 DrawBitmap(fTrayIcons[status]);
339 SetDrawingMode(B_OP_COPY);
343 void
344 NetworkStatusView::_ShowConfiguration(BMessage* message)
346 const char* name;
347 if (message->FindString("interface", &name) != B_OK)
348 return;
350 BNetworkInterface networkInterface(name);
351 if (!networkInterface.Exists())
352 return;
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();
370 BFont font;
372 view->SetStylable(true);
373 view->GetFont(&font);
374 font.SetFace(B_BOLD_FACE);
375 view->SetFontAndColor(0, boldLength, &font);
377 alert->Go(NULL);
381 void
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;
390 // Add interfaces
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;
397 label += ": ";
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;
421 uint32 cookie = 0;
422 while (wifiDevice.GetNextAssociatedNetwork(cookie, address) == B_OK)
423 associated.insert(address);
425 cookie = 0;
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);
435 int32 count = 0;
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);
447 menu->AddItem(item);
448 if (associated.find(network.address) != associated.end())
449 item->SetMarked(true);
451 count++;
453 if (count == 0) {
454 BMenuItem* item = new BMenuItem(
455 B_TRANSLATE("<no wireless networks found>"), NULL);
456 item->SetEnabled(false);
457 menu->AddItem(item);
459 menu->AddSeparatorItem();
462 menu->AddItem(new BMenuItem(B_TRANSLATE(
463 "Open network preferences" B_UTF8_ELLIPSIS),
464 new BMessage(kMsgOpenNetworkPreferences)));
466 if (fInDeskbar) {
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);
477 void
478 NetworkStatusView::_AboutRequested()
480 BAboutWindow* window = new BAboutWindow(
481 B_TRANSLATE_SYSTEM_NAME("NetworkStatus"), kSignature);
483 const char* authors[] = {
484 "Axel Dörfler",
485 "Hugo Santos",
486 NULL
489 window->AddCopyright(2007, "Haiku, Inc.");
490 window->AddAuthors(authors);
492 window->Show();
496 int32
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))
509 return kStatusReady;
511 return kStatusUnknown;
515 void
516 NetworkStatusView::_Update(bool force)
518 BNetworkRoster& roster = BNetworkRoster::Default();
519 BNetworkInterface interface;
520 uint32 cookie = 0;
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]);
545 notification.Send();
547 Invalidate();
549 fInterfaceStatuses[interface.Name()] = status;
555 void
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(),
564 B_TRANSLATE("OK"));
565 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
567 // asynchronous alert in order to not block replicant host application
568 alert->Go(NULL);
573 // #pragma mark -
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);