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