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 <NetworkDevice.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 float minimumWidth = be_control_look->DefaultItemSpacing() * 16; 54 55 BStringView* statusLabel = new BStringView("status label", 56 B_TRANSLATE("Status:")); 57 statusLabel->SetAlignment(B_ALIGN_RIGHT); 58 fStatusField = new BStringView("status field", ""); 59 fStatusField->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET)); 60 BStringView* macAddressLabel = new BStringView("mac address label", 61 B_TRANSLATE("MAC address:")); 62 macAddressLabel->SetAlignment(B_ALIGN_RIGHT); 63 fMacAddressField = new BStringView("mac address field", ""); 64 fMacAddressField->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET)); 65 BStringView* linkSpeedLabel = new BStringView("link speed label", 66 B_TRANSLATE("Link speed:")); 67 linkSpeedLabel->SetAlignment(B_ALIGN_RIGHT); 68 fLinkSpeedField = new BStringView("link speed field", ""); 69 fLinkSpeedField->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET)); 70 71 // TODO: These metrics may be better in a BScrollView? 72 BStringView* linkTxLabel = new BStringView("tx label", 73 B_TRANSLATE("Sent:")); 74 linkTxLabel->SetAlignment(B_ALIGN_RIGHT); 75 fLinkTxField = new BStringView("tx field", ""); 76 fLinkTxField ->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET)); 77 BStringView* linkRxLabel = new BStringView("rx label", 78 B_TRANSLATE("Received:")); 79 linkRxLabel->SetAlignment(B_ALIGN_RIGHT); 80 fLinkRxField = new BStringView("rx field", ""); 81 fLinkRxField ->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET)); 82 83 fNetworkMenuField = new BMenuField(B_TRANSLATE("Network:"), new BMenu( 84 B_TRANSLATE("Choose automatically"))); 85 fNetworkMenuField->SetAlignment(B_ALIGN_RIGHT); 86 fNetworkMenuField->Menu()->SetLabelFromMarked(true); 87 88 // Construct the BButtons 89 fToggleButton = new BButton("onoff", B_TRANSLATE("Disable"), 90 new BMessage(kMsgInterfaceToggle)); 91 92 fRenegotiateButton = new BButton("heal", B_TRANSLATE("Renegotiate"), 93 new BMessage(kMsgInterfaceRenegotiate)); 94 fRenegotiateButton->SetEnabled(false); 95 96 BLayoutBuilder::Group<>(this) 97 .AddGrid() 98 .Add(statusLabel, 0, 0) 99 .Add(fStatusField, 1, 0) 100 .Add(fNetworkMenuField->CreateLabelLayoutItem(), 0, 1) 101 .Add(fNetworkMenuField->CreateMenuBarLayoutItem(), 1, 1) 102 .Add(macAddressLabel, 0, 2) 103 .Add(fMacAddressField, 1, 2) 104 .Add(linkSpeedLabel, 0, 3) 105 .Add(fLinkSpeedField, 1, 3) 106 .Add(linkTxLabel, 0, 4) 107 .Add(fLinkTxField, 1, 4) 108 .Add(linkRxLabel, 0, 5) 109 .Add(fLinkRxField, 1, 5) 110 .End() 111 .AddGlue() 112 .AddGroup(B_HORIZONTAL) 113 .AddGlue() 114 .Add(fToggleButton) 115 .Add(fRenegotiateButton) 116 .End(); 117 } 118 119 120 InterfaceView::~InterfaceView() 121 { 122 } 123 124 125 void 126 InterfaceView::SetTo(const char* name) 127 { 128 fInterface.SetTo(name); 129 } 130 131 132 void 133 InterfaceView::AttachedToWindow() 134 { 135 _Update(); 136 // Populate the fields 137 138 fToggleButton->SetTarget(this); 139 fRenegotiateButton->SetTarget(this); 140 } 141 142 143 void 144 InterfaceView::MessageReceived(BMessage* message) 145 { 146 switch (message->what) { 147 case kMsgJoinNetwork: 148 { 149 const char* name; 150 BNetworkAddress address; 151 if (message->FindString("name", &name) == B_OK 152 && message->FindFlat("address", &address) == B_OK) { 153 BNetworkDevice device(fInterface.Name()); 154 status_t status = device.JoinNetwork(address); 155 if (status != B_OK) { 156 // This does not really matter, as it's stored this way, 157 // anyway. 158 } 159 // TODO: store value 160 } 161 break; 162 } 163 case kMsgInterfaceToggle: 164 { 165 // Disable/enable interface 166 uint32 flags = fInterface.Flags(); 167 if ((flags & IFF_UP) != 0) 168 flags &= ~IFF_UP; 169 else 170 flags |= IFF_UP; 171 172 if (fInterface.SetFlags(flags) == B_OK) 173 _Update(); 174 break; 175 } 176 177 case kMsgInterfaceRenegotiate: 178 { 179 // TODO: renegotiate addresses 180 break; 181 } 182 183 default: 184 BView::MessageReceived(message); 185 } 186 } 187 188 189 void 190 InterfaceView::Pulse() 191 { 192 // Update the wireless network menu every 5 seconds 193 _Update((fPulseCount++ % 5) == 0); 194 } 195 196 197 /*! Populate fields with current settings. 198 */ 199 status_t 200 InterfaceView::_Update(bool updateWirelessNetworks) 201 { 202 BNetworkDevice device(fInterface.Name()); 203 bool isWireless = device.IsWireless(); 204 bool disabled = (fInterface.Flags() & IFF_UP) == 0; 205 206 if (fInterface.HasLink()) 207 fStatusField->SetText(B_TRANSLATE("connected")); 208 else 209 fStatusField->SetText(B_TRANSLATE("disconnected")); 210 211 BNetworkAddress hardwareAddress; 212 if (device.GetHardwareAddress(hardwareAddress) == B_OK) 213 fMacAddressField->SetText(hardwareAddress.ToString()); 214 else 215 fMacAddressField->SetText("-"); 216 217 int media = fInterface.Media(); 218 if ((media & IFM_ACTIVE) != 0) 219 fLinkSpeedField->SetText(media_type_to_string(media)); 220 else 221 fLinkSpeedField->SetText("-"); 222 223 // Update Link stats 224 ifreq_stats stats; 225 if (fInterface.GetStats(stats) == B_OK) { 226 char buffer[100]; 227 228 string_for_size(stats.send.bytes, buffer, sizeof(buffer)); 229 fLinkTxField->SetText(buffer); 230 231 string_for_size(stats.receive.bytes, buffer, sizeof(buffer)); 232 fLinkRxField->SetText(buffer); 233 } 234 235 // TODO: move the wireless info to a separate tab. We should have a 236 // BListView of available networks, rather than a menu, to make them more 237 // readable and easier to browse and select. 238 if (fNetworkMenuField->IsHidden(fNetworkMenuField) && isWireless) 239 fNetworkMenuField->Show(); 240 else if (!fNetworkMenuField->IsHidden(fNetworkMenuField) && !isWireless) 241 fNetworkMenuField->Hide(); 242 243 if (isWireless && updateWirelessNetworks) { 244 // Rebuild network menu 245 BMenu* menu = fNetworkMenuField->Menu(); 246 menu->RemoveItems(0, menu->CountItems(), true); 247 248 std::set<BNetworkAddress> associated; 249 BNetworkAddress address; 250 uint32 cookie = 0; 251 while (device.GetNextAssociatedNetwork(cookie, address) == B_OK) 252 associated.insert(address); 253 254 wireless_network network; 255 int32 count = 0; 256 cookie = 0; 257 if ((fPulseCount % 15) == 0 258 && device.GetNextNetwork(cookie, network) != B_OK) { 259 // We don't seem to know of any networks, and it's been long 260 // enough since the last scan, so trigger one to try and 261 // find some networks. 262 device.Scan(false, false); 263 264 // We don't want to block for the full length of the scan, but 265 // 50ms is often more than enough to find at least one network, 266 // and the increase in perceived QoS to the user of not seeing 267 // "no wireless networks" if we can avoid it is great enough 268 // to merit such a wait. It's only just over ~4 vertical 269 // retraces, anyway. 270 snooze(50 * 1000); 271 } 272 273 cookie = 0; 274 while (device.GetNextNetwork(cookie, network) == B_OK) { 275 BMessage* message = new BMessage(kMsgJoinNetwork); 276 277 message->AddString("device", fInterface.Name()); 278 message->AddString("name", network.name); 279 message->AddFlat("address", &network.address); 280 281 BMenuItem* item = new WirelessNetworkMenuItem(network.name, 282 network.signal_strength, 283 network.authentication_mode, message); 284 if (associated.find(network.address) != associated.end()) 285 item->SetMarked(true); 286 menu->AddItem(item); 287 288 count++; 289 } 290 if (count == 0) { 291 BMenuItem* item = new BMenuItem( 292 B_TRANSLATE("<no wireless networks found>"), NULL); 293 item->SetEnabled(false); 294 menu->AddItem(item); 295 } else { 296 BMenuItem* item = new BMenuItem( 297 B_TRANSLATE("Choose automatically"), NULL); 298 if (menu->FindMarked() == NULL) 299 item->SetMarked(true); 300 menu->AddItem(item, 0); 301 menu->AddItem(new BSeparatorItem(), 1); 302 } 303 menu->SetTargetForItems(this); 304 } 305 306 //fRenegotiateButton->SetEnabled(!disabled); 307 fToggleButton->SetLabel(disabled 308 ? B_TRANSLATE("Enable") : B_TRANSLATE("Disable")); 309 310 return B_OK; 311 } 312