1 /* 2 * Copyright 2004-2015 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, <axeld@pinc-software.de> 7 * Alexander von Gluck, kallisti5@unixzen.com 8 * John Scipione, jscipione@gmail.com 9 */ 10 11 12 #include "InterfaceView.h" 13 14 #include <set> 15 16 #include <net/if_media.h> 17 18 #include <AutoDeleter.h> 19 #include <Button.h> 20 #include <Catalog.h> 21 #include <ControlLook.h> 22 #include <LayoutBuilder.h> 23 #include <NetworkAddress.h> 24 #include <StringForSize.h> 25 #include <StringView.h> 26 #include <TextControl.h> 27 28 #include "MediaTypes.h" 29 #include "WirelessNetworkMenuItem.h" 30 31 32 static const uint32 kMsgInterfaceToggle = 'onof'; 33 static const uint32 kMsgInterfaceRenegotiate = 'redo'; 34 static const uint32 kMsgJoinNetwork = 'join'; 35 36 37 #undef B_TRANSLATION_CONTEXT 38 #define B_TRANSLATION_CONTEXT "IntefaceView" 39 40 41 // #pragma mark - InterfaceView 42 43 44 InterfaceView::InterfaceView() 45 : 46 BGroupView(B_VERTICAL), 47 fPulseCount(0) 48 { 49 SetFlags(Flags() | B_PULSE_NEEDED); 50 51 // TODO: Small graph of throughput? 52 53 BStringView* statusLabel = new BStringView("status label", B_TRANSLATE("Status:")); 54 statusLabel->SetAlignment(B_ALIGN_RIGHT); 55 fStatusField = new BStringView("status field", ""); 56 BStringView* macAddressLabel = new BStringView("mac address label", 57 B_TRANSLATE("MAC address:")); 58 macAddressLabel->SetAlignment(B_ALIGN_RIGHT); 59 fMacAddressField = new BStringView("mac address field", ""); 60 BStringView* linkSpeedLabel = new BStringView("link speed label", 61 B_TRANSLATE("Link speed:")); 62 linkSpeedLabel->SetAlignment(B_ALIGN_RIGHT); 63 fLinkSpeedField = new BStringView("link speed field", ""); 64 65 // TODO: These metrics may be better in a BScrollView? 66 BStringView* linkTxLabel = new BStringView("tx label", 67 B_TRANSLATE("Sent:")); 68 linkTxLabel->SetAlignment(B_ALIGN_RIGHT); 69 fLinkTxField = new BStringView("tx field", ""); 70 BStringView* linkRxLabel = new BStringView("rx label", 71 B_TRANSLATE("Received:")); 72 linkRxLabel->SetAlignment(B_ALIGN_RIGHT); 73 fLinkRxField = new BStringView("rx field", ""); 74 75 fNetworkMenuField = new BMenuField(B_TRANSLATE("Network:"), new BMenu( 76 B_TRANSLATE("Choose automatically"))); 77 fNetworkMenuField->SetAlignment(B_ALIGN_RIGHT); 78 fNetworkMenuField->Menu()->SetLabelFromMarked(true); 79 80 // Construct the BButtons 81 fToggleButton = new BButton("onoff", B_TRANSLATE("Disable"), 82 new BMessage(kMsgInterfaceToggle)); 83 84 fRenegotiateButton = new BButton("heal", B_TRANSLATE("Renegotiate"), 85 new BMessage(kMsgInterfaceRenegotiate)); 86 fRenegotiateButton->SetEnabled(false); 87 88 BLayoutBuilder::Group<>(this) 89 .AddGrid() 90 .Add(statusLabel, 0, 0) 91 .Add(fStatusField, 1, 0) 92 .AddMenuField(fNetworkMenuField, 0, 1, B_ALIGN_RIGHT, 1, 2) 93 .Add(macAddressLabel, 0, 2) 94 .Add(fMacAddressField, 1, 2) 95 .Add(linkSpeedLabel, 0, 3) 96 .Add(fLinkSpeedField, 1, 3) 97 .Add(linkTxLabel, 0, 4) 98 .Add(fLinkTxField, 1, 4) 99 .Add(linkRxLabel, 0, 5) 100 .Add(fLinkRxField, 1, 5) 101 .End() 102 .AddGlue() 103 .AddGroup(B_HORIZONTAL) 104 .AddGlue() 105 .Add(fToggleButton) 106 .Add(fRenegotiateButton) 107 .End(); 108 } 109 110 111 InterfaceView::~InterfaceView() 112 { 113 } 114 115 116 void 117 InterfaceView::SetTo(const char* name) 118 { 119 fInterface.SetTo(name); 120 } 121 122 123 void 124 InterfaceView::AttachedToWindow() 125 { 126 _Update(); 127 // Populate the fields 128 129 fToggleButton->SetTarget(this); 130 fRenegotiateButton->SetTarget(this); 131 } 132 133 134 void 135 InterfaceView::MessageReceived(BMessage* message) 136 { 137 switch (message->what) { 138 case kMsgJoinNetwork: 139 { 140 const char* name; 141 BNetworkAddress address; 142 if (message->FindString("name", &name) == B_OK 143 && message->FindFlat("address", &address) == B_OK) { 144 BNetworkDevice device(fInterface.Name()); 145 status_t status = device.JoinNetwork(address); 146 if (status != B_OK) { 147 // This does not really matter, as it's stored this way, 148 // anyway. 149 } 150 // TODO: store value 151 } 152 break; 153 } 154 case kMsgInterfaceToggle: 155 { 156 // Disable/enable interface 157 uint32 flags = fInterface.Flags(); 158 if ((flags & IFF_UP) != 0) 159 flags &= ~IFF_UP; 160 else 161 flags |= IFF_UP; 162 163 if (fInterface.SetFlags(flags) == B_OK) 164 _Update(); 165 break; 166 } 167 168 case kMsgInterfaceRenegotiate: 169 { 170 // TODO: renegotiate addresses 171 break; 172 } 173 174 default: 175 BView::MessageReceived(message); 176 } 177 } 178 179 180 void 181 InterfaceView::Pulse() 182 { 183 // Update the wireless network menu every 5 seconds 184 _Update((fPulseCount++ % 5) == 0); 185 } 186 187 188 /*! Populate fields with current settings. 189 */ 190 status_t 191 InterfaceView::_Update(bool updateWirelessNetworks) 192 { 193 BNetworkDevice device(fInterface.Name()); 194 bool isWireless = device.IsWireless(); 195 bool disabled = (fInterface.Flags() & IFF_UP) == 0; 196 197 if (fInterface.HasLink()) 198 fStatusField->SetText(B_TRANSLATE("connected")); 199 else 200 fStatusField->SetText(B_TRANSLATE("disconnected")); 201 202 BNetworkAddress hardwareAddress; 203 if (device.GetHardwareAddress(hardwareAddress) == B_OK) 204 fMacAddressField->SetText(hardwareAddress.ToString()); 205 else 206 fMacAddressField->SetText("-"); 207 208 int media = fInterface.Media(); 209 if ((media & IFM_ACTIVE) != 0) 210 fLinkSpeedField->SetText(media_type_to_string(media)); 211 else 212 fLinkSpeedField->SetText("-"); 213 214 // Update Link stats 215 ifreq_stats stats; 216 if (fInterface.GetStats(stats) == B_OK) { 217 char buffer[100]; 218 219 string_for_size(stats.send.bytes, buffer, sizeof(buffer)); 220 fLinkTxField->SetText(buffer); 221 222 string_for_size(stats.receive.bytes, buffer, sizeof(buffer)); 223 fLinkRxField->SetText(buffer); 224 } 225 226 // TODO: move the wireless info to a separate tab. We should have a 227 // BListView of available networks, rather than a menu, to make them more 228 // readable and easier to browse and select. 229 if (fNetworkMenuField->IsHidden(fNetworkMenuField) && isWireless) 230 fNetworkMenuField->Show(); 231 else if (!fNetworkMenuField->IsHidden(fNetworkMenuField) && !isWireless) 232 fNetworkMenuField->Hide(); 233 234 if (isWireless && updateWirelessNetworks) { 235 // Rebuild network menu 236 BMenu* menu = fNetworkMenuField->Menu(); 237 int32 count = menu->CountItems(); 238 239 // remove non-network items from menu and save them for later 240 BMenuItem* chooseItem = NULL; 241 BSeparatorItem* separatorItem = NULL; 242 if (count > 0 && strcmp(menu->ItemAt(0)->Label(), 243 B_TRANSLATE("Choose automatically")) == 0) { 244 // remove Choose automatically item 245 chooseItem = menu->RemoveItem((int32)0); 246 // remove separator item too 247 separatorItem = (BSeparatorItem*)menu->RemoveItem((int32)0); 248 count -= 2; 249 } 250 251 BMenuItem* noNetworksFoundItem = NULL; 252 if (menu->CountItems() > 0 && strcmp(menu->ItemAt(0)->Label(), 253 B_TRANSLATE("<no wireless networks found>")) == 0) { 254 // remove <no wireless networks found> item 255 noNetworksFoundItem = menu->RemoveItem((int32)0); 256 count--; 257 } 258 259 std::set<BNetworkAddress> associated; 260 BNetworkAddress address; 261 uint32 cookie = 0; 262 while (device.GetNextAssociatedNetwork(cookie, address) == B_OK) 263 associated.insert(address); 264 265 wireless_network* networks = NULL; 266 uint32 networksCount = 0; 267 device.GetNetworks(networks, networksCount); 268 269 if ((fPulseCount % 15) == 0 && networksCount == 0) { 270 // We don't seem to know of any networks, and it's been long 271 // enough since the last scan, so trigger one to try and 272 // find some networks. 273 device.Scan(false, false); 274 275 // We don't want to block for the full length of the scan, but 276 // 50ms is often more than enough to find at least one network, 277 // and the increase in perceived QoS to the user of not seeing 278 // "no wireless networks" if we can avoid it is great enough 279 // to merit such a wait. It's only just over ~4 vertical 280 // retraces, anyway. 281 snooze(50 * 1000); 282 283 device.GetNetworks(networks, networksCount); 284 } 285 286 ArrayDeleter<wireless_network> networksDeleter(networks); 287 288 // go through menu items and remove networks that have dropped out 289 for (int32 index = 0; index < count; index++) { 290 WirelessNetworkMenuItem* networkItem = 291 dynamic_cast<WirelessNetworkMenuItem*>( 292 menu->ItemAt(index)); 293 if (networkItem == NULL) 294 break; 295 296 bool networkFound = false; 297 for (uint32 i = 0; i < networksCount; i++) { 298 if (networkItem->Network() == networks[i]) { 299 networkFound = true; 300 break; 301 } 302 } 303 304 if (!networkFound) { 305 menu->RemoveItem(networkItem); 306 count--; 307 } 308 } 309 310 // go through networks and add new ones to menu 311 for (uint32 i = 0; i < networksCount; i++) { 312 const wireless_network& network = networks[i]; 313 314 bool networkFound = false; 315 for (int32 index = 0; index < count; index++) { 316 WirelessNetworkMenuItem* networkItem = 317 dynamic_cast<WirelessNetworkMenuItem*>( 318 menu->ItemAt(index)); 319 if (networkItem == NULL) 320 break; 321 322 if (networkItem->Network() == network) { 323 // found it 324 networkFound = true; 325 if (associated.find(network.address) != associated.end()) 326 networkItem->SetMarked(true); 327 break; 328 } 329 } 330 331 if (!networkFound) { 332 BMessage* message = new BMessage(kMsgJoinNetwork); 333 message->AddString("device", fInterface.Name()); 334 message->AddString("name", network.name); 335 message->AddFlat("address", &network.address); 336 BMenuItem* item = new WirelessNetworkMenuItem(network, 337 message); 338 menu->AddItem(item); 339 if (associated.find(network.address) != associated.end()) 340 item->SetMarked(true); 341 } 342 343 count++; 344 } 345 346 if (count == 0) { 347 // no networks found 348 if (noNetworksFoundItem != NULL) 349 menu->AddItem(noNetworksFoundItem); 350 else { 351 BMenuItem* item = new BMenuItem( 352 B_TRANSLATE("<no wireless networks found>"), NULL); 353 item->SetEnabled(false); 354 menu->AddItem(item); 355 } 356 } else { 357 // sort items by signal strength 358 menu->SortItems(WirelessNetworkMenuItem::CompareSignalStrength); 359 360 // add Choose automatically item to start 361 if (chooseItem != NULL) { 362 menu->AddItem(chooseItem, 0); 363 menu->AddItem(separatorItem, 1); 364 } else { 365 BMenuItem* item = new BMenuItem( 366 B_TRANSLATE("Choose automatically"), NULL); 367 if (menu->FindMarked() == NULL) 368 item->SetMarked(true); 369 menu->AddItem(item, 0); 370 menu->AddItem(new BSeparatorItem(), 1); 371 } 372 } 373 374 menu->SetTargetForItems(this); 375 } 376 377 //fRenegotiateButton->SetEnabled(!disabled); 378 fToggleButton->SetLabel(disabled 379 ? B_TRANSLATE("Enable") : B_TRANSLATE("Disable")); 380 381 return B_OK; 382 } 383