2 * Copyright 2004-2015 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Adrien Destugues, <pulkomandy@pulkomandy.tk>
7 * Axel Dörfler, <axeld@pinc-software.de>
8 * Alexander von Gluck, <kallisti5@unixzen.com>
12 #include "NetworkWindow.h"
20 #include <Application.h>
24 #include <ControlLook.h>
26 #include <Directory.h>
27 #include <LayoutBuilder.h>
28 #include <NetworkDevice.h>
29 #include <NetworkInterface.h>
30 #include <NetworkNotifications.h>
31 #include <NetworkRoster.h>
32 #include <OutlineListView.h>
34 #include <PathFinder.h>
35 #include <PathMonitor.h>
37 #include <ScrollView.h>
38 #include <StringItem.h>
41 #define ENABLE_PROFILES 0
43 # include <PopUpMenu.h>
46 #include "InterfaceListItem.h"
47 #include "InterfaceView.h"
48 #include "ServiceListItem.h"
51 const char* kNetworkStatusSignature
= "application/x-vnd.Haiku-NetworkStatus";
53 static const uint32 kMsgProfileSelected
= 'prof';
54 static const uint32 kMsgProfileManage
= 'mngp';
55 static const uint32 kMsgProfileNew
= 'newp';
56 static const uint32 kMsgRevert
= 'rvrt';
57 static const uint32 kMsgToggleReplicant
= 'trep';
58 static const uint32 kMsgItemSelected
= 'ItSl';
60 BMessenger gNetworkWindow
;
63 #undef B_TRANSLATION_CONTEXT
64 #define B_TRANSLATION_CONTEXT "NetworkWindow"
67 class TitleItem
: public BStringItem
{
69 TitleItem(const char* title
)
75 void DrawItem(BView
* owner
, BRect bounds
, bool complete
)
77 owner
->SetFont(be_bold_font
);
78 BStringItem::DrawItem(owner
, bounds
, complete
);
79 owner
->SetFont(be_plain_font
);
82 void Update(BView
* owner
, const BFont
* font
)
84 BStringItem::Update(owner
, be_bold_font
);
92 NetworkWindow::NetworkWindow()
94 BWindow(BRect(100, 100, 400, 400), B_TRANSLATE("Network"), B_TITLED_WINDOW
,
95 B_ASYNCHRONOUS_CONTROLS
| B_NOT_ZOOMABLE
| B_AUTO_UPDATE_SIZE_LIMITS
),
103 BPopUpMenu
* profilesPopup
= new BPopUpMenu("<none>");
104 _BuildProfilesMenu(profilesPopup
, kMsgProfileSelected
);
106 BMenuField
* profilesMenuField
= new BMenuField("profiles_menu",
107 B_TRANSLATE("Profile:"), profilesPopup
);
109 profilesMenuField
->SetFont(be_bold_font
);
110 profilesMenuField
->SetEnabled(false);
115 fRevertButton
= new BButton("revert", B_TRANSLATE("Revert"),
116 new BMessage(kMsgRevert
));
118 BMessage
* message
= new BMessage(kMsgToggleReplicant
);
119 BCheckBox
* showReplicantCheckBox
= new BCheckBox("showReplicantCheckBox",
120 B_TRANSLATE("Show network status in Deskbar"), message
);
121 showReplicantCheckBox
->SetExplicitMaxSize(
122 BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
123 showReplicantCheckBox
->SetValue(_IsReplicantInstalled());
125 fListView
= new BOutlineListView("list", B_SINGLE_SELECTION_LIST
,
126 B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
| B_FRAME_EVENTS
| B_NAVIGABLE
);
127 fListView
->SetSelectionMessage(new BMessage(kMsgItemSelected
));
129 BScrollView
* scrollView
= new BScrollView("ScrollView", fListView
,
131 scrollView
->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
133 fAddOnShellView
= new BView("add-on shell", 0,
134 new BGroupLayout(B_VERTICAL
));
135 fAddOnShellView
->SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
137 fInterfaceView
= new InterfaceView();
140 BLayoutBuilder::Group
<>(this, B_VERTICAL
)
141 .SetInsets(B_USE_WINDOW_SPACING
)
144 .AddGroup(B_HORIZONTAL
, B_USE_SMALL_SPACING
)
145 .Add(profilesMenuField
)
149 .AddGroup(B_HORIZONTAL
, B_USE_DEFAULT_SPACING
)
151 .Add(fAddOnShellView
)
153 .Add(showReplicantCheckBox
)
154 .AddGroup(B_HORIZONTAL
, B_USE_DEFAULT_SPACING
)
159 gNetworkWindow
= this;
163 _UpdateRevertButton();
165 fListView
->Select(0);
166 _SelectItem(fListView
->ItemAt(0));
167 // Call this manually, so that CenterOnScreen() below already
168 // knows the final window size.
170 // Set size of the list view from its contents
173 fListView
->GetPreferredSize(&width
, &height
);
174 width
+= 2 * be_control_look
->DefaultItemSpacing();
175 fListView
->SetExplicitSize(BSize(width
, B_SIZE_UNSET
));
176 fListView
->SetExplicitMinSize(BSize(width
, std::min(height
, 400.f
)));
180 fSettings
.StartMonitoring(this);
181 start_watching_network(B_WATCH_NETWORK_INTERFACE_CHANGES
182 | B_WATCH_NETWORK_LINK_CHANGES
| B_WATCH_NETWORK_WLAN_CHANGES
, this);
186 NetworkWindow::~NetworkWindow()
188 stop_watching_network(this);
189 fSettings
.StopMonitoring(this);
194 NetworkWindow::QuitRequested()
196 be_app
->PostMessage(B_QUIT_REQUESTED
);
202 NetworkWindow::MessageReceived(BMessage
* message
)
204 switch (message
->what
) {
208 case kMsgProfileSelected
:
211 if (message
->FindString("path", &path
) != B_OK
)
218 case kMsgItemSelected
:
220 BListItem
* listItem
= fListView
->FullListItemAt(
221 fListView
->FullListCurrentSelection());
222 if (listItem
== NULL
)
225 _SelectItem(listItem
);
231 SettingsMap::const_iterator iterator
= fSettingsMap
.begin();
232 for (; iterator
!= fSettingsMap
.end(); iterator
++)
233 iterator
->second
->Revert();
237 case kMsgToggleReplicant
:
240 message
->GetInt32("be:value", B_CONTROL_OFF
) == B_CONTROL_ON
);
246 fSettings
.Update(message
);
250 case B_NETWORK_MONITOR
:
251 _BroadcastConfigurationUpdate(*message
);
254 case BNetworkSettings::kMsgInterfaceSettingsUpdated
:
255 case BNetworkSettings::kMsgNetworkSettingsUpdated
:
256 case BNetworkSettings::kMsgServiceSettingsUpdated
:
257 _BroadcastSettingsUpdate(message
->what
);
260 case kMsgSettingsItemUpdated
:
261 // TODO: update list item
262 _UpdateRevertButton();
266 inherited::MessageReceived(message
);
272 NetworkWindow::_BuildProfilesMenu(BMenu
* menu
, int32 what
)
274 char currentProfile
[256] = { 0 };
276 menu
->SetRadioMode(true);
278 BDirectory
dir("/boot/system/settings/network/profiles");
279 if (dir
.InitCheck() == B_OK
) {
283 while (dir
.GetNextEntry(&entry
) >= 0) {
285 entry
.GetPath(&name
);
287 if (entry
.IsSymLink() &&
288 strcmp("current", name
.Leaf()) == 0) {
289 BSymLink
symlink(&entry
);
291 if (symlink
.IsAbsolute())
292 // oh oh, sorry, wrong symlink...
295 symlink
.ReadLink(currentProfile
, sizeof(currentProfile
));
299 if (!entry
.IsDirectory())
302 BMessage
* message
= new BMessage(what
);
303 message
->AddString("path", name
.Path());
305 BMenuItem
* item
= new BMenuItem(name
.Leaf(), message
);
310 menu
->AddSeparatorItem();
311 menu
->AddItem(new BMenuItem(B_TRANSLATE("New" B_UTF8_ELLIPSIS
),
312 new BMessage(kMsgProfileNew
)));
313 menu
->AddItem(new BMenuItem(B_TRANSLATE("Manage" B_UTF8_ELLIPSIS
),
314 new BMessage(kMsgProfileManage
)));
316 if (currentProfile
[0] != '\0') {
317 BMenuItem
* item
= menu
->FindItem(currentProfile
);
320 BString
label(item
->Label());
321 label
<< " (current)";
322 item
->SetLabel(label
.String());
323 item
->SetMarked(true);
330 NetworkWindow::_ScanInterfaces()
332 // Try existing devices first
333 BNetworkRoster
& roster
= BNetworkRoster::Default();
334 BNetworkInterface interface
;
337 while (roster
.GetNextInterface(&cookie
, interface
) == B_OK
) {
338 if ((interface
.Flags() & IFF_LOOPBACK
) != 0)
341 BNetworkDevice
device(interface
.Name());
342 BNetworkInterfaceType type
= B_NETWORK_INTERFACE_TYPE_OTHER
;
344 if (device
.IsWireless())
345 type
= B_NETWORK_INTERFACE_TYPE_WIFI
;
346 else if (device
.IsEthernet())
347 type
= B_NETWORK_INTERFACE_TYPE_ETHERNET
;
349 InterfaceListItem
* item
= new InterfaceListItem(interface
.Name(), type
);
350 item
->SetExpanded(true);
352 fInterfaceItemMap
.insert(std::pair
<BString
, InterfaceListItem
*>(
353 BString(interface
.Name()), item
));
354 fListView
->AddItem(item
);
357 // TODO: Then consider those from the settings (for example, for USB)
362 NetworkWindow::_ScanAddOns()
365 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY
, "Network Settings",
368 // Collect add-on paths by name, so that each name will only be
370 typedef std::map
<BString
, BPath
> PathMap
;
373 for (int32 i
= 0; i
< paths
.CountStrings(); i
++) {
374 BDirectory
directory(paths
.StringAt(i
));
376 while (directory
.GetNextEntry(&entry
) == B_OK
) {
378 if (entry
.GetPath(&path
) != B_OK
)
381 if (addOnMap
.find(path
.Leaf()) == addOnMap
.end())
382 addOnMap
.insert(std::pair
<BString
, BPath
>(path
.Leaf(), path
));
386 for (PathMap::const_iterator addOnIterator
= addOnMap
.begin();
387 addOnIterator
!= addOnMap
.end(); addOnIterator
++) {
388 const BPath
& path
= addOnIterator
->second
;
390 image_id image
= load_add_on(path
.Path());
392 printf("Failed to load %s addon: %s.\n", path
.Path(),
397 BNetworkSettingsAddOn
* (*instantiateAddOn
)(image_id image
,
398 BNetworkSettings
& settings
);
400 status_t status
= get_image_symbol(image
,
401 "instantiate_network_settings_add_on",
402 B_SYMBOL_TYPE_TEXT
, (void**)&instantiateAddOn
);
403 if (status
!= B_OK
) {
404 // No "addon instantiate function" symbol found in this addon
405 printf("No symbol \"instantiate_network_settings_add_on\" found "
406 "in %s addon: not a network setup addon!\n", path
.Path());
407 unload_add_on(image
);
411 BNetworkSettingsAddOn
* addOn
= instantiateAddOn(image
, fSettings
);
413 unload_add_on(image
);
417 fAddOns
.AddItem(addOn
);
419 // Per interface items
420 ItemMap::const_iterator iterator
= fInterfaceItemMap
.begin();
421 for (; iterator
!= fInterfaceItemMap
.end(); iterator
++) {
422 const BString
& interface
= iterator
->first
;
423 BListItem
* interfaceItem
= iterator
->second
;
427 BNetworkSettingsItem
* item
= addOn
->CreateNextInterfaceItem(
428 cookie
, interface
.String());
432 fSettingsMap
[item
->ListItem()] = item
;
433 fListView
->AddUnder(item
->ListItem(), interfaceItem
);
435 fListView
->SortItemsUnder(interfaceItem
, true,
436 NetworkWindow::_CompareListItems
);
442 BNetworkSettingsItem
* item
= addOn
->CreateNextItem(cookie
);
446 fSettingsMap
[item
->ListItem()] = item
;
447 fListView
->AddUnder(item
->ListItem(),
448 _ListItemFor(item
->Type()));
451 _SortItemsUnder(fServicesItem
);
452 _SortItemsUnder(fDialUpItem
);
453 _SortItemsUnder(fVPNItem
);
454 _SortItemsUnder(fOtherItem
);
457 fListView
->SortItemsUnder(NULL
, true,
458 NetworkWindow::_CompareTopLevelListItems
);
462 BNetworkSettingsItem
*
463 NetworkWindow::_SettingsItemFor(BListItem
* item
)
465 SettingsMap::const_iterator found
= fSettingsMap
.find(item
);
466 if (found
!= fSettingsMap
.end())
467 return found
->second
;
474 NetworkWindow::_SortItemsUnder(BListItem
* item
)
477 fListView
->SortItemsUnder(item
, true, NetworkWindow::_CompareListItems
);
482 NetworkWindow::_ListItemFor(BNetworkSettingsType type
)
485 case B_NETWORK_SETTINGS_TYPE_SERVICE
:
486 if (fServicesItem
== NULL
)
487 fServicesItem
= _CreateItem(B_TRANSLATE("Services"));
488 return fServicesItem
;
490 case B_NETWORK_SETTINGS_TYPE_DIAL_UP
:
491 if (fDialUpItem
== NULL
)
492 fDialUpItem
= _CreateItem(B_TRANSLATE("Dial Up"));
495 case B_NETWORK_SETTINGS_TYPE_VPN
:
496 if (fVPNItem
== NULL
)
497 fVPNItem
= _CreateItem(B_TRANSLATE("VPN"));
500 case B_NETWORK_SETTINGS_TYPE_OTHER
:
501 if (fOtherItem
== NULL
)
502 fOtherItem
= _CreateItem(B_TRANSLATE("Other"));
512 NetworkWindow::_CreateItem(const char* label
)
514 BListItem
* item
= new TitleItem(label
);
515 item
->SetExpanded(true);
516 fListView
->AddItem(item
);
522 NetworkWindow::_SelectItem(BListItem
* listItem
)
524 while (fAddOnShellView
->CountChildren() > 0)
525 fAddOnShellView
->ChildAt(0)->RemoveSelf();
527 BView
* nextView
= NULL
;
529 BNetworkSettingsItem
* item
= _SettingsItemFor(listItem
);
531 nextView
= item
->View();
533 InterfaceListItem
* item
= dynamic_cast<InterfaceListItem
*>(
536 fInterfaceView
->SetTo(item
->Name());
537 nextView
= fInterfaceView
;
541 if (nextView
!= NULL
)
542 fAddOnShellView
->AddChild(nextView
);
547 NetworkWindow::_BroadcastSettingsUpdate(uint32 type
)
549 for (int32 index
= 0; index
< fListView
->FullListCountItems(); index
++) {
550 BNetworkSettingsListener
* listener
551 = dynamic_cast<BNetworkSettingsListener
*>(
552 fListView
->FullListItemAt(index
));
553 if (listener
!= NULL
)
554 listener
->SettingsUpdated(type
);
557 SettingsMap::const_iterator iterator
= fSettingsMap
.begin();
558 for (; iterator
!= fSettingsMap
.end(); iterator
++)
559 iterator
->second
->SettingsUpdated(type
);
561 _UpdateRevertButton();
566 NetworkWindow::_BroadcastConfigurationUpdate(const BMessage
& message
)
568 for (int32 index
= 0; index
< fListView
->FullListCountItems(); index
++) {
569 BNetworkConfigurationListener
* listener
570 = dynamic_cast<BNetworkConfigurationListener
*>(
571 fListView
->FullListItemAt(index
));
572 if (listener
!= NULL
)
573 listener
->ConfigurationUpdated(message
);
576 SettingsMap::const_iterator iterator
= fSettingsMap
.begin();
577 for (; iterator
!= fSettingsMap
.end(); iterator
++)
578 iterator
->second
->ConfigurationUpdated(message
);
580 // TODO: improve invalidated region to the one that matters
581 fListView
->Invalidate();
582 _UpdateRevertButton();
587 NetworkWindow::_UpdateRevertButton()
589 bool enabled
= false;
590 SettingsMap::const_iterator iterator
= fSettingsMap
.begin();
591 for (; iterator
!= fSettingsMap
.end(); iterator
++) {
592 if (iterator
->second
->IsRevertable()) {
598 fRevertButton
->SetEnabled(enabled
);
603 NetworkWindow::_ShowReplicant(bool show
)
606 const char* argv
[] = {"--deskbar", NULL
};
608 status_t status
= be_roster
->Launch(kNetworkStatusSignature
, 1, argv
);
609 if (status
!= B_OK
) {
610 BString errorMessage
;
611 errorMessage
.SetToFormat(
612 B_TRANSLATE("Installing NetworkStatus in Deskbar failed: %s"),
614 BAlert
* alert
= new BAlert(B_TRANSLATE("launch error"),
615 errorMessage
, B_TRANSLATE("Ok"));
620 deskbar
.RemoveItem("NetworkStatus");
626 NetworkWindow::_IsReplicantInstalled()
629 return deskbar
.HasItem("NetworkStatus");
633 /*static*/ const char*
634 NetworkWindow::_ItemName(const BListItem
* item
)
636 if (const BNetworkInterfaceListItem
* listItem
= dynamic_cast<
637 const BNetworkInterfaceListItem
*>(item
))
638 return listItem
->Label();
640 if (const ServiceListItem
* listItem
= dynamic_cast<
641 const ServiceListItem
*>(item
))
642 return listItem
->Label();
644 if (const BStringItem
* stringItem
= dynamic_cast<const BStringItem
*>(item
))
645 return stringItem
->Text();
652 NetworkWindow::_CompareTopLevelListItems(const BListItem
* a
, const BListItem
* b
)
657 if (const InterfaceListItem
* itemA
658 = dynamic_cast<const InterfaceListItem
*>(a
)) {
659 if (const InterfaceListItem
* itemB
660 = dynamic_cast<const InterfaceListItem
*>(b
)) {
661 return strcasecmp(itemA
->Name(), itemB
->Name());
664 } else if (dynamic_cast<const InterfaceListItem
*>(b
) != NULL
)
667 if (a == fDialUpItem)
669 if (b == fDialUpItem)
672 if (a == fServicesItem)
674 if (b == fServicesItem)
677 return _CompareListItems(a
, b
);
682 NetworkWindow::_CompareListItems(const BListItem
* a
, const BListItem
* b
)
687 const char* nameA
= _ItemName(a
);
688 const char* nameB
= _ItemName(b
);
690 if (nameA
!= NULL
&& nameB
!= NULL
)
691 return strcasecmp(nameA
, nameB
);
697 return (addr_t
)a
> (addr_t
)b
? 1 : -1;