/* * Copyright 2006-2013, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Dario Casalinuovo * Axel Dörfler, axeld@pinc-software.de * Rene Gollent, rene@gollent.com * Hugo Santos, hugosantos@gmail.com */ #include "NetworkStatusView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "NetworkStatus.h" #include "NetworkStatusIcons.h" #include "RadioView.h" #include "WirelessNetworkMenuItem.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "NetworkStatusView" static const char *kStatusDescriptions[] = { B_TRANSLATE("Unknown"), B_TRANSLATE("No link"), B_TRANSLATE("No stateful configuration"), B_TRANSLATE("Configuring"), B_TRANSLATE("Ready") }; extern "C" _EXPORT BView *instantiate_deskbar_item(float maxWidth, float maxHeight); const uint32 kMsgShowConfiguration = 'shcf'; const uint32 kMsgOpenNetworkPreferences = 'onwp'; const uint32 kMsgJoinNetwork = 'join'; const uint32 kMinIconWidth = 16; const uint32 kMinIconHeight = 16; // #pragma mark - NetworkStatusView NetworkStatusView::NetworkStatusView(BRect frame, int32 resizingMode, bool inDeskbar) : BView(frame, kDeskbarItemName, resizingMode, B_WILL_DRAW | B_TRANSPARENT_BACKGROUND | B_FRAME_EVENTS), fInDeskbar(inDeskbar) { _Init(); if (!inDeskbar) { // we were obviously added to a standard window - let's add a dragger frame.OffsetTo(B_ORIGIN); frame.top = frame.bottom - 7; frame.left = frame.right - 7; BDragger* dragger = new BDragger(frame, this, B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); AddChild(dragger); } else _Update(); } NetworkStatusView::NetworkStatusView(BMessage* archive) : BView(archive), fInDeskbar(false) { app_info info; if (be_app->GetAppInfo(&info) == B_OK && !strcasecmp(info.signature, "application/x-vnd.Be-TSKB")) fInDeskbar = true; _Init(); } NetworkStatusView::~NetworkStatusView() { } void NetworkStatusView::_Init() { for (int i = 0; i < kStatusCount; i++) { fTrayIcons[i] = NULL; fNotifyIcons[i] = NULL; } _UpdateBitmaps(); } void NetworkStatusView::_UpdateBitmaps() { for (int i = 0; i < kStatusCount; i++) { delete fTrayIcons[i]; delete fNotifyIcons[i]; fTrayIcons[i] = NULL; fNotifyIcons[i] = NULL; } image_info info; if (our_image(info) != B_OK) return; BFile file(info.name, B_READ_ONLY); if (file.InitCheck() < B_OK) return; BResources resources(&file); #ifdef HAIKU_TARGET_PLATFORM_HAIKU if (resources.InitCheck() < B_OK) return; #endif for (int i = 0; i < kStatusCount; i++) { const void* data = NULL; size_t size; data = resources.LoadResource(B_VECTOR_ICON_TYPE, kNetworkStatusNoDevice + i, &size); if (data != NULL) { // Scale main tray icon BBitmap* trayIcon = new BBitmap(Bounds(), B_RGBA32); if (trayIcon->InitCheck() == B_OK && BIconUtils::GetVectorIcon((const uint8 *)data, size, trayIcon) == B_OK) { fTrayIcons[i] = trayIcon; } else delete trayIcon; // Scale notification icon BBitmap* notifyIcon = new BBitmap(BRect(0, 0, 31, 31), B_RGBA32); if (notifyIcon->InitCheck() == B_OK && BIconUtils::GetVectorIcon((const uint8 *)data, size, notifyIcon) == B_OK) { fNotifyIcons[i] = notifyIcon; } else delete notifyIcon; } } } void NetworkStatusView::_Quit() { if (fInDeskbar) { BDeskbar deskbar; deskbar.RemoveItem(kDeskbarItemName); } else be_app->PostMessage(B_QUIT_REQUESTED); } NetworkStatusView* NetworkStatusView::Instantiate(BMessage* archive) { if (!validate_instantiation(archive, "NetworkStatusView")) return NULL; return new NetworkStatusView(archive); } status_t NetworkStatusView::Archive(BMessage* archive, bool deep) const { status_t status = BView::Archive(archive, deep); if (status == B_OK) status = archive->AddString("add_on", kSignature); if (status == B_OK) status = archive->AddString("class", "NetworkStatusView"); return status; } void NetworkStatusView::AttachedToWindow() { BView::AttachedToWindow(); SetViewColor(B_TRANSPARENT_COLOR); start_watching_network( B_WATCH_NETWORK_INTERFACE_CHANGES | B_WATCH_NETWORK_LINK_CHANGES, this); _Update(); } void NetworkStatusView::DetachedFromWindow() { stop_watching_network(this); } void NetworkStatusView::MessageReceived(BMessage* message) { switch (message->what) { case B_NETWORK_MONITOR: _Update(); break; case kMsgShowConfiguration: _ShowConfiguration(message); break; case kMsgOpenNetworkPreferences: _OpenNetworksPreferences(); break; case kMsgJoinNetwork: { const char* deviceName; const char* name; BNetworkAddress address; if (message->FindString("device", &deviceName) == B_OK && message->FindString("name", &name) == B_OK && message->FindFlat("address", &address) == B_OK) { BNetworkDevice device(deviceName); status_t status = device.JoinNetwork(address); if (status != B_OK) { BString text = B_TRANSLATE("Could not join wireless network:\n"); text << strerror(status); BAlert* alert = new BAlert(name, text.String(), B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); alert->Go(NULL); } } break; } case B_ABOUT_REQUESTED: _AboutRequested(); break; case B_QUIT_REQUESTED: _Quit(); break; default: BView::MessageReceived(message); break; } } void NetworkStatusView::FrameResized(float width, float height) { _UpdateBitmaps(); Invalidate(); } void NetworkStatusView::Draw(BRect updateRect) { int32 status = kStatusUnknown; for (std::map::const_iterator it = fInterfaceStatuses.begin(); it != fInterfaceStatuses.end(); ++it) { if (it->second > status) status = it->second; } if (fTrayIcons[status] == NULL) return; SetDrawingMode(B_OP_ALPHA); DrawBitmap(fTrayIcons[status]); SetDrawingMode(B_OP_COPY); } void NetworkStatusView::_ShowConfiguration(BMessage* message) { const char* name; if (message->FindString("interface", &name) != B_OK) return; BNetworkInterface networkInterface(name); if (!networkInterface.Exists()) return; BString text(B_TRANSLATE("%ifaceName information:\n")); text.ReplaceFirst("%ifaceName", name); size_t boldLength = text.Length(); int32 numAddrs = networkInterface.CountAddresses(); for (int32 i = 0; i < numAddrs; i++) { BNetworkInterfaceAddress address; networkInterface.GetAddressAt(i, address); switch (address.Address().Family()) { case AF_INET: text << "\n" << B_TRANSLATE("IPv4 address:") << " " << address.Address().ToString() << "\n" << B_TRANSLATE("Broadcast:") << " " << address.Broadcast().ToString() << "\n" << B_TRANSLATE("Netmask:") << " " << address.Mask().ToString() << "\n"; break; case AF_INET6: text << "\n" << B_TRANSLATE("IPv6 address:") << " " << address.Address().ToString() << "/" << address.Mask().PrefixLength() << "\n"; break; default: break; } } BAlert* alert = new BAlert(name, text.String(), B_TRANSLATE("OK")); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); BTextView* view = alert->TextView(); BFont font; view->SetStylable(true); view->GetFont(&font); font.SetFace(B_BOLD_FACE); view->SetFontAndColor(0, boldLength, &font); alert->Go(NULL); } void NetworkStatusView::MouseDown(BPoint point) { BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false); menu->SetAsyncAutoDestruct(true); menu->SetFont(be_plain_font); BString wifiInterface; BNetworkDevice device; if (!fInterfaceStatuses.empty()) { for (std::map::const_iterator it = fInterfaceStatuses.begin(); it != fInterfaceStatuses.end(); ++it) { const BString& name = it->first; // we only show network of the first wireless device we find if (wifiInterface.IsEmpty()) { device.SetTo(name); if (device.IsWireless()) wifiInterface = name; } } } // Add wireless networks, if any, first so that we can sort the menu if (!wifiInterface.IsEmpty()) { std::set associated; BNetworkAddress address; uint32 cookie = 0; while (device.GetNextAssociatedNetwork(cookie, address) == B_OK) associated.insert(address); uint32 networksCount = 0; wireless_network* networks = NULL; device.GetNetworks(networks, networksCount); for (uint32 i = 0; i < networksCount; i++) { const wireless_network& network = networks[i]; BMessage* message = new BMessage(kMsgJoinNetwork); message->AddString("device", wifiInterface); message->AddString("name", network.name); message->AddFlat("address", &network.address); BMenuItem* item = new WirelessNetworkMenuItem(network, message); menu->AddItem(item); if (associated.find(network.address) != associated.end()) item->SetMarked(true); } delete[] networks; if (networksCount == 0) { BMenuItem* item = new BMenuItem( B_TRANSLATE(""), NULL); item->SetEnabled(false); menu->AddItem(item); } else menu->SortItems(WirelessNetworkMenuItem::CompareSignalStrength); menu->AddSeparatorItem(); } // add action menu items menu->AddItem(new BMenuItem(B_TRANSLATE( "Open network preferences" B_UTF8_ELLIPSIS), new BMessage(kMsgOpenNetworkPreferences))); if (fInDeskbar) { menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), new BMessage(B_QUIT_REQUESTED))); } // Add wired interfaces to top of menu if (!fInterfaceStatuses.empty()) { int32 wiredCount = 0; for (std::map::const_iterator it = fInterfaceStatuses.begin(); it != fInterfaceStatuses.end(); ++it) { const BString& name = it->first; BString label = name; label += ": "; label += kStatusDescriptions[ _DetermineInterfaceStatus(name.String())]; BMessage* info = new BMessage(kMsgShowConfiguration); info->AddString("interface", name.String()); menu->AddItem(new BMenuItem(label.String(), info), wiredCount); wiredCount++; } // add separator item between wired and wireless networks // (or between wired networks and actions if no wireless found) if (wiredCount > 0) menu->AddItem(new BSeparatorItem(), wiredCount); } menu->SetTargetForItems(this); ConvertToScreen(&point); menu->Go(point, true, true, true); } void NetworkStatusView::_AboutRequested() { BAboutWindow* window = new BAboutWindow( B_TRANSLATE_SYSTEM_NAME("NetworkStatus"), kSignature); const char* authors[] = { "Axel Dörfler", "Hugo Santos", NULL }; window->AddCopyright(2007, "Haiku, Inc."); window->AddAuthors(authors); window->Show(); } int32 NetworkStatusView::_DetermineInterfaceStatus( const BNetworkInterface& interface) { uint32 flags = interface.Flags(); if ((flags & IFF_LINK) == 0) return kStatusNoLink; if ((flags & (IFF_UP | IFF_LINK | IFF_CONFIGURING)) == IFF_LINK) return kStatusLinkNoConfig; if ((flags & IFF_CONFIGURING) == IFF_CONFIGURING) return kStatusConnecting; if ((flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) return kStatusReady; return kStatusUnknown; } void NetworkStatusView::_Update(bool force) { BNetworkRoster& roster = BNetworkRoster::Default(); BNetworkInterface interface; uint32 cookie = 0; std::set currentInterfaces; while (roster.GetNextInterface(&cookie, interface) == B_OK) { if ((interface.Flags() & IFF_LOOPBACK) == 0) { currentInterfaces.insert((BString)interface.Name()); int32 oldStatus = kStatusUnknown; if (fInterfaceStatuses.find(interface.Name()) != fInterfaceStatuses.end()) { oldStatus = fInterfaceStatuses[interface.Name()]; } int32 status = _DetermineInterfaceStatus(interface); if (oldStatus != status) { BNotification notification(B_INFORMATION_NOTIFICATION); notification.SetGroup(B_TRANSLATE("Network Status")); notification.SetTitle(interface.Name()); notification.SetMessageID(interface.Name()); notification.SetIcon(fNotifyIcons[status]); if (status == kStatusConnecting || (status == kStatusReady && oldStatus == kStatusConnecting) || (status == kStatusNoLink && oldStatus == kStatusReady) || (status == kStatusNoLink && oldStatus == kStatusConnecting)) { // A significant state change, raise notification. notification.SetContent(kStatusDescriptions[status]); notification.Send(); } Invalidate(); } fInterfaceStatuses[interface.Name()] = status; } } // Check every element in fInterfaceStatuses against our current interface // list. If it's not there, then the interface is not present anymore and // should be removed from fInterfaceStatuses. std::map::iterator it = fInterfaceStatuses.begin(); while (it != fInterfaceStatuses.end()) { std::map::iterator backupIt = it; if (currentInterfaces.find(it->first) == currentInterfaces.end()) fInterfaceStatuses.erase(it); it = ++backupIt; } } void NetworkStatusView::_OpenNetworksPreferences() { status_t status = be_roster->Launch("application/x-vnd.Haiku-Network"); if (status != B_OK && status != B_ALREADY_RUNNING) { BString errorMessage(B_TRANSLATE("Launching the network preflet " "failed.\n\nError: ")); errorMessage << strerror(status); BAlert* alert = new BAlert("launch error", errorMessage.String(), B_TRANSLATE("OK")); alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); // asynchronous alert in order to not block replicant host application alert->Go(NULL); } } // #pragma mark - extern "C" _EXPORT BView * instantiate_deskbar_item(float maxWidth, float maxHeight) { return new NetworkStatusView(BRect(0, 0, maxHeight - 1, maxHeight - 1), B_FOLLOW_LEFT | B_FOLLOW_TOP, true); }