From 9e51c5bb15149f2bebb603334842ecbacfbe8a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E7=82=9C?= Date: Thu, 2 Jun 2022 16:27:45 +0800 Subject: [PATCH] Import Upstream version 3.14.0.0+0512 --- AUTHORS | 1 + COPYING | 674 ++++++ README.md | 189 ++ kylin-nm.pro | 13 + man/kylin-nm.1 | 20 + nmqrc.qrc | 132 ++ plugins/component/DrownLabel/drownlabel.cpp | 36 + plugins/component/DrownLabel/drownlabel.h | 27 + plugins/component/InfoButton/infobutton.cpp | 105 + plugins/component/InfoButton/infobutton.h | 35 + .../component/SwitchButton/switchbutton.cpp | 327 +++ plugins/component/SwitchButton/switchbutton.h | 121 + plugins/component/drownlabel.pri | 9 + plugins/component/infobutton.pri | 9 + plugins/component/switchbutton.pri | 8 + plugins/mobilehotspot/mobilehotspot.cpp | 155 ++ plugins/mobilehotspot/mobilehotspot.h | 78 + plugins/mobilehotspot/mobilehotspot.pro | 42 + plugins/mobilehotspot/mobilehotspotwidget.cpp | 678 ++++++ plugins/mobilehotspot/mobilehotspotwidget.h | 119 + plugins/mobilehotspot/translations/bo.qm | 1 + plugins/mobilehotspot/translations/bo.ts | 104 + plugins/mobilehotspot/translations/tr.qm | 1 + plugins/mobilehotspot/translations/tr.ts | 104 + plugins/mobilehotspot/translations/zh_CN.qm | Bin 0 -> 1489 bytes plugins/mobilehotspot/translations/zh_CN.ts | 104 + plugins/netconnect/addnetbtn.cpp | 70 + plugins/netconnect/addnetbtn.h | 27 + plugins/netconnect/deviceframe.cpp | 46 + plugins/netconnect/deviceframe.h | 33 + plugins/netconnect/itemframe.cpp | 47 + plugins/netconnect/itemframe.h | 33 + plugins/netconnect/lanitem.cpp | 81 + plugins/netconnect/lanitem.h | 51 + plugins/netconnect/netconnect.cpp | 883 +++++++ plugins/netconnect/netconnect.h | 156 ++ plugins/netconnect/netconnect.pro | 53 + plugins/netconnect/netconnect.ui | 244 ++ plugins/netconnect/translations/bo.qm | 1 + plugins/netconnect/translations/bo.ts | 66 + plugins/netconnect/translations/tr.qm | 1 + plugins/netconnect/translations/tr.ts | 66 + plugins/netconnect/translations/zh_CN.qm | Bin 0 -> 661 bytes plugins/netconnect/translations/zh_CN.ts | 66 + plugins/plugin.pro | 5 + plugins/wlanconnect/deviceframe.cpp | 48 + plugins/wlanconnect/deviceframe.h | 30 + plugins/wlanconnect/itemframe.cpp | 61 + plugins/wlanconnect/itemframe.h | 33 + plugins/wlanconnect/translations/bo.qm | 1 + plugins/wlanconnect/translations/bo.ts | 60 + plugins/wlanconnect/translations/tr.qm | 1 + plugins/wlanconnect/translations/tr.ts | 60 + plugins/wlanconnect/translations/zh_CN.qm | Bin 0 -> 604 bytes plugins/wlanconnect/translations/zh_CN.ts | 60 + plugins/wlanconnect/wlanconnect.cpp | 1007 ++++++++ plugins/wlanconnect/wlanconnect.h | 165 ++ plugins/wlanconnect/wlanconnect.pro | 51 + plugins/wlanconnect/wlanconnect.ui | 192 ++ plugins/wlanconnect/wlanitem.cpp | 90 + plugins/wlanconnect/wlanitem.h | 52 + res.qrc | 1 + res/g/close_black.png | Bin 0 -> 205 bytes res/g/close_white.png | Bin 0 -> 152 bytes res/g/down_arrow.png | Bin 0 -> 113 bytes res/h/hide-pwd.png | Bin 0 -> 1452 bytes res/h/no-pwd-wifi.png | Bin 0 -> 1643 bytes res/h/right-pwd.png | Bin 0 -> 1398 bytes res/h/show-pwd.png | Bin 0 -> 1289 bytes res/hw/wifi-full-pwd.png | Bin 0 -> 1806 bytes res/hw/wifi-full.png | Bin 0 -> 557 bytes res/hw/wifi-high-pwd.png | Bin 0 -> 682 bytes res/hw/wifi-high.png | Bin 0 -> 605 bytes res/hw/wifi-low-pwd.png | Bin 0 -> 651 bytes res/hw/wifi-low.png | Bin 0 -> 589 bytes res/hw/wifi-medium-pwd.png | Bin 0 -> 663 bytes res/hw/wifi-medium.png | Bin 0 -> 606 bytes res/hw/wifi-none-pwd.png | Bin 0 -> 634 bytes res/hw/wifi-none.png | Bin 0 -> 565 bytes res/hw/wifi6+-full-pwd.png | Bin 0 -> 737 bytes res/hw/wifi6+-full.png | Bin 0 -> 665 bytes res/hw/wifi6+-high-pwd.png | Bin 0 -> 781 bytes res/hw/wifi6+-high.png | Bin 0 -> 705 bytes res/hw/wifi6+-low-pwd.png | Bin 0 -> 780 bytes res/hw/wifi6+-low.png | Bin 0 -> 704 bytes res/hw/wifi6+-medium-pwd.png | Bin 0 -> 781 bytes res/hw/wifi6+-medium.png | Bin 0 -> 712 bytes res/hw/wifi6+-none-pwd.png | Bin 0 -> 765 bytes res/hw/wifi6+-none.png | Bin 0 -> 690 bytes res/hw/wifi6-full-pwd.png | Bin 0 -> 762 bytes res/hw/wifi6-full.png | Bin 0 -> 690 bytes res/hw/wifi6-high-pwd.png | Bin 0 -> 803 bytes res/hw/wifi6-high.png | Bin 0 -> 733 bytes res/hw/wifi6-low-pwd.png | Bin 0 -> 806 bytes res/hw/wifi6-low.png | Bin 0 -> 755 bytes res/hw/wifi6-medium-pwd.png | Bin 0 -> 812 bytes res/hw/wifi6-medium.png | Bin 0 -> 742 bytes res/hw/wifi6-none-pwd.png | Bin 0 -> 789 bytes res/hw/wifi6-none.png | Bin 0 -> 718 bytes res/l/network-offline.png | Bin 0 -> 506 bytes res/l/network-offline.svg | 23 + res/l/network-online.png | Bin 0 -> 406 bytes res/l/network-online.svg | 1 + res/s/conning-a/1.png | Bin 0 -> 351 bytes res/s/conning-a/2.png | Bin 0 -> 3170 bytes res/s/conning-a/3.png | Bin 0 -> 349 bytes res/s/conning-a/4.png | Bin 0 -> 3180 bytes res/s/conning-a/5.png | Bin 0 -> 355 bytes res/s/conning-a/6.png | Bin 0 -> 3173 bytes res/s/conning-a/7.png | Bin 0 -> 359 bytes res/s/conning-a/8.png | Bin 0 -> 3201 bytes res/s/conning-b/1.png | Bin 0 -> 7242 bytes res/s/conning-b/10.png | Bin 0 -> 7873 bytes res/s/conning-b/11.png | Bin 0 -> 7879 bytes res/s/conning-b/12.png | Bin 0 -> 7847 bytes res/s/conning-b/2.png | Bin 0 -> 7844 bytes res/s/conning-b/3.png | Bin 0 -> 7820 bytes res/s/conning-b/4.png | Bin 0 -> 7887 bytes res/s/conning-b/5.png | Bin 0 -> 7927 bytes res/s/conning-b/6.png | Bin 0 -> 7850 bytes res/s/conning-b/7.png | Bin 0 -> 7340 bytes res/s/conning-b/8.png | Bin 0 -> 7833 bytes res/s/conning-b/9.png | Bin 0 -> 7867 bytes res/s/conning-s/1.png | Bin 0 -> 652 bytes res/s/conning-s/10.png | Bin 0 -> 627 bytes res/s/conning-s/11.png | Bin 0 -> 646 bytes res/s/conning-s/12.png | Bin 0 -> 623 bytes res/s/conning-s/1x/1.png | Bin 0 -> 354 bytes res/s/conning-s/1x/10.png | Bin 0 -> 355 bytes res/s/conning-s/1x/11.png | Bin 0 -> 345 bytes res/s/conning-s/1x/12.png | Bin 0 -> 364 bytes res/s/conning-s/1x/2.png | Bin 0 -> 376 bytes res/s/conning-s/1x/3.png | Bin 0 -> 367 bytes res/s/conning-s/1x/4.png | Bin 0 -> 405 bytes res/s/conning-s/1x/5.png | Bin 0 -> 411 bytes res/s/conning-s/1x/6.png | Bin 0 -> 406 bytes res/s/conning-s/1x/7.png | Bin 0 -> 381 bytes res/s/conning-s/1x/8.png | Bin 0 -> 362 bytes res/s/conning-s/1x/9.png | Bin 0 -> 354 bytes res/s/conning-s/2.png | Bin 0 -> 709 bytes res/s/conning-s/3.png | Bin 0 -> 686 bytes res/s/conning-s/4.png | Bin 0 -> 769 bytes res/s/conning-s/5.png | Bin 0 -> 787 bytes res/s/conning-s/6.png | Bin 0 -> 673 bytes res/s/conning-s/7.png | Bin 0 -> 736 bytes res/s/conning-s/8.png | Bin 0 -> 664 bytes res/s/conning-s/9.png | Bin 0 -> 613 bytes res/s/rescan/1.png | Bin 0 -> 15281 bytes res/s/rescan/10.png | Bin 0 -> 15390 bytes res/s/rescan/11.png | Bin 0 -> 15383 bytes res/s/rescan/12.png | Bin 0 -> 15316 bytes res/s/rescan/2.png | Bin 0 -> 15386 bytes res/s/rescan/3.png | Bin 0 -> 15391 bytes res/s/rescan/4.png | Bin 0 -> 15397 bytes res/s/rescan/5.png | Bin 0 -> 15381 bytes res/s/rescan/6.png | Bin 0 -> 15393 bytes res/s/rescan/7.png | Bin 0 -> 15271 bytes res/s/rescan/8.png | Bin 0 -> 15309 bytes res/s/rescan/9.png | Bin 0 -> 15391 bytes res/w/wifi-full-pwd.png | Bin 0 -> 1806 bytes res/w/wifi-full.png | Bin 0 -> 557 bytes res/w/wifi-high-pwd.png | Bin 0 -> 682 bytes res/w/wifi-high.png | Bin 0 -> 605 bytes res/w/wifi-low-pwd.png | Bin 0 -> 651 bytes res/w/wifi-low.png | Bin 0 -> 589 bytes res/w/wifi-medium-pwd.png | Bin 0 -> 663 bytes res/w/wifi-medium.png | Bin 0 -> 606 bytes res/w/wifi-none-pwd.png | Bin 0 -> 634 bytes res/w/wifi-none.png | Bin 0 -> 565 bytes res/w/wifi6+-full-pwd.png | Bin 0 -> 784 bytes res/w/wifi6+-full.png | Bin 0 -> 732 bytes res/w/wifi6+-high-pwd.png | Bin 0 -> 830 bytes res/w/wifi6+-high.png | Bin 0 -> 787 bytes res/w/wifi6+-low-pwd.png | Bin 0 -> 806 bytes res/w/wifi6+-low.png | Bin 0 -> 775 bytes res/w/wifi6+-medium-pwd.png | Bin 0 -> 833 bytes res/w/wifi6+-medium.png | Bin 0 -> 787 bytes res/w/wifi6+-meidum-pwd.png | Bin 0 -> 828 bytes res/w/wifi6+-none.png | Bin 0 -> 759 bytes res/w/wifi6-full-pwd.png | Bin 0 -> 761 bytes res/w/wifi6-full.png | Bin 0 -> 711 bytes res/w/wifi6-high-pwd.png | Bin 0 -> 812 bytes res/w/wifi6-high.png | Bin 0 -> 765 bytes res/w/wifi6-low-pwd.png | Bin 0 -> 804 bytes res/w/wifi6-low.png | Bin 0 -> 755 bytes res/w/wifi6-medium-pwd.png | Bin 0 -> 814 bytes res/w/wifi6-medium.png | Bin 0 -> 765 bytes res/w/wifi6-none-pwd.png | Bin 0 -> 783 bytes res/w/wifi6-none.png | Bin 0 -> 738 bytes res/x/control.svg | 15 + res/x/fly-mode-off.svg | 19 + res/x/fly-mode-on.svg | 14 + res/x/hot-spot-off.svg | 22 + res/x/hot-spot-on.svg | 17 + res/x/load-down.png | Bin 0 -> 192 bytes res/x/load-up.png | Bin 0 -> 194 bytes res/x/net-list-bg.svg | 16 + res/x/setup.png | Bin 0 -> 2152 bytes res/x/wifi-list-bg.svg | 15 + src/backend/backend.pri | 22 + src/backend/dbus-interface/dbus-interface.pri | 46 + .../dbus-interface/gsystem-local-alloc.h | 188 ++ .../kyenterpricesettinginfo.cpp | 176 ++ .../dbus-interface/kyenterpricesettinginfo.h | 146 ++ .../kylinactiveconnectresource.cpp | 717 ++++++ .../kylinactiveconnectresource.h | 74 + src/backend/dbus-interface/kylinagent.c | 836 +++++++ src/backend/dbus-interface/kylinagent.h | 60 + .../dbus-interface/kylinagentinterface.c | 428 ++++ .../dbus-interface/kylinagentinterface.h | 10 + .../dbus-interface/kylinapconnectitem.cpp | 19 + .../dbus-interface/kylinapconnectitem.h | 25 + .../kylinbluetoothconnectitem.cpp | 29 + .../kylinbluetoothconnectitem.h | 33 + .../dbus-interface/kylinconnectitem.cpp | 35 + src/backend/dbus-interface/kylinconnectitem.h | 31 + .../dbus-interface/kylinconnectoperation.cpp | 248 ++ .../dbus-interface/kylinconnectoperation.h | 44 + .../dbus-interface/kylinconnectresource.cpp | 777 ++++++ .../dbus-interface/kylinconnectresource.h | 69 + .../dbus-interface/kylinconnectsetting.cpp | 133 ++ .../dbus-interface/kylinconnectsetting.h | 72 + .../kylinnetworkdeviceresource.cpp | 310 +++ .../kylinnetworkdeviceresource.h | 55 + .../kylinnetworkresourcemanager.cpp | 1019 ++++++++ .../kylinnetworkresourcemanager.h | 207 ++ src/backend/dbus-interface/kylinutil.cpp | 85 + src/backend/dbus-interface/kylinutil.h | 20 + .../dbus-interface/kylinvpnconnectitem.cpp | 23 + .../dbus-interface/kylinvpnconnectitem.h | 30 + src/backend/dbus-interface/kylinvpnrequest.c | 762 ++++++ src/backend/dbus-interface/kylinvpnrequest.h | 30 + .../kylinwiredconnectoperation.cpp | 304 +++ .../kylinwiredconnectoperation.h | 36 + .../kywirelessconnectoperation.cpp | 1058 +++++++++ .../kywirelessconnectoperation.h | 140 ++ .../dbus-interface/kywirelessnetitem.cpp | 114 + .../dbus-interface/kywirelessnetitem.h | 45 + .../dbus-interface/kywirelessnetresource.cpp | 732 ++++++ .../dbus-interface/kywirelessnetresource.h | 76 + .../dbus-interface/nm-macros-internal.h | 1291 ++++++++++ src/backend/dbusadaptor.cpp | 250 ++ src/backend/dbusadaptor.h | 120 + src/backend/hotspot/dlghotspotcreate.cpp | 153 ++ src/backend/hotspot/dlghotspotcreate.h | 69 + src/backend/hotspot/dlghotspotcreate.ui | 169 ++ src/backend/hotspot/hotspot.pri | 11 + src/backend/kylinarping.h | 101 + src/backend/kylinipv4arping.cpp | 432 ++++ src/backend/kylinipv4arping.h | 88 + src/backend/kylinipv6arping.cpp | 405 ++++ src/backend/kylinipv6arping.h | 109 + src/backend/sysdbusregister.cpp | 83 + src/backend/sysdbusregister.h | 57 + src/backend/utils.cpp | 151 ++ src/backend/utils.h | 112 + src/backend/wifi-auth-thread.cpp | 53 + src/backend/wifi-auth-thread.h | 25 + src/frontend/customstyle.cpp | 152 ++ src/frontend/customstyle.h | 47 + .../enterprise-wlan/enterprise-wlan.pri | 9 + .../enterprise-wlan/enterprisewlandialog.cpp | 214 ++ .../enterprise-wlan/enterprisewlandialog.h | 65 + src/frontend/frontend.pri | 20 + src/frontend/list-items/lanlistitem.cpp | 222 ++ src/frontend/list-items/lanlistitem.h | 50 + src/frontend/list-items/list-items.pri | 18 + src/frontend/list-items/listitem.cpp | 166 ++ src/frontend/list-items/listitem.h | 68 + src/frontend/list-items/oneconnform.ui | 302 +++ src/frontend/list-items/onelancform.ui | 254 ++ src/frontend/list-items/wlanlistitem.cpp | 592 +++++ src/frontend/list-items/wlanlistitem.h | 107 + src/frontend/list-items/wlanmoreitem.cpp | 25 + src/frontend/list-items/wlanmoreitem.h | 26 + src/frontend/mainwindow.cpp | 789 +++++++ src/frontend/mainwindow.h | 183 ++ src/frontend/netdetails/coninfo.h | 269 +++ src/frontend/netdetails/creatnetpage.cpp | 227 ++ src/frontend/netdetails/creatnetpage.h | 62 + src/frontend/netdetails/customtabstyle.cpp | 73 + src/frontend/netdetails/customtabstyle.h | 19 + src/frontend/netdetails/detailpage.cpp | 346 +++ src/frontend/netdetails/detailpage.h | 102 + src/frontend/netdetails/detailwidget.cpp | 82 + src/frontend/netdetails/detailwidget.h | 45 + src/frontend/netdetails/ipv4page.cpp | 280 +++ src/frontend/netdetails/ipv4page.h | 67 + src/frontend/netdetails/ipv6page.cpp | 257 ++ src/frontend/netdetails/ipv6page.h | 66 + src/frontend/netdetails/netdetail.cpp | 964 ++++++++ src/frontend/netdetails/netdetail.h | 141 ++ src/frontend/netdetails/netdetails.pri | 22 + src/frontend/netdetails/securitypage.cpp | 834 +++++++ src/frontend/netdetails/securitypage.h | 113 + src/frontend/tab-pages/lanpage.cpp | 1257 ++++++++++ src/frontend/tab-pages/lanpage.h | 135 ++ src/frontend/tab-pages/tab-pages.pri | 12 + src/frontend/tab-pages/tabpage.cpp | 307 +++ src/frontend/tab-pages/tabpage.h | 131 + src/frontend/tab-pages/wlanpage.cpp | 1453 ++++++++++++ src/frontend/tab-pages/wlanpage.h | 186 ++ src/frontend/tools/divider.cpp | 23 + src/frontend/tools/divider.h | 15 + src/frontend/tools/infobutton.cpp | 93 + src/frontend/tools/infobutton.h | 31 + src/frontend/tools/kylable.cpp | 97 + src/frontend/tools/kylable.h | 33 + src/frontend/tools/loadingdiv.cpp | 78 + src/frontend/tools/loadingdiv.h | 51 + src/frontend/tools/radioitembutton.cpp | 246 ++ src/frontend/tools/radioitembutton.h | 61 + src/frontend/tools/switchbutton.cpp | 171 ++ src/frontend/tools/switchbutton.h | 59 + src/frontend/tools/tools.pri | 18 + src/frontend/wificonfigdialog.cpp | 199 ++ src/frontend/wificonfigdialog.h | 53 + src/frontend/wificonfigdialog.ui | 140 ++ src/frontend/xatom/xatom-helper.cpp | 210 ++ src/frontend/xatom/xatom-helper.h | 110 + src/frontend/xatom/xatom.pri | 7 + src/kylin-nm.desktop | 18 + src/main.cpp | 174 ++ src/org.ukui.kylin-nm.switch.gschema.xml | 14 + src/singleapplication/qt-local-peer.cpp | 205 ++ src/singleapplication/qt-local-peer.h | 80 + src/singleapplication/qt-locked-file-unix.cpp | 113 + src/singleapplication/qt-locked-file.cpp | 189 ++ src/singleapplication/qt-locked-file.h | 97 + .../qt-single-application.cpp | 353 +++ src/singleapplication/qt-single-application.h | 109 + .../qt-single-application.pri | 27 + src/src.pro | 68 + src/translations/kylin-nm_bo.qm | 1 + src/translations/kylin-nm_tr.qm | Bin 0 -> 21755 bytes translations/kylin-nm_bo.qm | 1 + translations/kylin-nm_bo.ts | 738 ++++++ translations/kylin-nm_tr.qm | Bin 0 -> 1958 bytes translations/kylin-nm_tr.ts | 2100 +++++++++++++++++ translations/kylin-nm_zh_CN.qm | Bin 0 -> 7744 bytes translations/kylin-nm_zh_CN.ts | 1180 +++++++++ 341 files changed, 37939 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 README.md create mode 100644 kylin-nm.pro create mode 100644 man/kylin-nm.1 create mode 100644 nmqrc.qrc create mode 100644 plugins/component/DrownLabel/drownlabel.cpp create mode 100644 plugins/component/DrownLabel/drownlabel.h create mode 100644 plugins/component/InfoButton/infobutton.cpp create mode 100644 plugins/component/InfoButton/infobutton.h create mode 100644 plugins/component/SwitchButton/switchbutton.cpp create mode 100644 plugins/component/SwitchButton/switchbutton.h create mode 100644 plugins/component/drownlabel.pri create mode 100644 plugins/component/infobutton.pri create mode 100644 plugins/component/switchbutton.pri create mode 100644 plugins/mobilehotspot/mobilehotspot.cpp create mode 100644 plugins/mobilehotspot/mobilehotspot.h create mode 100644 plugins/mobilehotspot/mobilehotspot.pro create mode 100644 plugins/mobilehotspot/mobilehotspotwidget.cpp create mode 100644 plugins/mobilehotspot/mobilehotspotwidget.h create mode 100644 plugins/mobilehotspot/translations/bo.qm create mode 100644 plugins/mobilehotspot/translations/bo.ts create mode 100644 plugins/mobilehotspot/translations/tr.qm create mode 100644 plugins/mobilehotspot/translations/tr.ts create mode 100644 plugins/mobilehotspot/translations/zh_CN.qm create mode 100644 plugins/mobilehotspot/translations/zh_CN.ts create mode 100644 plugins/netconnect/addnetbtn.cpp create mode 100644 plugins/netconnect/addnetbtn.h create mode 100644 plugins/netconnect/deviceframe.cpp create mode 100644 plugins/netconnect/deviceframe.h create mode 100644 plugins/netconnect/itemframe.cpp create mode 100644 plugins/netconnect/itemframe.h create mode 100644 plugins/netconnect/lanitem.cpp create mode 100644 plugins/netconnect/lanitem.h create mode 100644 plugins/netconnect/netconnect.cpp create mode 100644 plugins/netconnect/netconnect.h create mode 100644 plugins/netconnect/netconnect.pro create mode 100644 plugins/netconnect/netconnect.ui create mode 100644 plugins/netconnect/translations/bo.qm create mode 100644 plugins/netconnect/translations/bo.ts create mode 100644 plugins/netconnect/translations/tr.qm create mode 100644 plugins/netconnect/translations/tr.ts create mode 100644 plugins/netconnect/translations/zh_CN.qm create mode 100644 plugins/netconnect/translations/zh_CN.ts create mode 100644 plugins/plugin.pro create mode 100644 plugins/wlanconnect/deviceframe.cpp create mode 100644 plugins/wlanconnect/deviceframe.h create mode 100644 plugins/wlanconnect/itemframe.cpp create mode 100644 plugins/wlanconnect/itemframe.h create mode 100644 plugins/wlanconnect/translations/bo.qm create mode 100644 plugins/wlanconnect/translations/bo.ts create mode 100644 plugins/wlanconnect/translations/tr.qm create mode 100644 plugins/wlanconnect/translations/tr.ts create mode 100644 plugins/wlanconnect/translations/zh_CN.qm create mode 100644 plugins/wlanconnect/translations/zh_CN.ts create mode 100644 plugins/wlanconnect/wlanconnect.cpp create mode 100644 plugins/wlanconnect/wlanconnect.h create mode 100644 plugins/wlanconnect/wlanconnect.pro create mode 100644 plugins/wlanconnect/wlanconnect.ui create mode 100644 plugins/wlanconnect/wlanitem.cpp create mode 100644 plugins/wlanconnect/wlanitem.h create mode 100644 res.qrc create mode 100644 res/g/close_black.png create mode 100644 res/g/close_white.png create mode 100644 res/g/down_arrow.png create mode 100644 res/h/hide-pwd.png create mode 100644 res/h/no-pwd-wifi.png create mode 100644 res/h/right-pwd.png create mode 100644 res/h/show-pwd.png create mode 100644 res/hw/wifi-full-pwd.png create mode 100644 res/hw/wifi-full.png create mode 100644 res/hw/wifi-high-pwd.png create mode 100644 res/hw/wifi-high.png create mode 100644 res/hw/wifi-low-pwd.png create mode 100644 res/hw/wifi-low.png create mode 100644 res/hw/wifi-medium-pwd.png create mode 100644 res/hw/wifi-medium.png create mode 100644 res/hw/wifi-none-pwd.png create mode 100644 res/hw/wifi-none.png create mode 100644 res/hw/wifi6+-full-pwd.png create mode 100644 res/hw/wifi6+-full.png create mode 100644 res/hw/wifi6+-high-pwd.png create mode 100644 res/hw/wifi6+-high.png create mode 100644 res/hw/wifi6+-low-pwd.png create mode 100644 res/hw/wifi6+-low.png create mode 100644 res/hw/wifi6+-medium-pwd.png create mode 100644 res/hw/wifi6+-medium.png create mode 100644 res/hw/wifi6+-none-pwd.png create mode 100644 res/hw/wifi6+-none.png create mode 100644 res/hw/wifi6-full-pwd.png create mode 100644 res/hw/wifi6-full.png create mode 100644 res/hw/wifi6-high-pwd.png create mode 100644 res/hw/wifi6-high.png create mode 100644 res/hw/wifi6-low-pwd.png create mode 100644 res/hw/wifi6-low.png create mode 100644 res/hw/wifi6-medium-pwd.png create mode 100644 res/hw/wifi6-medium.png create mode 100644 res/hw/wifi6-none-pwd.png create mode 100644 res/hw/wifi6-none.png create mode 100644 res/l/network-offline.png create mode 100644 res/l/network-offline.svg create mode 100644 res/l/network-online.png create mode 100644 res/l/network-online.svg create mode 100644 res/s/conning-a/1.png create mode 100644 res/s/conning-a/2.png create mode 100644 res/s/conning-a/3.png create mode 100644 res/s/conning-a/4.png create mode 100644 res/s/conning-a/5.png create mode 100644 res/s/conning-a/6.png create mode 100644 res/s/conning-a/7.png create mode 100644 res/s/conning-a/8.png create mode 100644 res/s/conning-b/1.png create mode 100644 res/s/conning-b/10.png create mode 100644 res/s/conning-b/11.png create mode 100644 res/s/conning-b/12.png create mode 100644 res/s/conning-b/2.png create mode 100644 res/s/conning-b/3.png create mode 100644 res/s/conning-b/4.png create mode 100644 res/s/conning-b/5.png create mode 100644 res/s/conning-b/6.png create mode 100644 res/s/conning-b/7.png create mode 100644 res/s/conning-b/8.png create mode 100644 res/s/conning-b/9.png create mode 100644 res/s/conning-s/1.png create mode 100644 res/s/conning-s/10.png create mode 100644 res/s/conning-s/11.png create mode 100644 res/s/conning-s/12.png create mode 100644 res/s/conning-s/1x/1.png create mode 100644 res/s/conning-s/1x/10.png create mode 100644 res/s/conning-s/1x/11.png create mode 100644 res/s/conning-s/1x/12.png create mode 100644 res/s/conning-s/1x/2.png create mode 100644 res/s/conning-s/1x/3.png create mode 100644 res/s/conning-s/1x/4.png create mode 100644 res/s/conning-s/1x/5.png create mode 100644 res/s/conning-s/1x/6.png create mode 100644 res/s/conning-s/1x/7.png create mode 100644 res/s/conning-s/1x/8.png create mode 100644 res/s/conning-s/1x/9.png create mode 100644 res/s/conning-s/2.png create mode 100644 res/s/conning-s/3.png create mode 100644 res/s/conning-s/4.png create mode 100644 res/s/conning-s/5.png create mode 100644 res/s/conning-s/6.png create mode 100644 res/s/conning-s/7.png create mode 100644 res/s/conning-s/8.png create mode 100644 res/s/conning-s/9.png create mode 100644 res/s/rescan/1.png create mode 100644 res/s/rescan/10.png create mode 100644 res/s/rescan/11.png create mode 100644 res/s/rescan/12.png create mode 100644 res/s/rescan/2.png create mode 100644 res/s/rescan/3.png create mode 100644 res/s/rescan/4.png create mode 100644 res/s/rescan/5.png create mode 100644 res/s/rescan/6.png create mode 100644 res/s/rescan/7.png create mode 100644 res/s/rescan/8.png create mode 100644 res/s/rescan/9.png create mode 100644 res/w/wifi-full-pwd.png create mode 100644 res/w/wifi-full.png create mode 100644 res/w/wifi-high-pwd.png create mode 100644 res/w/wifi-high.png create mode 100644 res/w/wifi-low-pwd.png create mode 100644 res/w/wifi-low.png create mode 100644 res/w/wifi-medium-pwd.png create mode 100644 res/w/wifi-medium.png create mode 100644 res/w/wifi-none-pwd.png create mode 100644 res/w/wifi-none.png create mode 100644 res/w/wifi6+-full-pwd.png create mode 100644 res/w/wifi6+-full.png create mode 100644 res/w/wifi6+-high-pwd.png create mode 100644 res/w/wifi6+-high.png create mode 100644 res/w/wifi6+-low-pwd.png create mode 100644 res/w/wifi6+-low.png create mode 100644 res/w/wifi6+-medium-pwd.png create mode 100644 res/w/wifi6+-medium.png create mode 100644 res/w/wifi6+-meidum-pwd.png create mode 100644 res/w/wifi6+-none.png create mode 100644 res/w/wifi6-full-pwd.png create mode 100644 res/w/wifi6-full.png create mode 100644 res/w/wifi6-high-pwd.png create mode 100644 res/w/wifi6-high.png create mode 100644 res/w/wifi6-low-pwd.png create mode 100644 res/w/wifi6-low.png create mode 100644 res/w/wifi6-medium-pwd.png create mode 100644 res/w/wifi6-medium.png create mode 100644 res/w/wifi6-none-pwd.png create mode 100644 res/w/wifi6-none.png create mode 100644 res/x/control.svg create mode 100644 res/x/fly-mode-off.svg create mode 100644 res/x/fly-mode-on.svg create mode 100644 res/x/hot-spot-off.svg create mode 100644 res/x/hot-spot-on.svg create mode 100644 res/x/load-down.png create mode 100644 res/x/load-up.png create mode 100644 res/x/net-list-bg.svg create mode 100644 res/x/setup.png create mode 100644 res/x/wifi-list-bg.svg create mode 100644 src/backend/backend.pri create mode 100644 src/backend/dbus-interface/dbus-interface.pri create mode 100644 src/backend/dbus-interface/gsystem-local-alloc.h create mode 100644 src/backend/dbus-interface/kyenterpricesettinginfo.cpp create mode 100644 src/backend/dbus-interface/kyenterpricesettinginfo.h create mode 100644 src/backend/dbus-interface/kylinactiveconnectresource.cpp create mode 100644 src/backend/dbus-interface/kylinactiveconnectresource.h create mode 100644 src/backend/dbus-interface/kylinagent.c create mode 100644 src/backend/dbus-interface/kylinagent.h create mode 100644 src/backend/dbus-interface/kylinagentinterface.c create mode 100644 src/backend/dbus-interface/kylinagentinterface.h create mode 100644 src/backend/dbus-interface/kylinapconnectitem.cpp create mode 100644 src/backend/dbus-interface/kylinapconnectitem.h create mode 100644 src/backend/dbus-interface/kylinbluetoothconnectitem.cpp create mode 100644 src/backend/dbus-interface/kylinbluetoothconnectitem.h create mode 100644 src/backend/dbus-interface/kylinconnectitem.cpp create mode 100644 src/backend/dbus-interface/kylinconnectitem.h create mode 100644 src/backend/dbus-interface/kylinconnectoperation.cpp create mode 100644 src/backend/dbus-interface/kylinconnectoperation.h create mode 100644 src/backend/dbus-interface/kylinconnectresource.cpp create mode 100644 src/backend/dbus-interface/kylinconnectresource.h create mode 100644 src/backend/dbus-interface/kylinconnectsetting.cpp create mode 100644 src/backend/dbus-interface/kylinconnectsetting.h create mode 100644 src/backend/dbus-interface/kylinnetworkdeviceresource.cpp create mode 100644 src/backend/dbus-interface/kylinnetworkdeviceresource.h create mode 100644 src/backend/dbus-interface/kylinnetworkresourcemanager.cpp create mode 100644 src/backend/dbus-interface/kylinnetworkresourcemanager.h create mode 100644 src/backend/dbus-interface/kylinutil.cpp create mode 100644 src/backend/dbus-interface/kylinutil.h create mode 100644 src/backend/dbus-interface/kylinvpnconnectitem.cpp create mode 100644 src/backend/dbus-interface/kylinvpnconnectitem.h create mode 100644 src/backend/dbus-interface/kylinvpnrequest.c create mode 100644 src/backend/dbus-interface/kylinvpnrequest.h create mode 100644 src/backend/dbus-interface/kylinwiredconnectoperation.cpp create mode 100644 src/backend/dbus-interface/kylinwiredconnectoperation.h create mode 100644 src/backend/dbus-interface/kywirelessconnectoperation.cpp create mode 100644 src/backend/dbus-interface/kywirelessconnectoperation.h create mode 100644 src/backend/dbus-interface/kywirelessnetitem.cpp create mode 100644 src/backend/dbus-interface/kywirelessnetitem.h create mode 100644 src/backend/dbus-interface/kywirelessnetresource.cpp create mode 100644 src/backend/dbus-interface/kywirelessnetresource.h create mode 100644 src/backend/dbus-interface/nm-macros-internal.h create mode 100644 src/backend/dbusadaptor.cpp create mode 100644 src/backend/dbusadaptor.h create mode 100644 src/backend/hotspot/dlghotspotcreate.cpp create mode 100644 src/backend/hotspot/dlghotspotcreate.h create mode 100644 src/backend/hotspot/dlghotspotcreate.ui create mode 100644 src/backend/hotspot/hotspot.pri create mode 100644 src/backend/kylinarping.h create mode 100644 src/backend/kylinipv4arping.cpp create mode 100644 src/backend/kylinipv4arping.h create mode 100644 src/backend/kylinipv6arping.cpp create mode 100644 src/backend/kylinipv6arping.h create mode 100644 src/backend/sysdbusregister.cpp create mode 100644 src/backend/sysdbusregister.h create mode 100644 src/backend/utils.cpp create mode 100644 src/backend/utils.h create mode 100644 src/backend/wifi-auth-thread.cpp create mode 100644 src/backend/wifi-auth-thread.h create mode 100644 src/frontend/customstyle.cpp create mode 100644 src/frontend/customstyle.h create mode 100644 src/frontend/enterprise-wlan/enterprise-wlan.pri create mode 100644 src/frontend/enterprise-wlan/enterprisewlandialog.cpp create mode 100644 src/frontend/enterprise-wlan/enterprisewlandialog.h create mode 100644 src/frontend/frontend.pri create mode 100644 src/frontend/list-items/lanlistitem.cpp create mode 100644 src/frontend/list-items/lanlistitem.h create mode 100644 src/frontend/list-items/list-items.pri create mode 100644 src/frontend/list-items/listitem.cpp create mode 100644 src/frontend/list-items/listitem.h create mode 100644 src/frontend/list-items/oneconnform.ui create mode 100644 src/frontend/list-items/onelancform.ui create mode 100644 src/frontend/list-items/wlanlistitem.cpp create mode 100644 src/frontend/list-items/wlanlistitem.h create mode 100644 src/frontend/list-items/wlanmoreitem.cpp create mode 100644 src/frontend/list-items/wlanmoreitem.h create mode 100644 src/frontend/mainwindow.cpp create mode 100644 src/frontend/mainwindow.h create mode 100644 src/frontend/netdetails/coninfo.h create mode 100644 src/frontend/netdetails/creatnetpage.cpp create mode 100644 src/frontend/netdetails/creatnetpage.h create mode 100644 src/frontend/netdetails/customtabstyle.cpp create mode 100644 src/frontend/netdetails/customtabstyle.h create mode 100644 src/frontend/netdetails/detailpage.cpp create mode 100644 src/frontend/netdetails/detailpage.h create mode 100644 src/frontend/netdetails/detailwidget.cpp create mode 100644 src/frontend/netdetails/detailwidget.h create mode 100644 src/frontend/netdetails/ipv4page.cpp create mode 100644 src/frontend/netdetails/ipv4page.h create mode 100644 src/frontend/netdetails/ipv6page.cpp create mode 100644 src/frontend/netdetails/ipv6page.h create mode 100644 src/frontend/netdetails/netdetail.cpp create mode 100644 src/frontend/netdetails/netdetail.h create mode 100644 src/frontend/netdetails/netdetails.pri create mode 100644 src/frontend/netdetails/securitypage.cpp create mode 100644 src/frontend/netdetails/securitypage.h create mode 100644 src/frontend/tab-pages/lanpage.cpp create mode 100644 src/frontend/tab-pages/lanpage.h create mode 100644 src/frontend/tab-pages/tab-pages.pri create mode 100644 src/frontend/tab-pages/tabpage.cpp create mode 100644 src/frontend/tab-pages/tabpage.h create mode 100644 src/frontend/tab-pages/wlanpage.cpp create mode 100644 src/frontend/tab-pages/wlanpage.h create mode 100644 src/frontend/tools/divider.cpp create mode 100644 src/frontend/tools/divider.h create mode 100644 src/frontend/tools/infobutton.cpp create mode 100644 src/frontend/tools/infobutton.h create mode 100644 src/frontend/tools/kylable.cpp create mode 100644 src/frontend/tools/kylable.h create mode 100644 src/frontend/tools/loadingdiv.cpp create mode 100644 src/frontend/tools/loadingdiv.h create mode 100644 src/frontend/tools/radioitembutton.cpp create mode 100644 src/frontend/tools/radioitembutton.h create mode 100644 src/frontend/tools/switchbutton.cpp create mode 100644 src/frontend/tools/switchbutton.h create mode 100644 src/frontend/tools/tools.pri create mode 100644 src/frontend/wificonfigdialog.cpp create mode 100644 src/frontend/wificonfigdialog.h create mode 100644 src/frontend/wificonfigdialog.ui create mode 100644 src/frontend/xatom/xatom-helper.cpp create mode 100644 src/frontend/xatom/xatom-helper.h create mode 100644 src/frontend/xatom/xatom.pri create mode 100644 src/kylin-nm.desktop create mode 100644 src/main.cpp create mode 100644 src/org.ukui.kylin-nm.switch.gschema.xml create mode 100644 src/singleapplication/qt-local-peer.cpp create mode 100644 src/singleapplication/qt-local-peer.h create mode 100644 src/singleapplication/qt-locked-file-unix.cpp create mode 100644 src/singleapplication/qt-locked-file.cpp create mode 100644 src/singleapplication/qt-locked-file.h create mode 100644 src/singleapplication/qt-single-application.cpp create mode 100644 src/singleapplication/qt-single-application.h create mode 100644 src/singleapplication/qt-single-application.pri create mode 100644 src/src.pro create mode 100644 src/translations/kylin-nm_bo.qm create mode 100644 src/translations/kylin-nm_tr.qm create mode 100644 translations/kylin-nm_bo.qm create mode 100644 translations/kylin-nm_bo.ts create mode 100644 translations/kylin-nm_tr.qm create mode 100644 translations/kylin-nm_tr.ts create mode 100644 translations/kylin-nm_zh_CN.qm create mode 100644 translations/kylin-nm_zh_CN.ts diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..a8b57e7b --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +shine diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 00000000..9b77fa00 --- /dev/null +++ b/README.md @@ -0,0 +1,189 @@ +## kylin-nm介绍 +### 简介 + 麒麟网络工具(kylin-network-manager,简称kylin-nm)是操作系统的网络前端,其主要功能有: + 1、托盘栏图标及右键菜单 + 查看当前网络状态 + 左键点击托盘网络图标显示kylin-nm主界面 + 右键点击托盘网络图标显示右键菜单 + 2、对有线网络的管理 + 有线网络界面管理 + 连接/断开网络 + 网线拔插 + 多有线网卡支持 + 打开或关闭有线开关 + 查看或修改无线网络基本信息 + 3、对无线网络的管理 + 切换及刷新无线界面 + 查看无线网络列表 + 连接/断开无需密码的无线网 + 连接/断开有密码类型的无线网 + 无线网卡插拔 + 多无线网卡支持 + 打开或关闭无线开关 + 查看或修改无线网络基本信息 + 4、连接隐藏无线网络 + 连接无安全性的无线网络 + 连接WPA及WPA2个人安全性的无线网络 + 连接WPA及WPA2企业安全性的无线网络 + 连接WPA3个人安全性的无线网络 + 5、窗口样式调节 + 深色与浅色窗口样式切换 + 调节网络工具窗口透明度 + 普通窗口与具有毛玻璃效果的窗口的切换 +### 运行 + 麒麟网络工具的进程为kylin-nm,默认开机自启,依赖NetworkManger +### 命令行和dbus接口 + + kylin-nm进程的命令行如下: + Usage: kylin-nm + +#### dbus接口: + DBUS类型:SESSION BUS + DBUS名称:com.kylin.network + OBJECT路径:/ + 接口名称:com.kylin.network + +#### dbus方法 + getWirelessList + 参数:无 + 返回值:QMap> + 键:网卡名称 + 值:无线网络的集合(第一项为已连接网络信息,若无连接则为"--",否则为"名称/信号强度/加密类型/Uuid/是否为本机开放热点";其余为未连接的网络信息,为"名称/信号强度/加密类型/是否为本机开放热点" + 功能:获取无线列表及每个对应无线网络的信号强度、加密类型等信息。 + + getWiredList + 参数:无 + 返回值:QMap> + 键:网卡名称 + 值:有线网络的集合("名称/UUID/对应DBUS路径"),若无已连接网络则第一项默认为"--" + 功能:获取有线列表及每个对应的有线网络的uuid及dbus路径 + + setWiredSwitchEnable(bool enable) + 参数:(bool)开启有线总开关(true)关闭有线总开关(false) + 返回值:无 + 功能:打开或关闭有线总开关 + + setWirelessSwitchEnable(bool enable) + 参数:(bool)开启无线总开关(true)关闭无线总开关(false) + 返回值:无 + 功能:打开或关闭无线总开关 + + setDeviceEnable(QString devName, bool enable) + 参数:(QString)devName 设备名称,(bool) enable 开启/关闭 + 返回值:无 + 功能:打开或关闭单个有线网卡开关 + + activateConnect(int type, QString devName, QString ssid) + 参数:根据网卡类型 参数1 0:lan 1:wlan 参数2:网卡名称 参数3:uuid/ssid + 返回值:无 + 功能:激活一个网络连接 + + deActivateConnect(int type, QString devName, QString ssid) + 参数:根据网卡类型 参数1 0:lan 1:wlan 参数2:网卡名称 参数3:uuid/ssid + 返回值:无 + 功能:断开一个网络连接 + + getDeviceListAndEnabled(int devType) + 参数:int devType 0:lan 1:wlan + 返回值:QMap + 键:设备名称 + 值:bool 开启(true)关闭 (false) + 功能:获取设备列表和启用/禁用状态 + + getWirelessDeviceCap + 返回值:QMap + 键:设备名称 + 值:int 0:不支持热点 3:支持2.4GHz 7:支持5GHz&&2.4Ghz + 功能:获取无线设备能力(2.4G/5G) + + showPropertyWidget(QString devName, QString ssid) + 参数:QString devName 设备名称 QString ssid 根据网卡类型 有线为uuid/无线为ssid + 返回值:无 + 功能:唤起对应网络连接的属性页,可以对基础信息的显示或修改 + + showCreateWiredConnectWidget(QString devName) + 参数:QString devName 有线网卡名称 + 返回值:无 + 功能:唤起新建有线连接界面 + + activeWirelessAp(const QString apName, + const QString apPassword, + const QString band, + const QString apDevice) + 参数:QString apName 热点名称 + QString apPassword 热点密码 + QString band 频带 + QString apDevice + 返回值:无 + 功能:开启移动热点 + + deactiveWirelessAp(const QString apName, const QString uuid) + 参数:QStringList(名称/密码/设备名称/状态/UUID/频带) + 返回值:无 + 功能: 断开移动热点 + + getStoredApInfo + 参数:QStringList(名称/密码/设备名称/状态/UUID/频带) + 返回值:无 + 功能:获取已保存的移动热点信息 + + getApInfoBySsid(QString devName, QString ssid) + 参数:QString devName 设备名称 QString ssid + 返回值:无 + 功能:通过名称获取已保存的移动热点信息 + + reScan() + 参数:无 + 返回值:无 + 功能:申请重新进行无线扫描 + + keyRingInit() + 参数:QString apName 热点名称 QString uuid + 返回值:无 + 功能:断开移动热点 + + keyRingClear() + 参数:无 + 返回值:无 + 功能: 断开移动热点 + + +### 原理与主要使用的技术 + kylin-nm主要与NetworkManager进行交互,通过对应的dbus信号驱动UI界面的更新。并通过提供的dbus方法来进行网络相关功能的调用。 + + 麒麟网络工具按照功能划分成两层:UI层和逻辑层。UI层实现网络资源的显示,以及用户的交互,逻辑层通过NetworkManager提供的Dbus接口,完成网络资源的管理及其配置。 +### 配置文件 + kylin-nm的配置文件保存在如下路径: + ~/.config/ukui/kylin-nm.conf 有线无线开关状态 设备状态 + org.ukui.kylin-nm.switch 对应的gsetting值 (有线无线开关状态) +### 编译 + cd kylin-nm + mkdir build + cd build + qmake .. + make + sudo make install  +### 运行命令 + ./kylin-nm +### 调试 + kylin-nm目前并采用ukui-log4qt模块的日志功能。日志默认保存在~/.log/kylin-nm.log中 + + + + + + + + + + + + + + + + + + + + diff --git a/kylin-nm.pro b/kylin-nm.pro new file mode 100644 index 00000000..4999ff17 --- /dev/null +++ b/kylin-nm.pro @@ -0,0 +1,13 @@ +TEMPLATE = subdirs + +CONFIG += ordered \ + qt + +SUBDIRS = \ + plugins/plugin.pro \ + src \ + +TRANSLATIONS += \ + translations/kylin-nm_zh_CN.ts \ + translations/kylin-nm_tr.ts \ + translations/kylin-nm_bo.ts diff --git a/man/kylin-nm.1 b/man/kylin-nm.1 new file mode 100644 index 00000000..846cd7eb --- /dev/null +++ b/man/kylin-nm.1 @@ -0,0 +1,20 @@ +.\" Man page for Kylin-nm +.TH KYLIN-NM 1 "20 September 2019" "UKUI Desktop Environment" +.\" Please adjust this date when revising the manpage. +.\" +.SH "NAME" +kylin-nm \- The tool for the UKUI Desktop Environment +.SH "SYNOPSIS" +.B kylin-nm +.SH "DESCRIPTION" +The \fBkylin-nm\fR program is a part of the ukui-indicators, It provides network manager for the the UKUI Desktop Environment. Detect the system disk automatically, Can pop up and open the device. +.PP +This manual page documents the \fBkylin-nm\fR command. +.P +.SH "BUGS" +.SS Should you encounter any bugs, they may be reported at: +https://github.com/ukui/kylin-nm/issues +.SH "AUTHORS" +.SS This Man Page has been written for the UKUI Desktop Environment by: +shine (2019) +.SH "SEE ALSO" \ No newline at end of file diff --git a/nmqrc.qrc b/nmqrc.qrc new file mode 100644 index 00000000..db90969b --- /dev/null +++ b/nmqrc.qrc @@ -0,0 +1,132 @@ + + + res/g/down_arrow.png + res/s/rescan/1.png + res/s/rescan/2.png + res/s/rescan/3.png + res/s/rescan/4.png + res/s/rescan/5.png + res/s/rescan/6.png + res/s/rescan/7.png + res/s/rescan/8.png + res/s/rescan/9.png + res/s/rescan/10.png + res/s/rescan/11.png + res/s/rescan/12.png + res/h/hide-pwd.png + res/h/right-pwd.png + res/h/show-pwd.png + res/h/no-pwd-wifi.png + translations/kylin-nm_bo.qm + translations/kylin-nm_tr.qm + translations/kylin-nm_zh_CN.qm + res/x/fly-mode-off.svg + res/x/fly-mode-on.svg + res/x/hot-spot-off.svg + res/x/hot-spot-on.svg + res/x/net-list-bg.svg + res/x/wifi-list-bg.svg + res/x/load-down.png + res/x/load-up.png + res/l/network-offline.png + res/l/network-offline.svg + res/l/network-online.png + res/l/network-online.svg + res/w/wifi-full.png + res/w/wifi-full-pwd.png + res/w/wifi-high.png + res/w/wifi-high-pwd.png + res/w/wifi-low.png + res/w/wifi-low-pwd.png + res/w/wifi-medium.png + res/w/wifi-medium-pwd.png + res/w/wifi-none.png + res/w/wifi-none-pwd.png + res/s/conning-a/1.png + res/s/conning-a/2.png + res/s/conning-a/3.png + res/s/conning-a/4.png + res/s/conning-a/5.png + res/s/conning-a/6.png + res/s/conning-a/7.png + res/s/conning-a/8.png + res/g/close_black.png + res/g/close_white.png + res/s/conning-s/1.png + res/s/conning-s/2.png + res/s/conning-s/3.png + res/s/conning-s/4.png + res/s/conning-s/5.png + res/s/conning-s/6.png + res/s/conning-s/7.png + res/s/conning-s/8.png + res/s/conning-s/9.png + res/s/conning-s/10.png + res/s/conning-s/11.png + res/s/conning-s/12.png + res/x/setup.png + res/x/control.svg + res/w/wifi6-full-pwd.png + res/w/wifi6-full.png + res/w/wifi6-high-pwd.png + res/w/wifi6-high.png + res/w/wifi6-low-pwd.png + res/w/wifi6-low.png + res/w/wifi6-medium-pwd.png + res/w/wifi6-medium.png + res/w/wifi6-none.png + res/w/wifi6+-none.png + res/w/wifi6+-meidum-pwd.png + res/w/wifi6+-medium.png + res/w/wifi6+-medium-pwd.png + res/w/wifi6+-low.png + res/w/wifi6+-low-pwd.png + res/w/wifi6+-high.png + res/w/wifi6+-high-pwd.png + res/w/wifi6+-full.png + res/w/wifi6+-full-pwd.png + res/w/wifi6-none-pwd.png + res/hw/wifi-full-pwd.png + res/hw/wifi-full.png + res/hw/wifi-high-pwd.png + res/hw/wifi-high.png + res/hw/wifi-low-pwd.png + res/hw/wifi-low.png + res/hw/wifi-medium-pwd.png + res/hw/wifi-medium.png + res/hw/wifi-none-pwd.png + res/hw/wifi-none.png + res/hw/wifi6-full-pwd.png + res/hw/wifi6-full.png + res/hw/wifi6-high-pwd.png + res/hw/wifi6-high.png + res/hw/wifi6-low-pwd.png + res/hw/wifi6-low.png + res/hw/wifi6-medium-pwd.png + res/hw/wifi6-medium.png + res/hw/wifi6-none-pwd.png + res/hw/wifi6-none.png + res/hw/wifi6+-full-pwd.png + res/hw/wifi6+-full.png + res/hw/wifi6+-high-pwd.png + res/hw/wifi6+-high.png + res/hw/wifi6+-low-pwd.png + res/hw/wifi6+-low.png + res/hw/wifi6+-medium-pwd.png + res/hw/wifi6+-medium.png + res/hw/wifi6+-none-pwd.png + res/hw/wifi6+-none.png + res/s/conning-b/1.png + res/s/conning-b/2.png + res/s/conning-b/3.png + res/s/conning-b/4.png + res/s/conning-b/5.png + res/s/conning-b/6.png + res/s/conning-b/7.png + res/s/conning-b/8.png + res/s/conning-b/9.png + res/s/conning-b/10.png + res/s/conning-b/11.png + res/s/conning-b/12.png + + diff --git a/plugins/component/DrownLabel/drownlabel.cpp b/plugins/component/DrownLabel/drownlabel.cpp new file mode 100644 index 00000000..4e46523a --- /dev/null +++ b/plugins/component/DrownLabel/drownlabel.cpp @@ -0,0 +1,36 @@ +#include "drownlabel.h" +#include "deviceframe.h" + +#define ICONSIZE 16,16 +DrownLabel::DrownLabel(QString devName, QWidget * parent) : QLabel(parent) +{ + m_devName = devName; + setFixedSize(36,36); + loadPixmap(isChecked); + this->setProperty("useIconHighlightEffect", 0x2); +} + +DrownLabel::~DrownLabel() +{ + +} +void DrownLabel::setDropDownStatus(bool status) +{ + isChecked = status; + loadPixmap(isChecked); +} + +void DrownLabel::loadPixmap(bool isChecked) +{ + if (isChecked) { + setPixmap(QIcon::fromTheme("ukui-up-symbolic").pixmap(ICONSIZE)); + } else { + setPixmap(QIcon::fromTheme("ukui-down-symbolic").pixmap(ICONSIZE)); + } +} + +void DrownLabel::mouseReleaseEvent(QMouseEvent *event) +{ + emit labelClicked(); + QWidget::mouseReleaseEvent(event); +} diff --git a/plugins/component/DrownLabel/drownlabel.h b/plugins/component/DrownLabel/drownlabel.h new file mode 100644 index 00000000..806dc132 --- /dev/null +++ b/plugins/component/DrownLabel/drownlabel.h @@ -0,0 +1,27 @@ +#ifndef DROWNLABEL_H +#define DROWNLABEL_H + +#include +#include +#include +#include + +class DrownLabel : public QLabel +{ + Q_OBJECT +public: + explicit DrownLabel(QString devName, QWidget * parent = nullptr); + ~DrownLabel(); + void setDropDownStatus(bool status); + QString m_devName; + bool isChecked = true; +private: + void loadPixmap(bool isChecked); +protected: + virtual void mouseReleaseEvent(QMouseEvent * event); + +Q_SIGNALS: + void labelClicked(); +}; + +#endif // DROWNLABEL_H diff --git a/plugins/component/InfoButton/infobutton.cpp b/plugins/component/InfoButton/infobutton.cpp new file mode 100644 index 00000000..e434c0fb --- /dev/null +++ b/plugins/component/InfoButton/infobutton.cpp @@ -0,0 +1,105 @@ +#include "infobutton.h" +#include +#include +#include +#include + +#define BUTTON_SIZE 36,36 +#define ICON_SIZE 16,16 +#define BACKGROUND_COLOR QColor(0,0,0,0) +#define FOREGROUND_COLOR_NORMAL qApp->palette().text().color() +#define FOREGROUND_COLOR_HOVER QColor(55,144,250,255) +#define FOREGROUND_COLOR_PRESS QColor(36,109,212,255) +#define OUTER_PATH 8,8,16,16 +#define INNER_PATH 9,9,14,14 +#define TEXT_POS 14,5,16,16,0 + +#define BUTTON_SIZE 36,36 + +#define THEME_SCHAME "org.ukui.style" +#define COLOR_THEME "styleName" + +InfoButton::InfoButton(QWidget *parent) : QPushButton(parent) +{ + this->setFixedSize(BUTTON_SIZE); + initUI(); + const QByteArray style_id(THEME_SCHAME); + if (QGSettings::isSchemaInstalled(style_id)) { + m_styleGsettings = new QGSettings(style_id); + connect(m_styleGsettings, &QGSettings::changed, this, &InfoButton::onGSettingChaned); + } else { + qDebug() << "Gsettings interface \"org.ukui.style\" is not exist!"; + } +} + +void InfoButton::initUI() +{ + this->setFixedSize(BUTTON_SIZE); + m_backgroundColor = BACKGROUND_COLOR; + m_foregroundColor = FOREGROUND_COLOR_NORMAL; +} + +void InfoButton::onGSettingChaned(const QString &key) +{ + if (key == COLOR_THEME) { + m_foregroundColor = FOREGROUND_COLOR_NORMAL; + this->repaint(); + } +} + +void InfoButton::paintEvent(QPaintEvent *event) +{ + QPalette pal = this->palette(); + pal.setColor(QPalette::Base, m_backgroundColor); + pal.setColor(QPalette::Text, m_foregroundColor); + + QPainterPath cPath; + cPath.addRect(0, 0, ICON_SIZE); + cPath.addEllipse(0, 0, ICON_SIZE); + + QPainterPath outerPath; + outerPath.addEllipse(OUTER_PATH); + + QPainterPath innerPath; + innerPath.addEllipse(INNER_PATH); + outerPath -= innerPath; + + QPainter painter(this); + painter.setRenderHint(QPainter:: Antialiasing, true); //设置渲染,启动反锯齿 + painter.setPen(Qt::NoPen); + + painter.setBrush(pal.color(QPalette::Base)); + painter.drawPath(cPath); + + painter.fillPath(outerPath, pal.color(QPalette::Text)); + painter.setPen(m_foregroundColor); + QFont font("Noto Sans CJK SC", 11, QFont::Normal, false); + painter.setFont(font); + painter.drawText(TEXT_POS, "i"); +} + +void InfoButton::enterEvent(QEvent *event) +{ + m_foregroundColor = FOREGROUND_COLOR_HOVER; + this->repaint(); +} + +void InfoButton::leaveEvent(QEvent *event) +{ + m_foregroundColor = FOREGROUND_COLOR_NORMAL; + this->repaint(); +} + +void InfoButton::mousePressEvent(QMouseEvent *event) +{ + m_foregroundColor = FOREGROUND_COLOR_PRESS; + this->repaint(); + return QPushButton::mousePressEvent(event); +} + +void InfoButton::mouseReleaseEvent(QMouseEvent *event) +{ + m_foregroundColor = FOREGROUND_COLOR_HOVER; + this->repaint(); + return QPushButton::mouseReleaseEvent(event); +} diff --git a/plugins/component/InfoButton/infobutton.h b/plugins/component/InfoButton/infobutton.h new file mode 100644 index 00000000..6376b028 --- /dev/null +++ b/plugins/component/InfoButton/infobutton.h @@ -0,0 +1,35 @@ +#ifndef INFOBUTTON_H +#define INFOBUTTON_H +#include +#include +#include + +class InfoButton : public QPushButton +{ + Q_OBJECT +public: + explicit InfoButton(QWidget * parent = nullptr); + ~InfoButton() = default; + +protected: + void paintEvent(QPaintEvent *event); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +private: + void initUI(); + +private: + QColor m_backgroundColor; + QColor m_foregroundColor; + + //监听主题的Gsettings + QGSettings * m_styleGsettings = nullptr; + +private slots: + void onGSettingChaned(const QString &key); +}; + +#endif // INFOBUTTON_H diff --git a/plugins/component/SwitchButton/switchbutton.cpp b/plugins/component/SwitchButton/switchbutton.cpp new file mode 100644 index 00000000..a7e62f65 --- /dev/null +++ b/plugins/component/SwitchButton/switchbutton.cpp @@ -0,0 +1,327 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "switchbutton.h" + +#include +#define THEME_QT_SCHEMA "org.ukui.style" +#define THEME_GTK_SCHEMA "org.mate.interface" +#define TIMER_INTERVAL 5 //每隔5ms动画移动一次 +#define MOVING_STEPS 40 //动画总共移动40次 + +SwitchButton::SwitchButton(QWidget *parent, bool useDisableStyle) : + QWidget(parent) +{ +// this->resize(QSize(52, 24)); + this->setFixedSize(QSize(50, 24)); + + checked = false; + hover = false; + disabled = false; + isMoving = false; + isAnimation = true; + m_useDisableStyle = useDisableStyle; + + space = 4; + rectRadius = height()/2; + + mStep = width()/MOVING_STEPS;//也就是40次动画就可以走完,每次时间间隔是固定的5ms + mStartX = 0; + mEndX= 0; + + mTimer = new QTimer(this); + mTimer->setInterval(TIMER_INTERVAL);//动画更新时间 + connect(mTimer, SIGNAL(timeout()), this, SLOT(updatevalue())); + if(QGSettings::isSchemaInstalled(THEME_GTK_SCHEMA) && QGSettings::isSchemaInstalled(THEME_QT_SCHEMA)) { + QByteArray qtThemeID(THEME_QT_SCHEMA); + QByteArray gtkThemeID(THEME_GTK_SCHEMA); + + m_gtkThemeSetting = new QGSettings(gtkThemeID,QByteArray(),this); + m_qtThemeSetting = new QGSettings(qtThemeID,QByteArray(),this); + + QString style = m_qtThemeSetting->get("styleName").toString(); + changeColor(style); + + connect(m_qtThemeSetting,&QGSettings::changed, [this] (const QString &key) { + QString style = m_qtThemeSetting->get("styleName").toString(); + if (key == "styleName") { + changeColor(style); + } + }); + } +} + +SwitchButton::~SwitchButton() +{ +} + +void SwitchButton::paintEvent(QPaintEvent *){ + + QPainter painter(this); + //启用反锯齿 + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setCompositionMode(QPainter::CompositionMode_Source); + drawBg(&painter); + if(!isAnimation)//动画如果禁用,则圆形滑块isMoving始终为false + isMoving =false; + if(isMoving) + animation(&painter); + drawSlider(&painter); + painter.end(); +} + +void SwitchButton::changeColor(const QString &themes) { + if (hover) { + return ;//在鼠标下,禁止切换颜色鼠标离开时切换颜色 + } + if (themes == "ukui-dark" || themes == "ukui-black") { + bgColorOff = QColor(OFF_BG_DARK_COLOR); + bgColorOn = QColor(ON_BG_DARK_COLOR); + rectColorEnabled = QColor(ENABLE_RECT_DARK_COLOR); + rectColorDisabled = QColor(DISABLE_RECT_DARK_COLOR); + sliderColorDisabled = QColor(DISABLE_RECT_DARK_COLOR); + sliderColorEnabled = QColor(ENABLE_RECT_DARK_COLOR); + bgHoverOnColor = QColor(ON_HOVER_BG_DARK_COLOR); + bgHoverOffColor = QColor(OFF_HOVER_BG_DARK_COLOR); + bgColorDisabled = QColor(DISABLE_DARK_COLOR); + } else { + bgColorOff = QColor(OFF_BG_LIGHT_COLOR); + bgColorOn = QColor(ON_BG_LIGHT_COLOR); + rectColorEnabled = QColor(ENABLE_RECT_LIGHT_COLOR); + rectColorDisabled = QColor(DISABLE_RECT_LIGHT_COLOR); + sliderColorDisabled = QColor(DISABLE_RECT_LIGHT_COLOR); + sliderColorEnabled = QColor(ENABLE_RECT_LIGHT_COLOR); + bgHoverOnColor = QColor(ON_HOVER_BG_LIGHT_COLOR); + bgHoverOffColor = QColor(OFF_HOVER_BG_LIGHT_COLOR); + bgColorDisabled = QColor(DISABLE_LIGHT_COLOR); + } +} + +//动画绘制 +void SwitchButton::animation(QPainter *painter){ + painter->save(); + int h = height(); + int w = width(); + painter->setPen(Qt::NoPen); + //颜色设置 + if(checked){ + //开关在左侧时 + painter->setBrush(bgColorOn); + rect.setRect(0,0,h+mStartX,h); + }else{ + painter->setBrush(bgColorOff); + rect.setRect(mStartX,0,w-mStartX,h); + } + painter->drawRoundedRect(rect,rectRadius,rectRadius); + + painter->restore(); +} + +//绘制背景 +void SwitchButton::drawBg(QPainter *painter){ + int w = width(); + int h = height(); + painter->save(); + painter->setPen(Qt::NoPen); + if (disabled && m_useDisableStyle) { + painter->setPen(Qt::NoPen); + painter->setBrush(bgColorDisabled); + } else { + if(checked){ + if(isMoving){ + painter->setBrush(bgColorOff); + rect.setRect(mStartX,0,w-mStartX,h); + }else { + painter->setBrush(bgColorOn); + rect.setRect(0, 0, w, h); + } + }else{ + if(isMoving){ + painter->setBrush(bgColorOn); + rect.setRect(0,0,mStartX+h,h); + } + else { + painter->setBrush(bgColorOff); + rect.setRect(0, 0, w, h); + } + } + } + //半径为高度的一半 + painter->drawRoundedRect(rect,rectRadius,rectRadius); + + painter->restore(); +} + +//绘制滑块,也就是圆形按钮 +void SwitchButton::drawSlider(QPainter *painter){ + painter->save(); + painter->setPen(Qt::NoPen); + + if (!disabled || !m_useDisableStyle){ + painter->setBrush(sliderColorEnabled); + } + else + painter->setBrush(sliderColorDisabled); + if (disabled) { + if (!checked){ + QRect smallRect(8, height() / 2 - 2, 10 , 4); + painter->drawRoundedRect(smallRect,3,3); + }else{ + QRect smallRect(width() - 8 * 2, height() / 2 - 2, 10 , 4); + painter->drawRoundedRect(smallRect,3,3); + } + } + + QRect rect(0, 0, width(), height()); + int sliderWidth = rect.height() - space * 2; + QRect sliderRect(mStartX + space, space, sliderWidth, sliderWidth); + painter->drawEllipse(sliderRect); + + painter->restore(); +} + +void SwitchButton::mousePressEvent(QMouseEvent *){ + qDebug()<start(); + isMoving = true; + } +} + +void SwitchButton::resizeEvent(QResizeEvent *){ + //每次开始的x坐标都是跳过圆角,从直线的地方开始计算 + mStep = width() / MOVING_STEPS; + + if (checked){ + //circle out +// startX = width() - height() + space; + //circle in + mStartX = width() - height(); + } + else + mStartX = 0; + + rectRadius = height()/2; + update(); +} +void SwitchButton::enterEvent(QEvent *event) { + bgColorOn = bgHoverOnColor; + bgColorOff = bgHoverOffColor; + + hover = true; + update(); + return QWidget::enterEvent(event); +} + +void SwitchButton::leaveEvent(QEvent *event) { + hover = false; + + QString style = m_qtThemeSetting->get("styleName").toString(); + changeColor(style); + + update(); + return QWidget::leaveEvent(event); +} + +//根据事件向左还是向右移动 +void SwitchButton::updatevalue(){ + if (checked) + if (mStartX < mEndX-mStep){ + mStartX = mStartX + mStep; + } + else{ + mStartX = mEndX; + mTimer->stop(); + isMoving = false; + } + else{ + if (mStartX > mEndX+mStep){ + mStartX = mStartX - mStep; + } + else{ + mStartX = mEndX; + mTimer->stop(); + isMoving = false; + } + } + update(); +} + +void SwitchButton::setChecked(bool checked){ + if (this->checked != checked){ + this->checked = checked; + Q_EMIT checkedChanged(checked); + update(); + } + + mStep = width() / MOVING_STEPS; + + if (checked){ + //circle out +// endX = width() - height() + space; + //circle in + mEndX = width() - height(); + } + else{ + mEndX = 0; + } + mTimer->start(); + isMoving = true; +} + +bool SwitchButton::isChecked(){ + return this->checked; +} + +void SwitchButton::setDisabledFlag(bool value) +{ + disabled = value; + update(); +} + +bool SwitchButton::getDisabledFlag() +{ + return disabled; +} + +void SwitchButton::setAnimation(bool on){ + isAnimation = on; +} + diff --git a/plugins/component/SwitchButton/switchbutton.h b/plugins/component/SwitchButton/switchbutton.h new file mode 100644 index 00000000..a54e501b --- /dev/null +++ b/plugins/component/SwitchButton/switchbutton.h @@ -0,0 +1,121 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef SWITCHBUTTON_H +#define SWITCHBUTTON_H + +#include +#include +#include +#include +#include +#include + +#define OFF_BG_DARK_COLOR "#404040" +#define OFF_HOVER_BG_DARK_COLOR "#666666" +#define ON_BG_DARK_COLOR "#3790FA" +#define ON_HOVER_BG_DARK_COLOR "#40A9FB" +#define DISABLE_DARK_COLOR "#474747" +#define DISABLE_RECT_DARK_COLOR "#6E6E6E" +#define ENABLE_RECT_DARK_COLOR "#FFFFFF" + +#define OFF_BG_LIGHT_COLOR "#E0E0E0" +#define OFF_HOVER_BG_LIGHT_COLOR "#B3B3B3" +#define ON_BG_LIGHT_COLOR "#3790FA" +#define ON_HOVER_BG_LIGHT_COLOR "#40A9FB" +#define DISABLE_LIGHT_COLOR "#E9E9E9" +#define DISABLE_RECT_LIGHT_COLOR "#B3B3B3" +#define ENABLE_RECT_LIGHT_COLOR "#FFFFFF" + + +class SwitchButton : public QWidget +{ + Q_OBJECT + +public: + SwitchButton(QWidget *parent = 0, bool useDisableStyle = true); + ~SwitchButton(); + + void setChecked(bool checked); + void setAnimation(bool on); + + bool isChecked(); + void setDisabledFlag(bool); + bool getDisabledFlag(); +protected: + void mousePressEvent(QMouseEvent *); + void resizeEvent(QResizeEvent *); + void paintEvent(QPaintEvent *); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + + void drawBg(QPainter * painter); + void drawSlider(QPainter * painter); + void changeColor(const QString &themes); + +private: + bool checked; //切换的判断 + bool disabled; + + void animation(QPainter *painter); + QRect rect; + bool isMoving; //滑块动作判断 + bool isAnimation; // 是否允许动画执行 + + QColor bgColorOff; + QColor bgColorOn; + QColor bgHoverOnColor; + QColor bgHoverOffColor; + QColor bgColorDisabled; + + QColor sliderColorEnabled; + QColor sliderColorDisabled; + + + QColor rectColorEnabled; + QColor rectColorDisabled; + + QColor sliderColorOff; + QColor sliderColorOn; + QGSettings *m_qtThemeSetting; + QGSettings *m_gtkThemeSetting; + + int space; //滑块离背景间隔 + int rectRadius; //圆角角度 + + int mStep; //移动步长 + int mStartX; + int mEndX; + + bool hover; + QTimer * mTimer; + + bool m_useDisableStyle; + + +private Q_SLOTS: + void updatevalue(); + + +Q_SIGNALS: + void checkedChanged(bool checked); + void disabledClick(); +}; + +#endif // SWITCHBUTTON_H diff --git a/plugins/component/drownlabel.pri b/plugins/component/drownlabel.pri new file mode 100644 index 00000000..185bbd36 --- /dev/null +++ b/plugins/component/drownlabel.pri @@ -0,0 +1,9 @@ +#LIBINTERFACE_NAME = $$qtLibraryTarget(drownlabel) + +SOURCES += \ + $$PWD/DrownLabel/drownlabel.cpp \ + +HEADERS += \ + $$PWD/DrownLabel/drownlabel.h \ + + diff --git a/plugins/component/infobutton.pri b/plugins/component/infobutton.pri new file mode 100644 index 00000000..5e84a760 --- /dev/null +++ b/plugins/component/infobutton.pri @@ -0,0 +1,9 @@ +#LIBINTERFACE_NAME = $$qtLibraryTarget(infobutton) + +SOURCES += \ + $$PWD/InfoButton/infobutton.cpp \ + +HEADERS += \ + $$PWD/InfoButton/infobutton.h \ + + diff --git a/plugins/component/switchbutton.pri b/plugins/component/switchbutton.pri new file mode 100644 index 00000000..b1c4b1bc --- /dev/null +++ b/plugins/component/switchbutton.pri @@ -0,0 +1,8 @@ + +#LIBINTERFACE_NAME = $$qtLibraryTarget(switchbutton) + +SOURCES += \ + $$PWD/SwitchButton/switchbutton.cpp \ + +HEADERS += \ + $$PWD/SwitchButton/switchbutton.h \ diff --git a/plugins/mobilehotspot/mobilehotspot.cpp b/plugins/mobilehotspot/mobilehotspot.cpp new file mode 100644 index 00000000..2a456228 --- /dev/null +++ b/plugins/mobilehotspot/mobilehotspot.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "mobilehotspot.h" + + +#include + + +MobileHotspot::MobileHotspot() : mFirstLoad(true) { + + QTranslator* translator = new QTranslator(this); + qDebug() << "/usr/share/kylin-nm/mobilehotspot/" + QLocale::system().name(); + if (!translator->load("/usr/share/kylin-nm/mobilehotspot/" + QLocale::system().name())) { + qDebug() << "load failed"; + } + QApplication::installTranslator(translator); + + pluginName = tr("MobileHotspot"); + qDebug() << pluginName; + pluginType = NETWORK; + + qDBusRegisterMetaType >(); + qDBusRegisterMetaType >(); + needLoad = isExitWirelessDevice(); +} + +MobileHotspot::~MobileHotspot() +{ + if (!mFirstLoad) { +// delete pluginWidget; +// pluginWidget = nullptr; + + } +} + +QString MobileHotspot::plugini18nName() { + return pluginName; +} + +int MobileHotspot::pluginTypes() { + return pluginType; +} + +QWidget *MobileHotspot::pluginUi() { + if (mFirstLoad) { + mFirstLoad = false; + + pluginWidget = new MobileHotspotWidget; + pluginWidget->setAttribute(Qt::WA_DeleteOnClose); + } + return pluginWidget; +} + +const QString MobileHotspot::name() const { + + return QStringLiteral("mobilehotspot"); +} + +bool MobileHotspot::isEnable() const +{ + qDebug() << needLoad; + return needLoad; +} + + +bool MobileHotspot::isShowOnHomePage() const +{ + return false; +} + +QIcon MobileHotspot::icon() const +{ + return QIcon(); +} + +QString MobileHotspot::translationPath() const +{ + return "/usr/share/kylin-nm/mobilehotspot/%1.ts"; +} + +void MobileHotspot::initSearchText() +{ + //~ contents_path /mobilehotspot/mobilehotspot + tr("mobilehotspot"); + //~ contents_path /mobilehotspot/mobilehotspot open + tr("mobilehotspot open"); +} + +bool MobileHotspot::isExitWirelessDevice() +{ + QDBusInterface *interface = new QDBusInterface("com.kylin.network", "/com/kylin/network", + "com.kylin.network", + QDBusConnection::sessionBus()); + if (!interface->isValid()) { + qDebug() << "/com/kylin/network is invalid"; + return false; + } + + QDBusMessage result = interface->call(QStringLiteral("getDeviceListAndEnabled"),1); + if(result.type() == QDBusMessage::ErrorMessage) { + qWarning() << "getWirelessDeviceList error:" << result.errorMessage(); + return false; + } + + auto dbusArg = result.arguments().at(0).value(); + QMap deviceListMap; + dbusArg >> deviceListMap; + + QDBusReply > capReply = interface->call("getWirelessDeviceCap"); + if (!capReply.isValid()) { + qDebug()<<"execute dbus method 'getWirelessDeviceCap' is invalid in func initInterfaceInfo()" < devCapMap = capReply.value(); + + + if (deviceListMap.isEmpty()) { + qDebug() << "no wireless device"; + return false; + } else { + QMap::Iterator iter = deviceListMap.begin(); + while (iter != deviceListMap.end()) { + QString interfaceName = iter.key(); + if (!devCapMap.contains(interfaceName)) { + iter++; + continue; + } + if (deviceListMap[interfaceName] & 0x01) { + qDebug() << "wireless device" << interfaceName << "support hotspot"; + return true; + } + iter++; + } + } + qDebug() << "no wireless device support hotspot"; + return false; +} + diff --git a/plugins/mobilehotspot/mobilehotspot.h b/plugins/mobilehotspot/mobilehotspot.h new file mode 100644 index 00000000..7bb3cb63 --- /dev/null +++ b/plugins/mobilehotspot/mobilehotspot.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef MOBILEHOTSPOT_H +#define MOBILEHOTSPOT_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "interface.h" +#include "switchbutton.h" +#include "hoverbtn.h" +#include "mobilehotspotwidget.h" + + +namespace Ui { +class MobileHotspot; +} + +class MobileHotspot : public QObject, CommonInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.ukcc.CommonInterface") + Q_INTERFACES(CommonInterface) + +public: + MobileHotspot(); + ~MobileHotspot(); + + QString plugini18nName() Q_DECL_OVERRIDE; + int pluginTypes() Q_DECL_OVERRIDE; + QWidget * pluginUi() Q_DECL_OVERRIDE; + const QString name() const Q_DECL_OVERRIDE; + bool isShowOnHomePage() const Q_DECL_OVERRIDE; + QIcon icon() const Q_DECL_OVERRIDE; + bool isEnable() const Q_DECL_OVERRIDE; + QString translationPath() const Q_DECL_OVERRIDE; + +private: + + void initSearchText(); + bool isExitWirelessDevice(); + + QString pluginName; + int pluginType; + MobileHotspotWidget *pluginWidget; + +private: + bool mFirstLoad; + bool needLoad; + +}; +#endif // MOBILEHOTSPOT_H diff --git a/plugins/mobilehotspot/mobilehotspot.pro b/plugins/mobilehotspot/mobilehotspot.pro new file mode 100644 index 00000000..a9fde8d3 --- /dev/null +++ b/plugins/mobilehotspot/mobilehotspot.pro @@ -0,0 +1,42 @@ +QT += widgets network dbus gui core +TEMPLATE = lib +CONFIG += plugin + +TARGET = $$qtLibraryTarget(mobilehotspot) +DESTDIR = ../.. +target.path = $$[QT_INSTALL_LIBS]/ukui-control-center +trans.files = translations/* +trans.path = /usr/share/kylin-nm/mobilehotspot/ + +INCLUDEPATH += \ + $$PROJECT_COMPONENTSOURCE \ + $$PROJECT_ROOTDIR \ + /usr/include/ukcc/interface \ + /usr/include/ukcc/widgets + +LIBS += -L$$[QT_INSTALL_LIBS] -lgsettings-qt -lukcc + +CONFIG += c++11 \ + link_pkgconfig \ + +PKGCONFIG += gsettings-qt \ + +#DEFINES += QT_DEPRECATED_WARNINGS + +SOURCES += \ + mobilehotspot.cpp \ + mobilehotspotwidget.cpp + +HEADERS += \ + mobilehotspot.h \ + mobilehotspotwidget.h \ + libukcc_global.h + + +INSTALLS += target \ + trans + +TRANSLATIONS += \ + translations/zh_CN.ts \ + translations/tr.ts \ + translations/bo.ts diff --git a/plugins/mobilehotspot/mobilehotspotwidget.cpp b/plugins/mobilehotspot/mobilehotspotwidget.cpp new file mode 100644 index 00000000..9d4abdd4 --- /dev/null +++ b/plugins/mobilehotspot/mobilehotspotwidget.cpp @@ -0,0 +1,678 @@ +#include "mobilehotspotwidget.h" +#include + +#define LABEL_RECT 17, 0, 105, 23 +#define CONTENTS_MARGINS 0, 0, 0, 0 +#define FRAME_MIN_SIZE 550, 60 +#define FRAME_MAX_SIZE 16777215, 16777215 +#define CONTECT_FRAME_MAX_SIZE 16777215, 60 +#define LABLE_MIN_WIDTH 140 +#define COMBOBOX_MIN_WIDTH 200 +#define LINE_MAX_SIZE 16777215, 1 +#define LINE_MIN_SIZE 0, 1 +#define LAYOUT_LEFT_MARGINS 8 +#define ICON_SIZE 24,24 + +#define WIRELESS 1 + +#define AP_NAME_MAX_LENGTH 32 + +const QByteArray GSETTINGS_SCHEMA = "org.ukui.kylin-nm.switch"; +const QString WIRELESS_SWITCH = "wirelessswitch"; + +void MobileHotspotWidget::showDesktopNotify(const QString &message) +{ + QDBusInterface iface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::sessionBus()); + QList args; + args<<(tr("ukui control center")) + <<((unsigned int) 0) + <setContentsMargins(CONTENTS_MARGINS); + + qDBusRegisterMetaType >(); + qDBusRegisterMetaType >(); + qDBusRegisterMetaType >(); + qDBusRegisterMetaType >>(); + + initUI(); + m_switchBtn->installEventFilter(this); + m_interface = new QDBusInterface("com.kylin.network", "/com/kylin/network", + "com.kylin.network", + QDBusConnection::sessionBus()); + if(!m_interface->isValid()) { + qDebug() << "dbus interface com.kylin.network is invaild"; + m_switchBtn->setChecked(false); + setUiEnabled(false); + } + + m_hostName = getHostName(); + + initDbusConnect(); + + initInterfaceInfo(); + getApInfo(); + + connect(m_switchBtn, &SwitchButton::checkedChanged, this, &MobileHotspotWidget::setUiEnabled); + connect(m_interfaceComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, [=]() { + m_interfaceName = m_interfaceComboBox->currentText(); + updateBandCombox(); + }); + connect(m_pwdShowBox, &QCheckBox::clicked, this, [=]() { + if (m_pwdNameLine->echoMode() == QLineEdit::Password) { + m_pwdShowBox->setIcon(QIcon::fromTheme("ukui-eye-display-symbolic")); + m_pwdNameLine->setEchoMode(QLineEdit::Normal); + } else { + m_pwdNameLine->setEchoMode(QLineEdit::Password); + m_pwdShowBox->setIcon(QIcon::fromTheme("ukui-eye-hidden-symbolic")); + } + }); +} + +MobileHotspotWidget::~MobileHotspotWidget() +{ + delete m_interface; +} + +bool MobileHotspotWidget::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::MouseButtonDblClick && watched == m_switchBtn) { + return true; + } + + if (event->type() == QEvent::MouseButtonPress) { + if (watched == m_switchBtn) { + if (!m_interface->isValid()) { + return true; + } + if (m_switchBtn->getDisabledFlag()) { + showDesktopNotify(tr("wirless switch is close or no wireless device")); + return true; + } + if (m_switchBtn->isChecked()) { + showDesktopNotify(tr("start to close hotspot")); + QDBusReply reply = m_interface->call("deactiveWirelessAp", m_apNameLine->text(), m_uuid); + if (!reply.isValid()) { + qDebug() << "[MobileHotspotWidget] call deactiveWirelessAp failed "; + return true; + } + } else { + if (m_apNameLine->text().isEmpty() || m_interfaceName.isEmpty()) + { + showDesktopNotify(tr("hotpots name or device is invalid")); + return true; + } + if (m_pwdNameLine->text().length() < 8) { + showDesktopNotify(tr("can not create hotspot with password length less than eight!")); + return true; + } + showDesktopNotify(tr("start to open hotspot ") + m_apNameLine->text()); + QDBusReply reply = m_interface->call("activeWirelessAp", + m_apNameLine->text(), + m_pwdNameLine->text(), + m_freqBandComboBox->currentText(), + m_interfaceComboBox->currentText()); + if (!reply.isValid()) { + qDebug() << "[MobileHotspotWidget] call deactiveWirelessAp failed "; + return true; + } + } + return true; + } + } + return QWidget::eventFilter(watched, event); +} + +void MobileHotspotWidget::paintEvent(QPaintEvent *event) +{ + QWidget::paintEvent(event); +} + +void MobileHotspotWidget::initUI() +{ + QFrame *hotspotFrame = new QFrame(this); + hotspotFrame->setMinimumSize(FRAME_MIN_SIZE); + hotspotFrame->setMaximumSize(FRAME_MAX_SIZE); + hotspotFrame->setFrameShape(QFrame::Box); + + QVBoxLayout *hotspotLyt = new QVBoxLayout(hotspotFrame); + hotspotLyt->setContentsMargins(0, 0, 0, 0); + + m_hotspotTitleLabel = new TitleLabel(this); + m_hotspotTitleLabel->setText(tr("Hotspot")); + + setSwitchFrame(); + setApNameFrame(); + setPasswordFrame(); + setFreqBandFrame(); + setInterFaceFrame(); + + switchAndApNameLine = myLine(); + apNameAndPwdLine = myLine(); + pwdAndfreqBandLine = myLine(); + freqBandAndInterfaceLine = myLine(); + + /* add widget */ + hotspotLyt->addWidget(m_switchFrame); + hotspotLyt->addWidget(switchAndApNameLine); + hotspotLyt->addWidget(m_ApNameFrame); + hotspotLyt->addWidget(apNameAndPwdLine); + hotspotLyt->addWidget(m_passwordFrame); + hotspotLyt->addWidget(pwdAndfreqBandLine); + hotspotLyt->addWidget(m_freqBandFrame); + hotspotLyt->addWidget(freqBandAndInterfaceLine); + hotspotLyt->addWidget(m_interfaceFrame); + hotspotLyt->setSpacing(0); + + mVlayout->addWidget(m_hotspotTitleLabel); + mVlayout->setSpacing(8); + mVlayout->addWidget(hotspotFrame); + mVlayout->addStretch(); +} + +void MobileHotspotWidget::initDbusConnect() +{ + if(m_interface->isValid()) { + connect(m_interface,SIGNAL(activateFailed(QString)), this, SLOT(onActivateFailed(QString)), Qt::QueuedConnection); + connect(m_interface,SIGNAL(deactivateFailed(QString)), this, SLOT(onDeactivateFailed(QString)), Qt::QueuedConnection); + connect(m_interface,SIGNAL(deviceStatusChanged()), this, SLOT(onDeviceStatusChanged()), Qt::QueuedConnection); + connect(m_interface,SIGNAL(deviceNameChanged(QString, QString, int)), this, SLOT(onDeviceNameChanged(QString, QString, int)), Qt::QueuedConnection); + connect(m_interface,SIGNAL(hotspotDeactivated(QString, QString)), this, SLOT(onHotspotDeactivated(QString, QString)), Qt::QueuedConnection); + connect(m_interface,SIGNAL(hotspotActivated(QString, QString, QString)), this, SLOT(onHotspotActivated(QString, QString, QString)), Qt::QueuedConnection); + + connect(m_interface, SIGNAL(wlanactiveConnectionStateChanged(QString, QString, QString, int)), this, SLOT(onActiveConnectionChanged(QString, QString, QString, int)), Qt::QueuedConnection); + } + + if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA)) { + m_switchGsettings = new QGSettings(GSETTINGS_SCHEMA); + onGsettingChanged(WIRELESS_SWITCH); + connect(m_switchGsettings, &QGSettings::changed, this, &MobileHotspotWidget::onGsettingChanged, Qt::QueuedConnection); + } + + connect(m_apNameLine, &QLineEdit::textEdited, this, &MobileHotspotWidget::onApLineEditTextEdit); +} + +void MobileHotspotWidget::onApLineEditTextEdit(QString text) +{ + int count = 0; + int i = 0; + + for ( ; i < text.length(); ++i) { + count += text.mid(i,1).toLocal8Bit().length(); + if (count > AP_NAME_MAX_LENGTH) { + break; + } + } + + m_apNameLine->setText(text.left(i)); +} + + +void MobileHotspotWidget::onActiveConnectionChanged(QString deviceName, QString ssid, QString uuid, int status) +{ + if(m_uuid == uuid && status == 4) { + showDesktopNotify(tr("hotspot already close")); + m_switchBtn->setChecked(false); + setUiEnabled(false); + m_uuid.clear(); + } +} + +void MobileHotspotWidget::onGsettingChanged(const QString &key) +{ + if (key == WIRELESS_SWITCH) { + bool status = m_switchGsettings->get(WIRELESS_SWITCH).toBool(); + if (!status) { +// if (m_switchBtn->isChecked()) { +// if(m_interface->isValid()) { +// QDBusReply reply = m_interface->call("deactiveWirelessAp", +// m_apNameLine->text(), +// m_pwdNameLine->text(), +// m_interfaceComboBox->currentText()); +// if (!reply.isValid()) { +// qDebug() << "[MobileHotspotWidget] call deactiveWirelessAp failed "; +// return true; +// } +// } +// } + m_switchBtn->setChecked(status); + m_uuid.clear(); + m_switchBtn->setDisabledFlag(true); + } else { + m_switchBtn->setDisabledFlag(false); + } + } +} + +void MobileHotspotWidget::initInterfaceInfo() +{ + if(!m_interface->isValid()) { + return; + } + m_interfaceComboBox->clear(); + QDBusReply > reply = m_interface->call("getDeviceListAndEnabled",WIRELESS); + + if (!reply.isValid()) { + qDebug()<<"execute dbus method 'getDeviceListAndEnabled' is invalid in func initInterfaceInfo()"; + setWidgetHidden(true); + return; + } + QMap devMap = reply.value(); + + QDBusReply > capReply = m_interface->call("getWirelessDeviceCap"); + if (!capReply.isValid()) { + qDebug()<<"execute dbus method 'getWirelessDeviceCap' is invalid in func initInterfaceInfo()" < devCapMap = capReply.value(); + + + if (devMap.isEmpty()) { + qDebug() << "no wireless device"; + setWidgetHidden(true); + m_switchBtn->setDisabledFlag(true); + } else { + QMap::Iterator iter = devMap.begin(); + while (iter != devMap.end()) { + QString interfaceName = iter.key(); + if (devCapMap[interfaceName] & 0x01) { + m_interfaceComboBox->addItem(interfaceName); + } + iter++; + } + if (m_interfaceComboBox->count() > 0) { + setWidgetHidden(false); + m_interfaceName = m_interfaceComboBox->currentText(); + if (m_interfaceComboBox->count() == 1) { + updateBandCombox(); + } + } else { + qDebug() << "no useable wireless device"; + setWidgetHidden(true); + } + } +} + +void MobileHotspotWidget::getApInfo() +{ + if (!m_interface->isValid()) { + return; + } + + if (m_interfaceComboBox->count() <= 0) { + m_switchBtn->setChecked(false); + setWidgetHidden(true); + qWarning() << "getApInfo but interface is empty"; + return; + } + + + QDBusReply reply = m_interface->call("getStoredApInfo"); + if (!reply.isValid()) { + qDebug()<<"execute dbus method 'getStoredApInfo' is invalid in func getObjectPath()"; + } + + QStringList apInfo = reply.value(); + + if (apInfo.isEmpty()) { + qDebug() << "no stored hotspot info"; + m_apNameLine->setText(m_hostName); + m_pwdNameLine->setText("12345678"); + return; + } else { + int index = m_interfaceComboBox->findText(apInfo.at(2)); + if (index >= 0) { + m_apNameLine->setText(apInfo.at(0)); + m_pwdNameLine->setText(apInfo.at(1)); + m_interfaceComboBox->setCurrentIndex(index); + m_interfaceName = apInfo.at(2); + if (apInfo.at(3) == "true") { + m_switchBtn->setChecked(true); + setUiEnabled(true); + m_uuid = apInfo.at(4); + } else { + m_switchBtn->setChecked(false); + setUiEnabled(false); + } + } else { + qDebug() << "no such interface " << apInfo.at(2); + } + } +} + +void MobileHotspotWidget::setSwitchFrame() +{ + /* Open */ + m_switchFrame = new QFrame(this); + m_switchFrame->setFrameShape(QFrame::Shape::NoFrame); + m_switchFrame->setMinimumSize(FRAME_MIN_SIZE); + m_switchFrame->setMaximumSize(CONTECT_FRAME_MAX_SIZE); + + QHBoxLayout *switchLayout = new QHBoxLayout(); + + m_switchLabel = new QLabel(tr("Open"), this); + m_switchLabel->setMinimumWidth(LABLE_MIN_WIDTH); + m_switchBtn = new SwitchButton(this); + switchLayout->addSpacing(LAYOUT_LEFT_MARGINS); + switchLayout->addWidget(m_switchLabel); + switchLayout->addStretch(); + switchLayout->addWidget(m_switchBtn); + + m_switchFrame->setLayout(switchLayout); +} + +void MobileHotspotWidget::setApNameFrame() +{ + /* ApName */ + m_ApNameFrame = new QFrame(this); + m_ApNameFrame->setFrameShape(QFrame::Shape::NoFrame); + m_ApNameFrame->setMinimumSize(FRAME_MIN_SIZE); + m_ApNameFrame->setMaximumSize(CONTECT_FRAME_MAX_SIZE); + + QHBoxLayout *apNameHLayout = new QHBoxLayout(); + + m_apNameLabel = new QLabel(tr("Wi-Fi Name"), this); + m_apNameLabel->setMinimumWidth(LABLE_MIN_WIDTH); + m_apNameLine = new QLineEdit(this); + m_apNameLine->setMinimumWidth(COMBOBOX_MIN_WIDTH); + m_apNameLine->setMaxLength(AP_NAME_MAX_LENGTH); + apNameHLayout->addSpacing(LAYOUT_LEFT_MARGINS); + apNameHLayout->addWidget(m_apNameLabel); + apNameHLayout->addWidget(m_apNameLine); + m_ApNameFrame->setLayout(apNameHLayout); + +} + +void MobileHotspotWidget::setPasswordFrame() +{ + /* Password */ + m_passwordFrame = new QFrame(this); + m_passwordFrame->setFrameShape(QFrame::Shape::NoFrame); + m_passwordFrame->setMinimumSize(FRAME_MIN_SIZE); + m_passwordFrame->setMaximumSize(CONTECT_FRAME_MAX_SIZE); + + QHBoxLayout *passwordHLayout = new QHBoxLayout(); + + m_pwdLabel = new QLabel(tr("Password"), this); + m_pwdLabel->setMinimumWidth(LABLE_MIN_WIDTH); + m_pwdNameLine = new QLineEdit(this); + m_pwdNameLine->setMinimumWidth(COMBOBOX_MIN_WIDTH); + m_pwdNameLine->setEchoMode(QLineEdit::Password); + passwordHLayout->addSpacing(LAYOUT_LEFT_MARGINS); + passwordHLayout->addWidget(m_pwdLabel); + passwordHLayout->addWidget(m_pwdNameLine); + + m_passwordFrame->setLayout(passwordHLayout); + + m_pwdShowBox = new QPushButton(this); + m_pwdShowBox->setFlat(true); + m_pwdShowBox->setFixedSize(ICON_SIZE); + m_pwdShowBox->setIcon(QIcon::fromTheme("ukui-eye-hidden-symbolic")); + m_pwdShowBox->setCursor(Qt::PointingHandCursor); + //防止文本框输入内容位于按钮之下 + QMargins margins = m_pwdNameLine->textMargins(); + m_pwdNameLine->setTextMargins(margins.left(), margins.top(), m_pwdShowBox->width() + 10, margins.bottom()); + QHBoxLayout *pSearchLayout = new QHBoxLayout(); + pSearchLayout->addStretch(); + pSearchLayout->addWidget(m_pwdShowBox); + pSearchLayout->setSpacing(0); + pSearchLayout->setContentsMargins(0, 0, 10, 0); + m_pwdNameLine->setLayout(pSearchLayout); + m_pwdNameLine->setEchoMode(QLineEdit::Password); +} + +void MobileHotspotWidget::setFreqBandFrame() +{ + /* frequency band */ + m_freqBandFrame = new QFrame(this); + m_freqBandFrame->setFrameShape(QFrame::Shape::NoFrame); + m_freqBandFrame->setMinimumSize(FRAME_MIN_SIZE); + m_freqBandFrame->setMaximumSize(CONTECT_FRAME_MAX_SIZE); + + QHBoxLayout *freqBandHLayout = new QHBoxLayout(); + + m_freqBandLabel = new QLabel(tr("Frequency band"), this); + m_freqBandLabel->setMinimumWidth(LABLE_MIN_WIDTH); + m_freqBandComboBox = new QComboBox(this); + m_freqBandComboBox->setInsertPolicy(QComboBox::NoInsert); + m_freqBandComboBox->setMinimumWidth(COMBOBOX_MIN_WIDTH); + m_freqBandComboBox->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); + m_freqBandComboBox->addItem("2.4Ghz"); + m_freqBandComboBox->addItem("5Ghz"); + freqBandHLayout->addSpacing(LAYOUT_LEFT_MARGINS); + freqBandHLayout->addWidget(m_freqBandLabel); + freqBandHLayout->addWidget(m_freqBandComboBox); + + m_freqBandFrame->setLayout(freqBandHLayout); +} + +void MobileHotspotWidget::setInterFaceFrame() +{ + /* key tips */ + m_interfaceFrame = new QFrame(this); + m_interfaceFrame->setFrameShape(QFrame::Shape::NoFrame); + m_interfaceFrame->setMinimumSize(FRAME_MIN_SIZE); + m_interfaceFrame->setMaximumSize(CONTECT_FRAME_MAX_SIZE); + + QHBoxLayout *interfaceHLayout = new QHBoxLayout; + + m_interfaceLabel = new QLabel(tr("Net card"), this); + m_interfaceLabel->setMinimumWidth(LABLE_MIN_WIDTH); + m_interfaceComboBox = new QComboBox(this); + m_interfaceComboBox->setInsertPolicy(QComboBox::NoInsert); + m_interfaceComboBox->setMinimumWidth(COMBOBOX_MIN_WIDTH); + m_interfaceComboBox->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed); + interfaceHLayout->addSpacing(LAYOUT_LEFT_MARGINS); + interfaceHLayout->addWidget(m_interfaceLabel); + interfaceHLayout->addWidget(m_interfaceComboBox); + + m_interfaceFrame->setLayout(interfaceHLayout); +} + +void MobileHotspotWidget::onActivateFailed(QString errorMessage) +{ + if (errorMessage.indexOf("hotspot")) { + //todo +// showDesktopNotify(errorMessage); + } +} + +void MobileHotspotWidget::onDeactivateFailed(QString errorMessage) +{ + if (errorMessage.indexOf("hotspot")) { +// showDesktopNotify(errorMessage); + } +} + +//设备插拔 +void MobileHotspotWidget::onDeviceStatusChanged() +{ + initInterfaceInfo(); + getApInfo(); +} + +void MobileHotspotWidget::onDeviceNameChanged(QString oldName, QString newName, int type) +{ + if (WIRELESS != type) { + return; + } + int index = m_interfaceComboBox->findText(oldName); + if (index >= 0) { + m_interfaceComboBox->setItemText(index, newName); + if (m_interfaceName == oldName) { + m_interfaceName = newName; + } + } +} + +//热点断开 +void MobileHotspotWidget::onHotspotDeactivated(QString devName, QString ssid) +{ + if (!m_switchBtn->isChecked()) { + return; + } + if (devName == m_interfaceComboBox->currentText() && ssid == m_apNameLine->text()) { + m_switchBtn->setChecked(false); + m_uuid.clear(); +// setUiEnabled(true); + showDesktopNotify(tr("hotspot already close")); + } +} + +//热点连接 +void MobileHotspotWidget::onHotspotActivated(QString devName, QString ssid, QString uuid) +{ + qDebug() << "onHotspotActivated" <isChecked()) { + return; + } + + if (devName == m_interfaceComboBox->currentText() && ssid == m_apNameLine->text()) { + m_switchBtn->setChecked(true); + m_uuid = uuid; +// setUiEnabled(false); + showDesktopNotify(tr("hotspot already open")); + } else { + QStringList info; + if (!getApInfoBySsid(devName, ssid, info)) { + return; + } + int index = m_interfaceComboBox->findText(devName); + if (index >= 0) { + showDesktopNotify(tr("hotspot already open")); + m_apNameLine->setText(ssid); + m_interfaceComboBox->setCurrentIndex(index); + m_switchBtn->setChecked(true); + m_switchBtn->setDisabledFlag(false); + m_pwdNameLine->setText(info.at(0)); + m_interfaceName = devName; + updateBandCombox(); + index = m_freqBandComboBox->findText(info.at(1)); + if (index >= 0) { + m_freqBandComboBox->setCurrentIndex(index); + } + m_uuid = uuid; + } else { + qDebug() << "no such device in combo box"; + } + } +} + +bool MobileHotspotWidget::getApInfoBySsid(QString devName, QString ssid, QStringList &info) +{ + info.clear(); + if(!m_interface->isValid()) { + return false; + } + QDBusReply reply = m_interface->call("getApInfoBySsid", devName, ssid); + if (!reply.isValid()) { + qDebug()<<"execute dbus method 'getApInfoBySsid' is invalid in func getApInfoBySsid()"; + } + info = reply.value(); + if (info.size() != 2) { + return false; + } else { + return true; + } +} + +void MobileHotspotWidget::setUiEnabled(bool enable) +{ + qDebug() << "switch mode change to " << enable; + if (enable) { + m_pwdNameLine->setEnabled(false); + m_freqBandComboBox->setEnabled(false); + m_interfaceComboBox->setEnabled(false); + m_apNameLine->setEnabled(false); + } else { + m_pwdNameLine->setEnabled(true); + m_freqBandComboBox->setEnabled(true); + m_interfaceComboBox->setEnabled(true); + m_apNameLine->setEnabled(true); + } +} + +void MobileHotspotWidget::setWidgetHidden(bool isHidden) +{ + m_ApNameFrame->setHidden(isHidden); + m_passwordFrame->setHidden(isHidden); + m_freqBandFrame->setHidden(isHidden); + m_interfaceFrame->setHidden(isHidden); + + switchAndApNameLine->setHidden(isHidden); + apNameAndPwdLine->setHidden(isHidden); + pwdAndfreqBandLine->setHidden(isHidden); + freqBandAndInterfaceLine->setHidden(isHidden); + + if (isHidden) { + m_switchBtn->setChecked(false); + m_switchBtn->setDisabledFlag(true); + m_interfaceName = ""; + m_uuid = ""; + } else { + m_switchBtn->setDisabledFlag(false); + onGsettingChanged(WIRELESS_SWITCH); + } + +} + +void MobileHotspotWidget::updateBandCombox() +{ + m_freqBandComboBox->clear(); + QDBusReply > capReply = m_interface->call("getWirelessDeviceCap"); + if (!capReply.isValid()) { + qDebug()<<"execute dbus method 'getWirelessDeviceCap' is invalid in func initInterfaceInfo()" << capReply.error().message(); + setWidgetHidden(true); + return; + } + QMap devCapMap = capReply.value(); + if (devCapMap[m_interfaceName] & 0x02) { + m_freqBandComboBox->addItem("2.4Ghz"); + } + if (devCapMap[m_interfaceName] & 0x04) { + m_freqBandComboBox->addItem("5Ghz"); + } +} + +QFrame* MobileHotspotWidget::myLine() +{ + QFrame *line = new QFrame(this); + line->setMinimumSize(QSize(LINE_MIN_SIZE)); + line->setMaximumSize(QSize(LINE_MAX_SIZE)); + line->setLineWidth(0); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + + return line; +} + +QString MobileHotspotWidget::getHostName() +{ + int count = 0; + while (count < 3) { + QDBusInterface hostInterface("org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + QDBusConnection::systemBus()); + if (hostInterface.isValid()) { + return hostInterface.property("Hostname").value(); + } else { + count++; + } + } + return "default"; +} diff --git a/plugins/mobilehotspot/mobilehotspotwidget.h b/plugins/mobilehotspot/mobilehotspotwidget.h new file mode 100644 index 00000000..cd21d00f --- /dev/null +++ b/plugins/mobilehotspot/mobilehotspotwidget.h @@ -0,0 +1,119 @@ +#ifndef MOBILEHOTSPOTWIDGET_H +#define MOBILEHOTSPOTWIDGET_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include +#include +#include +#include +#include + +#include "switchbutton.h" +#include "titlelabel.h" + +class MobileHotspotWidget : public QWidget +{ + Q_OBJECT +public: + explicit MobileHotspotWidget(QWidget *parent = nullptr); + ~MobileHotspotWidget(); + +private: + QFrame *m_switchFrame = nullptr; //开关 + QFrame *m_ApNameFrame = nullptr; //wifi名称 + QFrame *m_passwordFrame = nullptr; //密码 + QFrame *m_freqBandFrame = nullptr; //频带 + QFrame *m_interfaceFrame = nullptr; //网卡 + + SwitchButton *m_switchBtn; + + TitleLabel *m_hotspotTitleLabel; + QLabel *m_switchLabel; + QLabel *m_apNameLabel; + QLabel *m_pwdLabel; + QLabel *m_freqBandLabel; + QLabel *m_interfaceLabel; + + + QPushButton *m_pwdShowBox; + + QFrame *switchAndApNameLine; + QFrame *apNameAndPwdLine; + QFrame *pwdAndfreqBandLine; + QFrame *freqBandAndInterfaceLine; + + QVBoxLayout *mVlayout; + + QLineEdit *m_apNameLine; + QLineEdit *m_pwdNameLine; + + QComboBox *m_freqBandComboBox; + QComboBox *m_interfaceComboBox; + + QDBusInterface *m_interface = nullptr; + + QString m_interfaceName = ""; + + QGSettings *m_switchGsettings = nullptr; + + QString m_uuid = ""; + QString m_hostName = ""; + + void initUI(); + void initDbusConnect(); + void initInterfaceInfo(); + void getApInfo(); + bool getApInfoBySsid(QString devName, QString ssid, QStringList &info); + void setSwitchFrame(); + void setApNameFrame(); + void setPasswordFrame(); + void setFreqBandFrame(); + void setInterFaceFrame(); + + void setUiEnabled(bool enable); + void setWidgetHidden(bool isHidden); + + void showDesktopNotify(const QString &message); + + void updateBandCombox(); + + QString getHostName(); + + bool eventFilter(QObject *watched, QEvent *event); + void paintEvent(QPaintEvent *event); + + QFrame* myLine(); + +signals: + +private slots: + void onActivateFailed(QString errorMessage); + void onDeactivateFailed(QString errorMessage); + //设备插拔 + void onDeviceStatusChanged(); + void onDeviceNameChanged(QString oldName, QString newName, int type); + //热点断开 + void onHotspotDeactivated(QString devName, QString ssid); + //热点连接 + void onHotspotActivated(QString devName, QString ssid, QString uuid); + + void onGsettingChanged(const QString &key); + + void onActiveConnectionChanged(QString deviceName, QString ssid, QString uuid, int status); + + void onApLineEditTextEdit(QString text); + +}; + +#endif // MOBILEHOTSPOTWIDGET_H diff --git a/plugins/mobilehotspot/translations/bo.qm b/plugins/mobilehotspot/translations/bo.qm new file mode 100644 index 00000000..be651eed --- /dev/null +++ b/plugins/mobilehotspot/translations/bo.qm @@ -0,0 +1 @@ + + + + + MobileHotspot + + + MobileHotspot + + + + + mobilehotspot + + /mobilehotspot/mobilehotspot + + + + mobilehotspot open + + /mobilehotspot/mobilehotspot open + + + + MobileHotspotWidget + + + ukui control center + + + + + ukui control center desktop message + + + + + wirless switch is close or no wireless device + + + + + start to close hotspot + + + + + hotpots name or device is invalid + + + + + can not create hotspot with password length less than eight! + + + + + start to open hotspot + + + + + Hotspot + + + + + + hotspot already close + + + + + Open + + + + + Wi-Fi Name + + + + + Password + + + + + Frequency band + + + + + Net card + + + + + + hotspot already open + + + + diff --git a/plugins/mobilehotspot/translations/tr.qm b/plugins/mobilehotspot/translations/tr.qm new file mode 100644 index 00000000..be651eed --- /dev/null +++ b/plugins/mobilehotspot/translations/tr.qm @@ -0,0 +1 @@ + + + + + MobileHotspot + + + MobileHotspot + + + + + mobilehotspot + + /mobilehotspot/mobilehotspot + + + + mobilehotspot open + + /mobilehotspot/mobilehotspot open + + + + MobileHotspotWidget + + + ukui control center + + + + + ukui control center desktop message + + + + + wirless switch is close or no wireless device + + + + + start to close hotspot + + + + + hotpots name or device is invalid + + + + + can not create hotspot with password length less than eight! + + + + + start to open hotspot + + + + + Hotspot + + + + + + hotspot already close + + + + + Open + + + + + Wi-Fi Name + + + + + Password + + + + + Frequency band + + + + + Net card + + + + + + hotspot already open + + + + diff --git a/plugins/mobilehotspot/translations/zh_CN.qm b/plugins/mobilehotspot/translations/zh_CN.qm new file mode 100644 index 0000000000000000000000000000000000000000..52e6c4f82b984a5241383e76793fccce1104248b GIT binary patch literal 1489 zcmah|O=uHA7@eelX_}W||Dy?re9aiSf|% zP)~)@9zs2|h@iIUMLdWeY!yUM@gm|$L8u-C+e#4xrRYqWY>m3>VVV8;-h1EszPBgt zM;o87&t+dW?_R!e?cuvq0Dx&wHnaqQ1A+E~UjZmdI*+q@07|=^W1X+X_d8{cP6U9n zvi#d(i#R{IZmoO}^)r?CCQpj`NAAZPmqq=e`%TxHsMo5F+_)}IkUx)A>GNSxci1yD z|3u(gJa^{=kK>u=bJHtcqlc0b3&gljn8GeU=1@U~y<};T(bAs0 zHA^j*G&u0|qGMa=-67J}L7*QEW82X{zju5yxGVf! zYG*Uyo9C+iQ-MTJcXC3;e5~tZ{7EQtT*D#JgrA(>qfi zO{zWrpb`sY2~Kr5EFywp_H#dg-;XU1f&klEVWh$D9XK1>AFGtUgIC8llM}K+{-ma8 gTZ$OD_%V@8B`%YaTd)j|Mnzl=ocp{LBui(%065LKZU6uP literal 0 HcmV?d00001 diff --git a/plugins/mobilehotspot/translations/zh_CN.ts b/plugins/mobilehotspot/translations/zh_CN.ts new file mode 100644 index 00000000..0867a0ec --- /dev/null +++ b/plugins/mobilehotspot/translations/zh_CN.ts @@ -0,0 +1,104 @@ + + + + + MobileHotspot + + + MobileHotspot + 移动热点 + + + + mobilehotspot + 移动热点 + /mobilehotspot/mobilehotspot + + + + mobilehotspot open + 移动热点 开启 + /mobilehotspot/mobilehotspot open + + + + MobileHotspotWidget + + + ukui control center + 控制面板 + + + + ukui control center desktop message + 控制面板桌面通知 + + + + wirless switch is close or no wireless device + 无线开关已关闭或不存在有热点功能的无线网卡 + + + + start to close hotspot + 开始关闭热点 + + + + hotpots name or device is invalid + 热点名称或设备错误 + + + + can not create hotspot with password length less than eight! + 不能创建密码长度小于八位的热点! + + + + start to open hotspot + 开始创建热点 + + + + Hotspot + 移动热点 + + + + + hotspot already close + 热点已关闭 + + + + Open + 开启 + + + + Wi-Fi Name + Wi-Fi名称 + + + + Password + 网络密码 + + + + Frequency band + 网络频带 + + + + Net card + 共享网卡端口 + + + + + hotspot already open + 热点已开启 + + + diff --git a/plugins/netconnect/addnetbtn.cpp b/plugins/netconnect/addnetbtn.cpp new file mode 100644 index 00000000..d0a14714 --- /dev/null +++ b/plugins/netconnect/addnetbtn.cpp @@ -0,0 +1,70 @@ +#include "addnetbtn.h" +#include +#include +#include +#include +#include +#include + +#define RADIUS 6.0 + +AddNetBtn::AddNetBtn(QWidget *parent) : QPushButton(parent) +{ + this->setObjectName("this"); + this->setMinimumSize(QSize(580, 60)); + this->setMaximumSize(QSize(16777215, 60)); + this->setStyleSheet("QPushButton:!checked{background-color: palette(base)}"); + this->setProperty("useButtonPalette", true); + + QHBoxLayout *addLyt = new QHBoxLayout; + + QLabel *iconLabel = new QLabel(); + QLabel *textLabel = new QLabel(tr("Add WiredNetork")); + + QIcon mAddIcon = QIcon::fromTheme("list-add-symbolic"); + iconLabel->setPixmap(mAddIcon.pixmap(mAddIcon.actualSize(QSize(24, 24)))); + iconLabel->setProperty("useIconHighlightEffect", true); + iconLabel->setProperty("iconHighlightEffectMode", 1); + + addLyt->addStretch(); + addLyt->addWidget(iconLabel); + addLyt->addWidget(textLabel); + addLyt->addStretch(); + this->setLayout(addLyt); + +} + +AddNetBtn::~AddNetBtn() +{ + +} + +void AddNetBtn::enterEvent(QEvent *event){ + Q_EMIT enterWidget(); + + QPushButton::enterEvent(event); +} + +void AddNetBtn::leaveEvent(QEvent *event){ + Q_EMIT leaveWidget(); + + QPushButton::leaveEvent(event); +} + +void AddNetBtn::paintEvent(QPaintEvent *event) +{ + QPalette pal = this->palette(); + + QPainter painter(this); + painter.setRenderHint(QPainter:: Antialiasing, true); //设置渲染,启动反锯齿 + painter.setPen(Qt::NoPen); + painter.setBrush(pal.color(QPalette::Base)); + + QRect rect = this->rect(); + QPainterPath path; + path.addRoundedRect (rect, RADIUS, RADIUS); + QRect temp_rect(rect.left(), rect.top(), rect.width(), rect.height()/2); + path.addRect(temp_rect); + painter.drawPath(path); + QPushButton::paintEvent(event); +} diff --git a/plugins/netconnect/addnetbtn.h b/plugins/netconnect/addnetbtn.h new file mode 100644 index 00000000..d989a150 --- /dev/null +++ b/plugins/netconnect/addnetbtn.h @@ -0,0 +1,27 @@ +#ifndef ADDNETBTN_H +#define ADDNETBTN_H + +#include +#include +#include +#include +#include + +class AddNetBtn : public QPushButton +{ + Q_OBJECT +public: + AddNetBtn(QWidget *parent = nullptr); + ~AddNetBtn(); + +protected: + virtual void leaveEvent(QEvent * event); + virtual void enterEvent(QEvent * event); + void paintEvent(QPaintEvent *event); + +Q_SIGNALS: + void enterWidget(); + void leaveWidget(); +}; + +#endif // ADDNETBTN_H diff --git a/plugins/netconnect/deviceframe.cpp b/plugins/netconnect/deviceframe.cpp new file mode 100644 index 00000000..12b59f69 --- /dev/null +++ b/plugins/netconnect/deviceframe.cpp @@ -0,0 +1,46 @@ +#include "deviceframe.h" + +#define LAYOUT_MARGINS 18,0,24,0 +#define FRAME_HEIGHT 58 +#define RADIUS 6.0 + +DeviceFrame::DeviceFrame(QString devName, QWidget *parent) : QFrame(parent) +{ + this->setFrameShape(QFrame::Box); + this->setFixedHeight(FRAME_HEIGHT); + QHBoxLayout *deviceLayout = new QHBoxLayout(this); + deviceLayout->setContentsMargins(LAYOUT_MARGINS); + setLayout(deviceLayout); + + deviceLabel = new QLabel(this); + dropDownLabel = new DrownLabel(devName, this); + deviceSwitch = new SwitchButton(this); + + deviceLayout->addWidget(deviceLabel); + deviceLayout->addStretch(); + deviceLayout->addWidget(dropDownLabel); + deviceLayout->addWidget(deviceSwitch); +} + +DeviceFrame::~DeviceFrame() +{ + +} + +void DeviceFrame::paintEvent(QPaintEvent *event) +{ + QPalette pal = this->palette(); + + QPainter painter(this); + painter.setRenderHint(QPainter:: Antialiasing, true); //设置渲染,启动反锯齿 + painter.setPen(Qt::NoPen); + painter.setBrush(pal.color(QPalette::Base)); + + QRect rect = this->rect(); + QPainterPath path; + path.addRoundedRect (rect, RADIUS, RADIUS); + QRect temp_rect(rect.left(), rect.top() + rect.height()/2, rect.width(), rect.height()/2); + path.addRect(temp_rect); + painter.drawPath(path); + QFrame::paintEvent(event); +} diff --git a/plugins/netconnect/deviceframe.h b/plugins/netconnect/deviceframe.h new file mode 100644 index 00000000..5834b62c --- /dev/null +++ b/plugins/netconnect/deviceframe.h @@ -0,0 +1,33 @@ +#ifndef DEVICEFRAME_H +#define DEVICEFRAME_H +#include +#include +#include +#include +#include +#include +#include +#include "switchbutton.h" +#include "../component/DrownLabel/drownlabel.h" + +class DeviceFrame : public QFrame +{ + +public: + DeviceFrame(QString devName, QWidget *parent = nullptr); + ~DeviceFrame(); +public: + QLabel * deviceLabel = nullptr; + SwitchButton * deviceSwitch = nullptr; + DrownLabel *dropDownLabel = nullptr; + +protected: + void paintEvent(QPaintEvent *event); + +private: + bool isDropDown = false; + int frameSize; + +}; + +#endif // DEVICEFRAME_H diff --git a/plugins/netconnect/itemframe.cpp b/plugins/netconnect/itemframe.cpp new file mode 100644 index 00000000..c144e80e --- /dev/null +++ b/plugins/netconnect/itemframe.cpp @@ -0,0 +1,47 @@ +#include "itemframe.h" + +#include +#include + +#define LAYOUT_MARGINS 0,0,0,0 +#define MAIN_LAYOUT_MARGINS 0,0,0,0 +ItemFrame::ItemFrame(QString devName, QWidget *parent) : QFrame(parent) +{ + deviceLanLayout = new QVBoxLayout(this); + deviceLanLayout->setContentsMargins(MAIN_LAYOUT_MARGINS); + lanItemFrame = new QFrame(this); + lanItemFrame->setFrameShape(QFrame::Shape::NoFrame); + + lanItemLayout = new QVBoxLayout(this); + lanItemLayout->setContentsMargins(LAYOUT_MARGINS); + lanItemLayout->setSpacing(1); + addLanWidget = new AddNetBtn(this); + + deviceLanLayout->setSpacing(1); + setLayout(deviceLanLayout); + lanItemFrame->setLayout(lanItemLayout); + + deviceFrame = new DeviceFrame(devName, this); + deviceLanLayout->addWidget(deviceFrame); + deviceLanLayout->addWidget(lanItemFrame); + deviceLanLayout->addWidget(addLanWidget); + + //下拉按钮 + connect(deviceFrame->dropDownLabel, &DrownLabel::labelClicked, this, &ItemFrame::onDrownLabelClicked); +} + +ItemFrame::~ItemFrame() +{ + +} + +void ItemFrame::onDrownLabelClicked() +{ + if (!deviceFrame->dropDownLabel->isChecked) { + lanItemFrame->show(); + deviceFrame->dropDownLabel->setDropDownStatus(true); + } else { + lanItemFrame->hide(); + deviceFrame->dropDownLabel->setDropDownStatus(false); + } +} diff --git a/plugins/netconnect/itemframe.h b/plugins/netconnect/itemframe.h new file mode 100644 index 00000000..20b23931 --- /dev/null +++ b/plugins/netconnect/itemframe.h @@ -0,0 +1,33 @@ +#ifndef ITEMFRAME_H +#define ITEMFRAME_H +#include +#include +#include "deviceframe.h" +#include +#include "addnetbtn.h" +#include "lanitem.h" + +class ItemFrame : public QFrame +{ + Q_OBJECT +public: + ItemFrame(QString devName, QWidget *parent = nullptr); + ~ItemFrame(); + //单设备整体layout + QVBoxLayout * deviceLanLayout = nullptr; + //单设备名称+下拉按钮Frame+d单设备开关 + DeviceFrame * deviceFrame = nullptr; + //单设备列表Frame + QFrame * lanItemFrame = nullptr; + //单设备列表layout + QVBoxLayout * lanItemLayout = nullptr; + //新建有线连接 + AddNetBtn * addLanWidget = nullptr; + //单设备item列表 key:uuid + QMap itemMap; + +private slots: + void onDrownLabelClicked(); +}; + +#endif // ITEMFRAME_H diff --git a/plugins/netconnect/lanitem.cpp b/plugins/netconnect/lanitem.cpp new file mode 100644 index 00000000..5d33d983 --- /dev/null +++ b/plugins/netconnect/lanitem.cpp @@ -0,0 +1,81 @@ +#include "lanitem.h" +#define FRAME_SPEED 150 +#define LIMIT_TIME 60*1000 +#define TOTAL_PAGE 8 + +#define THEME_QT_SCHEMA "org.ukui.style" +#define MODE_QT_KEY "style-name" + +LanItem::LanItem(bool isAcitve, QWidget *parent) + : QPushButton(parent),isAcitve(isAcitve) +{ + this->setMinimumSize(550, 58); + this->setProperty("useButtonPalette", true); + setStyleSheet("QPushButton:!checked{background-color: palette(base)}"); + QHBoxLayout *mLanLyt = new QHBoxLayout(this); + mLanLyt->setContentsMargins(16,0,16,0); + mLanLyt->setSpacing(16); + iconLabel = new QLabel(this); + iconLabel->setProperty("useIconHighlightEffect", 0x2); + titileLabel = new FixLabel(this); + statusLabel = new QLabel(this); + statusLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); +// statusLabel->setMinimumSize(36,36); + infoLabel = new InfoButton(this); + mLanLyt->addWidget(iconLabel); + mLanLyt->addWidget(titileLabel,Qt::AlignLeft); + mLanLyt->addStretch(); + mLanLyt->addWidget(statusLabel); + mLanLyt->addWidget(infoLabel); + + loadIcons.append(QIcon::fromTheme("ukui-loading-1-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-2-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-3-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-4-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-5-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-6-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-7-symbolic")); + waitTimer = new QTimer(this); + connect(waitTimer, &QTimer::timeout, this, &LanItem::updateIcon); +} + +LanItem::~LanItem() +{ + +} + +void LanItem::updateIcon() +{ + if (currentIconIndex > 6) { + currentIconIndex = 0; + } + statusLabel->setPixmap(loadIcons.at(currentIconIndex).pixmap(16,16)); + currentIconIndex ++; +} + +void LanItem::startLoading() +{ + waitTimer->start(FRAME_SPEED); + loading = true; +} + +void LanItem::stopLoading(){ + waitTimer->stop(); + loading = false; +} + +void LanItem::paintEvent(QPaintEvent *event) +{ + QPalette pal = this->palette(); + + QPainter painter(this); + painter.setRenderHint(QPainter:: Antialiasing, true); //设置渲染,启动反锯齿 + painter.setPen(Qt::NoPen); + painter.setBrush(pal.color(QPalette::Base)); + + QRect rect = this->rect(); + + painter.drawRect(rect); + QPushButton::paintEvent(event); +} + diff --git a/plugins/netconnect/lanitem.h b/plugins/netconnect/lanitem.h new file mode 100644 index 00000000..dbaacb04 --- /dev/null +++ b/plugins/netconnect/lanitem.h @@ -0,0 +1,51 @@ +#ifndef LANITEM_H +#define LANITEM_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fixlabel.h" +#include "infobutton.h" + +class LanItem : public QPushButton +{ +public: + LanItem(bool isAcitve, QWidget *parent = nullptr); + ~LanItem(); +public: + QLabel * iconLabel = nullptr; + InfoButton * infoLabel = nullptr; + FixLabel * titileLabel = nullptr; + QLabel * statusLabel = nullptr; + +public: + void startLoading(); + void stopLoading(); + + bool loading = false; + bool isAcitve = false; + + QString uuid; + QString dbusPath; + +protected: + void paintEvent(QPaintEvent *); + +private: + QTimer *waitTimer = nullptr; + QGSettings *themeGsettings = nullptr; + QList loadIcons; + int currentIconIndex=0; + +private slots: + void updateIcon(); + +}; + +#endif // LANITEM_H diff --git a/plugins/netconnect/netconnect.cpp b/plugins/netconnect/netconnect.cpp new file mode 100644 index 00000000..b4c0c240 --- /dev/null +++ b/plugins/netconnect/netconnect.cpp @@ -0,0 +1,883 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "netconnect.h" +#include "ui_netconnect.h" + +#include +#include +#include +#include +#include +#include +#include + +#define WIRED_TYPE 0 +#define ITEMHEIGH 50 +#define LAN_TYPE 0 +#define CONTROL_CENTER_WIFI "org.ukui.control-center.wifi.switch" + +const QString KLanSymbolic = "network-wired-connected-symbolic"; +const QString NoNetSymbolic = "network-wired-disconnected-symbolic"; + +const QString WIRED_SWITCH = "wiredswitch"; +const QByteArray GSETTINGS_SCHEMA = "org.ukui.kylin-nm.switch"; + +#define ACTIVATING 1 +#define ACTIVATED 2 +#define DEACTIVATING 3 +#define DEACTIVATED 4 + +#define NO_MARGINS 0,0,0,0 +#define TOP_MARGINS 0,8,0,0 +#define MAIN_LAYOUT_MARGINS 0,0,0,8 +#define SPACING 8 + +bool sortByVal(const QPair &l, const QPair &r) { + return (l.second < r.second); +} + +void NetConnect::showDesktopNotify(const QString &message) +{ + QDBusInterface iface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::sessionBus()); + QList args; + args<<(tr("ukui control center")) + <<((unsigned int) 0) + <load("/usr/share/kylin-nm/netconnect/" + QLocale::system().name()); + QApplication::installTranslator(translator); + + pluginName = tr("WiredConnect"); + pluginType = NETWORK; +} + +NetConnect::~NetConnect() { + if (!mFirstLoad) { + delete ui; + ui = nullptr; + delete m_interface; + delete m_switchGsettings; + } +} + +QString NetConnect::plugini18nName() { + return pluginName; +} + +int NetConnect::pluginTypes() { + return pluginType; +} + +QWidget *NetConnect::pluginUi() { + if (mFirstLoad) { + mFirstLoad = false; + + ui = new Ui::NetConnect; + pluginWidget = new QWidget; + pluginWidget->setAttribute(Qt::WA_DeleteOnClose); + ui->setupUi(pluginWidget); + qDBusRegisterMetaType>(); + m_interface = new QDBusInterface("com.kylin.network", + "/com/kylin/network", + "com.kylin.network", + QDBusConnection::sessionBus()); + if(!m_interface->isValid()) { + qWarning() << qPrintable(QDBusConnection::sessionBus().lastError().message()); + } + initSearchText(); + initComponent(); + } + return pluginWidget; +} + +const QString NetConnect::name() const { + + return QStringLiteral("netconnect"); +} + +bool NetConnect::isEnable() const +{ + return true; +} + + +bool NetConnect::isShowOnHomePage() const +{ + return false; +} + +QIcon NetConnect::icon() const +{ + return QIcon(); +} + +QString NetConnect::translationPath() const +{ + return "/usr/share/kylin-nm/netconnect/%1.ts"; +} + +void NetConnect::initSearchText() { + //~ contents_path /netconnect/Advanced settings" + ui->detailBtn->setText(tr("Advanced settings")); + ui->titleLabel->setText(tr("Wired Network")); + //~ contents_path /netconnect/open + ui->openLabel->setText(tr("open")); +} + +bool NetConnect::eventFilter(QObject *w, QEvent *e) { + if (e->type() == QEvent::Enter) { + if (w->findChild()) + w->findChild()->setStyleSheet("QWidget{background: palette(button);border-radius:4px;}"); + } else if (e->type() == QEvent::Leave) { + if (w->findChild()) + w->findChild()->setStyleSheet("QWidget{background: palette(base);border-radius:4px;}"); + } + return QObject::eventFilter(w,e); +} + +void NetConnect::initComponent() { + wiredSwitch = new SwitchButton(pluginWidget); + ui->openWIifLayout->addWidget(wiredSwitch); + ui->detailLayOut->setContentsMargins(MAIN_LAYOUT_MARGINS); + ui->verticalLayout_3->setContentsMargins(NO_MARGINS); + ui->availableLayout->setSpacing(SPACING); + ui->horizontalLayout->setContentsMargins(TOP_MARGINS); + + connect(wiredSwitch, &SwitchButton::disabledClick, this, [=]() { + showDesktopNotify(tr("No ethernet device avaliable")); + }); + + if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA)) { + m_switchGsettings = new QGSettings(GSETTINGS_SCHEMA); + connect(wiredSwitch, &SwitchButton::checkedChanged, this, [=] (bool checked) { + if (!m_interface->isValid()) { + return; + } + if (wiredSwitch->getDisabledFlag()) { + return; + } + qDebug() << "[NetConnect]call setWiredSwitchEnable" << checked << __LINE__; + m_interface->call(QStringLiteral("setWiredSwitchEnable"),checked); + qDebug() << "[NetConnect]call setWiredSwitchEnable Respond" << __LINE__; + }); + setSwitchStatus(); + connect(m_switchGsettings, &QGSettings::changed, this, [=] (const QString &key) { + if (key == WIRED_SWITCH) { + setSwitchStatus(); + } + }); + } else { + wiredSwitch->blockSignals(true); + wiredSwitch->setChecked(true); + wiredSwitch->blockSignals(false); + qDebug()<<"[Netconnect] org.ukui.kylin-nm.switch is not installed!"; + } + + getDeviceStatusMap(deviceStatusMap); + if (deviceStatusMap.isEmpty()) { + qDebug() << "[Netconnect] no device exist when init, set switch disable"; + wiredSwitch->setDisabledFlag(true); + wiredSwitch->setChecked(false); + } + initNet(); + + if (!wiredSwitch->isChecked() || deviceStatusMap.isEmpty() || !m_interface->isValid()) { + hideLayout(ui->availableLayout); + } + + + // 有线网络断开或连接时刷新可用网络列表 + connect(m_interface, SIGNAL(lanActiveConnectionStateChanged(QString, QString, int)), this, SLOT(onActiveConnectionChanged(QString, QString, int)), Qt::QueuedConnection); + //有线网络新增时添加网络 + connect(m_interface, SIGNAL(lanAdd(QString, QStringList)), this, SLOT(onLanAdd(QString, QStringList)), Qt::QueuedConnection); + //删除有线网络 + connect(m_interface, SIGNAL(lanRemove(QString)), this, SLOT(onLanRemove(QString)), Qt::QueuedConnection); + //更新有线网络 + connect(m_interface, SIGNAL(lanUpdate(QString, QStringList)), this, SLOT(updateLanInfo(QString, QStringList)),Qt::QueuedConnection); + //网卡插拔处理 + connect(m_interface, SIGNAL(deviceStatusChanged()), this, SLOT(onDeviceStatusChanged()),Qt::QueuedConnection); + //网卡name处理 + connect(m_interface, SIGNAL(deviceNameChanged(QString, QString, int)), this, SLOT(onDeviceNameChanged(QString, QString, int)),Qt::QueuedConnection); + + + connect(ui->detailBtn, &QPushButton::clicked, this, [=](bool checked) { + Q_UNUSED(checked) + runExternalApp(); + }); +} + +//获取网卡列表 +void NetConnect::getDeviceStatusMap(QMap &map) +{ + if (!m_interface->isValid()) { + return; + } + qDebug() << "[NetConnect]call getDeviceListAndEnabled" << __LINE__; + QDBusMessage result = m_interface->call(QStringLiteral("getDeviceListAndEnabled"),0); + qDebug() << "[NetConnect]call getDeviceListAndEnabled Respond" << __LINE__; + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "[NetConnect]getWiredDeviceList error:" << result.errorMessage(); + return; + } + auto dbusArg = result.arguments().at(0).value(); + dbusArg >> map; +} + +//lanUpdate +void NetConnect::updateLanInfo(QString deviceName, QStringList lanInfo) +{ + //she bei gui shu bian hua && you xian ming cheng bian hua + QMap::iterator iter; + for (iter = deviceFrameMap.begin(); iter != deviceFrameMap.end(); iter++) { + if (deviceName.isEmpty()) { + //变为无指定网卡,所有列表都要添加 + if (!iter.value()->itemMap.contains(lanInfo.at(1))) { + qDebug() << "[NetConnect]" << lanInfo.at(0) << " change to device none, add every list"; + addOneLanFrame(iter.value(), iter.key(), lanInfo); + } else { + if (iter.value()->itemMap[lanInfo.at(1)]->titileLabel->text() != lanInfo.at(0)) { + qDebug() << "[NetConnect]" << iter.key() + << iter.value()->itemMap[lanInfo.at(1)]->titileLabel->text() << "change to" << lanInfo.at(0); + iter.value()->itemMap[lanInfo.at(1)]->titileLabel->setText(lanInfo.at(0)); + } + } + } else { + if (iter.key() != deviceName) { + qDebug() << "[NetConnect]" << lanInfo.at(0) << " not belongs to " << iter.key(); + removeOneLanFrame(iter.value(), deviceName, lanInfo.at(1)); + } else { + if (!iter.value()->itemMap.contains(lanInfo.at(1))) { + qDebug() << "[NetConnect]" << lanInfo.at(0) << " now belongs to " << deviceName; + addOneLanFrame(iter.value(), deviceName, lanInfo); + } else { + qDebug() << "[NetConnect]" << deviceName + << iter.value()->itemMap[lanInfo.at(1)]->titileLabel->text() << "change to" << lanInfo.at(0); + if (iter.value()->itemMap[lanInfo.at(1)]->titileLabel->text() != lanInfo.at(0)) { + iter.value()->itemMap[lanInfo.at(1)]->titileLabel->setText(lanInfo.at(0)); + } + } + } + } + } +} + + +//总开关 +void NetConnect::setSwitchStatus() +{ + if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA)) { + bool status = m_switchGsettings->get(WIRED_SWITCH).toBool(); + wiredSwitch->blockSignals(true); + wiredSwitch->setChecked(status); + wiredSwitch->blockSignals(false); + if (!status) { + hideLayout(ui->availableLayout); + } else { + showLayout(ui->availableLayout); + } + } else { + qDebug()<<"[netconnect] org.ukui.kylin-nm.switch is not installed!"; + } + +} + +//总体隐藏 +void NetConnect::hideLayout(QVBoxLayout * layout) { + for (int i = layout->layout()->count()-1; i >= 0; --i) + { + QLayoutItem *it = layout->layout()->itemAt(i); + ItemFrame *itemFrame = qobject_cast(it->widget()); + itemFrame->hide(); + } +} + +//总体显示 +void NetConnect::showLayout(QVBoxLayout * layout) { + for (int i = layout->layout()->count()-1; i >= 0; --i) + { + QLayoutItem *it = layout->layout()->itemAt(i); + ItemFrame *itemFrame = qobject_cast(it->widget()); + itemFrame->show(); + } +} + +//初始化 +void NetConnect::initNet() +{ + //先构建每个设备的列表头 + QStringList deviceList = deviceStatusMap.keys(); + for (int i = 0; i < deviceList.size(); ++i) { + addDeviceFrame(deviceList.at(i)); + } + //再填充每个设备的列表 + for (int i = 0; i < deviceList.size(); ++i) { + initNetListFromDevice(deviceList.at(i)); + } +} + +void NetConnect::runExternalApp() { + QString cmd = "nm-connection-editor"; + QProcess process(this); + process.startDetached(cmd); +} + +//激活 +void NetConnect::activeConnect(QString ssid, QString deviceName, int type) { + qDebug() << "[NetConnect]call activateConnect" << __LINE__; + m_interface->call(QStringLiteral("activateConnect"),type, deviceName, ssid); + qDebug() << "[NetConnect]call activateConnect respond" << __LINE__; +} + +//断开 +void NetConnect::deActiveConnect(QString ssid, QString deviceName, int type) { + qDebug() << "[NetConnect]call deActivateConnect" << __LINE__; + m_interface->call(QStringLiteral("deActivateConnect"),type, deviceName, ssid); + qDebug() << "[NetConnect]call deActivateConnect respond" << __LINE__; +} + +//初始化设备列表 网卡标号问题? +void NetConnect::initNetListFromDevice(QString deviceName) +{ + qDebug() << "[NetConnect]initNetListFromDevice " << deviceName; + if (!deviceFrameMap.contains(deviceName)) { + qDebug() << "[NetConnect]initNetListFromDevice " << deviceName << " not exist"; + return; + } + if (!m_interface->isValid()) { + return; + } + qDebug() << "[NetConnect]call getWiredList" << __LINE__; + QDBusMessage result = m_interface->call(QStringLiteral("getWiredList")); + qDebug() << "[NetConnect]call getWiredList respond" << __LINE__; + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "getWiredList error:" << result.errorMessage(); + return; + } + auto dbusArg = result.arguments().at(0).value(); + QMap> variantList; + dbusArg >> variantList; + if (variantList.size() == 0) { + qDebug() << "[NetConnect]initNetListFromDevice " << deviceName << " list empty"; + return; + } + QMap>::iterator iter; + + for (iter = variantList.begin(); iter != variantList.end(); iter++) { + if (deviceName == iter.key()) { + QVector wlanListInfo = iter.value(); + //处理列表 已连接 + qDebug() << "[NetConnect]initNetListFromDevice " << deviceName << " acitved lan " << wlanListInfo.at(0); + addLanItem(deviceFrameMap[deviceName], deviceName, wlanListInfo.at(0), true); + //处理列表 未连接 + for (int i = 1; i < wlanListInfo.length(); i++) { + qDebug() << "[NetConnect]initNetListFromDevice " << deviceName << " deacitved lan " << wlanListInfo.at(i); + addLanItem(deviceFrameMap[deviceName], deviceName, wlanListInfo.at(i), false); + } + } + } + return; +} + +//初始化时添加一个项 不考虑顺序 +void NetConnect::addLanItem(ItemFrame *frame, QString devName, QStringList infoList, bool isActived) +{ + if (frame == nullptr) { + return; + } + if (infoList.size() == 1) { + return; + } + + LanItem * lanItem = new LanItem(pluginWidget); + QString iconPath = KLanSymbolic; + if (isActived) { + lanItem->statusLabel->setText(tr("connected")); + } else { + lanItem->statusLabel->setText(""); + } + QIcon searchIcon = QIcon::fromTheme(iconPath); +// if (iconPath != KLanSymbolic && iconPath != NoNetSymbolic) { +// lanItem->iconLabel->setProperty("useIconHighlightEffect", 0x10); +// } + lanItem->iconLabel->setPixmap(searchIcon.pixmap(searchIcon.actualSize(QSize(24, 24)))); + lanItem->titileLabel->setText(infoList.at(0)); + + lanItem->uuid = infoList.at(1); + lanItem->dbusPath = infoList.at(2); + + connect(lanItem->infoLabel, &InfoButton::clicked, this, [=]{ + // open landetail page + if (!m_interface->isValid()) { + return; + } + qDebug() << "[NetConnect]call showPropertyWidget" << __LINE__; + m_interface->call(QStringLiteral("showPropertyWidget"), devName, infoList.at(1)); + qDebug() << "[NetConnect]call showPropertyWidget respond" << __LINE__; + }); + + lanItem->isAcitve = isActived; + + connect(lanItem, &QPushButton::clicked, this, [=] { + if (lanItem->isAcitve || lanItem->loading) { + deActiveConnect(lanItem->uuid, devName, WIRED_TYPE); + } else { + activeConnect(lanItem->uuid, devName, WIRED_TYPE); + } + }); + + //记录到deviceFrame的itemMap中 + deviceFrameMap[devName]->itemMap.insert(infoList.at(1), lanItem); + qDebug()<<"insert " << infoList.at(1) << " to " << devName << " list"; + frame->lanItemLayout->addWidget(lanItem); +} + +//增加设备 +void NetConnect::addDeviceFrame(QString devName) +{ + qDebug() << "[NetConnect]addDeviceFrame " << devName; + + qDebug() << "[NetConnect]call getDeviceListAndEnabled" << __LINE__; + QDBusMessage result = m_interface->call(QStringLiteral("getDeviceListAndEnabled"),0); + qDebug() << "[NetConnect]call getDeviceListAndEnabled Respond" << __LINE__; + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "[NetConnect]getWiredDeviceList error:" << result.errorMessage(); + return; + } + auto dbusArg = result.arguments().at(0).value(); + QMap map; + dbusArg >> map; + + bool enable = true; + if (map.contains(devName)) { + enable = map[devName]; + } + + ItemFrame *itemFrame = new ItemFrame(devName, pluginWidget); + ui->availableLayout->addWidget(itemFrame); + itemFrame->deviceFrame->deviceLabel->setText(tr("card")+/*QString("%1").arg(count)+*/":"+devName); + itemFrame->deviceFrame->deviceSwitch->setChecked(enable); + if (enable) { + itemFrame->lanItemFrame->show(); + itemFrame->deviceFrame->dropDownLabel->show(); + } else { + itemFrame->lanItemFrame->hide(); + itemFrame->deviceFrame->dropDownLabel->hide(); + itemFrame->deviceFrame->dropDownLabel->setDropDownStatus(false); + } + deviceFrameMap.insert(devName, itemFrame); + qDebug() << "[NetConnect]deviceFrameMap insert" << devName; + + connect(itemFrame->deviceFrame->deviceSwitch, &SwitchButton::checkedChanged, this, [=] (bool checked) { + qDebug() << "[NetConnect]call setDeviceEnable" << devName << checked << __LINE__; + m_interface->call(QStringLiteral("setDeviceEnable"), devName, checked); + qDebug() << "[NetConnect]call setDeviceEnable Respond" << __LINE__; + if (checked) { + qDebug() << "[NetConnect]set " << devName << "status" << true; + itemFrame->lanItemFrame->show(); + itemFrame->deviceFrame->dropDownLabel->show(); + itemFrame->deviceFrame->dropDownLabel->setDropDownStatus(true); + deviceStatusMap[devName] = true; + } else { + qDebug() << "[NetConnect]set " << devName << "status" << false; + itemFrame->lanItemFrame->hide(); + itemFrame->deviceFrame->dropDownLabel->hide(); + deviceStatusMap[devName] = false; + } + }); + + connect(itemFrame->addLanWidget, &AddNetBtn::clicked, this, [=](){ + if (m_interface->isValid()) { + qDebug() << "[NetConnect]call showCreateWiredConnectWidget" << devName << __LINE__; + m_interface->call(QStringLiteral("showCreateWiredConnectWidget"), devName); + qDebug() << "[NetConnect]call setDeviceEnable Respond" << __LINE__; + } + }); +} + +//减少设备 +void NetConnect::removeDeviceFrame(QString devName) +{ + qDebug() << "[NetConnect]removeDeviceFrame " << devName; + if (deviceFrameMap.contains(devName)) { + ItemFrame *item = deviceFrameMap[devName]; + if (item->lanItemFrame->layout() != NULL) { + QLayoutItem* layoutItem; + while ((layoutItem = item->lanItemFrame->layout()->takeAt(0)) != NULL) { + delete layoutItem->widget(); + delete layoutItem; + layoutItem = nullptr; + } + item->itemMap.clear(); + } + delete item; + item = nullptr; + deviceFrameMap.remove(devName); + qDebug() << "[NetConnect]deviceFrameMap remove" << devName; + } +} + +//device add or remove================================= +void NetConnect::onDeviceStatusChanged() +{ + qDebug()<<"[NetConnect]onDeviceStatusChanged"; + QEventLoop eventloop; + QTimer::singleShot(300, &eventloop, SLOT(quit())); + eventloop.exec(); + QStringList list; + QMap map; + getDeviceStatusMap(map); + list = map.keys(); + + QStringList removeList; + QMap addMap; + + //remove的设备 + for (int i = 0; i< deviceStatusMap.keys().size(); ++i) { + if (!list.contains(deviceStatusMap.keys().at(i))) { + qDebug() << "[NetConnect]onDeviceStatusChanged " << deviceStatusMap.keys().at(i) << "was removed"; + removeList << deviceStatusMap.keys().at(i); + } + } + + //add的设备 + for (int i = 0; i< list.size(); ++i) { + if (!deviceStatusMap.keys().contains(list.at(i))) { + qDebug() << "[NetConnect]onDeviceStatusChanged " << list.at(i) << "was add, init status" << map[list.at(i)]; + addMap.insert(list.at(i),map[list.at(i)]); + } + } + + for (int i = 0; i < removeList.size(); ++i) { + removeDeviceFrame(removeList.at(i)); + } + + QStringList addList = addMap.keys(); + for (int i = 0; i < addList.size(); ++i) { + qDebug() << "add a device " << addList.at(i) << "status" << map[addList.at(i)]; + addDeviceFrame(addList.at(i)); + initNetListFromDevice(addList.at(i)); + } + deviceStatusMap = map; + if (deviceStatusMap.isEmpty()) { + wiredSwitch->setDisabledFlag(true); + wiredSwitch->setChecked(false); + } else { + wiredSwitch->setDisabledFlag(false); + setSwitchStatus(); + } + +} + +void NetConnect::onDeviceNameChanged(QString oldName, QString newName, int type) +{ + if (WIRED_TYPE != type || !deviceFrameMap.contains(oldName) || !deviceStatusMap.contains(oldName)) { + qDebug() << "[NetConnect]onDeviceNameChanged no such device " << oldName; + return; + } + + if (deviceFrameMap.contains(newName) && deviceStatusMap.contains(newName)) { + qDebug() << "[NetConnect]onDeviceNameChanged already has device " << newName; + return; + } + + qDebug() << "[NetConnect]onDeviceNameChanged " << oldName << "change to" << newName; + + //shan chu chong jian + removeDeviceFrame(oldName); + removeDeviceFrame(newName); + + getDeviceStatusMap(deviceStatusMap); + if (deviceStatusMap.contains(newName)) { + addDeviceFrame(newName); + initNetListFromDevice(newName); + } +} + +//wifi add=============================================================== +void NetConnect::onLanAdd(QString deviceName, QStringList lanInfo) +{ + qDebug()<<"[NetConnect]onLanAdd "<< deviceName << " " << lanInfo; + + if (!deviceName.isEmpty() && !deviceStatusMap.contains(deviceName)) { + return; + } + + QMap::iterator iter; + for (iter = deviceFrameMap.begin(); iter != deviceFrameMap.end(); iter++) { + if (deviceName.isEmpty()) { + qDebug() << "[NetConnect]onLanAdd every list" << iter.key(); + addOneLanFrame(iter.value(), iter.key(), lanInfo); + } else if (deviceName == iter.key()) { + qDebug() << "[NetConnect]onLanAdd "<< deviceName; + addOneLanFrame(iter.value(), deviceName, lanInfo); + break; + } + } +} + +//wifi remove ============================================================= +void NetConnect::onLanRemove(QString lanPath) +{ + //开关已关闭 忽略 +// if (!wifiSwtch->isChecked()) { +// qDebug() << "[NetConnect]recieve network remove,but wireless switch is off"; +// return; +// } + + qDebug()<<"[NetConnect]lan remove " << "dbus path:" << lanPath; + QMap::iterator iter; + for (iter = deviceFrameMap.begin(); iter != deviceFrameMap.end(); iter++) { + QMap::iterator itemIter; + for (itemIter = iter.value()->itemMap.begin(); itemIter != iter.value()->itemMap.end(); itemIter++) { + if (itemIter.value()->dbusPath == lanPath) { + qDebug()<<"[NetConnect]lan remove " << lanPath << " find in " << itemIter.value()->titileLabel->text(); + QString key = itemIter.key(); + iter.value()->lanItemLayout->removeWidget(itemIter.value()); + delete itemIter.value(); + iter.value()->itemMap.remove(key); + break; + } + } + } +} + +//增加一项 +void NetConnect::addOneLanFrame(ItemFrame *frame, QString deviceName, QStringList infoList) +{ + if (nullptr == frame) { + return; + } + + if (frame->itemMap.contains(infoList.at(1))) { + qDebug() << "[NetConnect]Already exist a lan " << infoList.at(1) << " in " << deviceName; + return; + } + + qDebug() << "[NetConnect]addOneLanFrame" << deviceName << infoList.at(0); + QString connName = infoList.at(0); + QString connUuid = infoList.at(1); + QString connDbusPath = infoList.at(2); + LanItem * lanItem = new LanItem(pluginWidget); + + QString iconPath; + iconPath = KLanSymbolic; + lanItem->statusLabel->setText(""); + + QIcon searchIcon = QIcon::fromTheme(iconPath); +// if (iconPath != KLanSymbolic && iconPath != NoNetSymbolic) { +// lanItem->iconLabel->setProperty("useIconHighlightEffect", 0x10); +// } + lanItem->iconLabel->setPixmap(searchIcon.pixmap(searchIcon.actualSize(QSize(24, 24)))); + lanItem->titileLabel->setText(connName); + + lanItem->uuid = connUuid; + lanItem->dbusPath = connDbusPath; + + connect(lanItem->infoLabel, &InfoButton::clicked, this, [=]{ + // open landetail page + if (!m_interface->isValid()) { + return; + } + qDebug() << "[NetConnect]call showPropertyWidget" << deviceName << connUuid << __LINE__; + m_interface->call(QStringLiteral("showPropertyWidget"), deviceName, connUuid); + qDebug() << "[NetConnect]call showPropertyWidget respond" << __LINE__; + }); + + lanItem->isAcitve = false; + + connect(lanItem, &QPushButton::clicked, this, [=] { + if (lanItem->isAcitve || lanItem->loading) { + deActiveConnect(lanItem->uuid, deviceName, WIRED_TYPE); + } else { + activeConnect(lanItem->uuid, deviceName, WIRED_TYPE); + } + }); + + //记录到deviceFrame的itemMap中 + deviceFrameMap[deviceName]->itemMap.insert(connUuid, lanItem); + int index = getInsertPos(connName, deviceName); + qDebug()<<"[NetConnect]addOneLanFrame " << connName << " to " << deviceName << " list at pos:" << index; + frame->lanItemLayout->insertWidget(index, lanItem); +} + +void NetConnect::removeOneLanFrame(ItemFrame *frame, QString deviceName, QString uuid) +{ + if (nullptr == frame) { + return; + } + + if (!frame->itemMap.contains(uuid)) { + qDebug() << "[NetConnect]not exist a lan " << uuid << " in " << deviceName; + return; + } + + qDebug()<<"[NetConnect]removeOneLanFrame " << uuid << " find in " << deviceName; + + frame->lanItemLayout->removeWidget(frame->itemMap[uuid]); + delete frame->itemMap[uuid]; + frame->itemMap.remove(uuid); +} + +//activeconnect status change +void NetConnect::onActiveConnectionChanged(QString deviceName, QString uuid, int status) +{ + if (uuid.isEmpty()) { + qDebug() << "[NetConnect]onActiveConnectionChanged but uuid is empty"; + return; + } + qDebug() << "[NetConnect]onActiveConnectionChanged " << deviceName << uuid << status; + LanItem * item= nullptr; + if (deviceName.isEmpty()) { + if (status != DEACTIVATED) { + return; + } + //断开时 设备为空 说明此有线未指定设备 添加到所有列表中 + QStringList infoList; + QMap::iterator iters; + for (iters = deviceFrameMap.begin(); iters != deviceFrameMap.end(); iters++) { + if (iters.value()->itemMap.contains(uuid)) { + item = iters.value()->itemMap[uuid]; + infoList << item->titileLabel->text() << item->uuid << item->dbusPath; + //为断开则重新插入 + int index = getInsertPos(item->titileLabel->text(), iters.key()); + qDebug() << "[NetConnect]reinsert" << item->titileLabel->text() << "pos" << index << "in" << iters.key() << "because status changes to deactive"; + deviceFrameMap[iters.key()]->lanItemLayout->removeWidget(item); + deviceFrameMap[iters.key()]->lanItemLayout->insertWidget(index,item); + itemActiveConnectionStatusChanged(item, status); + } + } + //添加到所有列表中 + if (!infoList.isEmpty()) { + QMap::iterator iter; + for (iter = deviceFrameMap.begin(); iter != deviceFrameMap.end(); iter++) { + if (!iter.value()->itemMap.contains(uuid)) { + addOneLanFrame(iter.value(), iter.key(), infoList); + } + } + } + } else { + if (deviceFrameMap.contains(deviceName)) { + if (deviceFrameMap[deviceName]->itemMap.contains(uuid)) { + item = deviceFrameMap[deviceName]->itemMap[uuid]; + if (status == ACTIVATED) { + //为已连接则放到第一个 + deviceFrameMap[deviceName]->lanItemLayout->removeWidget(item); + deviceFrameMap[deviceName]->lanItemLayout->insertWidget(0,item); + } else if (status == DEACTIVATED) { + //为断开则重新插入 + int index = getInsertPos(item->titileLabel->text(), deviceName); + qDebug() << "[NetConnect]reinsert" << item->titileLabel->text() << "pos" << index << "in" << deviceName << "because status changes to deactive"; + deviceFrameMap[deviceName]->lanItemLayout->removeWidget(item); + deviceFrameMap[deviceName]->lanItemLayout->insertWidget(index,item); + } + itemActiveConnectionStatusChanged(item, status); + } + } else { + if (status == ACTIVATED || status == DEACTIVATED) { + //虚拟网卡处理 + QMap::iterator iters; + for (iters = deviceFrameMap.begin(); iters != deviceFrameMap.end(); iters++) { + if (iters.value()->itemMap.contains(uuid)) { + removeOneLanFrame(iters.value(), iters.key(), uuid); + } + } + } + } + } +} + +void NetConnect::itemActiveConnectionStatusChanged(LanItem *item, int status) +{ +// QString iconPath = NoNetSymbolic; + if (status == ACTIVATING) { + item->startLoading(); + } else if (status == ACTIVATED) { + item->stopLoading(); +// iconPath = KLanSymbolic; + item->statusLabel->clear(); + item->statusLabel->setMinimumSize(36,36); + item->statusLabel->setMaximumSize(16777215,16777215); + item->statusLabel->setText(tr("connected")); + item->isAcitve = true; + } else if (status == DEACTIVATING) { + item->startLoading(); + } else { + item->stopLoading(); + item->statusLabel->setMinimumSize(36,36); + item->statusLabel->setMaximumSize(16777215,16777215); + item->statusLabel->clear(); + item->isAcitve = false; + } + +// QIcon searchIcon = QIcon::fromTheme(iconPath); +// item->iconLabel->setPixmap(searchIcon.pixmap(searchIcon.actualSize(QSize(24, 24)))); +} + +int NetConnect::getInsertPos(QString connName, QString deviceName) +{ + qDebug() << "[NetConnect]getInsertPos" << connName << deviceName; + int index = 0; + if(!m_interface->isValid()) { + index = 0; + } else { + qDebug() << "[NetConnect]call getWiredList" << __LINE__; + QDBusMessage result = m_interface->call(QStringLiteral("getWiredList")); + qDebug() << "[NetConnect]call getWiredList respond" << __LINE__; + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "getWiredList error:" << result.errorMessage(); + return 0; + } + auto dbusArg = result.arguments().at(0).value(); + QMap> variantList; + dbusArg >> variantList; + if (!variantList.contains(deviceName)) { + qDebug() << "[NetConnect] getInsertPos but " << deviceName << "not exist"; + return 0; + } + for (int i = 0; i < variantList[deviceName].size(); ++i ) { + if (variantList[deviceName].at(i).at(0) == connName) { + qDebug() << "pos in kylin-nm is " << i; + index = i; + break; + } + } + if (variantList[deviceName].at(0).size() == 1) { + index--; + } + } + return index; +} diff --git a/plugins/netconnect/netconnect.h b/plugins/netconnect/netconnect.h new file mode 100644 index 00000000..2126c399 --- /dev/null +++ b/plugins/netconnect/netconnect.h @@ -0,0 +1,156 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef NETCONNECT_H +#define NETCONNECT_H + + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "interface.h" +#include "addbtn.h" +#include "fixlabel.h" +#include "switchbutton.h" +#include "hoverbtn.h" +#include "lanitem.h" +#include "deviceframe.h" +#include "itemframe.h" + +enum { + DISCONNECTED, + NOINTERNET, + CONNECTED +}; + +namespace Ui { +class NetConnect; +} + +class NetConnect : public QObject, CommonInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.ukcc.CommonInterface") + Q_INTERFACES(CommonInterface) + +public: + NetConnect(); + ~NetConnect(); + + QString plugini18nName() Q_DECL_OVERRIDE; + int pluginTypes() Q_DECL_OVERRIDE; + QWidget * pluginUi() Q_DECL_OVERRIDE; + const QString name() const Q_DECL_OVERRIDE; + bool isShowOnHomePage() const Q_DECL_OVERRIDE; + QIcon icon() const Q_DECL_OVERRIDE; + bool isEnable() const Q_DECL_OVERRIDE; + QString translationPath() const Q_DECL_OVERRIDE; +private: + void initSearchText(); + void initComponent(); + void runExternalApp(); + + void showDesktopNotify(const QString &message); + + + //开关相关 + void setSwitchStatus(); + void hideLayout(QVBoxLayout * layout); + void showLayout(QVBoxLayout * layout); + + int getInsertPos(QString connName, QString deviceName); + + + void deleteOneLan(QString ssid); + void activeConnect(QString ssid, QString deviceName, int type); + void deActiveConnect(QString ssid, QString deviceName, int type); + + //获取设备列表 + void getDeviceStatusMap(QMap &map); + void initNet(); + void initNetListFromDevice(QString deviceName); + //处理列表增加 + void addLanItem(ItemFrame *frame, QString devName, QStringList infoList, bool isActived); + //增加设备 + void addDeviceFrame(QString devName); + //减少设备 + void removeDeviceFrame(QString devName); + //增加一项 + void addOneLanFrame(ItemFrame *frame, QString devName, QStringList infoList); + //减少一项 + void removeOneLanFrame(ItemFrame *frame, QString deviceName, QString uuid); + + //单个lan连接状态变化 + void itemActiveConnectionStatusChanged(LanItem *item, int status); + +protected: + bool eventFilter(QObject *w,QEvent *e); + +private: + Ui::NetConnect *ui; + + QString pluginName; + int pluginType; + QWidget *pluginWidget; + + QDBusInterface *m_interface = nullptr; + SwitchButton *wiredSwitch; + + bool mFirstLoad; + QGSettings *m_switchGsettings; + + QMap deviceStatusMap; + QMap deviceFrameMap; + +private slots: + void updateLanInfo(QString deviceName, QStringList lanInfo); + + void onLanAdd(QString deviceName, QStringList lanInfo); + void onLanRemove(QString dbusPath); + + void onActiveConnectionChanged(QString deviceName, QString uuid, int status); + + void onDeviceStatusChanged(); + void onDeviceNameChanged(QString, QString, int); +}; + +Q_DECLARE_METATYPE(QList); + +#endif // NETCONNECT_H diff --git a/plugins/netconnect/netconnect.pro b/plugins/netconnect/netconnect.pro new file mode 100644 index 00000000..c6245ada --- /dev/null +++ b/plugins/netconnect/netconnect.pro @@ -0,0 +1,53 @@ +QT += widgets network dbus gui core +TEMPLATE = lib +CONFIG += plugin + +include(../component/drownlabel.pri) + +TARGET = $$qtLibraryTarget(netconnect) +DESTDIR = ../.. +target.path = $$[QT_INSTALL_LIBS]/ukui-control-center +trans.files = translations/* +trans.path = /usr/share/kylin-nm/netconnect/ + +INCLUDEPATH += \ + $$PROJECT_COMPONENTSOURCE \ + $$PROJECT_ROOTDIR \ + /usr/include/ukcc/interface \ + /usr/include/ukcc/widgets + +LIBS += -L$$[QT_INSTALL_LIBS] -lgsettings-qt -lukcc + +CONFIG += c++11 \ + link_pkgconfig \ + +PKGCONFIG += gsettings-qt \ + +#DEFINES += QT_DEPRECATED_WARNINGS + +SOURCES += \ + addnetbtn.cpp \ + deviceframe.cpp \ +# drownlabel.cpp \ + itemframe.cpp \ + lanitem.cpp \ + netconnect.cpp + +HEADERS += \ + addnetbtn.h \ + deviceframe.h \ +# drownlabel.h \ + itemframe.h \ + lanitem.h \ + netconnect.h + +FORMS += \ + netconnect.ui + +INSTALLS += target \ + trans + +TRANSLATIONS += \ + translations/zh_CN.ts \ + translations/tr.ts \ + translations/bo.ts diff --git a/plugins/netconnect/netconnect.ui b/plugins/netconnect/netconnect.ui new file mode 100644 index 00000000..4c4e2267 --- /dev/null +++ b/plugins/netconnect/netconnect.ui @@ -0,0 +1,244 @@ + + + NetConnect + + + + 0 + 0 + 885 + 700 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + NetConnect + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Wired Network + + + true + + + + + + + 1 + + + 0 + + + + + + 0 + 60 + + + + + 16777215 + 50 + + + + QFrame::Box + + + + 18 + + + 0 + + + 9 + + + 0 + + + + + 0 + + + 8 + + + + + + 118 + 0 + + + + open + + + + + + + Qt::Horizontal + + + + 523 + 20 + + + + + + + + + + + + + + + 1 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + + + + + + + + + + + + 120 + 36 + + + + + 16777215 + 36 + + + + Advanced settings + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + TitleLabel + QLabel +
titlelabel.h
+
+
+ + +
diff --git a/plugins/netconnect/translations/bo.qm b/plugins/netconnect/translations/bo.qm new file mode 100644 index 00000000..be651eed --- /dev/null +++ b/plugins/netconnect/translations/bo.qm @@ -0,0 +1 @@ + + + + + AddNetBtn + + + Add WiredNetork + + + + + NetConnect + + + + Wired Network + + + + + + open + + /netconnect/open + + + + + Advanced settings + + /netconnect/Advanced settings" + + + + ukui control center + + + + + ukui control center desktop message + + + + + WiredConnect + + + + + No ethernet device avaliable + + + + + + connected + + + + + card + + + + diff --git a/plugins/netconnect/translations/tr.qm b/plugins/netconnect/translations/tr.qm new file mode 100644 index 00000000..be651eed --- /dev/null +++ b/plugins/netconnect/translations/tr.qm @@ -0,0 +1 @@ + + + + + AddNetBtn + + + Add WiredNetork + + + + + NetConnect + + + + Wired Network + + + + + + open + + /netconnect/open + + + + + Advanced settings + + /netconnect/Advanced settings" + + + + ukui control center + + + + + ukui control center desktop message + + + + + WiredConnect + + + + + No ethernet device avaliable + + + + + + connected + + + + + card + + + + diff --git a/plugins/netconnect/translations/zh_CN.qm b/plugins/netconnect/translations/zh_CN.qm new file mode 100644 index 0000000000000000000000000000000000000000..97f6e862995e743ad2b1ba56123b764c201bb308 GIT binary patch literal 661 zcmcE7ks@*G{hX<16=n7(EZlq7iGhJ3fPrmB3j+hAG6Q?sJ|JDeWLYc^d6o!l9zz;^LdXRPyzBk@C*H%4Wt<|85o$fnSl(R z+}}Y9(mCt)*9X?!;Q&do0WrU0N{T{wW>IR2UusEyQ8qhJj1wpZ*wdUS4W)30R*%x>m-4T<;(Qkfq)G zBH7{kW&H9LQcE&Yi}F%S6jD;lGLusk63Y^EG82<>Qn4Auf#Ggmu$vWtR+d9ujcz@r z9v-kBs1vbS!%`m@oCtR%OLAgS3N~eIv7h?yB`<|5S1MeZU7D!?G`plIKSv=sHLoPK2%Am;n9dBJ=}8l$%AdkbP{v{c VFuaSiOY#d8a#M?o6Vow*8vyRAteOA- literal 0 HcmV?d00001 diff --git a/plugins/netconnect/translations/zh_CN.ts b/plugins/netconnect/translations/zh_CN.ts new file mode 100644 index 00000000..0424b7b0 --- /dev/null +++ b/plugins/netconnect/translations/zh_CN.ts @@ -0,0 +1,66 @@ + + + + + AddNetBtn + + + Add WiredNetork + 添加有线网络 + + + + NetConnect + + + + Wired Network + 有线网络 + + + + + open + 开启 + /netconnect/open + + + + + Advanced settings + 高级设置 + /netconnect/Advanced settings" + + + + ukui control center + 控制面板 + + + + ukui control center desktop message + 控制面板桌面通知 + + + + WiredConnect + 有线网络 + + + + No ethernet device avaliable + 未检测到有线设备 + + + + + connected + 已连接 + + + + card + 网卡 + + + diff --git a/plugins/plugin.pro b/plugins/plugin.pro new file mode 100644 index 00000000..f370e3be --- /dev/null +++ b/plugins/plugin.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs +SUBDIRS = \ + netconnect \ + wlanconnect \ + mobilehotspot diff --git a/plugins/wlanconnect/deviceframe.cpp b/plugins/wlanconnect/deviceframe.cpp new file mode 100644 index 00000000..a616d65f --- /dev/null +++ b/plugins/wlanconnect/deviceframe.cpp @@ -0,0 +1,48 @@ +#include "deviceframe.h" +#include + +#define LAYOUT_MARGINS 18,0,8,0 +#define FRAME_HEIGHT 58 +#define LAYOUT_SPACING 16 +#define RADIUS 6.0 + +DeviceFrame::DeviceFrame(QString devName, QWidget *parent) : QFrame(parent) +{ + this->setFrameShape(QFrame::Box); + this->setFixedHeight(FRAME_HEIGHT); + QHBoxLayout *deviceLayout = new QHBoxLayout(this); + deviceLayout->setContentsMargins(LAYOUT_MARGINS); + setLayout(deviceLayout); + deviceLayout->setSpacing(LAYOUT_SPACING); + + deviceLabel = new QLabel(this); + dropDownLabel = new DrownLabel(devName, this); + + deviceLayout->addWidget(deviceLabel); + deviceLayout->addStretch(); + deviceLayout->addWidget(dropDownLabel); +} + +DeviceFrame::~DeviceFrame() +{ + +} + +void DeviceFrame::paintEvent(QPaintEvent *event) +{ + QPalette pal = this->palette(); + + QPainter painter(this); + painter.setRenderHint(QPainter:: Antialiasing, true); //设置渲染,启动反锯齿 + painter.setPen(Qt::NoPen); + painter.setBrush(pal.color(QPalette::Base)); + + QRect rect = this->rect(); + QPainterPath path; + path.addRoundedRect (rect, RADIUS, RADIUS); + QRect temp_rect(rect.left(), rect.top() + rect.height()/2, rect.width(), rect.height()/2); + path.addRect(temp_rect); + painter.drawPath(path); + QFrame::paintEvent(event); +} + diff --git a/plugins/wlanconnect/deviceframe.h b/plugins/wlanconnect/deviceframe.h new file mode 100644 index 00000000..f70eda47 --- /dev/null +++ b/plugins/wlanconnect/deviceframe.h @@ -0,0 +1,30 @@ +#ifndef DEVICEFRAME_H +#define DEVICEFRAME_H +#include +#include +#include +#include +#include +#include +#include "../component/DrownLabel/drownlabel.h" + +class DeviceFrame : public QFrame +{ + +public: + DeviceFrame(QString devName, QWidget *parent = nullptr); + ~DeviceFrame(); +public: + //仅设备名称+下拉label + QLabel * deviceLabel = nullptr; + DrownLabel *dropDownLabel = nullptr; + +protected: + void paintEvent(QPaintEvent *event); + +private: + int frameSize; + +}; + +#endif // DEVICEFRAME_H diff --git a/plugins/wlanconnect/itemframe.cpp b/plugins/wlanconnect/itemframe.cpp new file mode 100644 index 00000000..572de528 --- /dev/null +++ b/plugins/wlanconnect/itemframe.cpp @@ -0,0 +1,61 @@ +#include "itemframe.h" +#include + +#define LAYOUT_MARGINS 0,0,0,0 +#define MAIN_LAYOUT_MARGINS 0,0,0,0 +ItemFrame::ItemFrame(QString devName, QWidget *parent) +{ + deviceLanLayout = new QVBoxLayout(this); + deviceLanLayout->setContentsMargins(MAIN_LAYOUT_MARGINS); + lanItemFrame = new QFrame(this); + lanItemFrame->setFrameShape(QFrame::Shape::NoFrame); + lanItemFrame->setContentsMargins(LAYOUT_MARGINS); + + lanItemLayout = new QVBoxLayout(this); + lanItemLayout->setContentsMargins(LAYOUT_MARGINS); + lanItemLayout->setSpacing(1); + + deviceLanLayout->setSpacing(1); + setLayout(deviceLanLayout); + lanItemFrame->setLayout(lanItemLayout); + + deviceFrame = new DeviceFrame(devName, this); + deviceLanLayout->addWidget(deviceFrame); + deviceLanLayout->addWidget(lanItemFrame); + + //下拉按钮 + connect(deviceFrame->dropDownLabel, &DrownLabel::labelClicked, this, &ItemFrame::onDrownLabelClicked); +} + +ItemFrame::~ItemFrame() +{ + +} + +void ItemFrame::onDrownLabelClicked() +{ + if (!deviceFrame->dropDownLabel->isChecked) { + lanItemFrame->show(); + deviceFrame->dropDownLabel->setDropDownStatus(true); + } else { + lanItemFrame->hide(); + deviceFrame->dropDownLabel->setDropDownStatus(false); + } +} + +void ItemFrame::filletStyleChange() +{ + if (lanItemLayout->isEmpty()) { + return; + } + + for (int i = 0; i < lanItemLayout->count(); ++i) { + QLayoutItem *it = lanItemLayout->itemAt(i); + WlanItem *itemFrame = (WlanItem*)(it->widget()); + if (i != lanItemLayout->count()-1) { + itemFrame->setHalfFillet(false); + } else { + itemFrame->setHalfFillet(true); + } + } +} diff --git a/plugins/wlanconnect/itemframe.h b/plugins/wlanconnect/itemframe.h new file mode 100644 index 00000000..02e78876 --- /dev/null +++ b/plugins/wlanconnect/itemframe.h @@ -0,0 +1,33 @@ +#ifndef ITEMFRAME_H +#define ITEMFRAME_H +#include +#include +#include "deviceframe.h" +#include "wlanitem.h" + +class ItemFrame : public QFrame +{ + Q_OBJECT +public: + ItemFrame(QString devName, QWidget *parent = nullptr); + ~ItemFrame(); + //单设备整体layout + QVBoxLayout * deviceLanLayout = nullptr; + //单设备名称+下拉按钮Frame + DeviceFrame * deviceFrame = nullptr; + //单设备列表Frame + QFrame * lanItemFrame = nullptr; + //单设备列表layout + QVBoxLayout * lanItemLayout = nullptr; + //单设备item列表 + QMap itemMap; + //已激活uuid + QString uuid = ""; + + void filletStyleChange(); + +private slots: + void onDrownLabelClicked(); +}; + +#endif // ITEMFRAME_H diff --git a/plugins/wlanconnect/translations/bo.qm b/plugins/wlanconnect/translations/bo.qm new file mode 100644 index 00000000..be651eed --- /dev/null +++ b/plugins/wlanconnect/translations/bo.qm @@ -0,0 +1 @@ + + + + + WlanConnect + + + + WlanConnect + + + + + + WLAN + + + + + + open + + /wlanconnect/open + + + + + Advanced settings + + /wlanconnect/Advanced settings" + + + + ukui control center + + + + + ukui control center desktop message + + + + + No wireless network card detected + + + + + + + connected + + + + + card + + + + diff --git a/plugins/wlanconnect/translations/tr.qm b/plugins/wlanconnect/translations/tr.qm new file mode 100644 index 00000000..be651eed --- /dev/null +++ b/plugins/wlanconnect/translations/tr.qm @@ -0,0 +1 @@ + + + + + WlanConnect + + + + WlanConnect + + + + + + WLAN + + + + + + open + + /wlanconnect/open + + + + + Advanced settings + + /wlanconnect/Advanced settings" + + + + ukui control center + + + + + ukui control center desktop message + + + + + No wireless network card detected + + + + + + + connected + + + + + card + + + + diff --git a/plugins/wlanconnect/translations/zh_CN.qm b/plugins/wlanconnect/translations/zh_CN.qm new file mode 100644 index 0000000000000000000000000000000000000000..7d9a0216c79061f3f8cafdbb1864ad1b626d4bba GIT binary patch literal 604 zcmcE7ks@*G{hX<16=n7(EZlq7iGhK^gMsxx9FXo}V4Kkbq~9{Ir|n~4V02-!EEWaQ z%UQh-TmjOetPNctc}tEo`_Dj{flE3W#NWa5M)DF+J|`1M{9*=*aLl?bu^0gi U=;G{>`~rnsU=$>#r(zfh0N8M!$p8QV literal 0 HcmV?d00001 diff --git a/plugins/wlanconnect/translations/zh_CN.ts b/plugins/wlanconnect/translations/zh_CN.ts new file mode 100644 index 00000000..a96e660b --- /dev/null +++ b/plugins/wlanconnect/translations/zh_CN.ts @@ -0,0 +1,60 @@ + + + + + WlanConnect + + + + WlanConnect + 无线局域网 + + + + + WLAN + 无线局域网 + + + + + open + 开启 + /wlanconnect/open + + + + + Advanced settings + 高级设置 + /wlanconnect/Advanced settings" + + + + ukui control center + 控制面板 + + + + ukui control center desktop message + 控制面板桌面通知 + + + + No wireless network card detected + 未检测到无线网卡 + + + + + + connected + 已连接 + + + + card + 网卡 + + + diff --git a/plugins/wlanconnect/wlanconnect.cpp b/plugins/wlanconnect/wlanconnect.cpp new file mode 100644 index 00000000..7c415179 --- /dev/null +++ b/plugins/wlanconnect/wlanconnect.cpp @@ -0,0 +1,1007 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "wlanconnect.h" +#include "ui_wlanconnect.h" + +#include +#include +#include +#include +#include +#include +#include + +#define WIRELESS_TYPE 1 + +#define SCANTIMER 20 * 1000 +#define UPDATETIMER 5 * 1000 + +#define SPACING 8 + +const QString WIRELESS_SWITCH = "wirelessswitch"; +const QByteArray GSETTINGS_SCHEMA = "org.ukui.kylin-nm.switch"; + +const QString KWifiSymbolic = "network-wireless-signal-excellent"; +const QString KWifiLockSymbolic = "network-wireless-secure-signal-excellent"; +const QString KWifiGood = "network-wireless-signal-good"; +const QString KWifiLockGood = "network-wireless-secure-signal-good"; +const QString KWifiOK = "network-wireless-signal-ok"; +const QString KWifiLockOK = "network-wireless-secure-signal-ok"; +const QString KWifiLow = "network-wireless-signal-low"; +const QString KWifiLockLow = "network-wireless-secure-signal-low"; +const QString KWifiNone = "network-wireless-signal-none"; +const QString KWifiLockNone = "network-wireless-secure-signal-none"; + +const QString KLanSymbolic = ":/img/plugins/netconnect/eth.svg"; +const QString NoNetSymbolic = ":/img/plugins/netconnect/nonet.svg"; + +const QString KApSymbolic = "network-wireless-hotspot-symbolic"; + +const QString IsApConnection = "1"; + + +#define ACTIVATING 1 +#define ACTIVATED 2 +#define DEACTIVATING 3 +#define DEACTIVATED 4 + +#define NO_MARGINS 0,0,0,0 +#define MAIN_LAYOUT_MARGINS 0,0,0,8 + +bool intThan(int sign1, int sign2) +{ + return sign1 < sign2; +} + +void WlanConnect::showDesktopNotify(const QString &message) +{ + QDBusInterface iface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::sessionBus()); + QList args; + args<<(tr("ukui control center")) + <<((unsigned int) 0) + <load("/usr/share/kylin-nm/wlanconnect/" + QLocale::system().name()); + QApplication::installTranslator(translator); + + pluginName = tr("WlanConnect"); + pluginType = NETWORK; +} + +WlanConnect::~WlanConnect() +{ + if (!m_firstLoad) { + delete ui; + ui = nullptr; + } + delete m_interface; + delete m_switchGsettings; +} + +QString WlanConnect::plugini18nName() { + return pluginName; +} + +int WlanConnect::pluginTypes() { + return pluginType; +} + +QWidget *WlanConnect::pluginUi() { + if (m_firstLoad) { + m_firstLoad = false; + + ui = new Ui::WlanConnect; + pluginWidget = new QWidget; + pluginWidget->setAttribute(Qt::WA_DeleteOnClose); + ui->setupUi(pluginWidget); + qDBusRegisterMetaType>(); + m_interface = new QDBusInterface("com.kylin.network", "/com/kylin/network", + "com.kylin.network", + QDBusConnection::sessionBus()); + if(!m_interface->isValid()) { + qWarning() << qPrintable(QDBusConnection::sessionBus().lastError().message()); + } + initComponent(); + } + return pluginWidget; +} + +const QString WlanConnect::name() const { + + return QStringLiteral("wlanconnect"); +} + +bool WlanConnect::isEnable() const +{ + return true; +} + + +bool WlanConnect::isShowOnHomePage() const +{ + return true; +} + +QIcon WlanConnect::icon() const +{ + return QIcon(); +} + +QString WlanConnect::translationPath() const +{ + return "/usr/share/kylin-nm/wlanconnect/%1.ts"; +} + +void WlanConnect::initSearchText() { + //~ contents_path /wlanconnect/Advanced settings" + ui->detailBtn->setText(tr("Advanced settings")); + ui->titleLabel->setText(tr("WLAN")); + //~ contents_path /wlanconnect/open + ui->openLabel->setText(tr("open")); +} + +bool WlanConnect::eventFilter(QObject *w, QEvent *e) { + if (e->type() == QEvent::Enter) { + if (w->findChild()) + w->findChild()->setStyleSheet("QWidget{background: palette(button);border-radius:4px;}"); + } else if (e->type() == QEvent::Leave) { + if (w->findChild()) + w->findChild()->setStyleSheet("QWidget{background: palette(base);border-radius:4px;}"); + } + return QObject::eventFilter(w,e); +} + +void WlanConnect::initComponent() { + m_wifiSwitch = new SwitchButton(pluginWidget); + ui->openWIifLayout->addWidget(m_wifiSwitch); + ui->detailLayOut_3->setContentsMargins(MAIN_LAYOUT_MARGINS); + ui->verticalLayout_3->setContentsMargins(NO_MARGINS); + ui->availableLayout->setSpacing(SPACING); + + connect(m_wifiSwitch, &SwitchButton::disabledClick, this, [=]() { + showDesktopNotify(tr("No wireless network card detected")); + }); + + //开关 + if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA)) { + m_switchGsettings = new QGSettings(GSETTINGS_SCHEMA); + connect(m_wifiSwitch, &SwitchButton::checkedChanged, this, [=] (bool checked) { + if (!m_interface->isValid()) { + return; + } + if (m_wifiSwitch->getDisabledFlag()) { + return; + } + qDebug() << "[WlanConnect]call setWirelessSwitchEnable " << checked << __LINE__; + m_interface->call(QStringLiteral("setWirelessSwitchEnable"),checked); + qDebug() << "[WlanConnect]call setWirelessSwitchEnable respond" << __LINE__; + }); + setSwitchStatus(); + connect(m_switchGsettings, &QGSettings::changed, this, [=] (const QString &key) { + if (key == WIRELESS_SWITCH) { + setSwitchStatus(); + } + }); + } else { + m_wifiSwitch->blockSignals(true); + m_wifiSwitch->setChecked(true); + m_wifiSwitch->blockSignals(false); + qDebug()<<"[netconnect] org.ukui.kylin-nm.switch is not installed!"; + } + + //获取设备列表 + getDeviceList(deviceList); + if (deviceList.isEmpty()) { + qDebug() << "[WlanConnect]no device exist when init, set switch disable"; + m_wifiSwitch->setDisabledFlag(true); + m_wifiSwitch->setChecked(false); + } + initNet(); + + if (deviceList.isEmpty() || !m_interface->isValid()) { + hideLayout(ui->availableLayout); + } + + // 有线网络断开或连接时刷新可用网络列表 + connect(m_interface, SIGNAL(wlanactiveConnectionStateChanged(QString, QString, QString, int)), this, SLOT(onActiveConnectionChanged(QString, QString, QString, int)), Qt::QueuedConnection); + //无线网络新增时添加网络 + connect(m_interface, SIGNAL(wlanAdd(QString, QStringList)), this, SLOT(onNetworkAdd(QString, QStringList)), Qt::QueuedConnection); + //删除无线网络 + connect(m_interface, SIGNAL(wlanRemove(QString, QString)), this, SLOT(onNetworkRemove(QString, QString)), Qt::QueuedConnection); + //网卡插拔处理 + connect(m_interface, SIGNAL(deviceStatusChanged()), this, SLOT(onDeviceStatusChanged()), Qt::QueuedConnection); + //信号更新处理 改为每过固定时间 主动获取 +// connect(m_interface, SIGNAL(signalStrengthChange(QString, QString, int)), this, SLOT(updateStrengthList(QString, QString, int))); + //网卡name处理 + connect(m_interface, SIGNAL(deviceNameChanged(QString, QString, int)), this, SLOT(onDeviceNameChanged(QString, QString, int)), Qt::QueuedConnection); + + connect(m_interface, SIGNAL(timeToUpdate()), this, SLOT(updateList()), Qt::QueuedConnection); + //高级设置 + connect(ui->detailBtn, &QPushButton::clicked, this, [=](bool checked) { + Q_UNUSED(checked) + runExternalApp(); + }); + + //定时20s扫描 + m_scanTimer = new QTimer(this); + m_scanTimer->start(SCANTIMER); + connect(m_scanTimer, &QTimer::timeout, this, &WlanConnect::reScan, Qt::QueuedConnection); + reScan(); + +// m_updateTimer = new QTimer(this); +// m_updateTimer->start(UPDATETIMER); + +} + +void WlanConnect::reScan() +{ + qDebug() << "time to rescan wifi"; + if (m_interface->isValid()) { + qDebug() << "[WlanConnect]call reScan" << __LINE__; + m_interface->call("reScan"); + qDebug() << "[WlanConnect]call reScan respond" << __LINE__; + } +} + +//更新列表顺序 +void WlanConnect::updateList() +{ + if (!m_wifiSwitch->isChecked()) { + return; + } + qDebug() << "update list"; + if(m_interface->isValid()) { + qDebug() << "[WlanConnect]call getWirelessList" << __LINE__; + QDBusMessage result = m_interface->call(QStringLiteral("getWirelessList")); + qDebug() << "[WlanConnect]call getWirelessList respond" << __LINE__; + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "getWirelessList error:" << result.errorMessage(); + return; + } + auto dbusArg = result.arguments().at(0).value(); + QMap> variantList; + dbusArg >> variantList; + + if (variantList.size() == 0) { + qDebug() << "[WlanConnect]updateList " << " list empty"; + return; + } + + QMap>::iterator iter; + + for (iter = variantList.begin(); iter != variantList.end(); iter++) { + if (deviceFrameMap.contains(iter.key())) { + QVector wifiList = iter.value(); + resortWifiList(deviceFrameMap[iter.key()], wifiList); + deviceFrameMap[iter.key()]->filletStyleChange(); + } + } + } +} + +void WlanConnect::resortWifiList(ItemFrame *frame, QVector list) +{ + if(nullptr == frame || frame->lanItemLayout->count() <= 0 || list.isEmpty()) { + return; + } + qDebug() << "begin resort" << frame->deviceFrame->deviceLabel->text(); + + int frameIndex = 0; + int listIndex = 1; + if (list.at(0).size() > 1) { + if (frame->itemMap.contains(list.at(0).at(0))) { + frame->lanItemLayout->removeWidget(frame->itemMap[list.at(0).at(0)]); + frame->lanItemLayout->insertWidget(0, frame->itemMap[list.at(0).at(0)]); + qDebug() << "active resort insert position 0" << list.at(0).at(0); + frame->itemMap[list.at(0).at(0)]->isAcitve = true; + frame->itemMap[list.at(0).at(0)]->uuid = list.at(0).at(3); + frame->uuid = list.at(0).at(3); + frame->itemMap[list.at(0).at(0)]->statusLabel->setText(tr("connected")); + updateIcon(frame->itemMap[list.at(0).at(0)], list.at(0).at(1), list.at(0).at(2), list.at(0).at(4)); + frameIndex ++; + } + } else { + qDebug() << " no active connection when resort"; + if (!frame->uuid.isEmpty()) { + QMap::iterator itemIter; + for (itemIter = frame->itemMap.begin(); itemIter != frame->itemMap.end(); itemIter++) { + if (itemIter.value()->uuid == frame->uuid ) { + WlanItem * item= nullptr; + item = itemIter.value(); + qDebug() << "a active connect missing when resort"; + itemIter.value()->uuid.clear(); + itemActiveConnectionStatusChanged(item, DEACTIVATED); + break; + } + } + } + frame->uuid.clear(); + } + + for ( ; listIndex < list.size(); listIndex++) { + if (frameIndex > frame->lanItemLayout->count() - 1) { + return; + } + if (frame->itemMap.contains(list.at(listIndex).at(0))) { + frame->lanItemLayout->removeWidget(frame->itemMap[list.at(listIndex).at(0)]); + frame->lanItemLayout->insertWidget(frameIndex, frame->itemMap[list.at(listIndex).at(0)]); + qDebug() << "custom resort " << list.at(listIndex).at(0) <<" insert position" << frameIndex; + if (frame->itemMap[list.at(listIndex).at(0)]->isAcitve) { + frame->itemMap[list.at(listIndex).at(0)]->isAcitve = false; + frame->itemMap[list.at(listIndex).at(0)]->uuid.clear(); + frame->itemMap[list.at(listIndex).at(0)]->statusLabel->setText(""); + } + updateIcon(frame->itemMap[list.at(listIndex).at(0)], list.at(listIndex).at(1), list.at(listIndex).at(2), list.at(listIndex).at(3)); + frameIndex++; + } else { + qDebug() << "not find " << list.at(listIndex).at(0) << " in current list, ignore"; + } + } + qDebug() << "resort finish"; +} + +void WlanConnect::updateIcon(WlanItem *item, QString signalStrength, QString security, QString isApConnection) +{ + qDebug() << "updateIcon" << item->titileLabel->text(); + + int sign = setSignal(signalStrength); + bool isLock = true; + if (security.isEmpty()) { + isLock = false; + } else { + isLock = true; + } + + QString iconamePath; + if (isApConnection == IsApConnection) { + iconamePath = KApSymbolic; + } else { + iconamePath = wifiIcon(isLock, sign); + } + QIcon searchIcon = QIcon::fromTheme(iconamePath); + if (iconamePath != KLanSymbolic && iconamePath != NoNetSymbolic) { + item->iconLabel->setProperty("useIconHighlightEffect", 0x10); + } + item->iconLabel->setPixmap(searchIcon.pixmap(searchIcon.actualSize(QSize(24, 24)))); + qDebug() << "updateIcon" << item->titileLabel->text() << " finish"; +} + +//wifi strength update +//void WlanConnect::updateStrengthList(QString deviceName, QString ssid, int strength) +//{ +// return; +// if(!m_wifiSwitch->isChecked()) { +// return; +// } +// qDebug()<<"[WlanConnect]Update wireless network signal strength:" << deviceName <::iterator iters; +// for (iters = deviceWlanlistInfo.deviceLayoutMap.begin(); iters != deviceWlanlistInfo.deviceLayoutMap.end(); iters++) { +// if (iters.key() == deviceName) { +// qDebug() << "[WlanConnect] updateStrengthList find " << deviceName << " in deviceWlanlistInfo.deviceLayoutMap"; +// if (iters.value()->lanItemLayout->layout() != NULL) { +// WlanItem* item = nullptr; +// QMap::iterator iter; +// for (iter = deviceWlanlistInfo.wlanItemMap.begin(); iter != deviceWlanlistInfo.wlanItemMap.end(); iter++) { +// if (iter.key() == ssid) { +// item = iter.value(); +// break; +// } +// } +// isLock = item->isLock; +// //remove the item from layout +// iters.value()->lanItemLayout->removeWidget(item); +// //get position +// int index = sortWlanNet(deviceName,ssid, QString::number(strength)); +// //add the item to new position +// qDebug()<<"更新后位置:"<iconLabel->setProperty("useIconHighlightEffect", 0x10); +// } +// item->iconLabel->setPixmap(searchIcon.pixmap(searchIcon.actualSize(QSize(24, 24)))); + +// iters.value()->lanItemLayout->insertWidget(index, item); +// } +// } +// } +//} + +//device add or remove================================= +void WlanConnect::onDeviceStatusChanged() +{ + qDebug()<<"[WlanConnect]onDeviceStatusChanged"; + QEventLoop eventloop; + QTimer::singleShot(300, &eventloop, SLOT(quit())); + eventloop.exec(); + QStringList list; + getDeviceList(list); + + QStringList removeList,addList; + + //remove的设备 + for (int i = 0; i< deviceList.size(); ++i) { + if (!list.contains(deviceList.at(i))) { + qDebug() << "[WlanConnect]onDeviceStatusChanged " << deviceList.at(i) << "was removed"; + removeList << deviceList.at(i); + } + } + + //add的设备 + for (int i = 0; i< list.size(); ++i) { + if (!deviceList.contains(list.at(i))) { + qDebug() << "[WlanConnect]onDeviceStatusChanged " << list.at(i) << "was add"; + addList << list.at(i); + } + } + + for (int i = 0; i < removeList.size(); ++i) { + removeDeviceFrame(removeList.at(i)); + } + + for (int i = 0; i < addList.size(); ++i) { + addDeviceFrame(addList.at(i)); + } + deviceList = list; + if (deviceList.isEmpty()) { + m_wifiSwitch->setDisabledFlag(true); + m_wifiSwitch->setChecked(false); + } else { + m_wifiSwitch->setDisabledFlag(false); + setSwitchStatus(); + } +} + +void WlanConnect::onDeviceNameChanged(QString oldName, QString newName, int type) +{ + if (WIRELESS_TYPE != type || !deviceFrameMap.contains(oldName) || !deviceList.contains(oldName)) { + qDebug() << "[WlanConnect]onDeviceNameChanged no such device " << oldName; + return; + } + + if (deviceFrameMap.contains(newName) && deviceList.contains(newName)) { + qDebug() << "[WlanConnect]onDeviceNameChanged already has device " << newName; + return; + } + + qDebug() << "[WlanConnect]onDeviceNameChanged " << oldName << "change to" << newName; + + //shan chu chong jian + removeDeviceFrame(oldName); + removeDeviceFrame(newName); + + getDeviceList(deviceList); + if (deviceList.contains(newName)) { + addDeviceFrame(newName); + initNetListFromDevice(newName); + } +} + +//activeconnect status change +void WlanConnect::onActiveConnectionChanged(QString deviceName, QString ssid, QString uuid, int status) +{ + if (!m_wifiSwitch->isChecked()) { + return; + } + if (uuid.isEmpty()) { + return; + } + WlanItem * item= nullptr; + //device ssid 有可能均为空 + if (deviceName.isEmpty() || ssid.isEmpty()) { + if (status == ACTIVATING || status == ACTIVATED) { + return; + } + QMap::iterator iter; + for (iter = deviceFrameMap.begin(); iter != deviceFrameMap.end(); iter++) { + if (uuid == iter.value()->uuid) { + QMap::iterator itemIter; + for (itemIter = iter.value()->itemMap.begin(); itemIter != iter.value()->itemMap.end(); itemIter++) { + if (itemIter.value()->uuid == uuid ) { + item = itemIter.value(); + if (status == DEACTIVATED) { + itemIter.value()->uuid.clear(); + } + break; + } + } + break; + } + } + } else { + if (!deviceFrameMap.contains(deviceName)) { + return; + } + for (int i = 0; i < deviceFrameMap[deviceName]->itemMap.size(); ++i) { + if (deviceFrameMap[deviceName]->itemMap.contains(ssid)) { + item = deviceFrameMap[deviceName]->itemMap[ssid]; + if (status == ACTIVATED || status == ACTIVATING) { + deviceFrameMap[deviceName]->itemMap[ssid]->uuid = uuid; + deviceFrameMap[deviceName]->uuid = uuid; + if (status == ACTIVATED) { + deviceFrameMap[deviceName]->lanItemLayout->removeWidget(item); + deviceFrameMap[deviceName]->lanItemLayout->insertWidget(0,item); + deviceFrameMap[deviceName]->filletStyleChange(); + } + } else if (status == DEACTIVATED) { + deviceFrameMap[deviceName]->itemMap[ssid]->uuid.clear(); + deviceFrameMap[deviceName]->uuid.clear(); + //todo 断开后排序 现在等下次更新列表 自动排序 + } + break; + } + } + } + + if (nullptr != item) { + itemActiveConnectionStatusChanged(item, status); + } +} + +//wifi add=============================================================== +void WlanConnect::onNetworkAdd(QString deviceName, QStringList wlanInfo) +{ + qDebug()<<"[WlanConnect]onNetworkAdd "<< deviceName << " " << wlanInfo; + if(!m_wifiSwitch->isChecked() || deviceName.isEmpty()) { + return; + } + + if (!deviceList.contains(deviceName)) { + qDebug() << "[WlanConnect]onNetworkAdd not contain " << deviceName << "then add"; + deviceList.append(deviceName); + addDeviceFrame(deviceName); + onNetworkAdd(deviceName, wlanInfo); + return; + } + + bool isLock = true; + if (wlanInfo.at(2) == "") { + isLock = false; + } else { + isLock = true; + } + + QMap::iterator iter; + for (iter = deviceFrameMap.begin(); iter != deviceFrameMap.end(); iter++) { + if (deviceName == iter.key()) { + addOneWlanFrame(iter.value(), deviceName, wlanInfo.at(0), wlanInfo.at(1), "", isLock, false, WIRELESS_TYPE, wlanInfo.at(3)); + } + } + +} + +//wifi remove ============================================================= +void WlanConnect::onNetworkRemove(QString deviceName, QString wlannName) +{ + //开关已关闭 忽略 +// if (!m_wifiSwitch->isChecked()) { +// qDebug() << "[WlanConnect]recieve network remove,but wireless switch is off"; +// return; +// } + //当前无此设备 忽略 + if (deviceName.isEmpty() || !deviceFrameMap.contains(deviceName)) { + qDebug() << "[WlanConnect]recieve network remove,but no such device:" << deviceName; + return; + } + qDebug()<<"[WlanConnect]Wifi remove device:" << deviceName << ",wlan name:" << wlannName; + QMap::iterator iter; + for (iter = deviceFrameMap.begin(); iter != deviceFrameMap.end(); iter++) { + if (deviceName == iter.key()) { + removeOneWlanFrame(iter.value(), deviceName, wlannName); + } + } +} + +//获取设备列表======================================================= +void WlanConnect::getDeviceList(QStringList &list) +{ + if (!m_interface->isValid()) { + return; + } + qDebug() << "[WlanConnect]call getDeviceListAndEnabled" << __LINE__; + QDBusMessage result = m_interface->call(QStringLiteral("getDeviceListAndEnabled"),1); + qDebug() << "[WlanConnect]call getDeviceListAndEnabled respond" << __LINE__; + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "[WlanConnect]getWirelessDeviceList error:" << result.errorMessage(); + return; + } + auto dbusArg = result.arguments().at(0).value(); + QMap map; + dbusArg >> map; + list = map.keys(); +} + +//设置开关 +void WlanConnect::setSwitchStatus() +{ +// if (deviceList.size() == 0) { +// m_wifiSwitch->blockSignals(true); +// m_wifiSwitch->setChecked(false); +// m_wifiSwitch->blockSignals(false); +// return; +// } + bool status = m_switchGsettings->get(WIRELESS_SWITCH).toBool(); + qDebug() << "[WlanConnect]setSwitchStatus" << status; + m_wifiSwitch->blockSignals(true); + m_wifiSwitch->setChecked(status); + m_wifiSwitch->blockSignals(false); + if (!m_wifiSwitch->isChecked()) { + hideLayout(ui->availableLayout); + } else { + showLayout(ui->availableLayout); + } +} + +//初始化整体列表和单设备列表 +void WlanConnect::initNet() { +// int count = 1; + //先构建每个设备的列表头 + for (int i = 0; i < deviceList.size(); ++i) { + addDeviceFrame(deviceList.at(i)); + } + //再填充每个设备的列表 + for (int i = 0; i < deviceList.size(); ++i) { + initNetListFromDevice(deviceList.at(i)); + } +} + +//初始化设备列表 网卡标号问题? +void WlanConnect::initNetListFromDevice(QString deviceName) +{ + qDebug() << "[WlanConnect]initNetListFromDevice " << deviceName; + if (!m_wifiSwitch->isChecked()) { + qDebug() << "[WlanConnect]initNetListFromDevice " << deviceName << " switch off"; + return; + } + if (!deviceFrameMap.contains(deviceName)) { + qDebug() << "[WlanConnect]initNetListFromDevice " << deviceName << " not exist"; + return; + } + if (!m_interface->isValid()) { + return; + } + qDebug() << "[WlanConnect]call getWirelessList" << __LINE__; + QDBusMessage result = m_interface->call(QStringLiteral("getWirelessList")); + qDebug() << "[WlanConnect]call getWirelessList respond" << __LINE__; + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "getWirelessList error:" << result.errorMessage(); + return; + } + auto dbusArg = result.arguments().at(0).value(); + QMap> variantList; + dbusArg >> variantList; + if (variantList.size() == 0) { + qDebug() << "[WlanConnect]initNetListFromDevice " << deviceName << " list empty"; + return; + } + QMap>::iterator iter; + + for (iter = variantList.begin(); iter != variantList.end(); iter++) { + if (deviceName == iter.key()) { + QVector wlanListInfo = iter.value(); + //处理列表 已连接 + qDebug() << "[WlanConnect]initNetListFromDevice " << deviceName << " acitved wifi " << wlanListInfo.at(0); + addActiveItem(deviceFrameMap[deviceName], deviceName, wlanListInfo.at(0)); + //处理列表 未连接 + for (int i = 1; i < wlanListInfo.length(); i++) { + qDebug() << "[WlanConnect]initNetListFromDevice " << deviceName << " deacitved wifi " << wlanListInfo.at(i); + addCustomItem(deviceFrameMap[deviceName], deviceName, wlanListInfo.at(i)); + } + } + } + return; +} + +//高级设置 +void WlanConnect::runExternalApp() { + QString cmd = "nm-connection-editor"; + QProcess process(this); + process.startDetached(cmd); +} + +//根据信号强度分级+安全性分图标 +QString WlanConnect::wifiIcon(bool isLock, int strength) { + switch (strength) { + case 1: + return isLock ? KWifiLockSymbolic : KWifiSymbolic; + case 2: + return isLock ? KWifiLockGood : KWifiGood; + case 3: + return isLock ? KWifiLockOK : KWifiOK; + case 4: + return isLock ? KWifiLockLow : KWifiLow; + case 5: + return isLock ? KWifiLockNone : KWifiNone; + default: + return ""; + } +} + +//根据信号强度分级 +int WlanConnect::setSignal(QString lv) { + int signal = lv.toInt(); + int signalLv = 0; + + if (signal > 75) { + signalLv = 1; + } else if (signal > 55 && signal <= 75) { + signalLv = 2; + } else if (signal > 35 && signal <= 55) { + signalLv = 3; + } else if (signal > 15 && signal <= 35) { + signalLv = 4; + } else if (signal <= 15) { + signalLv = 5; + } + return signalLv; +} + +//隐藏 +void WlanConnect::hideLayout(QVBoxLayout * layout) { + for (int i = layout->layout()->count()-1; i >= 0; --i) { + QLayoutItem *it = layout->layout()->itemAt(i); + ItemFrame *itemFrame = qobject_cast(it->widget()); + itemFrame->hide(); + } +} + +//显示 +void WlanConnect::showLayout(QVBoxLayout * layout) { + for (int i = layout->layout()->count()-1; i >= 0; --i) { + QLayoutItem *it = layout->layout()->itemAt(i); + ItemFrame *itemFrame = qobject_cast(it->widget()); + itemFrame->show(); + } +} + +//获取应该插入哪个位置 +int WlanConnect::sortWlanNet(QString deviceName, QString name, QString signal) +{ + qDebug() << "[WlanConnect]call getWirelessList" << __LINE__; + QDBusMessage result = m_interface->call(QStringLiteral("getWirelessList")); + qDebug() << "[WlanConnect]call getWirelessList respond" << __LINE__; + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "getWirelessList error:" << result.errorMessage(); + return 0; + } + auto dbusArg = result.arguments().at(0).value(); + QMap> variantList; + dbusArg >> variantList; + QMap>::iterator iter; + for (iter = variantList.begin(); iter != variantList.end(); iter++) { + if (deviceName == iter.key()) { + QVector wlanListInfo = iter.value(); + for (int i = 0; i < wlanListInfo.size(); i++) { + if (name == wlanListInfo.at(i).at(0)) { + return i; + } + } + } + } + return 0; +} + +void WlanConnect::activeConnect(QString netName, QString deviceName, int type) { + if (!m_interface->isValid()) { + return; + } + qDebug() << "[WlanConnect]call activateConnect" << __LINE__; + m_interface->call("activateConnect",type, deviceName, netName); + qDebug() << "[WlanConnect]call activateConnect respond" << __LINE__; +} + +void WlanConnect::deActiveConnect(QString netName, QString deviceName, int type) { + if (!m_interface->isValid()) { + return; + } + qDebug() << "[WlanConnect]call deActivateConnect" << __LINE__; + m_interface->call("deActivateConnect",type, deviceName, netName); + qDebug() << "[WlanConnect]call deActivateConnect respond" << __LINE__; +} + + +//处理列表 已连接 +void WlanConnect::addActiveItem(ItemFrame *frame, QString devName, QStringList infoList) +{ + if (frame == nullptr) { + return; + } + if (infoList.size() == 1) { + return; + } + + bool isLock = true; + if (infoList.at(2) == "") { + isLock = false; + } else { + isLock = true; + } + addOneWlanFrame(frame, devName, infoList.at(0), infoList.at(1), infoList.at(3), isLock, true, WIRELESS_TYPE, infoList.at(4)); +} + +//处理列表 未连接 +void WlanConnect::addCustomItem(ItemFrame *frame, QString devName, QStringList infoList) +{ + if (frame == nullptr) { + return; + } + bool isLock = true; + if (infoList.at(2) == "") { + isLock = false; + } else { + isLock = true; + } + addOneWlanFrame(frame, devName, infoList.at(0), infoList.at(1), "", isLock, false, WIRELESS_TYPE, infoList.at(3)); +} + +//增加设备 +void WlanConnect::addDeviceFrame(QString devName) +{ + qDebug() << "[WlanConnect]addDeviceFrame " << devName; + ItemFrame *itemFrame = new ItemFrame(devName, pluginWidget); + ui->availableLayout->addWidget(itemFrame); + itemFrame->deviceFrame->deviceLabel->setText(tr("card")+/*QString("%1").arg(count)+*/":"+devName); + deviceFrameMap.insert(devName, itemFrame); +} + +//减少设备 +void WlanConnect::removeDeviceFrame(QString devName) +{ + qDebug() << "[WlanConnect]removeDeviceFrame " << devName; + if (deviceFrameMap.contains(devName)) { + ItemFrame *item = deviceFrameMap[devName]; + if (item->lanItemFrame->layout() != NULL) { + QLayoutItem* layoutItem; + while ((layoutItem = item->lanItemFrame->layout()->takeAt(0)) != NULL) { + delete layoutItem->widget(); + delete layoutItem; + item = nullptr; + } + item->itemMap.clear(); + } + delete item; + item = nullptr; + deviceFrameMap.remove(devName); + } +} + +//增加ap +void WlanConnect::addOneWlanFrame(ItemFrame *frame, QString deviceName, QString name, QString signal, QString uuid, bool isLock, bool status, int type, QString isApConnection) +{ + if (nullptr == frame) { + return; + } + + bool bApConnection = (isApConnection == IsApConnection); + + if (frame->itemMap.contains(name)) { + qDebug() << "[WlanConnect]Already exist a wifi " << name << " in " << deviceName; + return; + } + //设置单项的信息 + int sign = setSignal(signal); + WlanItem * wlanItem = new WlanItem(status, isLock, pluginWidget); + QString iconamePath; + if (bApConnection) { + iconamePath = KApSymbolic; + } else { + iconamePath = wifiIcon(isLock, sign); + } + if (iconamePath != KLanSymbolic && iconamePath != NoNetSymbolic) { + wlanItem->iconLabel->setProperty("useIconHighlightEffect", 0x10); + } + QIcon searchIcon = QIcon::fromTheme(iconamePath); + wlanItem->iconLabel->setPixmap(searchIcon.pixmap(searchIcon.actualSize(QSize(24, 24)))); + wlanItem->titileLabel->setText(name); + if (status) { + wlanItem->statusLabel->setText(tr("connected")); + frame->uuid = uuid; + wlanItem->uuid = uuid; + } else { + wlanItem->statusLabel->setText(""); + } + + connect(wlanItem->infoLabel, &InfoButton::clicked, this, [=]{ + // open detail page + if (!m_interface->isValid()) { + return; + } + qDebug() << "[WlanConnect]call showPropertyWidget" << __LINE__; + m_interface->call(QStringLiteral("showPropertyWidget"), deviceName, name); + qDebug() << "[WlanConnect]call showPropertyWidget respond" << __LINE__; + }); + + connect(wlanItem, &QPushButton::clicked, this, [=] { + if (wlanItem->isAcitve) { + deActiveConnect(name, deviceName, type); + } else { + activeConnect(name, deviceName, type); + } + }); + //记录到deviceFrame的itemMap中 + deviceFrameMap[deviceName]->itemMap.insert(name, wlanItem); + int index; + if (status) { + index = 0; + } else { + index = sortWlanNet(deviceName, name, signal); + } + qDebug()<<"insert " << name << " to " << deviceName << " list, postion " << index; + frame->lanItemLayout->insertWidget(index, wlanItem); + frame->filletStyleChange(); +} + +//减少ap +void WlanConnect::removeOneWlanFrame(ItemFrame *frame, QString deviceName, QString ssid) +{ + if (nullptr == frame) { + return; + } + + if (frame->itemMap.contains(ssid)) { + qDebug() << "[WlanConnect]removeOneWlanFrame " << deviceName << ssid; + frame->lanItemLayout->removeWidget(frame->itemMap[ssid]); + delete frame->itemMap[ssid]; + frame->itemMap.remove(ssid); + frame->filletStyleChange(); + } +} + +void WlanConnect::itemActiveConnectionStatusChanged(WlanItem *item, int status) +{ + if (status == ACTIVATING) { + item->startLoading(); + } else if (status == ACTIVATED) { + item->stopLoading(); + item->statusLabel->clear(); + item->statusLabel->setMinimumSize(36,36); + item->statusLabel->setMaximumSize(16777215,16777215); + item->statusLabel->setText(tr("connected")); + item->isAcitve = true; + } else if (status == DEACTIVATING) { + item->startLoading(); + } else if (status == DEACTIVATED) { + item->stopLoading(); + item->statusLabel->clear(); + item->statusLabel->setMinimumSize(36,36); + item->statusLabel->setMaximumSize(16777215,16777215); + item->isAcitve = false; + } +} + diff --git a/plugins/wlanconnect/wlanconnect.h b/plugins/wlanconnect/wlanconnect.h new file mode 100644 index 00000000..12550bbe --- /dev/null +++ b/plugins/wlanconnect/wlanconnect.h @@ -0,0 +1,165 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef WLANCONNECT_H +#define WLANCONNECT_H + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "switchbutton.h" +#include "hoverbtn.h" +#include "itemframe.h" +#include "wlanitem.h" +namespace Ui { +class WlanConnect; +} + + +class WlanConnect : public QObject, CommonInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.kycc.CommonInterface") + Q_INTERFACES(CommonInterface) + +public: + WlanConnect(); + ~WlanConnect(); + + QString plugini18nName() Q_DECL_OVERRIDE; + int pluginTypes() Q_DECL_OVERRIDE; + QWidget * pluginUi() Q_DECL_OVERRIDE; + const QString name() const Q_DECL_OVERRIDE; + bool isShowOnHomePage() const Q_DECL_OVERRIDE; + QIcon icon() const Q_DECL_OVERRIDE; + bool isEnable() const Q_DECL_OVERRIDE; + QString translationPath() const Q_DECL_OVERRIDE; + +private: + void initComponent(); + void runExternalApp(); + void initSearchText(); + + void showDesktopNotify(const QString &message); + + //点击item 连接/断开 + void activeConnect(QString netName, QString deviceName, int type); + void deActiveConnect(QString netName, QString deviceName, int type); + + + + int sortWlanNet(QString deviceName, QString name, QString signal); + void updateIcon(WlanItem *item, QString signalStrength, QString security, QString isApConnection); + void resortWifiList(ItemFrame *frame, QVector list); + + + //单wifi图标 + int setSignal(QString lv); + QString wifiIcon(bool isLock, int strength); + + + //开关相关 + void setSwitchStatus(); + void hideLayout(QVBoxLayout * layout); + void showLayout(QVBoxLayout * layout); + + + //获取设备列表 + void getDeviceList(QStringList &list); + //初始化设备wifi列表 + void initNet(); + void initNetListFromDevice(QString deviceName); + //处理列表 已连接 + void addActiveItem(ItemFrame *frame, QString devName, QStringList infoList); + //处理列表 未连接 + void addCustomItem(ItemFrame *frame, QString devName, QStringList infoList); + //增加设备 + void addDeviceFrame(QString devName); + //减少设备 + void removeDeviceFrame(QString devName); + //增加ap + void addOneWlanFrame(ItemFrame *frame, QString deviceName, QString name, QString signal, QString uuid, bool isLock, bool status, int type, QString isApConnection); + //减少ap + void removeOneWlanFrame(ItemFrame *frame, QString deviceName, QString ssid); + + + //单个wifi连接状态变化 + void itemActiveConnectionStatusChanged(WlanItem *item, int status); +protected: + bool eventFilter(QObject *w,QEvent *e); + +private: + Ui::WlanConnect *ui; + + QString pluginName; + int pluginType; + QWidget *pluginWidget; + + QDBusInterface *m_interface = nullptr; + + QGSettings *m_switchGsettings = nullptr; + + //设备列表 + QStringList deviceList; + //设备名 + 单设备frame + QMap deviceFrameMap; + + //QVector wlanSignalList; +// DeviceWlanlistInfo deviceWlanlistInfo; + + QTimer * m_scanTimer = nullptr; +// QTimer * m_updateTimer = nullptr; +private: + SwitchButton *m_wifiSwitch; + bool m_firstLoad; + +private slots: + + void onNetworkAdd(QString deviceName, QStringList wlanInfo); + void onNetworkRemove(QString deviceName, QString wlannName); + void onActiveConnectionChanged(QString deviceName, QString ssid, QString uuid, int status); + + void updateList(); + void onDeviceStatusChanged(); + void onDeviceNameChanged(QString, QString, int); + + void reScan(); + + +}; +#endif // WLANCONNECT_H diff --git a/plugins/wlanconnect/wlanconnect.pro b/plugins/wlanconnect/wlanconnect.pro new file mode 100644 index 00000000..86fef538 --- /dev/null +++ b/plugins/wlanconnect/wlanconnect.pro @@ -0,0 +1,51 @@ +QT += widgets network dbus gui core +TEMPLATE = lib +CONFIG += plugin + +include(../component/drownlabel.pri) + +TARGET = $$qtLibraryTarget(wlanconnect) +DESTDIR = ../.. +target.path = $$[QT_INSTALL_LIBS]/ukui-control-center +trans.files = translations/* +trans.path = /usr/share/kylin-nm/wlanconnect/ + +INCLUDEPATH += \ + $$PROJECT_COMPONENTSOURCE \ + $$PROJECT_ROOTDIR \ + /usr/include/ukcc/interface \ + /usr/include/ukcc/widgets + +LIBS += -L$$[QT_INSTALL_LIBS] -lgsettings-qt -lukcc + +CONFIG += c++11 \ + link_pkgconfig \ + +PKGCONFIG += gsettings-qt \ + +#DEFINES += QT_DEPRECATED_WARNINGS + +SOURCES += \ + deviceframe.cpp \ +# drownlabel.cpp \ + itemframe.cpp \ + wlanconnect.cpp \ + wlanitem.cpp + +HEADERS += \ + deviceframe.h \ +# drownlabel.h \ + itemframe.h \ + wlanconnect.h \ + wlanitem.h + +FORMS += \ + wlanconnect.ui + +INSTALLS += target \ + trans + +TRANSLATIONS += \ + translations/zh_CN.ts \ + translations/tr.ts \ + translations/bo.ts diff --git a/plugins/wlanconnect/wlanconnect.ui b/plugins/wlanconnect/wlanconnect.ui new file mode 100644 index 00000000..fa8cfe41 --- /dev/null +++ b/plugins/wlanconnect/wlanconnect.ui @@ -0,0 +1,192 @@ + + + WlanConnect + + + + 0 + 0 + 885 + 700 + + + + WlanConnect + + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + WLAN + + + true + + + + + + + 1 + + + 0 + + + + + + 0 + 60 + + + + + 16777215 + 50 + + + + QFrame::Box + + + + 18 + + + 0 + + + 9 + + + 0 + + + + + 16 + + + + + + 118 + 0 + + + + open + + + + + + + Qt::Horizontal + + + + 523 + 20 + + + + + + + + + + + + + + + + + 1 + + + + + + + 8 + + + + + + 120 + 36 + + + + + 16777215 + 36 + + + + Advanced settings + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + TitleLabel + QLabel +
titlelabel.h
+
+
+ + +
diff --git a/plugins/wlanconnect/wlanitem.cpp b/plugins/wlanconnect/wlanitem.cpp new file mode 100644 index 00000000..5d1bbf3c --- /dev/null +++ b/plugins/wlanconnect/wlanitem.cpp @@ -0,0 +1,90 @@ +#include "wlanitem.h" +#include +#define FRAME_SPEED 150 +#define LIMIT_TIME 60*1000 +#define TOTAL_PAGE 8 +#define RADIUS 6.0 + +#define THEME_QT_SCHEMA "org.ukui.style" +#define MODE_QT_KEY "style-name" + +WlanItem::WlanItem(bool bAcitve, bool isLock, QWidget *parent) + : isAcitve(bAcitve), isLock(isLock), QPushButton(parent) +{ + this->setMinimumSize(550, 58); + this->setProperty("useButtonPalette", true); + setStyleSheet("QPushButton:!checked{background-color: palette(base)}"); + QHBoxLayout *mLanLyt = new QHBoxLayout(this); + mLanLyt->setContentsMargins(16,0,16,0); + mLanLyt->setSpacing(16); + iconLabel = new QLabel(this); + titileLabel = new FixLabel(this); + statusLabel = new QLabel(this); + statusLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); +// statusLabel->setMinimumSize(36,36); + infoLabel = new InfoButton(this); + mLanLyt->addWidget(iconLabel); + mLanLyt->addWidget(titileLabel,Qt::AlignLeft); + mLanLyt->addStretch(); + mLanLyt->addWidget(statusLabel); + mLanLyt->addWidget(infoLabel); + + loadIcons.append(QIcon::fromTheme("ukui-loading-1-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-2-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-3-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-4-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-5-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-6-symbolic")); + loadIcons.append(QIcon::fromTheme("ukui-loading-7-symbolic")); + + waitTimer = new QTimer(this); + connect(waitTimer, &QTimer::timeout, this, &WlanItem::updateIcon); + +} + +WlanItem::~WlanItem() +{ + +} + +void WlanItem::updateIcon() +{ + if (currentIconIndex > 6) { + currentIconIndex = 0; + } + statusLabel->setPixmap(loadIcons.at(currentIconIndex).pixmap(16,16)); + currentIconIndex ++; +} + +void WlanItem::startLoading() +{ + waitTimer->start(FRAME_SPEED); + loading = true; +} + +void WlanItem::stopLoading(){ + waitTimer->stop(); + loading = false; +} + +void WlanItem::paintEvent(QPaintEvent *event) +{ + QPalette pal = this->palette(); + + QPainter painter(this); + painter.setRenderHint(QPainter:: Antialiasing, true); //设置渲染,启动反锯齿 + painter.setPen(Qt::NoPen); + painter.setBrush(pal.color(QPalette::Base)); + + QRect rect = this->rect(); + if (!useHalfFillet) { + painter.drawRect(rect); + } else { + QPainterPath path; + path.addRoundedRect (rect, RADIUS, RADIUS); + QRect temp_rect(rect.left(), rect.top(), rect.width(), rect.height()/2); + path.addRect(temp_rect); + painter.drawPath(path); + } + QPushButton::paintEvent(event); +} diff --git a/plugins/wlanconnect/wlanitem.h b/plugins/wlanconnect/wlanitem.h new file mode 100644 index 00000000..9d24757d --- /dev/null +++ b/plugins/wlanconnect/wlanitem.h @@ -0,0 +1,52 @@ +#ifndef WLANITEM_H +#define WLANITEM_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fixlabel.h" +#include "infobutton.h" + +class WlanItem : public QPushButton +{ +public: + WlanItem(bool bAcitve, bool isLock, QWidget *parent = nullptr); + ~WlanItem(); +public: + QLabel * iconLabel = nullptr; + InfoButton * infoLabel = nullptr; + FixLabel * titileLabel = nullptr; + QLabel * statusLabel = nullptr; + QString uuid = ""; + + void setHalfFillet(bool flag) {useHalfFillet = flag; repaint();} +public: + void startLoading(); + void stopLoading(); + bool isAcitve = false; + bool loading = false; + bool isLock = false; + + + +protected: + void paintEvent(QPaintEvent *event); + +private: + QTimer *waitTimer = nullptr; + QGSettings *themeGsettings = nullptr; + bool useHalfFillet = false; + QList loadIcons; + int currentIconIndex=0; + +private slots: + void updateIcon(); + +}; + +#endif // WLANITEM_H diff --git a/res.qrc b/res.qrc new file mode 100644 index 00000000..7646d2b3 --- /dev/null +++ b/res.qrc @@ -0,0 +1 @@ + diff --git a/res/g/close_black.png b/res/g/close_black.png new file mode 100644 index 0000000000000000000000000000000000000000..237e555213079ed8a44c34bd702e94bf8999d310 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3Q#@T9Lp(a)PCm$aK!L|~|6JX7 ziRzoHU*w4$Z02oKd$3fMziQ>d^he@-k1hVUc1Q^>y(SeFAOG6)7Nbm?=Uj%O9>b5V zANBiZ?R^wdhxDIGZJktT*?6`2Rt#2l<1Kr2q>FVdQ&MBb@ E0JbGncmMzZ literal 0 HcmV?d00001 diff --git a/res/g/close_white.png b/res/g/close_white.png new file mode 100644 index 0000000000000000000000000000000000000000..0943a4bb87ef3488385d4707b140c5a951fc95c1 GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3@t!V@As(G?FFSHI81T4UJUF@P zzjseZNRQ$LS0}HT>~|wx=r43~Nr|tSqawJby8k7!1Uvi3^7TqGN6&6w^}+s$UXbL! zV|QPq%l-`axn}!n+VQL3tX%_z{rS5))-WDm4HDBb@?X&K7iblOr>mdKI;Vst0D5XS AbpQYW literal 0 HcmV?d00001 diff --git a/res/g/down_arrow.png b/res/g/down_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..c7a343b36b90adec3fa8dbba3bc48ff71c181568 GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)Q!3HEdXMMW?q%1sL978x}#`bvfF(~jbrT_YB zC&Pa&;|WvZ=G}{Du`o=%s41GDE*_})V9h7C2Gh3ZiCs)rik}|Wxx80o=2z)yeM}Q)7{6;vDQqZnMg~r~97{yydLQ)bE))v1LL)_jHC9NF>)m^BLVM@+T#IoT z=v>fbMx9xdY*|hC!;iR#II=_|Y}N_?xJ5UH#7RQ3B`#U~SR%{Rxwk-_e;D87a_{rp z^Lw7(^K}QB8td|Mi*gYJ$@ABHo8fyId~z&i1VQVk8csl=Q1^xO&5ESQc?}@dq7nv} zKf-r_X26R*J10N|f*3!NgCRW>Xy61TV&&5|)en#^?}lckPWv3mcGnbHyry?Kat)i@vSrJl;oKjF z0m*#v@w-ipEBlB05@`PR?AGjK!R&$eEMgnd^4Ht-Gw48Y$>68oPWCkzicaT<*U^VM zcIEq2O3Y5x{rl`mK~`$z@t#DY@X%`aV{`x9^i*h*Y2D4NmWf+;@5(zAC0=|gQR(R& zLRQ^)^Oyyl%{@Hf$tv%iykxljyXOv?YVG~IFn=RDv^4l@4tB%D-LsDi>YC~g&b%== zg6>@V}O-D&9mpkeiS{PeFmAlJ zZ%i#Pg}1lY7a2>`b>m3;9cpaWe9cE8Kb7SUO2|-Qeth=9^tR;LAB^{cnAx{!4YFcv W=xE!cJr~pePrt9x`&CW*-v0n!l=BGy literal 0 HcmV?d00001 diff --git a/res/h/no-pwd-wifi.png b/res/h/no-pwd-wifi.png new file mode 100644 index 0000000000000000000000000000000000000000..dbec54a3af0844bb65373f4cf2204601d77d81ea GIT binary patch literal 1643 zcmV-x29)`UP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+P#)xlG`8*h5zdmJpz&d!EvxLGrd8N--k_{#L1@X zWT$3gu!1G=K0!jupWlss;S$G*e6SRAj1HI2K1;zMx$W9|c7DB&2X`6V*=hd*Zk?jn z?J+K6et|t)Uob!H)4iQ+9)8kr#Jl75(jK{w>qv4qQrYZ0o+*zPO1xRl_{com4(G>b zZxEvf_){TSl+~?w*&->j(5wahb{FXu#WwYF@lD?SqBiepiirK}@NoFJ#JuSGP&Ctz%A z%V^0JH-lQmQYUf~9x@9QZ|u2p;l|6_&pabVFN+v@ky&)LB{j)1uTXq%+{6fEswS|S zK~7j0Mb!AI=3gc2%* z?c+lTF<3|;hY~gV7-EbT`#Dui&0DZ&X34UZq{*j{VzQJ{P9;lL9AklU%$8HmxfBE~ zzQ9+(`~t%ylTLmLQ=Du{Q=ZC<@|pb{<~ZA&<~)}wRV$7FsK)i5|+5w zl9s%bhFWXddP5TxaI8D5(ZPmZ8I`+=+ppUsyupo!yn(m)r>Ip5-Rb$Wcf46Xd9)yCL_; z?FqG7*5g)JKz$cNQ=E{B?S~0VDw0b7U3#mhcVGOzqc)vNlAiiVr}JtweJalA=|zxq zX1Vke%j)jwvt?bw(mS-CWliT{Ki7_ah8e`Z@&6>N?~|j?!9Uc|H^9HDqj&4(_!Yff zh6lIwUip1pM;|TgADi|?J~!?zR(fBR<6e7RhEHGmDx>FS@^kM<0dOCdq zeYk0V;#cI~+5FGL|9jayX!OAuaC*~zEjIq`WcmwyL(y25*2Dn-00D$)LqkwWLqi~N za&Km7Y-IodD3N`UJxIeq9K~N#OI0ckb`WvMP@OD@Uv$(e6rn<>6nNgNw7S4z7YA_yOYL>ZIr*CH}7|w21NGxF7HCJ?`ECLcPp1t7{z4blXfN z;$kMdDh6NC0|5a{BPKJ;n3JR=Jjd5Pe0;r&@+|Lje~vyiXEDGh63;Tjw23!}XEtqv z^FDEy6=jw9oOsNn3lcwaU2*x1bD_xs&kURC)I4#RSS)m~(!s1~YQ$5-5mnPEU&y$u za^B*sm8-0EPyWJCPG4E(I?Z7uu!to{5TT%o63Va9w{N zIRg5(fs5<5rtATiJHX(RA)9hj^3xRZdEotwz9|a~+ycF8Zf~u9oIU_)>MD5y92^3p z1VyY4_ zG}IO#3mAduf=4IUTd`Pl%Eww>c8PtZW;EhbD_W8cMi5u-Ale;FtFtvVvvs^09$L9w zt5{N&6eWE~IvLIj39#EzkergG=wSQac5RVNZUU3@hi3_jAEEg=Kz?;A9N!Jh=4bLF ple9dP_YlGl&`J7rwY>V9^#`z*R8XiZJfHvo002ovPDHLkV1n_*0C)fZ literal 0 HcmV?d00001 diff --git a/res/h/right-pwd.png b/res/h/right-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..1bbc00d18e3edae496bed96128b6fef1e1ff046f GIT binary patch literal 1398 zcmaJ>ZD<>19KWV9%OdHz&ds)?dR(Vwy34)fa=p7X_VU&)YTB3vTSQCmRDDz3%)O{#BIPQ6# z`~Cj^-~a779_#LMI+`5_f;c0GLUH)5tGsp_g4CpXTDst-$qe_Ky?WBjO9nvvsxAXG zl97f$97yW;=$D`!L26%5C;H9)=re+%XWUW+<1S>fkc}Yi9fho<3@0j?{@ z1ct6b%wZq4E~-BoLxZ{jP_LWC70Sb*ZM>WE@*K?^KxvX<36du$1}7?&5cz!Go%gtPV~C)5o+n6}plKW;@Z7j&N(EfYwN@>JKu$5z zSu?F`XvLx=>tm)5gG$#!$Yi6@O~YDlBT+DAL_x|Dl$#_nnMz!1=$sh`|I64ColA^o z0TBl|eaui`J(8_eGR)n*9aVr(jnHGHVNs-!kgkknfM!NQJ`DbGt7%nWNS;(Bki->` zq;X1OXk6ks1y8bV3=i65hGp2QovrwRFdN`J92cfonxev-KM)RdvSF6>!fG%cf0Y~2 za;Bsypz1pfeQ$8v?#UH`29QkMNa*@VwF_b?-PCg_J&Oi|m8!R)uD-OU>iOLMN}1N9 z4gn+m5>Pu0J%g_0Sx9f8!OO~On?&-stgsvoJdA{MBuT>flO8}OWs>od*aly{D}U>K z3Qu4h*{U2tp-9?Gl3rYqR1T*oz~MYE%Q(+a6zgSaPNEfzfD(yH>)h2!)g#!SmF4DO zz|H1p0S(TR0SD*GyYsu@?3N-Sf1>b{e49^QM63_ah}QZ=i?zNrcG`Jq-g3NbId`9k z6ho&brUaY)kD*E9(Mh%VaXmWGIQRZPh}^Vio~}a&maNxK*Nj?AyDa6Wi>po%S-Ra` zb6paVTJ*VOvHA1jzR`x#H^o)qQX`Lkf9$D!fk(uv6ogIfu333iT(Fg2B)>R1<+QE5 zEG`^VnPn%c_@Do_HL~n)`N1}Qplf=tRzBidY*ZI^x^B$Ab`Tju?LQ+C%_38%CQc(i z-u%jXvQS$-I5D%g^jPh=$Lr5q?Bq;edIfwtvV7fY%9Izm+mh&iJ$EGE&43_ zsiUmL_I|gt-F5j5+pkZYkkH?6*yN6se8DCz-D)UZEUsdSzaBdAX0!N8-5J-zrH?GP zT3ko!T=8PRqjV?mnK%$rCp&v~B(m6*j@ib)?Pbrz^3KRE`-ev@+uyS7n8aF6jWo|) z{$<;WCl&dVVg}9(?r(S?{z2?*@%q1X{T<5+5!0FAHC38(AQw*dwpfD9)yhp633rFi I1_mep0|fTVc>n+a literal 0 HcmV?d00001 diff --git a/res/h/show-pwd.png b/res/h/show-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..08c4de5105349165d8f21927d596272a8fbdbdee GIT binary patch literal 1289 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 literal 0 HcmV?d00001 diff --git a/res/hw/wifi-high-pwd.png b/res/hw/wifi-high-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..81a4d11eae658f9d1775a78a233b6daa81665433 GIT binary patch literal 682 zcmV;b0#*HqP)J*%sn%6A(NTd#D3WVU&pD#yBt1dfLS05{4p*sz<=Nw=)dhQy{1lpq;o(4 z$R|8pA9xasN+uXVya3Dtxs)mkc?Y<6B@#Huh4`<7cmQ;XftFd7XPQdo`Q0Y+fIW}S z1Q?|3kUEmFo8;JtH$xQ*=9R>`iINYLbJ=7qjx9J2(&&%a5_6Hgq}8}W?0%LDt4i`$ z5_cTID)9u^MaNHOQ;VH0XUWv5l zl(}v86j`3ln0?z?jdAzDElZ*U)GX_)%wUVqSErBK8;zbqmIU3jch}$xYjP-d}$10 zYJJMbn(hrqTyfkJE^7uUd%)S4y2ju3uQ-V%Nnk4T%Qi{irxIxT`n?M<0E3ACR401l Q=>Px#07*qoM6N<$f>UEHK>z>% literal 0 HcmV?d00001 diff --git a/res/hw/wifi-high.png b/res/hw/wifi-high.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec5305256852940cf8f0d26993a3194e30d0228 GIT binary patch literal 605 zcmV-j0;2tiP)%L&}fWrk_Xw0D|g2UHcawz z7j|dn&70Xh5{b2~?Vl|fcH_q7V;YYvumz-mO||76_ypd7@z+`DBQ^mhEdWIzAF;S0 z-~)qsWRi0d?*Q9CI;P5!d;q+F$Rv;el1t)UOX3Nu8dL6>%tm`Md=~hwiM+z+*-C&x z_AOG2j6NKPQOZWVELAM3B`KhXc;guXZmy`RCE%aGg%lL0Ue-?SR>Wu z3g<)=@XZ;J!5NxI2ER$QSFCgqljIkO*;XX-BLBQ&C<$ADtCmFS8=K7VsZzx{pQg#) zV^fWwd!VO+c&x+=Ie0?m9H^S);lKA|?=LYr#``H855guFlfB}D*9P05V+LQvsKRBi zYt{g~Tv1I{FFt7mkkpg?xe|BQC~-K^z=(K$9*EkN^V?81Y_n8n2Jn(nmEcyyT>` zz3<+0&pYoG$JvhU*zizU0}EG=9$iX=J%UTv)j}qO55kZz{k%Ay=O%!n6+%GpEt{JX zB1Re+l~VYSa6s^Ks?5a)geMabWhl;tj|_!pgb^^4GK=LYLMb1=bs{e`r*9;HBsGK7 zv5X^|wkcl;DhAps&eM&_D-m4NSaV>H0%v*jr`(*g3NJ}D?kM-v#e=$#e2&5cLoy$` zAXaGy^QMCIP^yInx-^#wgoyW0vmGlC;3X$|U}gqG?iOA za^7Yo(1!wZ93YX6^An8Mw>pYp%!$E}0Xp*yG7t2l7hAk&+Eun6qX4pc8Fs+ZiI?kZ zNX4tziuw+C7w3V#6IhAfs@$%{mn|MF<+EIxgcI-kFj_1Yr*t_Hq4!Fu_PR^ptTLpG zLvc1C7aSAH1b)_&B=Vf|AtUKM5x69T+Ov?K+b)4#_>3;s=!oyRyL0OL#t{Cd-T&GR zg*BTm$Hf!we_XEASi2vrwG9m_G@*in?ps3U23;B%(sWc#4>V!8mcI6X{;$|(*bpc< l8y;H%|3#paHSSA*0RRED|0o143K9SS002ovPDHLkV1jDj9b5na literal 0 HcmV?d00001 diff --git a/res/hw/wifi-low.png b/res/hw/wifi-low.png new file mode 100644 index 0000000000000000000000000000000000000000..3fe8bdede28c3af2566a5bdce14a7430e9e1e827 GIT binary patch literal 589 zcmV-T0SvL1!-t<0!oemN1)3Q=r{r#feTQip-BN06iKl`a^oT?NZ|&Acq2VWtJTIIjEs=H z($hNG-I+IUXU2A%deq~OMnn7*#@gX;TYlJu0jR`H&D0c0=CtG?B+h$bHMbrX}72P)`$U!0->G* zk~k~lfKOs>i3*VHR|W}By3@dX(xAH=4IqF znmm;-H#=Ubw;`PO*-Xp~Ps+7rAttSQl4ZM@Q-C-tAuSzGIlnn1CxAiDeL*E(GjKE% zRPse+I6lX8103+LH{c4_a$nfi4~h3Q=MJBHl_@|KE->Bl=Ov=QtNe>rNRUW9>hXWa bw*UhG-8ai9>hoU@00000NkvXXu0mjf`;7Cz literal 0 HcmV?d00001 diff --git a/res/hw/wifi-medium-pwd.png b/res/hw/wifi-medium-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..221fdbc16c4783fd9744c2c3b06e2baf1804c56e GIT binary patch literal 663 zcmV;I0%-k-P)~!-*T1y?n~!QUun39I$GHjDUBb4~##~cBiQcu;@Ba2Hb?3 z8w0)|ii{IccnjDB@+no8;yvKmM#Qlcm%`hY!V_RXOtj3ZJq0S|^0!Ik1#7xi0!&i1 zNFB&HNV09@D^kU>@ksJK1mqnRTs2rrUJEFJ1&knUV*MQ9@VJQFS1Er zdq5Ztj0Ita)W*z25c^>~ZpaN3jyi^((YHWipF}5?Ub|eg9%Vc|av{w3DN}4i@|nQ5 z7|&(S+qeX}RA9;%h@ZsyA;#+_dKJ@{Bg2p-Tuy)^;J{eY25x``)(nmJLBV#al75V& z@D)&+6Dq|oQ1JTKzJ@3+g-?y?H$p~*&_k4{&C8vODZrEZ-Q*5C2FVH)I_A`u;bybh zAuf9=^xkot)}k#CgEivDvACG9#nwpsWUix0qL=uf~OV1kPagE~0iP)NFTkVF|+G{$q2Q_{RV`eC6i zeaXp#zW44u_uPBiXBhd&$3Krk+>I+&FD9lkum%)?in8Ss_y*pA$!;EnpKcQ>rY^J>U&QrjZ2TTpVv}98Ve5gmTYh4y32VxyIirks;0u zv;=5mUn8~1=;GiwN!YMgriyiCNdkC+y~&IKH@B3i#o@C~wuD4VRJ5+BA+|lAz+u3- zEWTpVofFkMWj2XbM&*-!*QtfG78MQ#dG8Ytc1`BF@5`eUcC(J z9b6dfLSmR0{Yj3F{v<|Rj0uBvaL}Qebm;&Ibf^PHe9wGMp2xNHsD_10zLd7_-uv#m z?_F`6{n(Ek4^=m?b^GMer9(I-xP(J3WI^~OObOZ7^~JI=0Tk^L0)lV3xQvi6s>rB{ z!e@k2g4a-GE}jyeO+=KTI2S%Q6wV1VAeSXyN>~$e;B)vfr59u^$WNu9NkaiCQb2JOZd^bS3O9+O%t~W)S{;7iKpLMk0_l9avvad+ zdtSG?_0OwU_rZ&ocOO3?a0&E*p?c&K_y#t>?&tabt2F^8je!J+8Wy(&QXnfM(<@1Q z3)}#HOO+*g2CP6NuOxuvlK9S%IA>Km%DtD_x}Jc)G5>2KYn)j+2{6c`Lu!#R#ldZp zvJo$&iiz5i0_KRf+Y{i!6IHb&T)1RwNF<=5iBdyiX9a;LE4gfbU^6{X)fHv-i2bWr_dF})QHH#B+-sTf)_}1)fUzblKom%A4~TKL>Of3lev1we z_Zvkb68WpzaD2c(nM-C$Jmi_sF%xF4dC)(=e4>I#fMr=Ha8F=JW@L3##F*2%1PrD3 zXh_pUcanLdx5m6e(`Xd)uxEFJHJW2qrQ0^+=*?B<1jU`Vc^Sp5rcNiU^u%lRHbn9f zhlzH0(XImrF&Wjn4XcIg?RgFloX>FG>ei{&uK)u87;efOb}a+t00000NkvXXu0mjf Dw#D`T literal 0 HcmV?d00001 diff --git a/res/hw/wifi6+-full-pwd.png b/res/hw/wifi6+-full-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..b2936c6ff2aa2775e8f4ae0fff7edc8c99850598 GIT binary patch literal 737 zcmV<70v`Q|P)2-0w5w*vnG zuumo;@8BsT(KUhBYG>QMiQ^?mzzNZbHxHwcvm{Lp zj2V$Y70|viIsh&L5n<#$E7HzRtrYjf&S}C@Ww`Q^aEQLVrV9AYg!y1A0AmS07Y?@} zkJ}1KMHxCGw*Y$ZNMz`nK0AzBl5jb30ly67$5bm`?K}++Y1;35BW97*@lBl&9mJS~ z0vnm`N#%W@;mQ()_(FP+c+&et(z`IjEOhi>qb-q&dF{98D3Th zQ*h6B9mjRH@Irm-i;XJb#z^l(+LCz(8ntEik}crGX8;~G7{^4+L13Utm?>h3B)+sH z+LT+WLnmmIZC!9|Uuy3k$1enamXLVo7LA;`2`Dj=Zf$u89Z0;S>w6^`xiiEbWWo0q z;UDV86S;%MyEQerITdls9M~qDzxR9pGdiU0L!gthV6Q0tC(zNnP5)6Be*_o+12h}Z Tr+8sl00000NkvXXu0mjf&gfD_ literal 0 HcmV?d00001 diff --git a/res/hw/wifi6+-full.png b/res/hw/wifi6+-full.png new file mode 100644 index 0000000000000000000000000000000000000000..faf2297d3393d2879d7983736a79d77d563f3521 GIT binary patch literal 665 zcmV;K0%rY*P)N=HZN=zw29?Cgx49oW$sJ3A>UV@G9t>+CV>O};P$8+K;r&f{|L-Mihr z(=h(oOx20wxQ?a;w|Qs-Uz(=5sFJ`bgV)fwaB9~KPJ|r%wgHJ{r37GC4E_$V2cgIf zyh%z_P2il{=k3{;<{BjYs$)PApxs8K1VEdtMxhuu8NJQ98^S-~G(Y_6Uqpq25TKqI z3%PA_yT~E|0&&Vu^vyjy$Rcy1{T%@8$Eh#k!w}ix6jYby&EOv(IfL7|1OOZ+^lJ$V zZwg zhf)Ex+ZF*cc#Fr>ZfsGcWIxde08WZD;4+(t*c=2qQUO(n>e@P_hEFLeV!$Is60(5o zh13D?B0&*S?va&N){v#R#_Uv0SaTXiS`fMz^K-I*zL`=y$PB<-0-rMu9fNO{EhHIf zsE7;!G|@;n7z=(~MkPt8m}tX03Gor|j-%AuaG%EKJr>2x;OcmpSBUmv6od@c6T{QY z_dvm&SuAmz>w%?}-X|`-EAb7@wb?BFX`$#Rdrx9%dA##4}I3 zj`M7#Nj=ny4QIkYl-}vSBin_K3(j*7@}i{+hcBJ-ui_nn^?;-!>Z$6s(^`KdZfQYh zYbjZ+0_v>!#bc<8<>3F-95mpLcC@POTYv!oNuKLC$u!D_00000NkvXXu0mjf8~H6@ literal 0 HcmV?d00001 diff --git a/res/hw/wifi6+-high-pwd.png b/res/hw/wifi6+-high-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..684d51f6dc7435225c1e194cf547baaf7cb32f66 GIT binary patch literal 781 zcmV+o1M>WdP)KtdT%#%$#$v>R)-ZcVptZGQnC8G~4|RG0;IBMeB00Y&QKfl8qRid59_9=xl* zULD8Au~_(|Pv<+^_uhT?zVGZH_{SEyi}`%Mj;051X=nyN4Z|?0lfat58)&xSY*T@g zP#5=RAaPnN0oYT4e+Jkq6OkA2HX~6tfk$db*LxF&DM-Y1YrqlUYNw0@K--E&Ar0I) z_}J0qclb~EH~7WX?&f6~0<=h5F;>4V4FpR_G`)61#Lm&2QB!$j*1Y}@U}I@ug66qM zB_>z%!GYfnB+O?INKNpUY{V0Co@U1A#jt8yKhFKH!}T8v(c49)Shk)7V69%M>Yl zooNJsbKVna!5N8lMC4B;G=*s0n}^YenUZGp1M~+b3*MUN-%&3BPBOBEdU&EpI6JYW zxFO}cnvg0Dr`}dT-)sRV-UM*Jz;hI03<5o6!pNOBD)D1WqDs28>5J~O zZ0iHZjnb_;R^i9WZek5vy1P-IHFwR9DAAo4`l@=`KzXNWz>GW!+6&%J)B?vBlK zeJ^wiOp}BaHE=CBfA35G8LiUF5UAxWC>6#31ZtYM=pS|ROMn3Y?Eorc#^$Gw00000 LNkvXXu0mjfb*fvX literal 0 HcmV?d00001 diff --git a/res/hw/wifi6+-high.png b/res/hw/wifi6+-high.png new file mode 100644 index 0000000000000000000000000000000000000000..0ffb9ff97c0f67677a387d78d19bc1922e075957 GIT binary patch literal 705 zcmV;y0zUnTP)E8Sv;Kk{8;zD0$(C@-h9DY3aB)bmh7gMETsmLezTWdk`fTyS z<@@p8`_6gyo_pU5h5p$bGwJvHQ4DE#j9Wc;-Eo{+kOUS99>$P`%h)Ek5K7@%4Ixl1jJmXqQP~78VC&`5wXTNiFGivL{o#vEL!Udz#7y*6T>b^)l{{lMy!ko z{S9P9*bgA``YVDzYLejAJZwFa(hD4VPVHPXdXI1ov;<#)?|{gfUj!uou9PEyd#W@; z`pxaRi3CI3p^M!CY#HM!!3~i(_C;?uc;-b%z`Si*fC--FUXx=?H7Py!Gy#Cixm9Yx zm=H^n$X6#+jVNlJBhe^JNvgLmVqH{KaH;OU61@Pp=20!8hb>0JSXH;;FqLn5LYrmS zwNjDAURfR7_XvPIa|}NMnXbB5z>|zZ1J_#4q>aRffF^PPpd*V!1bgitAEOCL7)@kY zy0$(l@SKjuudm@W4TPjhN=&IS3Hj#0J}@m45fUt~wvPw?4~+O`E<>DQLvqz+QT~~P z8{kd#jNI>HuRIX}9#k4u|1=R^ajZY0A>eXY!9(GV0gcHs=fgfme%7=?ePA~mRziUd z%{S+hC_4);jd)*nCV#X5N{CP8uXs#gTac2XJknAb`>a0_cP-Jytda~i0w&iOPPokZ n)p^hwn4RF}Hn*VcQ-A>gGkN*T*iXyW00000NkvXXu0mjf;IB69 literal 0 HcmV?d00001 diff --git a/res/hw/wifi6+-low-pwd.png b/res/hw/wifi6+-low-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..c2b8ece57dc7ace0d2e090410110f8dd619bf927 GIT binary patch literal 780 zcmV+n1M~ceP)Mmw>yJY;}h~)4>gR(GY{7DcL*Lw&L(_#yn-W|I z#dtOYiL*)xK%No&E5MGGMP5TAu|(Ab_Sp7*`6digkVvYI0Y^Y~I}u9&G@>Ba)sXvifH{%n44HyHLXkL<3 zUu8?2Gcy|0Imk$0e*=-5-z<19Ckbx)HSIPjL<4i_y~C&^ zi7hmnbfM^$c_rnJ=b5MBDuw<&psAfmP=5R;1QZcT!+n=&;`34j=s#!XhKcAgO`YnB z$8_89ffpjch4wxE^@#-DMWGwf67XGj$e0M9vcKse=Jolq!zfE`jHxedv0)~(ROubE zw!}QpoJeiy{f!G`;cWn)=Xg&_@dyMuEN(q_-mt_EG>I}%9*Jf>m$J7sEw^{6r#lpHpR*n~4C0wQVD>TrN&S-Ftc}2 z(eMub2Yv%@|NXMwO+$c|RVo^KE~^Hj0VHx>EQ(kQ;hHt|KQgo4^AW%rs(~wn4UsxE z-BKO0Gjj4S$gr?~fyn(c1)ub!z`b$C`%l$AV9~Fto$pew3AaFV@I3qzh^+Kefcej| z9Rb`itr^*!`(vksByP!RKLECfxGZo-q=+(;0D00K)0|SK1c~?C-x}LsPf&J zkf;rBy;N*su5K3o91wsh`OSK;9e{TUUXcn7{B|9WdXV%XV2QB$n;wZA=8`9u(TF4# z5ayLa`hxdLq8)ep-iG5e<$Fz1J7ZCPn+gF#gl$-IoyMGxL_qm-GUvRCZb|CIo_K1v zxe$0N0$QjP$ZyD4c$cZZhz5YKxJ$;X@V3VD0b;?N54w!{*}W(ArGMC{6N={OZH~L3 z^6=7-@8zfRMGK%ZeJo$aDS`Q&ux~CTURXOw%TemJUPwIa-G&9tO-BK#z}xdck6YM% mJ({+WgowP`yS-ieC%^!A3;4sy^DuS*0000WdP)tq*3zs|Z&CEOJ-LE$j1eYwKyO_`CTQD7HOH)(mVHk#Kodnhe-hk;sX)}S7 zP=b3?kT|TB0OX#)KLhNw@yKInOi$EJ;HK>0EH+`7fkeOV9B>4<+X+1Z&{*Cmlmlze zKel!G4f+Fm0zLitWM@`}03}i=h8oApfnWuRrf0WBYz%YEoLWX^-5c)%Y^WR@9=82>}=00q|ad=h%w-Aka~B8@TgEEq-W8RLSy~y68UJ zwmxv|T+I2hAy}G_7&AnB*!8{vicAyon^OMwKnaPwq-%L48MrgV9%Q-W3hrmDUupP= zG55X~k^;-5VZ|IcOE^FFrT>gBQpylm3ocpy6R3+o(Ld_+mjD9*bEGOI9BBY100000 LNkvXXu0mjfOT}Zk literal 0 HcmV?d00001 diff --git a/res/hw/wifi6+-medium.png b/res/hw/wifi6+-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..bb9d50dd28adb1737cc04758f0425a18753d6133 GIT binary patch literal 712 zcmV;(0yq7MP)0kpcZ!nNLlt7B{9C~i~ zynQiwCNEw5;N!dZzWeSu_xtr;5d3Ee-C!^nEW*^FB`x)!Ct(;i^CU1Q@B&N>rA-7* zLRCELfy7C!1R(1I{|2x<CN@6Od@+odb>lZ`-9O09uwe#>#;w zJ7*OQe?oslub|gAA7A&g5THZ~MN9Ltav&H%qUhOW5$nKQGN^vbCccheafIWkKE^tR=8SBiqQ@nF+J>YIzBe1}0T5HReOp~(X zR3QMA=N>5+oRC;WME-O_(})(mbLfo*OVZrFj(I($3IciH3c#I|YGFRC$rDbFY%4A( z^IcC!REAwI6fxEs=HRy}4q!-rFdu9MU@yTd@X!a_3_- zA&CW;rwSo;!P}DP#{HpR!)c1*{YtKO!lXQ&2?0ZdWw_!hjWM5y01k}Q9p9-TMdmSk z!U4M}fZFCAhGaesJQD#NMBv>~^)J~5U_H|x(FkxJw&g8%O#@47Omz8bmapM`()L`< z8#X>Qw%UGXnbw$q;=&SoSlY^+4j0mvkksB=hxv ui#5z=O*e7-MlfrcX~I3qqvR#G0t^7U4*89EJ|VRL0000d`ag%{zc2{>hZ)e`jo}%c9C3b`5a=8uDh3;x;4n2$Gcu*#QO*yZ^971VRIVYe5 zzs&*StW*GyS91Oh&OVrkyo0tGiLwDam+jx?CXQ2p=#|X@2Y{!&VkF?St!QN8z{dNp z9Swg%e?c#yvzw!XMIHc@hEg%qye$qyYd|zT+Y+!T%!HhJ2+XE8{|IM8ao_^xtw0S- zwImrUqbXeg42k_6K(6ma@J>z2xi_wQ?<97DO+Se{r;6SfY8_ReJJ8nvvbQP%%zuOB z2*-0%nvp%ZKX#E&h^JD@2+r!T&*a45D3r+~EBOre|p=m_h-ad>*k0ohNe~kHYpb8>I;1Z6ffodTiUMdn!PHZc#iutZ5 zq$zR$;1OmX6{2(7}1>jja?yHR(HiG_?vn8sKGS$B04$jEb(hg9x%NVR?l&7%Ld&$?v3g6Q z3(X08OaE_Dz>Vv0Jj(DKTk!+{x@vBHci*VR4=soyEU)X*2@2b`K5*<>o9lT~@E{>E zXNpFyyU9_*G$Fq!g}(=KNW~*v%`3^s-68fO3(gCKe@FSf*JSj)kYw0q5EtaYE#ds! v=l*B(n34y;M)bt;A3#|Evi_qU{1#vU5i}|SF|;p&00000NkvXXu0mjfC&Ez? literal 0 HcmV?d00001 diff --git a/res/hw/wifi6+-none.png b/res/hw/wifi6+-none.png new file mode 100644 index 0000000000000000000000000000000000000000..fa20a4c21199767fca8635b068559eb7f5d5747e GIT binary patch literal 690 zcmV;j0!{siP)h48i-56+LYgZQ&=esMF}}gKGOl|j=XtSl zVA#FQW#{diH?!w?e{8;)OeT{R3|06lmqzew-}f6y0$615G=>AXi~)lSpbCB)0mOBp z0AM=|eurSEijhNjNtj3)zy`;QkKFiv01&mLHDCb9X`h4%1TB$`nKZC)a$Dx~8U6zA z!3VELTjMAI#Fm+gCZ9{xfHwt1&Ke5@*2mBlO??Mu(Rw~ZuqHL|h+&_g8md|*}~^aD0Mr*^K$UI(rNrQmt^E`Y3uF(CP0mU2Ykkt$73 zpWL1sCP;CgF4jV@C5&qfZh#c9E_%Dce+HTXbK0r|X7DQ49yzY6CZ*?2lK^npw@xh> z6JTWmdGCa(5v^GJ2pcsiNp<>TJRdu%z#{`a1nxVkMf9*uCXCf}D^64SrY8g}!zU{h z2Ux4AjrSY`NJ<_=54r;It^_ZWg(iOM`f(dc2mlR4)Zei{b#SsJBf|L}sOm~a4*AIcGEYW4Hl1!EXlWSgHp-pBU8knEp{_WqAvNr(+ Y0J1apw@37?)&Kwi07*qoM6N<$f*(61SpWb4 literal 0 HcmV?d00001 diff --git a/res/hw/wifi6-full-pwd.png b/res/hw/wifi6-full-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..427b721e730fe2c2acc8dbbddec10207cf96ae7b GIT binary patch literal 762 zcmVfL7?U+gV29vsJPzUVToPOerTA|R5{pI&z&;TC z4q!i3M2_HzM51W|dwk9xb7RaBB&JPkz!H$%t|SrwO&ATY23n^#BRt+RGTL(ZAGplV z|DN<#!6F3c$XO^-0GTEM0-M0i$u~dXv**aPoZmBmJ-ECpebliOK7s!R=?T13NdUmP z=e_NGa}3{ycd;`gj`w#Q8^A?3g_<9;!mgP5LANCkA>z3q| zh!4P-CnBPWUz~5Pt6OoK>}*dE(8ZhYxAHD2`mEY?m5J)d@N-=OAR*wF#E~>iO+_9L zsN}e2B?$O80G&G|QjE=>97YbXOgbwO(1kC|3~|CpC7zz}$=46c2wXdhWZX#>SOG$2 z$X@VB9^b#OYDCt?y6*AeekeFAKq6%O!pgJe^AX@K4!PLWNsZ{@BX<`&QMzF3Eu4Ok z=yie!lnC-Huo5I)LCwnqf{&O}SK+pSbv(qpIWIeotE{LJBoCXRs+=)UQ; zq(Bz#0r)sz-h*~PpwDWWGh#7Ye6C5 zRxMrIGNcbwS?q|e|THY0DrtBnO4W*3;+NC07*qoM6N<$f|2B0NB{r; literal 0 HcmV?d00001 diff --git a/res/hw/wifi6-full.png b/res/hw/wifi6-full.png new file mode 100644 index 0000000000000000000000000000000000000000..a4bcdcea9ab739e80acc79e184f078e6df1a5d02 GIT binary patch literal 690 zcmV;j0!{siP)S4%fxSHK1^;tPC!Sa4+Xh&+D8?cd})J={x70oo;95 z-g)o7H=8W`&*uDKwOZ}s(T8{RY6+h?=ZYo?JQjEhj{%&|Yk`wc5C2<&#H>*Quzi95 z0N5uJk=O8?k!YI0Gd;iDB0Z)LZT`&>=%@vKP26j%r4e@xQ$XJ`hf5AC_ z{`+`ukt{-h4ug#{15j8JAh1e29M^n=PwyhL6Z|~|*k9+^=%bGH;OFq4Abk$+R1yI2 zJTh(tYu>>R;9Z=|(J_9icSmsQrZn^0s<11iFPeh4ns^G}r76v0kr{4!e1I{9Zv-A{ z%eF6)-uXkmg_!9umh;$W|%xLQp1P^Tld?VTo_F*(8EJ<@JF5FYZynB*g zDLw!fk%&+epEciG*S6x8*!iA7(AlF8-#KQAUYRysWmNqber78GBnW;f9HwF7D)M+J zCAVu<0>R${=qw=7!@KFN%P0Wmq|q7yUBtqy5a)_i9_a}$WBp(qfvaGXLMJH_D}ZE9 z)C+FNWBv20M&xa5>mCaaQo&ULjF9gOi^!gLM?j|-XtPO>8tUSOPS*~ibfMN;2K^wn z`y>%qBgj!=B`{q{&C4Z%50%qU;l6=&BE+IOUw0i>Sy?4WT{b7Ca>oDk!-1NBj}qq% zw(>=PI22^5a-D9xS54yWKy;nD?b_W(6S^ldf3a-+SIt%h_h1j!l>HH4 Y0LCWt0l+@|sQ>@~07*qoM6N<$f=i}0DF6Tf literal 0 HcmV?d00001 diff --git a/res/hw/wifi6-high-pwd.png b/res/hw/wifi6-high-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..9458bb970cc7fbf130a12eb92232b0dbd47e9394 GIT binary patch literal 803 zcmV+;1Kj+HP)C$1kP$m9?xTG|l8>FLi7lVWZG@O*?3XmKZU?~Xm9(o%M z>$C6da7x2SqrE%t&U-uab~dS0uGj+iFqur&F>J!?`ZR|3gCK~@B(N&*H4Hm&j;X*& zsEOakAkirNG)>`YVvcCp|C%L$oIJf9JqT1i7l1G+6SfkhYJXR>lkjps|L8L`TXq`%Zzzn zu;$ZL9{|uZmEZutV{+H{`rNVnlfATu;12>wudVFpfvob++G$J*sGM(`H^I`T-=nEt;mBM+E> zhboITYUFiRnz5#-3~p<*#<@>yHl}VvFW89}B-T#ig2E%2=Mwe>d91%THFrjgwt3#e z!be5HX`IV7c-&PP{Fw7Ipk3@JSjQ_3b@5oc3%x9{JYB3w%o=7gvT+I#m?6l+f+vCH zD%8A!g5WLH)K|D`-SrG&nKjS5jAmJqCrFU2i=;a_ZS|I>E;MJn;(N=KfPotT-YYQg zk9I)7wey}b7OKS$EQ!h``g|mjWf};y^aqap7V-O{C73HnyeB%YT>})FmToOA(g*S^ z^h8(p+ci==bbniV-_xaz;fz`z>vh+jb924tSsYwa15?e))qdgsjQ*u9LSUtG#rB^- hSppyCxTG{44U*~H#ULR84JV~>0aD@uECt29C%uh^ z^__h_>q3)}M!WXy&U-uaX74JM|7^}r#^doShD~@we~sWh=Ulf;0!sp4#t_3f4g^j@ zO*|WcM6Xl=u!jQw02}rDE#H!opAo>%-2{kp1%u?{X1F*g{ zkYLynsUn?Fm!E?Sh5ZdgeoZCd!TlU0wp0#lpOx<4J|F;YVOW7z4RBTK`RoqMjCJ3! z=j+5h0HEh8!2y6rrZsm(W`Ew}1B`_33f$TdeheZ*OQ6YmtNy+dHl>3E1PXTph*YHu zON5%}gT%lE7tw04pV?@$B^4}^E;f`g?}^rbpk5HTn?i(Td7zSbdvCAeWu4gH2?Xuk zeD#TQmT22t)3b~%eFQ%QnNvf8;E$viX5hrlNFPu8TBj^x1b+#j!+=Dc>Hp_t6aW*j zrLtI~M&72S8GD+_;I>9I2!W8!TX1Un9bbaj$$PT`Tvo`ii#9{cZ4tj&mVWaclh z@o`>o8rO0UraLM_7;}0Cbc$HP+Cgckizhl=Eyxnf)4`s^>|rJ&8z&cm3_-SXb^^+*6r!S_|uP~raSZln-Pt@*5%QI;jM1PPK2k&LI+yt2TR8pB&T*Tq-zMN5H6WQ%_l z&j}m~GEuvB=8jd6cr6fJqv&<*7SV*>6SSSIJ}+AXS0}h@ySB3IuK)u8%@O-fU)mko P00000NkvXXu0mjfBf3fX literal 0 HcmV?d00001 diff --git a/res/hw/wifi6-low-pwd.png b/res/hw/wifi6-low-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..9b647b416290a5db6d5a493476fab8412d107f16 GIT binary patch literal 806 zcmV+>1KIqEP)?cL1GW7)wsA0#mi-hfp?R z4xD4yCa5Bn&?L_RMiToJK(1B-Fv`s{Kx{J{nvUu2YaM z>^b?X4?xi?hTs5-XR0*!2xdR8`A{Z+9#grdDflyhOf`V6#GCOqm9WJ;I0t~j)c_!M z=7k0#IWYo=*tZ%{%i1sA=t)T`s~*tZHL`^9n(HO;E+BZ~03u;|&OL@c=vKT&i%m}u zrNet~zW$&qAF8;fkyv;Jj&ubuS2k&Ycwp?OxUWS}wSp0tGy5Y8L{r><<1n%m%c6~h z1d3g5ri&Zgvu^E^wVz!5pqhYu(-I;APF%4y#Bah8!E{t}YEtG=Rsa!QzpQ)h!Yo8E z9|5FIPu3_i`99zXLz}|JR%lw}rj;8Lh9}k?kCoHVaxwu(NXU(hm2hfzywn>}it>)* z5SlY7h3-3m$%%NB=QLbJS7{09=y(x(OP2?lAvxmvMMAJFFDO3Fux^<<0ASjApZAS` zEq^Dp>Wc70NCdRN52c@eclk*ishJoYi`h(%iZBMtUgqV0@czr-I!Mj8Xu_Z zyT|9ix?5bDyQTF$E>wmNFUaZnj?-OO-&59`<2{WA{w;H0hY8oSW1sua=wDJE02S|! k=|6z70A&4-82lDs0670F%iMM)%>V!Z07*qoM6N<$g07QkU;qFB literal 0 HcmV?d00001 diff --git a/res/hw/wifi6-low.png b/res/hw/wifi6-low.png new file mode 100644 index 0000000000000000000000000000000000000000..5217b8dce2f190e6225c2da25878dfede940876d GIT binary patch literal 755 zcmVHQt@JxpLl>>q6y`m%dC!P zok~S~ZTFvJ LHlRWoGEiw+Uaa5*kmt860>W+^A=Mr^d5j?+dd@Y^ zc;|>jcK~yBSb!)n&>oO>8<`C)6023{0C9g}NJJukQ5$Y$7%270OoQE@O4$$FQXQ#S*8=t?8K||Z3ys94ioEe*RJ(;OG+sK@Ev75x2A|LNTl>c9l*y0YW?G#LIMEaz-5808WrOL~zhr+ODfJLnjhz))ud)|P^epv#}0|G8*JqBpZHoH(B zu&b%S1HNunQQ#d3_>wHBqEwJ0`^2}eQGK^60av9Cp?N5ssr52UnvQ91U+^_OyH>+N?-rWKO>Ie lSe?0$=;2saO5m>m0|0sLBMH_!jr9Nk002ovPDHLkV1hL(Pt*Va literal 0 HcmV?d00001 diff --git a/res/hw/wifi6-medium-pwd.png b/res/hw/wifi6-medium-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..c157f572606fd99da5db7f539f577ecbb455b210 GIT binary patch literal 812 zcmV+{1JnG8P)LmUJW^}Au79lPYa4-sjbw~qQ-q1oC&;}yL@5pz{ z>Gi#r_g;N;@xbBc{yOJ7Kktj@T{9W`W3gB?Fl>QaT$+LV#+Y7_0LoOpf?)?NV@Tx! zsExfDK=ca*0D3^>U!m-^^2lRwAf6~1z#Wd=i`Wgek200%I$A}*k(91eWAP0tw1QKU|0oLmEtPT%U)B$jC04h zbM#jqfTAZ1!2uM{RB7%J%zjq$p-cd+QMslm_#=Q!HGsCnoAI}mu*EzW0YL6*0FWy4 zLIaVU7yv|QoJQ1e?u$1%Qj*H5n{;=DEMYw7c}ctz2tJJfB4N4DJC@$+R=h%sZBG!T z{Tt6ef2S*Nskkm9vG5o?)D^&7*`xvDfw5WQz6L>6az>oNH>f-)OkCCHT4&9AO zpfJkKba9P$R-Jot_Cr)ZD90e*bcBe2qo~+g;-2u3VAfT0s#E5>v;c;5{hzwmE=)rN z^BzE&^kjuHDD1MyF(QARMXAMDOLSil<+J)7JN|CQx zx~5C>QcmN2C+S*FNy-yGvEP=?dYG#WV`BY^(?=3>Wxajgmr>7LF$XRZ&W(NMe@6e3 qvH&P~*G&Hb6a^sd|A^ji0R{j^H7saq1{}ly0000TYxZ literal 0 HcmV?d00001 diff --git a/res/hw/wifi6-medium.png b/res/hw/wifi6-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..a44f85a7cb322883071a39924807f57ba47d5658 GIT binary patch literal 742 zcmVe$Wx32}4Oty^`gQ}HioM#s9j6zU>@4n{$+4rw4uh8AkTHV`qMBj?K3 z+n4mc&=;3}@NwUdd%tt;_v_wF=0A(`52MkjjA0#G)u#cp>6|-AlR#GBa~SGSjy-{s z&>H>?K%$u{0mzELzX9y6@yIi1jh;xGz$1KpgOYb%1KqesZaEz^z{STt27`n`y zSaWox4*=-7La+_sfho;Rk$IVP`v4=M2LiX!hkgQ)z9q26cq{(C64sRm9T1qh8bBnk zys$)=6D^SFxu6l1gZ1=AgC%Kpbx-cjNg2iq&1d4B!0>4V5r$=1YrO2+Ry?O2`<_71 z=H1s{e#({iO(pkVF9A`n4{z&qx`GD%(m zFjn(&F~MnUEBJh74z6khF_zg literal 0 HcmV?d00001 diff --git a/res/hw/wifi6-none-pwd.png b/res/hw/wifi6-none-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..6c2973e501869a294a439cbb407f67c00f9c97e8 GIT binary patch literal 789 zcmV+w1M2*VP)t6EN}#o1qxB#h__+9 z-qU?JhnoxxtGnBsc{}rFcIA2ZEWY`%TCF-5cA-!BY5^VlzCWrGK%LIFFuaD!n9#WZ z8sOUkAjXvffV`sf?{M~BMdS^%FOjGkz;lknzq#@K1R##8=70epyS#rVCvTM2RMt! zfeQ?K1XZRILh2G=gxF^QGT#WmI5)omVvp(2a>8(58-s8VU}!=c%5j79m?dTQj%&{j~MQNDrvmtd?DT?3{M?EBrPww#>hwAinq99(-Xw$ z_`$nxKN-r$Dz8~0J3NQRx&p|R9d1A(F!3|O*CD8&WCoUGe{6vWCHyxIBg?T&+D;)* z;tDfE+~S(1wNB=KcJ+gL3i2Jxh!{9^<<@5QxXh!6SAC^hMy1d}{B*(lpcL2b&^C9mWZMOJG zgYfRq=bI#%Qvl$A7aaYrFYk+{U?U;%JZHHk%28XjbS*cYYb7@ySfp#%?HVDzGIvYs zeNw6nQ*v#>8|gY%$9tBH{5x{sRFZ|EwUrG@GHSeC~KY*$LBlGQ+0m7gR=oS zaE0NJpo&yNNL>Jo5c>i^rYiwh=k^;Q4w()uUm5P1H3$a*hAn7KIj(WN%rzy=Sa*p% z!@v3f933$QJvg4L((Dq<>%7N@GXeCJ&b3USrvNh10QM!{%)hOK1M=Vs018(FfYitf z4MaFG0EpN)ji}-5muMVINvcylVz>jUq;baeLcD7j9(#aDT6Vd|$_L$wH+W*(6U1r% z;hQf%7|O>guW2GDyo5%&0?3s;9zY^6GAZF}5L8ew0}HZ0azKO<{y#1w$FVGGClDz1 zg_$9)bI+EuPu70+^@C~x@;%3h7#R9;YlU;75y7-na|&tmBrAZ3q5oC)I)qt-U_Jpz zlaUN)GxNkfyyMowxP)X?F^nCXdi$7AKxwVg}=5(wGOSP2(y$4h+?r8sYSE};cU zDYEGSCO6_qp4)JhOw$PI>3C7)Uy5)7+RnJHc`q+oAs9(^_g8UF$1WkKEY~=F4h3>b}^-3@?r{{Y!v z$O0q;zDQ|1-1TXqtIf(=>KJ&QT@_#&=qMOM_#V+y5G4u-RFVQk{-fzDTRKNWe$ViC~6KcEngXjg#rH;trzY@X%fYf@h59JgX{y%zv zXoRBjiE{ujloWsz$WQ@faS=(`co6;O`QwGi;=82z;`x{u6+o7;G5U|8HJJaRjkx02KfE#74ndNnQhK7A66zqycgO)pG%vj%WZ<^>~yt wvMpHEz(~tt78(-uXa(H`sO1|#9Do1=00VlOW>+6u+W-In07*qoM6N<$f(T{U5dZ)H literal 0 HcmV?d00001 diff --git a/res/l/network-offline.svg b/res/l/network-offline.svg new file mode 100644 index 00000000..ca9dcea3 --- /dev/null +++ b/res/l/network-offline.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/res/l/network-online.png b/res/l/network-online.png new file mode 100644 index 0000000000000000000000000000000000000000..b8c5fe4a9e8f615f3797004ea6ac57d7e04df539 GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?3oArNM~bhqvg0|TRn zr;B4qL~HV&|Nrd|J1{L{+*)g3(Qxs^&ASiml`ku@xw*ORx&@T1KlcCsn+ORB36_6F zpEk$!&OGes|9k87C4Ulrev3;m;q9?XxWc%YPong|3T8K1gI5dz%`J^TkE@4m{hBK8 zdG*YR69Q$fITMR&-d*upTEWiolOv!9(acFqW=6l^PM)OaqMsi z+-$RP-E)SHdG|veIjoa(u{?0&sB=SOqax##Iu|kViBniR4fj<3t6-cpjh}~yhx^>S zn;#pK*d2814*-Gxg{3PPrYsZTuw;6}5D}2XJpbNav5JDni|pEkjG6oGxDU_%YTsGb z*wpsV)lM;R4j+T7EccYV4c-^;@2^(@`f=L-s$cyN|4LsH`VADZ0*b^v2zJhq;z&qH w&`>y8#@5D}{{77j2ak&x&r1?yfmSjwFjeGE;MZZD01RjbPgg&ebxsLQ0K}84g#Z8m literal 0 HcmV?d00001 diff --git a/res/l/network-online.svg b/res/l/network-online.svg new file mode 100644 index 00000000..7f10bfdf --- /dev/null +++ b/res/l/network-online.svg @@ -0,0 +1 @@ +gnome-dev-ethernet32 \ No newline at end of file diff --git a/res/s/conning-a/1.png b/res/s/conning-a/1.png new file mode 100644 index 0000000000000000000000000000000000000000..cd0fafa97f8b8adf20fafd14b3cba792d1097270 GIT binary patch literal 351 zcmV-l0igbgP)k*&3LlzRM;| zVGsIxa;SY{#pP4002ovPDHLkV1hWclYRgI literal 0 HcmV?d00001 diff --git a/res/s/conning-a/2.png b/res/s/conning-a/2.png new file mode 100644 index 0000000000000000000000000000000000000000..38b549a378e574fa0daaf90a0c049b80075a7a12 GIT binary patch literal 3170 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 IM6N<$f>E6dbN~PV literal 0 HcmV?d00001 diff --git a/res/s/conning-a/3.png b/res/s/conning-a/3.png new file mode 100644 index 0000000000000000000000000000000000000000..c002abd0edea76da5e0341be55a1e2f86bca5550 GIT binary patch literal 349 zcmV-j0iyniP)7+J|tQE5Fz16C93sCnNRunsJy9tPULw(t1h z8n6!>0<8qB3!DKbz*Y>L08^k392B4qI|WvN2jDseegNYDZCdUQxVP_4fb9ZrGsXSo zV6`L-B^^r|MDlx*9wl8$>W29k=vLFPMo!M5o!HsbIZO^WhdV$oIPe6V7a$kFX0Ufv z%PFoJ*}q>oufus8ow>4L>@7QN!<)hBUpn-$ci`Ffmu;2mupVGHz?bC~s&u%NgOv)u v!&l(e-sN`{EWEOtpO>mo4E!xVld6Cpx2UO~b0A*x00000NkvXXu0mjfv}lvu literal 0 HcmV?d00001 diff --git a/res/s/conning-a/4.png b/res/s/conning-a/4.png new file mode 100644 index 0000000000000000000000000000000000000000..ec398d051033ea4f75b7897ef0c74ff1ab0d33c0 GIT binary patch literal 3180 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 S?1W_i0000 literal 0 HcmV?d00001 diff --git a/res/s/conning-a/5.png b/res/s/conning-a/5.png new file mode 100644 index 0000000000000000000000000000000000000000..df58230e81f2fb0c4cb45211685c418c97754a4c GIT binary patch literal 355 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 LNkvXXu0mjfo@*3G literal 0 HcmV?d00001 diff --git a/res/s/conning-a/7.png b/res/s/conning-a/7.png new file mode 100644 index 0000000000000000000000000000000000000000..1dcfb6d0d6df2054840e31524d523b4f739ae713 GIT binary patch literal 359 zcmV-t0hs=YP)oTKI)Oh^}+2C_i_hk-R<8JPMn zP+_aUDDVcnyO*N-tw4s;(X7s=X(5--M z1GABLpGrAP_>~&?E2lP`1okY=SPh_^=oO}{r~U|7wEM%TzX9w5TfhO(FWXRI#|FCv zPR-x9Qe6Ufz#Py6#!|ougDn_r2Y9yLl9lS!HVdK6BH(%gR-}Feo&TNDl&$I_(h-sI zz{~5zD)yTqvLYh2+zUhb2F^>iD$+Y}9X7oG@{Q}T13$aca<;M~{M`Tm002ovPDHLk FV1jhAnP>n2 literal 0 HcmV?d00001 diff --git a/res/s/conning-a/8.png b/res/s/conning-a/8.png new file mode 100644 index 0000000000000000000000000000000000000000..01785950998ff739ce9eb250988eae6dad778749 GIT binary patch literal 3201 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 literal 0 HcmV?d00001 diff --git a/res/s/conning-b/1.png b/res/s/conning-b/1.png new file mode 100644 index 0000000000000000000000000000000000000000..31773e94819154003f380009569d191873028b30 GIT binary patch literal 7242 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 CYfy^- literal 0 HcmV?d00001 diff --git a/res/s/conning-b/10.png b/res/s/conning-b/10.png new file mode 100644 index 0000000000000000000000000000000000000000..41e3c140feaaa1d2c49a032dfe591b2c3a350555 GIT binary patch literal 7873 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 Zt-5eh2i^My1$|@#D0Z&4W!B*v{~NsvXjT9K literal 0 HcmV?d00001 diff --git a/res/s/conning-b/11.png b/res/s/conning-b/11.png new file mode 100644 index 0000000000000000000000000000000000000000..1892047880a7f5a71afee58903360d17f7d0776d GIT binary patch literal 7879 zcmeHMXIN8Nw+^8SNLMVB7(ft7ql6@a^b(~Bh{{AH<%9$R2}$UJp^tz%7Qg{PkdZ1P zB8p(iNRcXH1C*+G#e#rkP@4Auj>FvRo$s6Hx!=DbPXcG1cfEV9wb#3L5)Qh#IINUj zCk=r>RuUa;-NE0!3r{Ht@b&4*FdPH|eI4n!o9|8zgK@ZQI+F#!_z@fc281)|5J>pr zvCJQiz1b+6G`PnuL!h0|z(r@4zJaxM%7;eP&XBk+I;awV4+#Sr@saN6&*=GSUXlf8 zM~m9AtQW_44e8tTNtnrSA7D!5{@bba3LUeX&GpxX3M=gKn&m#ztGIKK!^821cSTGD zB+ePyUKx&Vd3Wu<=J1ZqpHFzy%BBY=lKOIO%#k)@?tS^Bu-bY;@<%lvu_S|#L@lz0 zwNlu)pV-BqV<|hjj_ZAJe3vmIuzIezLh0SRh$EP$7h>17KVxLZdvE%&TV6!AJV#`z zmJ3?KR2>ILxOKyRx48}t6*3$5oqd-(g0Jhu->E3uq*d^_v^a+qc{28C?1{D*M8Vpi z#=g+a42=Mm z+nTg~v}_MExHOkBR&lhRr(10J>zd(o&C3&2je|_`oy+dFUE`z;NS}~H=+##r+Q?Wk zeERshpSqBnfgdXu8v8I)Hgxd+t=k!CyM~k4lLk{n(ip z1H6B6DZ~BYxk8OgjE-X-k4{xR4{6@1yuC1^qrH50$mGbY-j4p%?pt;o3ICSHluUR3 z=WfHrx4aT#5=Vw?4)aHk%5Ry~a|qNsoso69?_9*@^3b>Uf}K9}l}Dkj%SnssHaglk zXkhoB*rDqm*y`lrmy3yb}L9-$9CV|8RY+yL{;(%@7o8FbXe0t%f8hj zD#j>!*CrjGk=0eZ*JN9-wR%NSCLk!5CnYLfYI~!P&gcYL&1Ke1S{#*;=|5nFg!EtO zyJ1saF?jXz!JInIb7SdHO}>7VP+hC`z?K-<=w*+&FDqLbH03|(VrN(89>{TtrC{nH z5Rn9?jg1@8#^(372b=v=^igxidP}v&eLn=GdJ=l9Qf!i5(X2chc9gj#NauSo9{xIV z55=H3x|7tzyW`?DQ+G~j?sI1-KKnJ;VxT{&@4D!0$2yOW zT#V&I1FdYA6`yaVC`8&R>`3TJt!Z$I_EIsLP}?^(G5HbMQgy9!4nLiKrUi1^(yX3e z5BJf{eSC?3XQlz;byVdk@9BDBCi2M$?t3As#c}sE4eD^ zs_ZP-e_#Mee81YS+RJY&>Yb{6%HwxR-AuU+hr|u66Ci6tJa@z=YL~CuwDIDl)9)s3 ztKW1){!H45=C)Hs^|f!y&r9B{Guf{EEXZA3Tx=g}hPp;-3|gKPctG<>=xZ@4Gtph% zw{Lcd6>Z#yq z$VhQn`$`T+D(%FksE%j-!*2)X&F{Y*mxShVBkOHcZbKjnx=e5k?RIt|P}zY@yf5yH*ki@XHz?*%go4qJrq1I*#Oo!wwIY%TyZMj9j0 z2)l4*s3Cl-G|Y@kqZ8b1?Y~lhSLSdApU)wnP+?(V$S@-$o9lKoujJres$Do}{JOxx^&&lw zPeT#G3u7=J5P&j7qj3ndA;J)kTJ8_tb$0&d9mM-uMX;Wza54vlL84KCfq(Mg@$EwY zSnsw}+ zjsmk<41M{c4n$|SZ$1nA;KvN)EP5=Umm_J^Z#Yf}m$itYQBeR32m~|Xfy|gc;X(gz z4&O~?k$kbz1Y0&WWPycfYYqo3NE(wyFfla2V2ttB_O{lx7>pgx+San3~WG5m*eFhA=iXF+`97oH2q5_OuBdi!}joz%mL~ zE)&!Pnf1L$uvkM=GJr-Sa9~UbG|m{0pwMYFgb5aBY-&oOi3WGJops`pq#>CVZZH!*#?FDdoV5={1 zV$etoZqc1aCD?BJJsW}z7a;T5Tu(NeWe#760Jh+|C?J^GS7{KO z*i`a@gciX7Z9!dM^<_==LoHsKq5cZ|znDB2?69DJkLM5QHx?@{Ka9-{aOJvE`~fQe zuX(-){>J1EPBtE&8$tXRN&OF;*-|Syg1&5S#Ip50fZ(O8rSZdJE|v-gTa-M3OkJp= zIh;oh1(v!1tmD!xDuW#42Y|Ent1f=GGr!ZWMy3=~I@N@Vpcr8(2xBVvf~SxH1kM;B zlW|~Um;(644gSW?W7GLzWG-Ok2MPz=XHd6``wY`t?6OUNMu#x~Fv!hl90BaDrR{~8 zp%%vH@9CK>%m-&@!tw%qt%)tbWrEq+bJ>B5xi3yC0&SUSmreuF0xFNgVDs5L2Ae~m zQvj-w5tV|V(ibW*{wders=(g@|I=Onl8}5`a!LR3=^qFJ z-(2_}ywPq_ z_gLH)cnD9$ocD%u*S&nLR+EYAe?aU=;g~48Nl1)vFA4x(uPXR`F_l;=z;I845{EPi z_Ye~kMjo1dD8LUTr23A<36#*pxcJ@)ZAt15$Homh=tgVoUK-R5VqTu%UXhoz*lfP42JNc5Cgno6*sO_gcXb#+`)*LzJ@&RyA8ylT>l zDT=YipdLd-9$F#!n_k(Phl8rcFHs)bQj0$Bkwim(?y!~f^Z=tgpcOw7+6le!~0P;oMjbk8C7K=^cS+d5ma7IgpN z`3*x=t&B)P1!M{mM!5E(S{U`bXEEoUaJSXNrLsUyEa=MSo0}2dB#O?k8sn2 zV2ksWI+WQh0+hDz{?~H9ZOXeT(u4Z4Uot4lu5AbOj1f%UtYtFuhFW;ey~bq0YfoF9 z{*u`hL+ggdh1nxYd!J%va^hFLy={^N%*G2>3imlWWPkD*awygOElSUF=2eHFiZclJ zf%?d)iM(|_IElcD4nppT2Z?NbIpLFG`TyzwJuRP&v`Cmmi;~4HPk` zmU7|S+9TdZVrK1!@U^|=pHzj98^uc{^@WKw$1A_=g$+~>1gD+BCn)x$JoxarF7|Yx znZ#~2Ps9b$aGeRAKGq(6F^n=NQMx=hZPYWrur$}UDhH;ZlOu8`_1Pv z$+W@aYnnhy0^x0gIJ7sfUxYy5Pl$|{(yOt;dO z3AerR#b9T1GU;kwO@G|OhbS-07=@~4=)@V>tN99AJ4Ma9yUgFkYQ?Jv{K#J28j@R4 z@Dy56btPohr{c<0cX8>%QpIk{mo^GclQUGeH(V&ct(<|*N-xjk${FV84KZxx-0o+T zo;;k?E9=$tC@08kuW}glsEk^zSly*hS7f&(V$i$ysXA4PA4k=cS~ILkOg8+d2cv{1k{A3xsti40mz5&0nMm6bi~j&5Uf` z7HxAj926)jXo}9~mB!%cnYARL^LC#fU;3P*S<0hir{w7Om|boDw~d zQg%m5&8yI(R7CpZ;TPv%ci&V8zczC+{n#eR^i|pSRNJ?Oyy-yEjk^!m!hPk!jgQ6L zVc74N{REk4!>!;vk2|-1nov0Yo;##ATOAjsZ=sfvjZb1ep39(f`!1LE+<@&;?-D8O zq&t=L52UrnDNR^6R_;l*b@hfOak@`N9kjm-rMGb7kmt&>E4}Z=)vs3MZ)Qk_5k9{2t* z)l0h5Q7~R0J#7phJTvIGD(}EqVf3YX*pv8Pg{>0TAos6CT1;$py5AF5G~WARhxiRY zs|P0g6}0;DStCfNs3B3q$9f-K)hi437dX~QRps5V`!%L)e$dLP?DO^9i#d8(ThG<| z85AdP#%%GplWsNktoUuc0r)>a%~oNu=N?x^0!p;=rvRA}M5!hu=CGoKpSo&}^XGZG zl;bPm#?=0lm9F*Y?9*uvNVIJ)Iwun48Q9r%pY(%9M!fv z#Oi=2g|qX<&Y_~0l}KYQw=V=dOA@VSvb+34t9o?u$<0Ozd?b%e6I~UvY*1mE74O)+Q+5i9m literal 0 HcmV?d00001 diff --git a/res/s/conning-b/12.png b/res/s/conning-b/12.png new file mode 100644 index 0000000000000000000000000000000000000000..f5515e95fe93e6ecf95a4939d86fa3bd0afc70de GIT binary patch literal 7847 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 EUmuNYuK)l5 literal 0 HcmV?d00001 diff --git a/res/s/conning-b/2.png b/res/s/conning-b/2.png new file mode 100644 index 0000000000000000000000000000000000000000..e227bb8c4f29f1135745b5e344ffb7c5c36d76c8 GIT binary patch literal 7844 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 literal 0 HcmV?d00001 diff --git a/res/s/conning-b/3.png b/res/s/conning-b/3.png new file mode 100644 index 0000000000000000000000000000000000000000..a1ad1bb108b850c090fc81752e6a0e5a39eec611 GIT binary patch literal 7820 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 literal 0 HcmV?d00001 diff --git a/res/s/conning-b/7.png b/res/s/conning-b/7.png new file mode 100644 index 0000000000000000000000000000000000000000..9f5e23fe712336ec284373b0d460c0b3667da1b1 GIT binary patch literal 7340 zcmeHLeLT}^|DO>>=7)-^**}(_;8!CMv`ve9*YzR`D_jsM2h1DAQFt@uwk&c z$G`6n${IA7oA%o}T3(Xgwk9EPpnMQ{t9AZyt0zC1k;}67`_&oCE;_6CN^R7gH2G1q z%|_sTV`*0Yi!4!7uB*v<>`2@O@SS?wz&~@x79I)d?G6szAy=|KGoU^(b$w%S+<4J5 zf1S-|s`ym#lHmRBt)i!eTelTp$j2g26s+p)dB1d^TBWYyoc*KNhARzeAC?BorLBxk z)umgf>Qz2HgJ3zwlMw ztspatiliH{n$ExV3eR_kT@yMrol!OjIripAFSa2wDmY|~ucPDJ$`iTVgagSv$vL+Y z&?Sp`S33ht!*wFKwojSq3V|i|b4_%;j(XVbv(SqahoLt=I?MPYaAxy)3*mnrbh^Xm?IZQ_T0M<_Oj=&)<+7cb;=P zNBtf1hBwupmR-&!yepzbprI>rUnFU

_{-m9Di;h9Kjv?h>vX&t{ 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 literal 0 HcmV?d00001 diff --git a/res/s/conning-b/8.png b/res/s/conning-b/8.png new file mode 100644 index 0000000000000000000000000000000000000000..429fa4a57dfa6bda6500efa8701ee874279b1b3d GIT binary patch literal 7833 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 literal 0 HcmV?d00001 diff --git a/res/s/conning-b/9.png b/res/s/conning-b/9.png new file mode 100644 index 0000000000000000000000000000000000000000..95bf53e8a39e85c293f4e6cb828b7719d88a3f98 GIT binary patch literal 7867 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 ZntICyk1btu3jC}FAzM3Im0S4l`X8b*e~$nF literal 0 HcmV?d00001 diff --git a/res/s/conning-s/1.png b/res/s/conning-s/1.png new file mode 100644 index 0000000000000000000000000000000000000000..69747259f43ba4fcf59b7f9f99030b79195ce56a GIT binary patch literal 652 zcmV;70(1R|P)~~~Ie>;OS>h2uC!UN)nF!G1D*zYjvrN`C-Wo62SBM@0 zI7+C~2GIIRkyHZo`VK%@c_Wj&ZgQLB%Or19lX16cYJ4L=;}HO#1Do+=JhU;KGM6ZF zLq|k`>m-RnVt_V2;sC47w1$MwIcsSoiYXm;^D-`&&!+D|Q0f7wQ;B*Il)Ca={`XYh zD|UFz%eVpOy5`(we_%y`(wp7h%+q*+M3b)<+^8!B&iQ=Iy5THI3>i@{DtKIb6E0m> z#uZxp*$~@>N?5()5u3z_v1=9kzdJn2kVaFvJ3C=x#>lP zzI8TqM8b)K{|piel;cm}FLsJ`8XeT1O~`jV)^LK;yp0o! z6vqzPP76GWANa?mtJpVSkrMIs%wv}Nk{}$zC@qO^VX|R|&juN~^xh=CrOjnUG@ni1 zf}kWzu`g?*E0!FM`>Y4#*^VdUW#-itQA<@U5eMXHJW>^xNy(ozA!|2@h!a)2^|nO+tGly$(B*=_I||_xAhl1eW3lVHdy!fHMGnK3bLl3IHDf z;yg=NHk$Vm@Pkl*ZU9`{lBFyF%=0Ww8WEtycK~kHXHoV}YZGH7*9y@Afb)`fgvj(a zkJJ*N)z1KWO36!}rNut8xxYv9#%r=~ROgKf0fJ8ebh&YyXX(-gxJH+FWJp8A15afV zogLt^@S!QV2=_ivmr3F+*?53)jL0@2TdPdA5qcvj>(m5>j9Ed;4+g$VI`~N%lQ83B zfs2?Nxa04lP0X8!$JiU|Ut9wcPkPIh^aoF6W@20CEx^UlM^=PL-(znFJDe-vKKns= zew=nxw{sc$M}n^y<|lq%dtktqokOFg%sC}zQen)4HGva92;cDCwJseJsEEPs{B7Ie zWkbNYMSlGty!V5!(zg$2y$Jz4#(sUmVZXj%nMhK`jyRZVSZV?jwLrd29f3n7ejx-t z)gLRKUKS3)!BdmShw*@Pz*kcyE|EF-ObB8x&(hdotm{M%zSU97kV(EZ_>M+^r8ng3 z%nZH?0rr7GMMHb=3Fl6s$9|m$WL8i7AdGawH~_dAMkAf*bp&EAlnqfEZ*XsR=rNSn*)UavL1o{Njixgp+>$NqW_;0rS`+kVImv6o#rA@K*v3RUkoCHACK#;d&_xS&o-n4sLhny`9~inN7hGxqQI~Pz7)cpvXrf zBHH}BJ6RdNjiUak8_rV1IF~Op^|ut){+<6HY>%2Ncnq&# zU`do*;4!uf21%0}0VZtziBd@*6BVH1dg%Hz&~}Mlm!nlT$SpFA&l?g-F)=X3+ z?ALm=JgH9LnoAjWV^x-`ftS0OC?h_N)Ya!xu4W?s<*2LKf!WwXh~MI7eUtztK@=PgB;u$-6+%!ot*R-eC_H#ne*g+qRfXaQ zRN=vc!cbL1J-ysHRDlFl)x2i*@^Zbx2JUhdeaYqc*nKlQ`|)OTu#8HzRs_%ha1Ef& zN5_iK8-PXNdwZMAM+sD_H34b^XxWlwOaV*+-&>tTfFte#=%}A1*$aTBd^V$GTOoP_ z;4Y%h7{KT^MPdnX)K37)YBZ8$eQen5UnY5@n)G{&Q{x8#f)4={*>Mo~-qHp*CYLDE z=Rib(UX(;G(m{i-*uiQs9UxI;&Uze)d`!cOq`)P1+s&uzmgWF%sl=`8mS*z1{O_o? zT{c)J1#W`5ddzLs2SNgr-mLaPlHd^%1AgrgGgP3>_w>{SXGvno9Tj3aoO`ofMrnbo zFc>Ytc6hvk2aDA{KPQM`c0r?{g`S$j!T1@}|002ov JPDHLkV1i$K3jY8A literal 0 HcmV?d00001 diff --git a/res/s/conning-s/1x/1.png b/res/s/conning-s/1x/1.png new file mode 100644 index 0000000000000000000000000000000000000000..258bd7041c124bb6bc2d6a23017dda7ea6f8de73 GIT binary patch literal 354 zcmV-o0iFJdP)TldWz7K@i7(97$_vGV5@6x!{Y!fad|g_Zd)Cs47$y zZ_z#g9u(#tfL!hzijM?U)eM>25DH;wm}IiMng4&jx5k;oN!kYbfSygWu7M1gl|`PN zGQ5L0NgKes5x;?XS>!9TwSXQlFh`TJ$PeCukv}bxH(toD3Zj{fJT6Ys57kSgD~>!q zvYJ&Vp$-h&R&HIyy#Y_ai-hyes^gnhGY^QW`_Qsy7TN53cVw`(v#T3hHE|GHie>s`4V*=`+p4-@p`zGKxpjSs zxFs+YaUH$uoUhd15OHIRK33dgKGRv0@b3)#0(JUnF;aq^?f?J)07*qoM6N<$g1B#) ABme*a literal 0 HcmV?d00001 diff --git a/res/s/conning-s/1x/10.png b/res/s/conning-s/1x/10.png new file mode 100644 index 0000000000000000000000000000000000000000..88398b807950bbadcf6b8c4bb526f11dbcc647f3 GIT binary patch literal 355 zcmV-p0i6DcP)DlRZwuP!NPaz!D9`bLTEmlmp}d^mH^-R8({{R8+30 zs5n3lK$QcePeXTch>zYtkWj;(i>udw< zi^QCDZjUNp!B)G*-a$p;-s%0+-8Tn-CFl~69t#!7I<+H1Lf>1&EE$c}?xW&-|BohU zd;`mYabl@7iH(8BY7U?xV-^3b9NAq0z5%%?920V5p}iQ2*f}xA%F^ml-_E*Z2TlQB-iKoCVAhSJ5lf^Jt!4nRed^c;X3AXlJDl`2&# zPLczpPnX6A0L$(|1xTr=&`94a2#HuCMq1g{{QqaX^XH7c@FCs+cYr;wl-&X~un%KKEx83ins*UUE4e*YZ7%4a8jd^jZfQ(faYHK2sWQDNi%2nUNwH%fkhOKQc&h5*&Y6Fa&t8VT6{q6PR r$OV?pRWnU)X1M=ZroAfp>kNDXG|gQVyJ$F~00000NkvXXu0mjftxc7M literal 0 HcmV?d00001 diff --git a/res/s/conning-s/1x/12.png b/res/s/conning-s/1x/12.png new file mode 100644 index 0000000000000000000000000000000000000000..60b88e98108d9c79760a34ca42ec6db4f5f0ebbe GIT binary patch literal 364 zcmV-y0h9iTP)bQ>$(QK@>eKq6Trt;bte ztji(~|05!Sb$$TgT?4-WY|0|v>D@hmM*tHgT9!qAj076I(oFI4Om(Lutn4k~tn(lA zb3q+M5ua(#^kpi6Vn>YaiYE>oWYF$o4N`q1e{S*SDzw=8aif_Cl#h5NixM!mFrBk zO+S^DV#KoT(ASl00Zc{Am}l;}qyK@3S*Vm3#9ikxwX&o?X8?bB_;gc~N$O4j0000< KMNUMnLSTYvQJQ-I literal 0 HcmV?d00001 diff --git a/res/s/conning-s/1x/2.png b/res/s/conning-s/1x/2.png new file mode 100644 index 0000000000000000000000000000000000000000..45eedb2bd3bfb08d5774729b017cb52df375dfd3 GIT binary patch literal 376 zcmV-;0f+vHP)TQ!P&eK@ffIS;TA*oZHO?RaJ$;^8@$;^)Hq=)KxjvLKH&AM~dMm#jp@D9{@%vhGLrtA!6PD+><|`zt-z9u+C2aT+qTyn{+Ie zv`c~flpSbJK0JWC6BKApv7k9kCSv$;zOVCrq!_+L%sU0X0o;k0XCCLCsuK~zWX{1_ zB;${W8F5ro3@^I`L<}c!Dq^lg%qt7FO`Nl%)_EQ&N!vi1T;!~I|8tpoJ`can0Db|j Wpo6xiH*HS<0000LQ>#wIKomU;0#k9v)y-tG3bhKge({Up8{ijEDAfJ} z4ux7(g;|H`2au+9RXmbaC~}%RFr5I|!bzr`*E#n&2gu-jm;+b>;J?(<9zYGC>e{AG z86yGb!v%m-0QUg4UE4hA{tUnZz)FQ~y0+Qz%w!H!(PAVMoOf+=3*cP0*3O3_wOl0g zne$Hpb!)#>uITR$w)1jQ#Lcy-{QQ05(?|YBYGrkBEH)a3*3;IDdP6 zdCzr3EZ@zQolac@c N002ovPDHLkV1hZpoH+mh literal 0 HcmV?d00001 diff --git a/res/s/conning-s/1x/4.png b/res/s/conning-s/1x/4.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a9a98291b8672a790d8d7c08c04c36f58cdb31 GIT binary patch literal 405 zcmV;G0c!qDlc7$-P!xtgx+NIIt*dL(pl0X;%<}+KQ@j8MHHDg* z;uS#c8K!2yfawD)H}|?J+9p*fa>nV>m1b=GNtd?weE&VC`|kxkij#B(WI&{=TObEk z-r2lO4EqdJ8}ICtAH!>449w#s6@!I$wwAR(3$EfMowwi@=^#$hT?gJ-F?$B4y8a~0 z-T;`jxAx9T%MU=^d0%^HA9bW1GmMio(|MSFR_;z=LW9c8<00000NkvXXu0mjf78|bf literal 0 HcmV?d00001 diff --git a/res/s/conning-s/1x/5.png b/res/s/conning-s/1x/5.png new file mode 100644 index 0000000000000000000000000000000000000000..0595e6b9e934e664cb7f718f3bf1352a70b080e5 GIT binary patch literal 411 zcmV;M0c8G(P)Dlc7!nK@f&Nt^!jr3+CKi4itt{C=3dPDh3P>03KC^ zLiH8EgF;ndz)*7!KsLATT-&4yMdmVXu5c-|lWcO?`@i{jHvew1qLMUw0lGj16u>Z) zzGxDi`A3pwT@lw8amjQtu0Hh3yZFS-9WnY8xg=v$$n%p^uU@%K=^YZ4E-{tCnrm=E`DQ=v}8KoEVjBpAf3qOMJIo_+ux{3$T-0hlTj>eQ+G z1zZ&$fJbqr4s&T*R8egcFsH~%XPa$vgySY@(w%wl&FC~0XzUm06YW89b5q56Tl*jV&<1sS$whJIpmdsudA|nvH%;* zv8KnBa)$0yUQD0k)UPJz0q^t80B!)JUE6GT33P3plARzYp+HcT#b$4shH?V$fX<_!KpjT$?7u|i)SepV;(+Gl

-T zFdrG?@9xCT$@QwT;0^fE#x7`No=Fg!lWT{#Dlc7$;Koms}g#@#rtEii%;MotrgI|S$UqG!|g<8dD zpzy3;1*RVWlNnbBBq$WQ!%Hd6rs;B%rp?SdcV0U8?a`Afs~K<(=v@G-82lqYdnYmY zmoCv7xU!l63j-g3O}E&L@3d~FpRIwx+HAU;UAeNl29AJk}@HjBZl13f7i%7rT{Dlf7-jFc5{G3777pjs)QWJb?QQ;3{2KaFxncDpy${ zU8)S=-UE05gCIMl_W;2coCSvYQIrD+3DkY>oyfbHn(!g6B;88l+1b}#(yrumZu18L zAL2sNgMqJ-j#t2kxUAmETa=voLBKYj%xPb8y0lJ}5qacq$#b@kb6;}Wjlfj5G$RLlP_)pK@`Tn!pe#_VAerM55NKH*VUjnKotsAg{rDT zp?ZKSeDTzCfMzn|#sLWjXg>0_q1z^%Nnf(r$-Mc!|L+Pd84O2b01W{CYiSdJ9e_;? zes@WH85j&l*8m;_t^sUg@bA*y0JxRm`xyM`n*qT~d3w5h@RwB5({qZ!FLEG7)WqOd zm2&1cEr6Fgzg5a>T@dyRz)A&fI`Y2F%{c}?kHNo+8Ph=hS?v-LQ+GY6zo`ev>+bF<@NzsWIV=I#KT*aZTQx}WF38oUXBMfZ5vCo|V$YjTTe<~VR1yq6!# zl;N3E%Nbxh&ndJYJS(l=mWg3=8_zIx|66AOA0_^3E_g0SPyhe`07*qoM6N<$f=s`Y AGynhq literal 0 HcmV?d00001 diff --git a/res/s/conning-s/2.png b/res/s/conning-s/2.png new file mode 100644 index 0000000000000000000000000000000000000000..eb6a4a41cdbe9f7f8e85e5bac32a7e7b8031d080 GIT binary patch literal 709 zcmV;$0y_PPP)z9Km&aOFmxqLTmm?Yqwuj70V+N!1XhJ} z0Jr)%SIM<`R^At~^PNOWGg3-`s!srPH&J8RtKHZluXfWo3hyfkF!&b$J`GRfD7r(iH*+QD>BTsPATBzy|mDMt~%qvNX9CF$-ov4g4%k zu9{?mBz|yX?yFFZwMM`)c+Aq|Ld5(6po@PuA&~nNFzP(v4T}0YFhC&fgP(@ z3Ac_Ffu<#Q6WFmlYeN(#oWF_HzVoaGw9R9f$669t@XOmR5|}ab=N-O}3;yG?>(tX0 zJRXTl`Wv_9SWA4bKd2mJ^HXrnzeA2uZ9pfh<6eL8^(CLncC1ZE#P}RQ0`}_XCL}~m zhnr2i-%!M8)4S7Vv_^michE~h0#s;=jn6%FQoJqPdyjbRvTLEXQQ00000NkvXXu0mjf-p|SCy zp`xLop`oGj7KJ1zBq1H?d^QW3?YoW)?f%|V_Sdr=C5?T1ByD2-?~`OnPe%w;x7R-g zFa~e}paWo3Xt@Ni2CxD!OXBDur1DmPZm&N8a1LM+3QOGpxK85eelGwTzbo`qip|;E zd*4fr&VbLp=HD9RbJd8{0bCFOG`vdUXkFJE)mEnHQjMCl`%pF-FRF^##S{ z!)czSKSKcWEPcqcbZ)I%aa5$vnYC_FtWgQTLUTe4s|svszqZ!h0ywRLs0Ap4$g}iY zC;;34w$@D*#Hm2Z_fG)2z5Ymygt`Jkra^{wx(h%7;*1clq^A6Kguie67cofU=*_MM zp^{Dl_{7KG%1deZ&pAJ5O___5I9hU9ZafXwuwO1;n*=C$OvKx8dj&U^pmqKn(ZlZt zW5c~2+~L}i_c}3Z+g!E?;JrE;p!U!>u)NpEuL6V~pNEk4GL1R~Xam6I*ow55QS&iq z3qZg48lV`lnw0li8)i5#$+RW!wK8h|A27)@Dth=Sm>Dh!e?VNeFaqA=M!fwhxaQ}C zM`n{6YtP3(A`waDzmZNXyQq#i(R4f#Pq}U&2Ik8HG8e1X$NyVywPq7mz zLXe;X%Ubu6Df;ofvex~Qdq&x_bvL79E}0AU^*&d#C9h_s$Nm3%IV}M>{Pi5bA6X14 Ut#fweQUCw|07*qoM6N<$g2muF-2eap literal 0 HcmV?d00001 diff --git a/res/s/conning-s/4.png b/res/s/conning-s/4.png new file mode 100644 index 0000000000000000000000000000000000000000..e8645dc4411745b205930e6cb1bb599deeeaf8c2 GIT binary patch literal 769 zcmV+c1OEJpP)2^(qANQz1l?pgLwN@`mQYDx8?@1+;VaU99cCq;?lyZhdK@7;Uv-k~I# zt#${12jCOGz0AxRfCYd#fN2y4%Q7n;V!&?zjk4nb+yWR!VX&ymfbRgl@$njfue)rv z+9y=x48VCtoe2%eRpbx@ghW(0;es~>V;eN~f`5q${U{9l?AZ4?lRabHOJiI=P2y+$ zUw#|o#sF5aV2u06es3d9;$@n|_r|z$sv-t`F~)sJllaLV>tx9RjA(r}$Pk5r_|>Nh z-F>P>VIYC|!2Kc))=^C+A_vh~JWPhY?GS8LmAb*9vPF7~@`3F7emT0(qa( zB)&_N_XAOR$$;2X00kf4h`Ur7;wsm`?|?3DA%cD<@efrp9AIc93DNC zJ=YaMxx95Es^jSGg3aX|JiB6KQ}Iz?b2+v@j<|8r{^K2Ego z*@*!B!?KGcY*e6H}58zVf7~?*sN$h9d3*ifZU)j4s?!|;s4y;nW_nKwhoP_ME zY;oJVnBYx`JLz5le5QO|W8980?nh(X)vmO2rmLPA;WFnXP2y7h#ont~&NZtH7Eu^n zRbqf;i0o4OM`_{2sBFFv-^%Ba{iSBu-2m_k{G}Ep_)*3U00000NkvXXu0mjfYeHky literal 0 HcmV?d00001 diff --git a/res/s/conning-s/5.png b/res/s/conning-s/5.png new file mode 100644 index 0000000000000000000000000000000000000000..089e6710a7ff22718fba95979002c4822652018f GIT binary patch literal 787 zcmV+u1MK{XP)OY zc%jtljUj-FwR+Cq48S<>y=4*0|3cskfNIW^M*w$$?=AA|9OVF1l+s;DucP^&j&@c=**z-s`b6asGmv_&8I-h=4l zD4fPoI8;iHNW>+{wo-Z)N8#^vtc?&@;9@{P`+@IG(>w%V$l-Zf%xm>Vlim?;iM|{7 z-rSz!t2A&C0ves0F-P=Qv?VqN#7HCw0l#R#_|3^ScPA^Qe*g$&*Js68-YccQ z0XU;4&Ll);{*h(uij&0c*G1AuV#>Iuf~SyZlf-cxg-@b$`1SJgg7f=Db9fh_%|4fO zOeoAAxa9LOE9QG5G3EIn@_t2tvG4HZU0L9k7!2`3MLf{ri}|)wYac=klZq`NaLU1` zY;a4S`1jPYa(KRV+5W`rv+DmMV6d9WNmxeeWr>aD?7VuFCvUbZQA^KXq@o~Mz%K;zI_QJmQ*N@^G zR z{CN*x7DwT#jO9WCuIC2;&H(iFSsaC@DeImRj~;|f9EFQK`v*C|vf~Q?=jO2(yDi`> zYXTDPyPkh1I$0h`^#FH00?;wL5hKeS!fOwRcU;f!ki2?L^oD8P*djpi34j(A$8i+S z3wHg3fO*yhaLtW~2L?$JP7b&%yetErCDgs7EwMQv#x?=Qd}P1}Wlu6?!Ost&je#LC zBGy_qA#gqaiU)4VomT-b6oO6}j=MMtZ#EvFTlBP#>juZSC&s)z*HCB(&Uk(JOUz4< zm@}g`m=0Z|=~9&Ab%QCLOT>a zXPy5dpjh2aA=*XiWr@XdDr?s)$x_^iSS-iHBWQ|%!J1nF-3AFL)=~((@aCjJ0*dt_ z1hl~n*vafvh@l~vIZO$CL0mMjd&Q6!-%7LKp4UFxsGSIH_bukWe0{vXy|&wjk{U;H zK-%rY$B*~7&pJsk^~UUqq#;?qdu$=PWjEAe@0MB@WB@TkNEPr2Z7=O>KUs>0>|TQ$ z52-e~SL^2NPo?Ycvb82)Lf#qmB(x z_0cS0bZa=xAz%m8Qp4$Q#`ASI8%Lo`JjpfkzpN`ao&)#-t`Pv^GBc1y00000NkvXX Hu0mjfMl2^Y literal 0 HcmV?d00001 diff --git a/res/s/conning-s/7.png b/res/s/conning-s/7.png new file mode 100644 index 0000000000000000000000000000000000000000..914deea16cfb64dd665e48013b8f988b18ac3673 GIT binary patch literal 736 zcmV<60w4W}P)a6qDS<8|vsg~c2Y+1hB_q4aWcY8(^;kaIx zh=xSeC!!uM#v%#>zp9-q-W(d>xZXJtT@%r$rr28|nnz(UZ6p9jnD|OPUa?VrYrc=d zU|leMD3{}U?}=zqG&Wa&<9h7gJ0d#AqbZC$m%WWbU)^6p$T$jvRd%3NK>h%7CiJ5) zxUWIZ7~p#X8*J+Vwu=QQ@-Ezc6NSN|2C>A<#&b~^d}AB1)y)%Osfk^&t z|IHwRZTL#W9k}*9@FnB+Cgd7(xmO}&Ij-fuGkSIXBcAzn-Ch-r>kY8PjL#fb;(<^2 zp0-6!FP>N+qe#49VLhbCOxhw>5RMUZTPkQ)HB+q-z#;WuXsMzxKO)Z|2v^_Io5~3V z2v<)F*ejDQVxB@>Pf;o6kqC4O9- zP=IhPE!j5NE!fLj1vq30W)@OnXM<=N!oeHgTCzZI5Us@mZ0~G*9sWp@cx{Z$squjW z#2EW~n#6zA#E~)fqcQfMG>LyTBxn2gI2AobVeo^W>q3-0K%NKiUR_S~}ME?NJ)c+OB SYwphg0000v1Y zx`0+c(i*`Wz@#klY8T-~p13Y7i+tLs0LjMy#3~^il|{ajRgOF{;)TOoH$H+t$d~+J zZ*tgmoSAc$eG~`3|@xF-SBxB})GQT;n-@uS$SlKWVg& zx*(6@q|dBaF@WVd;&fkKp(`-pb>4x`6-%b%lNv#X2N1tH37*7e`b;xjsdK1qUh1-!oBH;&$vm=w@URV7g_fl zPIcq6ZTSbj~fs5A0ypLDIHCG344EJ!~z-bcH?Dw6dN)e>UVp zw=LgxFp>VSDvNxvY2Jf4WDIG*RkXae<9Z)X@q2FQ{c$wQSY&M0Oifc-!>$U5FMUh4 y*Kw}#bnHN4Lb_Q!h+4?12$__23Et-Z0>BqNPvgI!5!>$o0000XOOS6Hh&O@cjd-P$*PY6$*u_ zFA7t9@vZM<5*NkCOcjb*=51$5KS)Eb!<*@3n#8zFkoN>1Vmcd>)+nLUi--@9LfU}Z! zuJbINY-Ipv-2>1xvl--BT5Ot)=`Z~{T(aA(3?TdtK)^NaZyMmQ`e8r`KBN08ZKk?9 zH1L4%6QA%fim#52jz2%%-(KavGi_b2dEpU$Lrz!UIW{C_xA#55m633{7aln0bL3;g z8Tgzb4CWR%uDx0|@Br@`__cVLO&7v;j8!nPREqO0)l|4-MKUxywj0dtqGu0q}N$$kNe0FSqJqPdw=9%GA|M~)d00000NkvXXu0mjf>-rQ~ literal 0 HcmV?d00001 diff --git a/res/s/rescan/1.png b/res/s/rescan/1.png new file mode 100644 index 0000000000000000000000000000000000000000..19c4f777ef4a38fc914b83636580f3e5390fd713 GIT binary patch literal 15281 zcmeI3duUr#9LH~MowIgDYIP3~?xGtGy}6GhH@Vj|UDmW~(X_Rz*nzs<$4PTplAGn; zENR7At03DH!9SdvQ|99jL9L1)ihuYhf}k+z5JW|sf;tr^qoVZ{&&~5RO?LW+f1W~` zU(WgdzQ1!n=W*{p_weZO?hWl*+9`_KFf4IIp57-`!uQ?%o&sAksgak-pA)xYv}CDYMpwN%O~(}Nt@ypm>lH^(NEY@1-xZ1g&c< zNPr})V?hRdLjdWxX`5EuY*Mn?U4+=Eux-Se0Cakqrh&ZCB+cn5r4`pyX{v(|N;E2u zWDL0cl|;nUvd(o4MMAK{tsA-w6h-8 z(8q?GAY&W^`as4^7B@^<$ymTyygjbU z>aFguGS%YpmyOk9(j^fRivdRAyc{F&UWHLLPGn+06}=wK<5vTGkX@Bt8xt%LEOqO( zxhfq#umN6mb!Ewt2r71Z@eI#{0IbTv=>r}h#k{)8`Mi}{h*_;x1r7pW_0kzM zEZfXXBOT5Vor7f$IIyy{(vdLM$gxTjo&mC=IY(B=Qs7)T!$#U_LoQ`Fg-U`3Q?RyW zu`0cJja(e0LhuX^FucGB(l|W8eID84zmW^b99J1?QPteUGGlg5$$;<_+(iXAeN$`oMJsi`1aYebZ_z1(Le%nTeB2zi2R zqh=(_xj#53I$TIfam&CHWmQ|fe!@A*64|2OFL3aW<(>cKRJ6N3b??Pf(eC=xTITI3 z!ww1MYIV&>mU9!6i!k=U2R9HBeLjyb$ksR4XN($okeG-V@ZM%QhX^m0bLu-9Chl#P zh;)<_OB)4w#47HiQY(CbgU^m^_2F^l7OS3I|8r2gqW{?#wIfAP8rYcN!XiQi1Q$vJ z8xveuM5ut^LTO-Qf(wfX6%bq~4QxzsVG*GMf(xaAjR`I+B2++dp){~D!G%SH3J5Ng z1~w+Ru!v9r!G+Sm#sn7@5h@_KP#V~n;KCw81q2sL0~-@uSVX9R;6iC&V}c8d2o(@q zC=F~(aA6Ul0)h*rfsF|+EFx4uaG^A?F~NmJgbD~Qlm<2?xUh&&0l|gRz{Ug@77;2S zxKJ9{nBc-9LIngDN&_1cTv$Y?fZ#%DU}J&{iwG4ETqq4}d{JEOwbwC03ciDxgD+cN z*yPE=7d2^Za4bquGutR?_5q4oF2L_|6qV&D>c>fnlAoZcjpk$fJ{W-Sf?7k7@Ob`n z^^ZcKccD-yY^K{{CjQle-{e-zK6PJVKdas{S+2o zKGCunHowz(IN7OP{}1fk4lPO^b6+gO-}~X`(Fe91a{7c1JO6rrcHO^)0{h@gr_REb zkK^t0@M~Yk!to#AE2z-*!PDXPos-@DXN6NQg^%;EcYQ&>Hnwf-jpt6cog3-+>gT0j zq{6$)?u8>=8y>%zy864x-ho_;^yjf7e_pb9Y;OHuXOxcFw!RzQdS>U*b32x&FDsfp zzj$+JOUsq(zJ6r)HIHb6Cl~JR?jC;blNV0*ZR_d`FI^wGu;byM7u;R<37-Zp^lm?M z;;Hk>H5<>rsUE$2aWS#=<5Zix`|VxXBiqDR4n6%%=f;!#ts(kudW5H{~BQp=d Q>7b~g{^7{{uF1Lo0Gc|_(EtDd literal 0 HcmV?d00001 diff --git a/res/s/rescan/10.png b/res/s/rescan/10.png new file mode 100644 index 0000000000000000000000000000000000000000..2b4c501f86f64ae1b247947c41411f28f28de99b GIT binary patch literal 15390 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 H9i9F^PmSz+ literal 0 HcmV?d00001 diff --git a/res/s/rescan/11.png b/res/s/rescan/11.png new file mode 100644 index 0000000000000000000000000000000000000000..c43ee8ce4a388a9ed1d32841e4eb34fda3028eba GIT binary patch literal 15383 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 literal 0 HcmV?d00001 diff --git a/res/s/rescan/2.png b/res/s/rescan/2.png new file mode 100644 index 0000000000000000000000000000000000000000..406289221559917c2bdba0eadc889f98d08e257b GIT binary patch literal 15386 zcmeI3e{37&8ON`rNt@7A>5qwa>$Z~%TekLcKm6l-7sny7lg1kELh258V_MF4uai@4 zpXa;OPGKT#fC23otB|M=gEou~bqr-c1eJyq(-O1^q=NWog>}FxO;EN`1`=bc=Dp{i zZydXC{@~B|B-@YQ_xn80d!P4t-@AX_`$u>0>g@Pp2Srhxp^@Mi{Oz(oYuhMl)#NAV zKZSqV(`W&~p!(2EW0F(O2!3@}QaCbyKxL=htN58+rmy=Psr=}p}1 zo|0R!rb<)ogHXa@`P&&CmcNn+8tRmNU!kBMcJyevCIdzB@T%9#XaevsV%)_uu^7-8 z(W&tb1}dPUbq@WK@pjl;u09I<`NmjSk`_2pyIBo8DO2aLkGI7Z-I3KLT~4-*G5kISh# z#h91(vCG_RV}c2MrEaY@SEbDdHo&{CR+cPz;2epX#>7=O&+t6(!mJ#eKHvmW+@-}h zx2uv130BKhfr9{8UOFSXX&ISmq{AMfy|L^8KU~^c=}71+cVz%g^mFiet0QO3k0~w&e!iX3~VkNgz?@}9AEgA?a7O0Zuxd`upfMq9F zAzckpQr5)?8=4-UUJ|3hY}qjoWql0zbk`J1Sy2>Ji^S1XIc{WB3sRY5QR`ynP5VeEk~ZouzxyPa+yTi;yoF{kimp#p*nrGd2xE-WHcKyaZnur|Sk zMT80nE|dn=Cb+POPyxY((!kmT7ZwpJAh=K(SexL&B0>cO7fJ(b6I@tCsDR)?X<%)F z3yTO95L_q?tW9uX5upNt3#Eaz2`(%mR6uZ{G_W?og++u42riTc)+V^Hh)@B+h0?&< z*TmIPJB|rb@C;@a9=3dPm)}0=Nvk7cVTw9<6GhF;Qq*S!`1?LZO>q=;d7Pr;;}ms+ zaqI_+!|*KVUqZowNbb$p)k2}arKM%|#_bQkXdm`06wYd;-^2WKPL9jLV-AUX+P5j= z9l{^#HNd2jFUZoP5ak6su*_ki>6 zcJcHht%0?@zwQ6RrH|h`cXY>KiybC${$%HM16{4JJud|^$2z*Hjhj2xJowmCt7ZD+ z+K&8qm+R1zEvx_i-7_--FP`vR*mm^l$H)5LSYzJ)3O&1FxA$*{nE2LpR|YTd{qlTh z!>#ZBGn+d8i~FMQKKNYpJ5PV~s(AZ+cYbT<<#jLrS^cs5RP=v$nD?!JV%we4F9R1> z&vmao_x77Ieevrz=dbVDxb6Wu)HV5)nTV2~z4@IhIj(zW+x^?moH`xqJazi?@s}3 zTzF~Mv;TRf?fBeJKDc<|Q2T%H`iJjV8~-|g?)}t5Z9hHo&TmFSGp@z2{j2ZshfW;b zw0_H_9TTHRPJZjk@6Sqa4<43>-&-_#+qT~E%tzl3ey}k0C2G}cPqfZHa_k;^B11#F KgU=3*AO1hkHSXyE literal 0 HcmV?d00001 diff --git a/res/s/rescan/3.png b/res/s/rescan/3.png new file mode 100644 index 0000000000000000000000000000000000000000..d6a64bc5cd82df44ed1a54a1bf9b24467f8b5659 GIT binary patch literal 15391 zcmeI3eQXow9mmg2$w&YdhSCHYG|v=DTh4dq^Xtuamc-7I6;mr@5G1DgeD~PCVf&2l z96PAeAcR8Oy+{=+X_++2w63jGRFJlIV(LOivQ1QUXwujw)NKf^u0byzfsUU9txWW)hsYOtWH|uc(>W*v2qTN+h*Ttwy~U(VLc3I zr&&&5Jp#*_D<6x$6Gq;w91}(ZLzQyyKc6L`>luMz^7*_q&s)`OoZ&nk55w9Rn~jDF zTANJkVu4O;8_OnDp8(LLtdh|cHElM0#i*LoeHM#RsP?H`S1J<<)k>wc3O&e?DTo<{ zv$9Mo#Wc&*^ifbBWYbJ-Y%&9wQJ|^0tOTHZV{@b61RP!EYL{RXoB(OPsTG{FI~z5t zFH5zYOfH)&ftDo(BtZ(+(I5k^DS%8u)m1H_wkTQaE**Q8I1v2g8 zWiINHqGHtM@-i)L>Z__!s!0jk0PF1U_w=*Zrh-fhs{-cBz2MP zNLtgyv;-<6SK!D+9)6^lOgrpuJ8iewowS>^1KI(vMgqLY$=hsIYb1S#fcZJb@CqY~?+V<76Z^Ahih zx;ZbiD7`)=SioEEmg{p>K73#Uyz1J@5+Zv!Fi?x1Ya!(+F435cA-mag%VFx9n2VvYOW(thz2tYi%zj#B?0Uex{mOZD>sK zGRD*;m4!1`Ze2K`)zz_h0T?@77|a3@E{ri%O)lkf8dK`ys=I-JsDm15jveEi0?P~b z8tIxzS=rzbQe`DJxxk~zY#APive5(H-DOc1m*j$K5g%C+Vrn)e>V739#sQN_$GuFw z5mDCn@}Qbjvv63z&wH6>%}ADUe=tsTxRAusmVqbAqP9l;xN(#vb0xjY#=;N78GkFO zXm?}k?mMTV-HoaB%&(;t-4BoB(m)%Uku2jTCX`_8fDdlK?{qkLhnHz=Zp;WP1&|yM zDDd8<8HWhxtmHIyG)>&yDiP_ZB$hV{@`zR1N99)d00*BPncBnS!Yx)ixjy5denp?L zF=|JOpfs>C!G%SH3J5Ng1~w+Ru!v9r!G+Sm#sn7@5h@_KP#V~n;KCw81q2sL0~-@u zSVX9R;6iC&V}c8d2o(@qC=F~(aA6Ul0)h*rfsF|+EFx4uaG^A?F~NmJgbD~Qlm<2? zxUh&&0l|gRz{Ug@77;2SxKJ9{nBc-9LIngDN&_1cTv$Y?fZ#%DU}J&{iwG4ETqq4} zOmJZlp#p*nrGbqJE-WHcKyaZnura}fMT80nE|dl~Cb+POPyxY((!j<97ZwpJAh=K( z*!YgPI_s}vf;4;wGY?<3JT-eg24B=POT(ifirT-4q7EFQsQ(q=?+uE|u@v=wgrbBQ zit1LM{>I!8d>8a@BZ2<0!k?p`6pMXrZEc4v8;_kgUiK^&-;DgG987U%*Y)(g0N-=i zdbr~ku}EY%9*MlTtR$NH`$5xuu~=LI-+p*Zjzn&~Jv%%5)-Bj_WaY}0{|Ybr152-g z(f8r^BS31@mVoZ;$-FYG2JG5=Mc)}-K-4Zq#1KjhwGaYw{tEVsL+fN0TZ;qYXuqU=+ zBD?d8_eK+KQyUU5A3VCOcjm?Ad(VEZ@3r5)dC9ev-^6ZvxBIxQ`|1bt?R~xMLkGe7 zOF#d0$15)%9X~nuqvO|}+}*WC^R9g;{=M8s*QSNTrl0-k`cK-vc{6k2ZpNOt?c27G zx#wH+H^RZ^*`s%TaAfM4x$f1|T~DPhI@f%m|M&BAmYJLPB~Nt!*Rk@ommW1I&WwNO z%6ps70qZAQ-uduYNAay^`6K`Si&GB=@B86<*NzVTkv;#?xBq%z*Uig=zFy}29__Kn z@z0NOzfya?{&@QScQ;J-vlsHoPj4SNc{n%yz$0DPXDkn|J00Bl@xFg}K0NtE*E6@C zbnkikc6ZO*+2e(C{F&K%-my#{3jg%H^9^~^G4K6_^;b;PiS{4gef3Kd9R~f8;P$}j IfymVV0e}wc{Qv*} literal 0 HcmV?d00001 diff --git a/res/s/rescan/4.png b/res/s/rescan/4.png new file mode 100644 index 0000000000000000000000000000000000000000..f92092f017eece76d62846c8888f7cc8689cbce6 GIT binary patch literal 15397 zcmeI3Yiu0V701UojuKafJZNhO3abf`VkP65*~je6u-?@1`UTnGI16#$1D?lSd&ut2 zWM}N1SS<-IrKtjqLTQC4Ehts1L9cg#Zp8G%N-1*(d%$NJMp&k8gt+%#P6xB935F3WStGv&O4xOppeS;&aofj4iue zbRn+{ZO_fpzJkQr+eLSK1$`s&AzzP~1-?+Hz-W9x^o6BPJ|v0$;1-{s;{}$JSU$*b zyv#{5$NNeb9c_hHB&#Ro;aFd(9Q@Zsk2{VfvurMx>&yi@&FmPd3hDpoN&5m+ACDKO81qv;_*tUj9sD!IkK)|v3w`T zrqgV_NZT0()lSxpv=jR+zzzf3oXBbb$~V+c)H?}#SGwCJB7o!6RSx*Ya%v=eQR*1T8k0cp%bMxJ zg0YmW4ZO+QMVDu9WrdZf7N);!Ebfyo36NM4FbWss7(X9W7**p0CJ9s_7|;S?RpcY= zqVQ^;V1Y=nS+9;&ar1!=c-NJgB})Pvqp+?sNiD=PJP$-zm4m|v0zgUzb(ISR%e9bX z)mmjZ2!O>?XTY!>GrJ#Y@Ve-2EW5zIh2C;Q%2*=D3Qf2N$cpA|Sy5YoW8rlh@m0HA zjPME-1$Bl1C`*|Zi^3b%$c=+c6z<_7!}}S(lzPNh-EHzQkhy5J9Sl;_Cg`&;1 zp?j_$iZ<7VRwM7t7*5n*s#fDhqP&NgT!66)zPN#?5DEoC5w_M{8!=?KAT<&*;Iqy4 zHW6MZ#nd*`4cyZx5NRj{78eTgid9%gMK64TgYS-P<>hhV607W7pR!TCqfa?8nvN7f zX<%)F3yTO95L_q?tW9uX5upNt3#Eaz2`(%mR6uZ{G_W?og++u42riTc)+V^Hh)@B+ zh0?&<1Q!+&Dj>K}8d#g)!XiQi1Q$vJYZF{pM5ut^LTO-af(wfX6%bq~4XjOYVG*GM zf(xaAwFxdPB2++dp){~I!G%SH3J5Ng2G%CHu!v9r!G+Sm+5{IC5h@_KP#Rd9;KCw8 z1q2sL18WmpSVX9R;6iC&ZGsDn2o(@qC=IMlaA6Ul0)h*rfwc)PEFx4uaG^A?_H}W! zR*z$X3_OFGgNH4jyKDX`JgDi@28QDlHMNnV4t|-UuI1tH-zaK=qo~WH6eT}RQJ*)T zd~mi8o(27IFxH)Lf3MEx^IMzYb<>8YPJ4$v^LfuTBR4mni9dbFJ4c$&Pv70${kiQI z`R3*x0s50g-So<{@bH5Lk3am;3pub6Hs6^zyy}BIjECMUUub!9^!~5IV-lC(=Sou{ zl|FZ}?Vyipc`H2qjrNCxW9NUf`uJ^U?p?9*m64t8o7WtB_kVXkoOyUqwEooBzkb`9 z%X863*Zv{6Y|HTrs}5~)FSdByNzBc(p5EF1=D+5S{^IzBO;2t8(G4Ac*>OisdF98; z-qo~|zdW8&gQvb_Uv_`~;k9=@`O-trpI);&a@R=nh1X9VVV1XyYezPG_w5(%{6Pmj zb?MK~pIUog2i@m>u%$ufBYh{&v_^zS?v2HuXD)+upoo{(-kewoN4Z1K{fu7s+flzb zeAL8(e;mlo*)z9XogUkAbT)jy+rRuhecu}E2>p(G_^&tqTYTgEx^qLP z#g(tka#7`y`QGeg+qt=e{oCLF_#yFteyD%_y7TX^o@_et*ohMd4}TP(td?WLGu$U|T2Rzr?~d3@ I+eWAV561ELkN^Mx literal 0 HcmV?d00001 diff --git a/res/s/rescan/5.png b/res/s/rescan/5.png new file mode 100644 index 0000000000000000000000000000000000000000..279fd345a487b9ae71b1280c822a3ec59aedd5f7 GIT binary patch literal 15381 zcmeI3e{36P8ON{PQWB@>DxsFB1?sulXlc)PXW!X(hvU?-6B0DHY3gl>RD{oWuj5l~ zpX<9MPGOK{4A?XkNGC1YnzmyCX^f4gLMWrsmS%LD=+FRx21rbU?hk%cTE&LOsv+~9 zf4*_-zWHN+z9*gU@%w(C=Xu}H`{VAP_u=7#U+i-1a8MN0H8dC*fzPe>-M)#UHl*Hv z;{*7oBR4o^QdH+B?Rx|D#F=i2l9u$?s5KhBM^=rDThWXJa2GN;sHUhuZy~3s$AINb zfN?$B<9hefOD?Cb^|?SDaWJNyIHpRPt2gKf6l54611!Z^$fUET zTysriy4=*B(aY5xI%V9=V)};88-63DY$*K%6UbnUvhJTBzpM$P7llZ7*=99 zo@P0jm1LH4RxVf20j)q@OUfgWfl4~~ug8_Lteng+Q&Us!DX-hek29PkNet^@JRTY< zXmdJiDFr%f?k)$Z#)$w^&FeW!H?mGUu97e&tR9!kPE@-p{mSH`(ORmkS)m6xG6f~a zaBh~#WSC~2rZocUjcn>^#-?+C83Cp-kyinf?`|Hbe-gH?(%W?e>YoHzt7#}WX15O1 zEWf7Kd~y@{bP2SkG9V2yFpmita7_W^Qif%iDWgToT6GCxv&5F3Yyr@&Y3T+EdJ8wZ zrtDUlQ{|x!Kq%3ud?>HO>93?ChB{&QYbX+g6>d$}WS}TMP8CI3^8g?1Px2g{NC1uY zdo?cMR})IY;}0+`!_;S0B~_CYUN7J0sElqz}U)v^4@ba_L5CChZ&Y*5uMt&Npu$yRaEJwic)uq*nw7yP`HJWe_kQLS5vVx`p$HHzl zQdVzr*~3m$7Bm@x^-YU4?yYl7r9d_a_i&NsJhVrO!3|vS$bz(w6=jyKHnpT`9mF(} z)|8S5p>eqUg5{USaCHxHhQszt!tdO3zn;~MDRXZ%>T0*v;nkFq9S2&FsXA5*8eIa6 zJ#=Y(^~jY=R}W})cC4KM_DYw-d7!|F5i^YRdb-o-Qg2s18i*(ssFCK_80V8&FG9K& zq^xX+5j8YDIlU@IlhLwcAj-xV@a?WCma?uWs1}K%sdCcDXA~=_XOwZkuWh|q>Vgm76^I+OtWId%YHxDJ35?5N@>Z!9c4{fqkh!h%F+`hz2C#ai{b3wiYr>( z=sI?5SG2m(weERuR=0wlO16e##LIq($t4&^;D;Ls`UJr%1enI+Mvq~=0Mero9lqO4 zdlTV&6`#h6rjBE+9FdBOV|k$tikimp#p*nrGd2xE-WHcKyaZnur|SkMT80nE|dn=Cb+POPyxY((!kmT7ZwpJ zAh=K(SexL&B0>cO7fJ(b6I@tCsDR)?X<%)F3yTO95L_q?tW9uX5upNt3#Eaz2`(%m zR6uZ{G_W?og++u42riTc)+V^Hh)@B+h0?&<1Q!+&Dj>K}8d#g)!XiQi1Q$vJYu^%= zqkbF{WZ@aiDR|hDzkcq2@Svts9UO^L)QP(&YUV+T`mhL}|DdP|mZJU{rzm-zqCREJ zedWpkJPZ2fP$U#9{5kPavDkYXyqMjOUA7N<7Kq}$K$yf$2VX8+3T=ad~JF8f_?b3Sd9JpsjeB= zip&4n*!?4CdosQ)-F5LRVjevN->j*(Wb7Iifr0 z+4kp`Q#-C+`tH>QpSSnSdygMj*|;n43$>H)e*4sy_x0>Ox$~9&*d2%7KN7j;_?4et z6qN;6;=#_hw*6*|7uB)Ax96|Tf8i$=u5@}Iz44sz-u2_JT|IYL{(W>$`^uh={_wrK z+d|GoLL_I`G7v*V%LIu0FpBot=_=isjM$!wqF=zmXdd-D3( zg$3!&Ez6x>U%c$UDE_UpS9!JV?JjdOd+LEZgQxp9-D~~!)xW=FewF9GfA-V&ZRoyx zW_xh>VDik|=k`DSpK~4OXUFC*ya$-G&wOzHJA1b;PX6%+-}+kM#;HxR_`~Q^ rpIPa>CcLvxecMi)QKG?!<2*$Zj)&zG>|?(hBj`?Bw)l*bWO7oe8rOt$ zi#g*fz}RSJ%%+S)A89eS83j$jK(DDoO#+kM;-DqY<}x|m7S8T=S^006tSskXSU1D* zG|LIBTVOd;?ql{gz{r!7!$P00D_0Kw)nbn5dO~2BR4Qdj*(_>unBm-RH^W*PtCfZd zTH77h#WWq)nzAN&A0N=9q>|7THEuF|#gICxx0uaFp~5G3U9m(UP$(7Ga`YfaCM_ly z&cZUW7*i=z)B8YikQFnvf!ztf^Z`vBO-caDn<^W1?t-JsUF|ZAI(LD%UeOB9+0BhA z)t9A0PGU3}&489A21G#&*3lpXt|EX$MAcO-qE;zc=*~c_R9IKSRR9`2RntIPsgh>& zWYaQhDm&F42qh2@9!x55`HNAXDvcV~)$Q}b4vVbF0uV(9C%Ig-Yy}S58Rj`U6aq5s zw8>n^DTTz4)#+iX+7wq+u2h8*Hb;lkZRKrjo6E*=o$hw{&u43AIh)UB=ezj4J(5~x z+a1?*F)o4J$Q3y9u#0s$?2?_9-8Pwa^SqmO@vM`!%RuINF$BHCd21xS%-RQ%$}S*x zCDmAF!z9F{28_kqV$QGL!Vb$(%`AUGDIb&Uc0gibK#MHT(pHWa>5#-a=r9O5c$;K% zhFqM7DN8So2^R2VyXE3sWe*?N0I#~RvIMsS&XH4=>9AzyXpRFeSe1p-2W-F{=H(D; zhiJ>pina1^5CG*%$FFF*n%s?a7(+A;mVU5vX=}bCsw|UZfhIfy1W_`MEUzZQxiE%} zv=xV(%`ggO1r?@XdCQ_Ky?TvQ1jN1Y40q9-mA1ME-~n#83cTy|o{7^Hh>L!{R z)>C2eC9zYmriJYb(AjvW2XxpNg%?7F`%l^P$12*1Dr!(d4aX( zNf%7A$`X%&Dl6gLOFSyfmf?XYOFiJ-T^4n5SuUs+@sTAVtR`ck?p0#qFklk#VGmPm zM3lw7+@VI*Bpep-+B{69W+cnFKNu%ETu5SO%fJ(*tgTcZG>)?9Xh!d}vhc%j#{aog zw7WEQ=;oG!Y)o)r5upNt3#EaL2`(%mR6uZ{G_Wzjg++u42riTcHYT{Rh)@B+h0?&r1Q!+& zDj>K}8rYcN!XiQi1Q$vJ8xveuM5ut^LTO-Qf(wfX6%bq~4QxzsVG*GMf(xaAjR`I+ zB2++dp){~D!G%SH3J5Ng1~w+Ru!v9r!G+Sm#sn7@5h@_KP#V~n;KCw81q2sL0~-@u zSVX9R;6iC&V}c8d2o(@qC=F~(aA6Ul0)h*rfsF|+EFx4uaG^A?F~NmJgbD~Qlm<4w zDXxa%>zE)8-@#15mn|>dGIRyLsA-b?eF2Kva}Pz0jZ@U8i}3d&iW+4p>dFp^5{^<- zqxz%oobH0}f?n?SwGE{I68da$v9-3gc6Mu5^LP#PSX?|M!yov&kNf+U&71FruQ|MS zvhFDx%lhHhm+kW$Z2JH~ZdaE-surv#(a4U)|6#0M3Y`uTg9oB#T!tY}Ds=GeiSs%#^EBX%CaH@CxUq@UtiJw*4U-n=eA8;xRAbWWZmIA(uc=Jj;wrW`y2l`$G*Bav*O6lX4g&4-QFKv zcysNeD}?#U7q@Ibad6_o~fE;s(|=s*7Z;MMatX11OS zni|(Wac1&R9laOS{Px4igFjt7@&n=FcLFuX(tA%|sk^k{?Hd>GZfCV~Bh(FlXWG}4Nfb3Me(lS@;h(ALj@2ebO}pE^ z#!;{Bo=Z{EVZA3}MZ%BBs*!9~G$RU{bICMRQqOL69siIgelT3m&^GPLbx#^o$X zthFt!pk2@z33oZ$j0|v!%^q6i1fSC@HFKimqAeOv(t zwEP(@CU=LHmde3@Ev~p_rDcZ6X0y#%q1njvGn^zz49hb-PeTQ54yG(6N2kpB#U$lC zAz-Q*J#Fbm%4z3SqQ-#L;&R!ADxcDQCDY+>rBuo+(SsbBoRVg^W|m1NnR=0?)eWkh ztQ%?e45k6o4NPMoqXHZXEYcI!m->T7BxCOwcz6hLb# z0}>z!>zI%MR~JA!ZdisHHyV_zxC;>L6}I$P1Aw-tVHn8i4Z>_sF|07BibL&(P{Lt( zMMj6yUrB@vb-=!_&QJh4nl)XMfueXg)#sx%9(ZVP%+1l!D9~uHpm9;J8daja*UvOe zQ=L_%QguoQK~@yo1fC801deN$f?}&@X=_`EcS}K@ms-o|k_Y7O1Kqs%{W1r$)j@r4E6Nz7A+hGe)woVA4v)1oq@@ zag}FpWrdZf7N)YJTZ^)yyIc!NR;^Wrg8&#k zbvks@GBSgR!|tNJv8)77jchGD68ac9R%pUKKvqyufw+|`1raT3#rSy?3mg8gv$1&R-i zuJRt@Y-_V05?<$mReDM@vgX2a){$Y2)2neM)ep1)Qw}UQ)Q0#Od*~AS$dN0yjvUbP z>=-=(?3FIJWq<-FMvq}6#&SEgA=Q3Wvw@IefeLAk?cqE!>y|mTOuCY!sI18mHZ(mp zI3h=#(Xw+O%Gw<8>8>f3GNvr37RjTja?Hpi6)T`8m43jaQ~iFX+K4EtYq`xx7#Y|t z5D@%Ky=ElJem>YcI-E#KVadQ9WmH?OK4NcWiGhON%d_yqaQ1g86m{2zuD*3B>aGo~ zMn0C(tpH!DR@00`*$*+f0AnS5aRUL5C<>yVsco)}=+bi_5eez=*=E|C2=0 zu5J{FI7)%Vg@U|d71mL)6~4g1cSol3@;GvdRd%l1Y*g>)HYY~Yks>G!tW9uX5upNt z3#Eaz2`(%mR6uZ{G_W?og++u42riTc)+V^Hh)@B+h0?&<1Q!+&Dj>K}8d#g)!XiQi z1Q$vJYZF{pM5ut^LTO-af(wfX6%bq~4XjOYVG*GMf(xaAwFxdPB2++dp){~I!G%SH z3J5Ng2G%CHu!v9r!G+Sm+5{IC5h@_KP#Rd9;KCw81q2sL18WmpSVX9R;6iC&ZGsDn z2o(@qC=IMlaA6Ul0)h*rfwc)PEFx4uaG^A?Ho=8OgbD~Qlm^zmB`!zxI3`HJGniR; z*mC*WcMrpZnohN&J4{hSO%%29X^Og@hrhp4)BsCSm-{G6-bqn&j2*q7EQM!5_0CYR zC-+12pL~AF$N7AI-rTO_{zTt!(b&xG+LjUI9f@juG zJgID&I{e1|nXo;74PL!BV4?{HLF<|Gf54U_@Y--4UgDAUJHDc!f5ZKUHoqR3y=K|_ ztyiG)(y(J(A;5a+=!}i*^oi&GoY(ZlqX+Mrcq24hnal?th`;dZ%TN9^`|8!3SNb>2 z8^5UQ!yS_rJ5C*6`1trcUp=*vk8H?ZxQ{X>drn{5weg7;PaU8C)-S`S7d`hc%$v_| z|Aup%=$rnGGHd^va~2%_{`5-U(2Q-vmoGfO+rM?nL&MzJbFX~XbYg{m{oy+fU2GaW zW}RDdvvw~ZFG0y%x;iTty;Jw+qG>1POAK$lUoA~yLeNzwby*f3r zul3tC2Yl_lTRyka+tukSj&#pew$AxzXLO6;Nxm?t<-4ozoTp|Te6YR!Qu;?5YG?cM K(1F&z&Hn?*+|`-@ literal 0 HcmV?d00001 diff --git a/res/s/rescan/8.png b/res/s/rescan/8.png new file mode 100644 index 0000000000000000000000000000000000000000..3930bef02fcfe8cf6679dbc7642a7645a41a945c GIT binary patch literal 15309 zcmeI3TWl0n7{><^jnop+VgMt$EMmAgJGZ^fu-&$FDXp?t+X}5WI+wHUgx#HWXG#}% zKxrUcLt=P9qrr*^m>87@6+{I=h#CwahD1R@(Fk552#T8EdSyZD|g*!T;mz?=3e|)TrJoN3X&kV-n3>CPm#^V}D0c zFT6OBqQu>LN2k>po-e6J%q44v0$izB0;(x0P?t)`>Jnf%6wsr`Yn{LE-RE@ZTCH=D zC(MQuji6U=UY-Q)%NKO0%a^F4=Bx`=1yT|;hyhD>q+-#yDWz(i8D9y;b~EF2WF*#- zT4#e@(9s!gbu=1D;PAM-w8{y7hfj2I9?{RcA8_z2=Ve%t;oLOKNvtTboFn&f2CHBc zNNN$OE!31N2Y=N%do3#=F-(7dzpGzx8Oa`o6Gf3>d4}g{sG!YdaZ66oadT?cB<~Xf zrkd0fmTtrywy&%heO9g0X%{Mda@Q40gu{hWaWh8`a%56+g5g{&6N@qBGEJ)u6bD&0 z)9hH508AS&jlQG`pnPh1qq$4r=yF#(0;9Q0LEI{91?TMgM&;^jY9S}lmyBjWYbpbx zAO`E0kO5Z~K%&>M471m$P_odSfmp7vrAI0Nw0kP1fs|e$&F;yjW!6-7ssj*8I4nJ! z)Zy}%qaj1>v#+Zq6oefvP1htK%U(|P`)Q2_UfLINbF`uWjrIu|r}$JwR(M~4sc2JN zRk>1SN(do=_cV$PY=d9mxVfTVmCVeSSsZIkzDCWj1e(qz|(Wfuz0^XiZ5Ymf0`~IcWlW@zy%? ztGBSja#St<;<3Yk_^)=Ni#;<E;8XlufEkuv91|=M$aZVR zxyl|sumN6mVP#387tWDS)98rm;b@Knepr=-(+30~M%9)xGOpXPX)7dzkq?%^fgJB#&8EL>q(tBk6$iD^WvemMymdf@g8W^Wp; z`7^}P*l6D*e2(dh^tfj9n=|sRL(?kTtG#l(2WUYipO|kbO$jje)J657GnZ{0I-&X1 zF?<2oJ6&o_0vRrh4#S9!cO7fJ&g6I@tCsDR)?X<%c53yTO95L_q?Y*}5P0rdM1`r_bD#ZT_X<KBgN5dR8Bk3;p* zvFA?z3(E^Hta|(W!t--3bZe`#xz@44+Xq_5hmN12M%T={xYIqbf5#hZC-#m$SU+mZ zN39>P`|jxb?8|4qnZ1{Ja_5OVk9F5R_#xMHcJ-83XCB$peqibLZTwrOsTDUr_T`Rq zHQPG>*l^FZ_s%gZXWcWUD|BjeyL5QU^!2OQUt6Y6o4KiJ`-JhY*MB~k9}fS%ClP?cWK{uFP+`^)bX=xf4lAb>h?g}0>@xTfzG*YWnNHLLf1Yf}IIFvB^S?|R4v&@y*H=#4qutNsV~BJ0!u literal 0 HcmV?d00001 diff --git a/res/s/rescan/9.png b/res/s/rescan/9.png new file mode 100644 index 0000000000000000000000000000000000000000..bd22fa1b32131cd241824416ecebb698f036e0d2 GIT binary patch literal 15391 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 literal 0 HcmV?d00001 diff --git a/res/w/wifi-high-pwd.png b/res/w/wifi-high-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..81a4d11eae658f9d1775a78a233b6daa81665433 GIT binary patch literal 682 zcmV;b0#*HqP)J*%sn%6A(NTd#D3WVU&pD#yBt1dfLS05{4p*sz<=Nw=)dhQy{1lpq;o(4 z$R|8pA9xasN+uXVya3Dtxs)mkc?Y<6B@#Huh4`<7cmQ;XftFd7XPQdo`Q0Y+fIW}S z1Q?|3kUEmFo8;JtH$xQ*=9R>`iINYLbJ=7qjx9J2(&&%a5_6Hgq}8}W?0%LDt4i`$ z5_cTID)9u^MaNHOQ;VH0XUWv5l zl(}v86j`3ln0?z?jdAzDElZ*U)GX_)%wUVqSErBK8;zbqmIU3jch}$xYjP-d}$10 zYJJMbn(hrqTyfkJE^7uUd%)S4y2ju3uQ-V%Nnk4T%Qi{irxIxT`n?M<0E3ACR401l Q=>Px#07*qoM6N<$f>UEHK>z>% literal 0 HcmV?d00001 diff --git a/res/w/wifi-high.png b/res/w/wifi-high.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec5305256852940cf8f0d26993a3194e30d0228 GIT binary patch literal 605 zcmV-j0;2tiP)%L&}fWrk_Xw0D|g2UHcawz z7j|dn&70Xh5{b2~?Vl|fcH_q7V;YYvumz-mO||76_ypd7@z+`DBQ^mhEdWIzAF;S0 z-~)qsWRi0d?*Q9CI;P5!d;q+F$Rv;el1t)UOX3Nu8dL6>%tm`Md=~hwiM+z+*-C&x z_AOG2j6NKPQOZWVELAM3B`KhXc;guXZmy`RCE%aGg%lL0Ue-?SR>Wu z3g<)=@XZ;J!5NxI2ER$QSFCgqljIkO*;XX-BLBQ&C<$ADtCmFS8=K7VsZzx{pQg#) zV^fWwd!VO+c&x+=Ie0?m9H^S);lKA|?=LYr#``H855guFlfB}D*9P05V+LQvsKRBi zYt{g~Tv1I{FFt7mkkpg?xe|BQC~-K^z=(K$9*EkN^V?81Y_n8n2Jn(nmEcyyT>` zz3<+0&pYoG$JvhU*zizU0}EG=9$iX=J%UTv)j}qO55kZz{k%Ay=O%!n6+%GpEt{JX zB1Re+l~VYSa6s^Ks?5a)geMabWhl;tj|_!pgb^^4GK=LYLMb1=bs{e`r*9;HBsGK7 zv5X^|wkcl;DhAps&eM&_D-m4NSaV>H0%v*jr`(*g3NJ}D?kM-v#e=$#e2&5cLoy$` zAXaGy^QMCIP^yInx-^#wgoyW0vmGlC;3X$|U}gqG?iOA za^7Yo(1!wZ93YX6^An8Mw>pYp%!$E}0Xp*yG7t2l7hAk&+Eun6qX4pc8Fs+ZiI?kZ zNX4tziuw+C7w3V#6IhAfs@$%{mn|MF<+EIxgcI-kFj_1Yr*t_Hq4!Fu_PR^ptTLpG zLvc1C7aSAH1b)_&B=Vf|AtUKM5x69T+Ov?K+b)4#_>3;s=!oyRyL0OL#t{Cd-T&GR zg*BTm$Hf!we_XEASi2vrwG9m_G@*in?ps3U23;B%(sWc#4>V!8mcI6X{;$|(*bpc< l8y;H%|3#paHSSA*0RRED|0o143K9SS002ovPDHLkV1jDj9b5na literal 0 HcmV?d00001 diff --git a/res/w/wifi-low.png b/res/w/wifi-low.png new file mode 100644 index 0000000000000000000000000000000000000000..3fe8bdede28c3af2566a5bdce14a7430e9e1e827 GIT binary patch literal 589 zcmV-T0SvL1!-t<0!oemN1)3Q=r{r#feTQip-BN06iKl`a^oT?NZ|&Acq2VWtJTIIjEs=H z($hNG-I+IUXU2A%deq~OMnn7*#@gX;TYlJu0jR`H&D0c0=CtG?B+h$bHMbrX}72P)`$U!0->G* zk~k~lfKOs>i3*VHR|W}By3@dX(xAH=4IqF znmm;-H#=Ubw;`PO*-Xp~Ps+7rAttSQl4ZM@Q-C-tAuSzGIlnn1CxAiDeL*E(GjKE% zRPse+I6lX8103+LH{c4_a$nfi4~h3Q=MJBHl_@|KE->Bl=Ov=QtNe>rNRUW9>hXWa bw*UhG-8ai9>hoU@00000NkvXXu0mjf`;7Cz literal 0 HcmV?d00001 diff --git a/res/w/wifi-medium-pwd.png b/res/w/wifi-medium-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..221fdbc16c4783fd9744c2c3b06e2baf1804c56e GIT binary patch literal 663 zcmV;I0%-k-P)~!-*T1y?n~!QUun39I$GHjDUBb4~##~cBiQcu;@Ba2Hb?3 z8w0)|ii{IccnjDB@+no8;yvKmM#Qlcm%`hY!V_RXOtj3ZJq0S|^0!Ik1#7xi0!&i1 zNFB&HNV09@D^kU>@ksJK1mqnRTs2rrUJEFJ1&knUV*MQ9@VJQFS1Er zdq5Ztj0Ita)W*z25c^>~ZpaN3jyi^((YHWipF}5?Ub|eg9%Vc|av{w3DN}4i@|nQ5 z7|&(S+qeX}RA9;%h@ZsyA;#+_dKJ@{Bg2p-Tuy)^;J{eY25x``)(nmJLBV#al75V& z@D)&+6Dq|oQ1JTKzJ@3+g-?y?H$p~*&_k4{&C8vODZrEZ-Q*5C2FVH)I_A`u;bybh zAuf9=^xkot)}k#CgEivDvACG9#nwpsWUix0qL=uf~OV1kPagE~0iP)NFTkVF|+G{$q2Q_{RV`eC6i zeaXp#zW44u_uPBiXBhd&$3Krk+>I+&FD9lkum%)?in8Ss_y*pA$!;EnpKcQ>rY^J>U&QrjZ2TTpVv}98Ve5gmTYh4y32VxyIirks;0u zv;=5mUn8~1=;GiwN!YMgriyiCNdkC+y~&IKH@B3i#o@C~wuD4VRJ5+BA+|lAz+u3- zEWTpVofFkMWj2XbM&*-!*QtfG78MQ#dG8Ytc1`BF@5`eUcC(J z9b6dfLSmR0{Yj3F{v<|Rj0uBvaL}Qebm;&Ibf^PHe9wGMp2xNHsD_10zLd7_-uv#m z?_F`6{n(Ek4^=m?b^GMer9(I-xP(J3WI^~OObOZ7^~JI=0Tk^L0)lV3xQvi6s>rB{ z!e@k2g4a-GE}jyeO+=KTI2S%Q6wV1VAeSXyN>~$e;B)vfr59u^$WNu9NkaiCQb2JOZd^bS3O9+O%t~W)S{;7iKpLMk0_l9avvad+ zdtSG?_0OwU_rZ&ocOO3?a0&E*p?c&K_y#t>?&tabt2F^8je!J+8Wy(&QXnfM(<@1Q z3)}#HOO+*g2CP6NuOxuvlK9S%IA>Km%DtD_x}Jc)G5>2KYn)j+2{6c`Lu!#R#ldZp zvJo$&iiz5i0_KRf+Y{i!6IHb&T)1RwNF<=5iBdyiX9a;LE4gfbU^6{X)fHv-i2bWr_dF})QHH#B+-sTf)_}1)fUzblKom%A4~TKL>Of3lev1we z_Zvkb68WpzaD2c(nM-C$Jmi_sF%xF4dC)(=e4>I#fMr=Ha8F=JW@L3##F*2%1PrD3 zXh_pUcanLdx5m6e(`Xd)uxEFJHJW2qrQ0^+=*?B<1jU`Vc^Sp5rcNiU^u%lRHbn9f zhlzH0(XImrF&Wjn4XcIg?RgFloX>FG>ei{&uK)u87;efOb}a+t00000NkvXXu0mjf Dw#D`T literal 0 HcmV?d00001 diff --git a/res/w/wifi6+-full-pwd.png b/res/w/wifi6+-full-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..b4498c0cfaaae58c22994ba848921969529a92d5 GIT binary patch literal 784 zcmV+r1MmEaP)gvj8NQ#2*5QHP9{L-ok8-Cx*QwT5(|g3~Q#r0vP0L z0P49wiG@X%Z6jVn1Wy87d5d_UT- zu7s3h`m1S?-=y#mT39p?*+Tz;_p<;7*E`#G8Ggu48>R0$T{;Ee9Yagn8b{%8cb_`&E0v~UbFSCoN|(os=8iADS6RG z3Fp4!^|}p_e9ANR3|G0vfx?1k?#C1RWwY4`TqD>%d%|)s7*wXw)Dkk#@n(p(&!c)_ zTo;I$-$MY-X+S<}xeKtMBqzp5e7XazEq4&}kTG*2CY(9s#&Wy!5o`)OgT+$0EatN} zO&1PH793okc$DS2KLT~iP?^G=!|{N}(MU*qUs0}^Nu&xX-SRTTfsy`*g|>NQ#m)lH;xl2uW4b^Dli6cY#HEv6$UYTE4;+wgBz*N)9DZi2^R$HY*n| z>Lf!+j$~^F$yvaG4-TJcDDhvtu(>p2hi`D=bG=uFiaFZoKE?{{T zn7AVF5$q7wZOO6(&tazkGOGoE;1c+o6IdPHlI|y(O?eX7XR?(z?+@|Lzy&}jKOL&a zLM2`-nxqYXDH+^%v~mD{(v$6@ZhWs|L~oV(fxo8@trGxdlOt99^>J-Br6eR zkLT4C-N55gqL+J%J$ioVr#CZwNcH9Y9~#)Qd1Mwi?KYei7Uo zwdjNe)qfU%=nMUIZTO=^K$jlPq&Ov0sWTIHy?Iau;CJApU?#U4rjtNj^l+HG^K)cCf=|S0|=fu?eiet z0)UuP6Fmezu3_tic4`42xMcm60Vl9IMfM4H2LO|b4^Ct!XVax6(9ClLQ@XDJ>^;j- zDM(z;*sl3&0c`Pij-YmUx&_2^pLxL2DM~&uPR-H(o0P()$QJF~ZZ#3p~5gGt53Rv!eC-E5HCucseaA1P3wz O0000P)J& zez{!Ih)X|sxG#6_&*%No7791#=KSXqf<&Zd%zuF3%G4WegMCL@4)2GaqZW& z1EA9~PytFSCN~0xK$;aykm2|qa2MFVCd=YH1->GXpGyOPb8&p%Hk1{|22e-T-Lip@(XraR|lHDrv*s9$BoIPm;g|>`mqYPDEe`uW+Exr?myA{n z8)7?k0W_3z`S=MRhaO`Q9!;PI6cq*(c@HGeO+30KGOUw)=K_%E#JFVp!O_ES;@~-O z0E9Uc9|IlW1h&Se%=_#YVPL3xK*nvPM+WaC+cRdl&JpJ?Ve`ZUQ4;#I#!wu-23qD* zA9(Ad89H^cc)+JVBKuUcv1|`q7$XTYNpd!x1oHx@>)@gP;EL=58y$5&g5u-6&gF=# z=-_(DGFY3-S240^(b%~ujr7G6W>+*UNqM@^8JNk5w4HVT7&X}!c_u;1)!x`&LpX1< zrnbZTyoa8^yfwFHOaGY7WN2SG4ai)LX%BFKc4lcmJ2iG4NR$QAPxg`)Vnh7HWlfMEC0EO&q!WYIGmH+?%07*qo IM6N<$f`P1qT>t<8 literal 0 HcmV?d00001 diff --git a/res/w/wifi6+-high.png b/res/w/wifi6+-high.png new file mode 100644 index 0000000000000000000000000000000000000000..5144960f1d0bf59d9ceccdd915b9fee7d0beaefd GIT binary patch literal 787 zcmV+u1MK{XP)7YRAYziclDs<^E9bKG%kaTpIj*g^Dmy|9|mj=mnktlK$NFfcD++2Yr<06U#!n~2* zMw7|jhtG#-veMJ-d3R^tyqTSIrE)j!#(y3`-i`O(eObn16?gzF0r$<8pTKY62QdBf zy7_Bi0(80tG=Tb?!Ht0t5T}s|QXD@59skJKXL2nWaWgbjPERIy=}B!Dy6n_d&(<_#0IIE;L<%}Hd1 ziZ+ZIVmo#Owv=;O{EEeq$5;!GHqZoW3PW!B1PpL?;5l2T!aB&0j)1Te;}Y#>ClA-e z!7i{3ggFDB00+Q6Y)wp>H&`#Cz)07CT(_~lGWboRJz|tahB$u#aPWCv z=Q3okoWXR5X|Oi4TQQ>O(Acp_jnu^pMprW`NqYJ?Gc-4+rR@~`7`0g!d2WKFt6SRF z5YD^Isjcvc^Uy=gn{zvs^vi5E6S>xbDw|6b1i?f#n#6(x$&e&FX*o|+0M6T<>vIJh zgG9`lXu|PchFZ63CzAl2OVqFEu*a{S6xlQ290Ym^&RTR975!GY6u8LyaOUJb0kJiv zBefv$KIC=n7bURHXAGyhdGuKWcGOJKg+<%7OF@@a?K5&HHIR_&BfjqB&3PID*XvI> z-(qTF5I8KO;1Y)rAgb!Os{7sUE%R^rjc)-Wz4Fcf<#0uKE`=zK2#zDwlw2>k5xJXV zZ?`N0r@RSm7!S?WbREy-xufRgC?Izqj(e)`T;v0+ow_}YzM^`?!sD+10|0b5Iv1|B RYhwTa002ovPDHLkV1jV-Y6buR literal 0 HcmV?d00001 diff --git a/res/w/wifi6+-low-pwd.png b/res/w/wifi6+-low-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..802a36688a99aab68bf22dc074831cfc2a37e8b3 GIT binary patch literal 806 zcmV+>1KIqEP)>7%N-if3^AY>za!rbC&!!j z!&F>);p5%r-Sg|a-#M>Qbhqx-e_nCfgZJKkO7N%y4}mS9VUBzSegHSX^ymJI?;9t; zpefJ-niY$i0As-AB@^caeg-@Rk_}mw;10M%BD2B-5L^O34+Qp(rlfmGv$~!dnQC%x z?XO^S5lnziUIwTZ8pkl4R4E(&>SVEHUP%ET;cvQ3fQP%LY6*B2qOD>gH8R>VY>02? z6X>hp^70%n$7`y3M7k3~I^vs3`XQsrJ;cN+HiM`fLw@XQrw_TJunkaL-Sy3P3B$W zL>ye?bpqcBGA6U5I;u&ZCuI}JNPR>>W>t7+G9Rd|U0z{UX%y2q(%qm&bIPi0+d7Wk zndzKHsH>`ej8axprV}ph#M|g=h~PUMCRX9TT^|GzlTm$AgWhQ2Y)bt!+T12e$9a+7&vO8j=<*yY%R3|pMCn2fKtdd z&}U%-U3nWg0QP|=lDQc3x7T?e9EvQsXy36c%kTCI{GnrHcFp|&2UA{0t|0OEjP2@F zplxoQF?S&iwEasM_SE}hYQV0I8l}qF^G5MFKb^a9DO&8GliwHk9B9Hm8#T6G*LdoH+;u{;Pjj%>B$m`+vDS z1D{2R(jo|ariPNA1uL}ry=US!5*V=)_GAv2)*SMA#OE8ek8b4(*!%C;2_0fiO^o~* kKO^*how`fLAN~?x0E>fcWS|%{RR91007*qoM6N<$g0@_5ng9R* literal 0 HcmV?d00001 diff --git a/res/w/wifi6+-low.png b/res/w/wifi6+-low.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e4a96d829c2664ecee608fc339056c5ff5ca21 GIT binary patch literal 775 zcmV+i1Ni)jP)K@{E<138Q2V6WJN z3*;ra z1n4vbYCw=PxIWMW;xsaTisO0UD^Mz^vN(@{OAxusBmn2)_?yGAF*KmuuQFTLGe@NY z&h_~#oVjokppg}a)FNXK2m5)#hP?_^tf?gl;1BE#UI}n>Lq#nPyDr&s5}Bi-HKm5w zjtzk|%egF`VX>DG)o03GBBUbjEz&nGwQx3|!gi3q3;|)c3`n#mMjn-rUmW+EmUve{ zWLE%fH5h;>(6e(u%B`;sgd|ofPyv$tks=X@{8?qVl#oENOJ+)3<(Z13C!DzY!E6Ha zhB6`sF48K2Yl4Vm7OaX2X!B@X0uiP6n2=E(-AU#*b8CrL80BikJnY%kV72CuQR%Xc z*?R9(Z2^%BRMb}4&~k#fiiF{ zwd(u+E&D@iLOMELluH3PU$+v^j0d0v5;1e40>@_=YQ4#wOagE&QE$*;o5{2a`~c2D zpq=1sna;YCek;rsn8-VDcFp+#h=sfk?O4>>1?v1QXSP73Gbw>ZK4Umtwbnb#0X;q% z=Nxsuo9sU!obKRpn_y&5a=@WN6OVoV+(h)Pq6B2fT^?}0#;b`zU@svDn#_TgS7ZnQ zqHMh@oA-L}H2;>*jL6~p53c#Y93~0RB@v|&!Ex7WN~RZ#L~iETyE82U$E<{P7_Y0+ zJmk~dbJLnnM**pQIBr{sXCgnu+OgTg=sl}f6do@E3;;FyIs7`Zg8%>k002ovPDHLk FV1gB9VtN1o literal 0 HcmV?d00001 diff --git a/res/w/wifi6+-medium-pwd.png b/res/w/wifi6+-medium-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..68f47cf3c12bc7a15c911e05fed6474731f88c27 GIT binary patch literal 833 zcmV-H1HSx;P)#j3)c4Bv(S6asIH9~A8aD}lDJd1Zinpa#PrqF<5{06=~ zCgbM4JQK0Gg^z4@cV@o%c4m_xxEfdEKabqH2d_Q*kjF;>xDM<9MRVjU@DsQIX1|UO zer!yDK`THNC}&J=0*rw;iA*jb@NM80kl#>c2_6HVLF8L10R)%8cRYceqZ#GClv!QR z9+fI{Ztc%ubLJ&LC-*&4i;QC!PO_v8e+8;oHCvLvd-$7O65!#c$yx%Ae6nREvPVU$ zMh)>DI|6MLTsA*qbG#y}8Lbk15ETo_kFy zymKJZ9Y9}ACLjuobqz?nO^iW>#PS<-fK-2ENR&nXv^LydQ9!XzW=dS7O~Lafoci8_ zofXWR#)&vMOX>vf2_llYt2(MopKsSC5K;Po1sP@0on+pzw)T02QKqMu#)0kztd;Q5 z0UiP^prlL&z-yonn=htwioUMO`eP)4p98fujz2}f-qwB%5nP--G0RhFCC+OeV$x>! z(tIz~z+$nGd#?k#fXFz?<#JQbwbYW~z>utwWS2`X;BS{#^*|jR!e{u00yx}Jh3Cc` zIDtWo0OI%nfe%yE>auoD;ImDiAr+IFX*01mXM+w0if>rypZ&;OO z-Clt^)CV&scZh=(UPod<;=9-!BI~{iTVN;Ew$fg4cJjKMOQN|8HgQx z$!BcVCB||WP?D*2!f|Y!sJnj2uaD)f3eMjBy6=m9 zibDjO(@97I73&;ldcMGa^@qjW&)hfvm%}OWNeWR~1c68DQF3p=lB{m-l-LG=Av>WX z?|^Ad_u@IZyJ{Y6`Ke20*mnF2sA#48SB6KG7k?`&4b zvqYx?&h7n|*mL0&z#uOjR*Q{8Y#ilD8}TZ1v0=TE1im2N_)Y*9H?6FNaNx2nr;sH& z+OXV^*s&3?t;pr&H@qAsWc8GGmk8;IUrO|q%Ps5;=&(-m$OsU18-QecX6&&F@|ojY z(+ckrh;#=qR+9;c14CT{(rzPbL!H7(b98`Ie`JXW#Qs@rxRy{rvCC#!T;-mM<4!np z-3N;a%9~aY32>3t34AArDCULgsDLq#vkHi4y~l!#^7u|MC#Z-gyMne1%2s13-1!1SKUqg^fu=m#EYiT9ULx(VF zv&~ttr)pp_naH`X0%bsK9QeL}&AGN(GVEI-LyBzk(WhJvfV`y&&x~8(03u=#NZ@r4 z@62$k&DuH0ECA$^^&1j)+3QkduYhw1=q8YDkZf?@-wJaCCi5Q1jNOM2R_Ae~79_sQ zo*fA3-B-XWKVy(?srNnBfIVtf(S=q^=KD_w(hjTI6=Y9pAmOa{`SoqqGfxxXv8@g{ z-{#T85U^K7!6gn6K$O+9vUzT=E%I;q!jv7pf9sn6&tZ!2TnbSxA`lPMrDX1csm#qB zYdb{=IAtfa5InS6)7*IGoZD(%j04j9AnvNdGnx1C?$oSd_MW;c<{tk97yv{tIhn|# R;wAt9002ovPDHLkV1kQUXV3ru literal 0 HcmV?d00001 diff --git a/res/w/wifi6+-meidum-pwd.png b/res/w/wifi6+-meidum-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..5d64fddc0a9bf8c375aa09f7e3aca02008a02631 GIT binary patch literal 828 zcmV-C1H=4@P)>h>OGj}? zSTYpEQVQH4Ay+P4rc7ym0$s|KbhPPkN$K($B&R`^QecWmB*i6?n=4`|xCoX&m^ac} zvmW-oa1c#adb-Ws&di&)Gvj#P-MU-T-h1=8f=3m22y6j0b>u7X1Goa_KM$UN z-Rl(=N3#5LMf9W&v?H4nB@ zn0J&Dad4K^3H&CANoL3DsDM6?ixP+_y~l!#%jixrA6Z+wyu!HDD5kMzcY`&WLuRGh zHsR>KQJqtBbye9PqmbDY>4bAV@izK3MDQIB6J6n^T^~4z$*4Zg^SxFB%jHt;y#?F{ zE~Qs}-@oR3$Sg?5#Eaoi{B(hr*3mJ1Mt>-P!+TcYnY920Fi2PvRRliGQEOE@@Y$nJ z2`GuwfgTf!?95xhA#eaRg}E5>-F`-}wjk|0?b}*qd9Sa)92$bzCHD~yhP)0PSk!t8 zH2JsKyhheTD^Q)=M42b98fcl1Fzj02hpYj;>Lxf>lka|4H|*d`o{4%dG1j|)l3Z&i zTu0gov+m>Zj(@&F_5&;XE{iX3jcfw4CM+0_#UfXefWT2o4fI)X1AX^J=ZhZ25rQq~ zBqD)OJI9$rSKz<;heh4b!nOaG%P81+fankPzifcxy(J zy*r&xA&nOvyVmZ`ym>n_=P0@#_v1g0c+rC^AHF7-I=~}f73i8H*T7HU3Yh$Q|MEv; z0(6=IJs_~*MO&umEmUtn)?OMr*_CTejw49QlLNSliGj2dFw zc>-I?xon8p@hxSLX89pooZK-j$kB-&#yk4eZUfp;xSybB=L z6+l~k1|SNIbPhX5YE@wOl*exa(xgWCawCg%Jy6i%w{ur_jRBJTuQCR zar}+_p)?@_9WSq?0Gw~D#Pj0@*aHd2oan&uNrhT}!T;UJ`L`(hF^1s`&%lx-|WloMb zz6q`W&MRoZqSSaukr-hvR`t pJRkW1_Ky4+MjxqO(Rln8U;riGIl4*B7svnr002ovPDHLkV1fu|R2KjM literal 0 HcmV?d00001 diff --git a/res/w/wifi6-full-pwd.png b/res/w/wifi6-full-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..cd21cd7efe714a1cc2b193a69357cce939a7e4ee GIT binary patch literal 761 zcmV|<{sOySP=%RAWnpz#p`ln+CRF!r=bdM}S;^99(vsZFj->P5 z?z^{dSMp%+I3CA;9#PwcPd?raaY?!Z{Si^q8a=HKYepL1bz@QV@83T720^~C+ zYXTEB1bzp53mf)iS%PPZMSLjd_=2!(T!M&mFB?z~6cYfQxU;sdFye^x+@AV={Vf*bv`Y0PsTvmme?j`lqAKIF3iK+rAd=rqt{1bODHvH*PI8_kZz;T#!qXKcW&8Zz z+8Q5obuWT^*`*9+t?ZFJGY!_~{#Hz34s0}_&a2(PSny{ z_*~s%0E;J1`Bd^Qz=ndF7$NZG9=3McfzL60uE>~ZX3T@F^vPq`6!r!-6yySkbFZ6T zI24&XxIZy-iOky+1d3LqTtqtYnq44_LD{1#vU4Tok(Qc^B}00000NkvXXu0mjf>k?oO literal 0 HcmV?d00001 diff --git a/res/w/wifi6-full.png b/res/w/wifi6-full.png new file mode 100644 index 0000000000000000000000000000000000000000..becdaa0797c59e43eec5f54a635c57011800ded5 GIT binary patch literal 711 zcmV;&0yzDNP)vacvf+h0&eB91z17Oe|Y>$DfECF%>i;KVn z1%VG?zhSMKEKBehb_yVADF6hQz$Zpv=jfhvztOD96T-I0R%o0b;!NKJKqr3;s>ebl z4klI7hQEjm?i*S;fWNyp0Ni}-Wu0(iT8Dr54$0`g$A|K$K+uioXxGu>33AV&F?J8|3d*zJOnPu>OmXBfzv!S~|jpiL@=eO;Iqc`SffUF6QZkQ-9*s zx(yM$&zkxQckLP)z|5NatEpdRv)MX6#D2gY#YqqZnHn^?f;1-Huo43Z-ZR=~Dt7@u zJW>;F1U@QZ>w|Vm0U&rtl9UnppyA2ojMKptL2F8VQ>>aj77hYPv}$bQyT6 thSKeq{Aa{5+*LIf5DCh%Q~)mm3;>X8BRWD~*oy!F002ovPDHLkV1jh3LfilV literal 0 HcmV?d00001 diff --git a/res/w/wifi6-high-pwd.png b/res/w/wifi6-high-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..cf650101a6b91e57d1ca4afbc1a6b95898c28c29 GIT binary patch literal 812 zcmV+{1JnG8P)>%B84npY^x7&=HTieaiZf?fS&Ezj=|Ae?@5zejP&~p&frGYFr7&tSOfMR@Jc`m%Z z{atd`T3!0W!+rhUujlcbi?o4_5Q3fwkFz5zdgufX)@^Vat( z2SBF{pb6AVCN}}bK(Z_tZ;9hA;4ZLsMV7^R0(?Or=PLt%b8&pn;W#riCEW{}Re3hZ zRGs%${|q{xodM9u0|(VY;~0vQGHJu!CRuEnPm;hX>`fN|@bIq5S{%kM+DZnpK}MT~ z4Y8eM0ffrAeEf)yV~4Q-mk!VZY6>GBc?U$$jU2jlGOUw)n*|`ziE+vHeMS$ziGydr z9^e;DJO%nd54KWM=3VxSFfi6VAmcXCBZGI6?GdxQjEM7>u-P*~)P??KV<-+^1N-Jv z47_pC44pPv+~ZY?oPDg>ShWXEjgg3%LgWk<4lgi( z!yU)?%z$GkBh~UlR1U@ zfq56mds~8xq9G|48Q@@Aq#^M<;{UvQ zVdk7vNe}qTehKroTd6--OnqjKF}zmE0{BGgb`*^bHsiGhQB~VbCLyHbnOaIG|KvX- quHjJCeD=FbJ+E{CWv1Yl00RIuM`ki(+=(Or0000M3JLF3N)PL#zib0E|4Xnm^bKc zG)&faXFG-_D?Q!Ww>$66o7p{kW#wkvjQ>1>vJ-E;{=ACII&c@L0C&ukAHX?q28=FV zG=42CfTSBh9jMKi+z{vkgFG=o4)Hx;6Ifl)Wg#B`-yq~?p#YEz@qG`m3pAqLmo}^N ztkJ0&@9p~USo75@fI%L5tQH&lSU8*|ZNyuri*@rz5;#G;(WL-R-ZohaVdArGMj>l- zv~Jvx*aIgZQsnaR3m*18!J4?VfCf-iNIB&L5MyoZSzDvSI?4A=fT)w;lI{ssQY~^d|Z}X zhRl^UxZPtJY|P@L7+JI#?8uZx`r-+*s~VT2JUy&Q&CY4+IP<=YTI`E5J3-3Txwf|< z$UCg5?eK*6p+}gv=5|!{hsk8Jg^S=e&;29_f=nGWQyUT@LyBzY)qJW0Aa8lL&los@ zhyi<|0pk4vx32U~p#YGFR4!Q`lQ^QFQwTVoLe?Z%vd}Lg2zW;8CEHZF@@F68ySShs zQ*PJZtO8sQY_X{_kVgC!l&T)EqiUKiocFkP4sdXE&bs&5oYkBocWz?uyTFl~ZMf9o1e~%IkJJv>U^6aNL`9u$vJ)Z_ vkJX{{@t^!N;xQblnu~}SWm&3#-vSH(xGN(hlHf}{00000NkvXXu0mjf(HvSb literal 0 HcmV?d00001 diff --git a/res/w/wifi6-low-pwd.png b/res/w/wifi6-low-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..b58198bf51570e1ec5869ccec496a750b2b7d933 GIT binary patch literal 804 zcmV+<1Ka$GP)byTBUIFt2-i1tvgJL?$dq{1A8m)K*kkk|)3^h+LEsKypd^$dlLwno;hB%<6d7 zsZ@)f?fx_DIq?!;kS89gMaBs>POFrScy+4SF^{BxPlz{L5a7#wQ?(=<`DCj|WSxq3 zj2aRmolm0>Z8bNVP{!9%IP+o^vfS z-Wd?<4q&W43lIe++5^&VQ?sE>Vzm`IK-?c25-pLxtPQtv43zj}ro;{Ise5L^k#8QX zZlfD9xO{Brt|>~8CTJrWIobcTRg(J(kSNUKzD;0&23g? z+cx3oy)d2A@O4$yAEP#_S*8;T*9)kDM_{-FR~eyWV;Rt5~PA)zPgNPJwP*1|i888N0L zltP+7#KMNU@-FZicmXtpxe(&LX9dZ6g1qP4-!u1ma^D_-lW0iEW$od`G$*mB^%m&z z`xuv(sCrK&YVu=ZwkJLhY*M0>vWerz$^&*aU2?82@BMZ+_92#B6LUY2`&T?bNm3hx zOFS_4sIZI6dwzY6>IbU&9h$Ff?P~(EMm&a0nKH%qcR5F~OT!K9*)Ka^>?poWPN~EL z-5gZ{U*f;|gTyPO1G_KbTj{2Nw_1}XD38VoT;I7_)q>b;uwz9nP-HQt@JxpLl>>q6y`m%dC!P zok~S~ZTFvJ LHlRWoGEiw+Uaa5*kmt860>W+^A=Mr^d5j?+dd@Y^ zc;|>jcK~yBSb!)n&>oO>8<`C)6023{0C9g}NJJukQ5$Y$7%270OoQE@O4$$FQXQ#S*8=t?8K||Z3ys94ioEe*RJ(;OG+sK@Ev75x2A|LNTl>c9l*y0YW?G#LIMEaz-5808WrOL~zhr+ODfJLnjhz))ud)|P^epv#}0|G8*JqBpZHoH(B zu&b%S1HNunQQ#d3_>wHBqEwJ0`^2}eQGK^60av9Cp?N5ssr52UnvQ91U+^_OyH>+N?-rWKO>Ie lSe?0$=;2saO5m>m0|0sLBMH_!jr9Nk002ovPDHLkV1hL(Pt*Va literal 0 HcmV?d00001 diff --git a/res/w/wifi6-medium-pwd.png b/res/w/wifi6-medium-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..b713c4ec78dd96f9cd805f0bc8c577d1ebcaffca GIT binary patch literal 814 zcmV+}1JV46P)d-JCRhc?^qa$P4Si7-NHa6fF(4P>tEJ$HNl{ydxB-+7^l*L0MnG6sKMEI`y zTwRZ6J0=vCKIxN_=XZDCeRucLdfwH#TK{?Ziypl8{9_f58gLz`0CjWZ3-ALt1*Sir zY9;5&}MsiP_7K9^aQ zXO&7d`Ptf^z~;zFfKKi^q!t;+Fr1W08~$okv29*S0w3USdQO0cyC!Q1ICRNYlE^9* zZ5uVjcbpOkRd9Lv2`|SPS>2%ARYE4>n<9PcQVX_CDy)-yn-UOq9YC@@Oyw~RdDBtX zyv92LB0T~0)nx*rz*zTyjN8N*v`DPF!~{t9M}|aGJK@lE zA5=1!ca0NqaFjO*d?$!V=B}EkCVjqJlt4u3JvL-iMt72V&)!;R38PY1F^xSv4cKeo zu?;)`wt$8*831p99&AQta_X+BD*Iz3fnNZ@0>_^r;ND;RHAHZ6_S9@AGD`d$IEcxZ z-7E5ap$F#k`5GRA6?Xg3_k9U8GEz&114A+($sKOJg1;4(YEvCOfY1JK3gGazYP@vK zfPEOm2q2C(5O}vhtsVBxK75As*{5RCGa(b}>&ydS4|ohTgt-vn-ou<=9YNN6+IP%e zNA_(AQi=MMTviVUlSLX5-=##m$hxNjr4tex^Vrn`Ym_LVY%SJ(s0iuRFijVjk@Un4 zzT_F3{aE%d^Z+GEtP?Kjfl)@SU-RoDskzF2i_KTG2AY7!v63>(R8uDS{<5gyZ0#4l zU+h(U7MxN^4+I&ic&@;I^aqQn&&)A~m%35{pDEp@lCi~Ryi^hub-T$VgbX}Wm(syM s`Ok>gaHwiNJL^*4FHNA#6#Nok00lo~wAjg=VE_OC07*qoM6N<$f)=@FIRF3v literal 0 HcmV?d00001 diff --git a/res/w/wifi6-medium.png b/res/w/wifi6-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..135b18173472849287790d6c0958f6cfde460620 GIT binary patch literal 765 zcmV4N3`vJV@sv?pGiFRaAzQa5Ter4f(2N`A{D6?FTmc{#;wKj3G|-TCPi!`} zXNgV){H)iXW6haW0E0ZYSS>d8u(0ovHsV$2VnaQW1im8Pa3X+{n<{G|?AUB`6tYA| z8_ErdoumRHgIped$HU%?tRB$rG9exD(-3`Oa|>$&I&3C+oC*+i>wsi?lG>vR@}Xs4 zvkLDVh|LaQtR@o>2YO}=NW1mbf;xqj=jZ^b{#c0!#QwZCT%A!siOptOT;-aIP1USp;1U?hQ6!X;RsDLp)6%`QEdXEJeyZBBqpIKY0+``!DDCT0%>;~Mc z;j#(51U7)0v7`+g0WGZQs?Mp}y2|xsB*brlu)y?v5boXD+Ysau?6tZ*kXGX7&>~FQ z>{(vysTvrM$1Au9?z7rEzV8cBS6M9?ww1_`BDZ<#y^{kV-!uv@9V6fXA`%ct;588M z=D4-Z+BqmJ0OTQ+OV)=Zjwt990uE=8t&^-X*DoRn*rN54ZP##ReWh9>bBb=OUtwZCR**YXJrTh$AAV;{(Ll00000NkvXXu0mjfxno-P literal 0 HcmV?d00001 diff --git a/res/w/wifi6-none-pwd.png b/res/w/wifi6-none-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..aee1b42a3370dd65a4001c4d69e203a3854491c8 GIT binary patch literal 783 zcmV+q1MvKbP)Db)n*CKy~qDGafTvq8-k7APu|gP169%v&*@ z?49l+oHSl|NV>b7dGq$ooE=5?>wf*`6<2-m!Mm>sKAON|U;}8GN4^8sz!fn2_2%Wz zwFxk32j~K6&Eh7&7;t4|;*!MAfoDLnrpl7s0p}nxUr7MTCGm?uVjpNmxtB7l<7rT- zl;7?C3+y=y5@3*50jWjCF*Z)>l#O^zs@OHRq<}AoH(L_m!+le=Bpiigt4X9mMY~1~ ziS0arfl4knPjPcxP}LL4oeCr?1w^#G~%#LHt0`805@WyZSz zGTi};)n@^sz*u`g+HGPsbVw{&qXWeKnIVyi{Hxkew|01hS*=mb&5`Z~HJUrD%C@cJ z=$)I+X@$C~?#rmdYN~X?xt(}xy$z9ki^Ie^+_&qa05KWWN2_Ar^1x!T*v5z8A>|y! zaXjUGC^ckY;$?^@arS^+_2@BT4*yU95AQ4E`IQ47u))z2O(Z^Dq1Mtnh&fdH5Px4;3=7Un{T*It*saA>mN;{Kkw)|2b@2)sl?N-pao9-LM&B!17?u1+Ot z^Q$x4UC0C5lxRxXB*CTffL)0(UBixR*@sy2>CAN}*Kc`%lK3;SOFVE{aMoVlk?wQE z+oSnbtwT-VCJ>JySEiie_xq~D%EXImwV_W=sl)@D1*$}$#DDb%i#eY>(1v%qJb`bN zZdb|ZXa1d%@JDdSP8cw8t_*hkPyRFF7#^rI&u?5B$F&L6>4M(^3;;UbWz^y7eE|Rf N002ovPDHLkV1lr&Z}0#B literal 0 HcmV?d00001 diff --git a/res/w/wifi6-none.png b/res/w/wifi6-none.png new file mode 100644 index 0000000000000000000000000000000000000000..c4563c20c0e08463cc320fd1a9af49dd4f81c7ff GIT binary patch literal 738 zcmV<80v-K{P)E1?p4ovCQgvDpV@w z&-VNk&Rlp2Fvxq4)FNXC2fJm;M!YIjY?xP4zz@Xhj|p&d(^M@9+dkP!5~)zphEYRe zXO2LklFQ38yzES=>J{aV26DWwl+$h3^^B=e2l+U6CerA9Fi2f7>7Xs)v=+qM}; z@4$3U&DT|BKSp&{GfO92+KIQ&*AU6KI83a=UAw;U5R*}TQWSf_1EbOC4IYB$l(QX% zVUP16*N~oxmzGih$v-LMxith1Kq8|jsz`iRpw=Vr6cRx4bv$-i^&t}{ByK=T&@A9pGmEQc1WgXt5I#CLSt-eg7x_8F36J>du8k3)iw#0`~$800HnL U3Z}|1hyVZp07*qoM6N<$f;R$2kpKVy literal 0 HcmV?d00001 diff --git a/res/x/control.svg b/res/x/control.svg new file mode 100644 index 00000000..a64182b7 --- /dev/null +++ b/res/x/control.svg @@ -0,0 +1,15 @@ + + + + +画板 1 + + + diff --git a/res/x/fly-mode-off.svg b/res/x/fly-mode-off.svg new file mode 100644 index 00000000..408b7b07 --- /dev/null +++ b/res/x/fly-mode-off.svg @@ -0,0 +1,19 @@ + + + + +画板 1 + + + + + diff --git a/res/x/fly-mode-on.svg b/res/x/fly-mode-on.svg new file mode 100644 index 00000000..9f5e543b --- /dev/null +++ b/res/x/fly-mode-on.svg @@ -0,0 +1,14 @@ + + + + +画板 1 + + diff --git a/res/x/hot-spot-off.svg b/res/x/hot-spot-off.svg new file mode 100644 index 00000000..cf39cadb --- /dev/null +++ b/res/x/hot-spot-off.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + diff --git a/res/x/hot-spot-on.svg b/res/x/hot-spot-on.svg new file mode 100644 index 00000000..2041db9d --- /dev/null +++ b/res/x/hot-spot-on.svg @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/res/x/load-down.png b/res/x/load-down.png new file mode 100644 index 0000000000000000000000000000000000000000..a96298ace4380a4cc8ab5eea0ce6c086df416f6f GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xl001;Ln>}1B}il(=}1B}iO(5bkuNM&Q`;HqQQMEDEg00?fh=bU1Hj zoM6?jmUCiT!d2ELHU?8}hLyh^bQ;vYGIoeIw$&&+Y5f~vV(f7AoA6WC72f}pKR22g pPZH@k(iF(NBH_RR<^~34hB`MXgMW5E4+8CD@O1TaS?83{1OUkBLnHtI literal 0 HcmV?d00001 diff --git a/res/x/net-list-bg.svg b/res/x/net-list-bg.svg new file mode 100644 index 00000000..c17c1fd8 --- /dev/null +++ b/res/x/net-list-bg.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/res/x/setup.png b/res/x/setup.png new file mode 100644 index 0000000000000000000000000000000000000000..fe242f89ca8be8b54c604ea34999ae85660e2072 GIT binary patch literal 2152 zcmV-u2$%PXP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U-|cvg;@e{AU%j1SAj=%fVo(W(TwUY1xiX9_NwV zldq{Uu|)(HqFW#w)_?w<;V)bqOAJ}mN-ZVg$~9MRs94;ux;-i8?YaL+M;qPh!`Z-4 zdHwjW?dad2myZ`NW6*M|4|cS$9fs&Myj{sh_WHPX5_;bCln+C7-7BSDDg%f4EU7P#S`P67!lc>+NZ~2Wh_>ZpD)mp&zA!IwcJdDP4>KkelgUWQ^AU z-GF?lg?seR+8y}Z4t=`AvK$zp@@WIfo&JM%z8*N89`S7hASw^jIqo@!&AINgYqjJdZI&#X zSTQxTYOQij*aM}Os?}<(t#N1=sKHi4{|3#%OIq?GOIdWWr7o=_e0uKEOV@6__BME= zfo)=xp~FTUZKbecr&#kUYgu)*wXSU<)@GV{$}Cf-%{tr3+QI5Wd4V-LSmTW;@24kg z&<53u3G(ekoHH=SnZUR@0|;n#&Z1Gy?94f5u`(8gFq{kWw zeG=9zQ=gP)^9<vJ0<^Ead`^Z}UwD*pFmh_} zs%#F$_!}8se8I{NSGjjD{Aym*WPnaeeIP*sU!{sr%o9<8;H_<$+I0YO?&Bvf+4so| zu-^gDjiFwwBN%_U^}pEX>4Tx0C=2zkv&MmKpe$iQ>8^J z3RVzt$WWauh>AFB6^c+H)C#RSm|XfHG-*guTpR`0f`cE6RRN)x5=kfJi*U4AUlFC!X504bJ<-QC5;w;&b9rlP*a7$aTfzH_kLk-WaV#C4iONMZqt zkRU=q6&onSMx0ii6botEPx$x;UB5&wg#t9I72Cnp$zfuRLKS{5(wAc~QvkhEaw=`uBxZD8-o($QPT`5RY zC=`JAGy0|+(02=Tt$MvR_i_3FWT>mu4RCM>j1(z*&F9_W_TK(I)9mjDTyk=r;*cus z00006VoOIv0RI600RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru)K_YUTC1K=ht&~vv`nJeV3`aj z)oVeoQtzp+)thQv$XnF$Y_JNIBuPk}7r?Ke$AN=D8*mQz8S*jUPui(eSzc{dKdJ-j zWA#*--$!L?Y6^9+v!_ozt+uK8M6Cp_1DAkLzy{zgFaR_GM}Y;vH=q}I4padgRL+w`AM)hAqp6&IpT0lLl4#hZ>l#LOnIg_AP{i1Hl$cNM()N1vH zI;buQWv|*3bdqswSFZ$J9ky51#*)Fp0N_CIcVlSFfv{G+9rPjfsoJ2D3uN~KPk{d5 z9|2o}PBP!E0S3VR0AXKgK6V5E%Tw9w)yXnFYmQzvrDV(0hiVsUYfN|+p>^tS^?ca$ zM4%3piHV7Hk`c9#sPlkZz%cLOP2QuEBr%w!FBsmD_MQgu&^L8%Sfvny9D7RlvuIWngPs0_Le zI3CM>5ttv3!U~`^?UW=5b0PunfY#{YAn+d8415m$L5x!W9M5#A@yoe*Nej`7v`uFq eTdC%d{ literal 0 HcmV?d00001 diff --git a/res/x/wifi-list-bg.svg b/res/x/wifi-list-bg.svg new file mode 100644 index 00000000..43f18664 --- /dev/null +++ b/res/x/wifi-list-bg.svg @@ -0,0 +1,15 @@ + + + + +画板 1 + + + + + + + diff --git a/src/backend/backend.pri b/src/backend/backend.pri new file mode 100644 index 00000000..feb4853f --- /dev/null +++ b/src/backend/backend.pri @@ -0,0 +1,22 @@ +INCLUDEPATH += $$PWD +include(hotspot/hotspot.pri) +include(dbus-interface/dbus-interface.pri) + +HEADERS += \ + $$PWD/dbusadaptor.h \ + $$PWD/kylinarping.h \ + $$PWD/kylinipv4arping.h \ + $$PWD/kylinipv6arping.h \ + $$PWD/sysdbusregister.h \ + $$PWD/utils.h \ + $$PWD/wifi-auth-thread.h + +SOURCES += \ + $$PWD/dbusadaptor.cpp \ + $$PWD/kylinipv4arping.cpp \ + $$PWD/kylinipv6arping.cpp \ + $$PWD/sysdbusregister.cpp \ + $$PWD/utils.cpp \ + $$PWD/wifi-auth-thread.cpp + +DISTFILES += diff --git a/src/backend/dbus-interface/dbus-interface.pri b/src/backend/dbus-interface/dbus-interface.pri new file mode 100644 index 00000000..056901df --- /dev/null +++ b/src/backend/dbus-interface/dbus-interface.pri @@ -0,0 +1,46 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/gsystem-local-alloc.h \ + $$PWD/kyenterpricesettinginfo.h \ + $$PWD/kylinactiveconnectresource.h \ + $$PWD/kylinagent.h \ + $$PWD/kylinagentinterface.h \ + $$PWD/kylinapconnectitem.h \ + $$PWD/kylinbluetoothconnectitem.h \ + $$PWD/kylinconnectitem.h \ + $$PWD/kylinconnectoperation.h \ + $$PWD/kylinconnectresource.h \ + $$PWD/kylinconnectsetting.h \ + $$PWD/kylinnetworkdeviceresource.h \ + $$PWD/kylinnetworkresourcemanager.h \ + $$PWD/kylinutil.h \ + $$PWD/kylinvpnconnectitem.h \ + $$PWD/kylinvpnrequest.h \ + $$PWD/kylinwiredconnectoperation.h \ + $$PWD/kywirelessconnectoperation.h \ + $$PWD/kywirelessnetitem.h \ + $$PWD/kywirelessnetresource.h \ + $$PWD/nm-macros-internal.h + +SOURCES += \ + $$PWD/kyenterpricesettinginfo.cpp \ + $$PWD/kylinactiveconnectresource.cpp \ + $$PWD/kylinagent.c \ + $$PWD/kylinagentinterface.c \ + $$PWD/kylinapconnectitem.cpp \ + $$PWD/kylinbluetoothconnectitem.cpp \ + $$PWD/kylinconnectitem.cpp \ + $$PWD/kylinconnectoperation.cpp \ + $$PWD/kylinconnectresource.cpp \ + $$PWD/kylinconnectsetting.cpp \ + $$PWD/kylinnetworkdeviceresource.cpp \ + $$PWD/kylinnetworkresourcemanager.cpp \ + $$PWD/kylinutil.cpp \ + $$PWD/kylinvpnconnectitem.cpp \ + $$PWD/kylinvpnrequest.c \ + $$PWD/kylinwiredconnectoperation.cpp \ + $$PWD/kywirelessconnectoperation.cpp \ + $$PWD/kywirelessnetitem.cpp \ + $$PWD/kywirelessnetresource.cpp + diff --git a/src/backend/dbus-interface/gsystem-local-alloc.h b/src/backend/dbus-interface/gsystem-local-alloc.h new file mode 100644 index 00000000..b74cc0aa --- /dev/null +++ b/src/backend/dbus-interface/gsystem-local-alloc.h @@ -0,0 +1,188 @@ +#ifndef __GSYSTEM_LOCAL_ALLOC_H__ +#define __GSYSTEM_LOCAL_ALLOC_H__ + +#include + +G_BEGIN_DECLS + +#define GS_DEFINE_CLEANUP_FUNCTION(Type, name, func) \ + static inline void name (void *v) \ + { \ + func (*(Type*)v); \ + } + +#define GS_DEFINE_CLEANUP_FUNCTION0(Type, name, func) \ + static inline void name (void *v) \ + { \ + if (*(Type*)v) \ + func (*(Type*)v); \ + } + +/* These functions shouldn't be invoked directly; + * they are stubs that: + * 1) Take a pointer to the location (typically itself a pointer). + * 2) Provide %NULL-safety where it doesn't exist already (e.g. g_object_unref) + */ + +/** + * gs_free: + * + * Call g_free() on a variable location when it goes out of scope. + */ +#define gs_free __attribute__ ((cleanup(gs_local_free))) +GS_DEFINE_CLEANUP_FUNCTION(void*, gs_local_free, g_free) + +/** + * gs_unref_object: + * + * Call g_object_unref() on a variable location when it goes out of + * scope. Note that unlike g_object_unref(), the variable may be + * %NULL. + */ +#define gs_unref_object __attribute__ ((cleanup(gs_local_obj_unref))) +GS_DEFINE_CLEANUP_FUNCTION0(GObject*, gs_local_obj_unref, g_object_unref) + +/** + * gs_unref_variant: + * + * Call g_variant_unref() on a variable location when it goes out of + * scope. Note that unlike g_variant_unref(), the variable may be + * %NULL. + */ +#define gs_unref_variant __attribute__ ((cleanup(gs_local_variant_unref))) +GS_DEFINE_CLEANUP_FUNCTION0(GVariant*, gs_local_variant_unref, g_variant_unref) + +/** + * gs_free_variant_iter: + * + * Call g_variant_iter_free() on a variable location when it goes out of + * scope. + */ +#define gs_free_variant_iter __attribute__ ((cleanup(gs_local_variant_iter_free))) +GS_DEFINE_CLEANUP_FUNCTION0(GVariantIter*, gs_local_variant_iter_free, g_variant_iter_free) + +/** + * gs_free_variant_builder: + * + * Call g_variant_builder_unref() on a variable location when it goes out of + * scope. + */ +#define gs_unref_variant_builder __attribute__ ((cleanup(gs_local_variant_builder_unref))) +GS_DEFINE_CLEANUP_FUNCTION0(GVariantBuilder*, gs_local_variant_builder_unref, g_variant_builder_unref) + +/** + * gs_unref_array: + * + * Call g_array_unref() on a variable location when it goes out of + * scope. Note that unlike g_array_unref(), the variable may be + * %NULL. + + */ +#define gs_unref_array __attribute__ ((cleanup(gs_local_array_unref))) +GS_DEFINE_CLEANUP_FUNCTION0(GArray*, gs_local_array_unref, g_array_unref) + +/** + * gs_unref_ptrarray: + * + * Call g_ptr_array_unref() on a variable location when it goes out of + * scope. Note that unlike g_ptr_array_unref(), the variable may be + * %NULL. + + */ +#define gs_unref_ptrarray __attribute__ ((cleanup(gs_local_ptrarray_unref))) +GS_DEFINE_CLEANUP_FUNCTION0(GPtrArray*, gs_local_ptrarray_unref, g_ptr_array_unref) + +/** + * gs_unref_hashtable: + * + * Call g_hash_table_unref() on a variable location when it goes out + * of scope. Note that unlike g_hash_table_unref(), the variable may + * be %NULL. + */ +#define gs_unref_hashtable __attribute__ ((cleanup(gs_local_hashtable_unref))) +GS_DEFINE_CLEANUP_FUNCTION0(GHashTable*, gs_local_hashtable_unref, g_hash_table_unref) + +/** + * gs_free_list: + * + * Call g_list_free() on a variable location when it goes out + * of scope. + */ +#define gs_free_list __attribute__ ((cleanup(gs_local_free_list))) +GS_DEFINE_CLEANUP_FUNCTION(GList*, gs_local_free_list, g_list_free) + +/** + * gs_free_slist: + * + * Call g_slist_free() on a variable location when it goes out + * of scope. + */ +#define gs_free_slist __attribute__ ((cleanup(gs_local_free_slist))) +GS_DEFINE_CLEANUP_FUNCTION(GSList*, gs_local_free_slist, g_slist_free) + +/** + * gs_free_checksum: + * + * Call g_checksum_free() on a variable location when it goes out + * of scope. Note that unlike g_checksum_free(), the variable may + * be %NULL. + */ +#define gs_free_checksum __attribute__ ((cleanup(gs_local_checksum_free))) +GS_DEFINE_CLEANUP_FUNCTION0(GChecksum*, gs_local_checksum_free, g_checksum_free) + +/** + * gs_unref_bytes: + * + * Call g_bytes_unref() on a variable location when it goes out + * of scope. Note that unlike g_bytes_unref(), the variable may + * be %NULL. + */ +#define gs_unref_bytes __attribute__ ((cleanup(gs_local_bytes_unref))) +GS_DEFINE_CLEANUP_FUNCTION0(GBytes*, gs_local_bytes_unref, g_bytes_unref) + +/** + * gs_strfreev: + * + * Call g_strfreev() on a variable location when it goes out of scope. + */ +#define gs_strfreev __attribute__ ((cleanup(gs_local_strfreev))) +GS_DEFINE_CLEANUP_FUNCTION(char**, gs_local_strfreev, g_strfreev) + +/** + * gs_free_error: + * + * Call g_error_free() on a variable location when it goes out of scope. + */ +#define gs_free_error __attribute__ ((cleanup(gs_local_free_error))) +GS_DEFINE_CLEANUP_FUNCTION0(GError*, gs_local_free_error, g_error_free) + +/** + * gs_unref_keyfile: + * + * Call g_key_file_unref() on a variable location when it goes out of scope. + */ +#define gs_unref_keyfile __attribute__ ((cleanup(gs_local_keyfile_unref))) +GS_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, gs_local_keyfile_unref, g_key_file_unref) + +static inline void +gs_cleanup_close_fdp (int *fdp) +{ + int fd; + + g_assert (fdp); + + fd = *fdp; + if (fd != -1) + (void) close (fd); +} + +/** + * gs_fd_close: + * + * Call close() on a variable location when it goes out of scope. + */ +#define gs_fd_close __attribute__((cleanup(gs_cleanup_close_fdp))) + +G_END_DECLS + +#endif diff --git a/src/backend/dbus-interface/kyenterpricesettinginfo.cpp b/src/backend/dbus-interface/kyenterpricesettinginfo.cpp new file mode 100644 index 00000000..8bff2062 --- /dev/null +++ b/src/backend/dbus-interface/kyenterpricesettinginfo.cpp @@ -0,0 +1,176 @@ +#include "kyenterpricesettinginfo.h" + +void assembleEapMethodTlsSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodTlsInfo &info) +{ + NetworkManager::Security8021xSetting::Ptr wifi_8021x_sett + = connSettingPtr->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + + QList list; + list.append(NetworkManager::Security8021xSetting::EapMethod::EapMethodTls); + wifi_8021x_sett->setInitialized(true); + wifi_8021x_sett->setEapMethods(list); + wifi_8021x_sett->setIdentity(info.identity); + if (!info.domain.isEmpty()){ + wifi_8021x_sett->setDomainSuffixMatch(info.domain); + } + if (info.bNeedCa) { + QByteArray caCerEndWithNull("file://" + info.caCertPath.toUtf8() + '\0'); + wifi_8021x_sett->setCaCertificate(caCerEndWithNull); + } else { + QByteArray caCerEndWithNull(""); + wifi_8021x_sett->setCaCertificate(caCerEndWithNull); + } + QByteArray cliCertEndWithNull("file://" + info.clientCertPath.toUtf8() + '\0'); + wifi_8021x_sett->setClientCertificate(cliCertEndWithNull); + QByteArray cliPriKeyEndWithNull("file://" + info.clientPrivateKey.toUtf8() + '\0'); + wifi_8021x_sett->setPrivateKey(cliPriKeyEndWithNull); + wifi_8021x_sett->setPrivateKeyPassword(info.clientPrivateKeyPWD); + wifi_8021x_sett->setPrivateKeyPasswordFlags(info.m_privateKeyPWDFlag); + + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = connSettingPtr->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + security_sett->setInitialized(true); + security_sett->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaEap); + + return; +} + +void assembleEapMethodPeapSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodPeapInfo &info) +{ + qDebug() << "assembleEapMethodPeapSettings"; + + NetworkManager::Security8021xSetting::Ptr wifi_8021x_sett + = connSettingPtr->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + + QList list; + list.append(NetworkManager::Security8021xSetting::EapMethod::EapMethodPeap); + wifi_8021x_sett->setInitialized(true); + wifi_8021x_sett->setEapMethods(list); + wifi_8021x_sett->setPhase2AuthMethod((NetworkManager::Security8021xSetting::AuthMethod)info.phase2AuthMethod); + wifi_8021x_sett->setIdentity(info.userName); + wifi_8021x_sett->setPassword(info.userPWD); + wifi_8021x_sett->setPasswordFlags(info.m_passwdFlag); + + + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = connSettingPtr->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + security_sett->setInitialized(true); + security_sett->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaEap); + return; +} + +void assembleEapMethodTtlsSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodTtlsInfo &info) +{ + + NetworkManager::Security8021xSetting::Ptr wifi_8021x_sett + = connSettingPtr->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + + QList list; + list.append(NetworkManager::Security8021xSetting::EapMethod::EapMethodTtls); + wifi_8021x_sett->setInitialized(true); + wifi_8021x_sett->setEapMethods(list); + if (info.authType == KyTtlsAuthMethod::AUTH_EAP) + { + wifi_8021x_sett->setPhase2AuthEapMethod((NetworkManager::Security8021xSetting::AuthEapMethod)info.authEapMethod);//gtc md5 mschapv2 otp tls + } else if (info.authType == KyTtlsAuthMethod::AUTH_NO_EAP) + { + wifi_8021x_sett->setPhase2AuthMethod((NetworkManager::Security8021xSetting::AuthMethod)info.authNoEapMethod);//chap md5 mschapv2 pap gtc mschap otp tls + } + wifi_8021x_sett->setIdentity(info.userName); + wifi_8021x_sett->setPassword(info.userPWD); + wifi_8021x_sett->setPasswordFlags(info.m_passwdFlag); + + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = connSettingPtr->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + security_sett->setInitialized(true); + security_sett->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaEap); + return; +} + + +void modifyEapMethodTlsSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodTlsInfo &tlsInfo) +{ + NetworkManager::Security8021xSetting::Ptr setting = connSettingPtr->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + setting->setInitialized(true); + + QList list; + list.append(NetworkManager::Security8021xSetting::EapMethod::EapMethodTls); + setting->setEapMethods(list); + setting->setIdentity(tlsInfo.identity); + if(!tlsInfo.domain.isEmpty()) + { + setting->setDomainSuffixMatch(tlsInfo.domain); + } + if (tlsInfo.bNeedCa) + { + QByteArray caCerEndWithNull("file://" + tlsInfo.caCertPath.toUtf8() + '\0'); + setting->setCaCertificate(caCerEndWithNull); + } else { + QByteArray caCerEndWithNull(""); + setting->setCaCertificate(caCerEndWithNull); + } + + QByteArray cliCertEndWithNull("file://" + tlsInfo.clientCertPath.toUtf8() + '\0'); + setting->setClientCertificate(cliCertEndWithNull); + QByteArray cliPriKeyEndWithNull("file://" + tlsInfo.clientPrivateKey.toUtf8() + '\0'); + setting->setPrivateKey(cliPriKeyEndWithNull); + setting->setPrivateKeyPasswordFlags(tlsInfo.m_privateKeyPWDFlag); + if(tlsInfo.bChanged) + { + setting->setPrivateKeyPassword(tlsInfo.clientPrivateKeyPWD); + } + return; +} + +void modifyEapMethodPeapSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodPeapInfo &peapInfo) +{ + qDebug() << "assembleEapMethodPeapSettings"; + + NetworkManager::Security8021xSetting::Ptr wifi_8021x_sett + = connSettingPtr->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + wifi_8021x_sett->setInitialized(true); + + QList list; + list.append(NetworkManager::Security8021xSetting::EapMethod::EapMethodPeap); + wifi_8021x_sett->setEapMethods(list); + wifi_8021x_sett->setPhase2AuthMethod((NetworkManager::Security8021xSetting::AuthMethod)peapInfo.phase2AuthMethod); + wifi_8021x_sett->setIdentity(peapInfo.userName); + if(peapInfo.bChanged) + { + wifi_8021x_sett->setPassword(peapInfo.userPWD); + } + wifi_8021x_sett->setPasswordFlags(peapInfo.m_passwdFlag); + + QByteArray caCerEndWithNull(""); + wifi_8021x_sett->setCaCertificate(caCerEndWithNull); + + return; +} + +void modifyEapMethodTtlsSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodTtlsInfo &ttlsInfo) +{ + NetworkManager::Security8021xSetting::Ptr wifi_8021x_sett + = connSettingPtr->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + + QList list; + list.append(NetworkManager::Security8021xSetting::EapMethod::EapMethodTtls); + wifi_8021x_sett->setInitialized(true); + wifi_8021x_sett->setEapMethods(list); + if (ttlsInfo.authType == KyTtlsAuthMethod::AUTH_EAP) + { + wifi_8021x_sett->setPhase2AuthEapMethod((NetworkManager::Security8021xSetting::AuthEapMethod)ttlsInfo.authEapMethod);//gtc md5 mschapv2 otp tls + } else if (ttlsInfo.authType == KyTtlsAuthMethod::AUTH_NO_EAP) + { + wifi_8021x_sett->setPhase2AuthMethod((NetworkManager::Security8021xSetting::AuthMethod)ttlsInfo.authNoEapMethod);//chap md5 mschapv2 pap gtc mschap otp tls + } + wifi_8021x_sett->setIdentity(ttlsInfo.userName); + if(ttlsInfo.bChanged) + { + wifi_8021x_sett->setPassword(ttlsInfo.userPWD); + } + wifi_8021x_sett->setPasswordFlags(ttlsInfo.m_passwdFlag); + + QByteArray caCerEndWithNull(""); + wifi_8021x_sett->setCaCertificate(caCerEndWithNull); + return; +} diff --git a/src/backend/dbus-interface/kyenterpricesettinginfo.h b/src/backend/dbus-interface/kyenterpricesettinginfo.h new file mode 100644 index 00000000..01c8b29e --- /dev/null +++ b/src/backend/dbus-interface/kyenterpricesettinginfo.h @@ -0,0 +1,146 @@ +#ifndef KYENTERPRICESETTINGINFO_H +#define KYENTERPRICESETTINGINFO_H + +#include +#include + +#include "kylinnetworkresourcemanager.h" +#include + +enum KyEapMethodType { + TLS = 0, + PEAP, + TTLS, +}; + +class KyEapMethodTlsInfo +{ +public: + QString identity; + QString domain; + QString devIfaceName; + QString caCertPath; + bool bNeedCa; + QString clientCertPath; + QString clientPrivateKey; + QString clientPrivateKeyPWD; + NetworkManager::Setting::SecretFlags m_privateKeyPWDFlag; + // only valid when update + bool bChanged; + + inline bool operator == (const KyEapMethodTlsInfo& info) const + { + if (this->identity == info.identity + && this->domain == info.domain +// && this->devIfaceName == info.devIfaceName + && this->caCertPath == info.caCertPath + && this->bNeedCa == info.bNeedCa + && this->clientCertPath == info.clientCertPath + && this->clientPrivateKey == info.clientPrivateKey + && this->clientPrivateKeyPWD == info.clientPrivateKeyPWD + && this->m_privateKeyPWDFlag == info.m_privateKeyPWDFlag) { + return true; + } else { + return false; + } + } +}; + +typedef enum { + KyAuthEapMethodUnknown = 0, + KyAuthEapMethodMd5, + KyAuthEapMethodMschapv2, + KyAuthEapMethodOtp, + KyAuthEapMethodGtc, + KyAuthEapMethodTls +} KyEapMethodAuth; + +typedef enum{ + KyAuthMethodUnknown = 0, + KyAuthMethodPap, + KyAuthMethodChap, + KyAuthMethodMschap, + KyAuthMethodMschapv2, + KyAuthMethodGtc, + KyAuthMethodOtp, + KyAuthMethodMd5, + KyAuthMethodTls +} KyNoEapMethodAuth; + + +class KyEapMethodPeapInfo +{ +public: + KyNoEapMethodAuth phase2AuthMethod; + QString userName; + QString userPWD; + NetworkManager::Setting::SecretFlags m_passwdFlag; + // only valid when update + bool bChanged; + + inline bool operator == (const KyEapMethodPeapInfo& info) const + { + if (this->phase2AuthMethod == info.phase2AuthMethod + && this->userName == info.userName + && this->userPWD == info.userPWD + && this->m_passwdFlag == info.m_passwdFlag) { + return true; + } else { + return false; + } + } +}; + +enum KyTtlsAuthMethod +{ + AUTH_EAP, + AUTH_NO_EAP +}; + +class KyEapMethodTtlsInfo +{ +public: + KyTtlsAuthMethod authType; + KyEapMethodAuth authEapMethod; + KyNoEapMethodAuth authNoEapMethod; + QString userName; + QString userPWD; + NetworkManager::Setting::SecretFlags m_passwdFlag; + // only valid when update + bool bChanged; + + inline bool operator == (const KyEapMethodTtlsInfo& info) const + { + if (this->authType == info.authType) { + if (authType == AUTH_EAP) { + if (this->authEapMethod == info.authEapMethod + && this ->userName == info.userName + && this->userPWD == info.userPWD + && this->m_passwdFlag == info.m_passwdFlag) { + return true; + } + } else { + if (authType == AUTH_EAP) { + if (this->authNoEapMethod == info.authNoEapMethod + && this ->userName == info.userName + && this->userPWD == info.userPWD + && this->m_passwdFlag == info.m_passwdFlag) { + return true; + } + } + } + + } + return false; + } +}; + +void assembleEapMethodTlsSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodTlsInfo &tlsInfo); +void assembleEapMethodPeapSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodPeapInfo &peapInfo); +void assembleEapMethodTtlsSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodTtlsInfo &ttlsInfo); + +void modifyEapMethodTlsSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodTlsInfo &tlsInfo); +void modifyEapMethodPeapSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodPeapInfo &peapInfo); +void modifyEapMethodTtlsSettings(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyEapMethodTtlsInfo &ttlsInfo); + +#endif // KYENTERPRICESETTINGINFO_H diff --git a/src/backend/dbus-interface/kylinactiveconnectresource.cpp b/src/backend/dbus-interface/kylinactiveconnectresource.cpp new file mode 100644 index 00000000..e56de5ac --- /dev/null +++ b/src/backend/dbus-interface/kylinactiveconnectresource.cpp @@ -0,0 +1,717 @@ +#include "kylinutil.h" +#include "kylinactiveconnectresource.h" +#include "kywirelessconnectoperation.h" + +#include +#include +#include + +#define LOG_FLAG "[KyActiveConnectResourse]" + +KyActiveConnectResourse::KyActiveConnectResourse(QObject *parent) : QObject(parent) +{ + m_networkResourceInstance = KyNetworkResourceManager::getInstance(); + m_networkdevice = new KyNetworkDeviceResourse(); + + connect(m_networkResourceInstance, &KyNetworkResourceManager::activeConnectionRemove, + this, &KyActiveConnectResourse::activeConnectRemove); + connect(m_networkResourceInstance, &KyNetworkResourceManager::activeConnectStateChangeReason, + this, &KyActiveConnectResourse::stateChangeReason); + connect(m_networkResourceInstance, &KyNetworkResourceManager::vpnActiveConnectStateChangeReason, + this, &KyActiveConnectResourse::vpnConnectChangeReason); +} + +KyActiveConnectResourse::~KyActiveConnectResourse() +{ + m_networkResourceInstance = nullptr; + if (nullptr != m_networkdevice) { + delete m_networkdevice; + m_networkdevice = nullptr; + } +} + +KyConnectItem *KyActiveConnectResourse::getActiveConnectionItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr) +{ + qDebug()<<"[KyActiveConnectResourse]"<<"get active connect item"; + + if (nullptr == activeConnectPtr) { + qWarning()<<"[KyActiveConnectResourse]"<<"the active connect is empty"; + return nullptr; + } + + if (NetworkManager::ActiveConnection::State::Activated != activeConnectPtr->state()) { + qWarning()<<"[KyActiveConnectResourse]"<<"the active connect is not activated" + <<"connect name:"<connection()->name() + <<"connect state"<< activeConnectPtr->state(); + return nullptr; + } + + KyConnectItem *activeConnectItem = new KyConnectItem(); + activeConnectItem->m_connectUuid = activeConnectPtr->uuid(); + + NetworkManager::Connection::Ptr connectPtr = activeConnectPtr->connection(); + activeConnectItem->m_connectName = connectPtr->name(); + activeConnectItem->m_connectPath = connectPtr->path(); + + activeConnectItem->m_connectState = NetworkManager::ActiveConnection::State::Activated; + + return activeConnectItem; +} + +KyConnectItem *KyActiveConnectResourse::getActiveConnectionByUuid(QString connectUuid) +{ + NetworkManager::ActiveConnection::List activeConnectList; + activeConnectList.clear(); + activeConnectList = m_networkResourceInstance->getActiveConnectList(); + + if (activeConnectList.empty()) { + qWarning()<<"[KyActiveConnectResourse]"<<"the active connect list is empty"; + return nullptr; + } + //可能存在已无效的ActiveConnection,所以使用uuid遍历处理需要满足device不为空且ActiveConnection状态为已连接 + NetworkManager::ActiveConnection::Ptr activeConnectPtr = nullptr; + KyConnectItem *activeConnectItem = nullptr; + bool isFind = false; + for (int index = 0; index < activeConnectList.size(); index++) { + activeConnectPtr = activeConnectList.at(index); + if (activeConnectPtr.isNull()) { + continue; + } + + if (connectUuid != activeConnectPtr->uuid()) { + continue; + } + + activeConnectItem = getActiveConnectionItem(activeConnectPtr); + + if (nullptr == activeConnectItem || activeConnectPtr->devices().isEmpty()) { + continue; + } else { + isFind = true; + } + } + + if (!isFind) { + return nullptr; + } + + QString ifaceUni = activeConnectPtr->devices().at(0); + NetworkManager::Device:: Ptr devicePtr = + m_networkResourceInstance->findDeviceUni(ifaceUni); + activeConnectItem->m_ifaceName = devicePtr->interfaceName(); + activeConnectItem->m_itemType = activeConnectPtr->type(); + + return activeConnectItem; +} + +KyConnectItem *KyActiveConnectResourse::getActiveConnectionByUuid(QString connectUuid, + QString deviceName) +{ + NetworkManager::ActiveConnection::Ptr activeConnectPtr = + m_networkResourceInstance->getActiveConnect(connectUuid); + + if (nullptr == activeConnectPtr) { + qWarning()<< "[KyActiveConnectResourse]" <<"it can not find connect "<< connectUuid; + return nullptr; + } + + QStringList interfaces = activeConnectPtr->devices(); + for (int index = 0; index < interfaces.size(); ++index) { + QString ifaceUni = interfaces.at(index); + NetworkManager::Device:: Ptr devicePtr = + m_networkResourceInstance->findDeviceUni(ifaceUni); + if (devicePtr.isNull()) { + continue; + } + + if (devicePtr->interfaceName() == deviceName) { + KyConnectItem *activeConnectItem = + getActiveConnectionItem(activeConnectPtr); + if (nullptr == activeConnectItem) { + return nullptr; + } + activeConnectItem->m_ifaceName = deviceName; + activeConnectItem->m_itemType = activeConnectPtr->type(); + return activeConnectItem; + } + } + + return nullptr; +} + +void KyActiveConnectResourse::getActiveConnectionList(QString deviceName, + NetworkManager::ConnectionSettings::ConnectionType connectionType, + QList &activeConnectItemList) +{ + qDebug()<<"[KyActiveConnectResourse]"<<"get activate connect for device" + << deviceName <<"connect type:"<getActiveConnectList(); + + if (activeConnectList.empty()) { + qWarning()<<"[KyActiveConnectResourse]"<<"the active connect list is empty"; + return; + } + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = nullptr; + for (int index = 0; index < activeConnectList.size(); index++) { + activeConnectPtr = activeConnectList.at(index); + if (activeConnectPtr.isNull()) { + continue; + } + + if (connectionType != activeConnectPtr->type()) { + qDebug()<<"[KyActiveConnectResourse]" <<"the connect type " << activeConnectPtr->type() + <<"connect name" << activeConnectPtr->connection()->name(); + continue; + } + + QStringList interfaces = activeConnectPtr->devices(); + for (int index = 0; index < interfaces.size(); ++index) { + QString ifaceUni = interfaces.at(index); + NetworkManager::Device:: Ptr devicePtr = + m_networkResourceInstance->findDeviceUni(ifaceUni); + if (devicePtr->interfaceName() == deviceName) { + KyConnectItem *activeConnectItem = + getActiveConnectionItem(activeConnectPtr); + if (nullptr != activeConnectItem) { + activeConnectItem->m_ifaceName = deviceName; + activeConnectItem->m_itemType = connectionType; + activeConnectItemList << activeConnectItem; + //activeConnectItem->dumpInfo(); + } + + activeConnectPtr = nullptr; + break; + } + } + } + + return; +} + +#if 0 +void KyActiveConnectResourse::getWiredActivateConnect(QList &wiredActiveConnectItemList) +{ + int index = 0; + NetworkManager::ActiveConnection::List activeConnectList; + qDebug()<<"[KyActiveConnectResourse]"<<"get wired activate connect"; + + activeConnectList.clear(); + activeConnectList = m_networkResourceInstance->getActiveConnectList(); + + if (activeConnectList.empty()) { + qWarning()<<"[KyActiveConnectResourse]"<<"the active connect list is empty"; + return; + } + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = nullptr; + for (index = 0; index < activeConnectList.size(); index++) { + activeConnectPtr = activeConnectList.at(index); + if (NetworkManager::ConnectionSettings::ConnectionType::Wired + != activeConnectPtr->type()) { + continue; + } + + KyWiredConnectItem *activeConnectItem = + getWiredActiveConnectItem(activeConnectPtr); + if (nullptr != activeConnectItem) { + wiredActiveConnectItemList << activeConnectItem; + activeConnectItem->dumpInfo(); + } + + activeConnectPtr = nullptr; + } + + return; +} + +KyWiredConnectItem *KyActiveConnectResourse::getWiredActiveConnectItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr) +{ + qDebug()<<"[KyActiveConnectResourse]"<<"get active connect item"; + + if (nullptr == activeConnectPtr) { + qWarning()<<"[KyActiveConnectResourse]"<<"the active connect is empty"; + return nullptr; + } + + if (NetworkManager::ActiveConnection::State::Activated != activeConnectPtr->state()) { + qWarning()<<"[KyActiveConnectResourse]"<<"the active connect is not activated" + <connection()->name() << activeConnectPtr->state(); + return nullptr; + } + + KyWiredConnectItem *wiredItem = new KyWiredConnectItem(); + NetworkManager::Connection::Ptr connectPtr = activeConnectPtr->connection(); + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + + wiredItem->m_connectName = connectPtr->name(); + qDebug() <<"[KyActiveConnectResourse]"<< "connect uuid" << connectPtr->uuid() + << "active connect uuid" << activeConnectPtr->uuid(); + wiredItem->m_connectUuid = activeConnectPtr->uuid(); + wiredItem->m_ifaceName = settingPtr->interfaceName(); + + + getActiveConnectIp(activeConnectPtr, wiredItem->m_ipv4, wiredItem->m_ipv6); + + m_networkdevice->getWiredHardwareInfo(settingPtr->interfaceName(), wiredItem); + + wiredItem->m_state = NetworkManager::ActiveConnection::State::Activated; + //wiredItem->m_itemType; + + return wiredItem; +} + +#endif + +void KyActiveConnectResourse::getActiveConnectIpInfo( + const QString &connectUuid, + QString &ipv4Address, + QString &ipv6Address) +{ + NetworkManager::ActiveConnection::Ptr activeConnectPtr = + m_networkResourceInstance->getActiveConnect(connectUuid); + + if (activeConnectPtr.isNull()) { + qWarning()<< "[KyActiveConnectResourse]" <<"it can not find connect "<< connectUuid; + return; + } + + getActiveConnectIp(activeConnectPtr, ipv4Address, ipv6Address); + + return; +} + +void KyActiveConnectResourse::getActiveConnectIp( + NetworkManager::ActiveConnection::Ptr activeConnectPtr, + QString &ipv4Address, + QString &ipv6Address) +{ + qDebug()<<"[KyActiveConnectResourse]"<<"get active connect ip info"; + + NetworkManager::IpConfig ipv4Config =activeConnectPtr->ipV4Config(); + if (ipv4Config.isValid()) { + if (!ipv4Config.addresses().isEmpty()) { + NetworkManager::IpAddress address = ipv4Config.addresses().at(0); + ipv4Address = address.ip().toString(); + } else { + qWarning()<<"[KyActiveConnectResourse]"<<"the ipv4 address is empty."; + } + } else { + qWarning()<<"[KyActiveConnectResourse]"<<"ipv4 config is not valid"; + } + + NetworkManager::IpConfig ipv6Config =activeConnectPtr->ipV6Config(); + if (ipv6Config.isValid()) { + if (!ipv6Config.addresses().isEmpty()) { + NetworkManager::IpAddress address = ipv6Config.addresses().at(0); + ipv6Address = address.ip().toString(); + } else { + qWarning()<<"[KyActiveConnectResourse]"<<"ipv6 address is empty"; + } + } else { + qWarning()<<"[KyActiveConnectResourse]"<<"ipv6 config is not valid"; + } + + return; +} + +void KyActiveConnectResourse::getActiveConnectDnsInfo( + const QString &connectUuid, + QList &ipv4Dns, + QList &ipv6Dns) +{ + NetworkManager::ActiveConnection::Ptr activeConnectPtr = + m_networkResourceInstance->getActiveConnect(connectUuid); + if (activeConnectPtr.isNull()) { + qWarning()<< "[KyActiveConnectResourse]" <<"it can not find connect "<< connectUuid; + return; + } + + getActiveConnectDns(activeConnectPtr, ipv4Dns, ipv6Dns); + + return; +} + +void KyActiveConnectResourse::getActiveConnectDns( + NetworkManager::ActiveConnection::Ptr activeConnectPtr, + QList &ipv4Dns, + QList &ipv6Dns) +{ + qDebug()<<"[KyActiveConnectResourse]"<<"get active connect nameservice info"; + + NetworkManager::IpConfig ipv4Config = activeConnectPtr->ipV4Config(); + if (ipv4Config.isValid()) { + ipv4Dns = ipv4Config.nameservers(); + } else { + qWarning()<<"[KyActiveConnectResourse]"<<"ipv4 config is not valid"; + } + + NetworkManager::IpConfig ipv6Config =activeConnectPtr->ipV6Config(); + if (ipv6Config.isValid()) { + ipv6Dns = ipv6Config.nameservers(); + } else { + qWarning()<<"[KyActiveConnectResourse]"<<"ipv6 config is not valid"; + } + + return; +} + +KyVpnConnectItem *KyActiveConnectResourse::getVpnActiveConnectItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr) +{ + qDebug()<<"[KyActiveConnectResourse]"<<"get vpn active connect item"; + + if (nullptr == activeConnectPtr) { + qWarning()<<"[KyActiveConnectResourse]"<<"get vpn active connect failed, the active connect is empty"; + return nullptr; + } + + if (NetworkManager::ActiveConnection::State::Activated != activeConnectPtr->state()) { + qWarning()<<"[KyActiveConnectResourse]"<<"the active connect is not activated" + <connection()->name() << activeConnectPtr->state(); + return nullptr; + } + + KyVpnConnectItem *vpnItem = new KyVpnConnectItem(); + NetworkManager::Connection::Ptr connectPtr = activeConnectPtr->connection(); + vpnItem->m_vpnName = connectPtr->name(); + + vpnItem->m_vpnUuid = activeConnectPtr->uuid(); + + NetworkManager::ConnectionSettings::Ptr settingsPtr = connectPtr->settings(); + NetworkManager::VpnSetting::Ptr vpnSetting = settingsPtr->setting(NetworkManager::Setting::Vpn).dynamicCast(); + NMStringMap vpnDataMap = vpnSetting->data(); + if (vpnDataMap.isEmpty()) { + qWarning()<<"[KyActiveConnectResourse]"<<"get vpn connection Data failed, the data is empty"; + } else { + vpnItem->m_vpnGateWay = vpnDataMap["gateway"]; + vpnItem->m_vpnUser = vpnDataMap["user"]; + if ( "yes" == vpnDataMap["require-mppe"]) { + vpnItem->m_vpnMppe = true; + } else { + vpnItem->m_vpnMppe = false; + qDebug()<<"[KyActiveConnectResourse]"<<"vpn mppe required:"<< vpnDataMap["require-mppe"]; + } + } + + getActiveConnectIp(activeConnectPtr, vpnItem->m_vpnIpv4Address, vpnItem->m_vpnIpv6Address); + + NetworkManager::VpnConnection *p_vpnConnect = qobject_cast(activeConnectPtr.data()); + vpnItem->m_vpnState = p_vpnConnect->state(); + + return vpnItem; +} + +void KyActiveConnectResourse::getVpnActivateConnect(QList &vpnActiveConnectItemList) +{ + int index = 0; + NetworkManager::ActiveConnection::List activeConnectList; + + activeConnectList.clear(); + activeConnectList = m_networkResourceInstance->getActiveConnectList(); + + if (activeConnectList.empty()) { + qWarning()<<"[KyActiveConnectResourse]"<<"get vpn active connect failed, the active connect list is empty"; + return; + } + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = nullptr; + for (index = 0; index < activeConnectList.size(); index++) { + activeConnectPtr = activeConnectList.at(index); + if (activeConnectPtr.isNull()) { + continue; + } + + if (!activeConnectPtr->vpn()) { + continue; + } + + KyVpnConnectItem *activeConnectItem = + getVpnActiveConnectItem(activeConnectPtr); + if (nullptr != activeConnectItem) { + vpnActiveConnectItemList << activeConnectItem; + } + + activeConnectPtr = nullptr; + } + + return; +} + +KyBluetoothConnectItem *KyActiveConnectResourse::getBtActiveConnectItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr) +{ + qDebug()<<"[KyActiveConnectResourse]"<<"get bluetooth active connect item"; + + if (nullptr == activeConnectPtr) { + qWarning()<<"[KyActiveConnectResourse]"<<"get bluetooth item failed, the active connect is empty"; + return nullptr; + } + + if (NetworkManager::ActiveConnection::State::Activated != activeConnectPtr->state()) { + qWarning()<<"[KyActiveConnectResourse]"<<"get bluetooth item failed, the active connect is not activated" + <connection()->name() << activeConnectPtr->state(); + return nullptr; + } + + KyBluetoothConnectItem *bluetoothItem = new KyBluetoothConnectItem(); + NetworkManager::Connection::Ptr connectPtr = activeConnectPtr->connection(); + + bluetoothItem->m_connectName = connectPtr->name(); + bluetoothItem->m_connectUuid = activeConnectPtr->uuid(); + bluetoothItem->m_state = NetworkManager::ActiveConnection::State::Activated; + + getActiveConnectIp(activeConnectPtr, bluetoothItem->m_ipv4Address, bluetoothItem->m_ipv6Address); + + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + NetworkManager::BluetoothSetting::Ptr bluetoothSetting = + settingPtr->setting(NetworkManager::Setting::Bluetooth).dynamicCast(); + bluetoothItem->m_deviceAddress = bluetoothSetting->bluetoothAddress(); + QByteArray btAddrArray = bluetoothSetting->bluetoothAddress(); + for (int index = 0; index < btAddrArray.size(); ++index) { + qDebug("bt address %d %s", index, btAddrArray[index]); + } + qDebug()<<"bluetooth device address"<m_deviceAddress; + //wiredItem->m_itemType; + + return bluetoothItem; +} + +void KyActiveConnectResourse::getBtActivateConnect(QList &btActiveConnectItemList) +{ + int index = 0; + NetworkManager::ActiveConnection::List activeConnectList; + + activeConnectList.clear(); + activeConnectList = m_networkResourceInstance->getActiveConnectList(); + + if (activeConnectList.empty()) { + qWarning()<<"[KyActiveConnectResourse]"<<"get bluetooth active connect failed, the active connect list is empty"; + return; + } + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = nullptr; + for (index = 0; index < activeConnectList.size(); index++) { + activeConnectPtr = activeConnectList.at(index); + if (activeConnectPtr.isNull()) { + continue; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Bluetooth + != activeConnectPtr->type()) { + continue; + } + + KyBluetoothConnectItem *activeConnectItem = getBtActiveConnectItem(activeConnectPtr); + if (nullptr != activeConnectItem) { + btActiveConnectItemList << activeConnectItem; + } + + activeConnectPtr = nullptr; + } + + return; +} + +KyApConnectItem *KyActiveConnectResourse::getApActiveConnectItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr) +{ + qDebug()<<"[KyActiveConnectResourse]"<<"get wireless ap active connect item"; + + if (nullptr == activeConnectPtr) { + qWarning()<<"[KyActiveConnectResourse]"<<"get wireless ap item failed, the active connect is empty"; + return nullptr; + } + + if (NetworkManager::ActiveConnection::State::Activated != activeConnectPtr->state()) { + qWarning()<<"[KyActiveConnectResourse]"<<"get ap item failed, the active connect is not activated" + <connection()->name() << activeConnectPtr->state(); + return nullptr; + } + + NetworkManager::Connection::Ptr connectPtr = activeConnectPtr->connection(); + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + NetworkManager::WirelessSetting::Ptr wirelessSetting + = settingPtr->setting(NetworkManager::Setting::Wireless).dynamicCast(); + if (NetworkManager::WirelessSetting::NetworkMode::Ap + != wirelessSetting->mode()) { + qDebug() << "[KyActiveConnectResourse]" <<"get ap item failed, the active connect mode is not ap."; + return nullptr; + } + + KyApConnectItem *apConnectItem = new KyApConnectItem(); + apConnectItem->m_connectName = connectPtr->name(); + apConnectItem->m_connectUuid = activeConnectPtr->uuid(); + apConnectItem->m_ifaceName = settingPtr->interfaceName(); + + QByteArray rawSsid = wirelessSetting->ssid(); + apConnectItem->m_connectSsid = getSsidFromByteArray(rawSsid); + + + KyWirelessConnectOperation wirelessOperation; + apConnectItem->m_password = wirelessOperation.getPsk(apConnectItem->m_connectUuid); + + return apConnectItem; +} + +void KyActiveConnectResourse::getApActivateConnect(QList &apConnectItemList) +{ + int index = 0; + NetworkManager::ActiveConnection::List activeConnectList; + + activeConnectList.clear(); + activeConnectList = m_networkResourceInstance->getActiveConnectList(); + + if (activeConnectList.empty()) { + qWarning()<<"[KyActiveConnectResourse]" + <<"get ap active connect failed, the active connect list is empty"; + return; + } + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = nullptr; + for (index = 0; index < activeConnectList.size(); index++) { + activeConnectPtr = activeConnectList.at(index); + if (activeConnectPtr.isNull()) { + continue; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Wireless + != activeConnectPtr->type()) { + continue; + } + + KyApConnectItem *apConnectItem = getApActiveConnectItem(activeConnectPtr); + if (nullptr != apConnectItem) { + apConnectItemList << apConnectItem; + } + + activeConnectPtr = nullptr; + } + + return; +} + +QString KyActiveConnectResourse::getDeviceOfActivateConnect(QString conUuid) +{ + QString deviceName = ""; + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = + m_networkResourceInstance->getActiveConnect(conUuid); + + if (nullptr == activeConnectPtr) { + qWarning()<< "[KyActiveConnectResourse]" <<"it can not find connect "<< conUuid; + return deviceName; + } + + QStringList interfaces = activeConnectPtr->devices(); + if (interfaces.isEmpty()) { + qWarning()<< LOG_FLAG << "get device of active connection failed."; + return deviceName; + } + + QString ifaceUni = interfaces.at(0); + NetworkManager::Device:: Ptr devicePtr = + m_networkResourceInstance->findDeviceUni(ifaceUni); + deviceName = devicePtr->interfaceName(); + + return deviceName; +} + +bool KyActiveConnectResourse::connectionIsVirtual(QString uuid) +{ + NetworkManager::ActiveConnection::Ptr activeConnectPtr = + m_networkResourceInstance->getActiveConnect(uuid); + + if (activeConnectPtr.isNull()) { + qWarning()<< LOG_FLAG << "check connection virtual is failed."; + return false; + } + + QStringList interfaces = activeConnectPtr->devices(); + if (interfaces.isEmpty()) { + qWarning()<< LOG_FLAG << "active connection get device failed."; + return false; + } + + QString ifaceUni = interfaces.at(0); + NetworkManager::Device:: Ptr devicePtr = + m_networkResourceInstance->findDeviceUni(ifaceUni); + if (devicePtr.isNull()) { + return false; + } + + return !m_networkdevice->deviceIsWired(devicePtr->interfaceName()); +} + +bool KyActiveConnectResourse::wiredConnectIsActived() +{ + int index = 0; + NetworkManager::ActiveConnection::List activeConnectList; + + activeConnectList.clear(); + activeConnectList = m_networkResourceInstance->getActiveConnectList(); + + if (activeConnectList.empty()) { + qWarning()<<"[KyActiveConnectResourse]" + <<"get active connect failed, the active connect list is empty"; + return false; + } + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = nullptr; + for (index = 0; index < activeConnectList.size(); index++) { + activeConnectPtr = activeConnectList.at(index); + if (activeConnectPtr.isNull()) { + continue; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Wired + != activeConnectPtr->type()) { + continue; + } + + if (connectionIsVirtual(activeConnectPtr->uuid())) { + continue; + } + + if (activeConnectPtr->state() == NetworkManager::ActiveConnection::State::Activated) { + return true; + } + } + + return false; +} + +bool KyActiveConnectResourse::checkWirelessStatus(NetworkManager::ActiveConnection::State state) +{ + int index = 0; + NetworkManager::ActiveConnection::List activeConnectList; + + activeConnectList.clear(); + activeConnectList = m_networkResourceInstance->getActiveConnectList(); + + if (activeConnectList.empty()) { + qWarning()<<"[KyActiveConnectResourse]" + <<"get active connect failed, the active connect list is empty"; + return false; + } + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = nullptr; + for (index = 0; index < activeConnectList.size(); index++) { + activeConnectPtr = activeConnectList.at(index); + if (activeConnectPtr.isNull()) { + continue; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Wireless + != activeConnectPtr->type()) { + continue; + } + + if (activeConnectPtr->state() == state) { + return true; + } + } + + return false; + +} diff --git a/src/backend/dbus-interface/kylinactiveconnectresource.h b/src/backend/dbus-interface/kylinactiveconnectresource.h new file mode 100644 index 00000000..db781917 --- /dev/null +++ b/src/backend/dbus-interface/kylinactiveconnectresource.h @@ -0,0 +1,74 @@ +#ifndef KYLINACTIVECONNECTRESOURCE_H +#define KYLINACTIVECONNECTRESOURCE_H + +#include +#include "kylinnetworkresourcemanager.h" +#include "kylinconnectitem.h" +#include "kylinnetworkdeviceresource.h" +#include "kylinvpnconnectitem.h" +#include "kylinbluetoothconnectitem.h" +#include "kylinapconnectitem.h" + +class KyActiveConnectResourse : public QObject +{ + Q_OBJECT +public: + explicit KyActiveConnectResourse(QObject *parent = nullptr); + ~KyActiveConnectResourse(); + +public: + KyConnectItem *getActiveConnectionByUuid(QString connectUuid); + KyConnectItem *getActiveConnectionByUuid(QString connectUuid, QString deviceName); + void getActiveConnectionList(QString deviceName, + NetworkManager::ConnectionSettings::ConnectionType connectionType, + QList &connectItemList); + void getActiveConnectIpInfo(const QString &uuid, + QString &ipv4Address, + QString &ipv6Address); + void getActiveConnectDnsInfo(const QString &uuid, + QList &ipv4Dns, + QList &ipv6Dns); + + //void getWiredActivateConnect(QList &wiredActiveConnectItemList); + void getVpnActivateConnect(QList &vpnActiveConnectItemList); + void getBtActivateConnect(QList &btActiveConnectItemList); + + void getApActivateConnect(QList &apConnectItemList); + + QString getDeviceOfActivateConnect(QString conUuid); + + bool connectionIsVirtual(QString uuid); + bool wiredConnectIsActived(); + bool checkWirelessStatus(NetworkManager::ActiveConnection::State state); + +private: + void getActiveConnectIp(NetworkManager::ActiveConnection::Ptr activeConnectPtr, + QString &ipv4Address, + QString &ipv6Address); + void getActiveConnectDns(NetworkManager::ActiveConnection::Ptr activeConnectPtr, + QList &ipv4Dns, + QList &ipv6Dns); + + KyConnectItem *getActiveConnectionItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr); + + // KyWiredConnectItem *getWiredActiveConnectItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr); + KyVpnConnectItem *getVpnActiveConnectItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr); + KyBluetoothConnectItem *getBtActiveConnectItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr); + KyApConnectItem *getApActiveConnectItem(NetworkManager::ActiveConnection::Ptr activeConnectPtr); + +signals: + void activeConnectAdd(QString activeConnectUuid); + void updateActiveConnect(QString activeConnectUuid); + void activeConnectRemove(QString activeConnectUuid); + void stateChangeReason(QString uuid, + NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason); + void vpnConnectChangeReason(QString uuid, + NetworkManager::VpnConnection::State state, + NetworkManager::VpnConnection::StateChangeReason reason); + +private: + KyNetworkResourceManager *m_networkResourceInstance = nullptr; + KyNetworkDeviceResourse *m_networkdevice = nullptr; +}; +#endif // KYLINACTIVECONNECTRESOURCE_H diff --git a/src/backend/dbus-interface/kylinagent.c b/src/backend/dbus-interface/kylinagent.c new file mode 100644 index 00000000..393d9fc4 --- /dev/null +++ b/src/backend/dbus-interface/kylinagent.c @@ -0,0 +1,836 @@ +#include "kylinagent.h" +#include + +#define KEYRING_UUID_TAG "connection-uuid" +#define KEYRING_SN_TAG "setting-name" +#define KEYRING_SK_TAG "setting-key" + +static const SecretSchema network_manager_secret_schema = { + "org.freedesktop.NetworkManager.Connection", + SECRET_SCHEMA_DONT_MATCH_NAME, + { + { KEYRING_UUID_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING }, + { KEYRING_SN_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING }, + { KEYRING_SK_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING }, + { NULL, 0 }, + } +}; + +G_DEFINE_TYPE (AppletAgent, applet_agent, NM_TYPE_SECRET_AGENT_OLD); + +#define APPLET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), APPLET_TYPE_AGENT, AppletAgentPrivate)) + +typedef struct { + GHashTable *requests; + gboolean vpn_only; + + gboolean disposed; +} AppletAgentPrivate; + +enum { + GET_SECRETS, + CANCEL_SECRETS, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + + +/*******************************************************/ + +typedef struct { + guint id; + + NMSecretAgentOld *agent; + NMConnection *connection; + char *path; + char *setting_name; + char **hints; + guint32 flags; + NMSecretAgentOldGetSecretsFunc get_callback; + NMSecretAgentOldSaveSecretsFunc save_callback; + NMSecretAgentOldDeleteSecretsFunc delete_callback; + gpointer callback_data; + + GCancellable *cancellable; + gint keyring_calls; +} Request; + +static Request * +request_new (NMSecretAgentOld *agent, + NMConnection *connection, + const char *connection_path, + const char *setting_name, + const char **hints, + guint32 flags, + NMSecretAgentOldGetSecretsFunc get_callback, + NMSecretAgentOldSaveSecretsFunc save_callback, + NMSecretAgentOldDeleteSecretsFunc delete_callback, + gpointer callback_data) +{ + static guint32 counter = 1; + Request *r; + + r = g_slice_new0 (Request); + r->id = counter++; + r->agent = agent; + r->connection = g_object_ref (connection); + r->path = g_strdup (connection_path); + r->setting_name = g_strdup (setting_name); + if (hints) + r->hints = g_strdupv ((gchar **) hints); + r->flags = flags; + r->get_callback = get_callback; + r->save_callback = save_callback; + r->delete_callback = delete_callback; + r->callback_data = callback_data; + r->cancellable = g_cancellable_new (); + return r; +} + +static void +request_free (Request *r) +{ + if (!g_cancellable_is_cancelled (r->cancellable)) + g_hash_table_remove (APPLET_AGENT_GET_PRIVATE (r->agent)->requests, GUINT_TO_POINTER (r->id)); + + /* By the time the request is freed, all keyring calls should be completed */ + g_warn_if_fail (r->keyring_calls == 0); + + g_object_unref (r->connection); + g_free (r->path); + g_free (r->setting_name); + g_strfreev (r->hints); + g_object_unref (r->cancellable); + memset (r, 0, sizeof (*r)); + g_slice_free (Request, r); +} + +/*******************************************************/ + +static void +get_save_cb (NMSecretAgentOld *agent, + NMConnection *connection, + GError *error, + gpointer user_data) +{ + /* Ignored */ +} + +static void +get_secrets_cb (AppletAgent *self, + GVariant *secrets, + GError *error, + gpointer user_data) +{ + Request *r = user_data; + + /* 'secrets' shouldn't be valid if there was an error */ + if (error) { + g_warn_if_fail (secrets == NULL); + secrets = NULL; + } + + if (!g_cancellable_is_cancelled (r->cancellable)) { + /* Save updated secrets as long as user-interaction was allowed; otherwise + * we'd be saving secrets we just pulled out of the keyring which is somewhat + * redundant. + */ + if (secrets && (r->flags != NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE)) { + NMConnection *dupl; + GVariantIter iter; + const char *setting_name; + + /* Copy the existing connection and update its secrets */ + dupl = nm_simple_connection_new_clone (r->connection); + g_variant_iter_init (&iter, secrets); + while (g_variant_iter_next (&iter, "{&s@a{sv}}", (gpointer) &setting_name, NULL)) + nm_connection_update_secrets (dupl, setting_name, secrets, NULL); + + /* And save updated secrets to the keyring */ + nm_secret_agent_old_save_secrets (NM_SECRET_AGENT_OLD (self), dupl, get_save_cb, NULL); + g_object_unref (dupl); + } + + r->get_callback (NM_SECRET_AGENT_OLD (r->agent), r->connection, secrets, error, r->callback_data); + } + request_free (r); +} + +static void +ask_for_secrets (Request *r) +{ + /* Ask the applet to get some secrets for us */ + g_signal_emit (r->agent, + signals[GET_SECRETS], + 0, + GUINT_TO_POINTER (r->id), + r->connection, + r->setting_name, + r->hints, + r->flags, + get_secrets_cb, + r); +} + +static void +check_always_ask_cb (NMSetting *setting, + const char *key, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + gboolean *always_ask = user_data; + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + + if (flags & NM_SETTING_PARAM_SECRET) { + if (nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) { + if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) + *always_ask = TRUE; + } + } +} + +static gboolean +has_always_ask (NMSetting *setting) +{ + gboolean always_ask = FALSE; + + nm_setting_enumerate_values (setting, check_always_ask_cb, &always_ask); + return always_ask; +} + +static gboolean +is_connection_always_ask (NMConnection *connection) +{ + NMSettingConnection *s_con; + const char *ctype; + NMSetting *setting; + + /* For the given connection type, check if the secrets for that connection + * are always-ask or not. + */ + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + ctype = nm_setting_connection_get_connection_type (s_con); + + setting = nm_connection_get_setting_by_name (connection, ctype); + g_return_val_if_fail (setting != NULL, FALSE); + + if (has_always_ask (setting)) + return TRUE; + + /* Try type-specific settings too; be a bit paranoid and only consider + * secrets from settings relevant to the connection type. + */ + if (NM_IS_SETTING_WIRELESS (setting)) { + setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + if (setting && has_always_ask (setting)) + return TRUE; + setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); + if (setting && has_always_ask (setting)) + return TRUE; + } else if (NM_IS_SETTING_WIRED (setting)) { + setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE); + if (setting && has_always_ask (setting)) + return TRUE; + setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); + if (setting && has_always_ask (setting)) + return TRUE; + } + + return FALSE; +} + +static void +keyring_find_secrets_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Request *r = user_data; + GError *error = NULL; + GError *search_error = NULL; + const char *connection_id = NULL; + GVariantBuilder builder_setting, builder_connection; + GVariant *settings = NULL; + GList *list = NULL; + GList *iter; + gboolean hint_found = FALSE, ask = FALSE; + + r->keyring_calls--; + if (g_cancellable_is_cancelled (r->cancellable)) { + /* Callback already called by NM or dispose */ + request_free (r); + return; + } + + list = secret_service_search_finish (NULL, result, &search_error); + connection_id = nm_connection_get_id (r->connection); + + if (g_error_matches (search_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + error = g_error_new_literal (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_USER_CANCELED, + "The secrets request was canceled by the user"); + g_error_free (search_error); + goto done; + } else if ( (r->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION) + && g_error_matches (search_error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { + /* If the connection always asks for secrets, tolerate + * keyring service not being present. */ + g_clear_error (&search_error); + } else if (search_error) { + error = g_error_new (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "%s.%d - failed to read secrets from keyring (%s)", + __FILE__, __LINE__, search_error->message); + g_error_free (search_error); + goto done; + } + + /* Only ask if we're allowed to, so that eg a connection editor which + * requests secrets for its UI, for a connection which doesn't have any + * secrets yet, doesn't trigger the applet secrets dialog. + */ + if ( (r->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION) + && g_list_length (list) == 0) { + g_message ("No keyring secrets found for %s/%s; asking user.", connection_id, r->setting_name); + ask_for_secrets (r); + return; + } + + g_variant_builder_init (&builder_setting, NM_VARIANT_TYPE_SETTING); + + /* Extract the secrets from the list of matching keyring items */ + for (iter = list; iter != NULL; iter = g_list_next (iter)) { + SecretItem *item = iter->data; + SecretValue *secret; + const char *key_name; + GHashTable *attributes; + + secret = secret_item_get_secret (item); + if (secret) { + attributes = secret_item_get_attributes (item); + key_name = g_hash_table_lookup (attributes, KEYRING_SK_TAG); + if (!key_name) { + g_hash_table_unref (attributes); + secret_value_unref (secret); + continue; + } + + g_variant_builder_add (&builder_setting, "{sv}", key_name, + g_variant_new_string (secret_value_get (secret, NULL))); + + /* See if this property matches a given hint */ + if (r->hints && r->hints[0]) { + if (!g_strcmp0 (r->hints[0], key_name) || !g_strcmp0 (r->hints[1], key_name)) + hint_found = TRUE; + } + + g_hash_table_unref (attributes); + secret_value_unref (secret); + break; + } + } + + /* If there were hints, and none of the hints were returned by the keyring, + * get some new secrets. + */ + if (r->flags) { + if (r->hints && r->hints[0] && !hint_found) + ask = TRUE; + else if (r->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) { + g_message ("New secrets for %s/%s requested; ask the user", connection_id, r->setting_name); + ask = TRUE; + } else if ( (r->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION) + && is_connection_always_ask (r->connection)) + ask = TRUE; + } + + /* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that + * will contain all the individual settings hashes. + */ + g_variant_builder_init (&builder_connection, NM_VARIANT_TYPE_CONNECTION); + g_variant_builder_add (&builder_connection, "{sa{sv}}", r->setting_name, &builder_setting); + settings = g_variant_builder_end (&builder_connection); + +done: + g_list_free_full (list, g_object_unref); + if (ask) { + GVariantIter dict_iter; + const char *setting_name; + GVariant *setting_dict; + + /* Stuff all the found secrets into the connection for the UI to use */ + g_variant_iter_init (&dict_iter, settings); + while (g_variant_iter_next (&dict_iter, "{s@a{sv}}", &setting_name, &setting_dict)) { + nm_connection_update_secrets (r->connection, + setting_name, + setting_dict, + NULL); + g_variant_unref (setting_dict); + } + + ask_for_secrets (r); + } else { + /* Otherwise send the secrets back to NetworkManager */ + r->get_callback (NM_SECRET_AGENT_OLD (r->agent), r->connection, error ? NULL : settings, error, r->callback_data); + request_free (r); + } + + if (settings) + g_variant_unref (settings); + g_clear_error (&error); +} + +static void +get_secrets (NMSecretAgentOld *agent, + NMConnection *connection, + const char *connection_path, + const char *setting_name, + const char **hints, + guint32 flags, + NMSecretAgentOldGetSecretsFunc callback, + gpointer callback_data) +{ + AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent); + Request *r; + GError *error = NULL; + NMSettingConnection *s_con; + NMSetting *setting; + const char *uuid, *ctype; + GHashTable *attrs; + + setting = nm_connection_get_setting_by_name (connection, setting_name); + if (!setting) { + error = g_error_new (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "%s.%d - Connection didn't have requested setting '%s'.", + __FILE__, __LINE__, setting_name); + callback (agent, connection, NULL, error, callback_data); + g_error_free (error); + return; + } + + uuid = nm_connection_get_uuid (connection); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + ctype = nm_setting_connection_get_connection_type (s_con); + + if (!uuid || !ctype) { + error = g_error_new (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "%s.%d - Connection didn't have required UUID.", + __FILE__, __LINE__); + callback (agent, connection, NULL, error, callback_data); + g_error_free (error); + return; + } + + /* Track the secrets request */ + r = request_new (agent, connection, connection_path, setting_name, hints, flags, callback, NULL, NULL, callback_data); + g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r); + + /* VPN passwords are handled by the VPN plugin's auth dialog */ + if (!strcmp (ctype, NM_SETTING_VPN_SETTING_NAME)) { + ask_for_secrets (r); + return; + } + + /* Only handle non-VPN secrets if we're supposed to */ + if (priv->vpn_only == TRUE) { + error = g_error_new_literal (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_NO_SECRETS, + "Only handling VPN secrets at this time."); + callback (agent, connection, NULL, error, callback_data); + g_error_free (error); + return; + } + + /* For everything else we scrape the keyring for secrets first, and ask + * later if required. + */ + attrs = secret_attributes_build (&network_manager_secret_schema, + KEYRING_UUID_TAG, uuid, + KEYRING_SN_TAG, setting_name, + NULL); + + secret_service_search (NULL, &network_manager_secret_schema, attrs, + SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS, + r->cancellable, keyring_find_secrets_cb, r); + + r->keyring_calls++; + g_hash_table_unref (attrs); +} + +/*******************************************************/ + +static void +cancel_get_secrets (NMSecretAgentOld *agent, + const char *connection_path, + const char *setting_name) +{ + AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent); + GHashTableIter iter; + Request *r; + GError *error; + + error = g_error_new_literal (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_AGENT_CANCELED, + "Canceled by NetworkManager"); + + g_hash_table_iter_init (&iter, priv->requests); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &r)) { + /* Only care about GetSecrets requests here */ + if (r->get_callback == NULL) + continue; + + /* Cancel any matching GetSecrets call */ + if ( g_strcmp0 (r->path, connection_path) == 0 + && g_strcmp0 (r->setting_name, setting_name) == 0) { + /* cancel outstanding keyring operations */ + g_cancellable_cancel (r->cancellable); + + r->get_callback (NM_SECRET_AGENT_OLD (r->agent), r->connection, NULL, error, r->callback_data); + g_hash_table_iter_remove (&iter); + g_signal_emit (r->agent, signals[CANCEL_SECRETS], 0, GUINT_TO_POINTER (r->id)); + } + } + + g_error_free (error); +} + +/*******************************************************/ + +static void +save_request_try_complete (Request *r) +{ + /* Only call the SaveSecrets callback and free the request when all the + * secrets have been saved to the keyring. + */ + if (r->keyring_calls == 0) { + if (!g_cancellable_is_cancelled (r->cancellable)) + r->save_callback (NM_SECRET_AGENT_OLD (r->agent), r->connection, NULL, r->callback_data); + request_free (r); + } +} + +static void +save_secret_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + secret_password_store_finish (result, NULL); + save_request_try_complete (user_data); +} + + + +static GHashTable * +_create_keyring_add_attr_list (NMConnection *connection, + const char *setting_name, + const char *setting_key, + char **out_display_name) +{ + const char *connection_id, *connection_uuid; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (setting_name != NULL, NULL); + g_return_val_if_fail (setting_key != NULL, NULL); + + connection_uuid = nm_connection_get_uuid (connection); + g_assert (connection_uuid); + connection_id = nm_connection_get_id (connection); + g_assert (connection_id); + + if (out_display_name) { + *out_display_name = g_strdup_printf ("Network secret for %s/%s/%s", + connection_id, + setting_name, + setting_key); + } + + return secret_attributes_build (&network_manager_secret_schema, + KEYRING_UUID_TAG, connection_uuid, + KEYRING_SN_TAG, setting_name, + KEYRING_SK_TAG, setting_key, + NULL); +} + +static void +save_one_secret (Request *r, + NMSetting *setting, + const char *key, + const char *secret, + const char *display_name) +{ + GHashTable *attrs; + char *alt_display_name = NULL; + const char *setting_name; + NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; + + /* Don't system-owned or always-ask secrets */ + if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) + return; + if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED) + return; + + setting_name = nm_setting_get_name (setting); + g_assert (setting_name); + + attrs = _create_keyring_add_attr_list (r->connection, + setting_name, + key, + display_name ? NULL : &alt_display_name); + g_assert (attrs); + + secret_password_storev (&network_manager_secret_schema, attrs, NULL, + display_name ? display_name : alt_display_name, secret, + r->cancellable, save_secret_cb, r); + r->keyring_calls++; + + g_hash_table_unref (attrs); + g_free (alt_display_name); +} + +static void +vpn_secret_iter_cb (const char *key, const char *secret, gpointer user_data) +{ + Request *r = user_data; + NMSetting *setting; + const char *service_name, *id; + char *display_name; + + if (secret && strlen (secret)) { + setting = nm_connection_get_setting (r->connection, NM_TYPE_SETTING_VPN); + g_assert (setting); + service_name = nm_setting_vpn_get_service_type (NM_SETTING_VPN (setting)); + g_assert (service_name); + id = nm_connection_get_id (r->connection); + g_assert (id); + + display_name = g_strdup_printf ("VPN %s secret for %s/%s/" NM_SETTING_VPN_SETTING_NAME, + key, + id, + service_name); + save_one_secret (r, setting, key, secret, display_name); + g_free (display_name); + } +} + +static void +write_one_secret_to_keyring (NMSetting *setting, + const char *key, + const GValue *value, + GParamFlags flags, + gpointer user_data) +{ + Request *r = user_data; + GType type = G_VALUE_TYPE (value); + const char *secret; + + /* Non-secrets obviously don't get saved in the keyring */ + if (!(flags & NM_SETTING_PARAM_SECRET)) + return; + + if (NM_IS_SETTING_VPN (setting) && (g_strcmp0 (key, NM_SETTING_VPN_SECRETS) == 0)) { + g_return_if_fail (type == G_TYPE_HASH_TABLE); + + /* Process VPN secrets specially since it's a hash of secrets, not just one */ + nm_setting_vpn_foreach_secret (NM_SETTING_VPN (setting), vpn_secret_iter_cb, r); + } else { + /* FIXME: password-raw is not string */ + if (!g_strcmp0 (key, NM_SETTING_802_1X_PASSWORD_RAW)) + return; + g_return_if_fail (type == G_TYPE_STRING); + secret = g_value_get_string (value); + if (secret && strlen (secret)) + save_one_secret (r, setting, key, secret, NULL); + } +} + +static void +save_delete_cb (NMSecretAgentOld *agent, + NMConnection *connection, + GError *error, + gpointer user_data) +{ + Request *r = user_data; + + /* Ignore errors; now save all new secrets */ + nm_connection_for_each_setting_value (connection, write_one_secret_to_keyring, r); + + /* If no secrets actually got saved there may be nothing to do so + * try to complete the request here. If there were secrets to save the + * request will get completed when those keyring calls return. + */ + save_request_try_complete (r); +} + +static void +save_secrets (NMSecretAgentOld *agent, + NMConnection *connection, + const char *connection_path, + NMSecretAgentOldSaveSecretsFunc callback, + gpointer callback_data) +{ + AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent); + Request *r; + + r = request_new (agent, connection, connection_path, NULL, NULL, FALSE, NULL, callback, NULL, callback_data); + g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r); + + g_message("save secret ........................"); + /* First delete any existing items in the keyring */ + nm_secret_agent_old_delete_secrets (agent, connection, save_delete_cb, r); +} + +/*******************************************************/ + +static void +delete_find_items_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + Request *r = user_data; + GError *secret_error = NULL; + GError *error = NULL; + + r->keyring_calls--; + if (g_cancellable_is_cancelled (r->cancellable)) { + /* Callback already called by NM or dispose */ + request_free (r); + return; + } + + secret_password_clear_finish (result, &secret_error); + if (secret_error != NULL) { + error = g_error_new (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "The request could not be completed (%s)", + secret_error->message); + g_error_free (secret_error); + } + + r->delete_callback (r->agent, r->connection, error, r->callback_data); + request_free (r); +} + +static void +delete_secrets (NMSecretAgentOld *agent, + NMConnection *connection, + const char *connection_path, + NMSecretAgentOldDeleteSecretsFunc callback, + gpointer callback_data) +{ + AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent); + Request *r; + NMSettingConnection *s_con; + const char *uuid; + + r = request_new (agent, connection, connection_path, NULL, NULL, FALSE, NULL, NULL, callback, callback_data); + g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r); + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + uuid = nm_setting_connection_get_uuid (s_con); + g_assert (uuid); + + secret_password_clear (&network_manager_secret_schema, r->cancellable, + delete_find_items_cb, r, + KEYRING_UUID_TAG, uuid, + NULL); + r->keyring_calls++; +} + +void +applet_agent_handle_vpn_only (AppletAgent *agent, gboolean vpn_only) +{ + g_return_if_fail (agent != NULL); + g_return_if_fail (APPLET_IS_AGENT (agent)); + + APPLET_AGENT_GET_PRIVATE (agent)->vpn_only = vpn_only; +} + +/*******************************************************/ + +AppletAgent * +applet_agent_new (GError **error) +{ + AppletAgent *agent; + + agent = g_object_new (APPLET_TYPE_AGENT, + NM_SECRET_AGENT_OLD_IDENTIFIER, "org.freedesktop.nm-applet", + NM_SECRET_AGENT_OLD_CAPABILITIES, NM_SECRET_AGENT_CAPABILITY_VPN_HINTS, + NULL); + if (!g_initable_init (G_INITABLE (agent), NULL, error)) { + g_object_unref (agent); + return NULL; + } + + return agent; +} + +static void +applet_agent_init (AppletAgent *self) +{ + AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (self); + + priv->requests = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void +dispose (GObject *object) +{ + AppletAgent *self = APPLET_AGENT (object); + AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (self); + + if (!priv->disposed) { + GHashTableIter iter; + Request *r; + + /* Mark any outstanding requests as canceled */ + g_hash_table_iter_init (&iter, priv->requests); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &r)) + g_cancellable_cancel (r->cancellable); + + g_hash_table_destroy (priv->requests); + priv->disposed = TRUE; + } + + G_OBJECT_CLASS (applet_agent_parent_class)->dispose (object); +} + +static void applet_agent_class_init (AppletAgentClass *agent_class) +{ + g_message("applet_agent_class_init........................"); + GObjectClass *object_class = G_OBJECT_CLASS (agent_class); + NMSecretAgentOldClass *parent_class = NM_SECRET_AGENT_OLD_CLASS (agent_class); + + g_type_class_add_private (agent_class, sizeof (AppletAgentPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + parent_class->get_secrets = get_secrets; + parent_class->cancel_get_secrets = cancel_get_secrets; + parent_class->save_secrets = save_secrets; + parent_class->delete_secrets = delete_secrets; + + /* Signals */ + signals[GET_SECRETS] = + g_signal_new (APPLET_AGENT_GET_SECRETS, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (AppletAgentClass, get_secrets), + NULL, NULL, NULL, + G_TYPE_NONE, 7, + G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_POINTER); + + signals[CANCEL_SECRETS] = + g_signal_new (APPLET_AGENT_CANCEL_SECRETS, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (AppletAgentClass, cancel_secrets), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} + diff --git a/src/backend/dbus-interface/kylinagent.h b/src/backend/dbus-interface/kylinagent.h new file mode 100644 index 00000000..82ec3445 --- /dev/null +++ b/src/backend/dbus-interface/kylinagent.h @@ -0,0 +1,60 @@ +#ifndef __KYLINNMANGENT_H__ +#define __KYLINNMANGENT_H__ + +#ifdef __cplusplus +extern "C"{ +#endif + +#include +#include +#include +#include +#include + +#define APPLET_TYPE_AGENT (applet_agent_get_type ()) +#define APPLET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), APPLET_TYPE_AGENT, AppletAgent)) +#define APPLET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), APPLET_TYPE_AGENT, AppletAgentClass)) +#define APPLET_IS_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APPLET_TYPE_AGENT)) +#define APPLET_IS_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APPLET_TYPE_AGENT)) +#define APPLET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), APPLET_TYPE_AGENT, AppletAgentClass)) + +#define APPLET_AGENT_GET_SECRETS "get-secrets" +#define APPLET_AGENT_CANCEL_SECRETS "cancel-secrets" + +typedef struct { + NMSecretAgentOld parent; +} AppletAgent; + +typedef void (*AppletAgentSecretsCallback) (AppletAgent *self, + GVariant *secrets, + GError *error, + gpointer user_data); + +typedef struct { + NMSecretAgentOldClass parent_class; + + void (*get_secrets) (AppletAgent *self, + void *request_id, + NMConnection *connection, + const char *setting_name, + const char **hints, + guint32 flags, + AppletAgentSecretsCallback callback, + gpointer callback_data); + + void (*cancel_secrets) (AppletAgent *self, + void *request_id); +} AppletAgentClass; + + +GType applet_agent_get_type (void) G_GNUC_CONST; + +AppletAgent *applet_agent_new (GError **error); + +void applet_agent_handle_vpn_only (AppletAgent *agent, gboolean vpn_only); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/backend/dbus-interface/kylinagentinterface.c b/src/backend/dbus-interface/kylinagentinterface.c new file mode 100644 index 00000000..35a7d41a --- /dev/null +++ b/src/backend/dbus-interface/kylinagentinterface.c @@ -0,0 +1,428 @@ +#include "kylinagentinterface.h" +#include "kylinagent.h" +#include "kylinvpnrequest.h" + +#include +#include + +GSList * secrets_reqs; +#if 0 +typedef struct _SecretsRequest SecretsRequest; +typedef void (*SecretsRequestFreeFunc) (SecretsRequest *req); + +struct _SecretsRequest { + size_t totsize; + gpointer reqid; + char *setting_name; + char **hints; + guint32 flags; + AppletAgent *agent; + AppletAgentSecretsCallback callback; + gpointer callback_data; + + NMConnection *connection; + + /* Class-specific stuff */ + SecretsRequestFreeFunc free_func; +}; +#endif +typedef struct { + SecretsRequest req; + GtkWidget *dialog; +} NMWifiInfo; + +static AppletAgent * kylinAgent = NULL; + +void +applet_secrets_request_set_free_func (SecretsRequest *req, + SecretsRequestFreeFunc free_func) +{ + req->free_func = free_func; +} + +static void +show_ignore_focus_stealing_prevention (GtkWidget *widget) +{ + gtk_widget_realize (widget); + gtk_widget_show (widget); + gtk_window_present (GTK_WINDOW (widget)); +} + +static GVariant * +remove_unwanted_secrets (GVariant *secrets, gboolean keep_8021X) +{ + GVariant *copy, *setting_dict; + const char *setting_name; + GVariantBuilder conn_builder; + GVariantIter conn_iter; + + g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION); + g_variant_iter_init (&conn_iter, secrets); + while (g_variant_iter_next (&conn_iter, "{&s@a{sv}}", &setting_name, &setting_dict)) { + if ( !strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) + || (!strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) && keep_8021X)) + g_variant_builder_add (&conn_builder, "{s@a{sv}}", setting_name, setting_dict); + + g_variant_unref (setting_dict); + } + copy = g_variant_builder_end (&conn_builder); + g_variant_unref (secrets); + + return copy; +} + +static void +free_wifi_info (SecretsRequest *req) +{ + NMWifiInfo *info = (NMWifiInfo *) req; + + if (info->dialog) { + gtk_widget_hide (info->dialog); + gtk_widget_destroy (info->dialog); + info->dialog = NULL; + } +} + + +void +applet_secrets_request_free (SecretsRequest *req) +{ + g_return_if_fail (req != NULL); + + if (req->free_func) + req->free_func (req); + + secrets_reqs = g_slist_remove (secrets_reqs, req); + + g_object_unref (req->connection); + g_free (req->setting_name); + g_strfreev (req->hints); + memset (req, 0, req->totsize); + g_free (req); +} + +void +applet_secrets_request_complete (SecretsRequest *req, + GVariant *settings, + GError *error) +{ + req->callback (req->agent, error ? NULL : settings, error, req->callback_data); +} + +static void +get_secrets_dialog_response_cb (GtkDialog *foo, + gint response, + gpointer user_data) +{ + SecretsRequest *req = user_data; + NMWifiInfo *info = (NMWifiInfo *) req; + NMAWifiDialog *dialog = NMA_WIFI_DIALOG (info->dialog); + NMConnection *connection = NULL; + NMSettingWirelessSecurity *s_wireless_sec; + GVariant *secrets = NULL; + const char *key_mgmt, *auth_alg; + gboolean keep_8021X = FALSE; + GError *error = NULL; + + if (response != GTK_RESPONSE_OK) { + g_set_error (&error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_USER_CANCELED, + "%s.%d (%s): canceled", + __FILE__, __LINE__, __func__); + goto done; + } + + connection = nma_wifi_dialog_get_connection (dialog, NULL, NULL); + if (!connection) { + g_set_error (&error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "%s.%d (%s): couldn't get connection from Wi-Fi dialog.", + __FILE__, __LINE__, __func__); + goto done; + } + + /* Second-guess which setting NM wants secrets for. */ + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); + if (!s_wireless_sec) { + g_set_error (&error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "%s.%d (%s): requested setting '802-11-wireless-security'" + " didn't exist in the connection.", + __FILE__, __LINE__, __func__); + goto done; /* Unencrypted */ + } + + secrets = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); + if (!secrets) { + g_set_error (&error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "%s.%d (%s): failed to hash connection '%s'.", + __FILE__, __LINE__, __func__, nm_connection_get_id (connection)); + goto done; + } + /* If the user chose an 802.1x-based auth method, return 802.1x secrets, + * not wireless secrets. Can happen with Dynamic WEP, because NM doesn't + * know the capabilities of the AP (since Dynamic WEP APs don't broadcast + * beacons), and therefore defaults to requesting WEP secrets from the + * wireless-security setting, not the 802.1x setting. + */ + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec); + if (!strcmp (key_mgmt, "ieee8021x") || !strcmp (key_mgmt, "wpa-eap")) { + /* LEAP secrets aren't in the 802.1x setting */ + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wireless_sec); + if (!auth_alg || strcmp (auth_alg, "leap")) { + NMSetting8021x *s_8021x; + + s_8021x = nm_connection_get_setting_802_1x (connection); + if (!s_8021x) { + g_set_error (&error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "%s.%d (%s): requested setting '802-1x' didn't" + " exist in the connection.", + __FILE__, __LINE__, __func__); + goto done; + } + keep_8021X = TRUE; + } + } + + /* Remove all not-relevant secrets (inner dicts) */ + secrets = remove_unwanted_secrets (secrets, keep_8021X); + g_variant_take_ref (secrets); + +done: + applet_secrets_request_complete (req, secrets, error); + applet_secrets_request_free (req); + + if (secrets) + g_variant_unref (secrets); + if (connection) + nm_connection_clear_secrets (connection); +} + +static gboolean +wifi_get_secrets (SecretsRequest *req, GError **error) +{ + NMWifiInfo *info = (NMWifiInfo *) req; + + g_return_val_if_fail (!info->dialog, FALSE); + +#if GTK_CHECK_VERSION(3,90,0) + gtk_init (); +#else + int argc = 0; + char ***argv = NULL; + gtk_init (&argc, &argv); +#endif + + NMClient *nm_client = nm_client_new (NULL, NULL); + if (!nm_client) { + g_set_error (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "%s.%d (%s): create nm client failed.", + __FILE__, __LINE__, __func__); + g_warning ("create nm client failed"); + goto l_out; + } + + + info->dialog = nma_wifi_dialog_new_for_secrets (nm_client, + req->connection, + req->setting_name, + (const char *const*) req->hints); + + if (info->dialog) { + applet_secrets_request_set_free_func (req, free_wifi_info); + g_signal_connect (info->dialog, "response", + G_CALLBACK (get_secrets_dialog_response_cb), + info); + show_ignore_focus_stealing_prevention (info->dialog); + } else { + g_set_error (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "%s.%d (%s): couldn't display secrets UI", + __FILE__, __LINE__, __func__); + } + + g_clear_object(&nm_client); + +l_out: + return !!info->dialog; + //return 0; +} + +static SecretsRequest * +applet_secrets_request_new (size_t totsize, + NMConnection *connection, + gpointer request_id, + const char *setting_name, + const char **hints, + guint32 flags, + AppletAgentSecretsCallback callback, + gpointer callback_data, + AppletAgent *agent) +{ + SecretsRequest *req; + + g_return_val_if_fail (totsize >= sizeof (SecretsRequest), NULL); + g_return_val_if_fail (connection != NULL, NULL); + + req = g_malloc0 (totsize); + req->totsize = totsize; + req->connection = g_object_ref (connection); + req->reqid = request_id; + req->setting_name = g_strdup (setting_name); + req->hints = g_strdupv ((char **) hints); + req->flags = flags; + req->callback = callback; + req->callback_data = callback_data; + req->agent = agent; + return req; +} + +static void +get_existing_secrets_cb (NMSecretAgentOld *agent, + NMConnection *connection, + GVariant *secrets, + GError *secrets_error, + gpointer user_data) +{ + SecretsRequest *req = user_data; + //NMADeviceClass *dclass; + GError *error = NULL; + + if (secrets) + nm_connection_update_secrets (connection, req->setting_name, secrets, NULL); + else + nm_connection_clear_secrets (connection); + + /* Let the device class handle secrets */ + if (!wifi_get_secrets(req, &error)) { + g_warning ("%s:%d - %s", __func__, __LINE__, error ? error->message : "(unknown)"); + applet_secrets_request_complete (req, NULL, error); + applet_secrets_request_free (req); + g_error_free (error); + } + /* Otherwise success; wait for the secrets callback */ +} + + +static void +applet_agent_get_secrets_cb (AppletAgent *agent, + gpointer request_id, + NMConnection *connection, + const char *setting_name, + const char **hints, + guint32 flags, + AppletAgentSecretsCallback callback, + gpointer callback_data, + gpointer user_data) +{ + NMSettingConnection *s_con; + GError *error = NULL; + SecretsRequest *req = NULL; + + s_con = nm_connection_get_setting_connection (connection); + g_return_if_fail (s_con != NULL); + + /* VPN secrets get handled a bit differently */ + if (!strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_VPN_SETTING_NAME)) { + req = applet_secrets_request_new (applet_vpn_request_get_secrets_size (), + connection, + request_id, + setting_name, + hints, + flags, + callback, + callback_data, + agent); + if (!applet_vpn_request_get_secrets (req, &error)) + goto error; + + secrets_reqs = g_slist_prepend (secrets_reqs, req); + return; + } + + + req = applet_secrets_request_new (sizeof (NMWifiInfo), + connection, + request_id, + setting_name, + hints, + flags, + callback, + callback_data, + agent); + secrets_reqs = g_slist_prepend (secrets_reqs, req); + + /* Get existing secrets, if any */ + nm_secret_agent_old_get_secrets (NM_SECRET_AGENT_OLD (agent), + connection, + setting_name, + hints, + NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE, + get_existing_secrets_cb, + req); + return; + +error: + g_warning ("%s", error->message); + callback (agent, NULL, error, callback_data); + g_error_free (error); + + if (req) + applet_secrets_request_free (req); +} + +static void +applet_agent_cancel_secrets_cb (AppletAgent *agent, + gpointer request_id, + gpointer user_data) +{ + GSList *iter, *next; + + for (iter = secrets_reqs; iter; iter = next) { + SecretsRequest *req = iter->data; + + next = g_slist_next (iter); + + if (req->reqid == request_id) { + /* cancel and free this password request */ + applet_secrets_request_free (req); + break; + } + } +} + +void agent_init() +{ + if (NULL != kylinAgent) { + return; + } + GError *error = NULL; + kylinAgent = applet_agent_new (&error); + + g_signal_connect (kylinAgent, APPLET_AGENT_GET_SECRETS, + G_CALLBACK (applet_agent_get_secrets_cb), NULL); + g_signal_connect (kylinAgent, APPLET_AGENT_CANCEL_SECRETS, + G_CALLBACK (applet_agent_cancel_secrets_cb), NULL); + + + return; +} + +void agent_clear() +{ + if (NULL == kylinAgent) { + return; + } + g_clear_object (&kylinAgent); + kylinAgent = NULL; +} diff --git a/src/backend/dbus-interface/kylinagentinterface.h b/src/backend/dbus-interface/kylinagentinterface.h new file mode 100644 index 00000000..2a097923 --- /dev/null +++ b/src/backend/dbus-interface/kylinagentinterface.h @@ -0,0 +1,10 @@ +#ifndef __KYLINAGENTINTERFACE_H__ +#define __KYLINAGENTINTERFACE_H__ +#ifdef __cplusplus +extern "C"{ +void agent_init(); +void agent_clear(); +} +#endif + +#endif // NMAGENT_H diff --git a/src/backend/dbus-interface/kylinapconnectitem.cpp b/src/backend/dbus-interface/kylinapconnectitem.cpp new file mode 100644 index 00000000..1f8945d7 --- /dev/null +++ b/src/backend/dbus-interface/kylinapconnectitem.cpp @@ -0,0 +1,19 @@ + +#include "kylinapconnectitem.h" + +KyApConnectItem::KyApConnectItem(QObject *parent) : QObject(parent) +{ + m_connectName = ""; + m_connectUuid = ""; + m_connectSsid = ""; + m_ifaceName = ""; + m_password = ""; + m_band = ""; + m_isActivated = false; +} + + +KyApConnectItem::~KyApConnectItem() +{ + +} diff --git a/src/backend/dbus-interface/kylinapconnectitem.h b/src/backend/dbus-interface/kylinapconnectitem.h new file mode 100644 index 00000000..f7a1e27d --- /dev/null +++ b/src/backend/dbus-interface/kylinapconnectitem.h @@ -0,0 +1,25 @@ +#ifndef KYLINAPCONNECTITEM_H +#define KYLINAPCONNECTITEM_H + +#include + +class KyApConnectItem : public QObject +{ + Q_OBJECT + +public: + explicit KyApConnectItem(QObject *parent = nullptr); + ~KyApConnectItem(); + +public: + QString m_connectName; + QString m_connectSsid; + QString m_connectUuid; + QString m_ifaceName; + QString m_password; + QString m_band; + bool m_isActivated; + +}; + +#endif // KYLINAPCONNECTITEM_H diff --git a/src/backend/dbus-interface/kylinbluetoothconnectitem.cpp b/src/backend/dbus-interface/kylinbluetoothconnectitem.cpp new file mode 100644 index 00000000..1fc2897b --- /dev/null +++ b/src/backend/dbus-interface/kylinbluetoothconnectitem.cpp @@ -0,0 +1,29 @@ +#include "kylinbluetoothconnectitem.h" + + +KyBluetoothConnectItem::KyBluetoothConnectItem(QObject *parent) : QObject(parent) +{ + m_connectName = ""; + m_connectUuid = ""; + + m_deviceAddress = ""; + + m_ipv4Address = ""; + m_ipv6Address = ""; + + m_state = NetworkManager::ActiveConnection::State::Deactivated; +} + +KyBluetoothConnectItem::~KyBluetoothConnectItem() +{ + m_connectName = ""; + m_connectUuid = ""; + + m_deviceAddress = ""; + + m_ipv4Address = ""; + m_ipv6Address = ""; + + m_state = NetworkManager::ActiveConnection::State::Deactivated; +} + diff --git a/src/backend/dbus-interface/kylinbluetoothconnectitem.h b/src/backend/dbus-interface/kylinbluetoothconnectitem.h new file mode 100644 index 00000000..3bedeb21 --- /dev/null +++ b/src/backend/dbus-interface/kylinbluetoothconnectitem.h @@ -0,0 +1,33 @@ +#ifndef KYLINBLUETOOTHCONNECTITEM_H +#define KYLINBLUETOOTHCONNECTITEM_H + +#include +#include +#include +#include + +class KyBluetoothConnectItem : public QObject +{ + Q_OBJECT +public: + explicit KyBluetoothConnectItem(QObject *parent = nullptr); + ~KyBluetoothConnectItem(); + +public: + void dumpInfo(); + +public: + QString m_connectName; + QString m_connectUuid; + + QString m_deviceAddress; + + QString m_ipv4Address; + QString m_ipv6Address; + + NetworkManager::ActiveConnection::State m_state; //deactive、activing and actived + //QString m_itemType; //activeconnect or connect + +}; + +#endif // KYLINBLUETOOTHCONNECTITEM_H diff --git a/src/backend/dbus-interface/kylinconnectitem.cpp b/src/backend/dbus-interface/kylinconnectitem.cpp new file mode 100644 index 00000000..011dee63 --- /dev/null +++ b/src/backend/dbus-interface/kylinconnectitem.cpp @@ -0,0 +1,35 @@ + +#include "kylinconnectitem.h" + +KyConnectItem::KyConnectItem(QObject *parent) : QObject(parent) +{ + m_connectName = ""; + m_connectUuid = ""; + m_connectPath = ""; + + m_ifaceName = ""; + + m_connectState = NetworkManager::ActiveConnection::State::Unknown; //deactive、activing and actived + m_itemType = NetworkManager::ConnectionSettings::ConnectionType::Unknown; +} + +KyConnectItem::~KyConnectItem() +{ + +} + +void KyConnectItem::setConnectUuid(QString uuid) +{ + m_connectUuid = uuid; +} + +void KyConnectItem::dumpInfo() +{ + qDebug()<<"wired connection item info:"; + qDebug()<<"connect name:"< +#include "kylinnetworkresourcemanager.h" + +class KyConnectItem : public QObject +{ + Q_OBJECT +public: + explicit KyConnectItem(QObject *parent = nullptr); + ~KyConnectItem(); + +public: + void dumpInfo(); + + void setConnectUuid(QString uuid); + +public: + QString m_connectName; + QString m_connectUuid; + QString m_connectPath; + + QString m_ifaceName; + + NetworkManager::ActiveConnection::State m_connectState; //deactive、activing and actived + + NetworkManager::ConnectionSettings::ConnectionType m_itemType; //wired or wireless vpn etc +}; + +#endif // KYLINWIREDCONNECTITEM_H diff --git a/src/backend/dbus-interface/kylinconnectoperation.cpp b/src/backend/dbus-interface/kylinconnectoperation.cpp new file mode 100644 index 00000000..daf8234f --- /dev/null +++ b/src/backend/dbus-interface/kylinconnectoperation.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include + +KyConnectOperation::KyConnectOperation(QObject *parent) : QObject(parent) +{ + m_networkResourceInstance = KyNetworkResourceManager::getInstance(); +} + +KyConnectOperation::~KyConnectOperation() +{ + m_networkResourceInstance = nullptr; +} + +void KyConnectOperation::ipv4SettingSet( + NetworkManager::Ipv4Setting::Ptr &ipv4Setting, + const KyConnectSetting &connectSettingsInfo) +{ + ipv4Setting->setInitialized(true); + ipv4Setting->setDns(connectSettingsInfo.m_ipv4Dns); + + if (CONFIG_IP_DHCP == connectSettingsInfo.m_ipv4ConfigIpType) { + ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Automatic); + return; + } else { + ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Manual); + } + ipv4Setting->setAddresses(connectSettingsInfo.m_ipv4Address); + + return; +} +void KyConnectOperation::ipv6SettingSet( + NetworkManager::Ipv6Setting::Ptr &ipv6Setting, + const KyConnectSetting &connectSettingsInfo) +{ + ipv6Setting->setInitialized(true); + ipv6Setting->setDns(connectSettingsInfo.m_ipv6Dns); + + if (CONFIG_IP_DHCP == connectSettingsInfo.m_ipv6ConfigIpType) { + ipv6Setting->setMethod(NetworkManager::Ipv6Setting::Automatic); + ipv6Setting->setPrivacy(NetworkManager::Ipv6Setting::Disabled); + return; + } + + ipv6Setting->setMethod(NetworkManager::Ipv6Setting::Manual); + ipv6Setting->setAddresses(connectSettingsInfo.m_ipv6Address); + + return ; +} + +void KyConnectOperation::connectSettingSet( + NetworkManager::ConnectionSettings::Ptr connectionSettings, + const KyConnectSetting &connectSettingsInfo) +{ + connectionSettings->setId(connectSettingsInfo.m_connectName); + connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); + connectionSettings->setAutoconnect(true); + connectionSettings->setAutoconnectPriority(0); + if (!connectSettingsInfo.m_ifaceName.isEmpty()) { + connectionSettings->setInterfaceName(connectSettingsInfo.m_ifaceName); + } + return; +} + +void KyConnectOperation::setAutoConnect(NetworkManager::ConnectionSettings::Ptr &connectSetting, + bool bAutoConnect) +{ + if (connectSetting.isNull()) { + return; + } + + connectSetting->setAutoconnect(bAutoConnect); + + return; +} + +void KyConnectOperation::createConnect(KyConnectSetting &connectSettingsInfo) +{ + qDebug()<<"create connect need to do"; + return; +} + +void KyConnectOperation::updateConnect(NetworkManager::ConnectionSettings::Ptr connectionSettings, const KyConnectSetting &connectSettingsInfo) +{ + qDebug()<<"update connect"<uuid(); + + NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast(); + ipv4SettingSet(ipv4Setting, connectSettingsInfo); + + NetworkManager::Ipv6Setting::Ptr ipv6Setting = connectionSettings->setting(NetworkManager::Setting::Ipv6).dynamicCast(); + ipv6SettingSet(ipv6Setting, connectSettingsInfo); + +// NetworkManager::WiredSetting::Ptr wiredSetting = connectionSettings->setting(NetworkManager::Setting::Wired).dynamicCast(); +// wiredSetting->setInitialized(true); + + return ; +} + +void KyConnectOperation::deleteConnect(const QString &connectUuid) +{ + qDebug()<<"delete connect uuid " << connectUuid; + + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(connectUuid); + if (nullptr == connectPtr) { + QString errorMessage = tr("it can not find connection") + connectUuid; + qWarning()<remove(); + + return ; +} + +void KyConnectOperation::activateConnection(const QString connectUuid, const QString deviceName) +{ + QString connectPath = ""; + QString deviceIdentifier = ""; + QString connectName = ""; + QString specificObject = ""; + NetworkManager::Connection::Ptr connectPtr = nullptr; + + qDebug()<<"it will activate connect"<settings()->connectionType()) { + QString errorMessage = tr("the connect type is") + + connectPtr->settings()->connectionType() + + tr(", but it is not wired"); + qWarning()<path(); + connectName = connectPtr->name(); +// deviceName = connectPtr->settings()->interfaceName(); + +// for (auto const & dev : m_networkResourceInstance->m_devices) { +// for (auto const & dev_conn : dev->availableConnections()) { +// if (dev_conn == connectPtr) { +// deviceIdentifier = dev->uni(); +// deviceName = dev->interfaceName(); +// break; +// } +// } +// } + + auto dev = m_networkResourceInstance->findDeviceInterface(deviceName); + if (!dev.isNull()) { + deviceIdentifier = dev->uni(); + } + + + if (deviceIdentifier.isEmpty()) { + QString errorMessage = tr("device Identifier is empty, its name") + deviceName; + qWarning() << errorMessage; + Q_EMIT activateConnectionError(errorMessage); + return ; + } + + qDebug() <<"active wired connect: path "<< connectPath + << "device identify " << deviceIdentifier + << "connect name " << connectName + << "device name" << deviceName + << "specific parameter"<< specificObject; + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::activateConnection(connectPath, deviceIdentifier, specificObject), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [this, connectName, deviceName] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = tr("activate connection failed: ") + watcher->error().message(); + qWarning()<activateConnectionError(errorMessage); + } else { + qWarning()<<"active wired connect complete."; + } + + watcher->deleteLater(); + }); + + return ; +} + +void KyConnectOperation::deactivateConnection(const QString activeConnectName, const QString &activeConnectUuid) +{ + NetworkManager::ActiveConnection::Ptr activateConnectPtr = nullptr; + + qDebug()<<"deactivetate connect name"<getActiveConnect(activeConnectUuid); + if (nullptr == activateConnectPtr) { + QString errorMessage = tr("it can not find the activate connect") + + activeConnectName + tr("uuid") + activeConnectUuid; + qWarning()<path(); + QDBusPendingReply<> reply = NetworkManager::deactivateConnection(activateConnectPtr->path()); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, &QDBusPendingCallWatcher::finished, [this, activateConnectPtr] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = tr("deactivation of connection") + + activateConnectPtr->connection()->name() + tr("failed:") + + watcher->error().message(); + + qWarning()<deactivateConnectionError(errorMessage); + } else { + qWarning() << "deactive connect operation finished" << activateConnectPtr->connection()->name(); + } + watcher->deleteLater(); + }); + + return; +} diff --git a/src/backend/dbus-interface/kylinconnectoperation.h b/src/backend/dbus-interface/kylinconnectoperation.h new file mode 100644 index 00000000..fc735d7e --- /dev/null +++ b/src/backend/dbus-interface/kylinconnectoperation.h @@ -0,0 +1,44 @@ +#ifndef KYLINCONNECTOPERATION_H +#define KYLINCONNECTOPERATION_H + +#include "kylinnetworkresourcemanager.h" +#include "kylinconnectsetting.h" + +class KyConnectOperation : public QObject +{ + Q_OBJECT +public: + explicit KyConnectOperation(QObject *parent = nullptr); + ~KyConnectOperation(); + +public: + void createConnect(KyConnectSetting &connectSettingsInfo); + void updateConnect(NetworkManager::ConnectionSettings::Ptr connectionSettings, const KyConnectSetting &connectSettingsInfo); + void deleteConnect(const QString &connectUuid); + void activateConnection(const QString connectUuid, const QString deviceName); + void deactivateConnection(const QString activeConnectName, const QString &activeConnectUuid); + +public: + void connectSettingSet( + NetworkManager::ConnectionSettings::Ptr connectionSettings, + const KyConnectSetting &connectSettingsInfo); + void ipv4SettingSet(NetworkManager::Ipv4Setting::Ptr &ipv4Setting, + const KyConnectSetting &connectSettingsInfo); + void ipv6SettingSet(NetworkManager::Ipv6Setting::Ptr &ipv6Setting, + const KyConnectSetting &connectSettingsInfo); + void setAutoConnect(NetworkManager::ConnectionSettings::Ptr &connectSetting, + bool bAutoConnect); + inline void errorProcess(QString errorMessage); + +signals: + void createConnectionError(QString errorMessage); + void updateConnectionError(QString errorMessage); + void deleteConnectionError(QString errorMessage); + void activateConnectionError(QString errorMessage); + void deactivateConnectionError(QString errorMessage); + +protected: + KyNetworkResourceManager *m_networkResourceInstance = nullptr; +}; + +#endif // KYLINCONNECTOPERATION_H diff --git a/src/backend/dbus-interface/kylinconnectresource.cpp b/src/backend/dbus-interface/kylinconnectresource.cpp new file mode 100644 index 00000000..00d0570a --- /dev/null +++ b/src/backend/dbus-interface/kylinconnectresource.cpp @@ -0,0 +1,777 @@ + +#include "kylinconnectresource.h" +#include "kywirelessconnectoperation.h" +#include "kylinutil.h" + +#include +#include +#include +#include +#include + +const QString str2GBand = "2.4Ghz"; +const QString str5GBand = "5Ghz"; + +static bool subLanListSort(const KyConnectItem* info1, const KyConnectItem* info2) +{ + QString name1 = info1->m_connectName; + QString name2 = info2->m_connectName; + bool result = true; + if (QString::compare(name1, name2, Qt::CaseInsensitive) > 0) { + result = false; + } + return result; +} + +static void lanListSort(QList &list) +{ + qSort(list.begin(), list.end(), subLanListSort); +} + +KyConnectResourse::KyConnectResourse(QObject *parent) : QObject(parent) +{ + m_networkResourceInstance = KyNetworkResourceManager::getInstance(); + + connect(m_networkResourceInstance, &KyNetworkResourceManager::connectionAdd, this, &KyConnectResourse::connectionAdd); + connect(m_networkResourceInstance, &KyNetworkResourceManager::connectionRemove, this, &KyConnectResourse::connectionRemove); + connect(m_networkResourceInstance, &KyNetworkResourceManager::connectionUpdate, this, &KyConnectResourse::connectionUpdate); + connect(m_networkResourceInstance, &KyNetworkResourceManager::connectivityChanged, this, &KyConnectResourse::connectivityChanged); +} + +KyConnectResourse::~KyConnectResourse() +{ + m_networkResourceInstance = nullptr; +} + +KyConnectItem *KyConnectResourse::getConnectionItem(NetworkManager::Connection::Ptr connectPtr, QString devName) +{ + if (nullptr == connectPtr) { + qWarning()<<"[KyConnectResourse]"<<"the connect is empty"; + return nullptr; + } + + KyConnectItem *connectionItem = new KyConnectItem(); + connectionItem->m_connectName = connectPtr->name(); + connectionItem->m_connectUuid = connectPtr->uuid(); + connectionItem->m_connectPath = connectPtr->path(); + + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + connectionItem->m_ifaceName = settingPtr->interfaceName(); + connectionItem->m_itemType = settingPtr->connectionType(); + + if (m_networkResourceInstance->isActivatingConnection(connectPtr->uuid())) { + if (connectionItem->m_ifaceName == "" && devName != "") { + if (isActiveDevice(connectionItem->m_connectUuid, devName)) { + connectionItem->m_connectState = NetworkManager::ActiveConnection::State::Activating; + } else { + connectionItem->m_connectState = NetworkManager::ActiveConnection::State::Deactivated; + } + } else { + connectionItem->m_connectState = NetworkManager::ActiveConnection::State::Activating; + } + } else { + connectionItem->m_connectState = NetworkManager::ActiveConnection::State::Deactivated; + } + return connectionItem; +} + +bool KyConnectResourse::isActiveDevice(QString conUuid, QString devName) +{ + QString deviceName = ""; + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = + m_networkResourceInstance->getActiveConnect(conUuid); + + if (nullptr == activeConnectPtr) { + qWarning()<< "[KyConnectResourse]" <<"it can not find activating connect "<< conUuid; + return false; + } + + QStringList interfaces = activeConnectPtr->devices(); + if (interfaces.isEmpty()) { + qWarning()<< "[KyConnectResourse]" << "get device of activing connection failed."; + return false; + } + + QString ifaceUni = ""; + for (int index=0; index < interfaces.size(); index++) { + ifaceUni = interfaces.at(index); + NetworkManager::Device:: Ptr devicePtr = + m_networkResourceInstance->findDeviceUni(ifaceUni); + deviceName = devicePtr->interfaceName(); + if (deviceName == devName) { + return true; + } + } + return false; +} + +KyConnectItem * KyConnectResourse::getConnectionItemByUuid(QString connectUuid) +{ + NetworkManager::Connection::Ptr connectPtr = + m_networkResourceInstance->getConnect(connectUuid); + + if (nullptr == connectPtr) { + qWarning()<< "[KyConnectResourse]" <<"get connect failed, connect uuid"<isActiveConnection(connectPtr->uuid())) { + qDebug()<<"[KyConnectResourse]"<name()<<"is active connection"; + return nullptr; + } + + KyConnectItem *connectItem = getConnectionItem(connectPtr, ""); + if (nullptr != connectItem) { + //connectItem->dumpInfo(); + return connectItem; + } + + return nullptr; +} + +KyConnectItem * KyConnectResourse::getConnectionItemByUuid(QString connectUuid, QString deviceName) +{ + NetworkManager::Connection::Ptr connectPtr = + m_networkResourceInstance->getConnect(connectUuid); + + if (nullptr == connectPtr) { + qWarning()<< "[KyConnectResourse]" <<"get connect failed, connect uuid"<settings()->interfaceName(); + if (!connectInterface.isEmpty() + && deviceName != connectInterface) { + qDebug()<<"[KyConnectResourse]" << "connect name:"<< connectPtr->name() + << "connect device name" << connectInterface; + return nullptr; + } + + if (m_networkResourceInstance->isActiveConnection(connectPtr->uuid())) { + qDebug()<<"[KyConnectResourse]"<name()<<"is active connection"; + return nullptr; + } + + KyConnectItem *connectItem = getConnectionItem(connectPtr, deviceName); + if (nullptr != connectItem) { + //connectItem->dumpInfo(); + return connectItem; + } + + return nullptr; +} + +void KyConnectResourse::getConnectionList(QString deviceName, + NetworkManager::ConnectionSettings::ConnectionType connectionType, + QList &connectItemList) +{ + NetworkManager::Connection::List connectList; + + qDebug()<<"[KyConnectResourse]"<<"get connections item, device" + <getConnectList(); + + if (connectList.empty()) { + qWarning()<<"[KyConnectResourse]"<<"get connection failed, the connect list is empty"; + return; + } + + NetworkManager::Connection::Ptr connectPtr = nullptr; + for (int index = 0; index < connectList.size(); index++) { + connectPtr = connectList.at(index); + if (connectPtr.isNull()) { + continue; + } + + if (connectionType != connectPtr->settings()->connectionType()) { + qDebug()<<"[KyConnectResourse]"<<"connect name:" << connectPtr->name() + <<"connect type:"<settings()->connectionType(); + continue; + } + + QString connectInterface = connectPtr->settings()->interfaceName(); + if (!connectInterface.isEmpty() + && deviceName != connectInterface) { + qDebug() << "[KyConnectResourse]" << "connect name:"<< connectPtr->name() + << "connect device name" << connectInterface; + continue; + } + + if (m_networkResourceInstance->isActiveConnection(connectPtr->uuid())) { + qDebug()<<"[KyConnectResourse]"<name()<<"is active connection"; + continue; + } + + KyConnectItem *connectItem = getConnectionItem(connectPtr, deviceName); + if (nullptr != connectItem) { + // connectItem->m_itemType = connectionType; + connectItemList << connectItem; + //connectItem->dumpInfo(); + } + + connectPtr = nullptr; + } + + if (connectItemList.size() > 1) { + lanListSort(connectItemList); + } + return; +} + +#if 0 +void KyConnectResourse::getWiredConnections(QList &wiredConnectItemList) +{ + int index = 0; + NetworkManager::Connection::List connectList; + + qDebug()<<"[KyConnectResourse]"<<"get wired connections"; + + connectList.clear(); + connectList = m_networkResourceInstance->getConnectList(); + + if (connectList.empty()) { + qWarning()<<"[KyConnectResourse]"<<"get wired connection failed, the connect list is empty"; + return; + } + + NetworkManager::Connection::Ptr connectPtr = nullptr; + for (index = 0; index < connectList.size(); index++) { + connectPtr = connectList.at(index); + if (NetworkManager::ConnectionSettings::ConnectionType::Wired + != connectPtr->settings()->connectionType()) { + qDebug()<<"[KyConnectResourse]"<<"connect name:" << connectPtr->name() + <<"connect type:"<settings()->connectionType(); + continue; + } + + if (m_networkResourceInstance->isActiveConnection(connectPtr->uuid())) { + qDebug()<<"[KyConnectResourse]"<name()<<"is active connection"; + continue; + } + + KyWiredConnectItem *connectItem = getWiredConnectItem(connectPtr); + if (nullptr != connectItem) { + wiredConnectItemList << connectItem; + connectItem->dumpInfo(); + } + + connectPtr = nullptr; + } + + return; +} + +KyWiredConnectItem *KyConnectResourse::getWiredConnectItem(NetworkManager::Connection::Ptr connectPtr) +{ + qDebug()<<"[KyConnectResourse]"<<"get connect item"; + + if (nullptr == connectPtr) { + qWarning()<<"[KyConnectResourse]"<<"the connect is empty"; + return nullptr; + } + + KyWiredConnectItem *wiredItem = new KyWiredConnectItem(); + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + + wiredItem->m_connectName = connectPtr->name(); + wiredItem->m_connectUuid = connectPtr->uuid(); + wiredItem->m_ifaceName = settingPtr->interfaceName(); + + getConnectIp(settingPtr, wiredItem->m_ipv4, wiredItem->m_ipv6); + + m_networkdevice->getWiredHardwareInfo(settingPtr->interfaceName(), wiredItem); + + wiredItem->m_state = NetworkManager::ActiveConnection::State::Deactivated; + //wiredItem->m_itemType; + + return wiredItem; +} + +#endif + +void KyConnectResourse::getConnectIp( + NetworkManager::ConnectionSettings::Ptr settingPtr, + QString &ipv4Address, + QString &ipv6Address) +{ + NetworkManager::Ipv4Setting::Ptr ipv4Setting = + settingPtr->setting(NetworkManager::Setting::Ipv4).dynamicCast(); + if (NetworkManager::Ipv4Setting::Manual == ipv4Setting->method()) { + QList ipv4AddressList = ipv4Setting->addresses(); + NetworkManager::IpAddress settingIpv4Address = ipv4AddressList.at(0); + if (settingIpv4Address.isValid()) { + ipv4Address = settingIpv4Address.ip().toString(); + } else { + qWarning()<<"[KyConnectResourse]"<<"get connect ipv4 failed, ipv4Address is not valid"; + } + } else { + qWarning()<<"[KyConnectResourse]"<<"get connect ipv4 failed, ipv4 config with dhcp"; + } + + NetworkManager::Ipv6Setting::Ptr ipv6Setting = settingPtr->setting(NetworkManager::Setting::Ipv6).dynamicCast(); + if (nullptr !=ipv6Setting + && NetworkManager::Ipv4Setting::Manual == ipv6Setting->method()) { + QList ipv6AddressList = ipv6Setting->addresses(); + NetworkManager::IpAddress settingIpv6Address = ipv6AddressList.at(0); + if (settingIpv6Address.isValid()) { + ipv6Address = settingIpv6Address.ip().toString(); + } else { + qWarning()<<"[KyConnectResourse]"<<"get connect ipv6 failed, ipv6Address is not valid"; + } + } else { + qWarning()<<"[KyConnectResourse]"<<"get connect ipv6 failed, ipv6 config with dhcp"; + } + + return; +} + + +void KyConnectResourse::getIpv4ConnectSetting( + NetworkManager::Ipv4Setting::Ptr &ipv4Setting, + KyConnectSetting &connectSetting) +{ + if (NetworkManager::Ipv4Setting::Automatic == ipv4Setting->method()) { + connectSetting.m_ipv4ConfigIpType = CONFIG_IP_DHCP; + return; + } + + connectSetting.m_ipv4ConfigIpType = CONFIG_IP_MANUAL; + + connectSetting.m_ipv4Address = ipv4Setting->addresses(); + connectSetting.m_ipv4Dns = ipv4Setting->dns(); + + return; +} + +void KyConnectResourse::getIpv6ConnectSetting( + NetworkManager::Ipv6Setting::Ptr &ipv6Setting, + KyConnectSetting &connectSetting) +{ + + if (NetworkManager::Ipv6Setting::Automatic == ipv6Setting->method()) { + connectSetting.m_ipv6ConfigIpType = CONFIG_IP_DHCP; + return; + } + + connectSetting.m_ipv6ConfigIpType = CONFIG_IP_MANUAL; + + connectSetting.m_ipv6Address = ipv6Setting->addresses(); + + connectSetting.m_ipv6Dns = ipv6Setting->dns(); + + return; +} + +void KyConnectResourse::getConnectivity(NetworkManager::Connectivity &connectivity) +{ + m_networkResourceInstance->getConnectivity(connectivity); +} + +void KyConnectResourse::getConnectionSetting(QString connectUuid, KyConnectSetting &connectSetting) +{ + qDebug() <<"[KyConnectResourse]" << connectUuid <<"get connect setting info, connect uuid"; + + NetworkManager::Connection::Ptr connectPtr = + m_networkResourceInstance->getConnect(connectUuid); + + if (nullptr == connectPtr + || !connectPtr->isValid()) { + qWarning() <<"[KyConnectResourse]" << "it can not find valid connection" << connectUuid; + return; + } + + connectSetting.m_connectName = connectPtr->name(); + + NetworkManager::ConnectionSettings::Ptr connectionSettings = connectPtr->settings(); + connectSetting.m_ifaceName = connectionSettings->interfaceName(); + connectSetting.m_isAutoConnect = connectionSettings->autoconnect(); + + NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast(); + getIpv4ConnectSetting(ipv4Setting, connectSetting); + + NetworkManager::Ipv6Setting::Ptr ipv6Setting = connectionSettings->setting(NetworkManager::Setting::Ipv6).dynamicCast(); + getIpv6ConnectSetting(ipv6Setting, connectSetting); + + return; +} + +bool KyConnectResourse::getInterfaceByUuid(QString &deviceName, const QString connUuid) +{ + deviceName.clear(); + NetworkManager::Connection::Ptr connectPtr = + m_networkResourceInstance->getConnect(connUuid); + + if (nullptr == connectPtr) { + qWarning()<<"getInterfaceByUuid failed, connect uuid"<settings(); + + if (connectSettingPtr.isNull()) { + qWarning()<<"getInterfaceByUuid failed, connect uuid"<settings()->interfaceName(); + qDebug() << "getInterfaceByUuid success " << deviceName; + return true; +} + +void KyConnectResourse::getVpnConnectData(NetworkManager::ConnectionSettings::Ptr settingsPtr, + KyVpnConnectItem *vpnItem) +{ + NetworkManager::VpnSetting::Ptr vpnSetting = settingsPtr->setting(NetworkManager::Setting::Vpn).dynamicCast(); + NMStringMap vpnDataMap = vpnSetting->data(); + if (vpnDataMap.isEmpty()) { + qWarning()<<"get vpn connection Data failed, the data is empty"; + return; + } + + vpnItem->m_vpnGateWay = vpnDataMap["gateway"]; + vpnItem->m_vpnUser = vpnDataMap["user"]; + if ( "yes" == vpnDataMap["require-mppe"]) { + vpnItem->m_vpnMppe = true; + } else { + vpnItem->m_vpnMppe = false; + qDebug()<<"vpn mppe required:"<< vpnDataMap["require-mppe"]; + } + + return; +} + +KyVpnConnectItem *KyConnectResourse::getVpnConnectItem(NetworkManager::Connection::Ptr connectPtr) +{ + if (nullptr == connectPtr) { + qWarning()<<"[KyConnectResourse]"<<"get vpn connection item failed, the connect is empty"; + return nullptr; + } + + KyVpnConnectItem *vpnItem = new KyVpnConnectItem(); + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + + vpnItem->m_vpnName = connectPtr->name(); + vpnItem->m_vpnUuid = connectPtr->uuid(); + vpnItem->m_vpnState = NetworkManager::VpnConnection::State::Disconnected; + + getConnectIp(settingPtr, vpnItem->m_vpnIpv4Address, vpnItem->m_vpnIpv6Address); + getVpnConnectData(settingPtr, vpnItem); + + return vpnItem; +} + +void KyConnectResourse::getVpnConnections(QList &vpnConnectItemList) +{ + int index = 0; + NetworkManager::Connection::List connectList; + + qDebug()<<"[KyConnectResourse]"<<"get vpn connections"; + + connectList.clear(); + connectList = m_networkResourceInstance->getConnectList(); + + if (connectList.empty()) { + qWarning()<<"[KyConnectResourse]"<<"get vpn connections failed, the connect list is empty"; + return; + } + + NetworkManager::Connection::Ptr connectPtr = nullptr; + for (index = 0; index < connectList.size(); index++) { + connectPtr = connectList.at(index); + if (connectPtr.isNull()) { + continue; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Vpn + != connectPtr->settings()->connectionType()) { + qDebug()<<"[KyConnectResourse]"<<"connect name:" << connectPtr->name() + <<"connect type:"<settings()->connectionType(); + continue; + } + + if (m_networkResourceInstance->isActiveConnection(connectPtr->uuid())) { + qDebug()<<"[KyConnectResourse]"<name()<<"is active connection"; + continue; + } + + KyVpnConnectItem *connectItem = getVpnConnectItem(connectPtr); + if (nullptr != connectItem) { + vpnConnectItemList << connectItem; + //connectItem->dumpInfo(); + } + + connectPtr = nullptr; + } + + return; +} + + +KyBluetoothConnectItem *KyConnectResourse::getBluetoothConnectItem(NetworkManager::Connection::Ptr connectPtr) +{ + if (nullptr == connectPtr) { + qWarning()<<"[KyConnectResourse]"<<"get bluetooth connection item failed, the connect is empty"; + return nullptr; + } + + KyBluetoothConnectItem *bluetoothItem = new KyBluetoothConnectItem(); + bluetoothItem->m_connectName = connectPtr->name(); + bluetoothItem->m_connectUuid = connectPtr->uuid(); + bluetoothItem->m_state = NetworkManager::ActiveConnection::State::Deactivated; + + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + getConnectIp(settingPtr, bluetoothItem->m_ipv4Address, bluetoothItem->m_ipv6Address); + + NetworkManager::BluetoothSetting::Ptr bluetoothSetting = + settingPtr->setting(NetworkManager::Setting::Bluetooth).dynamicCast(); + bluetoothItem->m_deviceAddress = bluetoothSetting->bluetoothAddress(); + QByteArray btAddrArray = bluetoothSetting->bluetoothAddress(); + for (int index = 0; index < btAddrArray.size(); ++index) { + qDebug("bt address %d %s", index, btAddrArray[index]); + } + // qDebug()<<"bt address 0:"<< btAddrArray[0]; + // qDebug()<<"bt address 1:"<< btAddrArray[1]; + // qDebug()<<"array size:"<m_deviceAddress.toInt(nullptr, 16); + + return bluetoothItem; +} + +void KyConnectResourse::getBluetoothConnections(QList &bluetoothConnectItemList) +{ + int index = 0; + NetworkManager::Connection::List connectList; + + qDebug()<<"[KyConnectResourse]"<<"get bluetooth connections"; + + connectList.clear(); + connectList = m_networkResourceInstance->getConnectList(); + + if (connectList.empty()) { + qWarning()<<"[KyConnectResourse]"<<"get bluetooth connections failed, the connect list is empty"; + return; + } + + NetworkManager::Connection::Ptr connectPtr = nullptr; + for (index = 0; index < connectList.size(); index++) { + connectPtr = connectList.at(index); + if (connectPtr.isNull()) { + continue; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Bluetooth + != connectPtr->settings()->connectionType()) { + qDebug()<<"[KyConnectResourse]"<<"connect name:" << connectPtr->name() + <<"connect type:"<settings()->connectionType(); + continue; + } + + if (m_networkResourceInstance->isActiveConnection(connectPtr->uuid())) { + qDebug()<<"[KyConnectResourse]"<name()<<"is active connection"; + continue; + } + + KyBluetoothConnectItem *connectItem = getBluetoothConnectItem(connectPtr); + if (nullptr != connectItem) { + bluetoothConnectItemList << connectItem; + //connectItem->dumpInfo(); + } + + connectPtr = nullptr; + } + + return; +} + + +KyApConnectItem *KyConnectResourse::getApConnectItem(NetworkManager::Connection::Ptr connectPtr) +{ + if (nullptr == connectPtr) { + qWarning()<<"[KyConnectResourse]"<<"get bluetooth connection item failed, the connect is empty"; + return nullptr; + } + + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + NetworkManager::WirelessSetting::Ptr wirelessSetting + = settingPtr->setting(NetworkManager::Setting::Wireless).dynamicCast(); + + if (NetworkManager::WirelessSetting::NetworkMode::Ap + != wirelessSetting->mode()) { + qDebug() << "[KyConnectResourse]" <<"get ap item failed, the active connect mode is not ap."; + return nullptr; + } + + KyNetworkDeviceResourse deviceResource; + if (!deviceResource.wirelessDeviceIsExist(settingPtr->interfaceName())) { + qDebug() << "[KyConnectResourse]" <<"get ap item failed, the ap device is not exist yet"; + return nullptr; + } + + QByteArray rawSsid = wirelessSetting->ssid(); + + KyApConnectItem *apConnectItem = new KyApConnectItem(); + apConnectItem->m_connectName = connectPtr->name(); + apConnectItem->m_connectSsid = getSsidFromByteArray(rawSsid); + apConnectItem->m_connectUuid = connectPtr->uuid(); + if (wirelessSetting->band() == NetworkManager::WirelessSetting::FrequencyBand::A) { + apConnectItem->m_band = str2GBand; + } else if (wirelessSetting->band() == NetworkManager::WirelessSetting::FrequencyBand::Bg) { + apConnectItem->m_band = str5GBand; + } + apConnectItem->m_ifaceName = settingPtr->interfaceName(); + apConnectItem->m_isActivated = m_networkResourceInstance->isActiveConnection(connectPtr->uuid()); + + //NetworkManager::WirelessSecuritySetting::Ptr wirelessSecuritySetting + // = settingPtr->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + KyWirelessConnectOperation wirelessOperation; + apConnectItem->m_password = wirelessOperation.getPsk(apConnectItem->m_connectUuid);// wirelessSecuritySetting->psk(); + + return apConnectItem; +} + +KyApConnectItem *KyConnectResourse::getApConnectionByUuid(QString connectUuid) +{ + NetworkManager::Connection::Ptr connectPtr = nullptr; + + connectPtr = m_networkResourceInstance->getConnect(connectUuid); + if (nullptr == connectPtr) { + return nullptr; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Wireless + != connectPtr->settings()->connectionType()) { + return nullptr; + } + + KyApConnectItem *connectItem = getApConnectItem(connectPtr); + + return connectItem; +} + +void KyConnectResourse::getApConnections(QList &apConnectItemList) +{ + QList apActiveConnectItemList; + QList apDeactiveConnectItemList; + int index = 0; + NetworkManager::Connection::List connectList; + + qDebug()<<"[KyConnectResourse]"<<"get ap connections"; + + connectList.clear(); + connectList = m_networkResourceInstance->getConnectList(); + + if (connectList.empty()) { + qWarning()<<"[KyConnectResourse]"<<"get ap connections failed, the connect list is empty"; + return; + } + + NetworkManager::Connection::Ptr connectPtr = nullptr; + for (index = 0; index < connectList.size(); index++) { + connectPtr = connectList.at(index); + if (NetworkManager::ConnectionSettings::ConnectionType::Wireless + != connectPtr->settings()->connectionType()) { + qDebug()<<"[KyConnectResourse]"<<"connect name:" << connectPtr->name() + <<"connect type:"<settings()->connectionType(); + continue; + } + + KyApConnectItem *connectItem = getApConnectItem(connectPtr); + if (nullptr != connectItem) { + if (connectItem->m_isActivated) { + apActiveConnectItemList << connectItem; + } else { + apDeactiveConnectItemList << connectItem; + } + } + + connectPtr = nullptr; + } + + apConnectItemList << apActiveConnectItemList << apDeactiveConnectItemList; + + return; +} + + +bool KyConnectResourse::isWiredConnection(QString uuid) +{ + NetworkManager::Connection::Ptr connectPtr = + m_networkResourceInstance->getConnect(uuid); + if (connectPtr.isNull()) { + qWarning()<<"[KyConnectResourse]"<<"can not find wired connection"<path(); + QString connectionType = getConnectTypeByDbus(connectPath); + if (ETHERNET_TYPE == connectionType) { + return true; + } + + return false; +} + +bool KyConnectResourse::isWirelessConnection(QString uuid) +{ + NetworkManager::Connection::Ptr connectPtr = + m_networkResourceInstance->getConnect(uuid); + if (connectPtr.isNull()) { + qWarning()<<"[KyConnectResourse]"<<"can not find wireless connection"<isValid()) { + NetworkManager::ConnectionSettings::Ptr connectSettingPtr = connectPtr->settings(); + + if (connectSettingPtr.isNull()) { + qWarning()<<"[KyConnectResourse]"<<"get connect setting failed, connect uuid"<settings()->connectionType()) { + return true; + } + } + + return false; +} + +bool KyConnectResourse::isActivatedConnection(QString uuid) +{ + return m_networkResourceInstance->isActiveConnection(uuid); +} + +bool KyConnectResourse::isApConnection(QString uuid) +{ + NetworkManager::Connection::Ptr connectPtr = nullptr; + + connectPtr = m_networkResourceInstance->getConnect(uuid); + if (nullptr == connectPtr) { + return false; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Wireless + != connectPtr->settings()->connectionType()) { + return false; + } + + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + NetworkManager::WirelessSetting::Ptr wirelessSetting + = settingPtr->setting(NetworkManager::Setting::Wireless).dynamicCast(); + if (NetworkManager::WirelessSetting::NetworkMode::Ap + != wirelessSetting->mode()) { + return false; + } + + return true; +} diff --git a/src/backend/dbus-interface/kylinconnectresource.h b/src/backend/dbus-interface/kylinconnectresource.h new file mode 100644 index 00000000..f79024ae --- /dev/null +++ b/src/backend/dbus-interface/kylinconnectresource.h @@ -0,0 +1,69 @@ +#ifndef KYLINCONNECTRESOURCE_H +#define KYLINCONNECTRESOURCE_H + +#include +#include "kylinnetworkresourcemanager.h" +#include "kylinconnectitem.h" +#include "kylinvpnconnectitem.h" +#include "kylinbluetoothconnectitem.h" +#include "kylinnetworkdeviceresource.h" +#include "kylinconnectsetting.h" +#include "kylinapconnectitem.h" + +class KyConnectResourse : public QObject +{ + Q_OBJECT +public: + explicit KyConnectResourse(QObject *parent = nullptr); + ~KyConnectResourse(); + +public: + KyConnectItem *getConnectionItemByUuid(QString connectUuid); + KyConnectItem *getConnectionItemByUuid(QString connectUuid, QString deviceName); + void getConnectionList(QString deviceName, + NetworkManager::ConnectionSettings::ConnectionType connectionType, + QList &connectItemList); + // void getWiredConnections(QList &wiredConnectItemList); + void getVpnConnections(QList &vpnConnectItemList); + void getBluetoothConnections(QList &bluetoothConnectItemList); + void getApConnections(QList &apConnectItemList); + KyApConnectItem *getApConnectionByUuid(QString connectUuid); + + void getConnectionSetting(QString connectUuid, KyConnectSetting &connectSetting); + bool getInterfaceByUuid(QString &deviceName, const QString connUuid); + void getConnectivity(NetworkManager::Connectivity &connectivity); + + bool isWiredConnection(QString uuid); + bool isWirelessConnection(QString uuid); + bool isActivatedConnection(QString uuid); + bool isApConnection(QString uuid); + +private: + bool isActiveDevice(QString conUuid, QString devName); + KyConnectItem *getConnectionItem(NetworkManager::Connection::Ptr connectPtr, QString devName); + void getConnectIp(NetworkManager::ConnectionSettings::Ptr settingPtr, + QString &ipv4Address, + QString &ipv6Address); +// KyWiredConnectItem *getWiredConnectItem(NetworkManager::Connection::Ptr connectPtr); + + void getVpnConnectData(NetworkManager::ConnectionSettings::Ptr settingPtr, + KyVpnConnectItem *vpnItem); + KyVpnConnectItem *getVpnConnectItem(NetworkManager::Connection::Ptr connectPtr); + KyBluetoothConnectItem *getBluetoothConnectItem(NetworkManager::Connection::Ptr connectPtr); + KyApConnectItem *getApConnectItem(NetworkManager::Connection::Ptr connectPtr); + + void getIpv4ConnectSetting(NetworkManager::Ipv4Setting::Ptr &ipv4Setting, + KyConnectSetting &connectSetting); + void getIpv6ConnectSetting(NetworkManager::Ipv6Setting::Ptr &ipv6Setting, + KyConnectSetting &connectSetting); + +signals: + void connectionAdd(QString uuid); + void connectionUpdate(QString uuid); + void connectionRemove(QString path); + void connectivityChanged(NetworkManager::Connectivity connectivity); + +private: + KyNetworkResourceManager *m_networkResourceInstance = nullptr; +}; +#endif // KYLINCONNECTRESOURCE_H diff --git a/src/backend/dbus-interface/kylinconnectsetting.cpp b/src/backend/dbus-interface/kylinconnectsetting.cpp new file mode 100644 index 00000000..88aeb57d --- /dev/null +++ b/src/backend/dbus-interface/kylinconnectsetting.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +KyConnectSetting::KyConnectSetting(/*QObject *parent) : QObject(parent*/) +{ + m_connectName = ""; + m_ifaceName = ""; + + m_ipv4ConfigIpType = CONFIG_IP_DHCP; + m_ipv4Address.clear(); + m_ipv4Dns.clear(); + + m_ipv6ConfigIpType = CONFIG_IP_DHCP; + m_ipv6Address.clear(); + m_ipv6Dns.clear(); + + m_isAutoConnect = true; +} + +KyConnectSetting::~KyConnectSetting() +{ + +} + +void KyConnectSetting::setIfaceName(QString &ifaceName) +{ + m_ifaceName = ifaceName; +} + +void KyConnectSetting::setConnectName(QString &connectName) +{ + m_connectName = connectName; +} + +int KyConnectSetting::setIpConfigType(KyIpAddressType ipType, KyIpConfigType ipConfigType) +{ + if (ipType != IPADDRESS_V4 && ipType != IPADDRESS_V6) { + qWarning()<<"set config ip type failed, the ip address type undefined"< +#include +//#include + +#include +#include +#include +#include +#include + +typedef enum { + CONFIG_IP_MANUAL, + CONFIG_IP_DHCP, +}KyIpConfigType; + +typedef enum { + IPADDRESS_V4, + IPADDRESS_V6, +}KyIpAddressType; + +class KyConnectSetting/* : public QObject*/ +{ +// Q_OBJECT +public: +/* explicit*/ KyConnectSetting(/*QObject *parent = nullptr*/); + ~KyConnectSetting(); + +public: + void setIfaceName(QString &ifaceName); + void setConnectName(QString &connectName); + int setIpConfigType(KyIpAddressType ipType, KyIpConfigType configType); + void ipv4AddressConstruct(QString &ipv4Address, QString &ipv4NetMask, QString &ipv4GateWay, QStringList &ipv4Dns); + void ipv6AddressConstruct(QString &ipv6Address, QString &ipv6NetMask, QString &ipv6GateWay, QStringList &ipv6Dns); + void dumpInfo(); + +public: + QString m_connectName; + QString m_ifaceName; + + KyIpConfigType m_ipv4ConfigIpType; + QList m_ipv4Address; + QList m_ipv4Dns; + + KyIpConfigType m_ipv6ConfigIpType; + QList m_ipv6Address; + QList m_ipv6Dns; + + bool m_isAutoConnect; +}; + +#endif // KYLINCONNECTSETTING_H diff --git a/src/backend/dbus-interface/kylinnetworkdeviceresource.cpp b/src/backend/dbus-interface/kylinnetworkdeviceresource.cpp new file mode 100644 index 00000000..d6d3ebdc --- /dev/null +++ b/src/backend/dbus-interface/kylinnetworkdeviceresource.cpp @@ -0,0 +1,310 @@ + +#include "kylinnetworkdeviceresource.h" +#include "kywirelessnetitem.h" + +#define VIRTURAL_DEVICE_PATH "/sys/devices/virtual/net" +#define LOG_FLAG "KyNetworkDeviceResourse" + +KyNetworkDeviceResourse::KyNetworkDeviceResourse(QObject *parent) : QObject(parent) +{ + m_networkResourceInstance = KyNetworkResourceManager::getInstance(); + + m_deviceMap.clear(); + + initDeviceMap(); + + connect(m_networkResourceInstance, &KyNetworkResourceManager::deviceAdd, + this, &KyNetworkDeviceResourse::onDeviceAdd, Qt::ConnectionType::DirectConnection); + connect(m_networkResourceInstance, &KyNetworkResourceManager::deviceRemove, + this, &KyNetworkDeviceResourse::onDeviceRemove, Qt::ConnectionType::DirectConnection); + connect(m_networkResourceInstance, &KyNetworkResourceManager::deviceUpdate, + this, &KyNetworkDeviceResourse::onDeviceUpdate, Qt::ConnectionType::DirectConnection); + + connect(m_networkResourceInstance, &KyNetworkResourceManager::deviceCarrierChanage, + this, &KyNetworkDeviceResourse::carrierChanage); + connect(m_networkResourceInstance, &KyNetworkResourceManager::deviceBitRateChanage, + this, &KyNetworkDeviceResourse::deviceBitRateChanage); + connect(m_networkResourceInstance, &KyNetworkResourceManager::deviceMacAddressChanage, + this, &KyNetworkDeviceResourse::deviceMacAddressChanage); + connect(m_networkResourceInstance, &KyNetworkResourceManager::deviceActiveChanage, + this, &KyNetworkDeviceResourse::deviceActiveChanage); + +} + +KyNetworkDeviceResourse::~KyNetworkDeviceResourse() +{ + m_networkResourceInstance = nullptr; +} + +void KyNetworkDeviceResourse::initDeviceMap() +{ + NetworkManager::Device::List deviceList + = m_networkResourceInstance->getNetworkDeviceList(); + + if (deviceList.isEmpty()) { + qDebug() << LOG_FLAG << "there is not interface in computer."; + return; + } + + NetworkManager::Device::Ptr devicePtr = nullptr; + for (int index = 0; index < deviceList.size(); ++index) { + devicePtr = deviceList.at(index); + if (devicePtr.isNull()) { + continue; + } + + m_deviceMap.insert(devicePtr->uni(), devicePtr->interfaceName()); + } + + return; +} + +void KyNetworkDeviceResourse::getNetworkDeviceList( + NetworkManager::Device::Type deviceType, + QStringList &networkDeviceList) +{ + NetworkManager::Device::List deviceList + = m_networkResourceInstance->getNetworkDeviceList(); + + if (deviceList.isEmpty()) { + qDebug() << LOG_FLAG <<"network device is not exist. device type" << deviceType; + return; + } + + NetworkManager::Device::Ptr devicePtr = nullptr; + for (int index = 0; index < deviceList.size(); ++index) { + devicePtr = deviceList.at(index); + if (devicePtr.isNull()) { + continue; + } + + if (devicePtr->type() == deviceType) { + if (NetworkManager::Device::Type::Ethernet == deviceType) { + //为了区分有线网卡和虚拟网卡 + qDebug()<< LOG_FLAG << "device uni" << devicePtr->udi(); + if (devicePtr->udi().startsWith(VIRTURAL_DEVICE_PATH)) { + continue; + } + } + + networkDeviceList<interfaceName(); + } + } + + return; +} + +void KyNetworkDeviceResourse::getHardwareInfo(QString ifaceName, QString &hardAddress, int &bandWith) +{ + NetworkManager::Device::Ptr connectDevice = + m_networkResourceInstance->getNetworkDevice(ifaceName); + + if (nullptr == connectDevice || !connectDevice->isValid()) { + qWarning()<< LOG_FLAG <<"get hardware info failed, the device" << ifaceName << "is not existed"; + hardAddress.clear(); + bandWith = 0; + return; + } + + switch (connectDevice->type()) { + case NetworkManager::Device::Ethernet: + { + NetworkManager::WiredDevice *wiredDevicePtr = + qobject_cast(connectDevice.data()); + hardAddress = wiredDevicePtr->hardwareAddress(); + bandWith = wiredDevicePtr->bitRate(); + break; + } + case NetworkManager::Device::Wifi: + { + NetworkManager::WirelessDevice *wirelessDevicePtr = + qobject_cast(connectDevice.data()); + hardAddress = wirelessDevicePtr->hardwareAddress(); + bandWith = wirelessDevicePtr->bitRate(); + break; + } + default: + { + hardAddress = ""; + bandWith = 0; + qWarning()<< LOG_FLAG << "the network device type is undefined" << connectDevice->type(); + break; + } + } + + return; +} + +NetworkManager::Device::State KyNetworkDeviceResourse::getDeviceState(QString deviceName) +{ + NetworkManager::Device::Ptr connectDevice = + m_networkResourceInstance->findDeviceInterface(deviceName); + if (!connectDevice.isNull() && connectDevice->isValid()) { + return connectDevice->state(); + } + + qWarning()<< LOG_FLAG <<"get device state failed, the device is " << deviceName; + + return NetworkManager::Device::State::UnknownState; +} + +bool KyNetworkDeviceResourse::wiredDeviceIsCarriered(QString deviceName) +{ + NetworkManager::Device::Ptr connectDevice = + m_networkResourceInstance->findDeviceInterface(deviceName); + if (connectDevice.isNull()) { + qDebug()<< LOG_FLAG << "check device carriered failed."; + return false; + } + + + if (connectDevice->isValid() + && NetworkManager::Device::Type::Ethernet == connectDevice->type()) { + NetworkManager::WiredDevice *wiredDevicePtr = + qobject_cast(connectDevice.data()); + return wiredDevicePtr->carrier(); + } + + qWarning()<< LOG_FLAG << deviceName <<" can not get carrier state."; + + return false; +} + +void KyNetworkDeviceResourse::setDeviceRefreshRate(QString deviceName, int ms) +{ + NetworkManager::Device::Ptr connectDevice = + m_networkResourceInstance->findDeviceInterface(deviceName); + if (connectDevice.isNull()) { + return; + } + + if (connectDevice->isValid()) { + NetworkManager::DeviceStatistics::Ptr deviceStatistics = connectDevice->deviceStatistics(); + deviceStatistics->setRefreshRateMs(ms); + } + + return; +} + +void KyNetworkDeviceResourse::getDeviceActiveAPInfo(const QString devName, QString &strMac, uint &iHz, uint &iChan, QString &secuType) +{ + strMac.clear(); + iHz = 0; + iChan = 0; + secuType.clear(); + + NetworkManager::Device::Ptr connectDevice = + m_networkResourceInstance->getNetworkDevice(devName); + + if (nullptr == connectDevice || !connectDevice->isValid()) { + qWarning()<< LOG_FLAG <<"getDeviceActiveAPInfo failed, the device" << devName << "is not existed"; + return; + } + + switch (connectDevice->type()) { + case NetworkManager::Device::Wifi: + { + NetworkManager::WirelessDevice *wirelessDevicePtr = + qobject_cast(connectDevice.data()); + NetworkManager::AccessPoint::Ptr apPtr = wirelessDevicePtr->activeAccessPoint(); + if (apPtr.isNull()) { + break; + } + strMac = apPtr->hardwareAddress(); + iHz = apPtr->frequency(); + iChan = NetworkManager::findChannel(iHz); + NetworkManager::AccessPoint::Capabilities cap = apPtr->capabilities(); + NetworkManager::AccessPoint::WpaFlags wpaFlag = apPtr->wpaFlags(); + NetworkManager::AccessPoint::WpaFlags rsnFlag = apPtr->rsnFlags(); + secuType = enumToQstring(cap, wpaFlag, rsnFlag); + break; + } + case NetworkManager::Device::Ethernet: + break; + default: + break; + } + + return; +} + +int KyNetworkDeviceResourse::getWirelessDeviceCapability(const QString deviceName) +{ + NetworkManager::Device::Ptr connectDevice = + m_networkResourceInstance->findDeviceInterface(deviceName); + if (connectDevice->isValid() + && NetworkManager::Device::Type::Wifi == connectDevice->type()) { + NetworkManager::WirelessDevice *wirelessDevicePtr = + qobject_cast(connectDevice.data()); + + int cap = 0; + if (wirelessDevicePtr->wirelessCapabilities() & NetworkManager::WirelessDevice::ApCap) { + cap = cap | 0x01; + } + if (wirelessDevicePtr->wirelessCapabilities() & NetworkManager::WirelessDevice::Freq2Ghz) { + cap = cap | 0x02; + } + if (wirelessDevicePtr->wirelessCapabilities() & NetworkManager::WirelessDevice::Freq5Ghz) { + cap = cap | 0x04; + } + return cap; + } else { + qWarning()<<"[KyNetworkDeviceResourse]"<findDeviceInterface(deviceName); + + if (devicePtr.isNull()) { + qDebug() << LOG_FLAG << "check device type failed, it is not exist"; + return false; + } + + if (NetworkManager::Device::Type::Ethernet == devicePtr->type() + && !devicePtr->udi().startsWith(VIRTURAL_DEVICE_PATH)) { + return true; + } + + return false; +} diff --git a/src/backend/dbus-interface/kylinnetworkdeviceresource.h b/src/backend/dbus-interface/kylinnetworkdeviceresource.h new file mode 100644 index 00000000..e3283c6c --- /dev/null +++ b/src/backend/dbus-interface/kylinnetworkdeviceresource.h @@ -0,0 +1,55 @@ +#ifndef KYLINNETORKDEVICERESOURCE_H +#define KYLINNETORKDEVICERESOURCE_H + +#include +#include "kylinnetworkresourcemanager.h" +#include "kylinconnectitem.h" +#include "kylinwiredconnectoperation.h" + +class KyNetworkDeviceResourse : public QObject +{ + Q_OBJECT +public: + explicit KyNetworkDeviceResourse(QObject *parent = nullptr); + ~KyNetworkDeviceResourse(); + +signals: + void deviceAdd(QString deviceName, NetworkManager::Device::Type deviceType); + void deviceRemove(QString deviceName); + void deviceUpdate(QString deviceName); + + void deviceNameUpdate(QString oldName, QString newName); + void stateChange(QString deviceName, int state); + + void carrierChanage(QString deviceName, bool pluged); + void deviceBitRateChanage(QString deviceName, int bitRate); + void deviceMacAddressChanage(QString deviceName, const QString &hwAddress); + void deviceActiveChanage(QString deviceName, bool deivceActive); + +public slots: + void onDeviceAdd(QString deviceName, QString uni, NetworkManager::Device::Type deviceType); + void onDeviceRemove(QString deviceName, QString uni); + void onDeviceUpdate(QString interface, QString dbusPath); + +public: + void getNetworkDeviceList(NetworkManager::Device::Type deviceType, QStringList &networkDeviceList); + void getHardwareInfo(QString ifaceName, QString &hardAddress, int &bandWith); + void getDeviceActiveAPInfo(const QString devName, QString &strMac, uint &iHz, uint &iChan, QString &secuType); + int getWirelessDeviceCapability(const QString deviceName); + NetworkManager::Device::State getDeviceState(QString deviceName); + + bool wiredDeviceIsCarriered(QString deviceName); + bool wirelessDeviceIsExist(const QString devName); + bool deviceIsWired(QString deviceName); + + void setDeviceRefreshRate(QString deviceName, int ms); + +private: + KyWiredConnectOperation wiredOperation; + KyNetworkResourceManager *m_networkResourceInstance = nullptr; + QStringList m_activeConnectUuidList; + QMap m_deviceMap; + + void initDeviceMap(); +}; +#endif // KYLINNETORKDEVICERESOURCE_H diff --git a/src/backend/dbus-interface/kylinnetworkresourcemanager.cpp b/src/backend/dbus-interface/kylinnetworkresourcemanager.cpp new file mode 100644 index 00000000..a6c2ada7 --- /dev/null +++ b/src/backend/dbus-interface/kylinnetworkresourcemanager.cpp @@ -0,0 +1,1019 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include "kylinutil.h" + +#define SIGNAL_DELAY 80000 +#define EMIT_DELAY 10000 + +#define LOG_FLAG "[KyNetworkResourceManager]" + +KyNetworkResourceManager* KyNetworkResourceManager::m_pInstance = nullptr; + +KyNetworkResourceManager* KyNetworkResourceManager::getInstance() +{ + if (m_pInstance == NULL) { + m_pInstance = new KyNetworkResourceManager(); + } + return m_pInstance; +} + +void KyNetworkResourceManager::Release() +{ + if (m_pInstance != NULL) { + delete m_pInstance; + m_pInstance = NULL; + } +} + +KyNetworkResourceManager::KyNetworkResourceManager(QObject *parent) : QObject(parent) +{ + qRegisterMetaType("NetworkManager::ActiveConnection::State"); + qRegisterMetaType("NetworkManager::Connectivity"); + qRegisterMetaType("NetworkManager::ActiveConnection::Reason"); + qRegisterMetaType("NetworkManager::Device::Type"); + + QDBusConnection::systemBus().connect(QString("org.freedesktop.DBus"), + QString("/org/freedesktop/DBus"), + QString("org.freedesktop.DBus"), + QString("NameOwnerChanged"), this, SLOT(onServiceAppear(QString,QString,QString))); +} + +void KyNetworkResourceManager::onInitNetwork() +{ + insertActiveConnections(); + insertConnections(); + insertDevices(); + insertWifiNetworks(); + + //initialize NetworkManager signals + connect(NetworkManager::notifier(), &NetworkManager::Notifier::deviceAdded, this, &KyNetworkResourceManager::onDeviceAdded); + connect(NetworkManager::notifier(), &NetworkManager::Notifier::deviceRemoved, this, &KyNetworkResourceManager::onDeviceRemoved); + connect(NetworkManager::notifier(), &NetworkManager::Notifier::activeConnectionAdded, this, &KyNetworkResourceManager::onActiveConnectionAdded); + connect(NetworkManager::notifier(), &NetworkManager::Notifier::activeConnectionRemoved, this, &KyNetworkResourceManager::onActiveConnectionRemoved); + connect(NetworkManager::settingsNotifier(), &NetworkManager::SettingsNotifier::connectionAdded, this, &KyNetworkResourceManager::onConnectionAdded); + connect(NetworkManager::settingsNotifier(), &NetworkManager::SettingsNotifier::connectionRemoved, this, static_cast(&KyNetworkResourceManager::onConnectionRemoved)); + + connect(NetworkManager::notifier(), &NetworkManager::Notifier::connectivityChanged, this, &KyNetworkResourceManager::connectivityChanged); + //todo wifi开关信号 + connect(NetworkManager::notifier(), &NetworkManager::Notifier::wirelessEnabledChanged, this, &KyNetworkResourceManager::wifiEnabledChanged); + + // Note: the connectionRemoved is never emitted in case network-manager service stop, + // we need remove the connections manually. + connect(NetworkManager::notifier(), &NetworkManager::Notifier::serviceDisappeared, this, &KyNetworkResourceManager::clearConnections); + connect(NetworkManager::notifier(), &NetworkManager::Notifier::serviceDisappeared, this, &KyNetworkResourceManager::clearWifiNetworks); + + qDebug() <<"[KyNetworkResourceManager]" + << "active connections:" << m_activeConns.size() + << "connections:" << m_connections.size() + << "network device:" << m_devices.size(); + m_initFinished = true; + + return; +} + +KyNetworkResourceManager::~KyNetworkResourceManager() +{ + +} + +void KyNetworkResourceManager::removeActiveConnection(int pos) +{ + //active connections signals + NetworkManager::ActiveConnection::Ptr conn = m_activeConns.takeAt(pos); + conn->disconnect(this); +} + +void KyNetworkResourceManager::clearActiveConnections() +{ + while (0 < m_activeConns.size()) + removeActiveConnection(0); +} + +void KyNetworkResourceManager::addActiveConnection(NetworkManager::ActiveConnection::Ptr conn) +{ + m_activeConns.push_back(conn); +#if 0 + connect(conn.data(), &NetworkManager::ActiveConnection::connectionChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::default4Changed, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::default6Changed, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::dhcp4ConfigChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::dhcp6ConfigChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::ipV4ConfigChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::ipV6ConfigChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::idChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::typeChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::masterChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::specificObjectChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); +#endif + //connect(conn.data(), &NetworkManager::ActiveConnection::stateChangedReason, this, &KyNetworkResourceManager::onActiveConnectionChangedReason); + connect(conn.data(), &NetworkManager::ActiveConnection::stateChanged, this, &KyNetworkResourceManager::onActiveConnectionChanged); +#if 0 + connect(conn.data(), &NetworkManager::ActiveConnection::vpnChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::uuidChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(conn.data(), &NetworkManager::ActiveConnection::devicesChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); +#endif + if (conn->vpn()) { + connect(qobject_cast(conn.data()), &NetworkManager::VpnConnection::bannerChanged, this, &KyNetworkResourceManager::onActiveConnectionUpdated); + connect(qobject_cast(conn.data()), &NetworkManager::VpnConnection::stateChanged, this, &KyNetworkResourceManager::onVpnActiveConnectChanagedReason); + } +} + +void KyNetworkResourceManager::insertActiveConnections() +{ + for (auto const & conn : NetworkManager::activeConnections()) { + if (conn.isNull()) { + continue; + } + + addActiveConnection(conn); + } +} + +void KyNetworkResourceManager::removeConnection(int pos) +{ + //connections signals + QString path = m_connections.at(pos)->path(); + NetworkManager::Connection::Ptr conn = m_connections.takeAt(pos); + conn->disconnect(this); + emit connectionRemove(path); +} + +void KyNetworkResourceManager::clearConnections() +{ + while (0 < m_connections.size()) { + removeConnection(0); + } +} + +void KyNetworkResourceManager::addConnection(NetworkManager::Connection::Ptr conn) +{ + m_connections.push_back(conn); + + //connections signals + connect(conn.data(), &NetworkManager::Connection::updated, this, &KyNetworkResourceManager::onConnectionUpdated); + connect(conn.data(), &NetworkManager::Connection::removed, this, &KyNetworkResourceManager::onConnectionRemoved); +} + +void KyNetworkResourceManager::insertConnections() +{ + for (auto const & connectPtr : NetworkManager::listConnections()) { + if (connectPtr.isNull()) { + continue; + } + + if (connectPtr->name().isEmpty() || connectPtr->uuid().isEmpty()) { + qWarning() <<"[KyNetworkResourceManager]" << " the name of connection is empty."; + continue; + } + + addConnection(connectPtr); + } + + return; +} + +void KyNetworkResourceManager::removeDevice(int pos) +{ + //connections signals + NetworkManager::Device::Ptr device = m_devices.takeAt(pos); + device->disconnect(this); +} + +void KyNetworkResourceManager::clearDevices() +{ + while (0 < m_devices.size()) + removeDevice(0); +} + +void KyNetworkResourceManager::addDevice(NetworkManager::Device::Ptr device) +{ + m_devices.push_back(device); + //device signals +#if 0 + //connect(device.data(), &NetworkManager::Device::stateChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::activeConnectionChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::autoconnectChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::availableConnectionChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::availableConnectionAppeared, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::availableConnectionDisappeared, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::capabilitiesChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::dhcp4ConfigChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::dhcp6ConfigChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::driverChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::driverVersionChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::firmwareMissingChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::firmwareVersionChanged, this, &KyNetworkResourceManager::onDeviceUpdated); +#endif + connect(device.data(), &NetworkManager::Device::activeConnectionChanged, this, &KyNetworkResourceManager::onDeviceActiveChanage); + connect(device.data(), &NetworkManager::Device::interfaceNameChanged, this, &KyNetworkResourceManager::onDeviceUpdated); +#if 0 + connect(device.data(), &NetworkManager::Device::ipV4AddressChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::ipV4ConfigChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::ipV6ConfigChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::ipInterfaceChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::managedChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::physicalPortIdChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::mtuChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::nmPluginMissingChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::meteredChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::connectionStateChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(device.data(), &NetworkManager::Device::stateReasonChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + + connect(device.data(), &NetworkManager::Device::stateChanged, this, &KyNetworkResourceManager::onDeviceStateChanged); + connect(device.data(), &NetworkManager::Device::udiChanged, this, &KyNetworkResourceManager::onDeviceUpdated); +#endif + switch (device->type()) + { + case NetworkManager::Device::Ethernet: + connect(qobject_cast(device.data()), &NetworkManager::WiredDevice::bitRateChanged, this, &KyNetworkResourceManager::onDeviceBitRateChanage); + connect(qobject_cast(device.data()), &NetworkManager::WiredDevice::carrierChanged, this, &KyNetworkResourceManager::onDeviceCarrierChanage); + connect(qobject_cast(device.data()), &NetworkManager::WiredDevice::hardwareAddressChanged, this, &KyNetworkResourceManager::onDeviceMacAddressChanage); + // connect(qobject_cast(device.data()), &NetworkManager::WiredDevice::permanentHardwareAddressChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + break; + + case NetworkManager::Device::Wifi: +#if 0 + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::bitRateChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::activeAccessPointChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::modeChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::wirelessCapabilitiesChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::hardwareAddressChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::permanentHardwareAddressChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::wirelessPropertiesChanged, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::accessPointAppeared, this, &KyNetworkResourceManager::onDeviceUpdated); + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::accessPointDisappeared, this, &KyNetworkResourceManager::onDeviceUpdated); +#endif + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::networkAppeared, this, &KyNetworkResourceManager::onWifiNetworkAppeared); + connect(qobject_cast(device.data()), &NetworkManager::WirelessDevice::networkDisappeared, this, &KyNetworkResourceManager::onWifiNetworkDisappeared); + break; + default: + //TODO: other device types! + break; + } +} + +void KyNetworkResourceManager::insertDevices() +{ + for (auto const & device : NetworkManager::networkInterfaces()) { + if (device.isNull()) { + continue; + } + + addDevice(device); + } + + return; +} + +void KyNetworkResourceManager::removeWifiNetwork(int pos) +{ + //network signals + NetworkManager::WirelessNetwork::Ptr net = m_wifiNets.takeAt(pos); + net->disconnect(this); +} + +void KyNetworkResourceManager::clearWifiNetworks() +{ + while (0 < m_wifiNets.size()) + removeWifiNetwork(0); +} + +void KyNetworkResourceManager::addWifiNetwork(NetworkManager::WirelessNetwork::Ptr net) +{ + m_wifiNets.push_back(net); + //device signals + connect(net.data(), &NetworkManager::WirelessNetwork::signalStrengthChanged, this, &KyNetworkResourceManager::onUpdateWirelessNet); + connect(net.data(), &NetworkManager::WirelessNetwork::referenceAccessPointChanged, this, &KyNetworkResourceManager::onUpdateWirelessNet); + connect(net.data(), &NetworkManager::WirelessNetwork::disappeared, this, &KyNetworkResourceManager::onUpdateWirelessNet); +} + +void KyNetworkResourceManager::insertWifiNetworks() +{ + for (auto const & device : m_devices) { + if (device.isNull()) { + continue; + } + + if (NetworkManager::Device::Wifi == device->type()) { + NetworkManager::WirelessDevice::Ptr w_dev = device.objectCast(); + for (auto const & net : w_dev->networks()) { + if (!net.isNull()) { + addWifiNetwork(net); + emit wifiNetworkAdded(device->interfaceName(),net->ssid()); + } + } + } + } +} + +NetworkManager::ActiveConnection::Ptr KyNetworkResourceManager::findActiveConnection(QString const & path) +{ + auto i = std::find_if(m_activeConns.cbegin(), m_activeConns.cend(), [&path] (NetworkManager::ActiveConnection::Ptr const & conn) -> bool { + return conn->path() == path; + }); + return m_activeConns.cend() == i ? NetworkManager::ActiveConnection::Ptr{} : *i; +} + +template +NetworkManager::Device::Ptr KyNetworkResourceManager::findDevice(Predicate const & pred) +{ + auto i = std::find_if(m_devices.cbegin(), m_devices.cend(), pred); + return m_devices.cend() == i ? NetworkManager::Device::Ptr{} : *i; +} + +NetworkManager::Device::Ptr KyNetworkResourceManager::findDeviceUni(QString const & uni) +{ + return findDevice([&uni] (NetworkManager::Device::Ptr const & dev) { return dev->uni() == uni; }); +} + +NetworkManager::Device::Ptr KyNetworkResourceManager::findDeviceInterface(QString const & interfaceName) +{ + return findDevice([&interfaceName] (NetworkManager::Device::Ptr const & dev) { return dev->interfaceName() == interfaceName; }); +} + +NetworkManager::WirelessNetwork::Ptr KyNetworkResourceManager::findWifiNetwork(QString const & ssid, QString const & devUni) +{ + auto i = std::find_if(m_wifiNets.cbegin(), m_wifiNets.cend(), [&ssid, &devUni] (NetworkManager::WirelessNetwork::Ptr const & net) -> bool { + return net->ssid() == ssid && net->device() == devUni; + }); + return m_wifiNets.cend() == i ? NetworkManager::WirelessNetwork::Ptr{} : *i; +} + +NetworkManager::Device::Ptr KyNetworkResourceManager::getNetworkDevice(const QString ifaceName) +{ + NetworkManager::Device::Ptr devicePtr = nullptr; + if (ifaceName.isEmpty()) { + return nullptr; + } + + for (int index = 0; index < m_devices.size(); ++index) { + devicePtr = m_devices.at(index); + if (devicePtr.isNull()) { + continue; + } + + if (ifaceName == devicePtr->interfaceName()) { + return devicePtr; + } + } + + return nullptr; +} + +NetworkManager::ActiveConnection::Ptr KyNetworkResourceManager::getActiveConnect(const QString activeConnectUuid) +{ + int index = 0; + NetworkManager::ActiveConnection::Ptr activateConnectPtr = nullptr; + + qDebug()<<"[KyNetworkResourceManager]"<<"get activetate connect with uuid"<< activeConnectUuid; + if (activeConnectUuid.isEmpty()) { + return nullptr; + } + + for (index = 0; index < m_activeConns.size(); ++index) { + activateConnectPtr = m_activeConns.at(index); + if (activateConnectPtr.isNull()) { + continue; + } + + if (activateConnectPtr->uuid() == activeConnectUuid) { + return activateConnectPtr; + } + } + + qWarning()<<"[KyNetworkResourceManager]"<<"it can not find the activate connect with uuid" <uuid() == connectUuid) { + return connectPtr; + } + } + + qWarning()<<"[KyNetworkResourceManager]"<<"it can not find connect with uuid"<uuid() == uuid + && NetworkManager::ActiveConnection::State::Activated == activateConnectPtr->state()) { + return true; + } + } + + return false; +} + +bool KyNetworkResourceManager::isActivatingConnection(QString uuid) +{ + int index = 0; + NetworkManager::ActiveConnection::Ptr activateConnectPtr = nullptr; + + if (uuid.isEmpty()) { + return false; + } + + for (index = 0; index < m_activeConns.size(); ++index) { + activateConnectPtr = m_activeConns.at(index); + if (activateConnectPtr.isNull()) { + continue; + } + + if (activateConnectPtr->uuid() == uuid + && NetworkManager::ActiveConnection::State::Activating == activateConnectPtr->state()) { + return true; + } + } + + return false; +} + +void KyNetworkResourceManager::getConnectivity(NetworkManager::Connectivity &connectivity) +{ + connectivity = NetworkManager::connectivity(); +} + +bool KyNetworkResourceManager::NetworkManagerIsInited() +{ + return m_initFinished; +} + +void KyNetworkResourceManager::requestScan(NetworkManager::WirelessDevice * dev) +{ + if (nullptr == dev) { + qWarning() << LOG_FLAG << "request scan failed, wireless device is invalid."; + return; + } + + qDebug() <<"[KyNetworkResourceManager]"<< dev->interfaceName()<<"start scan wifi ap"; + QDBusPendingReply<> reply = dev->requestScan(); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, dev); + connect(watcher, &QDBusPendingCallWatcher::finished, [dev] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) + { + //TODO: in what form should we output the warning messages + qWarning() << QStringLiteral("requestScan on device '%1' failed: %3").arg(dev->interfaceName()) + .arg(watcher->error().message()); + } + watcher->deleteLater(); + }); + + return; +} + +void KyNetworkResourceManager::onServiceAppear(QString interface, QString oldOwner, QString newOwner) +{ + if (interface == "org.freedesktop.NetworkManager" + && oldOwner.isEmpty() && !newOwner.isEmpty()) { + qDebug() << LOG_FLAG << "org.freedesktop.NetworkManager start"; + QTimer::singleShot(500,this,&KyNetworkResourceManager::insertWifiNetworks); + } +} + +void KyNetworkResourceManager::onConnectionUpdated() +{ + NetworkManager::Connection *connectPtr = + qobject_cast(sender()); + if (nullptr != connectPtr && connectPtr->isValid()) { + qDebug()<< LOG_FLAG <<"connection will Update, connection name"<name() + << "connection uuid" << connectPtr->uuid(); + emit connectionUpdate(connectPtr->uuid()); + } else { + qWarning()<< LOG_FLAG + <<"onConnectionUpdate failed, the connect is invalid"; + } + + return; +} + +void KyNetworkResourceManager::onActiveConnectionUpdated() +{ + //emit activeConnectionUpdate(qobject_cast(sender())); +} + +void KyNetworkResourceManager::onActiveConnectionChangedReason( + NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason) +{ + NetworkManager::ActiveConnection * activeConnect = + qobject_cast(sender()); + if (nullptr != activeConnect && activeConnect->isValid()) { + qDebug()<< LOG_FLAG <<"connect uuid"<uuid() + <<"state change"<uuid(), state, reason); + } else { + qWarning() << LOG_FLAG << "onActiveConnectionChangedReason failed, the connection is invalid."; + } + + return; +} + +void KyNetworkResourceManager::onActiveConnectionChanged( + NetworkManager::ActiveConnection::State state) +{ + NetworkManager::ActiveConnection * activeConnect = + qobject_cast(sender()); + if (nullptr != activeConnect && activeConnect->isValid()) { + qDebug()<< LOG_FLAG << "connection uuid" << activeConnect->uuid() + <<"state change "<state() != state) { + qDebug() << "[KyNetworkResourceManager]" + <<"connect real state"<state() <<"change state"<uuid(), state, + NetworkManager::ActiveConnection::Reason::UknownReason); + } else { + qWarning() << LOG_FLAG << "onActiveConnectionChanged failed, the connection is invalid."; + } + + return; +} + +void KyNetworkResourceManager::onVpnActiveConnectChanagedReason(NetworkManager::VpnConnection::State state, + NetworkManager::VpnConnection::StateChangeReason reason) +{ + NetworkManager::ActiveConnection *activeConnect = + qobject_cast(sender()); + + if (nullptr != activeConnect && activeConnect->isValid()) { + qDebug()<<"vpn connect uuid" << activeConnect->uuid() + <<"state change " <uuid(), state, reason); + } else { + qWarning() << LOG_FLAG << "onVpnActiveConnectChanagedReason failed, the connection is invalid."; + } + + return; +} + +void KyNetworkResourceManager::onDeviceActiveChanage() +{ + NetworkManager::Device *p_device = qobject_cast(sender()); + if (nullptr == p_device) { + return; + } + + QString deviceName = p_device->interfaceName(); + //此处需要取反,因为激活连接的网卡状态是false,断开连接的网卡状态是true + bool isActive = !p_device->isActive(); + + qDebug()<< LOG_FLAG << "device active change, device name " << deviceName + << "active state" << isActive; + + emit deviceActiveChanage(deviceName, isActive); + + return; +} + +void KyNetworkResourceManager::onDeviceUpdated() +{ + NetworkManager::Device *p_device = qobject_cast(sender()); + if (nullptr == p_device) { + return; + } + + QString deviceName = p_device->interfaceName(); + QString deviceUni = p_device->uni(); + + emit deviceUpdate(deviceName, deviceUni); + + return; +} + +void KyNetworkResourceManager::onDeviceCarrierChanage(bool pluged) +{ + NetworkManager::WiredDevice * networkDevice + = qobject_cast(sender()); + + qDebug()<< LOG_FLAG<<"device carrier chanage"<< pluged; + if (nullptr !=networkDevice && networkDevice->isValid()) { + emit deviceCarrierChanage(networkDevice->interfaceName(), pluged); + } else { + qWarning()<< LOG_FLAG<<"onDeviceCarrierChanage failed."; + } + + return; +} + +void KyNetworkResourceManager::onDeviceBitRateChanage(int bitRate) +{ + NetworkManager::WiredDevice * networkDevice + = qobject_cast(sender()); + + if (nullptr != networkDevice + && networkDevice->isValid()) { + emit deviceBitRateChanage(networkDevice->interfaceName(), bitRate); + } else { + qWarning()<< LOG_FLAG <<"the device is not invalid with bitrate" << bitRate; + } + + return; +} + +void KyNetworkResourceManager::onDeviceMacAddressChanage(const QString &hwAddress) +{ + NetworkManager::WiredDevice * networkDevice + = qobject_cast(sender()); + + if (nullptr !=networkDevice && networkDevice->isValid()) { + emit deviceMacAddressChanage(networkDevice->interfaceName(), hwAddress); + } else { + qWarning()<< LOG_FLAG <<"the device is not invalid with mac" << hwAddress; + } + + return; +} + +void KyNetworkResourceManager::onDeviceStateChanged( + NetworkManager::Device::State newstate, + NetworkManager::Device::State oldstate, + NetworkManager::Device::StateChangeReason reason) +{ + qWarning() << LOG_FLAG <<"the device state "<interfaceName() << ssid; + + NetworkManager::WirelessDevice * w_dev = qobject_cast(dev); + NetworkManager::WirelessNetwork::Ptr net = w_dev->findNetwork(ssid); + if (!net.isNull()) { + if (0 > m_wifiNets.indexOf(net)){ + addWifiNetwork(net); + } else { + //TODO: onWifiNetworkUpdate + qDebug()<< LOG_FLAG << "add but already exist"; + } + + NetworkManager::AccessPoint::Ptr accessPoitPtr = net->referenceAccessPoint(); + QByteArray rawSsid = accessPoitPtr->rawSsid(); + QString wifiSsid = getSsidFromByteArray(rawSsid); + emit wifiNetworkAdded(dev->interfaceName(), wifiSsid); + } + + return; +} + +void KyNetworkResourceManager::onWifiNetworkUpdate(NetworkManager::WirelessNetwork * net) +{ + if (nullptr == net) { + return; + } + + auto index = std::find(m_wifiNets.cbegin(), m_wifiNets.cend(), net); + if (m_wifiNets.cend() != index) { + if (net->accessPoints().isEmpty()) { + //emit + bool bFlag = false; + QString devIface; + NetworkManager::Device::Ptr dev = findDeviceUni(net->device()); + if(dev.isNull()) { + qDebug()<< LOG_FLAG << "device invalid"; + bFlag = true; + } else { + devIface = dev->interfaceName(); + } + + //remove + auto pos = index - m_wifiNets.cbegin(); + removeWifiNetwork(pos); + if(bFlag) { + //device invalid + qDebug() << LOG_FLAG << "wifiNetworkDeviceDisappear"; + emit wifiNetworkDeviceDisappear(); + } else { + qDebug()<< LOG_FLAG << "wifiNetwork disappear" << net << net->ssid(); + NetworkManager::AccessPoint::Ptr accessPoitPtr = net->referenceAccessPoint(); + QByteArray rawSsid = accessPoitPtr->rawSsid(); + QString wifiSsid = getSsidFromByteArray(rawSsid); + emit wifiNetworkRemoved(devIface, wifiSsid); + } + } else { + qDebug()<< LOG_FLAG << "wifiNetworkPropertyChange " << net << net->ssid(); + emit wifiNetworkPropertyChange(net); + } + } + + return; +} + +void KyNetworkResourceManager::onWifiNetworkRemove(NetworkManager::Device * dev, QString const & ssid) +{ + if (nullptr == dev) { + return; + } + + qDebug() << "wifiNetworkRemove" << dev << dev->interfaceName() << ssid; + + NetworkManager::WirelessNetwork::Ptr net = findWifiNetwork(ssid, dev->uni()); + if (!net.isNull()) { + auto pos = m_wifiNets.indexOf(net); + if (0 <= pos) { + removeWifiNetwork(pos); + NetworkManager::AccessPoint::Ptr accessPoitPtr = net->referenceAccessPoint(); + QByteArray rawSsid = accessPoitPtr->rawSsid(); + QString wifiSsid = getSsidFromByteArray(rawSsid); + emit wifiNetworkRemoved(dev->interfaceName(), wifiSsid); + } + } + + return; +} + +void KyNetworkResourceManager::onWifiNetworkAppeared(QString const & ssid) +{ + NetworkManager::Device * p_device = qobject_cast(sender()); + if (nullptr != p_device) { + onWifiNetworkAdd(p_device, ssid); + + QString deviceName = p_device->interfaceName(); + QString deviceUni = p_device->uni(); + + emit deviceUpdate(deviceName, deviceUni); + } else { + qWarning()<< LOG_FLAG << "onWifiNetworkAppeared failed."; + } + + return; +} + +void KyNetworkResourceManager::onWifiNetworkDisappeared(QString const & ssid) +{ + NetworkManager::Device *p_device = qobject_cast(sender()); + if (nullptr != p_device) { + onWifiNetworkRemove(p_device, ssid); + + QString deviceName = p_device->interfaceName(); + QString deviceUni = p_device->uni(); + + emit deviceUpdate(deviceName, deviceUni); + } else { + qWarning()<< LOG_FLAG << "onWifiNetworkDisappeared failed."; + } + + return; +} + +void KyNetworkResourceManager::onUpdateWirelessNet() +{ + NetworkManager::WirelessNetwork *p_wirelessNet = + qobject_cast(sender()); + if (nullptr != p_wirelessNet) { + onWifiNetworkUpdate(p_wirelessNet); + } + + return; +} + +void KyNetworkResourceManager::onDeviceAdded(QString const & uni) +{ + qDebug()<< "onDeviceAdded"<isValid()) { + qWarning() << uni << " is currently not invalid"; + return; + } + + if (0 > m_devices.indexOf(networkDevice)) { + addDevice(networkDevice); + emit deviceAdd(networkDevice->interfaceName(), networkDevice->uni(), networkDevice->type()); + } else { + qWarning() << networkDevice->interfaceName() <<"the device is exist in network device list."; + } + + return; +} + +void KyNetworkResourceManager::onDeviceRemoved(QString const & uni) +{ + NetworkManager::Device::Ptr networkDevice = findDeviceUni(uni); + if (networkDevice.isNull()) { + qWarning()<<"the device is not exist in network device list ."<< uni; + return; + } + + qDebug()<<"remove network device"<< networkDevice->interfaceName() <<"uni:" << uni; + + auto index = std::find(m_devices.cbegin(), m_devices.cend(), networkDevice); + if (m_devices.cend() != index) { + const int pos = index - m_devices.cbegin(); + removeDevice(pos); + emit deviceRemove(networkDevice->interfaceName(), networkDevice->uni()); + } + + return; +} + +void KyNetworkResourceManager::onActiveConnectionAdded(QString const & path) +{ + NetworkManager::ActiveConnection::Ptr activeConnectPtr = NetworkManager::findActiveConnection(path); + if (activeConnectPtr.isNull()) { + qWarning() << "[KyNetworkResourceManager]" << "the active connect is null, so do not add it."<isValid()) { + qWarning() << "[KyNetworkResourceManager]" << path << " connect is currently not valid"; + return; + } + + qDebug()<<"add active connect"<connection()->name(); + + if (0 > m_activeConns.indexOf(activeConnectPtr)) { + addActiveConnection(activeConnectPtr); + emit activeConnectionAdd(activeConnectPtr->uuid()); + } else { + //TODO: onActiveConnectionUpdate + qWarning() << "[KyNetworkResourceManager]" << "update active connection to do"; + //emit activeConnectionUpdate(conn->uuid()); + } + + return; +} + +void KyNetworkResourceManager::onActiveConnectionRemoved(QString const & path) +{ + NetworkManager::ActiveConnection::Ptr activeConnectPtr = findActiveConnection(path);//XXX: const QString &uni + if (activeConnectPtr.isNull()) { + qWarning() <<"[KyNetworkResourceManager]" + <<"the active connect is null, so do not remove it. "<< path; + return; + } + + qDebug() <<"[KyNetworkResourceManager]"<<"remove active connect"<uuid(); + + auto index = std::find(m_activeConns.cbegin(), m_activeConns.cend(), activeConnectPtr); + if (m_activeConns.cend() != index) { + const int pos = index - m_activeConns.cbegin(); + removeActiveConnection(pos); + emit activeConnectionRemove(activeConnectPtr->uuid()); + } + + return; +} + +void KyNetworkResourceManager::onConnectionAdded(QString const & path) +{ + NetworkManager::Connection::Ptr connectPtr = NetworkManager::findConnection(path); + if (connectPtr.isNull()) { + qWarning() <<"[KyNetworkResourceManager]" <<"it can not find in networkmanager."<isValid()) { + qWarning() <<"[KyNetworkResourceManager]" << path << " is currently not invalid"; + return; + } + + qDebug() <<"[KyNetworkResourceManager]" <<"add connect "<< connectPtr->name() << connectPtr->path(); + + if (connectPtr->name().isEmpty() || connectPtr->uuid().isEmpty()) { + qWarning() <<"[KyNetworkResourceManager]" << "the name or uuid of connection is empty"; + return; + } + + if (0 > m_connections.indexOf(connectPtr)) { + addConnection(connectPtr); + emit connectionAdd(connectPtr->uuid()); + } else { + //TODO::updateconnect + qWarning() << "[KyNetworkResourceManager]" << connectPtr->uuid() <<" connect is exist in connect list."; + } + + return; +} + +void KyNetworkResourceManager::onConnectionRemoved(QString const & path) +{ + if (path.isEmpty()) { + qDebug() <<"[KyNetworkResourceManager]" <<"the connect path is empty"; + return; + } + + qDebug() <<"[KyNetworkResourceManager]" << "remove connection path" << path; + NetworkManager::Connection::Ptr connectionPtr = nullptr; + for (int index = 0; index < m_connections.size(); ++index) { + connectionPtr = m_connections.at(index); + if (connectionPtr->path() == path) { + removeConnection(index); + return; + } + } + + qWarning() <<"[KyNetworkResourceManager]" << path <<" connect is no exist in connect list"; + + return; +} + +void KyNetworkResourceManager::removeConnection(QString const & uuid) +{ + NetworkManager::Connection::Ptr conn = this->getConnect(uuid); + if(!conn.isNull()) { + conn->disconnect(this); + conn->remove(); + } +} + +void KyNetworkResourceManager::setWirelessNetworkEnabled(bool enabled) +{ + NetworkManager::setWirelessEnabled(enabled); +} + +void KyNetworkResourceManager::connectionDump() +{ + NetworkManager::Connection::Ptr connectionPtr = nullptr; + for (int index = 0; index < m_connections.size(); ++index) { + connectionPtr = m_connections.at(index); + qDebug()<<"connection info**********************"; + qDebug()<<"connection name"<< connectionPtr->name(); + qDebug()<<"connection uuid"<< connectionPtr->uuid(); + qDebug()<<"connection path"<< connectionPtr->path(); + } +} diff --git a/src/backend/dbus-interface/kylinnetworkresourcemanager.h b/src/backend/dbus-interface/kylinnetworkresourcemanager.h new file mode 100644 index 00000000..14e4ec27 --- /dev/null +++ b/src/backend/dbus-interface/kylinnetworkresourcemanager.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class KyNetworkResourceManager : public QObject +{ + Q_OBJECT +public: + static KyNetworkResourceManager* getInstance(); + static void Release(); + +public: + explicit KyNetworkResourceManager(QObject *parent = nullptr); + ~KyNetworkResourceManager(); + + void removeActiveConnection(int pos); + void clearActiveConnections(); + void insertActiveConnections(); + void addActiveConnection(NetworkManager::ActiveConnection::Ptr conn); + + void removeConnection(int pos); + void clearConnections(); + void insertConnections(); + void addConnection(NetworkManager::Connection::Ptr conn); + + void removeDevice(int pos); + void clearDevices(); + void insertDevices(); + void addDevice(NetworkManager::Device::Ptr conn); + + void removeWifiNetwork(int pos); + void clearWifiNetworks(); + void addWifiNetwork(NetworkManager::WirelessNetwork::Ptr net); + + NetworkManager::ActiveConnection::Ptr findActiveConnection(QString const & path); + template + NetworkManager::Device::Ptr findDevice(Predicate const & pred); + NetworkManager::Device::Ptr findDeviceUni(QString const & uni); + NetworkManager::Device::Ptr findDeviceInterface(QString const & interfaceName); + NetworkManager::WirelessNetwork::Ptr findWifiNetwork(QString const & ssid, QString const & devUni); + + void requestScan(NetworkManager::WirelessDevice * dev); + + void removeConnection(QString const & uuid); + + void connectionDump(); + +public: + NetworkManager::Device::Ptr getNetworkDevice(const QString ifaceName); + NetworkManager::ActiveConnection::Ptr getActiveConnect(const QString activeConnectUuid); + NetworkManager::Connection::Ptr getConnect(const QString uuid); + NetworkManager::WirelessNetwork::Ptr getWifiNetwork(const QString apName); + + NetworkManager::ActiveConnection::List getActiveConnectList(); + NetworkManager::Connection::List getConnectList(); + NetworkManager::Device::List getNetworkDeviceList(); + bool isActiveConnection(QString uuid); + bool isActivatingConnection(QString uuid); + + void getConnectivity(NetworkManager::Connectivity &connectivity); + + bool NetworkManagerIsInited(); + +signals: + void connectionAdd(QString uuid); + void connectionUpdate(QString uuid); + void connectionRemove(QString path); + + void deviceAdd(QString deviceName, QString uni, NetworkManager::Device::Type deviceType); + void deviceUpdate(QString deviceName, QString deviceUni); + void deviceRemove(QString deviceName, QString uni); + + void deviceActiveChanage(QString deviceName, bool deviceActive); + void deviceCarrierChanage(QString deviceName, bool pluged); + void deviceBitRateChanage(QString deviceName, int bitRate); + void deviceMacAddressChanage(QString deviceName, const QString &hwAddress); + + //to KyWirelessNetResource + void wifiNetworkRemoved(QString, QString); + void wifiNetworkAdded(QString, QString); + void wifiNetworkPropertyChange(NetworkManager::WirelessNetwork * net); + void wifiNetworkDeviceDisappear(); + void wifiEnabledChanged(bool); + + void activeConnectionsReset(); + void activeConnectionAdd(QString uuid); + void activeConnectionUpdate(QString uuid); + void activeConnectionRemove(QString uuid); + void activeConnectStateChangeReason(QString uuid, + NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason); + void vpnActiveConnectStateChangeReason(QString uuid, + NetworkManager::VpnConnection::State state, + NetworkManager::VpnConnection::StateChangeReason reason); + + void connectivityChanged(NetworkManager::Connectivity connectivity); + +public slots: + void onInitNetwork(); + void setWirelessNetworkEnabled(bool enabled); + +private slots: + void insertWifiNetworks(); + void onServiceAppear(QString, QString, QString); + //connection + void onConnectionUpdated(); + //void onConnectionRemoved(); + + //active connection + void onActiveConnectionUpdated(); + void onActiveConnectionChangedReason(NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason); + + void onActiveConnectionChanged(NetworkManager::ActiveConnection::State state); + void onVpnActiveConnectChanagedReason(NetworkManager::VpnConnection::State state, + NetworkManager::VpnConnection::StateChangeReason reason); + + //device + + void onDeviceActiveChanage(); + + void onDeviceUpdated(); + void onDeviceCarrierChanage(bool pluged); + void onDeviceBitRateChanage(int bitRate); + void onDeviceMacAddressChanage(const QString &hwAddress); + void onDeviceStateChanged(NetworkManager::Device::State newstate, + NetworkManager::Device::State oldstate, + NetworkManager::Device::StateChangeReason reason); + + void onWifiNetworkAppeared(QString const & ssid); + void onWifiNetworkDisappeared(QString const & ssid); + + //wifi network + void onUpdateWirelessNet(); + + //notifier + void onDeviceAdded(QString const & uni); + void onDeviceRemoved(QString const & uni); + void onActiveConnectionAdded(QString const & path); + void onActiveConnectionRemoved(QString const & path); + + //settings notifier + void onConnectionAdded(QString const & path); + void onConnectionRemoved(QString const & path); + +private: + void onWifiNetworkAdd(NetworkManager::Device * dev, QString const & ssid); + void onWifiNetworkUpdate(NetworkManager::WirelessNetwork * net); + void onWifiNetworkRemove(NetworkManager::Device * dev, QString const & ssid); + +private: + bool m_initFinished = false; + +public: + static KyNetworkResourceManager* m_pInstance; + + NetworkManager::ActiveConnection::List m_activeConns; //已连接资源类 + NetworkManager::Connection::List m_connections; //配置文件资源类 + NetworkManager::Device::List m_devices; //设备类 + NetworkManager::WirelessNetwork::List m_wifiNets; //无线热点类 +}; + + +#endif // KYLINNETWORKRESOURCEMANAGER_H diff --git a/src/backend/dbus-interface/kylinutil.cpp b/src/backend/dbus-interface/kylinutil.cpp new file mode 100644 index 00000000..6fce5409 --- /dev/null +++ b/src/backend/dbus-interface/kylinutil.cpp @@ -0,0 +1,85 @@ +#include "kylinutil.h" +#include + +#define LOG_FLAG "[kylin-util]" + +QString getConnectTypeByDbus(QString &connectPath) +{ + QString connectType = ""; + + if (connectPath.isEmpty()) { + qWarning()<< LOG_FLAG << "connect path is empty, so can not get connect type"; + return connectType; + } + + QDBusInterface dbusInterface("org.freedesktop.NetworkManager", + connectPath, + "org.freedesktop.NetworkManager.Settings.Connection", + QDBusConnection::systemBus()); + + QDBusMessage result = dbusInterface.call("GetSettings"); + const QDBusArgument &dbusArg1st = result.arguments().at( 0 ).value(); + QMap> map; + dbusArg1st >> map; + + if (map.isEmpty()) { + qWarning() << LOG_FLAG <<"get connection settings failed."; + return connectType; + } + + QMap connectMap = map.value(KEY_CONNECTION); + if (connectMap.isEmpty()) { + qWarning() << LOG_FLAG <<"threre is not connection settings"; + return connectType; + } + + connectType = connectMap.value(KEY_CONNECT_TYPE).toString(); + +// qDebug() << LOG_FLAG << "connection type" << connectType; + + return connectType; + +} + +QString getSsidFromByteArray(QByteArray &rawSsid) +{ + QString wifiSsid = ""; + + if (rawSsid.isEmpty()) { + qWarning() << LOG_FLAG << "wifi raw ssid is empty"; + return wifiSsid; + } + + /* + * 由于区分GB2312和UTF-8的方法比较困难,加之会存在中英文混合的情况,所以暂时 + * 不区分,统一经过gb2312转换,经过测试没有影响。 + */ +// QTextCodec *p_textGBK = QTextCodec::codecForName("GB2312"); +// wifiSsid = p_textGBK->toUnicode(rawSsid); + + QTextCodec::ConverterState state; + QTextCodec *codec = QTextCodec::codecForName("UTF-8"); + codec->toUnicode( rawSsid.constData(), rawSsid.size(), &state); + if (state.invalidChars > 0) + { + wifiSsid = QTextCodec::codecForName("GBK")->toUnicode(rawSsid); + } + else + { + wifiSsid = rawSsid; + } + +// if (!QString::fromUtf8(rawSsid).contains("?")) { +// QTextCodec *p_textGBK = QTextCodec::codecForName("GB2312"); +// wifiSsid = p_textGBK->toUnicode(rawSsid); + +// qDebug() << LOG_FLAG <<"gb2312 to string ssid" << wifiSsid; +// //qDebug() << LOG_FLAG << "-------------> GB2312 " << byteArrayGB; +// } else { +// wifiSsid = QString::fromUtf8(rawSsid); + +// qDebug()<< LOG_FLAG <<" UTF-8 ssid: " < UTF-8 " << bytearray; +// } + return wifiSsid; +} diff --git a/src/backend/dbus-interface/kylinutil.h b/src/backend/dbus-interface/kylinutil.h new file mode 100644 index 00000000..08c980b8 --- /dev/null +++ b/src/backend/dbus-interface/kylinutil.h @@ -0,0 +1,20 @@ +#ifndef KYLINUTIL_H +#define KYLINUTIL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEY_CONNECTION "connection" +#define KEY_CONNECT_TYPE "type" +#define ETHERNET_TYPE "802-3-ethernet" + +QString getConnectTypeByDbus(QString &connectPath); +QString getSsidFromByteArray(QByteArray &rawSsid); + +#endif // KYLINUTIL_H diff --git a/src/backend/dbus-interface/kylinvpnconnectitem.cpp b/src/backend/dbus-interface/kylinvpnconnectitem.cpp new file mode 100644 index 00000000..7b770089 --- /dev/null +++ b/src/backend/dbus-interface/kylinvpnconnectitem.cpp @@ -0,0 +1,23 @@ + +#include "kylinvpnconnectitem.h" + + +KyVpnConnectItem::KyVpnConnectItem(QObject *parent) : QObject(parent) +{ + m_vpnName = ""; + m_vpnUuid = ""; + + m_vpnUser = ""; + m_vpnGateWay = ""; + + m_vpnIpv4Address = ""; + m_vpnIpv6Address = ""; + + m_vpnMppe = false; + m_vpnState = NetworkManager::VpnConnection::State::Disconnected; +} + +KyVpnConnectItem::~KyVpnConnectItem() +{ + +} diff --git a/src/backend/dbus-interface/kylinvpnconnectitem.h b/src/backend/dbus-interface/kylinvpnconnectitem.h new file mode 100644 index 00000000..e4340fbb --- /dev/null +++ b/src/backend/dbus-interface/kylinvpnconnectitem.h @@ -0,0 +1,30 @@ +#ifndef KYLINVPNCONNECTITEM_H +#define KYLINVPNCONNECTITEM_H + +#include +#include +#include + +class KyVpnConnectItem : public QObject +{ + Q_OBJECT +public: + explicit KyVpnConnectItem(QObject *parent = nullptr); + ~KyVpnConnectItem(); + +public: + QString m_vpnName; + QString m_vpnUuid; + + QString m_vpnUser; + QString m_vpnGateWay; + + QString m_vpnIpv4Address; + QString m_vpnIpv6Address; + + bool m_vpnMppe; + NetworkManager::VpnConnection::State m_vpnState; +}; + + +#endif // KYLINVPNCONNECTITEM_H diff --git a/src/backend/dbus-interface/kylinvpnrequest.c b/src/backend/dbus-interface/kylinvpnrequest.c new file mode 100644 index 00000000..c294bd66 --- /dev/null +++ b/src/backend/dbus-interface/kylinvpnrequest.c @@ -0,0 +1,762 @@ +#include "kylinvpnrequest.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "gsystem-local-alloc.h" +#include "nma-vpn-password-dialog.h" +#include "nm-macros-internal.h" + +extern char **environ; + +/*****************************************************************************/ + +typedef struct { + char *name; + char *label; + char *value; + gboolean is_secret; + gboolean should_ask; +} EuiSecret; + +typedef struct { + char *uuid; + char *id; + char *service_type; + + guint watch_id; + GPid pid; + + int child_stdout; + GString *child_response; + GIOChannel *channel; + guint channel_eventid; + GVariantBuilder secrets_builder; + gboolean external_ui_mode; + + /* These are just for the external UI mode */ + EuiSecret *eui_secrets; +} RequestData; + +typedef struct { + SecretsRequest req; + RequestData *req_data; +} VpnSecretsInfo; + + +gint +_nm_utils_ascii_str_to_bool (const char *str, + gint default_value) +{ + gsize len; + char *s = NULL; + + if (!str) + return default_value; + + while (str[0] && g_ascii_isspace (str[0])) + str++; + + if (!str[0]) + return default_value; + + len = strlen (str); + if (g_ascii_isspace (str[len - 1])) { + s = g_strdup (str); + g_strchomp (s); + str = s; + } + + if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1")) + default_value = TRUE; + else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0")) + default_value = FALSE; + if (s) + g_free (s); + return default_value; +} + + +/*****************************************************************************/ + +static void request_data_free (RequestData *req_data); +static void complete_request (VpnSecretsInfo *info); + +/*****************************************************************************/ + +size_t +applet_vpn_request_get_secrets_size (void) +{ + return sizeof (VpnSecretsInfo); +} + +/*****************************************************************************/ + +static void +external_ui_add_secrets (VpnSecretsInfo *info) +{ + RequestData *req_data = info->req_data; + EuiSecret *secret; + guint i; + + for (i = 0; req_data->eui_secrets[i].name; i++) { + secret = &req_data->eui_secrets[i]; + if ( secret->is_secret + && secret->value + && secret->value[0]) { + g_variant_builder_add (&req_data->secrets_builder, "{ss}", + secret->name, + secret->value); + } + } +} + +static void +external_ui_dialog_response (GtkDialog *dialog, int response_id, gpointer user_data) +{ + VpnSecretsInfo *info = user_data; + RequestData *req_data = info->req_data; + NMAVpnPasswordDialog *vpn_dialog = NMA_VPN_PASSWORD_DIALOG (dialog); + EuiSecret *secret; + const char *value; + guint i_secret, i_pw; + + for (i_secret = 0, i_pw = 0; req_data->eui_secrets[i_secret].name; i_secret++) { + secret = &req_data->eui_secrets[i_secret]; + if ( secret->is_secret + && secret->should_ask) { + switch (i_pw) { + case 0: + value = nma_vpn_password_dialog_get_password (vpn_dialog); + break; + case 1: + value = nma_vpn_password_dialog_get_password_secondary (vpn_dialog); + break; + case 2: + value = nma_vpn_password_dialog_get_password_ternary (vpn_dialog); + break; + default: + continue; + } + g_free (secret->value); + secret->value = g_strdup (value); + i_pw++; + } + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); + external_ui_add_secrets (info); + complete_request (info); +} + +static gboolean +external_ui_from_child_response (VpnSecretsInfo *info, GError **error) +{ + RequestData *req_data = info->req_data; + gs_unref_keyfile GKeyFile *keyfile = NULL; + gs_strfreev char **groups = NULL; + NMAVpnPasswordDialog *dialog = NULL; + gs_free char *version = NULL; + gs_free char *title = NULL; + gs_free char *message = NULL; + gsize num_groups; + guint num_ask = 0; + guint i_group, i_secret, i_pw; + + /* Parse response key file */ + keyfile = g_key_file_new (); + + if (!g_key_file_load_from_data (keyfile, + req_data->child_response->str, + req_data->child_response->len, + G_KEY_FILE_NONE, + error)) { + return FALSE; + } + + groups = g_key_file_get_groups (keyfile, &num_groups); + if (g_strcmp0 (groups[0], "VPN Plugin UI") != 0) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "Expected [VPN Plugin UI]"); + return FALSE; + } + + version = g_key_file_get_string (keyfile, "VPN Plugin UI", "Version", error); + if (!version) + return FALSE; + if (strcmp (version, "2") != 0) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "Expected Version=2"); + return FALSE; + } + + title = g_key_file_get_string (keyfile, "VPN Plugin UI", "Title", error); + if (!title) + return FALSE; + + message = g_key_file_get_string (keyfile, "VPN Plugin UI", "Description", error); + if (!message) + return FALSE; + + /* Create a secret instance for each group */ + req_data->eui_secrets = g_new0 (EuiSecret, num_groups); + for (i_group = 1, i_secret = 0; i_group < num_groups; i_group++) { + EuiSecret *secret = &req_data->eui_secrets[i_secret]; + const char *group = groups[i_group]; + char *label; + + label = g_key_file_get_string (keyfile, group, "Label", NULL); + if (!label) { + g_warning ("Skipping entry: no label\n"); + continue; + } + + secret->name = g_strdup (group); + secret->label = label; + secret->value = g_key_file_get_string (keyfile, group, "Value", NULL); + secret->is_secret = g_key_file_get_boolean (keyfile, group, "IsSecret", NULL); + secret->should_ask = g_key_file_get_boolean (keyfile, group, "ShouldAsk", NULL); + + i_secret++; + + if (secret->is_secret && secret->should_ask) + num_ask++; + } + + /* If there are any secrets that must be asked to user, + * create a dialog and display it. */ + if (num_ask > 0) { + dialog = (NMAVpnPasswordDialog *) nma_vpn_password_dialog_new (title, message, NULL); + g_object_ref_sink (dialog); + + nma_vpn_password_dialog_set_show_password (dialog, FALSE); + nma_vpn_password_dialog_set_show_password_secondary (dialog, FALSE); + nma_vpn_password_dialog_set_show_password_ternary (dialog, FALSE); + + for (i_secret = 0, i_pw = 0; req_data->eui_secrets[i_secret].name; i_secret++) { + EuiSecret *secret = &req_data->eui_secrets[i_secret]; + + if ( secret->is_secret + && secret->should_ask) { + switch (i_pw) { + case 0: + nma_vpn_password_dialog_set_show_password (dialog, TRUE); + nma_vpn_password_dialog_set_password_label (dialog, secret->label); + if (secret->value) + nma_vpn_password_dialog_set_password (dialog, secret->value); + break; + case 1: + nma_vpn_password_dialog_set_show_password_secondary (dialog, TRUE); + nma_vpn_password_dialog_set_password_secondary_label (dialog, secret->label); + if (secret->value) + nma_vpn_password_dialog_set_password_secondary (dialog, secret->value); + break; + case 2: + nma_vpn_password_dialog_set_show_password_ternary (dialog, TRUE); + nma_vpn_password_dialog_set_password_ternary_label (dialog, secret->label); + if (secret->value) + nma_vpn_password_dialog_set_password_ternary (dialog, secret->value); + break; + default: + g_warning ("Skipping entry: more than 3 passwords not supported\n"); + continue; + } + i_pw++; + } + } + g_signal_connect (dialog, + "response", + G_CALLBACK (external_ui_dialog_response), + info); + gtk_widget_show (GTK_WIDGET (dialog)); + return TRUE; + } + + /* Nothing to ask, return known secrets */ + external_ui_add_secrets (info); + complete_request (info); + return TRUE; +} + +/*****************************************************************************/ + +static void +complete_request (VpnSecretsInfo *info) +{ + SecretsRequest *req = (SecretsRequest *) info; + RequestData *req_data = info->req_data; + GVariantBuilder settings_builder, vpn_builder; + gs_unref_variant GVariant *settings = NULL; + + g_variant_builder_init (&settings_builder, NM_VARIANT_TYPE_CONNECTION); + g_variant_builder_init (&vpn_builder, NM_VARIANT_TYPE_SETTING); + + g_variant_builder_add (&vpn_builder, "{sv}", + NM_SETTING_VPN_SECRETS, + g_variant_builder_end (&req_data->secrets_builder)); + g_variant_builder_add (&settings_builder, "{sa{sv}}", + NM_SETTING_VPN_SETTING_NAME, + &vpn_builder); + settings = g_variant_builder_end (&settings_builder); + + applet_secrets_request_complete (req, settings, NULL); + applet_secrets_request_free (req); +} + +static void +process_child_response (VpnSecretsInfo *info) +{ + SecretsRequest *req = (SecretsRequest *) info; + RequestData *req_data = info->req_data; + gs_free_error GError *error = NULL; + + if (req_data->external_ui_mode) { + if (!external_ui_from_child_response (info, &error)) { + applet_secrets_request_complete (req, NULL, error); + applet_secrets_request_free (req); + } + } else { + char **lines = g_strsplit (req_data->child_response->str, "\n", -1); + int i; + + for (i = 0; lines[i] && *(lines[i]); i += 2) { + if (lines[i + 1] == NULL) + break; + g_variant_builder_add (&req_data->secrets_builder, "{ss}", lines[i], lines[i + 1]); + } + + g_strfreev (lines); + complete_request (info); + } +} + +static void +child_finished_cb (GPid pid, int status, gpointer user_data) +{ + VpnSecretsInfo *info = user_data; + SecretsRequest *req = (SecretsRequest *) info; + RequestData *req_data = info->req_data; + gs_free_error GError *error = NULL; + + req_data->pid = 0; + req_data->watch_id = 0; + + if (status) { + error = g_error_new (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_USER_CANCELED, + "%s.%d (%s): canceled", __FILE__, __LINE__, __func__); + + applet_secrets_request_complete (req, NULL, error); + applet_secrets_request_free (req); + } else if (req_data->channel_eventid == 0) { + /* We now have both the child response and its exit status. Process it. */ + process_child_response (info); + } +} + +static gboolean +child_stdout_data_cb (GIOChannel *source, GIOCondition condition, gpointer user_data) +{ + SecretsRequest *req = user_data; + VpnSecretsInfo *info = (VpnSecretsInfo *) req; + RequestData *req_data = info->req_data; + GIOStatus status; + char buf[4096]; + size_t bytes_read; + gs_free_error GError *error = NULL; + + status = g_io_channel_read_chars (source, buf, sizeof (buf)-1, &bytes_read, &error); + switch (status) { + case G_IO_STATUS_ERROR: + req_data->channel_eventid = 0; + applet_secrets_request_complete (req, NULL, error); + applet_secrets_request_free (req); + return FALSE; + case G_IO_STATUS_EOF: + req_data->channel_eventid = 0; + if (req_data->pid == 0) { + /* We now have both the childe respons and + * its exit status. Process it. */ + process_child_response (info); + } + return FALSE; + case G_IO_STATUS_NORMAL: + g_string_append_len (req_data->child_response, buf, bytes_read); + break; + default: + /* What just happened... */ + g_return_val_if_reached (FALSE); + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +_str_append (GString *str, + const char *tag, + const char *val) +{ + const char *s; + gsize i; + + nm_assert (str); + nm_assert (tag && tag[0]); + nm_assert (val); + + g_string_append (str, tag); + g_string_append_c (str, '='); + + s = strchr (val, '\n'); + if (s) { + gs_free char *val2 = g_strdup (val); + + for (i = 0; val2[i]; i++) { + if (val2[i] == '\n') + val2[i] = ' '; + } + g_string_append (str, val2); + } else + g_string_append (str, val); + g_string_append_c (str, '\n'); +} + +static char * +connection_to_data (NMConnection *connection, + gsize *out_length, + GError **error) +{ + NMSettingVpn *s_vpn; + GString *buf; + const char **keys; + guint i, len; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + s_vpn = nm_connection_get_setting_vpn (connection); + if (!s_vpn) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + ("Connection had no VPN setting")); + return NULL; + } + + buf = g_string_new_len (NULL, 100); + + keys = nm_setting_vpn_get_data_keys (s_vpn, &len); + for (i = 0; i < len; i++) { + _str_append (buf, "DATA_KEY", keys[i]); + _str_append (buf, "DATA_VAL", nm_setting_vpn_get_data_item (s_vpn, keys[i])); + } + nm_clear_g_free (&keys); + + keys = nm_setting_vpn_get_secret_keys (s_vpn, &len); + for (i = 0; i < len; i++) { + _str_append (buf, "SECRET_KEY", keys[i]); + _str_append (buf, "SECRET_VAL", nm_setting_vpn_get_secret (s_vpn, keys[i])); + } + nm_clear_g_free (&keys); + + g_string_append (buf, "DONE\n\nQUIT\n\n"); + NM_SET_OUT (out_length, buf->len); + return g_string_free (buf, FALSE); +} + +/*****************************************************************************/ + +static gboolean +connection_to_fd (NMConnection *connection, + int fd, + GError **error) +{ + gs_free char *data = NULL; + gsize data_len; + gssize w; + int errsv; + + data = connection_to_data (connection, &data_len, error); + if (!data) + return FALSE; + +again: + w = write (fd, data, data_len); + if (w < 0) { + errsv = errno; + if (errsv == EINTR) + goto again; + g_set_error (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + ("Failed to write connection to VPN UI: %s (%d)"), g_strerror (errsv), errsv); + return FALSE; + } + + if ((gsize) w != data_len) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + ("Failed to write connection to VPN UI: incomplete write")); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +vpn_child_setup (gpointer user_data) +{ + /* We are in the child process at this point */ + pid_t pid = getpid (); + setpgid (pid, pid); +} + +static gboolean +auth_dialog_spawn (const char *con_id, + const char *con_uuid, + const char *const*hints, + const char *auth_dialog, + const char *service_type, + gboolean supports_hints, + gboolean external_ui_mode, + guint32 flags, + GPid *out_pid, + int *out_stdin, + int *out_stdout, + GError **error) +{ + gsize hints_len; + gsize i, j; + gs_free const char **argv = NULL; + gs_free const char **envp = NULL; + gsize environ_len; + + g_return_val_if_fail (con_id, FALSE); + g_return_val_if_fail (con_uuid, FALSE); + g_return_val_if_fail (auth_dialog, FALSE); + g_return_val_if_fail (service_type, FALSE); + g_return_val_if_fail (out_pid, FALSE); + g_return_val_if_fail (out_stdin, FALSE); + g_return_val_if_fail (out_stdout, FALSE); + + hints_len = NM_PTRARRAY_LEN (hints); + argv = g_new (const char *, 11 + (2 * hints_len)); + i = 0; + argv[i++] = auth_dialog; + argv[i++] = "-u"; + argv[i++] = con_uuid; + argv[i++] = "-n"; + argv[i++] = con_id; + argv[i++] = "-s"; + argv[i++] = service_type; + if (flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION) + argv[i++] = "-i"; + if (flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) + argv[i++] = "-r"; + for (j = 0; supports_hints && (j < hints_len); j++) { + argv[i++] = "-t"; + argv[i++] = hints[j]; + } + if (external_ui_mode) + argv[i++] = "--external-ui-mode"; + nm_assert (i <= 10 + (2 * hints_len)); + argv[i++] = NULL; + + environ_len = NM_PTRARRAY_LEN (environ); + envp = g_new (const char *, environ_len + 1); + memcpy (envp, environ, sizeof (const char *) * environ_len); + for (i = 0, j = 0; i < environ_len; i++) { + const char *e = environ[i]; + + if (g_str_has_prefix (e, "G_MESSAGES_DEBUG=")) { + /* skip this environment variable. We interact with the auth-dialog via stdout. + * G_MESSAGES_DEBUG may enable additional debugging messages from GTK. */ + continue; + } + envp[j++] = e; + } + envp[j] = NULL; + + if (!g_spawn_async_with_pipes (NULL, + (char **) argv, + (char **) envp, + G_SPAWN_DO_NOT_REAP_CHILD, + vpn_child_setup, + NULL, + out_pid, + out_stdin, + out_stdout, + NULL, + error)) + return FALSE; + + return TRUE; +} + +/*****************************************************************************/ + +static void +free_vpn_secrets_info (SecretsRequest *req) +{ + request_data_free (((VpnSecretsInfo *) req)->req_data); +} + +gboolean +applet_vpn_request_get_secrets (SecretsRequest *req, GError **error) +{ + VpnSecretsInfo *info = (VpnSecretsInfo *) req; + RequestData *req_data; + NMSettingConnection *s_con; + NMSettingVpn *s_vpn; + const char *connection_type; + const char *service_type; + const char *auth_dialog; + gs_unref_object NMVpnPluginInfo *plugin = NULL; + int child_stdin; + + applet_secrets_request_set_free_func (req, free_vpn_secrets_info); + + s_con = nm_connection_get_setting_connection (req->connection); + s_vpn = nm_connection_get_setting_vpn (req->connection); + + connection_type = nm_setting_connection_get_connection_type (s_con); + g_return_val_if_fail (nm_streq0 (connection_type, NM_SETTING_VPN_SETTING_NAME), FALSE); + + service_type = nm_setting_vpn_get_service_type (s_vpn); + g_return_val_if_fail (service_type, FALSE); + + plugin = nm_vpn_plugin_info_new_search_file (NULL, service_type); + auth_dialog = plugin ? nm_vpn_plugin_info_get_auth_dialog (plugin) : NULL; + if (!auth_dialog) { + g_set_error (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "Could not find the authentication dialog for VPN connection type '%s'", + service_type); + return FALSE; + } + + info->req_data = g_slice_new0 (RequestData); + if (!info->req_data) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "Could not create VPN secrets request object"); + return FALSE; + } + req_data = info->req_data; + + g_variant_builder_init (&req_data->secrets_builder, G_VARIANT_TYPE ("a{ss}")); + + req_data->external_ui_mode = _nm_utils_ascii_str_to_bool ( + nm_vpn_plugin_info_lookup_property (plugin, + "GNOME", + "supports-external-ui-mode"), + FALSE); + + if (!auth_dialog_spawn (nm_setting_connection_get_id (s_con), + nm_setting_connection_get_uuid (s_con), + (const char *const*) req->hints, + auth_dialog, + service_type, + nm_vpn_plugin_info_supports_hints (plugin), + req_data->external_ui_mode, + req->flags, + &req_data->pid, + &child_stdin, + &req_data->child_stdout, + error)) + return FALSE; + + /* catch when child is reaped */ + req_data->watch_id = g_child_watch_add (req_data->pid, child_finished_cb, info); + + /* listen to what child has to say */ + req_data->channel = g_io_channel_unix_new (req_data->child_stdout); + req_data->child_response = g_string_sized_new (4096); + req_data->channel_eventid = g_io_add_watch (req_data->channel, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + child_stdout_data_cb, + info); + + if (!connection_to_fd (req->connection, child_stdin, error)) + return FALSE; + close (child_stdin); + + g_io_channel_set_encoding (req_data->channel, NULL, NULL); + + /* Dump parts of the connection to the child */ + return TRUE; +} + +/*****************************************************************************/ + +static gboolean +ensure_killed (gpointer data) +{ + pid_t pid = GPOINTER_TO_INT (data); + + if (kill (pid, 0) == 0) + kill (pid, SIGKILL); + /* ensure the child is reaped */ + waitpid (pid, NULL, 0); + return FALSE; +} + +static void +request_data_free (RequestData *req_data) +{ + guint i; + + if (!req_data) + return; + + g_free (req_data->uuid); + g_free (req_data->id); + g_free (req_data->service_type); + + nm_clear_g_source (&req_data->watch_id); + + nm_clear_g_source (&req_data->channel_eventid); + if (req_data->channel) + g_io_channel_unref (req_data->channel); + + if (req_data->pid) { + g_spawn_close_pid (req_data->pid); + if (kill (req_data->pid, SIGTERM) == 0) + g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (req_data->pid)); + else { + kill (req_data->pid, SIGKILL); + /* ensure the child is reaped */ + waitpid (req_data->pid, NULL, 0); + } + } + + if (req_data->child_response) + g_string_free (req_data->child_response, TRUE); + + g_variant_builder_clear (&req_data->secrets_builder); + + if (req_data->eui_secrets) { + for (i = 0; req_data->eui_secrets[i].name; i++) { + g_free (req_data->eui_secrets[i].name); + g_free (req_data->eui_secrets[i].label); + g_free (req_data->eui_secrets[i].value); + } + g_free (req_data->eui_secrets); + } + + g_slice_free (RequestData, req_data); +} diff --git a/src/backend/dbus-interface/kylinvpnrequest.h b/src/backend/dbus-interface/kylinvpnrequest.h new file mode 100644 index 00000000..a779acd0 --- /dev/null +++ b/src/backend/dbus-interface/kylinvpnrequest.h @@ -0,0 +1,30 @@ +#ifndef KYLIN_VPN_REQUEST_H +#define KYLIN_VPN_REQUEST_H + +#include +#include "kylinagent.h" + +typedef struct _SecretsRequest SecretsRequest; +typedef void (*SecretsRequestFreeFunc) (SecretsRequest *req); +struct _SecretsRequest { + size_t totsize; + gpointer reqid; + char *setting_name; + char **hints; + guint32 flags; + AppletAgent *agent; + AppletAgentSecretsCallback callback; + gpointer callback_data; + + NMConnection *connection; + + /* Class-specific stuff */ + SecretsRequestFreeFunc free_func; +}; + + +size_t applet_vpn_request_get_secrets_size(void); + +gboolean applet_vpn_request_get_secrets(SecretsRequest *req, GError **error); + +#endif /* APPLET_VPN_REQUEST_H */ diff --git a/src/backend/dbus-interface/kylinwiredconnectoperation.cpp b/src/backend/dbus-interface/kylinwiredconnectoperation.cpp new file mode 100644 index 00000000..0f477c6f --- /dev/null +++ b/src/backend/dbus-interface/kylinwiredconnectoperation.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include + +KyWiredConnectOperation::KyWiredConnectOperation(QObject *parent) : KyConnectOperation(parent) +{ + +} + +KyWiredConnectOperation::~KyWiredConnectOperation() +{ +} + +void KyWiredConnectOperation::createWiredConnect(KyConnectSetting &connectSettingsInfo) +{ + qDebug()<<"[KyWiredConnectOperation]" << "create connect "; + connectSettingsInfo.dumpInfo(); + + NetworkManager::ConnectionSettings::Ptr wiredConnectionSettings = NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wired)); + connectSettingSet(wiredConnectionSettings, connectSettingsInfo); + + NetworkManager::Ipv4Setting::Ptr ipv4Setting = wiredConnectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast(); + ipv4SettingSet(ipv4Setting, connectSettingsInfo); + + NetworkManager::Ipv6Setting::Ptr ipv6Setting = wiredConnectionSettings->setting(NetworkManager::Setting::Ipv6).dynamicCast(); + ipv6SettingSet(ipv6Setting, connectSettingsInfo); + + NetworkManager::WiredSetting::Ptr wiredSetting = wiredConnectionSettings->setting(NetworkManager::Setting::Wired).dynamicCast(); + wiredSetting->setInitialized(true); + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addConnection(wiredConnectionSettings->toMap()), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = tr("create wired connection failed: ") + watcher->error().message(); + qWarning()<createConnectionError(errorMessage); + } else { + qDebug()<<"create wired connect complete"; + } + watcher->deleteLater(); + }); + + return; +} + +void KyWiredConnectOperation::updateWiredConnect(const QString &connectUuid, const KyConnectSetting &connectSettingsInfo) +{ + qDebug()<<"update connect"<settings(); + updateConnect(connectSettingPtr, connectSettingsInfo); + + NMVariantMapMap mapmap1 = connectSettingPtr->toMap(); + if (mapmap1.contains("ipv4")) { + QVariantMap map1 = mapmap1.value(QLatin1String("ipv4")); + bool isAuto = false; + if (map1.contains("method") && map1["method"] == "auto") { + qDebug() << "[KyWiredConnectOperation] set ipv4 method auto, clear address-data && addresses && gateway"; + isAuto = true; + } + if (isAuto) { + if (map1.contains("address-data")) { + map1.remove("address-data"); + } + if (map1.contains("addresses")) { + map1.remove("addresses"); + } + if (map1.contains("gateway")) { + map1.remove("gateway"); + } + } + mapmap1["ipv4"] = map1; + } + + if (mapmap1.contains("ipv6")) { + QVariantMap map2 = mapmap1.value(QLatin1String("ipv6")); + bool isAuto = false; + if (map2.contains("method") && map2["method"] == "auto") { + qDebug() << "[KyWiredConnectOperation] set ipv6 method auto, clear address-data && addresses && gateway"; + isAuto = true; + } + if (isAuto) { + if (map2.contains("address-data")) { + map2.remove("address-data"); + } + if (map2.contains("addresses")) { + map2.remove("addresses"); + } + if (map2.contains("gateway")) { + map2.remove("gateway"); + } + } + mapmap1["ipv6"] = map2; + } + + connectPtr->update(mapmap1); + + return ; +} + +void KyWiredConnectOperation::deleteWiredConnect(const QString &connectUuid) +{ + qDebug()<<"delete wired connect uuid " << connectUuid; + + deleteConnect(connectUuid); + + return ; +} + +void KyWiredConnectOperation::activateWiredConnection(const QString connectUuid, const QString devName) +{ + activateConnection(connectUuid, devName); + return ; +} + +void KyWiredConnectOperation::deactivateWiredConnection(const QString activeConnectName, const QString &activeConnectUuid) +{ + qDebug()<<"deactivetate connect name"<settings()->connectionType()) { + QString errorMessage = tr("the connect type is") + + connectPtr->settings()->connectionType() + + tr(", but it is not vpn"); + qWarning()<path(); + connectName = connectPtr->name(); + //deviceName = connectPtr->settings()->interfaceName(); + specificObject = deviceIdentifier = QStringLiteral("/"); + + qDebug() <<"active wired connect: path "<< connectPath + << "device identify " << deviceIdentifier + << "connect name " << connectName + // << "device name" << deviceName + << "specific parameter"<< specificObject; + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::activateConnection(connectPath, deviceIdentifier, specificObject), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [this, connectName] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = tr("activate vpn connection failed: ") + watcher->error().message(); + qWarning()<activateConnectionError(errorMessage); + } else { + qWarning()<<"active vpn connect complete."; + } + + watcher->deleteLater(); + }); + + return; +} + +void KyWiredConnectOperation::saveActiveConnection(QString &deviceName, QString &connectUuid) +{ + QSettings *p_settings = new QSettings(WIRED_NETWORK_STATE_CONF_FILE, QSettings::IniFormat); + + QString settingValue = p_settings->value(deviceName).toString(); + if (settingValue.isEmpty()) { + p_settings->setValue(deviceName, connectUuid); + p_settings->sync(); + } + + delete p_settings; + p_settings = nullptr; + + return; +} + +void KyWiredConnectOperation::getActiveConnection(QString &deviceName, QString &connectUuid) +{ + QSettings *p_settings = new QSettings(WIRED_NETWORK_STATE_CONF_FILE, QSettings::IniFormat); + + connectUuid = p_settings->value(deviceName).toString(); + p_settings->remove(deviceName); + + delete p_settings; + p_settings = nullptr; + + return; +} + +int KyWiredConnectOperation::closeWiredNetworkWithDevice(QString deviceName) +{ + NetworkManager::Device::Ptr wiredDevicePtr = + m_networkResourceInstance->findDeviceInterface(deviceName); + + if (wiredDevicePtr.isNull()) { + qWarning()<<"[KyWiredConnectOperation]"<<"the network device" << deviceName <<"is not exist."; + return -ENXIO; + } + + if (NetworkManager::Device::Type::Ethernet != wiredDevicePtr->type()) { + qWarning()<<"[KyWiredConnectOperation]"<<"the device type" + << wiredDevicePtr->type() <<"is not Ethernet."; + return -EINVAL; + } + + NetworkManager::ActiveConnection::Ptr activeConnectPtr = wiredDevicePtr->activeConnection(); + if (nullptr != activeConnectPtr) { + QString activeConnectUuid = activeConnectPtr->uuid(); + if (!activeConnectUuid.isEmpty()) { + qDebug()<<"[KyWiredConnectOperation]" <<"close wired network save connection uuid" + << activeConnectUuid <<"device name " << deviceName; + saveActiveConnection(deviceName, activeConnectUuid); + } + } + + //wiredDevicePtr->setAutoconnect(false); + wiredDevicePtr->disconnectInterface(); + + return 0; +} + +int KyWiredConnectOperation::openWiredNetworkWithDevice(QString deviceName) +{ + NetworkManager::Device::Ptr wiredDevicePtr = + m_networkResourceInstance->findDeviceInterface(deviceName); + + if (wiredDevicePtr.isNull() || !wiredDevicePtr->isValid()) { + qWarning()<<"[KyWiredConnectOperation]"<<"the network device" << deviceName <<"is not exist."; + return -ENXIO; + } + + if (NetworkManager::Device::Type::Ethernet != wiredDevicePtr->type()) { + qWarning()<<"[KyWiredConnectOperation]"<<"the device type" + << wiredDevicePtr->type() <<"is not Ethernet."; + return -EINVAL; + } + + NetworkManager::WiredDevice *p_wiredDevice = + qobject_cast(wiredDevicePtr.data()); + + if (p_wiredDevice->carrier()) { + QString connectUuid; + getActiveConnection(deviceName, connectUuid); + if (!connectUuid.isEmpty()) { + qDebug()<<"[KyWiredConnectOperation]" << "open wired network active connection" + << connectUuid <<"device name" << deviceName; + activateConnection(connectUuid, deviceName); + } + } + + wiredDevicePtr->setAutoconnect(true); + + return 0; +} diff --git a/src/backend/dbus-interface/kylinwiredconnectoperation.h b/src/backend/dbus-interface/kylinwiredconnectoperation.h new file mode 100644 index 00000000..4a0e0ee6 --- /dev/null +++ b/src/backend/dbus-interface/kylinwiredconnectoperation.h @@ -0,0 +1,36 @@ +#ifndef KYLINWIREDCONNECTOPERATION_H +#define KYLINWIREDCONNECTOPERATION_H + +#include +#include + +#include "kylinnetworkresourcemanager.h" +#include "kylinconnectsetting.h" +#include "kylinconnectoperation.h" + +const QString WIRED_NETWORK_STATE_CONF_FILE = QDir::homePath() + "/.config/ukui/kylin-nm-wired.ini"; + +class KyWiredConnectOperation : public KyConnectOperation +{ + Q_OBJECT +public: + explicit KyWiredConnectOperation(QObject *parent = nullptr); + ~KyWiredConnectOperation(); + +public: + void createWiredConnect(KyConnectSetting &connectSettingsInfo); + void updateWiredConnect(const QString &connectUuid, const KyConnectSetting &connectSettingsInfo); + void deleteWiredConnect(const QString &connectUuid); + void activateWiredConnection(const QString connectUuid, const QString devName); + void activateVpnConnection(const QString connectUuid); + void deactivateWiredConnection(const QString activeConnectName, const QString &activeConnectUuid); + + int closeWiredNetworkWithDevice(QString deviceName); + int openWiredNetworkWithDevice(QString deviceName); + +private: + void getActiveConnection(QString &deviceName, QString &connectUuid); + void saveActiveConnection(QString &deviceName, QString &connectUuid); +}; + +#endif // KYLINWIREDCONNECTOPERATION_H diff --git a/src/backend/dbus-interface/kywirelessconnectoperation.cpp b/src/backend/dbus-interface/kywirelessconnectoperation.cpp new file mode 100644 index 00000000..77394034 --- /dev/null +++ b/src/backend/dbus-interface/kywirelessconnectoperation.cpp @@ -0,0 +1,1058 @@ +#include "kywirelessconnectoperation.h" +#include "kylinutil.h" + +#include + +#define PSK_SETTING_NAME "802-11-wireless-security" +#define PRIVATE_PSK_SETTING_NAME "802-1x" + +NetworkManager::ConnectionSettings::Ptr assembleWpaXPskSettings(NetworkManager::AccessPoint::Ptr accessPoint, QString &psk, bool isAutoConnect) +{ + QByteArray rawSsid = accessPoint->rawSsid(); + QString wifiSsid = getSsidFromByteArray(rawSsid); + + NetworkManager::ConnectionSettings::Ptr settings{new NetworkManager::ConnectionSettings{NetworkManager::ConnectionSettings::Wireless}}; + settings->setId(wifiSsid); + settings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); + settings->setAutoconnect(isAutoConnect); + //Note: workaround for wrongly (randomly) initialized gateway-ping-timeout + settings->setGatewayPingTimeout(0); + + NetworkManager::WirelessSetting::Ptr wifi_sett + = settings->setting(NetworkManager::Setting::Wireless).dynamicCast(); + wifi_sett->setInitialized(true); + wifi_sett->setSsid(rawSsid); + wifi_sett->setSecurity("802-11-wireless-security"); + + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = settings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + security_sett->setInitialized(true); + if (NetworkManager::AccessPoint::Adhoc == accessPoint->mode()) { + wifi_sett->setMode(NetworkManager::WirelessSetting::Adhoc); + security_sett->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaNone); + } else { + security_sett->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk); + } + if (!psk.isEmpty()) { + security_sett->setPsk(psk); + } + + return settings; +} + +NetworkManager::ConnectionSettings::Ptr assembleSaeSettings(NetworkManager::AccessPoint::Ptr accessPoint, QString &psk, bool isAutoConnect) +{ + QByteArray rawSsid = accessPoint->rawSsid(); + QString wifiSsid = getSsidFromByteArray(rawSsid); + + NetworkManager::ConnectionSettings::Ptr settings{new NetworkManager::ConnectionSettings{NetworkManager::ConnectionSettings::Wireless}}; + settings->setId(wifiSsid); + settings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); + settings->setAutoconnect(isAutoConnect); + //Note: workaround for wrongly (randomly) initialized gateway-ping-timeout + settings->setGatewayPingTimeout(0); + + NetworkManager::WirelessSetting::Ptr wifi_sett + = settings->setting(NetworkManager::Setting::Wireless).dynamicCast(); + wifi_sett->setInitialized(true); + wifi_sett->setSsid(rawSsid); + wifi_sett->setSecurity("802-11-wireless-security"); + + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = settings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + security_sett->setInitialized(true); + if (NetworkManager::AccessPoint::Adhoc == accessPoint->mode()) { + wifi_sett->setMode(NetworkManager::WirelessSetting::Adhoc); + security_sett->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaNone); + } else { + security_sett->setKeyMgmt(NetworkManager::WirelessSecuritySetting::SAE); + } + if (!psk.isEmpty()) { + security_sett->setPsk(psk); + } + + return settings; +} + +NetworkManager::ConnectionSettings::Ptr assembleWirelessSettings( + const NetworkManager::AccessPoint::Ptr accessPointPtr, + const KyWirelessConnectSetting &connSettingInfo, + bool isHidden) +{ + QByteArray rawSsid; + if (nullptr == accessPointPtr || accessPointPtr.isNull()) { + rawSsid = connSettingInfo.m_ssid.toUtf8(); + } else { + rawSsid = accessPointPtr->rawSsid(); + } + + NetworkManager::ConnectionSettings::Ptr settings{new NetworkManager::ConnectionSettings{NetworkManager::ConnectionSettings::Wireless}}; + settings->setId(connSettingInfo.m_connectName); + settings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); + settings->setAutoconnect(connSettingInfo.isAutoConnect); + //Note: workaround for wrongly (randomly) initialized gateway-ping-timeout + settings->setGatewayPingTimeout(0); + settings->setInterfaceName(connSettingInfo.m_ifaceName); + + NetworkManager::WirelessSetting::Ptr wifi_sett + = settings->setting(NetworkManager::Setting::Wireless).dynamicCast(); + wifi_sett->setInitialized(true); + wifi_sett->setSsid(rawSsid); + wifi_sett->setSecurity("802-11-wireless-security"); + wifi_sett->setHidden(isHidden); + + if (connSettingInfo.m_type != KyKeyMgmt::WpaNone && connSettingInfo.m_type != KyKeyMgmt::Unknown) + { + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = settings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + security_sett->setInitialized(true); + security_sett->setKeyMgmt((NetworkManager::WirelessSecuritySetting::KeyMgmt)connSettingInfo.m_type); + if (KyKeyMgmt::SAE == connSettingInfo.m_type || KyKeyMgmt::WpaPsk == connSettingInfo.m_type) + { + security_sett->setPsk(connSettingInfo.m_psk); + } + } + return settings; +} + + +KyWirelessConnectOperation::KyWirelessConnectOperation(QObject *parent) : KyConnectOperation(parent) +{ + connect(m_networkResourceInstance, &KyNetworkResourceManager::wifiEnabledChanged, + this, &KyWirelessConnectOperation::wifiEnabledChanged); + connect(this, &KyWirelessConnectOperation::enabledWirelessNetwork, + m_networkResourceInstance, &KyNetworkResourceManager::setWirelessNetworkEnabled, Qt::ConnectionType::QueuedConnection); +} + +KyWirelessConnectOperation::~KyWirelessConnectOperation() +{ + m_networkResourceInstance = nullptr; +} + +void KyWirelessConnectOperation::activeWirelessConnect(QString devIfaceName, QString connUuid) +{ + activateConnection(connUuid, devIfaceName); + return; +} + +void KyWirelessConnectOperation::deActivateWirelessConnection(const QString activeConnectName, const QString &activeConnectUuid) +{ + deactivateConnection(activeConnectName, activeConnectUuid); + return; +} + +//普通wifi +void KyWirelessConnectOperation::addConnect(const KyWirelessConnectSetting &connSettingInfo) +{ + NetworkManager::WirelessNetwork::Ptr wifiNet = + checkWifiNetExist(connSettingInfo.m_ssid, connSettingInfo.m_ifaceName); + if (wifiNet.isNull()) { + QString errorMessage = "the ssid " + connSettingInfo.m_ssid + + " is not exsit in " + connSettingInfo.m_ifaceName; + qWarning()<referenceAccessPoint(); + + NetworkManager::ConnectionSettings::Ptr connSetting = + assembleWirelessSettings(accessPointPtr, connSettingInfo, false); + setIpv4AndIpv6Setting(connSetting, connSettingInfo); + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addConnection(connSetting->toMap()), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = tr("create wireless connection failed: ") + watcher->error().message(); + qWarning()<createConnectionError(errorMessage); + } else { + qDebug()<<"create wireless connect complete"; + } + watcher->deleteLater(); + }); + + return; +} +//tls +void KyWirelessConnectOperation::addTlsConnect(const KyWirelessConnectSetting &connSettingInfo, const KyEapMethodTlsInfo &tlsInfo) +{ + NetworkManager::WirelessNetwork::Ptr wifiNet = + checkWifiNetExist(connSettingInfo.m_ssid, connSettingInfo.m_ifaceName); + if (wifiNet.isNull()) { + QString errorMessage = "the ssid " + connSettingInfo.m_ssid + + " is not exsit in " + connSettingInfo.m_ifaceName; + qWarning()<referenceAccessPoint(); + NetworkManager::ConnectionSettings::Ptr connSetting = + assembleWirelessSettings(accessPointPtr, connSettingInfo, false); + setIpv4AndIpv6Setting(connSetting, connSettingInfo); + assembleEapMethodTlsSettings(connSetting, tlsInfo); + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addConnection(connSetting->toMap()), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = tr("create wireless tls connection failed: ") + watcher->error().message(); + qWarning()<createConnectionError(errorMessage); + } else { + qDebug()<<"create wireless connect complete"; + } + watcher->deleteLater(); + }); + + return; +} +//peap +void KyWirelessConnectOperation::addPeapConnect(const KyWirelessConnectSetting &connSettingInfo, const KyEapMethodPeapInfo &peapInfo) +{ + NetworkManager::WirelessNetwork::Ptr wifiNet = + checkWifiNetExist(connSettingInfo.m_ssid, connSettingInfo.m_ifaceName); + if (wifiNet.isNull()) { + QString errorMessage = "the ssid " + connSettingInfo.m_ssid + + " is not exsit in " + connSettingInfo.m_ifaceName; + qWarning()<referenceAccessPoint(); + NetworkManager::ConnectionSettings::Ptr connSetting = + assembleWirelessSettings(accessPointPtr, connSettingInfo, false); + setIpv4AndIpv6Setting(connSetting, connSettingInfo); + assembleEapMethodPeapSettings(connSetting, peapInfo); + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addConnection(connSetting->toMap()), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = tr("create wireless peap connection failed: ") + watcher->error().message(); + qWarning()<createConnectionError(errorMessage); + } else { + qDebug()<<"create wireless connect complete"; + } + watcher->deleteLater(); + }); + + return; +} + +void KyWirelessConnectOperation::addTtlsConnect(const KyWirelessConnectSetting &connSettingInfo, const KyEapMethodTtlsInfo &ttlsInfo) +{ + NetworkManager::WirelessNetwork::Ptr wifiNet = + checkWifiNetExist(connSettingInfo.m_ssid, connSettingInfo.m_ifaceName); + if (wifiNet.isNull()) { + QString errorMessage = "the ssid " + connSettingInfo.m_ssid + + " is not exsit in " + connSettingInfo.m_ifaceName; + qWarning()<referenceAccessPoint(); + NetworkManager::ConnectionSettings::Ptr connSetting = + assembleWirelessSettings(accessPointPtr, connSettingInfo, false); + setIpv4AndIpv6Setting(connSetting, connSettingInfo); + assembleEapMethodTtlsSettings(connSetting, ttlsInfo); + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addConnection(connSetting->toMap()), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = tr("create wireless ttls connection failed: ") + watcher->error().message(); + qWarning()<createConnectionError(errorMessage); + } else { + qDebug()<<"create wireless connect complete"; + } + watcher->deleteLater(); + }); + + return; +} + +void KyWirelessConnectOperation::setWirelessAutoConnect(const QString &uuid, bool bAutoConnect) +{ + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(uuid); + if (nullptr == connectPtr) { + QString errorMessage = tr("it can not find connection") + uuid; + qWarning()<settings(); + setAutoConnect(connectionSettings, bAutoConnect); + connectPtr->update(connectionSettings->toMap()); + return; +} + +void KyWirelessConnectOperation::deleteWirelessConnect(const QString &connectUuid) +{ + qDebug()<<"delete wireless connect uuid " << connectUuid; + + deleteConnect(connectUuid); + + return ; +} + +QString KyWirelessConnectOperation::getPsk(const QString &connectUuid) +{ + qDebug() << "getPsk" << connectUuid; + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(connectUuid); + if (connectPtr.isNull()) { + QString errorMessage = tr("it can not find connection") + connectUuid; + qWarning()< reply = connectPtr->secrets(PSK_SETTING_NAME); + QMap map(reply.value()); + if (map.contains("802-11-wireless-security") + && map.value("802-11-wireless-security").contains("psk")) { + QString psk = map.value("802-11-wireless-security").value("psk").toString(); + return psk; + } + return ""; +} + +QString KyWirelessConnectOperation::getPrivateKeyPassword(const QString &connectUuid) +{ + qDebug() << "getPsk" << connectUuid; + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(connectUuid); + if (connectPtr.isNull()) { + QString errorMessage = tr("it can not find connection") + connectUuid; + qWarning()< reply = connectPtr->secrets(PRIVATE_PSK_SETTING_NAME); + QMap map(reply.value()); + if (map.contains("802-1x") + && map.value("802-1x").contains("private-key-password")) { + QString psk = map.value("802-1x").value("private-key-password").toString(); + return psk; + } + return ""; +} + +QString KyWirelessConnectOperation::get8021xPassword(const QString &connectUuid) +{ + qDebug() << "getPsk" << connectUuid; + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(connectUuid); + if (connectPtr.isNull()) { + QString errorMessage = tr("it can not find connection") + connectUuid; + qWarning()< reply = connectPtr->secrets(PRIVATE_PSK_SETTING_NAME); + QMap map(reply.value()); + if (map.contains("802-1x") && map.value("802-1x").contains("password")) + { + QString psk = map.value("802-1x").value("password").toString(); + return psk; + } + return ""; +} + +void KyWirelessConnectOperation::updateIpv4AndIpv6SettingInfo(const QString &uuid, const KyConnectSetting &connectSettingsInfo) +{ + qDebug()<<"updateIpv4AndIpv6SettingInfo wireless connect uuid " << uuid; + + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(uuid); + if (nullptr == connectPtr) { + QString errorMessage = tr("it can not find connection") + uuid; + qWarning()<settings(); + + setIpv4AndIpv6Setting(connectionSettings,connectSettingsInfo); + connectPtr->update(connectionSettings->toMap()); + return; +} + +void KyWirelessConnectOperation::updateWirelessPersonalConnect(const QString &uuid, const KyWirelessConnectSetting &connSettingInfo, bool bPwdChange) +{ + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(uuid); + if (nullptr == connectPtr) { + QString errorMessage = tr("it can not find connection") + uuid; + qWarning()<settings(); + + if (connSettingInfo.m_type != Unknown &&connSettingInfo.m_type != WpaNone && connSettingInfo.m_type != WpaPsk && connSettingInfo.m_type != SAE) + { + qDebug() << "updateWirelessPersonalConnect " << connSettingInfo.m_type << " not support"; + return; + } + + NetworkManager::Security8021xSetting::Ptr wifi_8021x_sett + = connectionSettings->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + wifi_8021x_sett->setInitialized(false); + + updateWirelessSecu(connectionSettings, connSettingInfo, bPwdChange); + connectPtr->update(connectionSettings->toMap()); + return; +} + +void KyWirelessConnectOperation::updateWirelessEnterPriseTlsConnect(const QString &uuid, const KyEapMethodTlsInfo &tlsInfo) +{ + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(uuid); + if (connectPtr.isNull()) { + QString errorMessage = tr("it can not find connection") + uuid; + qWarning()<settings(); + + setWirelessSecuWpaXEap(connectionSettings); + modifyEapMethodTlsSettings(connectionSettings, tlsInfo); + connectPtr->update(connectionSettings->toMap()); + return; +} + +void KyWirelessConnectOperation::updateWirelessEnterPrisePeapConnect(const QString &uuid, const KyEapMethodPeapInfo &peapInfo) +{ + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(uuid); + if (nullptr == connectPtr) { + QString errorMessage = tr("it can not find connection") + uuid; + qWarning()<settings(); + + setWirelessSecuWpaXEap(connectionSettings); + modifyEapMethodPeapSettings(connectionSettings, peapInfo); + connectPtr->update(connectionSettings->toMap()); + return; +} + +void KyWirelessConnectOperation::updateWirelessEnterPriseTtlsConnect(const QString &uuid, const KyEapMethodTtlsInfo &ttlsInfo) +{ + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(uuid); + if (nullptr == connectPtr) { + QString errorMessage = tr("it can not find connection") + uuid; + qWarning()<settings(); + + setWirelessSecuWpaXEap(connectionSettings); + modifyEapMethodTtlsSettings(connectionSettings, ttlsInfo); + connectPtr->update(connectionSettings->toMap()); + return; +} + +void KyWirelessConnectOperation::addAndActiveWirelessConnect(QString & devIface,KyWirelessConnectSetting &connSettingInfo,bool isHidden) +{ + qDebug() << "addAndActiveWirelessConnect" << connSettingInfo.m_ssid << devIface <findDeviceInterface(devIface); + if (dev.isNull()) { + emit addAndActivateConnectionError("can not find device"); + return; + } + + qDebug() << "addAndActiveWirelessConnect device " << devIface << " exist"; + dev_uni = dev->uni(); + auto spec_dev = dev->as(); + if (!isHidden) { + qDebug() << "start assemble"; + auto access_point = wifiNet->referenceAccessPoint(); + conn_uni = access_point->uni(); + conn_name = access_point->ssid(); + spec_object = conn_uni; + + NetworkManager::WirelessSecurityType sec_type = NetworkManager::findBestWirelessSecurity(spec_dev->wirelessCapabilities() + , true, (spec_dev->mode() == NetworkManager::WirelessDevice::Adhoc) + , access_point->capabilities(), access_point->wpaFlags(), access_point->rsnFlags()); + + qDebug() << "findBestWirelessSecurity type "<< sec_type; + + NetworkManager::ConnectionSettings::Ptr settings = nullptr; + switch (sec_type) + { + case NetworkManager::UnknownSecurity: + qWarning() << QStringLiteral("unknown security to use for '%1'").arg(conn_name); + case NetworkManager::NoneSecurity: + //nothing to do + break; + case NetworkManager::WpaPsk: + case NetworkManager::Wpa2Psk: + settings = assembleWpaXPskSettings(access_point, connSettingInfo.m_psk, connSettingInfo.isAutoConnect); + if (nullptr != settings) { + map_settings = settings->toMap(); + } else { + qWarning() << QStringLiteral("connection settings assembly for '%1' failed, abandoning activation...").arg(conn_name); + return; + } + break; + //TODO: other types... + case NetworkManager::SAE: + settings = assembleSaeSettings(access_point, connSettingInfo.m_psk, connSettingInfo.isAutoConnect); + if (nullptr != settings) { + map_settings = settings->toMap(); + } else { + qWarning() << QStringLiteral("connection settings assembly for '%1' failed, abandoning activation...").arg(conn_name); + return; + } + break; + default: + qDebug() << "addAndActiveWirelessConnect not support"; + break; + } + + qDebug() << "finish assemble"; + } else { + qDebug() << "start assembleWirelessSettings"; + map_settings = assembleWirelessSettings(nullptr, connSettingInfo, isHidden)->toMap(); + qDebug() << "finish assembleWirelessSettings"; + } + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addAndActivateConnection(map_settings, dev_uni, spec_object), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [&] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = watcher->error().message(); + qDebug() << "activation of connection failed " << errorMessage; + emit addAndActivateConnectionError(errorMessage); + } + watcher->deleteLater(); + }); +} + +void KyWirelessConnectOperation::requestWirelessScan() +{ + for (auto const & dev : m_networkResourceInstance->m_devices) { + auto spec_dev = dev->as(); + if (nullptr != spec_dev) { + m_networkResourceInstance->requestScan(spec_dev); + } + } +} + +void KyWirelessConnectOperation::addAndActiveWirelessEnterPriseTlsConnect(KyEapMethodTlsInfo &info, KyWirelessConnectSetting &connSettingInfo, + QString & devIface, bool isHidden) +{ + QString conn_uni; + QString dev_uni; + QString spec_object; + NMVariantMapMap map_settings; + NetworkManager::AccessPoint::Ptr accessPointPtr = nullptr; + + if (!isHidden) { + NetworkManager::WirelessNetwork::Ptr wifiNet = checkWifiNetExist(connSettingInfo.m_ssid, devIface); + if (wifiNet.isNull()) { + QString errorMessage = "the ssid " + connSettingInfo.m_ssid + " is not exsit in " + devIface; + qWarning()<referenceAccessPoint(); + conn_uni = accessPointPtr->uni(); + spec_object = conn_uni; + } + + auto dev = m_networkResourceInstance->findDeviceInterface(devIface); + if (dev.isNull()) { + emit addAndActivateConnectionError("can not find device"); + return; + } + dev_uni = dev->uni(); + + NetworkManager::ConnectionSettings::Ptr settings = assembleWirelessSettings(accessPointPtr, connSettingInfo, isHidden); + assembleEapMethodTlsSettings(settings, info); + + if(settings.isNull()) { + qDebug() << "assembleEapMethodTlsSettings failed"; + return; + } + + map_settings = settings->toMap(); + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addAndActivateConnection(map_settings, dev_uni, spec_object), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [&] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = watcher->error().message(); + qDebug() << "addAndActiveWirelessEnterPriseTlsConnect failed " << errorMessage; + emit addAndActivateConnectionError(errorMessage); + } + watcher->deleteLater(); + }); + +} + +void KyWirelessConnectOperation::addAndActiveWirelessEnterPrisePeapConnect(KyEapMethodPeapInfo &info, KyWirelessConnectSetting &connSettingInfo, + QString & devIface, bool isHidden) +{ + qDebug() <<"addAndActiveWirelessEnterPrisePeapConnect"; + QString conn_uni; + QString dev_uni; + QString spec_object; + NMVariantMapMap map_settings; + NetworkManager::AccessPoint::Ptr accessPointPtr = nullptr; + + if (!isHidden) { + NetworkManager::WirelessNetwork::Ptr wifiNet = checkWifiNetExist(connSettingInfo.m_ssid, devIface); + if (wifiNet.isNull()) { + QString errorMessage = "the ssid " + connSettingInfo.m_ssid + " is not exsit in " + devIface; + qWarning()<referenceAccessPoint(); + conn_uni = accessPointPtr->uni(); + spec_object = conn_uni; + } + + auto dev = m_networkResourceInstance->findDeviceInterface(devIface); + if (dev.isNull()) { + emit addAndActivateConnectionError("can not find device"); + return; + } + dev_uni = dev->uni(); + + NetworkManager::ConnectionSettings::Ptr settings = + assembleWirelessSettings(accessPointPtr, connSettingInfo, isHidden); + assembleEapMethodPeapSettings(settings, info); + + if(settings.isNull()) { + qDebug() << "assembleEapMethodPeapSettings failed"; + return; + } + map_settings = settings->toMap(); + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addAndActivateConnection(map_settings, dev_uni, spec_object), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [&] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = watcher->error().message(); + qDebug() << "addAndActiveWirelessEnterPrisePeapConnect failed " << errorMessage; + emit addAndActivateConnectionError(errorMessage); + } + watcher->deleteLater(); + }); + +} + +void KyWirelessConnectOperation::addAndActiveWirelessEnterPriseTtlsConnect(KyEapMethodTtlsInfo &info, KyWirelessConnectSetting &connSettingInfo, + QString & devIface, bool isHidden) +{ + QString conn_uni; + QString dev_uni; + QString spec_object; + NMVariantMapMap map_settings; + NetworkManager::AccessPoint::Ptr accessPointPtr = nullptr; + + if (!isHidden) { + NetworkManager::WirelessNetwork::Ptr wifiNet = checkWifiNetExist(connSettingInfo.m_ssid, devIface); + if (wifiNet.isNull()) { + QString errorMessage = "the ssid " + connSettingInfo.m_ssid + " is not exsit in " + devIface; + qWarning()<referenceAccessPoint(); + conn_uni = accessPointPtr->uni(); + spec_object = conn_uni; + } + + auto dev = m_networkResourceInstance->findDeviceInterface(devIface); + if (dev.isNull()) { + emit addAndActivateConnectionError("can not find device"); + return; + } + dev_uni = dev->uni(); + + NetworkManager::ConnectionSettings::Ptr settings = + assembleWirelessSettings(accessPointPtr, connSettingInfo, isHidden); + assembleEapMethodTtlsSettings(settings, info); + + if(settings.isNull()) { + qDebug() << "assembleEapMethodTtlsSettings failed"; + return; + } + + map_settings = settings->toMap(); + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addAndActivateConnection(map_settings, dev_uni, spec_object), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [&] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = watcher->error().message(); + qDebug() << "addAndActiveWirelessEnterPriseTtlsConnect failed " << errorMessage; + emit addAndActivateConnectionError(errorMessage); + } + watcher->deleteLater(); + }); +} + +//无线网络开关设置 +void KyWirelessConnectOperation::setWirelessEnabled(bool enabled) +{ + emit enabledWirelessNetwork(enabled); + + return; +} + +bool KyWirelessConnectOperation::getWirelessEnabled() +{ + return NetworkManager::isWirelessEnabled(); +} + +bool KyWirelessConnectOperation::getConnSecretFlags(QString &connUuid, NetworkManager::Setting::SecretFlags &flag) +{ + NetworkManager::Connection::Ptr conn; + conn = m_networkResourceInstance->getConnect(connUuid); + if (conn.isNull()) { + qDebug() <<"get failed"; + return false; + } + + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = conn->settings()->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + flag = security_sett->pskFlags(); + return true; +} + +NetworkManager::ConnectionSettings::Ptr + KyWirelessConnectOperation::createWirelessApSetting(const QString apSsid, + const QString apPassword, + const QString apDevice, + const QString wirelessBand) +{ + NetworkManager::ConnectionSettings::Ptr connectionSettings = + NetworkManager::ConnectionSettings::Ptr(new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Wireless)); + connectionSettings->setId(apSsid); + connectionSettings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); + connectionSettings->setAutoconnect(false); + connectionSettings->setAutoconnectPriority(0); + connectionSettings->setInterfaceName(apDevice); + //Note: workaround for wrongly (randomly) initialized gateway-ping-timeout + connectionSettings->setGatewayPingTimeout(0); + + NetworkManager::Ipv4Setting::Ptr ipv4Setting = connectionSettings->setting(NetworkManager::Setting::Ipv4).dynamicCast(); + ipv4Setting->setInitialized(true); + ipv4Setting->setMethod(NetworkManager::Ipv4Setting::Shared); + + NetworkManager::Ipv6Setting::Ptr ipv6Setting = connectionSettings->setting(NetworkManager::Setting::Ipv6).dynamicCast(); + ipv6Setting->setInitialized(true); + ipv6Setting->setMethod(NetworkManager::Ipv6Setting::Ignored); + + NetworkManager::WirelessSetting::Ptr wirelessSetting + = connectionSettings->setting(NetworkManager::Setting::Wireless).dynamicCast(); + wirelessSetting->setInitialized(true); + wirelessSetting->setSsid(apSsid.toUtf8()); + wirelessSetting->setMode(NetworkManager::WirelessSetting::NetworkMode::Ap); + wirelessSetting->setSecurity("802-11-wireless-security"); + if (wirelessBand == WIFI_BAND_2_4GHZ) { + wirelessSetting->setBand(NetworkManager::WirelessSetting::FrequencyBand::Bg); + } else if (wirelessBand == WIFI_BAND_5GHZ) { + wirelessSetting->setBand(NetworkManager::WirelessSetting::FrequencyBand::A); + } else { + qWarning()<<"[KyWirelessConnectOperation] the band type undefined"<setBand(NetworkManager::WirelessSetting::FrequencyBand::Automatic); + } + + + NetworkManager::WirelessSecuritySetting::Ptr wirelessSecuritySetting + = connectionSettings->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + if (apPassword.isEmpty()) { + wirelessSecuritySetting->setInitialized(false); + } else { + wirelessSecuritySetting->setInitialized(true); + wirelessSecuritySetting->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk); + wirelessSecuritySetting->setPsk(apPassword); + } + + return connectionSettings; +} + +void KyWirelessConnectOperation::updateWirelessApSetting( + NetworkManager::Connection::Ptr apConnectPtr, + const QString apName, const QString apPassword, + const QString apDevice, const QString wirelessBand) + +{ + NetworkManager::ConnectionSettings::Ptr apConnectSettingPtr = apConnectPtr->settings(); + apConnectSettingPtr->setId(apName); + apConnectSettingPtr->setInterfaceName(apDevice); + apConnectSettingPtr->setAutoconnect(false); + + NetworkManager::WirelessSetting::Ptr wirelessSetting + = apConnectSettingPtr->setting(NetworkManager::Setting::Wireless).dynamicCast(); + wirelessSetting->setInitialized(true); + wirelessSetting->setSsid(apName.toUtf8()); + if (wirelessBand == WIFI_BAND_2_4GHZ) { + wirelessSetting->setBand(NetworkManager::WirelessSetting::FrequencyBand::Bg); + } else if (wirelessBand == WIFI_BAND_5GHZ) { + wirelessSetting->setBand(NetworkManager::WirelessSetting::FrequencyBand::A); + } else { + qWarning()<<"[KyWirelessConnectOperation] the band type undefined"<setBand(NetworkManager::WirelessSetting::FrequencyBand::Automatic); + } + + + NetworkManager::WirelessSecuritySetting::Ptr wirelessSecuritySetting + = apConnectSettingPtr->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + if (apPassword.isEmpty()) { + wirelessSecuritySetting->setInitialized(false); + } else { + wirelessSecuritySetting->setInitialized(true); + wirelessSecuritySetting->setKeyMgmt(NetworkManager::WirelessSecuritySetting::WpaPsk); + wirelessSecuritySetting->setPsk(apPassword); + } + + apConnectPtr->update(apConnectSettingPtr->toMap()); +} + +void KyWirelessConnectOperation::activeWirelessAp(const QString apUuid, const QString apName, + const QString apPassword, const QString apDevice, + const QString wirelessBand) + +{ + qDebug() << "activeWirelessAp]" << apUuid << apName << apPassword << apDevice << wirelessBand; + //1、检查连接是否存在 + NetworkManager::Connection::Ptr connectPtr = m_networkResourceInstance->getConnect(apUuid); + if (nullptr == connectPtr) { + NetworkManager::Device::Ptr devicePtr = m_networkResourceInstance->findDeviceInterface(apDevice); + if (devicePtr.isNull()) { + QString errorMsg ="Create hotspot faild. " + apDevice + " is not existed"; + qWarning()<< errorMsg; + emit addAndActivateConnectionError(errorMsg); + return; + } + + QString deviceIdentifier = devicePtr->uni(); + + NetworkManager::ConnectionSettings::Ptr apConnectSettingPtr = + createWirelessApSetting(apName, apPassword, apDevice, wirelessBand); + QString specificObject = ""; + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::addAndActivateConnection(apConnectSettingPtr->toMap(), deviceIdentifier, specificObject), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [&] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMsg = "Create hotspot faild. " + watcher->error().message(); + qWarning() << errorMsg; + emit addAndActivateConnectionError(errorMsg); + } + watcher->deleteLater(); + }); + } else { + updateWirelessApSetting(connectPtr, apName, apPassword, apDevice, wirelessBand); + QTimer::singleShot(500, this, [=](){ + activateApConnectionByUuid(apUuid, apDevice); + }); + } + + return; +} + +void KyWirelessConnectOperation::deactiveWirelessAp(const QString apName, const QString apUuid) +{ + deactivateConnection(apName, apUuid); + return; +} + +//private +NetworkManager::WirelessNetwork::Ptr KyWirelessConnectOperation::checkWifiNetExist(QString ssid, QString devName) +{ + for (auto const & net : m_networkResourceInstance->m_wifiNets) { + auto dev = m_networkResourceInstance->findDeviceUni(net->device()); + if (dev == nullptr) { + continue; + } + if (dev->type() != NetworkManager::Device::Wifi || dev->interfaceName() != devName) { + continue; + } + + NetworkManager::AccessPoint::Ptr accessPointPtr = net->referenceAccessPoint(); + QByteArray rawSsid = accessPointPtr->rawSsid(); + QString wifiSsid = getSsidFromByteArray(rawSsid); + + if (ssid == wifiSsid) { + return net; + } + } + return nullptr; +} + +KyKeyMgmt KyWirelessConnectOperation::getConnectKeyMgmt(const QString &uuid) +{ + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(uuid); + if (connectPtr.isNull()) { + return KyKeyMgmt::Unknown; + } + + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = connectPtr->settings()->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + + if(security_sett.isNull()) { + return KyKeyMgmt::Unknown; + } + return (KyKeyMgmt)security_sett->keyMgmt(); +} + +void KyWirelessConnectOperation::updateWirelessSecu(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyWirelessConnectSetting &connSettingInfo, bool bPwdChange) +{ + qDebug() << "updateWirelessSecu " << connSettingInfo.m_type; + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = connSettingPtr->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + + KyKeyMgmt type = connSettingInfo.m_type; + qDebug() << "set keyMgmt " << type; + if (type == WpaNone) { + security_sett->setInitialized(false); + return; + } else { + security_sett->setInitialized(true); + } + security_sett->setKeyMgmt((NetworkManager::WirelessSecuritySetting::KeyMgmt)type); + if (bPwdChange) { + security_sett->setPsk(connSettingInfo.m_psk); + NetworkManager::Setting::SecretFlags flag = NetworkManager::Setting::None; + security_sett->setPskFlags(flag); + } + return; +} + +void KyWirelessConnectOperation::setWirelessSecuWpaXEap(NetworkManager::ConnectionSettings::Ptr connSettingPtr) +{ + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = connSettingPtr->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + + security_sett->setInitialized(true); + security_sett->setKeyMgmt(NetworkManager::WirelessSecuritySetting::KeyMgmt::WpaEap); + return; +} + +void KyWirelessConnectOperation::setIpv4AndIpv6Setting(NetworkManager::ConnectionSettings::Ptr connSetting, const KyConnectSetting &connSettingInfo) +{ + NetworkManager::Ipv4Setting::Ptr ipv4Setting = connSetting->setting(NetworkManager::Setting::Ipv4).dynamicCast(); + ipv4SettingSet(ipv4Setting, connSettingInfo); + + NetworkManager::Ipv6Setting::Ptr ipv6Setting = connSetting->setting(NetworkManager::Setting::Ipv6).dynamicCast(); + ipv6SettingSet(ipv6Setting, connSettingInfo); +} + +void KyWirelessConnectOperation::activateApConnectionByUuid(const QString apUuid, const QString apDevice) +{ + QString connectPath = ""; + QString deviceIdentifier = ""; + QString connectName = ""; + QString specificObject = ""; + + qDebug()<<"it will activate hotspot connect"<getConnect(apUuid); + if (connectPtr.isNull()) + { + QString errorMessage = tr("Create hotspot faild.UUID is empty, its name") + apUuid; + qWarning() << errorMessage; + Q_EMIT activateConnectionError(errorMessage); + return; + } + connectPath = connectPtr->path(); + connectName = connectPtr->name(); + + auto dev = m_networkResourceInstance->findDeviceInterface(apDevice); + if (!dev.isNull()) { + deviceIdentifier = dev->uni(); + } + + if (deviceIdentifier.isEmpty()) { + QString errorMessage = tr("Create hotspot faild.Device Identifier is empty, its name") + apDevice; + qWarning() << errorMessage; + Q_EMIT activateConnectionError(errorMessage); + return ; + } + + QDBusPendingCallWatcher * watcher; + watcher = new QDBusPendingCallWatcher{NetworkManager::activateConnection(connectPath, deviceIdentifier, specificObject), this}; + connect(watcher, &QDBusPendingCallWatcher::finished, [this, connectName, apDevice] (QDBusPendingCallWatcher * watcher) { + if (watcher->isError() || !watcher->isValid()) { + QString errorMessage = tr("Create hotspot faild. ") + watcher->error().message(); + qWarning()<activateConnectionError(errorMessage); + } else { + qWarning()<<"active wired connect complete."; + } + + watcher->deleteLater(); + }); + + return ; +} + +bool KyWirelessConnectOperation::getEnterpiseEapMethod(const QString &uuid, KyEapMethodType &type) +{ + NetworkManager::Connection::Ptr connectPtr = + NetworkManager::findConnectionByUuid(uuid); + if (connectPtr.isNull()) { + qWarning() << "getEnterpiseEapMethod faild.Can't find uuid = " << uuid; + return false; + } + + KyKeyMgmt keyMgmt = getConnectKeyMgmt(uuid); + if (keyMgmt != WpaEap) { + qWarning() << "getEnterpiseEapMethod but not WpaEap.it's " << keyMgmt; + return false; + } + + NetworkManager::ConnectionSettings::Ptr connectionSettings = connectPtr->settings(); + + NetworkManager::Security8021xSetting::Ptr wifi_8021x_sett + = connectionSettings->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + + QList list = wifi_8021x_sett->eapMethods(); + + if (list.contains(NetworkManager::Security8021xSetting::EapMethod::EapMethodTls)) { + type = TLS; + } else if (list.contains(NetworkManager::Security8021xSetting::EapMethod::EapMethodPeap)) { + type = PEAP; + } else if (list.contains(NetworkManager::Security8021xSetting::EapMethod::EapMethodTtls)) { + type = TTLS; + } + + return true; +} diff --git a/src/backend/dbus-interface/kywirelessconnectoperation.h b/src/backend/dbus-interface/kywirelessconnectoperation.h new file mode 100644 index 00000000..89618905 --- /dev/null +++ b/src/backend/dbus-interface/kywirelessconnectoperation.h @@ -0,0 +1,140 @@ +#ifndef KYWIRELESSCONNECTOPERATION_H +#define KYWIRELESSCONNECTOPERATION_H + +#include +#include +#include +#include "kyenterpricesettinginfo.h" +#include "kylinconnectoperation.h" +#include "kyenterpricesettinginfo.h" + +const QByteArray GSETTINGS_SCHEMA = "org.ukui.kylin-nm.switch"; +const QString WIRELESS_SWITCH = "wirelessswitch"; + +const QString WIFI_BAND_2_4GHZ = "2.4Ghz"; +const QString WIFI_BAND_5GHZ = "5Ghz"; + +enum KySecuType { + NONE = 0, + WPA_AND_WPA2_PERSONAL, + WPA_AND_WPA2_ENTERPRISE, + WPA3_PERSONAL, +}; + +enum KyKeyMgmt { + Unknown = -1, + Wep, Ieee8021x, + WpaNone, + WpaPsk, + WpaEap, + SAE +}; + +class KyWirelessConnectSetting : public KyConnectSetting +{ +// Q_OBJECT + +public: + QString m_ssid; + bool isAutoConnect; + QString m_psk; + NetworkManager::Setting::SecretFlags m_secretFlag; + KyKeyMgmt m_type; + //only if m_type == WpaEap + KyEapMethodType m_eapMethodType; +}; + + +class KyWirelessConnectOperation : public KyConnectOperation +{ + Q_OBJECT +public: + explicit KyWirelessConnectOperation(QObject *parent = nullptr); + ~KyWirelessConnectOperation(); + + //无线开关 + void setWirelessEnabled(bool enabled); + bool getWirelessEnabled(); + + //获取密码保存策略 + bool getConnSecretFlags(QString &connUuid, NetworkManager::Setting::SecretFlags &); + + //获取KeyMgmt + KyKeyMgmt getConnectKeyMgmt(const QString &uuid); + + //获取企业网类型 + bool getEnterpiseEapMethod(const QString &uuid, KyEapMethodType &type); + + //激活连接 + void activeWirelessConnect(QString , QString); + //断开连接 + void deActivateWirelessConnection(const QString activeConnectName, const QString &activeConnectUuid); + //新增普通连接 + void addConnect(const KyWirelessConnectSetting &connSettingInfo); + //新增TLS连接 + void addTlsConnect(const KyWirelessConnectSetting &connSettingInfo, const KyEapMethodTlsInfo &tlsinfo); + //新增PEAP连接 + void addPeapConnect(const KyWirelessConnectSetting &connSettingInfo, const KyEapMethodPeapInfo &peapInfo); + //新增TTLS连接 + void addTtlsConnect(const KyWirelessConnectSetting &connSettingInfo, const KyEapMethodTtlsInfo &ttlsInfo); + //新增连接并激活(普通wifi) + void addAndActiveWirelessConnect(QString & devIface,KyWirelessConnectSetting &connSettingInfo,bool isHidden); + + //新增连接并激活(企业wifi) + void addAndActiveWirelessEnterPriseTlsConnect(KyEapMethodTlsInfo &info, KyWirelessConnectSetting &connSettingInfo, + QString & devIface, bool isHidden); + void addAndActiveWirelessEnterPrisePeapConnect(KyEapMethodPeapInfo &info, KyWirelessConnectSetting &connSettingInfo, + QString & devIface, bool isHidden); + void addAndActiveWirelessEnterPriseTtlsConnect(KyEapMethodTtlsInfo &info, KyWirelessConnectSetting &connSettingInfo, + QString & devIface, bool isHidden); + //属性页 page1 AutoConnect + void setWirelessAutoConnect(const QString &uuid, bool bAutoConnect); + //属性页 page2 page3 ipv6 + void updateIpv4AndIpv6SettingInfo(const QString &uuid, const KyConnectSetting &connectSettingsInfo); + //属性页 page4 wifi Security + //连接修改(安全改为个人/None) + void updateWirelessPersonalConnect(const QString &uuid, const KyWirelessConnectSetting &connSettingInfo, bool bPwdChange); + //连接修改(安全改为改为企业) + void updateWirelessEnterPriseTlsConnect(const QString &uuid, const KyEapMethodTlsInfo &tlsinfo); + void updateWirelessEnterPrisePeapConnect(const QString &uuid, const KyEapMethodPeapInfo &peapInfo); + void updateWirelessEnterPriseTtlsConnect(const QString &uuid, const KyEapMethodTtlsInfo &ttlsInfo); + //忘记 + void deleteWirelessConnect(const QString &connectUuid); + //获取密码 + QString getPsk(const QString &connectUuid); + QString getPrivateKeyPassword(const QString &connectUuid); + QString get8021xPassword(const QString &connectUuid); + + //申请扫描 + void requestWirelessScan(); + + void activeWirelessAp(const QString apUuid, const QString apName, + const QString apPassword, const QString apDevice, + const QString wirelessBand); + void deactiveWirelessAp(const QString apName, const QString apUuid); + +private: + NetworkManager::ConnectionSettings::Ptr createWirelessApSetting(const QString apSsid, + const QString apPassword, + const QString apDevice, + const QString wirelessBand); + void updateWirelessApSetting(NetworkManager::Connection::Ptr apConnectPtr, + const QString apName, + const QString apPassword, + const QString apDevice, + const QString wirelessBand); + +signals: + void wifiEnabledChanged(bool); + void enabledWirelessNetwork(bool enabled); + void addAndActivateConnectionError(QString errorMessage); + +private: + NetworkManager::WirelessNetwork::Ptr checkWifiNetExist(QString ssid, QString devName); + void updateWirelessSecu(NetworkManager::ConnectionSettings::Ptr connSettingPtr, const KyWirelessConnectSetting &connSettingInfo, bool bPwdChange = false); + void setIpv4AndIpv6Setting(NetworkManager::ConnectionSettings::Ptr connSetting, const KyConnectSetting &connSettingInfo); + void setWirelessSecuWpaXEap(NetworkManager::ConnectionSettings::Ptr connSettingPtr); + void activateApConnectionByUuid(const QString apUuid, const QString apDevice); +}; + +#endif // KYWIRELESSCONNECTOPERATION_H diff --git a/src/backend/dbus-interface/kywirelessnetitem.cpp b/src/backend/dbus-interface/kywirelessnetitem.cpp new file mode 100644 index 00000000..a1d4606d --- /dev/null +++ b/src/backend/dbus-interface/kywirelessnetitem.cpp @@ -0,0 +1,114 @@ +#include "kywirelessnetitem.h" +#include +#include "kylinutil.h" + +const QString ENTERPRICE_TYPE = "802.1X"; +const QString WPA1_AND_WPA2 = "WPA"; +const QString WPA3 = "WPA3"; + +QString enumToQstring(NetworkManager::AccessPoint::Capabilities cap, NetworkManager::AccessPoint::WpaFlags wpa_flags,NetworkManager::AccessPoint::WpaFlags rsn_flags) +{ + QString out; + if ( (cap & NM_802_11_AP_FLAGS_PRIVACY) + && (wpa_flags == NM_802_11_AP_SEC_NONE) + && (rsn_flags == NM_802_11_AP_SEC_NONE)) { + out += "WEP "; + } + if (wpa_flags != NM_802_11_AP_SEC_NONE) { + out += "WPA1 "; + } + if ((rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + out += "WPA2 "; + } + if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE) { + out += "WPA3 "; + } + if ( (wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + out += "802.1X "; + } + return out; +} + +KyWirelessNetItem::KyWirelessNetItem(NetworkManager::WirelessNetwork::Ptr net) +{ + m_networkResourceInstance = KyNetworkResourceManager::getInstance(); + + m_bssid = ""; + m_connectUuid = ""; + m_isConfigured = false; + m_connName = ""; + m_connDbusPath = ""; + m_secuType = ""; + m_kySecuType = NONE; + m_device = ""; + m_channel = 0; + + init(net); +} + + +KyWirelessNetItem::~KyWirelessNetItem() +{ + m_networkResourceInstance = nullptr; +} + + +void KyWirelessNetItem::init(NetworkManager::WirelessNetwork::Ptr net) +{ + // m_NetSsid = net->ssid(); + + NetworkManager::AccessPoint::Ptr accessPointPtr = net->referenceAccessPoint(); + QByteArray rawSsid = accessPointPtr->rawSsid(); + m_NetSsid = getSsidFromByteArray(rawSsid); + + m_signalStrength = net->signalStrength(); + m_frequency = net->referenceAccessPoint()->frequency(); + m_channel = NetworkManager::findChannel(m_frequency); + NetworkManager::AccessPoint::Capabilities cap = net->referenceAccessPoint()->capabilities(); + NetworkManager::AccessPoint::WpaFlags wpaFlag = net->referenceAccessPoint()->wpaFlags(); + NetworkManager::AccessPoint::WpaFlags rsnFlag = net->referenceAccessPoint()->rsnFlags(); + m_secuType = enumToQstring(cap, wpaFlag, rsnFlag); + if (m_secuType.indexOf(ENTERPRICE_TYPE) >= 0) { + m_kySecuType = WPA_AND_WPA2_ENTERPRISE; + } else if (m_secuType.indexOf(WPA3) >= 0) { + m_kySecuType = WPA3_PERSONAL; + } else if ( m_secuType.indexOf(WPA1_AND_WPA2) >= 0) { + m_kySecuType = WPA_AND_WPA2_PERSONAL; + } + m_bssid = net->referenceAccessPoint()->hardwareAddress(); + m_device = net->device(); + initInfoBySsid(); +} + +void KyWirelessNetItem::initInfoBySsid() +{ + for (auto const & conn : m_networkResourceInstance->m_connections) { + NetworkManager::ConnectionSettings::Ptr settings = conn->settings(); + if (settings->connectionType() != NetworkManager::ConnectionSettings::Wireless) { + continue; + } + + NetworkManager::WirelessSetting::Ptr wifi_sett + = settings->setting(NetworkManager::Setting::Wireless).dynamicCast(); + QString devName = m_networkResourceInstance->findDeviceUni(m_device)->interfaceName(); + QByteArray rawSsid = wifi_sett->ssid(); + QString wifiSsid = getSsidFromByteArray(rawSsid); + if (wifiSsid == m_NetSsid + && (settings->interfaceName().compare(devName) == 0 || settings->interfaceName().isEmpty())) { + m_connectUuid = settings->uuid(); + m_connName = conn->name(); + m_connDbusPath = conn->path(); + m_isConfigured = true; + /* + * 如果有激活的链接,则取激活的链接,没有则取最后一个,因为一个热点可以创建多个链接 + */ + if (nullptr != m_networkResourceInstance->getActiveConnect(m_connectUuid)) { + break; + } + } + } + + return; +} diff --git a/src/backend/dbus-interface/kywirelessnetitem.h b/src/backend/dbus-interface/kywirelessnetitem.h new file mode 100644 index 00000000..6f7b105b --- /dev/null +++ b/src/backend/dbus-interface/kywirelessnetitem.h @@ -0,0 +1,45 @@ +#ifndef KYWIRELESSNETITEM_H +#define KYWIRELESSNETITEM_H + +#include + +#include "kylinnetworkresourcemanager.h" +#include "kywirelessconnectoperation.h" + +QString enumToQstring(NetworkManager::AccessPoint::Capabilities, NetworkManager::AccessPoint::WpaFlags, NetworkManager::AccessPoint::WpaFlags); + +class KyWirelessNetItem +{ +public: + KyWirelessNetItem(NetworkManager::WirelessNetwork::Ptr net); + KyWirelessNetItem() {;} + ~KyWirelessNetItem(); + +private: + void init(NetworkManager::WirelessNetwork::Ptr net); + void initInfoBySsid(); + +public: + QString m_NetSsid; + QString m_connectUuid; + QString m_bssid; + int m_signalStrength; + uint m_frequency; + QString m_secuType; + KySecuType m_kySecuType; + + //only for m_isConfiged = true + bool m_isConfigured; + QString m_connName; + QString m_connDbusPath; + uint m_channel; + + + +private: + KyNetworkResourceManager *m_networkResourceInstance = nullptr; + QString m_device; + +}; + +#endif // KYWIRELESSNETITEM_H diff --git a/src/backend/dbus-interface/kywirelessnetresource.cpp b/src/backend/dbus-interface/kywirelessnetresource.cpp new file mode 100644 index 00000000..386f863b --- /dev/null +++ b/src/backend/dbus-interface/kywirelessnetresource.cpp @@ -0,0 +1,732 @@ +#include "kywirelessnetresource.h" +#include "kylinutil.h" + +#define LOG_FLAG "[KyWirelessNetResource]" + +static bool subWifiListSort(const KyWirelessNetItem info1, const KyWirelessNetItem info2) +{ + if (info1.m_isConfigured == info2.m_isConfigured) { + if (info1.m_signalStrength != info2.m_signalStrength) { + return info1.m_signalStrength > info2.m_signalStrength; + } else { + if (QString::compare(info1.m_NetSsid, info2.m_NetSsid, Qt::CaseInsensitive) > 0) { + return false; + } else { + return true; + } + } + } + return info1.m_isConfigured; +} + +static void wifiListSort(QList &list) +{ + qSort(list.begin(), list.end(), subWifiListSort); +} + +KyWirelessNetResource::KyWirelessNetResource(QObject *parent) + : QObject(parent) +{ + qDebug()<< LOG_FLAG <<"KyWirelessNetResource"; + + qRegisterMetaType("KyWirelessNetItem&"); + + m_networkResourceInstance = KyNetworkResourceManager::getInstance(); + m_operation = new KyWirelessConnectOperation(this); + m_networkDevice = new KyNetworkDeviceResourse(this); + + kyWirelessNetItemListInit(); + + //TODO:connect device signal + connect(m_networkResourceInstance, &KyNetworkResourceManager::wifiNetworkAdded, + this, &KyWirelessNetResource::onWifiNetworkAdded, Qt::ConnectionType::DirectConnection); + connect(m_networkResourceInstance, &KyNetworkResourceManager::wifiNetworkRemoved, + this, &KyWirelessNetResource::onWifiNetworkRemoved, Qt::ConnectionType::DirectConnection); + connect(m_networkResourceInstance, &KyNetworkResourceManager::wifiNetworkPropertyChange, + this, &KyWirelessNetResource::onWifiNetworkPropertyChange, Qt::ConnectionType::DirectConnection); + connect(m_networkResourceInstance, &KyNetworkResourceManager::wifiNetworkDeviceDisappear, + this, &KyWirelessNetResource::onWifiNetworkDeviceDisappear, Qt::ConnectionType::DirectConnection); + + connect(m_networkResourceInstance, &KyNetworkResourceManager::connectionAdd, + this, &KyWirelessNetResource::onConnectionAdd); + connect(m_networkResourceInstance, &KyNetworkResourceManager::connectionRemove, + this, &KyWirelessNetResource::onConnectionRemove); + connect(m_networkResourceInstance, &KyNetworkResourceManager::connectionUpdate, + this, &KyWirelessNetResource::onConnectionUpdate); + + connect(m_networkDevice, &KyNetworkDeviceResourse::deviceAdd, this, &KyWirelessNetResource::onDeviceAdd); + connect(m_networkDevice, &KyNetworkDeviceResourse::deviceRemove, this, &KyWirelessNetResource::onDeviceRemove); + connect(m_networkDevice, &KyNetworkDeviceResourse::deviceNameUpdate, this, &KyWirelessNetResource::onDeviceNameUpdate); +} + +KyWirelessNetResource::~KyWirelessNetResource() +{ + m_networkResourceInstance = nullptr; +} + +bool KyWirelessNetResource::getAllDeviceWifiNetwork(QMap> &map) +{ +// onWifiNetworkDeviceDisappear(); + if (m_WifiNetworkList.isEmpty()) { + return false; + } else { + QMap >::iterator iter = m_WifiNetworkList.begin(); + while (iter != m_WifiNetworkList.end()) { + wifiListSort(m_WifiNetworkList[iter.key()]); + iter++; + } + map = m_WifiNetworkList; + return true; + } +} + + +bool KyWirelessNetResource::getDeviceWifiNetwork(QString devIfaceName, QList &wirelessNetResource) +{ + if (!m_WifiNetworkList.contains(devIfaceName)) { + return false; + } else { + wifiListSort(m_WifiNetworkList[devIfaceName]); + wirelessNetResource = m_WifiNetworkList[devIfaceName]; + return true; + } +} + +bool KyWirelessNetResource::getWifiNetwork(const QString &devIfaceName, + const QString &ssid, + KyWirelessNetItem &wirelessNetResource) +{ + if (!m_WifiNetworkList.contains(devIfaceName)) { + qDebug()<< LOG_FLAG << "getWifiNetwork fail, not contain " << devIfaceName; + return false; + } else { + for (int index = 0; index < m_WifiNetworkList[devIfaceName].size(); index++){ + if (m_WifiNetworkList[devIfaceName].at(index).m_NetSsid == ssid) { + wirelessNetResource = m_WifiNetworkList[devIfaceName].at(index); + qDebug()<< LOG_FLAG << "getWifiNetwork success"; + return true; + } + } + } + + qDebug()<< LOG_FLAG << "getWifiNetwork fail, not contain " << ssid; + + return false; +} + +void KyWirelessNetResource::getWirelessActiveConnection(NetworkManager::ActiveConnection::State state, QMap &map) +{ + int index = 0; + map.clear(); + NetworkManager::ActiveConnection::List activeConnectionList; + + activeConnectionList.clear(); + activeConnectionList = m_networkResourceInstance->getActiveConnectList(); + if (activeConnectionList.isEmpty()) { + return; + } + + NetworkManager::ActiveConnection::Ptr activeConnectionPtr = nullptr; + for (; index < activeConnectionList.size(); index++) { + activeConnectionPtr = activeConnectionList.at(index); + if (activeConnectionPtr.isNull()) { + continue; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Wireless != activeConnectionPtr->type()) { + continue; + } + + if (state != activeConnectionPtr->state()) { + continue; + } + + qDebug()<< LOG_FLAG << "getWirelessActiveConnection " << activeConnectionPtr->uuid(); + QString ssid; + QString ifaceName = getDeviceIFace(activeConnectionPtr, ssid); + if(ifaceName.isEmpty() || ssid.isNull()) { + continue; + } + + if (map.contains(ifaceName)) { + map[ifaceName].append(activeConnectionPtr->uuid()); + } else { + QStringList list; + list.append(activeConnectionPtr->uuid()); + map.insert(ifaceName,list); + } + } + + return; +} + +QString KyWirelessNetResource::getActiveConnectSsidByDevice(QString deviceName) +{ + QString ssid = ""; + + NetworkManager::ActiveConnection::List activeConnectionList; + activeConnectionList.clear(); + activeConnectionList = m_networkResourceInstance->getActiveConnectList(); + if (activeConnectionList.isEmpty()) { + return ssid; + } + + NetworkManager::ActiveConnection::Ptr activeConnectionPtr = nullptr; + for (int index = 0; index < activeConnectionList.size(); index++) { + activeConnectionPtr = activeConnectionList.at(index); + if (activeConnectionPtr.isNull()) { + continue; + } + + if (NetworkManager::ConnectionSettings::ConnectionType::Wireless != activeConnectionPtr->type()) { + continue; + } + + if (NetworkManager::ActiveConnection::State::Activated != activeConnectionPtr->state()) { + continue; + } + + QStringList interfaces = activeConnectionPtr->devices(); + if(interfaces.isEmpty()) { + qWarning()<< LOG_FLAG <<"get active device failed."; + continue; + } + + QString ifaceUni = interfaces.at(0); + NetworkManager::Device:: Ptr devicePtr = + m_networkResourceInstance->findDeviceUni(ifaceUni); + if (deviceName != devicePtr->interfaceName()) { + continue; + } + + NetworkManager::Connection::Ptr connectPtr = activeConnectionPtr->connection(); + NetworkManager::ConnectionSettings::Ptr settingPtr = connectPtr->settings(); + NetworkManager::WirelessSetting::Ptr wirelessSettingPtr = + settingPtr->setting(NetworkManager::Setting::Wireless).dynamicCast(); + + QByteArray rawSsid = wirelessSettingPtr->ssid(); + ssid = getSsidFromByteArray(rawSsid); + + break; + } + + return ssid; +} + +bool KyWirelessNetResource::getActiveWirelessNetItem(QString deviceName, KyWirelessNetItem &wirelessNetItem) +{ + if (!m_WifiNetworkList.contains(deviceName)) { + qDebug() << "getWifiNetwork fail,not contain " << deviceName; + return false; + } + + QString ssid = getActiveConnectSsidByDevice(deviceName); + if (ssid.isEmpty()) { + return false; + } + + for (int index = 0; index < m_WifiNetworkList[deviceName].size(); index ++) { + if (m_WifiNetworkList[deviceName].at(index).m_NetSsid == ssid) { + wirelessNetItem = m_WifiNetworkList[deviceName].at(index); + qDebug()<< LOG_FLAG << "getWifiNetwork success"; + return true; + } + } + + return false; +} + +QString KyWirelessNetResource::getDeviceIFace(NetworkManager::ActiveConnection::Ptr actConn, + QString &wirelessNetResourcessid) +{ + if (actConn.isNull()) { + return ""; + } + + NetworkManager::Connection::Ptr conn = actConn->connection(); + if (conn.isNull()) { + return ""; + } + + NetworkManager::ConnectionSettings::Ptr sett = conn->settings(); + if (sett.isNull()) { + return ""; + } + + NetworkManager::WirelessSetting::Ptr wireless_sett = sett->setting(NetworkManager::Setting::Wireless).dynamicCast(); + if (wireless_sett.isNull()) { + return ""; + } + + QByteArray rawSsid = wireless_sett->ssid(); + wirelessNetResourcessid = getSsidFromByteArray(rawSsid); + + QStringList interfaces = actConn->devices(); + if (interfaces.isEmpty()) { + return ""; + } + + QString ifaceUni = interfaces.at(0); + NetworkManager::Device:: Ptr devicePtr = + m_networkResourceInstance->findDeviceUni(ifaceUni); + + return devicePtr->interfaceName(); +} + +void KyWirelessNetResource::getSsidByUuid(const QString uuid, QString &ssid) +{ + ssid.clear(); + NetworkManager::Connection::Ptr connectPtr = m_networkResourceInstance->getConnect(uuid); + if (connectPtr.isNull()) { + return; + } + + NetworkManager::WirelessSetting::Ptr wireless_sett + = connectPtr->settings()->setting(NetworkManager::Setting::Wireless).dynamicCast(); + if (wireless_sett.isNull()) { + qDebug()<< LOG_FLAG << "don't have WirelessSetting connection"; + return; + } + + QByteArray rawSsid = wireless_sett->ssid(); + ssid = getSsidFromByteArray(rawSsid); + + qDebug()<< LOG_FLAG << "getSsidByUuid success " << ssid; + + return; +} + +void KyWirelessNetResource::getDeviceByUuid(const QString uuid, QString &deviceName) +{ + deviceName.clear(); + + NetworkManager::ActiveConnection::Ptr activeConnectionPtr = m_networkResourceInstance->getActiveConnect(uuid); + if (!activeConnectionPtr.isNull()) { + QStringList interfaces = activeConnectionPtr->devices(); + if (interfaces.size() > 0) { + QString ifaceUni = interfaces.at(0); + NetworkManager::Device:: Ptr devicePtr = + m_networkResourceInstance->findDeviceUni(ifaceUni); + deviceName = devicePtr->interfaceName(); + return; + } else { + qDebug() << LOG_FLAG << "get device of active connection failed."; + } + } + + NetworkManager::Connection::Ptr connectPtr = m_networkResourceInstance->getConnect(uuid); + if (connectPtr.isNull()) { + return; + } + + deviceName = connectPtr->settings()->interfaceName(); + + return; +} + +void KyWirelessNetResource::kyWirelessNetItemListInit() +{ + qDebug()<< LOG_FLAG << "wireless net size:" << m_networkResourceInstance->m_wifiNets.size(); + for (auto const & net : m_networkResourceInstance->m_wifiNets) { + QString devIface = getDeviceIFace(net); + if (devIface.isEmpty()) { + continue; + } + + KyWirelessNetItem item(net); + if (!m_WifiNetworkList.contains(devIface)){ + QList list; + list.append(item); + m_WifiNetworkList.insert(devIface,list); + } else { + m_WifiNetworkList[devIface].append(item); + } + } + + return; +} + +QString KyWirelessNetResource::getDeviceIFace(NetworkManager::WirelessNetwork::Ptr net) +{ + if (net.isNull()) { + return ""; + } + + QString devUni = net->device(); + NetworkManager::Device::Ptr dev = m_networkResourceInstance->findDeviceUni(devUni); + if (dev.isNull()) { + qDebug()<< LOG_FLAG << "KyWirelessNetResource: can't find " << net->ssid() << " find in device list"; + return ""; + } + + return dev->interfaceName(); +} + +void KyWirelessNetResource::onWifiNetworkAdded(QString devIfaceName, QString ssid) +{ + + NetworkManager::Device::Ptr dev = m_networkResourceInstance->findDeviceInterface(devIfaceName); + if (dev.isNull()) { + return; + } + + NetworkManager::WirelessDevice* w_dev = qobject_cast(dev.data()); + NetworkManager::WirelessNetwork::Ptr wifi = w_dev->findNetwork(ssid); + + if (wifi.isNull()) { + return; + } + + KyWirelessNetItem item(wifi); + + if (m_WifiNetworkList.contains(devIfaceName)) { + m_WifiNetworkList[devIfaceName].append(item); + } else { + QList list; + list.append(item); + m_WifiNetworkList.insert(devIfaceName,list); + } + + emit wifiNetworkAdd(devIfaceName, item); +} + +void KyWirelessNetResource::onWifiNetworkRemoved(QString devIfaceName, QString ssid) +{ + if (m_WifiNetworkList.contains(devIfaceName)) { + int index = 0; + for ( ; index < m_WifiNetworkList.value(devIfaceName).size(); index++) { + if ( m_WifiNetworkList[devIfaceName].at(index).m_NetSsid == ssid) { + m_WifiNetworkList[devIfaceName].removeAt(index); + } + } + //remove后为空则删除 + if (m_WifiNetworkList.value(devIfaceName).isEmpty()) { + m_WifiNetworkList.remove(devIfaceName); + } + emit wifiNetworkRemove(devIfaceName,ssid); + } +} + +void KyWirelessNetResource::onWifiNetworkPropertyChange(NetworkManager::WirelessNetwork * net) +{ + if (nullptr == net) { + return; + } + + NetworkManager::AccessPoint::Ptr accessPointPtr = net->referenceAccessPoint(); + QByteArray rawSsid = accessPointPtr->rawSsid(); + QString wifiSsid = getSsidFromByteArray(rawSsid); + + if (net->device().isEmpty()) { + return; + } + + QString devIface = m_networkResourceInstance->findDeviceUni(net->device())->interfaceName(); + if (m_WifiNetworkList.contains(devIface)) { + QList::iterator iter = m_WifiNetworkList[devIface].begin(); + while (iter != m_WifiNetworkList[devIface].end()) { + if (iter->m_NetSsid == wifiSsid) { +// qDebug()<< LOG_FLAG <<"recive properity changed signal, sender is" << iter->m_NetSsid; + if (iter->m_signalStrength != net->signalStrength()) { + iter->m_signalStrength = net->signalStrength(); + emit signalStrengthChange(devIface, wifiSsid, iter->m_signalStrength); + } + + if (iter->m_bssid != accessPointPtr->hardwareAddress()) { + iter->m_bssid = accessPointPtr->hardwareAddress(); + emit bssidChange(devIface, wifiSsid, iter->m_bssid); + } + + QString secuType = enumToQstring(accessPointPtr->capabilities(), + accessPointPtr->wpaFlags(), + accessPointPtr->rsnFlags()); + if (iter->m_secuType != secuType) { + iter->m_secuType = secuType; + emit secuTypeChange(devIface, wifiSsid, secuType); + } + + break; + } + iter++; + } + } +} + +void KyWirelessNetResource::onWifiNetworkDeviceDisappear() +{ + m_WifiNetworkList.clear(); + kyWirelessNetItemListInit(); +} + +bool KyWirelessNetResource::getEnterPriseInfoTls(QString &uuid, KyEapMethodTlsInfo &info) +{ + NetworkManager::Connection::Ptr conn = m_networkResourceInstance->getConnect(uuid); + if (conn.isNull()) { + qDebug()<< LOG_FLAG << "modifyEnterPriseInfoTls connection missing"; + return false; + } + + NetworkManager::WirelessSecuritySetting::Ptr security_sett = + conn->settings()->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + if (security_sett.isNull()) { + qDebug()<< LOG_FLAG << "don't have WirelessSecurity connection"; + return false; + } + if (security_sett->keyMgmt() != NetworkManager::WirelessSecuritySetting::WpaEap) { + return false; + } + NetworkManager::Security8021xSetting::Ptr setting = + conn->settings()->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + if (setting.isNull()) { + qDebug()<< LOG_FLAG << "don't have Security8021x connection"; + return false; + } + + + info.identity = setting->identity(); + info.domain = setting->domainSuffixMatch(); + info.caCertPath = setting->caPath(); + if (info.caCertPath.left(7) == "file://") { + info.caCertPath = info.caCertPath.mid(7); + } + + info.clientCertPath = setting->clientCertificate(); + if (info.clientCertPath.left(7) == "file://") { + info.clientCertPath = info.clientCertPath.mid(7); + } + + info.clientPrivateKey = QString(setting->privateKey()); + if (info.clientPrivateKey.left(7) == "file://") { + info.clientPrivateKey = info.clientPrivateKey.mid(7); + } + + info.m_privateKeyPWDFlag = setting->privateKeyPasswordFlags(); + if (!info.m_privateKeyPWDFlag) { + info.clientPrivateKeyPWD = m_operation->getPrivateKeyPassword(conn->uuid()); + } + + return true; +} + +bool KyWirelessNetResource::getEnterPriseInfoPeap(QString &uuid, KyEapMethodPeapInfo &info) +{ + NetworkManager::Connection::Ptr conn = m_networkResourceInstance->getConnect(uuid); + if (conn.isNull()) { + qDebug()<< LOG_FLAG << "getEnterPriseInfoPeap connection missing"; + return false; + } + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = conn->settings()->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + if (security_sett.isNull()) { + qDebug()<< LOG_FLAG << "don't have WirelessSecurity connection"; + return false; + } + + if (security_sett->keyMgmt() != NetworkManager::WirelessSecuritySetting::WpaEap) { + qDebug()<< LOG_FLAG << "keyMgmt not WpaEap " << security_sett->keyMgmt(); + return false; + } + + NetworkManager::Security8021xSetting::Ptr setting = + conn->settings()->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + if (setting.isNull() || !setting->eapMethods().contains(NetworkManager::Security8021xSetting::EapMethod::EapMethodPeap)) { + qDebug()<< LOG_FLAG << "don't have Security8021x connection"; + return false; + } + + info.phase2AuthMethod = (KyNoEapMethodAuth)setting->phase2AuthMethod(); + info.userName = setting->identity(); + info.m_passwdFlag = setting->passwordFlags(); + if (!info.m_passwdFlag) { + info.userPWD = m_operation->get8021xPassword(conn->uuid()); + } + + return true; +} + +bool KyWirelessNetResource::getEnterPriseInfoTtls(QString &uuid, KyEapMethodTtlsInfo &info) +{ + NetworkManager::Connection::Ptr conn = m_networkResourceInstance->getConnect(uuid); + if (conn.isNull()) { + qDebug()<< LOG_FLAG << "modifyEnterPriseInfoTtls connection missing"; + return false; + } + + NetworkManager::WirelessSecuritySetting::Ptr security_sett + = conn->settings()->setting(NetworkManager::Setting::WirelessSecurity).dynamicCast(); + if (security_sett.isNull()) { + qDebug()<< LOG_FLAG << "don't have WirelessSecurity connection"; + return false; + } + if (security_sett->keyMgmt() != NetworkManager::WirelessSecuritySetting::WpaEap) { + qDebug()<< LOG_FLAG << "not wpaeap"<keyMgmt(); + return false; + } + + NetworkManager::Security8021xSetting::Ptr setting = conn->settings()->setting(NetworkManager::Setting::Security8021x).dynamicCast(); + if (setting.isNull() || !setting->eapMethods().contains(NetworkManager::Security8021xSetting::EapMethod::EapMethodTtls)) { + qDebug()<< LOG_FLAG << "don't have Security8021x connection"; + return false; + } + + info.authEapMethod = (KyEapMethodAuth)setting->phase2AuthEapMethod(); + info.authNoEapMethod = (KyNoEapMethodAuth)setting->phase2AuthMethod(); + + info.authType = KyTtlsAuthMethod::AUTH_EAP; + if (info.authEapMethod != KyAuthEapMethodUnknown) { + info.authType = KyTtlsAuthMethod::AUTH_EAP; + } else { + info.authType = KyTtlsAuthMethod::AUTH_NO_EAP; + } + + info.userName = setting->identity(); + info.m_passwdFlag = setting->passwordFlags(); + if (!info.m_passwdFlag) { + info.userPWD = m_operation->get8021xPassword(conn->uuid()); + } + + return true; +} + +void KyWirelessNetResource::onConnectionAdd(QString uuid) +{ + qDebug() << LOG_FLAG << "onConnectionAdd " << uuid; + NetworkManager::Connection::Ptr conn = m_networkResourceInstance->getConnect(uuid); + if (conn.isNull()) { + qDebug()<< LOG_FLAG << "onConnectionAdd can not find connection" << uuid; + return; + } + + NetworkManager::ConnectionSettings::Ptr sett = conn->settings(); + if (sett->connectionType() != NetworkManager::ConnectionSettings::ConnectionType::Wireless) { + qDebug()<< LOG_FLAG << uuid << " is not wireless connection"; + return; + } + NetworkManager::WirelessSetting::Ptr wireless_sett = + sett->setting(NetworkManager::Setting::Wireless).dynamicCast(); + + QByteArray rawSsid = wireless_sett->ssid(); + QString wifiSsid = getSsidFromByteArray(rawSsid); + QMap map; + map.clear(); + QMap >::iterator iter = m_WifiNetworkList.begin(); + while (iter != m_WifiNetworkList.end()) { + for(int i = 0; i < iter.value().size(); i++) { + if (iter.value().at(i).m_NetSsid == wifiSsid + && (sett->interfaceName() == iter.key() || sett->interfaceName().isEmpty())) { + QString devIfaceName; + QString ssid; + m_WifiNetworkList[iter.key()][i].m_isConfigured = true; + m_WifiNetworkList[iter.key()][i].m_connName = conn->name(); + m_WifiNetworkList[iter.key()][i].m_connectUuid = conn->uuid(); + m_WifiNetworkList[iter.key()][i].m_connDbusPath = conn->path(); + m_WifiNetworkList[iter.key()][i].m_channel = wireless_sett->channel(); + + devIfaceName = iter.key(); + ssid = iter.value().at(i).m_NetSsid; + map.insert(devIfaceName, ssid); + + break; + } + } + iter++; + } + + if (!map.isEmpty()) { + for(auto var = map.cbegin(); var != map.cend(); var++) { + QString devIfaceName = var.key(); + QString ssid = var.value(); + emit connectionAdd(devIfaceName, ssid); + } + } +} + +void KyWirelessNetResource::onConnectionRemove(QString path) +{ + qDebug()<< LOG_FLAG << "onConnectionRemove remove " << path; + QMap map; + map.clear(); + + QMap >::iterator iter = m_WifiNetworkList.begin(); + while (iter != m_WifiNetworkList.end()) { + qDebug()<< LOG_FLAG <<"wifi network list key:" << iter.key(); + for(int i = 0; i < iter.value().size(); i++) { + qDebug() << LOG_FLAG << "connection path" << iter.value().at(i).m_connDbusPath; + if (iter.value().at(i).m_connDbusPath == path) { + QString devIfaceName; + QString ssid; + m_WifiNetworkList[iter.key()][i].m_isConfigured = false; + m_WifiNetworkList[iter.key()][i].m_connName = ""; + m_WifiNetworkList[iter.key()][i].m_connectUuid = ""; + m_WifiNetworkList[iter.key()][i].m_connDbusPath = ""; + m_WifiNetworkList[iter.key()][i].m_channel = 0; + + devIfaceName = iter.key(); + ssid = iter.value().at(i).m_NetSsid; + map.insert(devIfaceName, ssid); + break; + } + } + iter++; + } + + if (!map.isEmpty()) { + for(auto var = map.cbegin(); var != map.cend(); var++) { + QString devIfaceName = var.key(); + QString ssid = var.value(); + emit connectionRemove(devIfaceName, ssid); + } + } + +} + + +void KyWirelessNetResource::onConnectionUpdate(QString uuid) +{ + qDebug()<< LOG_FLAG << "onConnectionUpdate " << uuid; + NetworkManager::Connection::Ptr conn = m_networkResourceInstance->getConnect(uuid); + if (conn.isNull()) { + qDebug()<< LOG_FLAG << "onConnectionAdd can not find connection" << uuid; + return; + } + + NetworkManager::ConnectionSettings::Ptr sett= conn->settings(); + if (sett->connectionType() != NetworkManager::ConnectionSettings::ConnectionType::Wireless) { + return; + } + + m_WifiNetworkList.clear(); + kyWirelessNetItemListInit(); + emit wifiNetworkUpdate(); +} + + +void KyWirelessNetResource::onDeviceAdd(QString deviceName, NetworkManager::Device::Type deviceType) +{ + if(deviceType == NetworkManager::Device::Type::Wifi) { + if (!m_WifiNetworkList.contains(deviceName)) { + m_WifiNetworkList.insert(deviceName,QList()); + } + } +} + +void KyWirelessNetResource::onDeviceRemove(QString deviceName) +{ + if (m_WifiNetworkList.contains(deviceName)) { + m_WifiNetworkList.remove(deviceName); + } +} + +void KyWirelessNetResource::onDeviceNameUpdate(QString oldName, QString newName) +{ + if (!m_WifiNetworkList.contains(oldName)) { + return; + } + + QMap> newWifiNetworkList(m_WifiNetworkList); + QList list = m_WifiNetworkList[oldName]; + newWifiNetworkList.remove(oldName); + newWifiNetworkList.insert(newName,list); + m_WifiNetworkList = newWifiNetworkList; + +} diff --git a/src/backend/dbus-interface/kywirelessnetresource.h b/src/backend/dbus-interface/kywirelessnetresource.h new file mode 100644 index 00000000..c7ed1f0b --- /dev/null +++ b/src/backend/dbus-interface/kywirelessnetresource.h @@ -0,0 +1,76 @@ +#ifndef KYWIRELESSNETRESOURCE_H +#define KYWIRELESSNETRESOURCE_H + +#include +#include "kywirelessnetitem.h" +#include "kylinnetworkresourcemanager.h" +#include "kyenterpricesettinginfo.h" +#include "kylinconnectresource.h" +#include "kywirelessconnectoperation.h" +#include "kylinnetworkdeviceresource.h" + + +//class KyWirelessNetItem; + +class KyWirelessNetResource : public QObject +{ + Q_OBJECT +public: + explicit KyWirelessNetResource(QObject *parent = nullptr); + ~KyWirelessNetResource(); + + //ui层调用接口 + bool getWifiNetwork(const QString &devIfaceName, const QString &ssid, KyWirelessNetItem &wirelessNetResource); + bool getAllDeviceWifiNetwork(QMap> &map); + bool getDeviceWifiNetwork(QString devIfaceName, QList &wirelessNetResource); + + bool getEnterPriseInfoTls(QString &uuid, KyEapMethodTlsInfo &info); + bool getEnterPriseInfoPeap(QString &uuid, KyEapMethodPeapInfo &info); + bool getEnterPriseInfoTtls(QString &uuid, KyEapMethodTtlsInfo &info); + + void getWirelessActiveConnection(NetworkManager::ActiveConnection::State state, QMap &map); + bool getActiveWirelessNetItem(QString deviceName, KyWirelessNetItem &wirelessNetItem); + + QString getActiveConnectSsidByDevice(QString deviceName); + void getSsidByUuid(const QString uuid, QString &ssid); + void getDeviceByUuid(const QString uuid, QString &deviceName); + + +private: + void kyWirelessNetItemListInit(); + QString getDeviceIFace(NetworkManager::WirelessNetwork::Ptr net); + QString getDeviceIFace(NetworkManager::ActiveConnection::Ptr actConn, QString &wirelessNetResourcessid); + +public slots: + void onWifiNetworkAdded(QString, QString); + void onWifiNetworkRemoved(QString, QString); + void onWifiNetworkPropertyChange(NetworkManager::WirelessNetwork * net); + void onWifiNetworkDeviceDisappear(); + + void onConnectionAdd(QString uuid); + void onConnectionUpdate(QString uuid); + void onConnectionRemove(QString); + + void onDeviceAdd(QString deviceName, NetworkManager::Device::Type deviceType); + void onDeviceRemove(QString deviceName); + void onDeviceNameUpdate(QString oldName, QString newName); + +signals: + void signalStrengthChange(QString, QString, int); + void bssidChange(QString, QString, QString); + void secuTypeChange(QString, QString, QString); + void connectionRemove(QString, QString); + void connectionAdd(QString, QString); + void wifiNetworkUpdate(); + void wifiNetworkAdd(QString, KyWirelessNetItem&); + void wifiNetworkRemove(QString, QString); + +private: + KyNetworkResourceManager *m_networkResourceInstance = nullptr; + KyWirelessConnectOperation *m_operation = nullptr; + KyNetworkDeviceResourse *m_networkDevice = nullptr; + QMap > m_WifiNetworkList; + +}; + +#endif // KYWIRELESSNETRESOURCE_H diff --git a/src/backend/dbus-interface/nm-macros-internal.h b/src/backend/dbus-interface/nm-macros-internal.h new file mode 100644 index 00000000..300ff43e --- /dev/null +++ b/src/backend/dbus-interface/nm-macros-internal.h @@ -0,0 +1,1291 @@ +#ifndef __NM_MACROS_INTERNAL_H__ +#define __NM_MACROS_INTERNAL_H__ + +#include +#include +#include + +#define _nm_packed __attribute__ ((packed)) +#define _nm_unused __attribute__ ((unused)) +#define _nm_pure __attribute__ ((pure)) +#define _nm_const __attribute__ ((const)) +#define _nm_printf(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#define _nm_align(s) __attribute__ ((aligned (s))) +#define _nm_alignof(type) __alignof (type) +#define _nm_alignas(type) _nm_align (_nm_alignof (type)) + +#if __GNUC__ >= 7 +#define _nm_fallthrough __attribute__ ((fallthrough)) +#else +#define _nm_fallthrough +#endif + +/*****************************************************************************/ + +#ifdef thread_local +#define _nm_thread_local thread_local +/* + * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ + * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 + */ +#elif __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) +#define _nm_thread_local _Thread_local +#else +#define _nm_thread_local __thread +#endif + +/*****************************************************************************/ + +#if 0 +#include "nm-glib.h" + +/*****************************************************************************/ + +#define nm_offsetofend(t,m) (G_STRUCT_OFFSET (t,m) + sizeof (((t *) NULL)->m)) + +#define nm_auto(fcn) __attribute__ ((cleanup(fcn))) + +static inline int nm_close (int fd); + +/** + * nm_auto_free: + * + * Call free() on a variable location when it goes out of scope. + */ +#define nm_auto_free nm_auto(_nm_auto_free_impl) +GS_DEFINE_CLEANUP_FUNCTION(void*, _nm_auto_free_impl, free) + +static inline void +nm_free_secret (char *secret) +{ + if (secret) { + memset (secret, 0, strlen (secret)); + g_free (secret); + } +} + +static inline void +_nm_auto_free_secret_impl (char **v) +{ + nm_free_secret (*v); +} + +/** + * nm_auto_free_secret: + * + * Call g_free() on a variable location when it goes out of scope. + * Also, previously, calls memset(loc, 0, strlen(loc)) to clear out + * the secret. + */ +#define nm_auto_free_secret nm_auto(_nm_auto_free_secret_impl) + +static inline void +_nm_auto_unset_gvalue_impl (GValue *v) +{ + g_value_unset (v); +} +#define nm_auto_unset_gvalue nm_auto(_nm_auto_unset_gvalue_impl) + +static inline void +_nm_auto_unref_gtypeclass (gpointer v) +{ + if (v && *((gpointer *) v)) + g_type_class_unref (*((gpointer *) v)); +} +#define nm_auto_unref_gtypeclass nm_auto(_nm_auto_unref_gtypeclass) + +static inline void +_nm_auto_free_gstring_impl (GString **str) +{ + if (*str) + g_string_free (*str, TRUE); +} +#define nm_auto_free_gstring nm_auto(_nm_auto_free_gstring_impl) + +static inline void +_nm_auto_close_impl (int *pfd) +{ + if (*pfd >= 0) { + int errsv = errno; + + (void) nm_close (*pfd); + errno = errsv; + } +} +#define nm_auto_close nm_auto(_nm_auto_close_impl) + +static inline void +_nm_auto_fclose_impl (FILE **pfd) +{ + if (*pfd) { + int errsv = errno; + + (void) fclose (*pfd); + errno = errsv; + } +} +#define nm_auto_fclose nm_auto(_nm_auto_fclose_impl) + +static inline void +_nm_auto_protect_errno (int *p_saved_errno) +{ + errno = *p_saved_errno; +} +#define NM_AUTO_PROTECT_ERRNO(errsv_saved) nm_auto(_nm_auto_protect_errno) _nm_unused const int errsv_saved = (errno) + +#endif + +/*****************************************************************************/ + +/* http://stackoverflow.com/a/11172679 */ +#define _NM_UTILS_MACRO_FIRST(...) __NM_UTILS_MACRO_FIRST_HELPER(__VA_ARGS__, throwaway) +#define __NM_UTILS_MACRO_FIRST_HELPER(first, ...) first + +#define _NM_UTILS_MACRO_REST(...) __NM_UTILS_MACRO_REST_HELPER(__NM_UTILS_MACRO_REST_NUM(__VA_ARGS__), __VA_ARGS__) +#define __NM_UTILS_MACRO_REST_HELPER(qty, ...) __NM_UTILS_MACRO_REST_HELPER2(qty, __VA_ARGS__) +#define __NM_UTILS_MACRO_REST_HELPER2(qty, ...) __NM_UTILS_MACRO_REST_HELPER_##qty(__VA_ARGS__) +#define __NM_UTILS_MACRO_REST_HELPER_ONE(first) +#define __NM_UTILS_MACRO_REST_HELPER_TWOORMORE(first, ...) , __VA_ARGS__ +#define __NM_UTILS_MACRO_REST_NUM(...) \ + __NM_UTILS_MACRO_REST_SELECT_30TH(__VA_ARGS__, \ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\ + TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway) +#define __NM_UTILS_MACRO_REST_SELECT_30TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, ...) a30 + +/*****************************************************************************/ + +/* http://stackoverflow.com/a/2124385/354393 */ + +#define NM_NARG(...) \ + _NM_NARG(__VA_ARGS__,_NM_NARG_RSEQ_N()) +#define _NM_NARG(...) \ + _NM_NARG_ARG_N(__VA_ARGS__) +#define _NM_NARG_ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define _NM_NARG_RSEQ_N() \ + 63,62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +/*****************************************************************************/ + +#if defined (__GNUC__) +#define _NM_PRAGMA_WARNING_DO(warning) G_STRINGIFY(GCC diagnostic ignored warning) +#elif defined (__clang__) +#define _NM_PRAGMA_WARNING_DO(warning) G_STRINGIFY(clang diagnostic ignored warning) +#endif + +/* you can only suppress a specific warning that the compiler + * understands. Otherwise you will get another compiler warning + * about invalid pragma option. + * It's not that bad however, because gcc and clang often have the + * same name for the same warning. */ + +#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define NM_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("GCC diagnostic push") \ + _Pragma(_NM_PRAGMA_WARNING_DO(warning)) +#elif defined (__clang__) +#define NM_PRAGMA_WARNING_DISABLE(warning) \ + _Pragma("clang diagnostic push") \ + _Pragma(_NM_PRAGMA_WARNING_DO(warning)) +#else +#define NM_PRAGMA_WARNING_DISABLE(warning) +#endif + +#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define NM_PRAGMA_WARNING_REENABLE \ + _Pragma("GCC diagnostic pop") +#elif defined (__clang__) +#define NM_PRAGMA_WARNING_REENABLE \ + _Pragma("clang diagnostic pop") +#else +#define NM_PRAGMA_WARNING_REENABLE +#endif + +/*****************************************************************************/ + +/** + * NM_G_ERROR_MSG: + * @error: (allow-none): the #GError instance + * + * All functions must follow the convention that when they + * return a failure, they must also set the GError to a valid + * message. For external API however, we want to be extra + * careful before accessing the error instance. Use NM_G_ERROR_MSG() + * which is safe to use on NULL. + * + * Returns: the error message. + **/ +static inline const char * +NM_G_ERROR_MSG (GError *error) +{ + return error ? (error->message ? : "(null)") : "(no-error)"; \ +} + +/*****************************************************************************/ + +/* macro to return strlen() of a compile time string. */ +#define NM_STRLEN(str) ( sizeof ("" str) - 1 ) + +/* returns the length of a NULL terminated array of pointers, + * like g_strv_length() does. The difference is: + * - it operats on arrays of pointers (of any kind, requiring no cast). + * - it accepts NULL to return zero. */ +#define NM_PTRARRAY_LEN(array) \ + ({ \ + typeof (*(array)) *const _array = (array); \ + gsize _n = 0; \ + \ + if (_array) { \ + _nm_unused typeof (*(_array[0])) *_array_check = _array[0]; \ + while (_array[_n]) \ + _n++; \ + } \ + _n; \ + }) + +/* Note: @value is only evaluated when *out_val is present. + * Thus, + * NM_SET_OUT (out_str, g_strdup ("hallo")); + * does the right thing. + */ +#define NM_SET_OUT(out_val, value) \ + G_STMT_START { \ + typeof(*(out_val)) *_out_val = (out_val); \ + \ + if (_out_val) { \ + *_out_val = (value); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +#ifndef _NM_CC_SUPPORT_AUTO_TYPE +#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) +#define _NM_CC_SUPPORT_AUTO_TYPE 1 +#else +#define _NM_CC_SUPPORT_AUTO_TYPE 0 +#endif +#endif + +#ifndef _NM_CC_SUPPORT_GENERIC +#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9 ))) || (defined (__clang__)) +#define _NM_CC_SUPPORT_GENERIC 1 +#else +#define _NM_CC_SUPPORT_GENERIC 0 +#endif +#endif + +#if _NM_CC_SUPPORT_AUTO_TYPE +#define _nm_auto_type __auto_type +#endif + +#if _NM_CC_SUPPORT_GENERIC +#define _NM_CONSTCAST_FULL_1(type, obj_expr, obj) \ + (_Generic ((obj_expr), \ + const void *const: ((const type *) (obj)), \ + const void * : ((const type *) (obj)), \ + void *const: (( type *) (obj)), \ + void * : (( type *) (obj)), \ + const type *const: ((const type *) (obj)), \ + const type * : ((const type *) (obj)), \ + type *const: (( type *) (obj)), \ + type * : (( type *) (obj)))) +#define _NM_CONSTCAST_FULL_2(type, obj_expr, obj, alias_type2) \ + (_Generic ((obj_expr), \ + const void *const: ((const type *) (obj)), \ + const void * : ((const type *) (obj)), \ + void *const: (( type *) (obj)), \ + void * : (( type *) (obj)), \ + const alias_type2 *const: ((const type *) (obj)), \ + const alias_type2 * : ((const type *) (obj)), \ + alias_type2 *const: (( type *) (obj)), \ + alias_type2 * : (( type *) (obj)), \ + const type *const: ((const type *) (obj)), \ + const type * : ((const type *) (obj)), \ + type *const: (( type *) (obj)), \ + type * : (( type *) (obj)))) +#define _NM_CONSTCAST_FULL_3(type, obj_expr, obj, alias_type2, alias_type3) \ + (_Generic ((obj_expr), \ + const void *const: ((const type *) (obj)), \ + const void * : ((const type *) (obj)), \ + void *const: (( type *) (obj)), \ + void * : (( type *) (obj)), \ + const alias_type2 *const: ((const type *) (obj)), \ + const alias_type2 * : ((const type *) (obj)), \ + alias_type2 *const: (( type *) (obj)), \ + alias_type2 * : (( type *) (obj)), \ + const alias_type3 *const: ((const type *) (obj)), \ + const alias_type3 * : ((const type *) (obj)), \ + alias_type3 *const: (( type *) (obj)), \ + alias_type3 * : (( type *) (obj)), \ + const type *const: ((const type *) (obj)), \ + const type * : ((const type *) (obj)), \ + type *const: (( type *) (obj)), \ + type * : (( type *) (obj)))) +#define _NM_CONSTCAST_FULL_4(type, obj_expr, obj, alias_type2, alias_type3, alias_type4) \ + (_Generic ((obj_expr), \ + const void *const: ((const type *) (obj)), \ + const void * : ((const type *) (obj)), \ + void *const: (( type *) (obj)), \ + void * : (( type *) (obj)), \ + const alias_type2 *const: ((const type *) (obj)), \ + const alias_type2 * : ((const type *) (obj)), \ + alias_type2 *const: (( type *) (obj)), \ + alias_type2 * : (( type *) (obj)), \ + const alias_type3 *const: ((const type *) (obj)), \ + const alias_type3 * : ((const type *) (obj)), \ + alias_type3 *const: (( type *) (obj)), \ + alias_type3 * : (( type *) (obj)), \ + const alias_type4 *const: ((const type *) (obj)), \ + const alias_type4 * : ((const type *) (obj)), \ + alias_type4 *const: (( type *) (obj)), \ + alias_type4 * : (( type *) (obj)), \ + const type *const: ((const type *) (obj)), \ + const type * : ((const type *) (obj)), \ + type *const: (( type *) (obj)), \ + type * : (( type *) (obj)))) +#define _NM_CONSTCAST_FULL_x(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_##n (type, obj_expr, obj, ##__VA_ARGS__)) +#define _NM_CONSTCAST_FULL_y(type, obj_expr, obj, n, ...) (_NM_CONSTCAST_FULL_x (type, obj_expr, obj, n, ##__VA_ARGS__)) +#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) (_NM_CONSTCAST_FULL_y (type, obj_expr, obj, NM_NARG (dummy, ##__VA_ARGS__), ##__VA_ARGS__)) +#else +#define NM_CONSTCAST_FULL( type, obj_expr, obj, ...) ((type *) (obj)) +#endif + +#define NM_CONSTCAST(type, obj, ...) \ + NM_CONSTCAST_FULL(type, (obj), (obj), ##__VA_ARGS__) + +#define NM_GOBJECT_CAST(type, obj, is_check, ...) \ + ({ \ + const void *_obj = (obj); \ + \ + nm_assert (_obj || (is_check (_obj))); \ + NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \ + }) + +#define NM_GOBJECT_CAST_NON_NULL(type, obj, is_check, ...) \ + ({ \ + const void *_obj = (obj); \ + \ + nm_assert (is_check (_obj)); \ + NM_CONSTCAST_FULL (type, (obj), _obj, GObject, ##__VA_ARGS__); \ + }) + +#if _NM_CC_SUPPORT_GENERIC +/* returns @value, if the type of @value matches @type. + * This requires support for C11 _Generic(). If no support is + * present, this returns @value directly. + * + * It's useful to check the let the compiler ensure that @value is + * of a certain type. */ +#define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value))) +#else +#define _NM_ENSURE_TYPE(type, value) (value) +#endif + +#if _NM_CC_SUPPORT_GENERIC +#define NM_PROPAGATE_CONST(test_expr, ptr) \ + (_Generic ((test_expr), \ + const typeof (*(test_expr)) *: ((const typeof (*(ptr)) *) (ptr)), \ + default: (_Generic ((test_expr), \ + typeof (*(test_expr)) *: (ptr))))) +#else +#define NM_PROPAGATE_CONST(test_expr, ptr) (ptr) +#endif + +/*****************************************************************************/ + +#define _NM_IN_SET_EVAL_1( op, _x, y) (_x == (y)) +#define _NM_IN_SET_EVAL_2( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_1 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_3( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_2 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_4( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_3 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_5( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_4 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_6( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_5 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_7( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_6 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_8( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_7 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_9( op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_8 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_10(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_9 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_11(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_10 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_12(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_11 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_13(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_12 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_14(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_13 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_15(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_14 (op, _x, __VA_ARGS__) +#define _NM_IN_SET_EVAL_16(op, _x, y, ...) (_x == (y)) op _NM_IN_SET_EVAL_15 (op, _x, __VA_ARGS__) + +#define _NM_IN_SET_EVAL_N2(op, _x, n, ...) (_NM_IN_SET_EVAL_##n(op, _x, __VA_ARGS__)) +#define _NM_IN_SET_EVAL_N(op, type, x, n, ...) \ + ({ \ + type _x = (x); \ + \ + /* trigger a -Wenum-compare warning */ \ + nm_assert (TRUE || _x == (x)); \ + \ + !!_NM_IN_SET_EVAL_N2(op, _x, n, __VA_ARGS__); \ + }) + +#define _NM_IN_SET(op, type, x, ...) _NM_IN_SET_EVAL_N(op, type, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) + +/* Beware that this does short-circuit evaluation (use "||" instead of "|") + * which has a possibly unexpected non-function-like behavior. + * Use NM_IN_SET_SE if you need all arguments to be evaluted. */ +#define NM_IN_SET(x, ...) _NM_IN_SET(||, typeof (x), x, __VA_ARGS__) + +/* "SE" stands for "side-effect". Contrary to NM_IN_SET(), this does not do + * short-circuit evaluation, which can make a difference if the arguments have + * side-effects. */ +#define NM_IN_SET_SE(x, ...) _NM_IN_SET(|, typeof (x), x, __VA_ARGS__) + +/* the *_TYPED forms allow to explicitly select the type of "x". This is useful + * if "x" doesn't support typeof (bitfields) or you want to gracefully convert + * a type using automatic type conversion rules (but not forcing the conversion + * with a cast). */ +#define NM_IN_SET_TYPED(type, x, ...) _NM_IN_SET(||, type, x, __VA_ARGS__) +#define NM_IN_SET_SE_TYPED(type, x, ...) _NM_IN_SET(|, type, x, __VA_ARGS__) + +/*****************************************************************************/ + +static inline gboolean +_NM_IN_STRSET_streq (const char *x, const char *s) +{ + return s && strcmp (x, s) == 0; +} + +#define _NM_IN_STRSET_EVAL_1( op, _x, y) _NM_IN_STRSET_streq (_x, y) +#define _NM_IN_STRSET_EVAL_2( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_1 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_3( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_2 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_4( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_3 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_5( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_4 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_6( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_5 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_7( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_6 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_8( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_7 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_9( op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_8 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_10(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_9 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_11(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_10 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_12(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_11 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_13(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_12 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_14(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_13 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_15(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_14 (op, _x, __VA_ARGS__) +#define _NM_IN_STRSET_EVAL_16(op, _x, y, ...) _NM_IN_STRSET_streq (_x, y) op _NM_IN_STRSET_EVAL_15 (op, _x, __VA_ARGS__) + +#define _NM_IN_STRSET_EVAL_N2(op, _x, n, ...) (_NM_IN_STRSET_EVAL_##n(op, _x, __VA_ARGS__)) +#define _NM_IN_STRSET_EVAL_N(op, x, n, ...) \ + ({ \ + const char *_x = (x); \ + ( ((_x == NULL) && _NM_IN_SET_EVAL_N2 (op, ((const char *) NULL), n, __VA_ARGS__)) \ + || ((_x != NULL) && _NM_IN_STRSET_EVAL_N2 (op, _x, n, __VA_ARGS__)) \ + ); \ + }) + +/* Beware that this does short-circuit evaluation (use "||" instead of "|") + * which has a possibly unexpected non-function-like behavior. + * Use NM_IN_STRSET_SE if you need all arguments to be evaluted. */ +#define NM_IN_STRSET(x, ...) _NM_IN_STRSET_EVAL_N(||, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) + +/* "SE" stands for "side-effect". Contrary to NM_IN_STRSET(), this does not do + * short-circuit evaluation, which can make a difference if the arguments have + * side-effects. */ +#define NM_IN_STRSET_SE(x, ...) _NM_IN_STRSET_EVAL_N(|, x, NM_NARG (__VA_ARGS__), __VA_ARGS__) + +#define NM_STRCHAR_ALL(str, ch_iter, predicate) \ + ({ \ + gboolean _val = TRUE; \ + const char *_str = (str); \ + \ + if (_str) { \ + for (;;) { \ + const char ch_iter = _str[0]; \ + \ + if (ch_iter != '\0') { \ + if (predicate) {\ + _str++; \ + continue; \ + } \ + _val = FALSE; \ + } \ + break; \ + } \ + } \ + _val; \ + }) + +#define NM_STRCHAR_ANY(str, ch_iter, predicate) \ + ({ \ + gboolean _val = FALSE; \ + const char *_str = (str); \ + \ + if (_str) { \ + for (;;) { \ + const char ch_iter = _str[0]; \ + \ + if (ch_iter != '\0') { \ + if (predicate) { \ + ; \ + } else { \ + _str++; \ + continue; \ + } \ + _val = TRUE; \ + } \ + break; \ + } \ + } \ + _val; \ + }) + +/*****************************************************************************/ + +/* NM_CACHED_QUARK() returns the GQuark for @string, but caches + * it in a static variable to speed up future lookups. + * + * @string must be a string literal. + */ +#define NM_CACHED_QUARK(string) \ + ({ \ + static GQuark _nm_cached_quark = 0; \ + \ + (G_LIKELY (_nm_cached_quark != 0) \ + ? _nm_cached_quark \ + : (_nm_cached_quark = g_quark_from_static_string (""string""))); \ + }) + +/* NM_CACHED_QUARK_FCN() is essentially the same as G_DEFINE_QUARK + * with two differences: + * - @string must be a quoted string-literal + * - @fcn must be the full function name, while G_DEFINE_QUARK() appends + * "_quark" to the function name. + * Both properties of G_DEFINE_QUARK() are non favorable, because you can no + * longer grep for string/fcn -- unless you are aware that you are searching + * for G_DEFINE_QUARK() and omit quotes / append _quark(). With NM_CACHED_QUARK_FCN(), + * ctags/cscope can locate the use of @fcn (though it doesn't recognize that + * NM_CACHED_QUARK_FCN() defines it). + */ +#define NM_CACHED_QUARK_FCN(string, fcn) \ +GQuark \ +fcn (void) \ +{ \ + return NM_CACHED_QUARK (string); \ +} + +/*****************************************************************************/ + +#define nm_streq(s1, s2) (strcmp (s1, s2) == 0) +#define nm_streq0(s1, s2) (g_strcmp0 (s1, s2) == 0) + +/*****************************************************************************/ + +static inline GString * +nm_gstring_prepare (GString **l) +{ + if (*l) + g_string_set_size (*l, 0); + else + *l = g_string_sized_new (30); + return *l; +} + +static inline const char * +nm_str_not_empty (const char *str) +{ + return str && str[0] ? str : NULL; +} + +static inline char * +nm_strdup_not_empty (const char *str) +{ + return str && str[0] ? g_strdup (str) : NULL; +} + +static inline char * +nm_str_realloc (char *str) +{ + gs_free char *s = str; + + /* Returns a new clone of @str and frees @str. The point is that @str + * possibly points to a larger chunck of memory. We want to freshly allocate + * a buffer. + * + * We could use realloc(), but that might not do anything or leave + * @str in its memory pool for chunks of a different size (bad for + * fragmentation). + * + * This is only useful when we want to keep the buffer around for a long + * time and want to re-allocate a more optimal buffer. */ + + return g_strdup (s); +} + +/*****************************************************************************/ + +#define NM_PRINT_FMT_QUOTED(cond, prefix, str, suffix, str_else) \ + (cond) ? (prefix) : "", \ + (cond) ? (str) : (str_else), \ + (cond) ? (suffix) : "" +#define NM_PRINT_FMT_QUOTE_STRING(arg) NM_PRINT_FMT_QUOTED((arg), "\"", (arg), "\"", "(null)") + +/*****************************************************************************/ + +/* glib/C provides the following kind of assertions: + * - assert() -- disable with NDEBUG + * - g_return_if_fail() -- disable with G_DISABLE_CHECKS + * - g_assert() -- disable with G_DISABLE_ASSERT + * but they are all enabled by default and usually even production builds have + * these kind of assertions enabled. It also means, that disabling assertions + * is an untested configuration, and might have bugs. + * + * Add our own assertion macro nm_assert(), which is disabled by default and must + * be explicitly enabled. They are useful for more expensive checks or checks that + * depend less on runtime conditions (that is, are generally expected to be true). */ + +#ifndef NM_MORE_ASSERTS +#define NM_MORE_ASSERTS 0 +#endif + +#if NM_MORE_ASSERTS +#define nm_assert(cond) G_STMT_START { g_assert (cond); } G_STMT_END +#define nm_assert_se(cond) G_STMT_START { if (G_LIKELY (cond)) { ; } else { g_assert (FALSE && (cond)); } } G_STMT_END +#define nm_assert_not_reached() G_STMT_START { g_assert_not_reached (); } G_STMT_END +#else +#define nm_assert(cond) G_STMT_START { if (FALSE) { if (cond) { } } } G_STMT_END +#define nm_assert_se(cond) G_STMT_START { if (G_LIKELY (cond)) { ; } } G_STMT_END +#define nm_assert_not_reached() G_STMT_START { ; } G_STMT_END +#endif + +/*****************************************************************************/ + +#define NM_GOBJECT_PROPERTIES_DEFINE_BASE(...) \ +typedef enum { \ + _PROPERTY_ENUMS_0, \ + __VA_ARGS__ \ + _PROPERTY_ENUMS_LAST, \ +} _PropertyEnums; \ +static GParamSpec *obj_properties[_PROPERTY_ENUMS_LAST] = { NULL, } + +#define NM_GOBJECT_PROPERTIES_DEFINE(obj_type, ...) \ +NM_GOBJECT_PROPERTIES_DEFINE_BASE (__VA_ARGS__); \ +static inline void \ +_notify (obj_type *obj, _PropertyEnums prop) \ +{ \ + nm_assert (G_IS_OBJECT (obj)); \ + nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties)); \ + g_object_notify_by_pspec ((GObject *) obj, obj_properties[prop]); \ +} + +/*****************************************************************************/ + +#define _NM_GET_PRIVATE(self, type, is_check, ...) (&(NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__)->_priv)) +#if _NM_CC_SUPPORT_AUTO_TYPE +#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) \ + ({ \ + _nm_auto_type _self = NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__); \ + \ + NM_PROPAGATE_CONST (_self, _self->_priv); \ + }) +#else +#define _NM_GET_PRIVATE_PTR(self, type, is_check, ...) (NM_GOBJECT_CAST_NON_NULL (type, (self), is_check, ##__VA_ARGS__)->_priv) +#endif + +/*****************************************************************************/ + +static inline gpointer +nm_g_object_ref (gpointer obj) +{ + /* g_object_ref() doesn't accept NULL. */ + if (obj) + g_object_ref (obj); + return obj; +} +#define nm_g_object_ref(obj) ((typeof (obj)) nm_g_object_ref (obj)) + +static inline void +nm_g_object_unref (gpointer obj) +{ + /* g_object_unref() doesn't accept NULL. Usully, we workaround that + * by using g_clear_object(), but sometimes that is not convinient + * (for example as as destroy function for a hash table that can contain + * NULL values). */ + if (obj) + g_object_unref (obj); +} + +/* Assigns GObject @obj to destination @pdst, and takes an additional ref. + * The previous value of @pdst is unrefed. + * + * It makes sure to first increase the ref-count of @obj, and handles %NULL + * @obj correctly. + * */ +#define nm_g_object_ref_set(pp, obj) \ + ({ \ + typeof (*(pp)) *const _pp = (pp); \ + typeof (**_pp) *const _obj = (obj); \ + typeof (**_pp) *_p; \ + gboolean _changed = FALSE; \ + \ + if ( _pp \ + && ((_p = *_pp) != _obj)) { \ + if (_obj) { \ + nm_assert (G_IS_OBJECT (_obj)); \ + g_object_ref (_obj); \ + } \ + if (_p) { \ + nm_assert (G_IS_OBJECT (_p)); \ + *_pp = NULL; \ + g_object_unref (_p); \ + } \ + *_pp = _obj; \ + _changed = TRUE; \ + } \ + _changed; \ + }) + +/* basically, replaces + * g_clear_pointer (&location, g_free) + * with + * nm_clear_g_free (&location) + * + * Another advantage is that by using a macro and typeof(), it is more + * typesafe and gives you for example a compiler warning when pp is a const + * pointer or points to a const-pointer. + */ +#define nm_clear_g_free(pp) \ + ({ \ + typeof (*(pp)) *_pp = (pp); \ + typeof (**_pp) *_p; \ + gboolean _changed = FALSE; \ + \ + if ( _pp \ + && (_p = *_pp)) { \ + *_pp = NULL; \ + g_free (_p); \ + _changed = TRUE; \ + } \ + _changed; \ + }) + +#define nm_clear_g_object(pp) \ + ({ \ + typeof (*(pp)) *_pp = (pp); \ + typeof (**_pp) *_p; \ + gboolean _changed = FALSE; \ + \ + if ( _pp \ + && (_p = *_pp)) { \ + nm_assert (G_IS_OBJECT (_p)); \ + *_pp = NULL; \ + g_object_unref (_p); \ + _changed = TRUE; \ + } \ + _changed; \ + }) + +static inline gboolean +nm_clear_g_source (guint *id) +{ + if (id && *id) { + g_source_remove (*id); + *id = 0; + return TRUE; + } + return FALSE; +} + +static inline gboolean +nm_clear_g_signal_handler (gpointer self, gulong *id) +{ + if (id && *id) { + g_signal_handler_disconnect (self, *id); + *id = 0; + return TRUE; + } + return FALSE; +} + +static inline gboolean +nm_clear_g_variant (GVariant **variant) +{ + if (variant && *variant) { + g_variant_unref (*variant); + *variant = NULL; + return TRUE; + } + return FALSE; +} + +static inline gboolean +nm_clear_g_cancellable (GCancellable **cancellable) +{ + if (cancellable && *cancellable) { + g_cancellable_cancel (*cancellable); + g_object_unref (*cancellable); + *cancellable = NULL; + return TRUE; + } + return FALSE; +} + +/*****************************************************************************/ + +/* Determine whether @x is a power of two (@x being an integer type). + * Basically, this returns TRUE, if @x has exactly one bit set. + * For negative values and zero, this always returns FALSE. */ +#define nm_utils_is_power_of_two(x) ({ \ + typeof(x) __x = (x); \ + \ + ( (__x > ((typeof(__x)) 0)) \ + && ((__x & (__x - (((typeof(__x)) 1)))) == ((typeof(__x)) 0))); \ + }) + +/*****************************************************************************/ + +#define NM_UTILS_LOOKUP_DEFAULT(v) return (v) +#define NM_UTILS_LOOKUP_DEFAULT_WARN(v) g_return_val_if_reached (v) +#define NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(v) { nm_assert_not_reached (); return (v); } +#define NM_UTILS_LOOKUP_ITEM(v, n) (void) 0; case v: return (n); (void) 0 +#define NM_UTILS_LOOKUP_STR_ITEM(v, n) NM_UTILS_LOOKUP_ITEM(v, ""n"") +#define NM_UTILS_LOOKUP_ITEM_IGNORE(v) (void) 0; case v: break; (void) 0 +#define NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER() (void) 0; default: break; (void) 0 + +#define _NM_UTILS_LOOKUP_DEFINE(scope, fcn_name, lookup_type, result_type, unknown_val, ...) \ +scope result_type \ +fcn_name (lookup_type val) \ +{ \ + switch (val) { \ + (void) 0, \ + __VA_ARGS__ \ + (void) 0; \ + }; \ + { unknown_val; } \ +} + +#define NM_UTILS_LOOKUP_STR_DEFINE(fcn_name, lookup_type, unknown_val, ...) \ + _NM_UTILS_LOOKUP_DEFINE (, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) +#define NM_UTILS_LOOKUP_STR_DEFINE_STATIC(fcn_name, lookup_type, unknown_val, ...) \ + _NM_UTILS_LOOKUP_DEFINE (static, fcn_name, lookup_type, const char *, unknown_val, __VA_ARGS__) + +/* Call the string-lookup-table function @fcn_name. If the function returns + * %NULL, the numeric index is converted to string using a alloca() buffer. + * Beware: this macro uses alloca(). */ +#define NM_UTILS_LOOKUP_STR(fcn_name, idx) \ + ({ \ + typeof (idx) _idx = (idx); \ + const char *_s; \ + \ + _s = fcn_name (_idx); \ + if (!_s) { \ + _s = g_alloca (30); \ + \ + g_snprintf ((char *) _s, 30, "(%lld)", (long long) _idx); \ + } \ + _s; \ + }) + +/*****************************************************************************/ + +/* check if @flags has exactly one flag (@check) set. You should call this + * only with @check being a compile time constant and a power of two. */ +#define NM_FLAGS_HAS(flags, check) \ + ( G_STATIC_ASSERT_EXPR ((check) > 0 && ((check) & ((check) - 1)) == 0), NM_FLAGS_ANY ((flags), (check)) ) + +#define NM_FLAGS_ANY(flags, check) ( ( ((flags) & (check)) != 0 ) ? TRUE : FALSE ) +#define NM_FLAGS_ALL(flags, check) ( ( ((flags) & (check)) == (check) ) ? TRUE : FALSE ) + +#define NM_FLAGS_SET(flags, val) ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + _flags | _val; \ + }) + +#define NM_FLAGS_UNSET(flags, val) ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + _flags & (~_val); \ + }) + +#define NM_FLAGS_ASSIGN(flags, val, assign) ({ \ + const typeof(flags) _flags = (flags); \ + const typeof(flags) _val = (val); \ + \ + (assign) \ + ? _flags | (_val) \ + : _flags & (~_val); \ + }) + +/*****************************************************************************/ + +#define _NM_BACKPORT_SYMBOL_IMPL(VERSION, RETURN_TYPE, ORIG_FUNC, VERSIONED_FUNC, ARGS_TYPED, ARGS) \ +RETURN_TYPE VERSIONED_FUNC ARGS_TYPED; \ +RETURN_TYPE VERSIONED_FUNC ARGS_TYPED \ +{ \ + return ORIG_FUNC ARGS; \ +} \ +RETURN_TYPE ORIG_FUNC ARGS_TYPED; \ +__asm__(".symver "G_STRINGIFY(VERSIONED_FUNC)", "G_STRINGIFY(ORIG_FUNC)"@"G_STRINGIFY(VERSION)) + +#define NM_BACKPORT_SYMBOL(VERSION, RETURN_TYPE, FUNC, ARGS_TYPED, ARGS) \ +_NM_BACKPORT_SYMBOL_IMPL(VERSION, RETURN_TYPE, FUNC, _##FUNC##_##VERSION, ARGS_TYPED, ARGS) + +/*****************************************************************************/ + +#define nm_str_skip_leading_spaces(str) \ + ({ \ + typeof (*(str)) *_str = (str); \ + _nm_unused const char *_str_type_check = _str; \ + \ + if (_str) { \ + while (g_ascii_isspace (_str[0])) \ + _str++; \ + } \ + _str; \ + }) + +static inline char * +nm_strstrip (char *str) +{ + /* g_strstrip doesn't like NULL. */ + return str ? g_strstrip (str) : NULL; +} + +static inline const char * +nm_strstrip_avoid_copy (const char *str, char **str_free) +{ + gsize l; + char *s; + + nm_assert (str_free && !*str_free); + + if (!str) + return NULL; + + str = nm_str_skip_leading_spaces (str); + l = strlen (str); + if ( l == 0 + || !g_ascii_isspace (str[l - 1])) + return str; + while ( l > 0 + && g_ascii_isspace (str[l - 1])) + l--; + + s = g_new (char, l + 1); + memcpy (s, str, l); + s[l] = '\0'; + *str_free = s; + return s; +} + +/* g_ptr_array_sort()'s compare function takes pointers to the + * value. Thus, you cannot use strcmp directly. You can use + * nm_strcmp_p(). + * + * Like strcmp(), this function is not forgiving to accept %NULL. */ +static inline int +nm_strcmp_p (gconstpointer a, gconstpointer b) +{ + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); + + return strcmp (s1, s2); +} + +/* like nm_strcmp_p(), suitable for g_ptr_array_sort_with_data(). + * g_ptr_array_sort() just casts nm_strcmp_p() to a function of different + * signature. I guess, in glib there are knowledgeable people that ensure + * that this additional argument doesn't cause problems due to different ABI + * for every architecture that glib supports. + * For NetworkManager, we'd rather avoid such stunts. + **/ +static inline int +nm_strcmp_p_with_data (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const char *s1 = *((const char **) a); + const char *s2 = *((const char **) b); + + return strcmp (s1, s2); +} + +static inline int +nm_cmp_uint32_p_with_data (gconstpointer p_a, gconstpointer p_b, gpointer user_data) +{ + const guint32 a = *((const guint32 *) p_a); + const guint32 b = *((const guint32 *) p_b); + + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + +/*****************************************************************************/ + +/* Taken from systemd's UNIQ_T and UNIQ macros. */ + +#define NM_UNIQ_T(x, uniq) G_PASTE(__unique_prefix_, G_PASTE(x, uniq)) +#define NM_UNIQ __COUNTER__ + +/*****************************************************************************/ + +/* glib's MIN()/MAX() macros don't have function-like behavior, in that they evaluate + * the argument possibly twice. + * + * Taken from systemd's MIN()/MAX() macros. */ + +#define NM_MIN(a, b) __NM_MIN(NM_UNIQ, a, NM_UNIQ, b) +#define __NM_MIN(aq, a, bq, b) \ + ({ \ + typeof (a) NM_UNIQ_T(A, aq) = (a); \ + typeof (b) NM_UNIQ_T(B, bq) = (b); \ + ((NM_UNIQ_T(A, aq) < NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ + }) + +#define NM_MAX(a, b) __NM_MAX(NM_UNIQ, a, NM_UNIQ, b) +#define __NM_MAX(aq, a, bq, b) \ + ({ \ + typeof (a) NM_UNIQ_T(A, aq) = (a); \ + typeof (b) NM_UNIQ_T(B, bq) = (b); \ + ((NM_UNIQ_T(A, aq) > NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ + }) + +#define NM_CLAMP(x, low, high) __NM_CLAMP(NM_UNIQ, x, NM_UNIQ, low, NM_UNIQ, high) +#define __NM_CLAMP(xq, x, lowq, low, highq, high) \ + ({ \ + typeof(x)NM_UNIQ_T(X,xq) = (x); \ + typeof(low) NM_UNIQ_T(LOW,lowq) = (low); \ + typeof(high) NM_UNIQ_T(HIGH,highq) = (high); \ + \ + ( (NM_UNIQ_T(X,xq) > NM_UNIQ_T(HIGH,highq)) \ + ? NM_UNIQ_T(HIGH,highq) \ + : (NM_UNIQ_T(X,xq) < NM_UNIQ_T(LOW,lowq)) \ + ? NM_UNIQ_T(LOW,lowq) \ + : NM_UNIQ_T(X,xq)); \ + }) + +/*****************************************************************************/ + +static inline guint +nm_encode_version (guint major, guint minor, guint micro) +{ + /* analog to the preprocessor macro NM_ENCODE_VERSION(). */ + return (major << 16) | (minor << 8) | micro; +} + +static inline void +nm_decode_version (guint version, guint *major, guint *minor, guint *micro) +{ + *major = (version & 0xFFFF0000u) >> 16; + *minor = (version & 0x0000FF00u) >> 8; + *micro = (version & 0x000000FFu); +} + +/*****************************************************************************/ + +/* taken from systemd's DECIMAL_STR_MAX() + * + * Returns the number of chars needed to format variables of the + * specified type as a decimal string. Adds in extra space for a + * negative '-' prefix (hence works correctly on signed + * types). Includes space for the trailing NUL. */ +#define NM_DECIMAL_STR_MAX(type) \ + (2+(sizeof(type) <= 1 ? 3 : \ + sizeof(type) <= 2 ? 5 : \ + sizeof(type) <= 4 ? 10 : \ + sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) + +/*****************************************************************************/ + +/* if @str is NULL, return "(null)". Otherwise, allocate a buffer using + * alloca() of and fill it with @str. @str will be quoted with double quote. + * If @str is longer then @trunc_at, the string is truncated and the closing + * quote is instead '^' to indicate truncation. + * + * Thus, the maximum stack allocated buffer will be @trunc_at+3. */ +#define nm_strquote_a(trunc_at, str) \ + ({ \ + const char *const _str = (str); \ + \ + (_str \ + ? ({ \ + const gsize _trunc_at = (trunc_at); \ + const gsize _strlen_trunc = NM_MIN (strlen (_str), _trunc_at); \ + char *_buf; \ + \ + _buf = g_alloca (_strlen_trunc + 3); \ + _buf[0] = '"'; \ + memcpy (&_buf[1], _str, _strlen_trunc); \ + _buf[_strlen_trunc + 1] = _str[_strlen_trunc] ? '^' : '"'; \ + _buf[_strlen_trunc + 2] = '\0'; \ + _buf; \ + }) \ + : "(null)"); \ + }) + +#define nm_sprintf_buf(buf, format, ...) \ + ({ \ + char * _buf = (buf); \ + int _buf_len; \ + \ + /* some static assert trying to ensure that the buffer is statically allocated. + * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ + G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *)); \ + _buf_len = g_snprintf (_buf, sizeof (buf), \ + ""format"", ##__VA_ARGS__); \ + nm_assert (_buf_len < sizeof (buf)); \ + _buf; \ + }) + +#define nm_sprintf_bufa(n_elements, format, ...) \ + ({ \ + char *_buf; \ + int _buf_len; \ + typeof (n_elements) _n_elements = (n_elements); \ + \ + _buf = g_alloca (_n_elements); \ + _buf_len = g_snprintf (_buf, _n_elements, \ + ""format"", ##__VA_ARGS__); \ + nm_assert (_buf_len < _n_elements); \ + _buf; \ + }) + +/* aims to alloca() a buffer and fill it with printf(format, name). + * Note that format must not contain any format specifier except + * "%s". + * If the resulting string would be too large for stack allocation, + * it allocates a buffer with g_malloc() and assigns it to *p_val_to_free. */ +#define nm_construct_name_a(format, name, p_val_to_free) \ + ({ \ + const char *const _name = (name); \ + char **const _p_val_to_free = (p_val_to_free); \ + const gsize _name_len = strlen (_name); \ + char *_buf2; \ + \ + nm_assert (_p_val_to_free && !*_p_val_to_free); \ + if (NM_STRLEN (format) + _name_len < 200) \ + _buf2 = nm_sprintf_bufa (NM_STRLEN (format) + _name_len, format, _name); \ + else { \ + _buf2 = g_strdup_printf (format, _name); \ + *_p_val_to_free = _buf2; \ + } \ + (const char *) _buf2; \ + }) + +/*****************************************************************************/ + +/** + * The boolean type _Bool is C99 while we mostly stick to C89. However, _Bool is too + * convinient to miss and is effectively available in gcc and clang. So, just use it. + * + * Usually, one would include "stdbool.h" to get the "bool" define which aliases + * _Bool. We provide this define here, because we want to make use of it anywhere. + * (also, stdbool.h is again C99). + * + * Using _Bool has advantages over gboolean: + * + * - commonly _Bool is one byte large, instead of gboolean's 4 bytes (because gboolean + * is a typedef for gint). Especially when having boolean fields in a struct, we can + * thereby easily save some space. + * + * - _Bool type guarantees that two "true" expressions compare equal. E.g. the follwing + * will not work: + * gboolean v1 = 1; + * gboolean v2 = 2; + * g_assert_cmpint (v1, ==, v2); // will fail + * For that, we often to use !! to coerce gboolean values to 0 or 1: + * g_assert_cmpint (!!v2, ==, TRUE); + * With _Bool type, this will be handled properly by the compiler. + * + * - For structs, we might want to safe even more space and use bitfields: + * struct s1 { + * gboolean v1:1; + * }; + * But the problem here is that gboolean is signed, so that + * v1 will be either 0 or -1 (not 1, TRUE). Thus, the following + * fails: + * struct s1 s = { .v1 = TRUE, }; + * g_assert_cmpint (s1.v1, ==, TRUE); + * It will however work just fine with bool/_Bool while retaining the + * notion of having a boolean value. + * + * Also, add the defines for "true" and "false". Those are nicely highlighted by the editor + * as special types, contrary to glib's "TRUE"/"FALSE". + */ + +#ifndef bool +#define bool _Bool +#define true 1 +#define false 0 +#endif + + +#ifdef _G_BOOLEAN_EXPR +/* g_assert() uses G_LIKELY(), which in turn uses _G_BOOLEAN_EXPR(). + * As glib's implementation uses a local variable _g_boolean_var_, + * we cannot do + * g_assert (some_macro ()); + * where some_macro() itself expands to ({g_assert(); ...}). + * In other words, you cannot have a g_assert() inside a g_assert() + * without getting a -Werror=shadow failure. + * + * Workaround that by re-defining _G_BOOLEAN_EXPR() + **/ +#undef _G_BOOLEAN_EXPR +#define __NM_G_BOOLEAN_EXPR_IMPL(v, expr) \ + ({ \ + int NM_UNIQ_T(V, v); \ + \ + if (expr) \ + NM_UNIQ_T(V, v) = 1; \ + else \ + NM_UNIQ_T(V, v) = 0; \ + NM_UNIQ_T(V, v); \ + }) +#define _G_BOOLEAN_EXPR(expr) __NM_G_BOOLEAN_EXPR_IMPL (NM_UNIQ, expr) +#endif + +/*****************************************************************************/ + +static inline int +nm_steal_fd (int *p_fd) +{ + int fd; + + if ( p_fd + && ((fd = *p_fd) >= 0)) { + *p_fd = -1; + return fd; + } + return -1; +} + +/** + * nm_close: + * + * Like close() but throws an assertion if the input fd is + * invalid. Closing an invalid fd is a programming error, so + * it's better to catch it early. + */ +static inline int +nm_close (int fd) +{ + int r; + + r = close (fd); + nm_assert (r != -1 || fd < 0 || errno != EBADF); + return r; +} + +#endif /* __NM_MACROS_INTERNAL_H__ */ diff --git a/src/backend/dbusadaptor.cpp b/src/backend/dbusadaptor.cpp new file mode 100644 index 00000000..cb637e8e --- /dev/null +++ b/src/backend/dbusadaptor.cpp @@ -0,0 +1,250 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp com.kylin.weather.xml -a dbusadaptor -c DbusAdaptor -l MainWindow + * + * qdbusxml2cpp is Copyright (C) 2015 The Qt Company Ltd. + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#include "dbusadaptor.h" +#include +#include +#include +#include +#include +#include +#include + +const QByteArray GSETTINGS_SCHEMA_KYLIN_NM = "org.ukui.kylin-nm.switch"; +const QString KEY_WIRELESS_SWITCH = "wirelessswitch"; +const QString KEY_WIRED_SWITCH = "wiredswitch"; + +/* + * Implementation of adaptor class DbusAdaptor + */ + +DbusAdaptor::DbusAdaptor(MainWindow *parent) + : QDBusAbstractAdaptor(parent) +{ + // constructor + qDBusRegisterMetaType >(); + qDBusRegisterMetaType >(); + qDBusRegisterMetaType >(); + qDBusRegisterMetaType >>(); + //setAutoRelaySignals(true)后会自动转发mainwindow发出的同名信号,因此不必再额外写一个转发 + setAutoRelaySignals(true); +} + +DbusAdaptor::~DbusAdaptor() +{ + // destructor +} + +//无线列表 +QMap > DbusAdaptor::getWirelessList() +{ + QMap > map; + parent()->getWirelessList(map); + return map; +} + +//有线列表 +QMap> DbusAdaptor::getWiredList() +{ + QMap> map; + parent()->getWiredList(map); + return map; +} + +//有线开关 +void DbusAdaptor::setWiredSwitchEnable(bool enable) +{ + //todo mainwindow调用backend 对开关 打开/关闭 + if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_KYLIN_NM)) { + QGSettings *gsetting = new QGSettings(GSETTINGS_SCHEMA_KYLIN_NM); + if (gsetting->get(KEY_WIRED_SWITCH).toBool() != enable) { + gsetting->set(KEY_WIRED_SWITCH, enable); + } + delete gsetting; + gsetting = nullptr; + } else { + qDebug()<<"isSchemaInstalled false"; + } +} + +//无线开关 +void DbusAdaptor::setWirelessSwitchEnable(bool enable) +{ + //todo mainwindow调用backend 对开关 打开/关闭 +// if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_KYLIN_NM)) { +// QGSettings *gsetting = new QGSettings(GSETTINGS_SCHEMA_KYLIN_NM); +// if (gsetting->get(KEY_WIRELESS_SWITCH).toBool() != enable) { +// gsetting->set(KEY_WIRELESS_SWITCH, enable); +// } +// delete gsetting; +// gsetting = nullptr; +// } else { +// qDebug()<<"isSchemaInstalled false"; +// } + parent()->setWirelessSwitchEnable(enable); +} + +//启用/禁用网卡 +void DbusAdaptor::setDeviceEnable(QString devName, bool enable) +{ + parent()->setWiredDeviceEnable(devName, enable); + saveDeviceEnableState(devName, enable); +} + +//设置默认网卡 +//void DbusAdaptor::setDefaultWiredDevice(QString deviceName) +//{ +// if (!checkDeviceExist(WIRED, deviceName)) { +// return; +// } +// setDefaultDevice(WIRED, deviceName); +// parent()->setWiredDefaultDevice(deviceName); +// return; +//} + +//QString DbusAdaptor::getDefaultWiredDevice() +//{ +// QSettings * m_settings = new QSettings(CONFIG_FILE_PATH, QSettings::IniFormat); +// m_settings->beginGroup("DEFAULTCARD"); +// QString key("wired"); +// QString deviceName = m_settings->value(key, "").toString(); +// m_settings->endGroup(); +// delete m_settings; +// m_settings = nullptr; +// return deviceName; +//} + +//void DbusAdaptor::setDefaultWirelessDevice(QString deviceName) +//{ +// if (!checkDeviceExist(WIRED, deviceName)) { +// return; +// } +// setDefaultDevice(WIRELESS, deviceName); +// parent()->setWirelessDefaultDevice(deviceName); +// return; +//} + +//QString DbusAdaptor::getDefaultWirelessDevice() +//{ +// QSettings * m_settings = new QSettings(CONFIG_FILE_PATH, QSettings::IniFormat); +// m_settings->beginGroup("DEFAULTCARD"); +// QString key("wireless"); +// QString deviceName = m_settings->value(key, "").toString(); +// m_settings->endGroup(); +// delete m_settings; +// m_settings = nullptr; +// return deviceName; +//} + +//连接 根据网卡类型 参数1 0:lan 1:wlan 参数3 为ssid/uuid +void DbusAdaptor::activateConnect(int type, QString devName, QString ssid) +{ + if (type == WIRED) { + parent()->activateWired(devName,ssid); + } else if (type == WIRELESS) { + parent()->activateWireless(devName,ssid); + } else { + qDebug() << "[DbusAdaptor] activateConnect type is invalid"; + } +} + +//断开连接 根据网卡类型 参数1 0:lan 1:wlan 参数3 为ssid/uuid +void DbusAdaptor::deActivateConnect(int type, QString devName, QString ssid) +{ + if (type == WIRED) { + qDebug() << "deactivateWired"; + parent()->deactivateWired(devName,ssid); + } else if (type == WIRELESS) { + parent()->deactivateWireless(devName,ssid); + } else { + qDebug() << "[DbusAdaptor] deactivateConnect type is invalid"; + } +} + +//获取设备列表和启用/禁用状态 +QMap DbusAdaptor::getDeviceListAndEnabled(int devType) +{ + QMap map; + map.clear(); + getDeviceEnableState(devType, map); + return map; +} + +//获取无线设备能力 +QMap DbusAdaptor::getWirelessDeviceCap() +{ + QMap map; + parent()->getWirelessDeviceCap(map); + return map; +} + +//唤起属性页 根据网卡类型 参数2 为ssid/uuid +void DbusAdaptor::showPropertyWidget(QString devName, QString ssid) +{ + qDebug() << "showPropertyWidget"; + parent()->showPropertyWidget(devName,ssid); +} + +//唤起新建有线连接界面 +void DbusAdaptor::showCreateWiredConnectWidget(QString devName) +{ + qDebug() << "showCreateWiredConnectWidget"; + parent()->showCreateWiredConnectWidget(devName); +} + +//开启热点 +void DbusAdaptor::activeWirelessAp(const QString apName, const QString apPassword, const QString band, const QString apDevice) +{ + parent()->activeWirelessAp(apName, apPassword, band, apDevice); +} + +//断开热点 +void DbusAdaptor::deactiveWirelessAp(const QString apName, const QString uuid) +{ + parent()->deactiveWirelessAp(apName, uuid); +} + +//获取热点 +QStringList DbusAdaptor::getStoredApInfo() +{ + QStringList list; + list.clear(); + parent()->getStoredApInfo(list); + return list; +} + +QStringList DbusAdaptor::getApInfoBySsid(QString devName, QString ssid) +{ + QStringList list; + list.clear(); + parent()->getApInfoBySsid(devName, ssid, list); + return list; +} + +void DbusAdaptor::showKylinNM(int type) +{ + parent()->onShowMainWindow(type); +} + +//扫描 +void DbusAdaptor::reScan() +{ + parent()->rescan(); +} + +void DbusAdaptor::keyRingInit() +{ + parent()->keyRingInit(); +} + +void DbusAdaptor::keyRingClear() +{ + parent()->keyRingClear(); +} diff --git a/src/backend/dbusadaptor.h b/src/backend/dbusadaptor.h new file mode 100644 index 00000000..439851f9 --- /dev/null +++ b/src/backend/dbusadaptor.h @@ -0,0 +1,120 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp com.kylin.weather.xml -a dbusadaptor -c DbusAdaptor -l MainWindow + * + * qdbusxml2cpp is Copyright (C) 2015 The Qt Company Ltd. + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#ifndef DBUSADAPTOR_H +#define DBUSADAPTOR_H + +#include +#include +#include + +#include "tabpage.h" +#include "../dbus-interface/kylinnetworkdeviceresource.h" +QT_BEGIN_NAMESPACE +class QByteArray; +//template class QList; +template class QMap; +class QString; +class QStringList; +class QVariant; +template class QVector; +QT_END_NAMESPACE + +/* + * Adaptor class for interface com.kylin.weather + */ + +#include "mainwindow.h" + +class DbusAdaptor: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.kylin.network") +public: + DbusAdaptor(MainWindow *parent); + virtual ~DbusAdaptor(); + + inline MainWindow *parent() const + { return static_cast(QObject::parent()); } + +public: // PROPERTIES +public Q_SLOTS: // METHODS + //无线列表 + QMap > getWirelessList(); + //有线列表 + QMap> getWiredList(); + //有线总开关 + Q_NOREPLY void setWiredSwitchEnable(bool enable); + //无线总开关 + Q_NOREPLY void setWirelessSwitchEnable(bool enable); + //有线网卡开关 + Q_NOREPLY void setDeviceEnable(QString devName, bool enable); + //设置默认网卡 +// Q_NOREPLY void setDefaultWiredDevice(QString deviceName); +// QString getDefaultWiredDevice(); +// Q_NOREPLY void setDefaultWirelessDevice(QString deviceName); +// QString getDefaultWirelessDevice(); + //连接 根据网卡类型 参数1 0:lan 1:wlan 参数3 为ssid/uuid + Q_NOREPLY void activateConnect(int type, QString devName, QString ssid); + //断开连接 根据网卡类型 参数1 0:lan 1:wlan 参数3 为ssid/uuid + Q_NOREPLY void deActivateConnect(int type, QString devName, QString ssid); + //获取设备列表和启用/禁用状态 + QMap getDeviceListAndEnabled(int devType); + //获取无线设备能力 + QMap getWirelessDeviceCap(); + //唤起属性页 根据网卡类型 参数2 为ssid/uuid + Q_NOREPLY void showPropertyWidget(QString devName, QString ssid); + //唤起新建有线连接界面 + Q_NOREPLY void showCreateWiredConnectWidget(QString devName); + //开启热点 + void activeWirelessAp(const QString apName, const QString apPassword, const QString band, const QString apDevice); + //断开热点 + void deactiveWirelessAp(const QString apName, const QString uuid); + //获取热点 + QStringList getStoredApInfo(); + QStringList getApInfoBySsid(QString devName, QString ssid); + //wifi扫描 + void reScan(); + //keyring + void keyRingInit(); + void keyRingClear(); + //just show + void showKylinNM(int type); +Q_SIGNALS: // SIGNALS +// void wirelessActivating(QString devName, QString ssid); +// void wiredActivating(QString devName, QString ssid); + void lanAdd(QString devName, QStringList info); + void lanRemove(QString dbusPath); + void lanUpdate(QString devName, QStringList info); + void wlanAdd(QString devName, QStringList info); + void wlanRemove(QString devName,QString ssid); + void wlanactiveConnectionStateChanged(QString devName, QString ssid, QString uuid, int status); + void lanActiveConnectionStateChanged(QString devName, QString uuid, int status); + //仅失败,若成功直接发listUpdate + void activateFailed(QString errorMessage); + void deactivateFailed(QString errorMessage); + //设备插拔 + void deviceStatusChanged(); + void deviceNameChanged(QString oldName, QString newName, int type); + //热点断开 + void hotspotDeactivated(QString devName, QString ssid); + //热点连接 + void hotspotActivated(QString devName, QString ssid, QString uuid); + + //信号强度变化 + void signalStrengthChange(QString devName, QString ssid, int strength); + //安全性变化 + void secuTypeChange(QString devName, QString ssid, QString secuType); + //列表排序 + void timeToUpdate(); +}; + +#endif diff --git a/src/backend/hotspot/dlghotspotcreate.cpp b/src/backend/hotspot/dlghotspotcreate.cpp new file mode 100644 index 00000000..afeb5b9d --- /dev/null +++ b/src/backend/hotspot/dlghotspotcreate.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setStyleSheet("background-color:white;"); + + ui->lbLeftup->setStyleSheet("QLabel{background-color:#266ab5;}"); + ui->lbLeftupIcon->setStyleSheet("QLabel{background-image:url(:/res/h/no-pwd-wifi.png);background-color:transparent;}"); + ui->lbLeftupTitle->setStyleSheet("QLabel{font-size:12px;color:#ffffff;background-color:transparent;}"); + ui->btnCancel->setStyleSheet("QPushButton{border:1px solid #aaaaaa;background-color:#f5f5f5;}" + "QPushButton:Hover{border:2px solid #629ee8;background-color:#eeeeee;}" + "QPushButton:Pressed{border:1px solid #aaaaaa;background-color:#d8d8d8;}"); + ui->btnOk->setStyleSheet("QPushButton{border:1px solid #aaaaaa;background-color:#f5f5f5;}" + "QPushButton:Hover{border:2px solid #629ee8;background-color:#eeeeee;}" + "QPushButton:Pressed{border:1px solid #aaaaaa;background-color:#d8d8d8;}"); + ui->checkBoxPwd->setStyleSheet("QCheckBox::indicator {width: 18px; height: 9px;}" + "QCheckBox::indicator:checked {image: url(:/res/h/show-pwd.png);}" + "QCheckBox::indicator:unchecked {image: url(:/res/h/hide-pwd.png);}"); + + ui->lbLeftupTitle->setText(tr("Create Hotspot")); //创建个人热点 + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("WLAN security")); //WLAN 安全性: + ui->lbPassword->setText(tr("Password")); //密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnOk->setText(tr("Ok")); //确定 + ui->btnOk->setEnabled(false); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA and WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->setCurrentIndex(1); + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialog())); +} + +DlgHotspotCreate::~DlgHotspotCreate() +{ + delete ui; +} + +void DlgHotspotCreate::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgHotspotCreate::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; + this->setWindowOpacity(1); +} +void DlgHotspotCreate::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + this->setWindowOpacity(0.9); + event->accept(); + } +} + +void DlgHotspotCreate::on_btnCancel_clicked() +{ + this->close(); + emit btnHotspotState(); +} + +void DlgHotspotCreate::on_btnOk_clicked() +{ + //nmcli device wifi hotspot [ifname ifname] [con-name name] [ssid SSID] [band {a | bg}] [channel channel] [password password] + //example: nmcli device wifi hotspot ifname wlan0 con-name MyHostspot ssid MyHostspotSSID password 12345678 + QString str; + if(ui->cbxSecurity->currentIndex() == 0 ){ + str = "nmcli device wifi hotspot ifname '" + wirelessCardName + "' con-name '" + ui->leNetName->text() + "' ssid '" + ui->leNetName->text() + "' SSID"; + }else{ + str = "nmcli device wifi hotspot ifname '" + wirelessCardName + "' con-name '" + ui->leNetName->text() + "' ssid '" + ui->leNetName->text() + "' password '" + ui->lePassword->text() + "'"; + } + Utils::m_system(str.toUtf8().data()); + + this->close(); + emit updateHotspotList(); +} + +void DlgHotspotCreate::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword ->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void DlgHotspotCreate::on_leNetName_textEdited(const QString &arg1) +{ + if(ui->cbxSecurity->currentIndex() == 0 ){ + if (ui->leNetName->text() == ""){ + ui->btnOk->setEnabled(false); + } else { + ui->btnOk->setEnabled(true); + } + }else{ + if (ui->leNetName->text() == "" || ui->lePassword->text().size() < 5){ + ui->btnOk->setEnabled(false); + } else { + ui->btnOk->setEnabled(true); + } + } +} + +void DlgHotspotCreate::on_lePassword_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == "" || ui->lePassword->text().size() < 5){ + ui->btnOk->setEnabled(false); + } else { + ui->btnOk->setEnabled(true); + } +} + +void DlgHotspotCreate::changeDialog() +{ + if(ui->cbxSecurity->currentIndex()==0){ + ui->lbPassword->setEnabled(false); + ui->lePassword->setEnabled(false); + ui->checkBoxPwd->setEnabled(false); + } else { + ui->lbPassword->setEnabled(true); + ui->lePassword->setEnabled(true); + ui->checkBoxPwd->setEnabled(true); + } +} diff --git a/src/backend/hotspot/dlghotspotcreate.h b/src/backend/hotspot/dlghotspotcreate.h new file mode 100644 index 00000000..ee0ec0d2 --- /dev/null +++ b/src/backend/hotspot/dlghotspotcreate.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include + +namespace Ui { +class DlgHotspotCreate; +} + +class DlgHotspotCreate : public QDialog +{ + Q_OBJECT + +public: + explicit DlgHotspotCreate(QString wiFiCardName, QWidget *parent = nullptr); + ~DlgHotspotCreate(); + +public slots: + void changeDialog(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnOk_clicked(); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_lePassword_textEdited(const QString &arg1); + +private: + Ui::DlgHotspotCreate *ui; + + QString wirelessCardName; + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + bool isPress; + QPoint winPos; + QPoint dragPos; + +signals: + void updateHotspotList(); + void btnHotspotState(); +}; + +#endif // DLGHOTSPOTCREATE_H diff --git a/src/backend/hotspot/dlghotspotcreate.ui b/src/backend/hotspot/dlghotspotcreate.ui new file mode 100644 index 00000000..989108f6 --- /dev/null +++ b/src/backend/hotspot/dlghotspotcreate.ui @@ -0,0 +1,169 @@ + + + DlgHotspotCreate + + + + 0 + 0 + 432 + 250 + + + + Dialog + + + + + 0 + 0 + 120 + 32 + + + + + + + + + + 9 + 9 + 16 + 16 + + + + + + + + + + 34 + 6 + 80 + 20 + + + + + + + + + + 215 + 210 + 90 + 30 + + + + + + + + + + 315 + 210 + 90 + 30 + + + + + + + + + + 76 + 60 + 90 + 20 + + + + + + + + + + 175 + 55 + 200 + 32 + + + + + + + 76 + 105 + 90 + 20 + + + + + + + + + + 175 + 100 + 200 + 32 + + + + + + + 76 + 150 + 90 + 20 + + + + + + + + + + 175 + 145 + 200 + 32 + + + + QLineEdit::Password + + + + + + 350 + 157 + 18 + 9 + + + + + + + + + + diff --git a/src/backend/hotspot/hotspot.pri b/src/backend/hotspot/hotspot.pri new file mode 100644 index 00000000..76750347 --- /dev/null +++ b/src/backend/hotspot/hotspot.pri @@ -0,0 +1,11 @@ +INCLUDEPATH += $$PWD + +FORMS += \ + $$PWD/dlghotspotcreate.ui + +HEADERS += \ + $$PWD/dlghotspotcreate.h + +SOURCES += \ + $$PWD/dlghotspotcreate.cpp + diff --git a/src/backend/kylinarping.h b/src/backend/kylinarping.h new file mode 100644 index 00000000..79346955 --- /dev/null +++ b/src/backend/kylinarping.h @@ -0,0 +1,101 @@ +#ifndef KYLINARPING_H +#define KYLINARPING_H + +#include +#include +#include + +static cap_flag_value_t cap_raw = CAP_CLEAR; +static const cap_value_t caps[] = { CAP_NET_RAW }; + +static void limit_capabilities() +{ + cap_t cap_p; + + cap_p = cap_get_proc(); + if (!cap_p) { + qWarning()<<"cap_get_proc failed."; + } + + cap_get_flag(cap_p, CAP_NET_RAW, CAP_PERMITTED, &cap_raw); + + if (cap_raw != CAP_CLEAR) { + if (cap_clear(cap_p) < 0) { + qWarning()<<"cap clear failed"< +#include +#include + +#define FINAL_PACKS 2 + +__attribute__((const)) static inline size_t sll_len(const size_t halen) +{ + struct sockaddr_ll unused; + const size_t len = offsetof(struct sockaddr_ll, sll_addr) + halen; + + if (len < sizeof(unused)) + return sizeof(unused); + + return len; +} + + +KyIpv4Arping::KyIpv4Arping(QString ifaceName, QString ipAddress, int retryCount, int timeout, QObject *parent) : QObject(parent) +{ + m_ifaceName = ifaceName; + m_ipv4Address = ipAddress; + m_retryCount = retryCount; + m_timeout = timeout; + + memset(&m_srcAddress, 0, sizeof(m_srcAddress)); +} + +KyIpv4Arping::~KyIpv4Arping() +{ + +} + +void KyIpv4Arping::monoGetTime (struct timespec *ts) +{ +#ifdef CLOCK_MONOTONIC + if (clock_gettime (CLOCK_MONOTONIC, ts)) +#endif + { + static long freq = 0; + if (freq == 0) + freq = sysconf (_SC_CLK_TCK); + + struct tms dummy; + clock_t t = times (&dummy); + ts->tv_sec = t / freq; + ts->tv_nsec = (t % freq) * (1000000000 / freq); + } +} + +void KyIpv4Arping::saveMacAddress(const uint8_t *ptr, size_t len) +{ + int index; + char macAddress[64] = {0}; + + for (index = 0; index < len; index++) { + snprintf(&macAddress[strlen(macAddress)], sizeof(macAddress) - strlen(macAddress), "%02X", ptr[index]); + if (index != len - 1) { + snprintf(&macAddress[strlen(macAddress)], sizeof(macAddress) - strlen(macAddress), "%s", ":"); + } + } + + m_conflictMac = QString(macAddress); + return ; +} + +void KyIpv4Arping::setConflictFlag(bool isConflict) +{ + m_ipConflict = isConflict; +} + +int KyIpv4Arping::sendIpv4Packet() +{ + int err; + unsigned char buf[256]; + struct arphdr *ah = (struct arphdr *)buf; + unsigned char *p = (unsigned char *)(ah + 1); + struct sockaddr_ll *p_me = (struct sockaddr_ll *)&(m_me); + struct sockaddr_ll *p_he = (struct sockaddr_ll *)&(m_he); + + ah->ar_hrd = htons(p_me->sll_hatype); + if (ah->ar_hrd == htons(ARPHRD_FDDI)) + ah->ar_hrd = htons(ARPHRD_ETHER); + ah->ar_pro = htons(ETH_P_IP); + ah->ar_hln = p_me->sll_halen; + ah->ar_pln = 4; + ah->ar_op = htons(ARPOP_REQUEST); + + memcpy(p, &p_me->sll_addr, ah->ar_hln); + p += p_me->sll_halen; + + qWarning()<<"[KyIpv4Arping]" <<"m_src address:" << inet_ntoa(m_srcAddress); + memcpy(p, &m_srcAddress, 4); + p += 4; + + memcpy(p, &p_he->sll_addr, ah->ar_hln); + p += ah->ar_hln; + + memcpy(p, &m_destAddress, 4); + p += 4; + + err = sendto(m_ipv4Socket, buf, p - buf, 0, (struct sockaddr *)p_he, sll_len(ah->ar_hln)); + + return err; +} + +int KyIpv4Arping::ipv4PacketProcess(unsigned char *buf, ssize_t len, struct sockaddr_ll *from) +{ + struct arphdr *ah = (struct arphdr *)buf; + unsigned char *p = (unsigned char *)(ah + 1); + struct in_addr src_ip, dst_ip; + + /* Filter out wild packets */ + if (from->sll_pkttype != PACKET_HOST && + from->sll_pkttype != PACKET_BROADCAST && + from->sll_pkttype != PACKET_MULTICAST) + return 0; + + /* Only these types are recognised */ + if (ah->ar_op != htons(ARPOP_REQUEST) && + ah->ar_op != htons(ARPOP_REPLY)) + return 0; + + /* ARPHRD check and this darned FDDI hack here :-( */ + if (ah->ar_hrd != htons(from->sll_hatype) && + (from->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER))) + return 0; + + /* Protocol must be IP. */ + if (ah->ar_pro != htons(ETH_P_IP)) + return 0; + if (ah->ar_pln != 4) + return 0; + if (ah->ar_hln != ((struct sockaddr_ll *)&m_me)->sll_halen) + return 0; + + if (len < (ssize_t) sizeof(*ah) + 2 * (4 + ah->ar_hln)) + return 0; + + memcpy(&src_ip, p + ah->ar_hln, 4); + memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4); + /* + * DAD packet was: + * src_ip = 0 (or some src) + * src_hw = ME + * dst_ip = tested address + * dst_hw = + * + * We fail, if receive request/reply with: + * src_ip = tested_address + * src_hw != ME + * if src_ip in request was not zero, check + * also that it matches to dst_ip, otherwise + * dst_ip/dst_hw do not matter. + */ + if (src_ip.s_addr != m_destAddress.s_addr) + return 0; + + if (memcmp(p, ((struct sockaddr_ll *)&m_me)->sll_addr, + ((struct sockaddr_ll *)&m_me)->sll_halen) == 0) + return 0; + + saveMacAddress(p, ah->ar_hln); + setConflictFlag(true); + + return FINAL_PACKS; +} + +int KyIpv4Arping::ipv4EventLoop() +{ + int rc = -1; + int ret; + int index; + int exit_loop = 0; + ssize_t bytes; + + int tfd; + struct pollfd pfds[POLLFD_COUNT]; + + struct itimerspec timerfd_vals; + timerfd_vals.it_interval.tv_sec = m_timeout, + timerfd_vals.it_interval.tv_nsec = 0, + timerfd_vals.it_value.tv_sec = m_timeout, + timerfd_vals.it_value.tv_nsec = 0; + + uint64_t exp; + uint64_t total_expires = 1; + + unsigned char packet[4096]; + struct sockaddr_storage from = { 0 }; + socklen_t addr_len = sizeof(from); + + /* timerfd */ + tfd = timerfd_create(CLOCK_MONOTONIC, 0); + if (tfd == -1) { + qWarning()<<"[KyIpv4Arping]" << "timerfd_create failed, errno" << errno; + return -1; + } + + if (timerfd_settime(tfd, 0, &timerfd_vals, NULL)) { + close(tfd); + qWarning()<<"[KyIpv4Arping]" << "timerfd_settime failed, errno" << errno; + return -1; + } + + pfds[POLLFD_TIMER].fd = tfd; + pfds[POLLFD_TIMER].events = POLLIN | POLLERR | POLLHUP; + + /* socket */ + pfds[POLLFD_SOCKET].fd = m_ipv4Socket; + pfds[POLLFD_SOCKET].events = POLLIN | POLLERR | POLLHUP; + sendIpv4Packet(); + + while (!exit_loop) { + ret = poll(pfds, POLLFD_COUNT, -1); + if (ret <= 0) { + if (errno == EAGAIN) { + continue; + } + + if (errno) { + qWarning()<<"[KyIpv4Arping]" <<"poll failed, errno" << errno; + } + + exit_loop = 1; + continue; + } + + for (index = 0; index < POLLFD_COUNT; index++) { + if (pfds[index].revents == 0) { + continue; + } + + switch (index) { + case POLLFD_TIMER: + bytes = read(tfd, &exp, sizeof(uint64_t)); + if (bytes != sizeof(uint64_t)) { + qWarning() <<"[KyIpv4Arping]" << "could not read timerfd"; + continue; + } + + total_expires += exp; + if (0 < m_retryCount && (uint64_t)m_retryCount < total_expires) { + exit_loop = 1; + rc = 0; + continue; + } + + sendIpv4Packet(); + break; + case POLLFD_SOCKET: + bytes = recvfrom(m_ipv4Socket, packet, sizeof(packet), 0, + (struct sockaddr *)&from, &addr_len); + if (bytes < 0) { + qWarning()<<"[KyIpv4Arping]" << "recvfrom function failed, errno" << errno; + continue; + } + if (ipv4PacketProcess(packet, bytes, (struct sockaddr_ll *)&from) == FINAL_PACKS) { + exit_loop = 1; + rc = 0; + } + break; + default: + qWarning()<<"[KyIpv4Arping]" <<"the fd index is undefine" << index; + break; + } + } + } + + close(tfd); + freeifaddrs(m_ifa0); + + return rc; +} + +int KyIpv4Arping::checkIfflags(unsigned int ifflags) +{ + if (!(ifflags & IFF_UP)) { + qWarning()<<"[KyIpv4Arping]" <<"the iface" << m_ifaceName <<" is down."; + return -1; + } + + if (ifflags & (IFF_NOARP | IFF_LOOPBACK)) { + qWarning()<<"[KyIpv4Arping]" << "Interface" << m_ifaceName << "is not ARPable."; + return -1; + } + + return 0; +} + + +int KyIpv4Arping::checkDevice() +{ + int rc; + struct ifaddrs *ifa; + int n = 0; + + rc = getifaddrs(&m_ifa0); + if (rc < 0) { + qWarning()<<"[KyIpv4Arping]" <<"get iface address failed, erron"<< errno; + return -errno; + } + + for (ifa = m_ifa0; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) { + continue; + } + + if (ifa->ifa_addr->sa_family != AF_PACKET) { + continue; + } + + if (!m_ifaceName.isEmpty() + && (NULL != ifa->ifa_name) + && strcmp(ifa->ifa_name, m_ifaceName.toUtf8())) { + continue; + } + + if (checkIfflags(ifa->ifa_flags) < 0) { + continue; + } + + if (!((struct sockaddr_ll *)ifa->ifa_addr)->sll_halen) { + continue; + } + + if (!ifa->ifa_broadaddr) { + continue; + } + + m_ifa = ifa; + + if (n++) + break; + } + + if (n == 1 && m_ifa) { + m_ifindex = if_nametoindex(m_ifa->ifa_name); + if (!m_ifindex) { + qWarning()<<"[KyIpv4Arping]" <<"if_nametoindex is invalid"; + freeifaddrs(m_ifa0); + return -1; + } + m_ifaceName = m_ifa->ifa_name; + + return 0; + } + + freeifaddrs(m_ifa0); + return -1; +} + +void KyIpv4Arping::findBroadcastAddress() +{ + struct sockaddr_ll *he = (struct sockaddr_ll *)&(m_he); + + if (m_ifa) { + struct sockaddr_ll *sll = + (struct sockaddr_ll *)m_ifa->ifa_broadaddr; + + if (sll->sll_halen == he->sll_halen) { + memcpy(he->sll_addr, sll->sll_addr, he->sll_halen); + return; + } + } + + qWarning()<<"[KyIpv4Arping]" <<"using default broadcast address."; + + memset(he->sll_addr, -1, he->sll_halen); + + return; +} + +int KyIpv4Arping::ipv4ConflictCheck() +{ + limit_capabilities(); + + int ret = checkDevice(); + if (ret < 0) { + qWarning()<<"[KyIpv4Arping]"<<"the device is invalid" << m_ifaceName; + return -1; + } + + enable_capability_raw(); + m_ipv4Socket = socket(PF_PACKET, SOCK_DGRAM, 0); + disable_capability_raw(); + if (m_ipv4Socket < 0) { + qWarning()<<"[KyIpv4Arping]" << "create ipv4 socket failed, errno" << errno; + return -1; + } + + inet_aton(m_ipv4Address.toUtf8(), &m_destAddress); + m_destAddressFamily = AF_INET; + + ((struct sockaddr_ll *)&m_me)->sll_family = AF_PACKET; + ((struct sockaddr_ll *)&m_me)->sll_ifindex = m_ifindex; + ((struct sockaddr_ll *)&m_me)->sll_protocol = htons(ETH_P_ARP); + + ret = bind(m_ipv4Socket, (struct sockaddr *)&m_me, sizeof(m_me)); + if (ret < 0) { + qWarning()<<"[KyIpv4Arping]" <<"ipv4 socket bind failed, errno"<< errno; + close(m_ipv4Socket); + return -1; + } + + socklen_t alen = sizeof(m_me); + ret = getsockname(m_ipv4Socket, (struct sockaddr *)&m_me, &alen); + if (ret < 0) { + qWarning()<<"[KyIpv4Arping]" <<"ipv4 get socket name failed, errno" << errno; + close(m_ipv4Socket); + return -1; + } + + if (((struct sockaddr_ll *)&m_me)->sll_halen == 0) { + qWarning()<<"[KyIpv4Arping]" <<"the iface"<< m_ifaceName <<" is not suitable for arp"; + close(m_ipv4Socket); + return -1; + } + + m_he = m_me; + + findBroadcastAddress(); + + drop_capabilities(); + + ret = ipv4EventLoop(); + close(m_ipv4Socket); + + return ret; +} diff --git a/src/backend/kylinipv4arping.h b/src/backend/kylinipv4arping.h new file mode 100644 index 00000000..1cb8ea36 --- /dev/null +++ b/src/backend/kylinipv4arping.h @@ -0,0 +1,88 @@ +#ifndef KYLIN_IPV4_ARPING +#define KYLIN_IPV4_ARPING + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kylinarping.h" + +#include +#include +#include + +enum { + POLLFD_TIMER = 0, + POLLFD_SOCKET, + POLLFD_COUNT +}; + +class KyIpv4Arping : public QObject +{ + Q_OBJECT +public: + explicit KyIpv4Arping(QString ifaceName, QString ipAddress, int retryCount=3, int timeout=1, QObject *parent = nullptr); + ~KyIpv4Arping(); + +public: + bool ipv4IsConflict() { + return m_ipConflict; + } + + QString getMacAddress() { + return m_conflictMac; + } + + int ipv4ConflictCheck(); + +private: + void monoGetTime(struct timespec *ts); + void saveMacAddress(const uint8_t *ptr, size_t len); + void findBroadcastAddress(); + int checkDevice(); + int checkIfflags(unsigned int ifflags); + + int sendIpv4Packet(); + int ipv4EventLoop(); + int ipv4PacketProcess(unsigned char *buf, ssize_t len, struct sockaddr_ll *from); + + void setConflictFlag(bool isConflict); + +private: + struct in_addr m_srcAddress; + struct in_addr m_destAddress; + int m_destAddressFamily; + + struct sockaddr_storage m_me; + struct sockaddr_storage m_he; + int m_ipv4Socket = 0; + + QString m_ifaceName; + int m_ifindex; + struct ifaddrs *m_ifa; + struct ifaddrs *m_ifa0; + + QString m_ipv4Address; + int m_retryCount; + int m_timeout; + + bool m_ipConflict = false; + QString m_conflictMac = nullptr; +}; + +#endif diff --git a/src/backend/kylinipv6arping.cpp b/src/backend/kylinipv6arping.cpp new file mode 100644 index 00000000..79d2ee12 --- /dev/null +++ b/src/backend/kylinipv6arping.cpp @@ -0,0 +1,405 @@ +#include "kylinipv6arping.h" + +#include +#include +#include +#include "kylinarping.h" + +KyIpv6Arping::KyIpv6Arping(QString ifaceName, QString ipAddress, int retryCount, int timeout, QObject *parent) : QObject(parent) +{ + m_ifaceName = ifaceName; + m_ipv6Address = ipAddress; + m_retryCount = retryCount; + m_timeoutMs = timeout; + + m_ipv6Conflict = false; +} + +KyIpv6Arping::~KyIpv6Arping() +{ + +} + +void KyIpv6Arping::monoGetTime (struct timespec *ts) +{ +#ifdef CLOCK_MONOTONIC + if (clock_gettime (CLOCK_MONOTONIC, ts)) +#endif + { + static long freq = 0; + if (freq == 0) + freq = sysconf (_SC_CLK_TCK); + + struct tms dummy; + clock_t t = times (&dummy); + ts->tv_sec = t / freq; + ts->tv_nsec = (t % freq) * (1000000000 / freq); + } +} + +int KyIpv6Arping::getLocalMacAddress(const char *ifname, unsigned char *addr) +{ +# ifdef SIOCGIFHWADDR + struct ifreq req; + memset (&req, 0, sizeof (req)); + + if (((unsigned)strlen (ifname)) >= (unsigned)IFNAMSIZ) { + return -1; /* buffer overflow = local root */ + } + + strcpy (req.ifr_name, ifname); + + int fd = socket (AF_INET6, SOCK_DGRAM, 0); + if (fd == -1) { + return -1; + } + + if (ioctl (fd, SIOCGIFHWADDR, &req)) { + qWarning()<<"[KyIpv6Arping]"<<"get local mac address failed, errno" << errno; + close (fd); + return -1; + } + close (fd); + + memcpy (addr, req.ifr_hwaddr.sa_data, 6); + return 0; +# else + /* No SIOCGIFHWADDR, which seems Linux specific. */ + struct ifaddrs *ifa = NULL; + struct ifaddrs *ifp = NULL; + getifaddrs(&ifa); + ifp = ifa; /* preserve the address of ifa to free memory later */ + while (ifp != NULL) { + if (ifp->ifa_addr->sa_family == AF_LINK && strcmp(ifp->ifa_name, ifname) == 0) { + const struct sockaddr_dl* sdl = (const struct sockaddr_dl*) ifp->ifa_addr; + memcpy(addr, sdl->sdl_data + sdl->sdl_nlen, 6); + freeifaddrs(ifa); + return 0; + } + ifp = ifp->ifa_next; + } + freeifaddrs(ifa); + return -1; +# endif +} + +int KyIpv6Arping::getIpv6ByName(struct sockaddr_in6 *addr) +{ + struct addrinfo hints, *res; + memset (&hints, 0, sizeof (hints)); + hints.ai_family = PF_INET6; + hints.ai_socktype = SOCK_DGRAM; /* dummy */ + hints.ai_flags = 0; + + int val = getaddrinfo (m_ipv6Address.toUtf8(), NULL, &hints, &res); + if (val) { + qWarning()<<"[KyIpv6Arping]" << m_ipv6Address <<"get addrinfo failed, errno" << val; + return -1; + } + + memcpy (addr, res->ai_addr, sizeof (struct sockaddr_in6)); + freeaddrinfo (res); + + val = if_nametoindex (m_ifaceName.toUtf8()); + if (val == 0) { + qWarning()<<"[KyIpv6Arping]" <<"if_nametoindex failed, errno" << errno; + return -1; + } + addr->sin6_scope_id = val; + + return 0; +} + +int KyIpv6Arping::buildSolicitationPacket(solicit_packet *ns, struct sockaddr_in6 *tgt, const char *ifname) +{ + /* builds ICMPv6 Neighbor Solicitation packet */ + ns->hdr.nd_ns_type = ND_NEIGHBOR_SOLICIT; + ns->hdr.nd_ns_code = 0; + ns->hdr.nd_ns_cksum = 0; /* computed by the kernel */ + ns->hdr.nd_ns_reserved = 0; + memcpy (&ns->hdr.nd_ns_target, &tgt->sin6_addr, 16); + + /* determines actual multicast destination address */ + memcpy (&tgt->sin6_addr.s6_addr, "\xff\x02\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x01\xff", 13); + + /* gets our own interface's link-layer address (MAC) */ + if (getLocalMacAddress (m_ifaceName.toUtf8().constData(), ns->hw_addr)) { + return sizeof (ns->hdr); + } + + ns->opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR; + ns->opt.nd_opt_len = 1; /* 8 bytes */ + return sizeof (*ns); +} + +void KyIpv6Arping::saveMacAddress (const uint8_t *ptr, size_t len) +{ + int index; + char macAddress[64] = {0}; + + for (index = 0; index < len; index++) { + snprintf(&macAddress[strlen(macAddress)], sizeof(macAddress) - strlen(macAddress), "%02X", ptr[index]); + if (index != len - 1) { + snprintf(&macAddress[strlen(macAddress)], sizeof(macAddress) - strlen(macAddress), "%s", ":"); + } + } + + m_conflictMacAddress = QString(macAddress); + return ; +} + +int KyIpv6Arping::parseIpv6Packet(const uint8_t *buf, size_t len, const struct sockaddr_in6 *tgt) +{ + const struct nd_neighbor_advert *na = + (const struct nd_neighbor_advert *)buf; + const uint8_t *ptr; + + /* checks if the packet is a Neighbor Advertisement, and + * if the target IPv6 address is the right one */ + if ((len < sizeof (struct nd_neighbor_advert)) + || (na->nd_na_type != ND_NEIGHBOR_ADVERT) + || (na->nd_na_code != 0) + || memcmp (&na->nd_na_target, &tgt->sin6_addr, 16)) { + return -1; + } + len -= sizeof (struct nd_neighbor_advert); + + /* looks for Target Link-layer address option */ + ptr = buf + sizeof (struct nd_neighbor_advert); + + while (len >= 8) + { + uint16_t optlen; + + optlen = ((uint16_t)(ptr[1])) << 3; + if (optlen == 0) + break; /* invalid length */ + + if (len < optlen) /* length > remaining bytes */ + break; + len -= optlen; + + + /* skips unrecognized option */ + if (ptr[0] != ND_OPT_TARGET_LINKADDR) + { + ptr += optlen; + continue; + } + + /* Found! displays link-layer address */ + ptr += 2; + optlen -= 2; + + saveMacAddress (ptr, optlen); + setIpv6ConflictFlag(true); + return 0; + } + + return -1; +} + +int KyIpv6Arping::readIpv6Packet(void *buf, + size_t len, + int flags, + struct sockaddr_in6 *addr) +{ + char cbuf[CMSG_SPACE (sizeof (int))]; + + struct iovec iov = { + .iov_base = buf, + .iov_len = len + }; + + struct msghdr hdr = { + .msg_name = addr, + .msg_namelen = sizeof (*addr), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cbuf, + .msg_controllen = sizeof (cbuf) + }; + + ssize_t val = recvmsg (m_ipv6Socket, &hdr, flags); + if (val == -1) { + return val; + } + + /* ensures the hop limit is 255 */ + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&hdr); + cmsg != NULL; cmsg = CMSG_NXTHDR (&hdr, cmsg)) { + if ((cmsg->cmsg_level == IPPROTO_IPV6) + && (cmsg->cmsg_type == IPV6_HOPLIMIT)) { + if (255 != *(int *)CMSG_DATA (cmsg)) { + // pretend to be a spurious wake-up + errno = EAGAIN; + return -1; + } + } + } + + return val; +} + +int KyIpv6Arping::reciveAndProcessIpv6Packet(const struct sockaddr_in6 *tgt, unsigned wait_ms) +{ + struct timespec end; + struct pollfd fds; + + /* computes deadline time */ + monoGetTime (&end); + div_t d; + d = div (wait_ms, 1000); + end.tv_sec = end.tv_sec + d.quot; + end.tv_nsec = end.tv_nsec + (d.rem * 1000000); + + /* receive loop */ + for (;;) + { + /* waits for reply until deadline */ + struct timespec now; + int val = 0; + + monoGetTime (&now); + if (end.tv_sec >= now.tv_sec) { + val = (end.tv_sec - now.tv_sec) * 1000 + + (int)((end.tv_nsec - now.tv_nsec) / 1000000); + if (val < 0) { + val = 0; + } + } + + fds.fd = m_ipv6Socket; + fds.events = POLLIN; + val = poll (&fds, 1, val); + if (val < 0) + break; + + if (val == 0) + return 0; + + /* receives an ICMPv6 packet */ + // TODO: use interface MTU as buffer size + uint8_t buf[1460]; + struct sockaddr_in6 addr; + + val = readIpv6Packet(buf, sizeof (buf), MSG_DONTWAIT, &addr); + if (val < 0) { + if (errno != EAGAIN) + qWarning()<<"[KyIpv6Arping]"<<"Receiving ICMPv6 packet failed"; + continue; + } + + /* ensures the response came through the right interface */ + if (addr.sin6_scope_id + && (addr.sin6_scope_id != tgt->sin6_scope_id)) + continue; + + if (parseIpv6Packet(buf, val, tgt) == 0) { + return 1 /* = responses */; + } + } + + return -1; /* error */ +} + + +int KyIpv6Arping::ipv6ConflictCheck() +{ + struct sockaddr_in6 tgt; + struct icmp6_filter filter; + int retry = 0; + + limit_capabilities(); + + enable_capability_raw(); + m_ipv6Socket = socket (PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + disable_capability_raw(); + if (m_ipv6Socket < 0) { + qDebug()<<"[KyIpv6Arping]" <<"create ipv6 socket failed:"; + return -1; + } + + fcntl (m_ipv6Socket, F_SETFD, FD_CLOEXEC); + + /* set ICMPv6 filter */ + ICMP6_FILTER_SETBLOCKALL (&filter); + ICMP6_FILTER_SETPASS (ND_NEIGHBOR_ADVERT, &filter); + + enable_capability_raw(); + setsockopt (m_ipv6Socket, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof (filter)); + + int soDontRoute = 1; + setsockopt (m_ipv6Socket, SOL_SOCKET, SO_DONTROUTE, &soDontRoute, sizeof(soDontRoute)); + + /* sets Hop-by-hop limit to 255 */ + int multicastHopLimit = 255; + setsockopt (m_ipv6Socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &multicastHopLimit, sizeof (multicastHopLimit)); + + int unicastHops = 255; + setsockopt (m_ipv6Socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + &unicastHops, sizeof (unicastHops)); + + int recvHopLimit = 1; + setsockopt(m_ipv6Socket, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, + &recvHopLimit, sizeof (recvHopLimit)); + + disable_capability_raw(); + + drop_capabilities(); + + /* resolves target's IPv6 address */ + int ret = getIpv6ByName(&tgt); + if (ret < 0) { + qWarning()<<"[KyIpv6Arping]"<<"get ipv6 by name failed."; + close (m_ipv6Socket); + return ret; + } + + solicit_packet packet; + struct sockaddr_in6 dst; + ssize_t plen; + + memcpy (&dst, &tgt, sizeof (dst)); + plen = buildSolicitationPacket(&packet, &dst, m_ifaceName.toUtf8().constData()); + if (plen == -1) { + qWarning()<<"[KyIpv6Arping]"<<"build solicit packet failed."; + close (m_ipv6Socket); + return -1; + } + + while (retry < m_retryCount) { + /* sends a Solitication */ + if (sendto (m_ipv6Socket, &packet, plen, MSG_DONTROUTE, + (const struct sockaddr *)&dst, + sizeof (dst)) != plen) { + qWarning()<<"[KyIpv6Arping]"<<"Sending ICMPv6 packet failed."; + close (m_ipv6Socket); + return -1; + } + + retry++; + + /* receives an Advertisement */ + ssize_t val = reciveAndProcessIpv6Packet(&tgt, m_timeoutMs); + if (val > 0) { + close (m_ipv6Socket); + return 0; + } else if (val == 0) { + continue; + } else { + close (m_ipv6Socket); + return -1; + } + } + + close(m_ipv6Socket); + + if (retry == m_retryCount) { + return 0; + } else { + return -2; + } +} diff --git a/src/backend/kylinipv6arping.h b/src/backend/kylinipv6arping.h new file mode 100644 index 00000000..7f92c8ee --- /dev/null +++ b/src/backend/kylinipv6arping.h @@ -0,0 +1,109 @@ +#ifndef KYLINIPV6ARPING_H +#define KYLINIPV6ARPING_H + +//#include + +#include +#include +#include /* div() */ +#include /* uint8_t */ +#include /* UINT_MAX */ +#include +#include + +#include /* EMFILE */ +#include +#include /* close() */ +#include /* clock_gettime() */ +#include /* poll() */ +#include +#include +#include + +//#include "gettime.h" + +#ifdef HAVE_GETOPT_H +# include +#endif + +#include /* getaddrinfo() */ +#include /* inet_ntop() */ +#include /* if_nametoindex() */ + +#include +#include + +#ifndef IPV6_RECVHOPLIMIT +/* Using obsolete RFC 2292 instead of RFC 3542 */ +# define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT +#endif + +/* BSD-like systems define ND_RA_FLAG_HA instead of ND_RA_FLAG_HOME_AGENT */ +#ifndef ND_RA_FLAG_HOME_AGENT +# ifdef ND_RA_FLAG_HA +# define ND_RA_FLAG_HOME_AGENT ND_RA_FLAG_HA +# endif +#endif + +#ifndef AI_IDN +# define AI_IDN 0 +#endif + + +#include +#include +#include + +typedef struct +{ + struct nd_neighbor_solicit hdr; + struct nd_opt_hdr opt; + uint8_t hw_addr[6]; +} solicit_packet; + +class KyIpv6Arping: public QObject +{ + Q_OBJECT +public: + explicit KyIpv6Arping(QString ifaceName, QString ipAddress, int retryCount=3, int timeout=1000, QObject *parent = nullptr); + ~KyIpv6Arping(); + +public: + bool ipv6IsConflict() { + return m_ipv6Conflict; + } + + void setIpv6ConflictFlag(bool conflict) { + m_ipv6Conflict = conflict; + } + + QString getConflictMacAddress() { + return m_conflictMacAddress; + } + + int ipv6ConflictCheck(); + +private: + void monoGetTime (struct timespec *ts); + void saveMacAddress (const uint8_t *ptr, size_t len); + int getLocalMacAddress(const char *ifname, unsigned char *addr); + + int getIpv6ByName(struct sockaddr_in6 *addr); + int buildSolicitationPacket(solicit_packet *ns, struct sockaddr_in6 *tgt, const char *ifname); + int parseIpv6Packet(const uint8_t *buf, size_t len, const struct sockaddr_in6 *tgt); + int readIpv6Packet(void *buf, size_t len, int flags, struct sockaddr_in6 *addr); + int reciveAndProcessIpv6Packet(const struct sockaddr_in6 *tgt, unsigned wait_ms); + +private: + int m_ipv6Socket = 0; + + QString m_ifaceName; + QString m_ipv6Address; + int m_retryCount; + int m_timeoutMs; + + bool m_ipv6Conflict = false; + QString m_conflictMacAddress = nullptr; +}; + +#endif // KYLINIPV6ARPING_H diff --git a/src/backend/sysdbusregister.cpp b/src/backend/sysdbusregister.cpp new file mode 100644 index 00000000..fa1d5a38 --- /dev/null +++ b/src/backend/sysdbusregister.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include + +SysdbusRegister::SysdbusRegister() +{ +} + +SysdbusRegister::~SysdbusRegister() +{ +} + +void SysdbusRegister::systemRun(QString cmd){ + QProcess::execute(cmd); +} + +QStringList SysdbusRegister::getWifiInfo(QString wifiName) { + //返回名为wifiName的企业wifi的配置信息,包括eap,inner和用户列表 + QStringList wlist; + QString filename = "/usr/share/kylin-nm/wpaconn.conf"; + QFile file(filename); + if(!file.exists()) { + return wlist; + } + QSharedPointer autoSettings = QSharedPointer(new QSettings(filename, QSettings::IniFormat)); + wlist << autoSettings.get()->value("eap").toString(); + wlist << autoSettings.get()->value("inner").toString(); + int size = autoSettings.get()->beginReadArray(wifiName); + for (int i = 0; i < size; ++i) { + autoSettings.get()->setArrayIndex(i); + wlist << autoSettings.get()->value("user").toString(); + } + autoSettings.get()->endArray(); + return wlist; +} + +bool SysdbusRegister::appendWifiInfo(QString name, QString eap, QString inner, QString user) { + //向配置文件添加名为name的wifi配置,包括eap,inner和它的第一个用户 + QString filename = "/usr/share/kylin-nm/wpaconn.conf"; + QSharedPointer autoSettings = QSharedPointer(new QSettings(filename, QSettings::IniFormat)); + autoSettings.get()->beginGroup(name); + autoSettings.get()->setValue("eap", eap); + autoSettings.get()->setValue("inner", inner); + //以数组形式写入用户名 + autoSettings.get()->beginWriteArray(name); + autoSettings.get()->setArrayIndex(0); + autoSettings.get()->setValue("user", user); + autoSettings.get()->endArray(); + return true; +} + +bool SysdbusRegister::appendWifiUser(QString name, QString user) { + //向名为name的wifi用户列表添加名为user的用户名 + QString filename = "/usr/share/kylin-nm/wpaconn.conf"; + QSharedPointer autoSettings = QSharedPointer(new QSettings(filename, QSettings::IniFormat)); + autoSettings.get()->beginWriteArray(name); + autoSettings.get()->setArrayIndex(0); + //读到用户名列表长度并在队尾添加一个用户名 + int size = autoSettings.get()->beginReadArray(name); + autoSettings.get()->setArrayIndex(size); + autoSettings.get()->setValue("user", user); + autoSettings.get()->endArray(); + return true; +} diff --git a/src/backend/sysdbusregister.h b/src/backend/sysdbusregister.h new file mode 100644 index 00000000..942a54f3 --- /dev/null +++ b/src/backend/sysdbusregister.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include +#include + +#include + +class SysdbusRegister : public QObject +{ + Q_OBJECT + + Q_CLASSINFO("D-Bus Interface", "com.kylin.NetworkManager.interface") + +public: + explicit SysdbusRegister(); + ~SysdbusRegister(); + +private: +// QString m_name; + +signals: +// Q_SCRIPTABLE void nameChanged(QString); +// Q_SCRIPTABLE void computerinfo(QString); + +public slots: + + Q_SCRIPTABLE void systemRun(QString cmd); + Q_SCRIPTABLE QStringList getWifiInfo(QString wifiName); + Q_SCRIPTABLE bool appendWifiInfo(QString name, QString eap, QString inner, QString user); + Q_SCRIPTABLE bool appendWifiUser(QString name, QString user); + +}; + +#endif // SYSDBUSREGISTER_H diff --git a/src/backend/utils.cpp b/src/backend/utils.cpp new file mode 100644 index 00000000..8c2959b0 --- /dev/null +++ b/src/backend/utils.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include +#include +#include +#include + + +/////////////////////////////////////////////////////////////////////////////// +// The Utils class + +Utils::Utils(){} + +int Utils::m_system(char *cmd) +{ + int status = 0; + pid_t pid; + clock_t start,finish; + if ((pid = vfork()) <0) { + qDebug()<<"failed to create a subprocess by using vfork"; + status = -1; + } else if (pid==0) { + const char *new_argv[4]; + struct sigaction sa_cld; + sa_cld.sa_handler = SIG_DFL; + sa_cld.sa_flags = 0; + + // 在子进程中放开SIGINT信号 + sigemptyset(&sa_cld.sa_mask); + sigaction (SIGINT, &sa_cld, NULL); + sigaction (SIGQUIT, &sa_cld, NULL); + + new_argv[0] = "sh"; + new_argv[1] = "-c"; + new_argv[2] = cmd; + new_argv[3] = NULL; + + // execl("/bin/sh","sh","-c" ,cmd,(char *)0); + start = clock(); + if (execve("/bin/sh",(char *const *) new_argv, NULL) <0) { + qDebug()<<"failed to execve a shell command in function m_system"; + exit(1); + } else { + exit(0); + } + } else { + waitpid(pid,&status,0); + finish = clock(); + } + //double duration = (double)(finish-start)/CLOCKS_PER_SEC; + //qDebug()<<"It takes "< args; + args<<(tr("Kylin NM")) + <<((unsigned int) 0) + < +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* qt会将glib里的signals成员识别为宏,所以取消该宏 + * 后面如果用到signals时,使用Q_SIGNALS代替即可 + * +#ifdef signals +#undef signals +#endif + +extern "C" { +#include +#include +} + */ + + +/////////////////////////////////////////////////////////////////////////////// +// wifi property struct + typedef struct structWifiProperty{ + QString objectPath; + QString bssid; + int priority; +}structWifiProperty; + + +/////////////////////////////////////////////////////////////////////////////// +// The Utils class, used to do some assist function + +class Utils : public QObject +{ + Q_OBJECT +public: + Utils(); + + static int m_system(char *cmd); + +public slots: + void onRequestSendDesktopNotify(QString message); + +}; + + +/////////////////////////////////////////////////////////////////////////////// +// The UseQssFile class, set control style by using .qss file + +class UseQssFile +{ +public: + static void setStyle(const QString &style) + { + QString styleName = ":/qss/" + style; + + QFile qss(styleName); + qss.open(QFile::ReadOnly); + qApp->setStyleSheet(qss.readAll()); + qss.close(); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// The NetworkSpeed class, get the network upload and download speed + +class NetworkSpeed : public QObject +{ + Q_OBJECT +public: + explicit NetworkSpeed(QObject *parent = nullptr); + + int getCurrentDownloadRates(char *netname,long int * save_rate,long int * tx_rate); //获取当前的流量,参数为将获取到的流量保 +}; + +#endif // UTILS_H diff --git a/src/backend/wifi-auth-thread.cpp b/src/backend/wifi-auth-thread.cpp new file mode 100644 index 00000000..3ad12ca0 --- /dev/null +++ b/src/backend/wifi-auth-thread.cpp @@ -0,0 +1,53 @@ +#include "wifi-auth-thread.h" + +#include + +WifiAuthThread::WifiAuthThread() +{ + //检查连接状态 + naManager = new QNetworkAccessManager(this); +// QString url=QString("http://nmcheck.gnome.org/check_network_status.txt"); + QString url=QString("http://connectivity-check.ubuntu.com/"); + request.setUrl(QUrl(url)); + naManager->get(request); + //qDebug()<<"-------------------Network state check!----------------------"; + connect(naManager,&QNetworkAccessManager::finished,this,[=](QNetworkReply* reply){ + _isReply=true; + QNetworkReply::NetworkError err = reply->error(); + //qDebug()<<"----------------NetworkReply error:----------------------"<readAll().isEmpty()) + _isConnect=false; + } + else + _isConnect=false; + }); +} + +WifiAuthThread::~WifiAuthThread() +{ + delete naManager; +} + +void WifiAuthThread::run() +{ + sleep(3); + QDBusInterface interface( "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.NetworkManager", + QDBusConnection::systemBus() ); + QDBusReply reply= interface.call("CheckConnectivity"); + if (reply.isValid()) { + //qDebug()<<"---------------------wifi type:----------------------"< +#include +#include +#include +#include +#include +#include + +class WifiAuthThread:public QThread +{ +protected: + virtual void run(); + QNetworkRequest request; + QNetworkAccessManager* naManager=nullptr; + bool _isConnect=true;//是否已联网 + bool _isReply=false;//联网状态检查是否收到回复 + +public: + WifiAuthThread(); + ~WifiAuthThread(); +}; + +#endif //WIFI_AUTH_THREAD_H diff --git a/src/frontend/customstyle.cpp b/src/frontend/customstyle.cpp new file mode 100644 index 00000000..7c5c3397 --- /dev/null +++ b/src/frontend/customstyle.cpp @@ -0,0 +1,152 @@ +#include "customstyle.h" + +CustomStyle::CustomStyle(const QString &proxyStyleName) : QProxyStyle (proxyStyleName) +{ + +} + +QSize CustomStyle::sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const +{ + switch (type) { + case CT_TabBarTab: { + QSize size(0,40); + if (const QStyleOptionTab *tab= qstyleoption_cast(option)) { + switch (tab->shape) { + case QTabBar::RoundedNorth: + case QTabBar::RoundedSouth: { + return size + QSize(50,0); + break; + } + case QTabBar::RoundedWest: + case QTabBar::RoundedEast: { + return size + QSize(0,50); + break; + } + default: { + break; + } + } + } + return size; + break; + } + default: { + break; + } + } + return QProxyStyle::sizeFromContents(type, option, contentsSize, widget); +} +void CustomStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const +{ +// switch (element) { +// case CE_TabBarTab: { +// if (QStyle::State_Selected != option->state) { +// painter->save(); +// painter->setBrush(QColor(0,0,0,0)); +// painter->drawRect(widget->rect()); +//// painter->setOpacity(0.1); +// painter->restore(); +// } +// break; +// } +// default: { +// break; +// } +// } + return QProxyStyle::drawControl(element, option, painter, widget); +} + +//void CustomStyle::drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const +//{ +// return QProxyStyle::drawComplexControl(control, option, painter, widget); +//} + +//void CustomStyle::drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment, const QPixmap &pixmap) const +//{ +// return QProxyStyle::drawItemPixmap(painter, rectangle, alignment, pixmap); +//} + +//void CustomStyle::drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette, bool enabled, const QString &text, QPalette::ColorRole textRole) const +//{ +// return QProxyStyle::drawItemText(painter, rectangle, alignment, palette, enabled, text, textRole); +//} + +//void CustomStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const +//{ +// return QProxyStyle::drawPrimitive(element, option, painter, widget); +//} + +//QPixmap CustomStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *option) const +//{ +// return QProxyStyle::generatedIconPixmap(iconMode, pixmap, option); +//} + +//QStyle::SubControl CustomStyle::hitTestComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, const QPoint &position, const QWidget *widget) const +//{ +// return QProxyStyle::hitTestComplexControl(control, option, position, widget); +//} + +//QRect CustomStyle::itemPixmapRect(const QRect &rectangle, int alignment, const QPixmap &pixmap) const +//{ +// return QProxyStyle::itemPixmapRect(rectangle, alignment, pixmap); +//} + +//QRect CustomStyle::itemTextRect(const QFontMetrics &metrics, const QRect &rectangle, int alignment, bool enabled, const QString &text) const +//{ +// return QProxyStyle::itemTextRect(metrics, rectangle, alignment, enabled, text); +//} + +//int CustomStyle::pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const +//{ +// return QProxyStyle::pixelMetric(metric, option, widget); +//} + +//void CustomStyle::polish(QWidget *widget) +//{ +// return QProxyStyle::polish(widget); +//} + +//void CustomStyle::polish(QApplication *application) +//{ +// return QProxyStyle::polish(application); +//} + +//void CustomStyle::polish(QPalette &palette) +//{ +// return QProxyStyle::polish(palette); +//} + +//void CustomStyle::unpolish(QWidget *widget) +//{ +// return QProxyStyle::unpolish(widget); +//} + +//void CustomStyle::unpolish(QApplication *application) +//{ +// return QProxyStyle::unpolish(application); +//} + +//QIcon CustomStyle::standardIcon(QStyle::StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const +//{ +// return QProxyStyle::standardIcon(standardIcon, option, widget); +//} + +//QPalette CustomStyle::standardPalette() const +//{ +// return QProxyStyle::standardPalette(); +//} + +//int CustomStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const +//{ +// return QProxyStyle::styleHint(hint, option, widget, returnData); +//} + +//QRect CustomStyle::subControlRect(QStyle::ComplexControl control, const QStyleOptionComplex *option, QStyle::SubControl subControl, const QWidget *widget) const +//{ +// return QProxyStyle::subControlRect(control, option, subControl, widget); +//} + +//QRect CustomStyle::subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const +//{ +// return QProxyStyle::subElementRect(element, option, widget); +//} diff --git a/src/frontend/customstyle.h b/src/frontend/customstyle.h new file mode 100644 index 00000000..85245ccb --- /dev/null +++ b/src/frontend/customstyle.h @@ -0,0 +1,47 @@ +#ifndef CUSTOMSTYLE_H +#define CUSTOMSTYLE_H + +#include +#include +#include +#include +#include +#include +#include + +class CustomStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit CustomStyle(const QString &proxyStyleName = "windows"); + ~CustomStyle() = default; + virtual QSize sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &contentsSize, const QWidget *widget = nullptr) const; + virtual void drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const; +// virtual void drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = nullptr) const; + +// virtual void drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment, const QPixmap &pixmap) const; +// virtual void drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette, bool enabled, const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const; + +// virtual void drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const; +// virtual QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *option) const; +// virtual QStyle::SubControl hitTestComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, const QPoint &position, const QWidget *widget = nullptr) const; +// virtual QRect itemPixmapRect(const QRect &rectangle, int alignment, const QPixmap &pixmap) const; +// virtual QRect itemTextRect(const QFontMetrics &metrics, const QRect &rectangle, int alignment, bool enabled, const QString &text) const; +// //virtual int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption *option, const QWidget *widget); +// virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const; + +// virtual void polish(QWidget *widget); +// virtual void polish(QApplication *application); +// virtual void polish(QPalette &palette); +// virtual void unpolish(QWidget *widget); +// virtual void unpolish(QApplication *application); + +// virtual QIcon standardIcon(QStyle::StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const; +// virtual QPalette standardPalette() const; + +// virtual int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr, const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const; +// virtual QRect subControlRect(QStyle::ComplexControl control, const QStyleOptionComplex *option, QStyle::SubControl subControl, const QWidget *widget = nullptr) const; +// virtual QRect subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget = nullptr) const; +}; + +#endif // CUSTOMSTYLE_H diff --git a/src/frontend/enterprise-wlan/enterprise-wlan.pri b/src/frontend/enterprise-wlan/enterprise-wlan.pri new file mode 100644 index 00000000..c4571359 --- /dev/null +++ b/src/frontend/enterprise-wlan/enterprise-wlan.pri @@ -0,0 +1,9 @@ +INCLUDEPATH += $$PWD + +FORMS += \ + +HEADERS += \ + $$PWD/enterprisewlandialog.h \ + +SOURCES += \ + $$PWD/enterprisewlandialog.cpp \ diff --git a/src/frontend/enterprise-wlan/enterprisewlandialog.cpp b/src/frontend/enterprise-wlan/enterprisewlandialog.cpp new file mode 100644 index 00000000..30dd8941 --- /dev/null +++ b/src/frontend/enterprise-wlan/enterprisewlandialog.cpp @@ -0,0 +1,214 @@ +#include "enterprisewlandialog.h" +#include +#include +#include "xatom-helper.h" +#define MAIN_SIZE_EXPAND 400,500 +#define MAIN_SIZE_NARROW 400,400 + +#define THEME_SCHAME "org.ukui.style" +#define COLOR_THEME "styleName" + +EnterpriseWlanDialog::EnterpriseWlanDialog(KyWirelessNetItem &wirelessNetItem, QString device, QWidget *parent) : QWidget(parent) +{ + //设置窗口无边框,阴影 +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + MotifWmHints window_hints; + window_hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; + window_hints.functions = MWM_FUNC_ALL; + window_hints.decorations = MWM_DECOR_BORDER; + XAtomHelper::getInstance()->setWindowMotifHint(this->winId(), window_hints); +#else + this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); +#endif + this->setAttribute(Qt::WA_DeleteOnClose); + this->setWindowFlag(Qt::Window); + this->setWindowTitle(tr("Connect Enterprise WLAN")); + + m_wirelessNetItem = wirelessNetItem; + m_deviceName = device; + m_connectOperation = new KyWirelessConnectOperation(); + m_resource = new KyWirelessNetResource(); + + initUI(); + initData(); + centerToScreen(); +} + +EnterpriseWlanDialog::~EnterpriseWlanDialog() { + if (m_securityPage) { + delete m_securityPage; + m_securityPage = nullptr; + } + if (m_connectOperation) { + delete m_connectOperation; + m_connectOperation = nullptr; + } +} + +void EnterpriseWlanDialog::closeEvent(QCloseEvent *event) +{ + emit this->enterpriseWlanDialogClose(false); + return QWidget::closeEvent(event); +} + +void EnterpriseWlanDialog::initUI() +{ +#define MAIN_LAYOUT_MARGINS 8,8,8,8 +#define SSID_LAYOUT_MARGINS 8,8,8,0 +#define MAIN_LAYOUT_SPACING 0 +#define BUTTON_SPACING 8 + + m_mainLayout = new QVBoxLayout(this); + this->setLayout(m_mainLayout); + m_mainLayout->setContentsMargins(MAIN_LAYOUT_MARGINS); + m_mainLayout->setSpacing(MAIN_LAYOUT_SPACING); + + m_ssidLayout = new QHBoxLayout(); + m_ssidLayout->setContentsMargins(SSID_LAYOUT_MARGINS); + m_ssidTitleLabel = new QLabel(this); + m_ssidTitleLabel->setText("SSID"); + m_ssidLabel = new QLabel(this); + m_ssidLabel->setText(m_wirelessNetItem.m_NetSsid); + + m_ssidLayout->addWidget(m_ssidTitleLabel); + m_ssidLayout->addStretch(); + m_ssidLayout->addWidget(m_ssidLabel); + + m_securityPage = new SecurityPage(this); + m_securityPage->setSecurity(KySecuType::WPA_AND_WPA2_ENTERPRISE); + m_securityPage->setSecurityVisible(false); + + m_btnLayout = new QHBoxLayout(); + m_btnLayout->setSpacing(BUTTON_SPACING); + m_cancelBtn = new QPushButton(this); + m_connectBtn = new QPushButton(this); + m_cancelBtn->setText(tr("Cancel")); + m_connectBtn->setText(tr("Connect")); + m_connectBtn->setEnabled(false); + m_btnLayout->addStretch(); + m_btnLayout->addWidget(m_cancelBtn); + m_btnLayout->addWidget(m_connectBtn); + m_mainLayout->addLayout(m_ssidLayout); + m_mainLayout->addWidget(m_securityPage); + m_mainLayout->addLayout(m_btnLayout); + m_mainLayout->addStretch(); + + this->setFixedSize(MAIN_SIZE_EXPAND); + initConnections(); + onPaletteChanged(); +} + +void EnterpriseWlanDialog::centerToScreen() +{ + QDesktopWidget* m = QApplication::desktop(); + QRect desk_rect = m->screenGeometry(m->screenNumber(QCursor::pos())); + int desk_x = desk_rect.width(); + int desk_y = desk_rect.height(); + int x = this->width(); + int y = this->height(); + this->move(desk_x / 2 - x / 2 + desk_rect.left(), desk_y / 2 - y / 2 + desk_rect.top()); +} + +void EnterpriseWlanDialog::initConnections() +{ + connect(m_closeBtn, &QPushButton::clicked, this, &EnterpriseWlanDialog::close); + connect(m_cancelBtn, &QPushButton::clicked, this, &EnterpriseWlanDialog::close); + connect(m_connectBtn, &QPushButton::clicked, this, &EnterpriseWlanDialog::onBtnConnectClicked); + connect(m_securityPage, &SecurityPage::eapTypeChanged, this, &EnterpriseWlanDialog::onEapTypeChanged); + connect(m_securityPage, &SecurityPage::setSecuPageState, this, [ = ](bool status) { + m_connectBtn->setEnabled(status); + }); + + connect(qApp, &QApplication::paletteChanged, this, &EnterpriseWlanDialog::onPaletteChanged); +} + +void EnterpriseWlanDialog::onPaletteChanged() +{ + QPalette pal = qApp->palette(); + + QGSettings * styleGsettings = nullptr; + const QByteArray style_id(THEME_SCHAME); + if (QGSettings::isSchemaInstalled(style_id)) { + styleGsettings = new QGSettings(style_id); + QString currentTheme = styleGsettings->get(COLOR_THEME).toString(); + if(currentTheme == "ukui-default"){ + pal = lightPalette(this); + } + } + + this->setPalette(pal); + + setFramePalette(m_securityPage, pal); +} + +void EnterpriseWlanDialog::initData() +{ + if (m_wirelessNetItem.m_isConfigured) { + KyEapMethodType type; + m_connectOperation->getEnterpiseEapMethod(m_wirelessNetItem.m_connectUuid, type); + if (type) { + onEapTypeChanged(type); + } else { + qWarning() << "Get eap type failed!" << Q_FUNC_INFO << __LINE__; + } + } +} + +void EnterpriseWlanDialog::onBtnConnectClicked() +{ + qDebug() << "Clicked connectBtn, will connect enterprise WLAN, ssid = " + << m_wirelessNetItem.m_NetSsid << "." << Q_FUNC_INFO << __LINE__; + KySecuType secuType; + KyEapMethodType eapType; + m_securityPage->getSecuType(secuType, eapType); + + KyWirelessConnectSetting connetSetting; + connetSetting.setConnectName(m_wirelessNetItem.m_NetSsid); + connetSetting.setIfaceName(m_deviceName); + connetSetting.isAutoConnect = true; //ZJP_TODO 自动连接选项 + connetSetting.m_type = KyKeyMgmt::WpaEap; + connetSetting.m_ssid = m_wirelessNetItem.m_NetSsid; + connetSetting.m_secretFlag = 0; + connetSetting.dumpInfo(); + + if (eapType == KyEapMethodType::TLS) { + m_info.tlsInfo.devIfaceName = m_deviceName; + m_securityPage->updateTlsChange(m_info.tlsInfo); + m_connectOperation->addAndActiveWirelessEnterPriseTlsConnect(m_info.tlsInfo, connetSetting, m_deviceName, false); + } else if (eapType == KyEapMethodType::PEAP) { + m_securityPage->updatePeapChange(m_info.peapInfo); + m_connectOperation->addAndActiveWirelessEnterPrisePeapConnect(m_info.peapInfo, connetSetting, m_deviceName, false); + } else if (eapType == KyEapMethodType::TTLS) { + m_securityPage->updateTtlsChange(m_info.ttlsInfo); + m_connectOperation->addAndActiveWirelessEnterPriseTtlsConnect(m_info.ttlsInfo, connetSetting, m_deviceName, false); + } else { + qWarning() << "Connect enterprise wlan failed!(Unknown eap type)" << Q_FUNC_INFO << __LINE__; + } + close(); +} + +void EnterpriseWlanDialog::onEapTypeChanged(const KyEapMethodType &type) +{ + switch (type) { + case KyEapMethodType::TLS: + if (!m_wirelessNetItem.m_connectUuid.isEmpty()) { + m_resource->getEnterPriseInfoTls(m_wirelessNetItem.m_connectUuid, m_info.tlsInfo); + } + this->setFixedSize(MAIN_SIZE_EXPAND); + break; + case KyEapMethodType::PEAP: + if (m_wirelessNetItem.m_connectUuid.isEmpty()) { + m_resource->getEnterPriseInfoPeap(m_wirelessNetItem.m_connectUuid, m_info.peapInfo); + } + this->setFixedSize(MAIN_SIZE_NARROW); + break; + case KyEapMethodType::TTLS: + if (!m_wirelessNetItem.m_connectUuid.isEmpty()) { + m_resource->getEnterPriseInfoTtls(m_wirelessNetItem.m_connectUuid, m_info.ttlsInfo); + } + this->setFixedSize(MAIN_SIZE_NARROW); + break; + default: + break; + } +} diff --git a/src/frontend/enterprise-wlan/enterprisewlandialog.h b/src/frontend/enterprise-wlan/enterprisewlandialog.h new file mode 100644 index 00000000..3b094592 --- /dev/null +++ b/src/frontend/enterprise-wlan/enterprisewlandialog.h @@ -0,0 +1,65 @@ +#ifndef ENTERPRISEWLANDIALOG_H +#define ENTERPRISEWLANDIALOG_H +#include +#include +#include "securitypage.h" +#include "kywirelessnetitem.h" +#include "coninfo.h" + +class EnterpriseWlanDialog : public QWidget +{ + Q_OBJECT +public: + EnterpriseWlanDialog(KyWirelessNetItem &wirelessNetItem, QString device, QWidget *parent = nullptr); + ~EnterpriseWlanDialog(); + +protected: + void closeEvent(QCloseEvent *event); + +private: + void initUI(); //初始化UI界面 + void centerToScreen(); + void initConnections(); + void initData(); + +private: + KyWirelessNetItem m_wirelessNetItem; + ConInfo m_info; + QString m_deviceName; + KyWirelessConnectOperation *m_connectOperation = nullptr; + KyWirelessNetResource *m_resource = nullptr; + + /* 弹窗布局 + * Connect Enterprise WLAN············X + * SSID··························[SSID] + * -----------SecurityPage------------- + * | | + * ------------------------------------ + * ····················CANCEL···CONNECT + */ + QVBoxLayout *m_mainLayout = nullptr; + + QHBoxLayout *m_titleLayout = nullptr; + QLabel *m_titleLabel = nullptr; + QPushButton *m_closeBtn = nullptr; + + QHBoxLayout *m_ssidLayout = nullptr; + QLabel *m_ssidTitleLabel = nullptr; + QLabel *m_ssidLabel = nullptr; + + SecurityPage *m_securityPage = nullptr; + + QHBoxLayout *m_btnLayout = nullptr; + QPushButton *m_cancelBtn = nullptr; + QPushButton *m_connectBtn = nullptr; + +private slots: + void onBtnConnectClicked(); + void onEapTypeChanged(const KyEapMethodType &type); + void onPaletteChanged(); + +signals: + void enterpriseWlanDialogClose(bool); +}; + +#endif // ENTERPRISEWLANDIALOG_H diff --git a/src/frontend/frontend.pri b/src/frontend/frontend.pri new file mode 100644 index 00000000..ec60ca6c --- /dev/null +++ b/src/frontend/frontend.pri @@ -0,0 +1,20 @@ +INCLUDEPATH += $$PWD +include(tools/tools.pri) +include(xatom/xatom.pri) +include(tab-pages/tab-pages.pri) +include(list-items/list-items.pri) +include(netdetails/netdetails.pri) +include(enterprise-wlan/enterprise-wlan.pri) + +FORMS += \ + $$PWD/wificonfigdialog.ui + +HEADERS += \ + $$PWD/customstyle.h \ + $$PWD/mainwindow.h \ + $$PWD/wificonfigdialog.h + +SOURCES += \ + $$PWD/customstyle.cpp \ + $$PWD/mainwindow.cpp \ + $$PWD/wificonfigdialog.cpp diff --git a/src/frontend/list-items/lanlistitem.cpp b/src/frontend/list-items/lanlistitem.cpp new file mode 100644 index 00000000..18e8e4e1 --- /dev/null +++ b/src/frontend/list-items/lanlistitem.cpp @@ -0,0 +1,222 @@ +#include "lanlistitem.h" +#include "backend/dbus-interface/kylinconnectitem.h" + +#include + +#define LOG_FLAG "[LanListItem]" + +LanListItem::LanListItem(const KyConnectItem *lanConnectItem, + const QString &deviceName, QWidget *parent):ListItem(parent) +{ + m_connectOperation = new KyWiredConnectOperation(this); + m_deviceResource = new KyNetworkDeviceResourse(this); + + connectItemCopy(lanConnectItem); + m_deviceName = deviceName; + + m_nameLabel->setText(m_lanConnectItem.m_connectName); + m_netButton->setButtonIcon(QIcon::fromTheme("network-wired-connected-symbolic")); + + qDebug() << "LanListItem init:" << m_lanConnectItem.m_connectName << m_lanConnectItem.m_connectState << m_lanConnectItem.m_ifaceName; + + if (Deactivated == m_lanConnectItem.m_connectState || Activated == m_lanConnectItem.m_connectState) { + m_netButton->stopLoading(); + if (m_lanConnectItem.m_connectState == Activated) { + setIcon(true); + } else { + setIcon(false); + } + } else { + m_netButton->startLoading(); + } + + m_itemFrame->installEventFilter(this); + connect(this->m_infoButton, &InfoButton::clicked, this, &LanListItem::onInfoButtonClicked); + connect(m_menu, &QMenu::triggered, this, &LanListItem::onMenuTriggered); +} + + +LanListItem::LanListItem(QWidget *parent) : ListItem(parent) +{ + m_isActive = false; + m_netButton->setButtonIcon(QIcon::fromTheme("network-wired-disconnected-symbolic")); + setIcon(false); + const QString str=tr("Not connected"); + m_nameLabel->setText(str); + this->m_infoButton->hide(); +} + +LanListItem::~LanListItem() +{ + qDebug()<<"[LanPage] lan list item is deleted." << m_lanConnectItem.m_connectName; +} + +void LanListItem::setIcon(bool isOn) +{ + if (isOn) { + m_netButton->setActive(true); //设置图标显示不同颜色 + } else { + m_netButton->setActive(false); + } +} + +void LanListItem::connectItemCopy(const KyConnectItem *lanConnectItem) +{ + if (lanConnectItem) { + m_lanConnectItem.m_connectName = lanConnectItem->m_connectName; + m_lanConnectItem.m_connectPath = lanConnectItem->m_connectPath; + m_lanConnectItem.m_connectState = lanConnectItem->m_connectState; + m_lanConnectItem.m_connectUuid = lanConnectItem->m_connectUuid; + m_lanConnectItem.m_ifaceName = lanConnectItem->m_ifaceName; + m_lanConnectItem.m_itemType = lanConnectItem->m_itemType; + } else { + qDebug() << LOG_FLAG <<"the connect item is nullptr"; + m_lanConnectItem.m_connectName = ""; + m_lanConnectItem.m_connectPath = ""; + m_lanConnectItem.m_connectState = NetworkManager::ActiveConnection::State::Unknown; + m_lanConnectItem.m_connectUuid = ""; + m_lanConnectItem.m_ifaceName = ""; + m_lanConnectItem.m_itemType = NetworkManager::ConnectionSettings::ConnectionType::Unknown; + } + + return; +} + +void LanListItem::onNetButtonClicked() +{ + if (m_lanConnectItem.m_connectUuid.isEmpty()) { + qDebug() << LOG_FLAG << "connect is empty, so can not connect or disconnect."; + return; + } + + if (Deactivated == m_lanConnectItem.m_connectState) { + //断开的连接,点击激活连接 + if (m_deviceResource->wiredDeviceIsCarriered(m_deviceName)) { + m_connectOperation->activateWiredConnection(m_lanConnectItem.m_connectUuid, m_deviceName); + qDebug() << LOG_FLAG << "it will activate connection" << m_lanConnectItem.m_connectName + << ". it's device is" << m_deviceName; + m_netButton->startLoading(); + } else { + qDebug() << LOG_FLAG << m_deviceName << "is not carried, so can not activate connection"; + this->showDesktopNotify(tr("Wired Device not carried")); + } + } else { + qDebug() << LOG_FLAG <<"the connection" << m_lanConnectItem.m_connectName + << "is not deactived, so it can not be operation."; + } + + return; + +} + +void LanListItem::onRightButtonClicked() +{ + //右键点击事件 + qDebug()<< LOG_FLAG <<"onRightButtonClicked"; + if (!m_menu) { + return; + } + + m_menu->clear(); + if (Activated == m_lanConnectItem.m_connectState || Activating == m_lanConnectItem.m_connectState) { + m_menu->addAction(new QAction(tr("Disconnect"), this)); + } else if (Deactivated == m_lanConnectItem.m_connectState) { + m_menu->addAction(new QAction(tr("Connect"), this)); + } else { + return; + } + + m_menu->move(cursor().pos()); + m_menu->show(); + return; +} + +void LanListItem::onMenuTriggered(QAction *action) +{ + if (action->text() == tr("Connect")) { + this->onNetButtonClicked(); + } else if (action->text() == tr("Disconnect")) { + m_connectOperation->deactivateWiredConnection(m_lanConnectItem.m_connectName, m_lanConnectItem.m_connectUuid); + qDebug() << LOG_FLAG << "it will disconnect connection" << m_lanConnectItem.m_connectName + << ". it's device is" << m_deviceName; + m_netButton->startLoading(); + } + return; +} + + +void LanListItem::onInfoButtonClicked() +{ + if (m_lanConnectItem.m_connectUuid.isEmpty()) { + qDebug() << LOG_FLAG << "connect is empty, so can not show detail info."; + return; + } + + if(netDetail != nullptr){ + netDetail->activateWindow(); + return; + } + + qDebug()<< LOG_FLAG << "the info button of lan is clicked! uuid = " + << m_lanConnectItem.m_connectUuid << "; name = " << m_lanConnectItem.m_connectName + << "." <show(); + emit this->detailShow(true); + + return; +} + +void LanListItem::updateConnectionState(ConnectState state) +{ + m_lanConnectItem.m_connectState = (NetworkManager::ActiveConnection::State)state; + + if (Deactivated == state || Activated == state) { + m_netButton->stopLoading(); + if (state == Activated) { + setIcon(true); + } else { + setIcon(false); + } + } else { + m_netButton->startLoading(); + } + + return; +} + +QString LanListItem::getConnectionName() +{ + return m_lanConnectItem.m_connectName; +} + +void LanListItem::updateConnectionName(QString connectionName) +{ + m_lanConnectItem.m_connectName = connectionName; + m_nameLabel->setText(m_lanConnectItem.m_connectName); + return; +} + +QString LanListItem::getConnectionPath() +{ + return m_lanConnectItem.m_connectPath; +} + +void LanListItem::updateConnectionPath(QString connectionPath) +{ + m_lanConnectItem.m_connectPath = connectionPath; +} diff --git a/src/frontend/list-items/lanlistitem.h b/src/frontend/list-items/lanlistitem.h new file mode 100644 index 00000000..c778e7eb --- /dev/null +++ b/src/frontend/list-items/lanlistitem.h @@ -0,0 +1,50 @@ +#ifndef LANLISTITEM_H +#define LANLISTITEM_H +#include "listitem.h" +#include "kylinactiveconnectresource.h" + +#include +#include +#include + +class LanListItem : public ListItem +{ + Q_OBJECT + +public: + LanListItem(const KyConnectItem *lanConnectItem, const QString &deviceName, QWidget *parent = nullptr); + LanListItem(QWidget *parent = nullptr); + + ~LanListItem(); + +public: + void updateConnectionState(ConnectState state); + + QString getConnectionName(); + void updateConnectionName(QString connectionName); + + QString getConnectionPath(); + void updateConnectionPath(QString connectionPath); + +protected: + void setIcon(bool isOn); + void onRightButtonClicked(); + +private: + void connectItemCopy(const KyConnectItem *lanConnectItem); + +private slots: + void onInfoButtonClicked(); + void onNetButtonClicked(); + void onMenuTriggered(QAction *action); + +private: + KyConnectItem m_lanConnectItem; + + KyWiredConnectOperation *m_connectOperation = nullptr; + KyNetworkDeviceResourse *m_deviceResource = nullptr; + + QString m_deviceName = ""; +}; + +#endif // LANLISTITEM_H diff --git a/src/frontend/list-items/list-items.pri b/src/frontend/list-items/list-items.pri new file mode 100644 index 00000000..8fdc468e --- /dev/null +++ b/src/frontend/list-items/list-items.pri @@ -0,0 +1,18 @@ +INCLUDEPATH += $$PWD + +FORMS += \ + $$PWD/oneconnform.ui \ + $$PWD/onelancform.ui + +HEADERS += \ + $$PWD/lanlistitem.h \ + $$PWD/listitem.h \ + $$PWD/wlanlistitem.h \ + $$PWD/wlanmoreitem.h + +SOURCES += \ + $$PWD/lanlistitem.cpp \ + $$PWD/listitem.cpp \ + $$PWD/wlanlistitem.cpp \ + $$PWD/wlanmoreitem.cpp + diff --git a/src/frontend/list-items/listitem.cpp b/src/frontend/list-items/listitem.cpp new file mode 100644 index 00000000..1c459ce7 --- /dev/null +++ b/src/frontend/list-items/listitem.cpp @@ -0,0 +1,166 @@ +#include "listitem.h" +#include + +#define MAIN_LAYOUT_MARGINS 0,0,0,0 +#define MAIN_LAYOUT_SPACING 0 +#define ITEM_FRAME_MARGINS 16,6,16,6 +#define ITEM_FRAME_SPACING 10 +#define FRAME_WIDTH 395 +#define INFO_ICON_WIDTH 16 +#define INFO_ICON_HEIGHT 16 +#define LIGHT_HOVER_COLOR QColor(240,240,240,255) +#define DARK_HOVER_COLOR QColor(15,15,15,255) + +ListItem::ListItem(QWidget *parent) : QFrame(parent) +{ + m_connectState = UnknownState; + + initUI(); + initConnection(); + connect(qApp, &QApplication::paletteChanged, this, &ListItem::onPaletteChanged); +// m_itemFrame->installEventFilter(this); +} + +ListItem::~ListItem() +{ + if (nullptr != m_netButton) { + delete m_netButton; + m_netButton = nullptr; + } + + if (nullptr != m_infoButton) { + delete m_infoButton; + m_infoButton = nullptr; + } + +} + +void ListItem::setName(const QString &name) +{ + m_nameLabel->setText(name); +} + +//仅无线调用,有线自己获取 +void ListItem::setActive(const bool &isActive) +{ + m_netButton->setActive(isActive); + m_isActive = isActive; +} + +void ListItem::setConnectState(ConnectState state) +{ + m_connectState = state; +} + +void ListItem::showDesktopNotify(const QString &message) +{ + QDBusInterface iface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::sessionBus()); + QList args; + args<<(tr("Kylin NM")) + <<((unsigned int) 0) + <button() == Qt::LeftButton) { + onNetButtonClicked(); + } else if (event->button() == Qt::RightButton) { + onRightButtonClicked(); + } + return QFrame::mousePressEvent(event); +} + +void ListItem::enterEvent(QEvent *event) +{ +// QPalette pal = qApp->palette(); +// QColor baseColor = qApp->palette().base().color(); +// if (baseColor.red() > MIDDLE_COLOR) { +// pal.setColor(QPalette::Window, LIGHT_HOVER_COLOR); +// } else { +// pal.setColor(QPalette::Window, DARK_HOVER_COLOR); +// } +// this->setPalette(pal); +// return QFrame::enterEvent(event); +} + +void ListItem::leaveEvent(QEvent *event) +{ +// QPalette pal = qApp->palette(); +// pal.setColor(QPalette::Window, qApp->palette().base().color()); +// this->setPalette(pal); +// return QFrame::leaveEvent(event); +} + +void ListItem::paintEvent(QPaintEvent *event) +{ +// QPainter painter(this); +// painter.setRenderHint(QPainter::Antialiasing); //反锯齿 +// painter.setBrush(this->palette().brush(QPalette::Window)); +// painter.setPen(Qt::transparent); +// painter.drawRoundedRect(this->rect(), 6, 6); +// return QFrame::paintEvent(event); +} + +void ListItem::initUI() +{ + m_menu = new QMenu(this);//右键菜单 +// m_menu->setStyleSheet("QMenu::item{border:3px; border-radius:3px}"); +// m_menu->setStyleSheet("QMenu{border-radius:6px; margin:6px 6px 6px 6px}"); + connect(m_menu, &QMenu::triggered, this, &ListItem::onMenuTriggered); + + m_mainLayout = new QVBoxLayout(this); + m_mainLayout->setContentsMargins(MAIN_LAYOUT_MARGINS); + m_mainLayout->setSpacing(MAIN_LAYOUT_SPACING); + this->setLayout(m_mainLayout); + + m_itemFrame = new QFrame(this); + m_itemFrame->setFixedWidth(FRAME_WIDTH); + + m_hItemLayout = new QHBoxLayout(m_itemFrame); + m_hItemLayout->setContentsMargins(ITEM_FRAME_MARGINS); +// m_hItemLayout->setSpacing(ITEM_FRAME_SPACING); + + m_netButton = new RadioItemButton(m_itemFrame); + m_nameLabel = new FixLabel(m_itemFrame); + m_infoButton = new InfoButton(m_itemFrame); + m_infoButton->setIconSize(QSize(INFO_ICON_WIDTH,INFO_ICON_HEIGHT)); + + m_hItemLayout->addWidget(m_netButton); + m_hItemLayout->addWidget(m_nameLabel); + m_hItemLayout->addStretch(); + m_hItemLayout->addWidget(m_infoButton); + + m_mainLayout->addWidget(m_itemFrame); + +// this->setAutoFillBackground(true); +// this->setBackgroundRole(QPalette::Base); +// QPalette pal = qApp->palette(); +// pal.setColor(QPalette::Window, qApp->palette().base().color()); +// this->setPalette(pal); +} + + +void ListItem::initConnection() +{ + connect(this->m_netButton, &RadioItemButton::clicked, this, &ListItem::onNetButtonClicked); + // connect(this->m_infoButton, &InfoButton::clicked, this, &ListItem::onInfoButtonClicked); +} + +void ListItem::onPaletteChanged() +{ +// QPalette pal = qApp->palette(); +// pal.setColor(QPalette::Window, qApp->palette().base().color()); +// this->setPalette(pal); +} + diff --git a/src/frontend/list-items/listitem.h b/src/frontend/list-items/listitem.h new file mode 100644 index 00000000..c78c2799 --- /dev/null +++ b/src/frontend/list-items/listitem.h @@ -0,0 +1,68 @@ +#ifndef LISTITEM_H +#define LISTITEM_H +#include +#include +#include +#include +#include +#include +#include "radioitembutton.h" +#include "infobutton.h" +#include "netdetails/netdetail.h" + +typedef enum{ + UnknownState = 0, /**< The active connection is in an unknown state */ + Activating, /**< The connection is activating */ + Activated, /**< The connection is activated */ + Deactivating, /**< The connection is being torn down and cleaned up */ + Deactivated /**< The connection is no longer active */ +}ConnectState; + +class ListItem : public QFrame +{ + Q_OBJECT +public: + ListItem(QWidget *parent = nullptr); + ~ListItem(); + void setName(const QString &name); + void setActive(const bool &isActive); + void setConnectState(ConnectState state); + static void showDesktopNotify(const QString &message); + +protected: + void mousePressEvent(QMouseEvent *event); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + void paintEvent(QPaintEvent *event); + virtual void onRightButtonClicked() = 0; + +protected: + QFrame * m_itemFrame = nullptr; + + FixLabel * m_nameLabel = nullptr; + RadioItemButton * m_netButton = nullptr; + InfoButton * m_infoButton = nullptr; + + bool m_isActive = false; + ConnectState m_connectState; + + QMenu *m_menu = nullptr; +public: + QVBoxLayout * m_mainLayout = nullptr; + QHBoxLayout * m_hItemLayout = nullptr; + + NetDetail *netDetail = nullptr; +private: + void initUI(); + void initConnection(); + +public slots: + virtual void onNetButtonClicked() = 0; + void onPaletteChanged(); + virtual void onMenuTriggered(QAction *action)=0; + +signals: + void detailShow(bool isShow); +}; + +#endif // LISTITEM_H diff --git a/src/frontend/list-items/oneconnform.ui b/src/frontend/list-items/oneconnform.ui new file mode 100644 index 00000000..9e47beaa --- /dev/null +++ b/src/frontend/list-items/oneconnform.ui @@ -0,0 +1,302 @@ + + + OneConnForm + + + + 0 + 0 + 424 + 142 + + + + Form + + + + + 63 + 8 + 241 + 20 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 14 + 14 + 32 + 32 + + + + + + + + + + 63 + 31 + 140 + 20 + + + + + + + + + + 0 + 0 + 424 + 106 + + + + + + 64 + 64 + 352 + 30 + + + + + + + 390 + 75 + 18 + 9 + + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 0 + 0 + 424 + 138 + + + + + + 63 + 64 + 230 + 61 + + + + + + + + + + 63 + 62 + 230 + 20 + + + + + + + + + + 63 + 86 + 230 + 20 + + + + + + + + + + 63 + 110 + 230 + 20 + + + + + + + leInfo_1 + leInfo_2 + leInfo_3 + btnInfo + + + + + 2 + 59 + 421 + 2 + + + + background-color: rgba(156, 156, 156,0.1); + + + Qt::Horizontal + + + + + + 0 + 0 + 424 + 60 + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 380 + 20 + 20 + 20 + + + + + + + + + + 316 + 14 + 70 + 34 + + + + + + + wbg_3 + wbg_2 + lbName + lbSignal + lbConned + line + wbg + btnConnSub + btnConn + btnConnPWD + btnDisConn + btnHideConn + lbWaiting + lbWaitingIcon + btnCancel + + + + diff --git a/src/frontend/list-items/onelancform.ui b/src/frontend/list-items/onelancform.ui new file mode 100644 index 00000000..ee3f4a06 --- /dev/null +++ b/src/frontend/list-items/onelancform.ui @@ -0,0 +1,254 @@ + + + OneLancForm + + + + 0 + 0 + 424 + 167 + + + + Form + + + + + 63 + 8 + 190 + 20 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 63 + 31 + 200 + 20 + + + + + + + + + + 0 + 0 + 424 + 162 + + + + + + 63 + 64 + 238 + 85 + + + + + + + + + + 63 + 62 + 238 + 20 + + + + + + + + + + 63 + 86 + 260 + 20 + + + + + + + + + + 63 + 110 + 238 + 20 + + + + + + + + + + 63 + 134 + 238 + 20 + + + + + + + leInfo_1 + leInfo_2 + leInfo_3 + leInfo_4 + btnInfo + + + + + 14 + 14 + 32 + 32 + + + + + + + + + + 2 + 59 + 421 + 2 + + + + background-color: rgba(156, 156, 156,0.1); + + + Qt::Horizontal + + + + + + 0 + 0 + 424 + 60 + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 380 + 20 + 20 + 20 + + + + + + + + + + 316 + 14 + 70 + 34 + + + + + + + wbg + wbg_2 + lbName + lbConned + lbIcon + line + btnConnSub + btnDisConn + btnConn + lbWaiting + lbWaitingIcon + btnCancel + + + + diff --git a/src/frontend/list-items/wlanlistitem.cpp b/src/frontend/list-items/wlanlistitem.cpp new file mode 100644 index 00000000..d0acd7df --- /dev/null +++ b/src/frontend/list-items/wlanlistitem.cpp @@ -0,0 +1,592 @@ +#include "wlanlistitem.h" +#include +#include +#include + +#define EMPTY_SSID "EMPTY_SSID" +#define LOG_FLAG "[WlanListItem]" +#define WAIT_US 10*1000 + +WlanListItem::WlanListItem(KyWirelessNetItem &wirelessNetItem, QString device, bool isApMode, QWidget *parent) + : WlanListItem(wirelessNetItem, device, parent) +{ + m_isApMode = isApMode; + refreshIcon(false); // 额外刷新一次图标,因为WlanListItem执行时,m_isApMode尚未赋值 +} + +WlanListItem::WlanListItem(KyWirelessNetItem &wirelessNetItem, QString device, QWidget *parent) : ListItem(parent) +{ + m_wlanDevice = device; + m_wirelessNetItem = wirelessNetItem; + + qDebug()<<"[WlanPage] wlan list item is created." << m_wirelessNetItem.m_NetSsid; + + initWlanUI(); + setExpanded(false); + + connect(this->m_infoButton, &InfoButton::clicked, this, &WlanListItem::onInfoButtonClicked); + connect(m_menu, &QMenu::triggered, this, &WlanListItem::onMenuTriggered); + + m_wirelessConnectOperation = new KyWirelessConnectOperation(this); +} + +WlanListItem::WlanListItem(QWidget *parent) : ListItem(parent) +{ + m_wirelessNetItem.m_NetSsid = EMPTY_SSID; + + qDebug()<<"[WlanPage] wlan list item is created." << m_wirelessNetItem.m_NetSsid; + + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-offline-symbolic")); + m_netButton->setActive(false); +// m_netButton->setDefaultPixmap(); + const QString name = tr("Not connected"); + setExpanded(false); + this->setName(name); +// this->m_netButton->setEnabled(false); + this->m_infoButton->hide(); +} + +WlanListItem::~WlanListItem() +{ + qDebug()<<"[WlanPage] wlan list item is deleted." << m_wirelessNetItem.m_NetSsid; +} + +QString WlanListItem::getSsid() +{ + return m_wirelessNetItem.m_NetSsid; +} + +void WlanListItem::setSignalStrength(const int &signal) +{ + m_wirelessNetItem.m_signalStrength = signal; + if (Activated == m_connectState) { + refreshIcon(true); + } else if (Deactivated == m_connectState) { + refreshIcon(false); + } + + return; +} + +int WlanListItem::getSignalStrength() +{ + return m_wirelessNetItem.m_signalStrength; +} + +bool WlanListItem::isConfigured() +{ + return m_wirelessNetItem.m_isConfigured; +} + +void WlanListItem::setWlanState(const int &state) +{ +// m_wirelessNetItem.m_state = state; //ZJP_TODO 后端接口待补全 + refreshIcon(false); +} + +void WlanListItem::setExpanded(const bool &expanded) +{ + if (!m_pwdFrame || !m_autoConnectFrame) { + this->setFixedHeight(NORMAL_HEIGHT); + return; + } + + m_pwdFrame->setVisible(expanded); + m_autoConnectFrame->setVisible(expanded); + + if (expanded) { + m_pwdLineEdit->setFocus(); + setFixedHeight(EXPANDED_HEIGHT); + } else { + setFixedHeight(NORMAL_HEIGHT); + QPalette pal = qApp->palette(); + pal.setColor(QPalette::Window, qApp->palette().base().color()); + this->setPalette(pal); + } + + emit this->itemHeightChanged(expanded, m_wirelessNetItem.m_NetSsid); + + return; +} + +void WlanListItem::resizeEvent(QResizeEvent *event) +{ + this->blockSignals(true); + + if (this->height() == EXPANDED_HEIGHT) { + this->setExpanded(true); + } else { + this->setExpanded(false); + } + + this->blockSignals(false); + + return ListItem::resizeEvent(event); +} + +void WlanListItem::onRightButtonClicked() +{ + qDebug()<< LOG_FLAG <<"onRightButtonClicked"; + + if (!m_menu) { + return; + } + + m_menu->clear(); + + if (Activated == m_connectState || Activating == m_connectState) { + m_menu->addAction(new QAction(tr("Disconnect"), this)); + } else if (Deactivated == m_connectState) { + m_menu->addAction(new QAction(tr("Connect"), this)); + qDebug() << "add connect action"; + } else { + return; + } + + if (m_wirelessNetItem.m_isConfigured) { + m_menu->addAction(new QAction(tr("Forget"), this)); + } + + m_menu->move(cursor().pos()); + m_menu->show(); + + return; +} + +void WlanListItem::enterEvent(QEvent *event) +{ + //qDebug()<< LOG_FLAG <<"enterEvent" << m_wirelessNetItem.m_NetSsid; + m_mouseIsOut = false; + return ListItem::enterEvent(event); +} + +void WlanListItem::leaveEvent(QEvent *event) +{ + //qDebug()<< LOG_FLAG <<"leaveEvent"<< m_wirelessNetItem.m_NetSsid; + m_mouseIsOut = true; + if (m_pwdFrame && m_pwdFrame->isVisible()) { + if (m_focusIsOut) { + setExpanded(false); + } + return QFrame::leaveEvent(event); + } + + return ListItem::leaveEvent(event); +} + +bool WlanListItem::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == m_showPwdButton || watched == m_autoConnectCheckBox) { + if (event->type() == QEvent::FocusIn) { + m_pwdLineEdit->setFocus(); + } + } + + if (watched == m_pwdLineEdit) { + if (event->type() == QEvent::FocusOut) { + m_focusIsOut = true; + //qDebug()<< LOG_FLAG <<"focusOutEvent" << m_wirelessNetItem.m_NetSsid; + if (m_mouseIsOut) { + setExpanded(false); + } + } else if (event->type() == QEvent::FocusIn) { + //qDebug()<< LOG_FLAG <<"focusInEvent" << m_wirelessNetItem.m_NetSsid; + m_focusIsOut = false; + } + } + + return QFrame::eventFilter(watched, event); +} + +void WlanListItem::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + if (m_pwdFrame && m_pwdFrame->isVisible() && m_pwdLineEdit->text().length() >= PWD_LENGTH_LIMIT) { + onConnectButtonClicked(); + } + } + return QFrame::keyPressEvent(event); +} + +void WlanListItem::initWlanUI() +{ + m_hasPwd = (m_wirelessNetItem.m_secuType.isEmpty() || m_wirelessNetItem.m_secuType == "") ? false : true; + //设置显示的Wlan名称 +// this->setName((m_wirelessNetItem.m_connName != "") ? m_wirelessNetItem.m_connName : m_wirelessNetItem.m_NetSsid); + this->setName(m_wirelessNetItem.m_NetSsid); + //刷新左侧按钮图标 + refreshIcon(false); + +#define PWD_AREA_HEIGHT 36 +#define CONNECT_BUTTON_WIDTH 96 +#define FRAME_CONTENT_MARGINS 56,0,16,4 +#define FRAME_SPACING 8 +#define LINEEDIT_WIDTH 220 +#define PWD_CONTENT_MARGINS 8,0,34,0 +#define SHOW_PWD_BUTTON_SIZE 24,24 +#define PWD_LAYOUT_MARGINS 8,0,8,0 + //密码输入区域的UI + m_pwdFrame = new QFrame(this); + m_pwdFrameLyt = new QHBoxLayout(m_pwdFrame); + m_pwdFrameLyt->setContentsMargins(FRAME_CONTENT_MARGINS); + m_pwdFrameLyt->setSpacing(FRAME_SPACING); + m_pwdFrame->setLayout(m_pwdFrameLyt); + + m_pwdLineEdit = new QLineEdit(m_pwdFrame); + m_pwdLineEdit->setFixedWidth(LINEEDIT_WIDTH); +// m_pwdLineEdit->setAttribute(Qt::WA_InputMethodEnabled, false); + m_pwdLineEdit->setContextMenuPolicy(Qt::NoContextMenu); + + QRegExp rx("^[A-Za-z0-9`~!@#$%^&*()_-+=<>,.\\\/]+$"); + QRegExpValidator *latitude = new QRegExpValidator(rx, this); + m_pwdLineEdit->setValidator(latitude); + + m_pwdLineEdit->installEventFilter(this); + connect(m_pwdLineEdit, &QLineEdit::textChanged, this, &WlanListItem::onPwdEditorTextChanged); + m_pwdLineEdit->setFixedHeight(PWD_AREA_HEIGHT); + m_pwdLineEdit->setEchoMode(QLineEdit::EchoMode::Password); + m_pwdLineEdit->setTextMargins(PWD_CONTENT_MARGINS); + m_pwdFrameLyt->addWidget(m_pwdLineEdit); + + m_pwdLineEditLyt = new QHBoxLayout(m_pwdLineEdit); + m_pwdLineEditLyt->setContentsMargins(PWD_LAYOUT_MARGINS); + m_pwdLineEdit->setLayout(m_pwdLineEditLyt); + + m_showPwdButton = new QPushButton(m_pwdLineEdit); + m_showPwdButton->setFlat(true); //去除边框 + m_showPwdButton->installEventFilter(this); + m_showPwdButton->setFixedSize(SHOW_PWD_BUTTON_SIZE); + m_showPwdButton->setIcon(QIcon::fromTheme("ukui-eye-hidden-symbolic")); + m_showPwdButton->setCursor(Qt::PointingHandCursor); + connect(m_showPwdButton, &QPushButton::clicked, this, &WlanListItem::onShowPwdButtonClicked); + m_pwdLineEditLyt->addStretch(); + m_pwdLineEditLyt->addWidget(m_showPwdButton); + + m_connectButton = new QPushButton(m_pwdFrame); + m_connectButton->setFixedSize(CONNECT_BUTTON_WIDTH, PWD_AREA_HEIGHT); + m_connectButton->setText(tr("Connect")); + m_connectButton->setEnabled(false); + connect(m_connectButton, &QPushButton::clicked, this, &WlanListItem::onConnectButtonClicked); + m_pwdFrameLyt->addWidget(m_connectButton); + m_pwdFrameLyt->addStretch(); + + //自动连接选择区域UI + m_autoConnectFrame = new QFrame(this); + + m_autoConnectFrameLyt = new QHBoxLayout(m_autoConnectFrame); + m_autoConnectFrameLyt->setContentsMargins(FRAME_CONTENT_MARGINS); + m_autoConnectFrameLyt->setSpacing(FRAME_SPACING); + m_autoConnectFrame->setLayout(m_autoConnectFrameLyt); + + m_autoConnectCheckBox = new QCheckBox(m_autoConnectFrame); + m_autoConnectCheckBox->installEventFilter(this); + m_autoConnectCheckBox->setChecked(true); + m_autoConnectCheckBox->setFixedSize(SHOW_PWD_BUTTON_SIZE); + m_autoConnectFrameLyt->addWidget(m_autoConnectCheckBox); + + m_autoConnectLabel = new QLabel(m_autoConnectFrame); + m_autoConnectLabel->setText(tr("Auto Connect")); + m_autoConnectFrameLyt->addWidget(m_autoConnectLabel); + m_autoConnectFrameLyt->addStretch(); + + m_mainLayout->addWidget(m_pwdFrame); + m_mainLayout->addWidget(m_autoConnectFrame); + + m_pwdFrame->hide(); + m_autoConnectFrame->hide(); +} + +void WlanListItem::refreshIcon(bool isActivated) +{ +#define FULL_SIGNAL 5 +#define EXCELLENT_SIGNAL 4 +#define GOOD_SIGNAL 3 +#define OK_SIGNAL 2 +#define LOW_SIGNAL 1 +#define STEP 25 + if (m_isApMode) { + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-hotspot-symbolic", QIcon(":/res/w/wifi-full.png"))); + m_netButton->setActive(isActivated); + return; + } + + if (!m_hasPwd) { + //ZJP_TODO 无加密 注意信号格数计算方式,可能需要修改 + switch (m_wirelessNetItem.m_signalStrength / STEP + 1) { + case FULL_SIGNAL: + case EXCELLENT_SIGNAL: + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-signal-excellent-symbolic", + QIcon(":/res/w/wifi-full.png"))); + break; + case GOOD_SIGNAL: + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-signal-good-symbolic", + QIcon(":/res/w/wifi-high.png"))); + break; + case OK_SIGNAL: + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-signal-ok", + QIcon(":/res/w/wifi-medium.png"))); + break; + case LOW_SIGNAL: + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-signal-low", + QIcon(":/res/w/wifi-low.png"))); + break; + default: + qDebug() << "Set wlan(without passwd) icon failed, signal = " + << m_wirelessNetItem.m_signalStrength << Q_FUNC_INFO << __LINE__; + break; + } + } else { + //ZJP_TODO 有加密 + switch (m_wirelessNetItem.m_signalStrength / STEP + 1) { + case FULL_SIGNAL: + case EXCELLENT_SIGNAL: + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-signal-excellent-secure-symbolic", + QIcon(":/res/w/wifi-full-pwd.png"))); + break; + case GOOD_SIGNAL: + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-signal-good-secure-symbolic", + QIcon(":/res/w/wifi-high-pwd.png"))); + break; + case OK_SIGNAL: + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-signal-ok-secure-symbolic", + QIcon(":/res/w/wifi-medium-pwd.png"))); + break; + case LOW_SIGNAL: + m_netButton->setButtonIcon(QIcon::fromTheme("network-wireless-signal-low-secure-symbolic", + QIcon(":/res/w/wifi-low-pwd.png"))); + break; + default: + qDebug() << "Set wlan(with passwd) icon failed, signal = " + << m_wirelessNetItem.m_signalStrength << Q_FUNC_INFO << __LINE__; + break; + } + } + m_netButton->setActive(isActivated); +} + +void WlanListItem::onInfoButtonClicked() +{ + //ZJP_TODO 呼出无线详情页 + if(netDetail != nullptr){ + netDetail->activateWindow(); + return; + } + + qDebug() << LOG_FLAG << "Net active or not:"<< m_connectState; + qDebug() << LOG_FLAG << "On wlan info button clicked! ssid = " + << m_wirelessNetItem.m_NetSsid << "; name = " + << m_wirelessNetItem.m_connName << "." <show(); + emit this->detailShow(true); +} + +void WlanListItem::onNetButtonClicked() +{ + qDebug() << LOG_FLAG << "onNetButtonClicked"; + if (m_wirelessNetItem.m_NetSsid == EMPTY_SSID) { + return; + } + + if (Deactivated != m_connectState) { + qDebug() << LOG_FLAG <<"the connection" << m_wirelessNetItem.m_connName + << "is not deactived, so it can not be operation." << Q_FUNC_INFO << __LINE__; + return; + } + + //有配置或者无密码的wifi直接连接 + if (m_wirelessNetItem.m_isConfigured) { + m_wirelessConnectOperation->activeWirelessConnect(m_wlanDevice, m_wirelessNetItem.m_connectUuid); + qDebug()<<"[WlanListItem] Has configuration, will be activated. ssid = " + << m_wirelessNetItem.m_NetSsid << Q_FUNC_INFO << __LINE__; + m_netButton->startLoading(); + return; + } + + if (!this->m_connectButton->isVisible() && m_wirelessNetItem.m_secuType != "") { + if (m_wirelessNetItem.m_secuType.contains("802.1x", Qt::CaseInsensitive)) { + if (isEnterpriseWlanDialogShow && enterpriseWlanDialog != nullptr) { + qDebug() << LOG_FLAG <<"EnterpriseWlanDialog is show do not show again!"; + KWindowSystem::raiseWindow(enterpriseWlanDialog->winId()); + return; + } else { + enterpriseWlanDialog = new EnterpriseWlanDialog(m_wirelessNetItem, m_wlanDevice, this); + connect(enterpriseWlanDialog, &EnterpriseWlanDialog::enterpriseWlanDialogClose, this, &WlanListItem::onEnterpriseWlanDialogClose); + enterpriseWlanDialog->show(); + isEnterpriseWlanDialogShow = true; + } + } else { + this->setExpanded(true); + } + } else { + onConnectButtonClicked(); + } + + return; +} + +void WlanListItem::updateWirelessNetSecurity(QString ssid, QString securityType) +{ + if (ssid != m_wirelessNetItem.m_NetSsid) { + return; + } + + qDebug() << LOG_FLAG << "Security changed! ssid = " << m_wirelessNetItem.m_NetSsid + << "; security = " << m_wirelessNetItem.m_secuType << "." <text().length() < PWD_LENGTH_LIMIT) { + m_connectButton->setEnabled(false); + } else { + m_connectButton->setEnabled(true); + } + + return; +} + +void WlanListItem::onShowPwdButtonClicked() +{ + qDebug()<< LOG_FLAG << "onShowPwdButtonClicked"; + if (!m_pwdLineEdit) { + return; + } + + if (m_pwdLineEdit->echoMode() == QLineEdit::EchoMode::Password) { + m_showPwdButton->setIcon(QIcon::fromTheme("ukui-eye-display-symbolic")); + m_pwdLineEdit->setEchoMode(QLineEdit::EchoMode::Normal); + } else { + m_showPwdButton->setIcon(QIcon::fromTheme("ukui-eye-hidden-symbolic")); + m_pwdLineEdit->setEchoMode(QLineEdit::EchoMode::Password); + } + + return; +} + +void WlanListItem::onConnectButtonClicked() +{ + qDebug()<< LOG_FLAG << "onConnectButtonClicked"; + if ((Activating == m_connectState || Deactivating == m_connectState)) { + qDebug() << LOG_FLAG << "On wlan clicked! But there is nothing to do because it is already activating/deactivating!" + << Q_FUNC_INFO << __LINE__; + return; + } + + if (m_connectButton->isVisible() && !m_connectButton->isEnabled()) { + qWarning() << "Connect wlan failed because of null pointer or button state!" << Q_FUNC_INFO << __LINE__; + return; + } + + KyWirelessConnectSetting settings; + settings.m_connectName = m_wirelessNetItem.m_NetSsid; + settings.m_ssid = m_wirelessNetItem.m_NetSsid; + settings.isAutoConnect = m_autoConnectCheckBox->isChecked(); + settings.m_psk = m_pwdLineEdit->text(); + if (m_wirelessNetItem.m_secuType.isEmpty() || m_wirelessNetItem.m_secuType == "") { + settings.m_type = WpaNone; + } else if (m_wirelessNetItem.m_secuType.contains("WPA1") || m_wirelessNetItem.m_secuType.contains("WPA2")) { + settings.m_type = WpaPsk; + } else if (m_wirelessNetItem.m_secuType.contains("WPA3")) { + settings.m_type = SAE; + } + + qDebug() << "[WlanListItem] On button connect clicked, will connect wlan. ssid = " + << m_wirelessNetItem.m_NetSsid << Q_FUNC_INFO <<__LINE__; + + m_wirelessConnectOperation->addAndActiveWirelessConnect(m_wlanDevice, settings, false); + setExpanded(false); + m_netButton->startLoading(); + return; +} + +ConnectState WlanListItem::getConnectionState() +{ + return m_connectState; +} + +void WlanListItem::updateConnectState(ConnectState state) +{ + m_connectState = state; + + if (Activated == state) { + m_netButton->stopLoading(); + m_netButton->setActive(true); + } else if(Deactivated == state) { + qDebug() << "[WlanListItem] stop loading connect state:" << state; + m_netButton->stopLoading(); + m_netButton->setActive(false); + } else { + qDebug() << "[WlanListItem] start loading connect state:" << state; + m_netButton->startLoading(); + } + + return; +} + +void WlanListItem::onMenuTriggered(QAction *action) +{ + if (action->text() == tr("Connect")) { + this->onNetButtonClicked(); + } else if (action->text() == tr("Disconnect")) { + m_wirelessConnectOperation->deActivateWirelessConnection(m_wlanDevice, m_wirelessNetItem.m_connectUuid); + qDebug()<<"[WlanListItem] Clicked on connected wifi, it will be inactivated. ssid = " + << m_wirelessNetItem.m_NetSsid << Q_FUNC_INFO << __LINE__; + m_netButton->startLoading(); + } else if (action->text() == tr("Forget")) { + m_wirelessConnectOperation->deleteWirelessConnect(m_wirelessNetItem.m_connectUuid); + } + + return; +} + +void WlanListItem::onEnterpriseWlanDialogClose(bool isShow) +{ + isEnterpriseWlanDialogShow = isShow; + + return; +} + +void WlanListItem::forgetPwd() +{ + if (!this->isConfigured()) { + m_pwdLineEdit->setText(""); + return; + } +} + + diff --git a/src/frontend/list-items/wlanlistitem.h b/src/frontend/list-items/wlanlistitem.h new file mode 100644 index 00000000..641af74e --- /dev/null +++ b/src/frontend/list-items/wlanlistitem.h @@ -0,0 +1,107 @@ +#ifndef WLANLISTITEM_H +#define WLANLISTITEM_H +#include "listitem.h" +#include "kywirelessnetitem.h" +#include "kywirelessnetresource.h" +#include "wlanpage.h" +#include "kywirelessconnectoperation.h" +#include +#include "kylinactiveconnectresource.h" +#include +#include "enterprisewlandialog.h" + +#include + +#define PSK_SETTING_NAME "802-11-wireless-security" + +#define NORMAL_HEIGHT 48 +#define EXPANDED_HEIGHT 120 +#define PWD_LENGTH_LIMIT 8 + +class WlanListItem : public ListItem +{ + Q_OBJECT +public: + WlanListItem(KyWirelessNetItem &wirelessNetItem, QString device, bool isApMode, QWidget *parent = nullptr); + WlanListItem(KyWirelessNetItem &wirelessNetItem, QString device, QWidget *parent = nullptr); + WlanListItem(QWidget *parent = nullptr); + ~WlanListItem(); + +public: + QString getSsid(); + + void setSignalStrength(const int &signal); + int getSignalStrength(); + + bool isConfigured(); + + void setWlanState(const int &state); + void setExpanded(const bool &expanded); + + ConnectState getConnectionState(); + void updateConnectState(ConnectState state); + + void updateWirelessNetSecurity(QString ssid, QString securityType); + void updateWirelessNetItem(KyWirelessNetItem &wirelessNetItem); + + void forgetPwd(); + +protected: + void resizeEvent(QResizeEvent *event); + void onRightButtonClicked(); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + bool eventFilter(QObject *watched, QEvent *event); + void keyPressEvent(QKeyEvent *event); + +signals: + void itemHeightChanged(const bool isExpanded, const QString &ssid); + +private: + void initWlanUI(); + void refreshIcon(bool isActivated); + +private: + KyWirelessNetItem m_wirelessNetItem; + KyWirelessConnectOperation *m_wirelessConnectOperation = nullptr; + EnterpriseWlanDialog *enterpriseWlanDialog = nullptr; + + bool m_hasPwd = true; + QString m_wlanDevice; + + bool isEnterpriseWlanDialogShow = false; + + //密码输入区域的UI + QFrame *m_pwdFrame = nullptr; + QHBoxLayout *m_pwdFrameLyt = nullptr; + + QLineEdit * m_pwdLineEdit = nullptr; + QHBoxLayout *m_pwdLineEditLyt = nullptr; + QPushButton *m_showPwdButton = nullptr; + QPushButton *m_connectButton = nullptr; + + //自动连接选择区域UI + QFrame *m_autoConnectFrame = nullptr; + QHBoxLayout *m_autoConnectFrameLyt = nullptr; + QCheckBox *m_autoConnectCheckBox = nullptr; + QLabel *m_autoConnectLabel = nullptr; + + bool m_focusIsOut = true; + bool m_mouseIsOut = true; + + bool m_forgetConnection = false; + bool m_isApMode = false; + +protected slots: + void onInfoButtonClicked(); + +private slots: + void onNetButtonClicked(); + void onPwdEditorTextChanged(); + void onShowPwdButtonClicked(); + void onConnectButtonClicked(); + void onMenuTriggered(QAction *action); + void onEnterpriseWlanDialogClose(bool isShow); +}; + +#endif // WLANLISTITEM_H diff --git a/src/frontend/list-items/wlanmoreitem.cpp b/src/frontend/list-items/wlanmoreitem.cpp new file mode 100644 index 00000000..7284d703 --- /dev/null +++ b/src/frontend/list-items/wlanmoreitem.cpp @@ -0,0 +1,25 @@ +#include "wlanmoreitem.h" + +WlanMoreItem::WlanMoreItem(QWidget *parent) : ListItem(parent) +{ + setObjectName(WMI_OB_NAME); + m_netButton->setVisible(false); + m_infoButton->setVisible(false); + m_nameLabel->setText(tr("Add Others...")); +} + +WlanMoreItem::~WlanMoreItem() { + +} + +void WlanMoreItem::onNetButtonClicked() { + emit hiddenWlanClicked(); +} + +void WlanMoreItem::onRightButtonClicked() { + // Todo +} + +void WlanMoreItem::onMenuTriggered(QAction *action) { + +} diff --git a/src/frontend/list-items/wlanmoreitem.h b/src/frontend/list-items/wlanmoreitem.h new file mode 100644 index 00000000..52183403 --- /dev/null +++ b/src/frontend/list-items/wlanmoreitem.h @@ -0,0 +1,26 @@ +#ifndef WLANMOREITEM_H +#define WLANMOREITEM_H + +#include +#include "listitem.h" + +const QString WMI_OB_NAME = "WlanMoreItemObjName"; + +class WlanMoreItem : public ListItem +{ + Q_OBJECT + +protected: + void onRightButtonClicked(); + +public: + WlanMoreItem(QWidget *parent = nullptr); + ~WlanMoreItem(); + + void onNetButtonClicked(); + void onMenuTriggered(QAction *action); +signals: + void hiddenWlanClicked(); +}; + +#endif // WLANMOREITEM_H diff --git a/src/frontend/mainwindow.cpp b/src/frontend/mainwindow.cpp new file mode 100644 index 00000000..1372a8ff --- /dev/null +++ b/src/frontend/mainwindow.cpp @@ -0,0 +1,789 @@ +#include "mainwindow.h" +#include "customstyle.h" +#include +#include +#include +#include +#include +#include + +#include "kylinnetworkdeviceresource.h" +#include "../backend/dbus-interface/kylinagentinterface.h" + + +#define MAINWINDOW_WIDTH 420 +#define MAINWINDOW_HEIGHT 476 +#define LAYOUT_MARGINS 0,0,0,0 +#define LOADING_TRAYICON_TIMER_MS 60 +#define THEME_SCHAME "org.ukui.style" +#define COLOR_THEME "styleName" + +const QString v10Sp1 = "V10SP1"; +const QString intel = "V10SP1-edu"; + +#define KEY_PRODUCT_FEATURES "PRODUCT_FEATURES" + +#include +#include + +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) +{ + firstlyStart(); +} + +/** + * @brief MainWindow::showMainwindow show主窗口,同时也作为dbus接口提供给外部组件调用 + */ +void MainWindow::showMainwindow() +{ + if (!m_loadFinished) { + m_secondaryStartTimer->stop(); + secondaryStart(); + } + + /** + * 设置主界面跳过任务栏和分页器的属性,隐藏再次展示有可能辉冲刷掉该属性,需要展示时重新设置 + */ + const KWindowInfo info(this->winId(), NET::WMState); + if (!info.hasState(NET::SkipTaskbar) || !info.hasState(NET::SkipPager)) { + KWindowSystem::setState(this->winId(), NET::SkipTaskbar | NET::SkipPager); + } + + this->resetWindowPosition(); + this->showNormal(); + this->raise(); + this->activateWindow(); + emit this->mainWindowVisibleChanged(true); +#ifdef WITHKYSEC + if (!kysec_is_disabled() && kysec_get_3adm_status() && (getuid() || geteuid())){ + if (nullptr != m_wlanWidget) { + m_wlanWidget->hideSetting(); + } + if (nullptr != m_lanWidget) { + m_lanWidget->hideSetting(); + } + } else { + if (nullptr != m_wlanWidget) { + m_wlanWidget->showSetting(); + } + if (nullptr != m_lanWidget) { + m_lanWidget->showSetting(); + } + } +#endif + +} + +/** + * @brief MainWindow::hideMainwindow 隐藏主页面时要进行的操作,后续可以添加到此函数 + */ +void MainWindow::hideMainwindow() +{ + this->hide(); + emit this->mainWindowVisibleChanged(false); +} + +/** + * @brief MainWindow::setWiredDefaultDevice 设置有线设备默认网卡 + */ +void MainWindow::setWiredDefaultDevice(QString deviceName) +{ +// m_lanWidget->updateDefaultDevice(deviceName); +} + +/** + * @brief MainWindow::hideMainwindow 设置无线设备默认网卡 + */ +void MainWindow::setWirelessDefaultDevice(QString deviceName) +{ +// m_wlanWidget->updateDefaultDevice(deviceName); +} + +/** + * @brief MainWindow::firstlyStart 一级启动,执行重要且不耗时的启动操作 + */ +void MainWindow::firstlyStart() +{ + initWindowProperties(); + initTransparency(); + initUI(); + initDbusConnnect(); + initWindowTheme(); + initTrayIcon(); + initPlatform(); + installEventFilter(this); + m_secondaryStartTimer = new QTimer(this); + connect(m_secondaryStartTimer, &QTimer::timeout, this, [ = ]() { + m_secondaryStartTimer->stop(); + secondaryStart();//满足条件后执行比较耗时的二级启动 + }); + m_secondaryStartTimer->start(5 * 1000); + + m_createPagePtrMap.clear(); + + //加载key ring + agent_init(); +} + +/** + * @brief MainWindow::secondaryStart 二级启动,可以将较耗时的初始化操作放到此处执行 + */ +void MainWindow::secondaryStart() +{ + if (m_loadFinished) + return; + m_loadFinished = true; +} + +/** + * @brief MainWindow::initWindowProperties 初始化平台信息 + */ +void MainWindow::initPlatform() +{ + if(v10Sp1.compare(KDKGetPrjCodeName().c_str(),Qt::CaseInsensitive) == 0) { + QString feature = KDKGetOSRelease(KEY_PRODUCT_FEATURES).c_str(); + if (feature.toInt() == 3) { + m_isShowInCenter = true; + } + } else if (intel.compare(KDKGetPrjCodeName().c_str(),Qt::CaseInsensitive) == 0) { + m_isShowInCenter = true; + } + + qDebug() << KDKGetPrjCodeName().c_str() << KDKGetOSRelease(KEY_PRODUCT_FEATURES).c_str() << "m_isShowInCenter" << m_isShowInCenter; +} + +/** + * @brief MainWindow::initWindowProperties 初始化一些窗口属性 + */ +void MainWindow::initWindowProperties() +{ + this->setWindowTitle(tr("kylin-nm")); + this->setWindowIcon(QIcon::fromTheme("kylin-network", QIcon(":/res/x/setup.png"))); + this->setFixedSize(MAINWINDOW_WIDTH, MAINWINDOW_HEIGHT); +// //绘制毛玻璃特效 + this->setAttribute(Qt::WA_TranslucentBackground, true); //透明 + this->setFocusPolicy(Qt::NoFocus); + + QPainterPath path; + auto rect = this->rect(); + path.addRoundedRect(rect, 12, 12); + KWindowEffects::enableBlurBehind(this->winId(), true, QRegion(path.toFillPolygon().toPolygon())); //背景模糊 +} + +void MainWindow::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); // 反锯齿; + painter.setPen(Qt::transparent); + auto rect = this->rect(); + painter.drawRoundedRect(rect, 12, 12); //窗口圆角 +} + +void MainWindow::initTransparency() +{ + if(QGSettings::isSchemaInstalled(TRANSPARENCY_GSETTINGS)) { + m_transGsettings = new QGSettings(TRANSPARENCY_GSETTINGS); + if(m_transGsettings->keys().contains(QString("transparency"))) { + m_transparency=m_transGsettings->get("transparency").toDouble() + 0.15; + m_transparency = (m_transparency > 1) ? 1 : m_transparency; + connect(m_transGsettings, &QGSettings::changed, this, &MainWindow::onTransChanged); + } + } +} + +void MainWindow::onTransChanged() +{ + m_transparency = m_transGsettings->get("transparency").toDouble() + 0.15; + m_transparency = (m_transparency > 1) ? 1 : m_transparency; + paintWithTrans(); +} + +void MainWindow::paintWithTrans() +{ + QPalette pal = m_centralWidget->palette(); + QColor color = qApp->palette().base().color(); + color.setAlphaF(m_transparency); + pal.setColor(QPalette::Base, color); + m_centralWidget->setPalette(pal); + + QPalette tabPal = m_centralWidget->tabBar()->palette(); + tabPal.setColor(QPalette::Base, color); + + QColor inactiveColor = qApp->palette().window().color(); + inactiveColor.setAlphaF(0.86 *m_transparency); + tabPal.setColor(QPalette::Window, inactiveColor); + + m_centralWidget->tabBar()->setPalette(tabPal); +} + +/** + * @brief MainWindow::initUI 初始化窗口内控件 + */ +void MainWindow::initUI() +{ + m_centralWidget = new QTabWidget(this); + this->setCentralWidget(m_centralWidget); + m_centralWidget->tabBar()->setFixedWidth(this->width()+1); +// m_centralWidget->tabBar()->setStyleSheet("QTabBar::tab{min-height:40px}"); + m_lanWidget = new LanPage(m_centralWidget); + m_wlanWidget = new WlanPage(m_centralWidget); + connect(this, &MainWindow::mainWindowVisibleChanged, m_wlanWidget, &WlanPage::onMainWindowVisibleChanged); +// m_centralWidget->addTab(m_lanWidget, QIcon::fromTheme("network-wired-connected-symbolic", QIcon::fromTheme("network-wired-symbolic", QIcon(":/res/l/network-online.svg"))), tr("LAN")); +// m_centralWidget->addTab(m_wlanWidget, QIcon::fromTheme("network-wireless-signal-excellent-symbolic", QIcon(":/res/x/wifi-list-bg.svg")), tr("WLAN")); + + m_centralWidget->addTab(m_lanWidget, tr("")); + m_centralWidget->addTab(m_wlanWidget,tr("")); + m_tabBarLayout = new QHBoxLayout(this); + m_tabBarLayout->setContentsMargins(LAYOUT_MARGINS); + m_lanLabel = new QLabel(tr("LAN")); + m_lanLabel->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter); + m_wlanLabel = new QLabel(tr("WLAN")); + m_wlanLabel->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter); + m_tabBarLayout->addWidget(m_lanLabel); + m_tabBarLayout->addWidget(m_wlanLabel); + m_centralWidget->tabBar()->setLayout(m_tabBarLayout); + m_centralWidget->tabBar()->setProperty("useTabbarSeparateLine", false); // 去掉中间的分割线 + m_centralWidget->setAttribute(Qt::WA_TranslucentBackground, true); // 背景透明 解决切换黑屏问题 + connect(m_centralWidget, &QTabWidget::currentChanged, m_wlanWidget, &WlanPage::onWlanPageVisibleChanged); + paintWithTrans(); +} + +/** + * @brief MainWindow::initTrayIcon 初始化托盘图标和托盘右键菜单 + */ +void MainWindow::initTrayIcon() +{ + loadIcons.append(QIcon::fromTheme("kylin-network-1")); + loadIcons.append(QIcon::fromTheme("kylin-network-2")); + loadIcons.append(QIcon::fromTheme("kylin-network-3")); + loadIcons.append(QIcon::fromTheme("kylin-network-4")); + loadIcons.append(QIcon::fromTheme("kylin-network-5")); + loadIcons.append(QIcon::fromTheme("kylin-network-6")); + loadIcons.append(QIcon::fromTheme("kylin-network-7")); + loadIcons.append(QIcon::fromTheme("kylin-network-8")); + loadIcons.append(QIcon::fromTheme("kylin-network-9")); + loadIcons.append(QIcon::fromTheme("kylin-network-10")); + loadIcons.append(QIcon::fromTheme("kylin-network-11")); + loadIcons.append(QIcon::fromTheme("kylin-network-12")); + iconTimer = new QTimer(this); + connect(iconTimer, &QTimer::timeout, this, &MainWindow::onSetTrayIconLoading); + + m_trayIcon = new QSystemTrayIcon(); + m_trayIconMenu = new QMenu(); + m_showMainwindowAction = new QAction(tr("Show MainWindow"),this); + m_showSettingsAction = new QAction(tr("Settings"),this); + + m_trayIcon->setToolTip(QString(tr("kylin-nm"))); + m_showSettingsAction->setIcon(QIcon::fromTheme("document-page-setup-symbolic", QIcon(":/res/x/setup.png")) ); +// m_trayIconMenu->addAction(m_showMainwindowAction); + m_trayIconMenu->addAction(m_showSettingsAction); + m_trayIcon->setContextMenu(m_trayIconMenu); + m_trayIcon->setIcon(QIcon::fromTheme("network-wired-signal-excellent-symbolic")); + iconStatus = IconActiveType::LAN_CONNECTED; + onRefreshTrayIcon(); + + connect(m_trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::onTrayIconActivated); +// connect(m_showMainwindowAction, &QAction::triggered, this, &MainWindow::onShowMainwindowActionTriggled); + connect(m_showSettingsAction, &QAction::triggered, this, &MainWindow::onShowSettingsActionTriggled); + m_trayIcon->show(); +} + +void MainWindow::initDbusConnnect() +{ + connect(m_lanWidget, &LanPage::deviceStatusChanged, this, &MainWindow::deviceStatusChanged); + connect(m_lanWidget, &LanPage::deviceNameChanged, this, &MainWindow::deviceNameChanged); + connect(m_wlanWidget, &WlanPage::deviceStatusChanged, this, &MainWindow::deviceStatusChanged); + connect(m_wlanWidget, &WlanPage::deviceNameChanged, this, &MainWindow::deviceNameChanged); + + connect(m_wlanWidget, &WlanPage::activateFailed, this, &MainWindow::activateFailed); + connect(m_wlanWidget, &WlanPage::deactivateFailed, this, &MainWindow::deactivateFailed); + connect(m_lanWidget, &LanPage::activateFailed, this, &MainWindow::activateFailed); + connect(m_lanWidget, &LanPage::deactivateFailed, this, &MainWindow::deactivateFailed); + + connect(m_lanWidget, &LanPage::lanAdd, this, &MainWindow::lanAdd); + connect(m_lanWidget, &LanPage::lanRemove, this, &MainWindow::lanRemove); + connect(m_lanWidget, &LanPage::lanUpdate, this, &MainWindow::lanUpdate); + connect(m_lanWidget, &LanPage::lanActiveConnectionStateChanged, this, &MainWindow::lanActiveConnectionStateChanged); + connect(m_lanWidget, &LanPage::lanConnectChanged, this, &MainWindow::onLanConnectStatusToChangeTrayIcon); + + + connect(m_wlanWidget, &WlanPage::wlanAdd, this, &MainWindow::wlanAdd); + connect(m_wlanWidget, &WlanPage::wlanRemove, this, &MainWindow::wlanRemove); + connect(m_wlanWidget, &WlanPage::wlanActiveConnectionStateChanged, this, &MainWindow::wlanactiveConnectionStateChanged); + connect(m_wlanWidget, &WlanPage::wlanConnectChanged, this, &MainWindow::onWlanConnectStatusToChangeTrayIcon); + connect(m_wlanWidget, &WlanPage::hotspotDeactivated, this, &MainWindow::hotspotDeactivated); + connect(m_wlanWidget, &WlanPage::hotspotActivated, this, &MainWindow::hotspotActivated); + connect(m_wlanWidget, &WlanPage::secuTypeChange, this, &MainWindow::secuTypeChange); + connect(m_wlanWidget, &WlanPage::signalStrengthChange, this, &MainWindow::signalStrengthChange); + connect(m_wlanWidget, &WlanPage::timeToUpdate , this, &MainWindow::timeToUpdate); + connect(m_wlanWidget, &WlanPage::showMainWindow, this, &MainWindow::onShowMainWindow); + connect(m_wlanWidget, &WlanPage::connectivityChanged, this, &MainWindow::onConnectivityChanged); + + //模式切换 + QDBusConnection::sessionBus().connect(QString("com.kylin.statusmanager.interfacer"), + QString("/"), + QString("com.kylin.statusmanager.interface"), + QString("mode_change_signal"), this, SLOT(onTabletModeChanged(bool))); +} + +/** + * @brief MainWindow::resetWindowPosition 重新计算窗口位置 + */ +void MainWindow::resetWindowPosition() +{ + + if (m_isShowInCenter) { + QRect availableGeometry = qApp->primaryScreen()->availableGeometry(); + this->move((availableGeometry.width() - this->width())/2, (availableGeometry.height() - this->height())/2); + return; + } + +#define MARGIN 4 +#define PANEL_TOP 1 +#define PANEL_LEFT 2 +#define PANEL_RIGHT 3 +//#define PANEL_BOTTOM 4 + if (!m_positionInterface) { + m_positionInterface = new QDBusInterface("org.ukui.panel", + "/panel/position", + "org.ukui.panel", + QDBusConnection::sessionBus()); + } + QDBusReply reply = m_positionInterface->call("GetPrimaryScreenGeometry"); + //reply获取的参数共5个,分别是 主屏可用区域的起点x坐标,主屏可用区域的起点y坐标,主屏可用区域的宽度,主屏可用区域高度,任务栏位置 + if (!m_positionInterface->isValid() || !reply.isValid() || reply.value().size() < 5) { + qCritical() << QDBusConnection::sessionBus().lastError().message(); + this->setGeometry(0, 0, this->width(), this->height()); + return; + } + QVariantList position_list = reply.value(); + int position = position_list.at(4).toInt(); + switch(position){ + case PANEL_TOP: + //任务栏位于上方 + this->setGeometry(position_list.at(0).toInt() + position_list.at(2).toInt() - this->width() - MARGIN, + position_list.at(1).toInt() + MARGIN, + this->width(), this->height()); + break; + //任务栏位于左边 + case PANEL_LEFT: + this->setGeometry(position_list.at(0).toInt() + MARGIN, + position_list.at(1).toInt() + reply.value().at(3).toInt() - this->height() - MARGIN, + this->width(), this->height()); + break; + //任务栏位于右边 + case PANEL_RIGHT: + this->setGeometry(position_list.at(0).toInt() + position_list.at(2).toInt() - this->width() - MARGIN, + position_list.at(1).toInt() + reply.value().at(3).toInt() - this->height() - MARGIN, + this->width(), this->height()); + break; + //任务栏位于下方 + default: + this->setGeometry(position_list.at(0).toInt() + position_list.at(2).toInt() - this->width() - MARGIN, + position_list.at(1).toInt() + reply.value().at(3).toInt() - this->height() - MARGIN, + this->width(), this->height()); + break; + } + qDebug() << " Position of ukui-panel is " << position << "; Position of mainwindow is " << this->geometry() << "." << Q_FUNC_INFO << __LINE__; +} + +/** + * @brief MainWindow::resetTrayIconTool 重新获取网络连接状态并重新设置图标和tooltip + */ +void MainWindow::resetTrayIconTool() +{ + //ZJP_TODO 检测当前连接的是有线还是无线,是否可用,设置图标和tooltip,图标最好提前define +// int connectivity = objKyDBus->getNetworkConectivity(); +// qDebug() << "Value of current network Connectivity property : "<< connectivity; +// switch (connectivity) { +// case UnknownConnectivity: +// case Portal: +// case Limited: +// setTrayIcon(iconLanOnlineNoInternet); +// trayIcon->setToolTip(QString(tr("Network Connected But Can Not Access Internet"))); +// break; +// case NoConnectivity: +// case Full: +// setTrayIcon(iconLanOnline); +// trayIcon->setToolTip(QString(tr("kylin-nm"))); +// break; +// } + qDebug() << "Has set tray icon to be XXX." << Q_FUNC_INFO << __LINE__; +} + + +/** + * @brief MainWindow::initWindowTheme 初始化窗口主题并创建信号槽 + */ +void MainWindow::initWindowTheme() +{ + const QByteArray style_id(THEME_SCHAME); + if (QGSettings::isSchemaInstalled(style_id)) { + m_styleGsettings = new QGSettings(style_id); +// resetWindowTheme(); + connect(m_styleGsettings, &QGSettings::changed, this, &MainWindow::onThemeChanged); + } else { + qWarning() << "Gsettings interface \"org.ukui.style\" is not exist!" << Q_FUNC_INFO << __LINE__; + } +} + +/** + * @brief MainWindow::resetWindowTheme 读取和设置窗口主题 + */ +void MainWindow::resetWindowTheme() +{ + if (!m_styleGsettings) { return; } + QString currentTheme = m_styleGsettings->get(COLOR_THEME).toString(); + auto app = static_cast(QCoreApplication::instance()); + if(currentTheme == "ukui-dark" || currentTheme == "ukui-black"){ + app->setStyle(new CustomStyle("ukui-dark")); + qDebug() << "Has set color theme to ukui-dark." << Q_FUNC_INFO << __LINE__; + emit qApp->paletteChanged(qApp->palette()); + return; + } + app->setStyle(new CustomStyle("ukui-light")); + qDebug() << "Has set color theme to " << currentTheme << Q_FUNC_INFO << __LINE__; + emit qApp->paletteChanged(qApp->palette()); + return; +} + +/** + * @brief MainWindow::showControlCenter 打开控制面板网络界面 + */ +void MainWindow::showControlCenter() +{ + QProcess process; + if (!m_lanWidget->lanIsConnected() && m_wlanWidget->checkWlanStatus(NetworkManager::ActiveConnection::State::Activated)){ + process.startDetached("ukui-control-center -m wlanconnect"); + } else { + process.startDetached("ukui-control-center -m netconnect"); + } +} + +/** + * @brief MainWindow::onTrayIconActivated 点击托盘图标的槽函数 + */ +void MainWindow::onTrayIconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::ActivationReason::Context) { + m_trayIconMenu->popup(QCursor::pos()); + } else { + if (this->isVisible()) { + qDebug() << "Received signal of tray icon activated, will hide mainwindow." << Q_FUNC_INFO << __LINE__; + hideMainwindow(); + return; + } + qDebug() << "Received signal of tray icon activated, will show mainwindow." << Q_FUNC_INFO << __LINE__; + this->showMainwindow(); + } +} + +void MainWindow::onShowMainwindowActionTriggled() +{ + showMainwindow(); +} + +void MainWindow::onShowSettingsActionTriggled() +{ + showControlCenter(); +} + +void MainWindow::onThemeChanged(const QString &key) +{ + if (key == COLOR_THEME) { + qDebug() << "Received signal of theme changed, will reset theme." << Q_FUNC_INFO << __LINE__; +// resetWindowTheme(); + paintWithTrans(); + emit qApp->paletteChanged(qApp->palette()); + } else { + qDebug() << "Received signal of theme changed, key=" << key << " will do nothing." << Q_FUNC_INFO << __LINE__; + } +} + +void MainWindow::onRefreshTrayIcon() +{ + //更新托盘图标显示 + iconTimer->stop(); + if (m_lanWidget->lanIsConnected()) { + m_trayIcon->setIcon(QIcon::fromTheme("network-wired-connected-symbolic")); + iconStatus = IconActiveType::LAN_CONNECTED; + } else if (m_wlanWidget->checkWlanStatus(NetworkManager::ActiveConnection::State::Activated)){ + m_trayIcon->setIcon(QIcon::fromTheme("network-wireless-connected-symbolic")); + iconStatus = IconActiveType::WLAN_CONNECTED; + } else { + m_trayIcon->setIcon(QIcon::fromTheme("network-wired-disconnected-symbolic")); + iconStatus = IconActiveType::NOT_CONNECTED; + } + + NetworkManager::Connectivity connecttivity; + m_wlanWidget->getConnectivity(connecttivity); + if (connecttivity != NetworkManager::Connectivity::Full) { + if (iconStatus == IconActiveType::LAN_CONNECTED) { + m_trayIcon->setIcon(QIcon::fromTheme("network-error-symbolic")); + iconStatus = IconActiveType::LAN_CONNECTED_LIMITED; + } else if (iconStatus == IconActiveType::WLAN_CONNECTED) { + //todo 信号强度 + m_trayIcon->setIcon(QIcon::fromTheme("network-wireless-signal-excellent-error-symbolic")); + iconStatus = IconActiveType::WLAN_CONNECTED_LIMITED; + } + } +} + +void MainWindow::onSetTrayIconLoading() +{ + if (currentIconIndex > 11) { + currentIconIndex = 0; + } + m_trayIcon->setIcon(loadIcons.at(currentIconIndex)); + iconStatus = IconActiveType::ACTIVATING; + currentIconIndex ++; +} + +void MainWindow::onLanConnectStatusToChangeTrayIcon(int state) +{ + qDebug() << "lan state:" << state << Q_FUNC_INFO << __LINE__; + if (state==1 || state==3){ + m_lanIsLoading = true; + iconTimer->start(LOADING_TRAYICON_TIMER_MS); + } else { + m_lanIsLoading = false; + if (m_wlanIsLoading == false) { + onRefreshTrayIcon(); + } + } +} + +void MainWindow::onWlanConnectStatusToChangeTrayIcon(int state) +{ + qDebug() << "wlan state:" << state << Q_FUNC_INFO << __LINE__; + if (state==1 || state==3){ + m_wlanIsLoading = true; + iconTimer->start(LOADING_TRAYICON_TIMER_MS); + } else { + if (m_wlanWidget->checkWlanStatus(NetworkManager::ActiveConnection::State::Activating)) { + return; + } + m_wlanIsLoading = false; + if (m_lanIsLoading == false) { + onRefreshTrayIcon(); + } + } +} + +void MainWindow::onTabletModeChanged(bool mode) +{ + qDebug() << "TabletMode change" << mode; + Q_UNUSED(mode) + //模式切换时,隐藏主界面 + hideMainwindow(); +} + +void MainWindow::onShowMainWindow(int type) +{ + m_centralWidget->setCurrentIndex(type); + + if(QApplication::activeWindow() != this) { + this->showMainwindow(); + } +} + +void MainWindow::onConnectivityChanged(NetworkManager::Connectivity connectivity) +{ + if (!m_trayIcon) { + return; + } + + if (iconStatus == ACTIVATING) { + return; + } + + onRefreshTrayIcon(); +} + +/** + * @brief MainWindow::keyPressEvent 按esc键关闭主界面 + * @param event + */ +void MainWindow::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) { + hideMainwindow(); + } + return QWidget::keyPressEvent(event); +} + +/** + * @brief MainWindow::eventFilter 事件过滤器 + * @param watched + * @param event + * @return + */ +bool MainWindow::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::ActivationChange) { + if(QApplication::activeWindow() != this) { + hideMainwindow(); + } + } + return QMainWindow::eventFilter(watched,event); +} + +/** + * @brief MainWindow::getWirelessList 获取wifi列表,供dbus调用 + * @param map + */ +void MainWindow::getWirelessList(QMap > &map) +{ + map.clear(); + if (nullptr != m_wlanWidget) { + m_wlanWidget->getWirelessList(map); + } +} + +/** + * @brief MainWindow::getWiredList 获取lan列表,供dbus调用 + * @param map + */ +void MainWindow::getWiredList(QMap> &map) +{ + map.clear(); + if (nullptr != m_lanWidget) { + m_lanWidget->getWiredList(map); + } +} + +/** + * @brief MainWindow::activeWirelessAp 开启热点,供dbus调用 + * @param apName + * @param apPassword + * @param apDevice + */ +void MainWindow::activeWirelessAp(const QString apName, const QString apPassword, const QString band, const QString apDevice) +{ + m_wlanWidget->activeWirelessAp(apName, apPassword, band, apDevice); +} + +/** + * @brief MainWindow::activeWirelessAp 断开热点,供dbus调用 + * @param apName + */ +void MainWindow::deactiveWirelessAp(const QString apName, const QString uuid) +{ + m_wlanWidget->deactiveWirelessAp(apName, uuid); +} + +/** + * @brief MainWindow::activeWirelessAp 获取热点,供dbus调用 + * @param list + */ +void MainWindow::getApInfoBySsid(QString devName, QString ssid, QStringList &list) +{ + m_wlanWidget->getApInfoBySsid(devName, ssid, list); +} + +void MainWindow::getStoredApInfo(QStringList &list) +{ + m_wlanWidget->getStoredApInfo(list); +} + +//无线开关 +void MainWindow::setWirelessSwitchEnable(bool enable) +{ + m_wlanWidget->setWirelessSwitchEnable(enable); +} + +void MainWindow::setWiredDeviceEnable(const QString& devName, bool enable) +{ + m_lanWidget->setWiredDeviceEnable(devName, enable); +} +void MainWindow::showPropertyWidget(QString devName, QString ssid) +{ + KyNetworkDeviceResourse *devResourse = new KyNetworkDeviceResourse(); + QStringList wiredDeviceList; + wiredDeviceList.clear(); + devResourse->getNetworkDeviceList(NetworkManager::Device::Type::Ethernet, wiredDeviceList); + if (wiredDeviceList.contains(devName)) { + qDebug() << "showPropertyWidget device type wired device name " << devName << " uuid " << ssid; + m_lanWidget->showDetailPage(devName, ssid); + delete devResourse; + devResourse = nullptr; + return; + } + + QStringList wirelessDeviceList; + wirelessDeviceList.clear(); + devResourse->getNetworkDeviceList(NetworkManager::Device::Type::Wifi, wirelessDeviceList); + if (wirelessDeviceList.contains(devName)) { + qDebug() << "showPropertyWidget device type wireless device name " << devName << " ssid " << ssid; + m_wlanWidget->showDetailPage(devName, ssid); + delete devResourse; + devResourse = nullptr; + return; + } + + qWarning() << "showPropertyWidget no such device " << devName; + delete devResourse; + devResourse = nullptr; +} + +void MainWindow::showCreateWiredConnectWidget(const QString devName) +{ + qDebug() << "showCreateWiredConnectWidget! devName = " << devName; + if (m_createPagePtrMap.contains(devName)) { + if (m_createPagePtrMap[devName] != nullptr) { + qDebug() << "showCreateWiredConnectWidget" << devName << "already create,just raise"; + + KWindowSystem::raiseWindow(m_createPagePtrMap[devName]->winId()); + return; + } + } + NetDetail *netDetail = new NetDetail(devName, "", "", false, false, true, this); + connect(netDetail, &NetDetail::createPageClose, [&](QString interfaceName){ + if (m_createPagePtrMap.contains(interfaceName)) { + m_createPagePtrMap[interfaceName] = nullptr; + } + }); + m_createPagePtrMap.insert(devName, netDetail); + netDetail->show(); +} + +void MainWindow::getWirelessDeviceCap(QMap &map) +{ + m_wlanWidget->getWirelessDeviceCap(map); +} + +//有线连接断开 +void MainWindow::activateWired(const QString& devName, const QString& connUuid) +{ + m_lanWidget->activateWired(devName, connUuid); +} +void MainWindow::deactivateWired(const QString& devName, const QString& connUuid) +{ + m_lanWidget->deactivateWired(devName, connUuid); +} + +//无线连接断开 +void MainWindow::activateWireless(const QString& devName, const QString& ssid) +{ + m_wlanWidget->activateWirelessConnection(devName, ssid); +} + +void MainWindow::deactivateWireless(const QString& devName, const QString& ssid) +{ + m_wlanWidget->deactivateWirelessConnection(devName, ssid); +} + +void MainWindow::rescan() +{ + m_wlanWidget->requestScan(); +} + +void MainWindow::keyRingInit() +{ + agent_init(); +} + +void MainWindow::keyRingClear() +{ + agent_clear(); +} diff --git a/src/frontend/mainwindow.h b/src/frontend/mainwindow.h new file mode 100644 index 00000000..95ccf4a6 --- /dev/null +++ b/src/frontend/mainwindow.h @@ -0,0 +1,183 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lanpage.h" +#include "wlanpage.h" +#include "netdetails/netdetail.h" +#include + +#ifdef WITHKYSEC +#include +#include +#endif + +enum IconActiveType { + NOT_CONNECTED = 0, + LAN_CONNECTED, + WLAN_CONNECTED, + LAN_CONNECTED_LIMITED, + WLAN_CONNECTED_LIMITED, + ACTIVATING, +}; + +const QByteArray TRANSPARENCY_GSETTINGS = "org.ukui.control-center.personalise"; + +class LanPage; + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = nullptr); + void showMainwindow(); + void hideMainwindow(); + + void setWiredDefaultDevice(QString deviceName); + void setWirelessDefaultDevice(QString deviceName); + + //for dbus + void getWirelessList(QMap > &map); + void getWiredList(QMap> &map); + //开启热点 + void activeWirelessAp(const QString apName, const QString apPassword, const QString wirelessBand, const QString apDevice); + //断开热点 + void deactiveWirelessAp(const QString apName, const QString uuid); + //获取热点 + void getStoredApInfo(QStringList &list); + void getApInfoBySsid(QString devName, QString ssid, QStringList &list); + //有线连接断开 + void activateWired(const QString& devName, const QString& connUuid); + void deactivateWired(const QString& devName, const QString& connUuid); + //无线连接断开 + void activateWireless(const QString& devName, const QString& ssid); + void deactivateWireless(const QString& devName, const QString& ssid); + //无线总开关 + void setWirelessSwitchEnable(bool enable); + + void setWiredDeviceEnable(const QString& devName, bool enable); + + //唤起属性页 根据网卡类型 参数2 为ssid/uuid + void showPropertyWidget(QString devName, QString ssid); + //唤起新建有线连接界面 + void showCreateWiredConnectWidget(const QString devName); + + void getWirelessDeviceCap(QMap &map); + + void rescan(); + + void keyRingInit(); + void keyRingClear(); + +signals: + //设备插拔 + void deviceStatusChanged(); + //设备名称变化 + void deviceNameChanged(QString oldName, QString newName, int type); + //有线无线列表更新(有线增删、无线增加减少) + void lanAdd(QString devName, QStringList info); + void lanRemove(QString dbusPath); + void lanUpdate(QString devName, QStringList info); + void wlanAdd(QString devName, QStringList info); + void wlanRemove(QString devName,QString ssid); + void wlanactiveConnectionStateChanged(QString devName, QString ssid, QString uuid, int status); + void lanActiveConnectionStateChanged(QString devName, QString uuid, int status); + void activateFailed(QString errorMessage); + void deactivateFailed(QString errorMessage); + //热点断开 + void hotspotDeactivated(QString devName, QString ssid); + void hotspotActivated(QString devName, QString ssid, QString uuid); + //信号强度变化 + void signalStrengthChange(QString devName, QString ssid, int strength); + //安全性变化 + void secuTypeChange(QString devName, QString ssid, QString secuType); + void mainWindowVisibleChanged(const bool &visible); + //列表排序 + void timeToUpdate(); +public slots: + +protected: + void keyPressEvent(QKeyEvent *event); + bool eventFilter(QObject *watched, QEvent *event) override; + void paintEvent(QPaintEvent *event); + +private: + void firstlyStart(); //一级启动 + void secondaryStart(); //二级启动 + bool m_loadFinished = false; //是否二级启动已执行完 + QTimer * m_secondaryStartTimer = nullptr; //执行二级启动的倒计时 + void initPlatform(); + void initWindowProperties(); + void initTransparency(); + void paintWithTrans(); + void initUI(); + void initDbusConnnect(); + void initTrayIcon(); + void resetTrayIconTool(); + void initWindowTheme(); + void resetWindowTheme(); + void showControlCenter(); + double m_transparency=1.0; //透明度 + QGSettings * m_transGsettings; //透明度配置文件 + int currentIconIndex=0; + QList loadIcons; + QTimer *iconTimer = nullptr; + + //主窗口的主要构成控件 + QTabWidget * m_centralWidget = nullptr; + QHBoxLayout * m_tabBarLayout = nullptr; + QLabel * m_lanLabel = nullptr; + QLabel * m_wlanLabel = nullptr; + + LanPage * m_lanWidget = nullptr; + WlanPage * m_wlanWidget = nullptr; + + //监听主题的Gsettings + QGSettings * m_styleGsettings = nullptr; + + //获取和重置窗口位置 + void resetWindowPosition(); + QDBusInterface * m_positionInterface = nullptr; + + //托盘图标,托盘图标右键菜单 + QSystemTrayIcon * m_trayIcon = nullptr; + QMenu * m_trayIconMenu = nullptr; + QAction * m_showMainwindowAction = nullptr; + QAction * m_showSettingsAction = nullptr; + + bool m_lanIsLoading = false; + bool m_wlanIsLoading = false; + + bool m_isShowInCenter = false; + + IconActiveType iconStatus = IconActiveType::NOT_CONNECTED; + + QMap m_createPagePtrMap; + +public slots: + void onShowMainWindow(int type); + +private slots: + void onTransChanged(); + void onTrayIconActivated(QSystemTrayIcon::ActivationReason reason); + void onShowMainwindowActionTriggled(); + void onShowSettingsActionTriggled(); + void onThemeChanged(const QString &key); + void onRefreshTrayIcon(); + void onSetTrayIconLoading(); + void onLanConnectStatusToChangeTrayIcon(int state); + void onWlanConnectStatusToChangeTrayIcon(int state); + void onConnectivityChanged(NetworkManager::Connectivity connectivity); + void onTabletModeChanged(bool mode); +}; + +#endif // MAINWINDOW_H diff --git a/src/frontend/netdetails/coninfo.h b/src/frontend/netdetails/coninfo.h new file mode 100644 index 00000000..abb36082 --- /dev/null +++ b/src/frontend/netdetails/coninfo.h @@ -0,0 +1,269 @@ +#ifndef CONINFO_H +#define CONINFO_H + +#include +#include +#include +#include +#include +#include +#include "kywirelessnetitem.h" +#include "kylinconnectresource.h" +#include "kylinactiveconnectresource.h" +#include "kywirelessconnectoperation.h" +#include "kywirelessnetresource.h" +#include "kyenterpricesettinginfo.h" + +#define AUTO_CONFIG 0 +#define MANUAL_CONFIG 1 + +enum PeapInnerType +{ + MSCHAPV2_PEAP = 0, + MD5_PEAP, + GTC_PEAP, +}; + +enum TtlsInnerType +{ + PAP = 0, + MSCHAP, + MSCHAPV2_EAP, + MSCHAPV2, + CHAP, + MD5_EAP, + GTC_EAP +}; + +class LineEdit : public QLineEdit +{ + Q_OBJECT + +public: + explicit LineEdit(QWidget *parent = nullptr) : QLineEdit(parent) {} + ~LineEdit() {} + +protected: + void contextMenuEvent(QContextMenuEvent *event) { + QMenu *menu = createStandardContextMenu();//默认的标准右键菜单,如果不需要刻意完全自己实现 + menu->setPalette(this->palette()); + menu->exec(event->globalPos()); + delete menu; + } +}; + +class ConInfo { +public: + QString strConName; + QString strConType; + QString strSecType; + KySecuType secType = WPA_AND_WPA2_PERSONAL; + QString strPassword; + QString strChan; + QString strMac; + QString strHz; + QString strBandWidth; + QString strDynamicIpv4; + QString strDynamicIpv6; + QString strDynamicIpv4Dns; + bool isAutoConnect = false; + + KyIpConfigType ipv4ConfigType = CONFIG_IP_DHCP; + QString strIPV4Address; + QString strIPV4NetMask; + QString strIPV4FirDns; + QString strIPV4SecDns; + QString strIPV4GateWay; + + KyIpConfigType ipv6ConfigType = CONFIG_IP_DHCP; + QString strIPV6Address; + int iIPV6Prefix; + QString strIPV6FirDns; + QString strIPV6SecDns; + QString strIPV6GateWay; + + KyEapMethodType enterpriseType; + KyEapMethodTlsInfo tlsInfo; + KyEapMethodPeapInfo peapInfo; + KyEapMethodTtlsInfo ttlsInfo; +}; + +static void setFramePalette(QFrame *widget, QPalette &pal) { + QList lineEditList = widget->findChildren(); + for (int i = 0; i < lineEditList.count(); ++i) { + lineEditList.at(i)->setPalette(pal); + lineEditList.at(i)->setContextMenuPolicy(Qt::DefaultContextMenu); + } + QList comboBoxList = widget->findChildren(); + for (int i = 0; i < comboBoxList.count(); ++i) { + comboBoxList.at(i)->setPalette(pal); + if (comboBoxList.at(i)->view()) { + comboBoxList.at(i)->view()->setPalette(pal); + } + } +} + +static QPalette lightPalette(QWidget *widget) +{ + auto palette = widget->palette(); + //ukui-light palette UKUI3.1 + QColor windowText_at(38, 38, 38), + windowText_iat(38, 38, 38), + windowText_dis(166, 166, 166), + button_at(230, 230, 230), + button_iat(230, 230, 230), + button_dis(233, 233, 233), + light_at(255, 255, 255), + light_iat(255, 255, 255), + light_dis(242, 242, 242), + midlight_at(218, 218, 218), + midlight_iat(218, 218, 218), + midlight_dis(230, 230, 230), + dark_at(77, 77, 77), + dark_iat(77, 77, 77), + dark_dis(64, 64, 64), + mid_at(115, 115, 115), + mid_iat(115, 115, 115), + mid_dis(102, 102, 102), + text_at(38, 38, 38), + text_iat(38, 38, 38), + text_dis(140, 140, 140), + brightText_at(89, 89, 89), + brightText_iat(89, 89, 89), + brightText_dis(77, 77, 77), + buttonText_at(38, 38, 38), + buttonText_iat(38, 38, 38), + buttonText_dis(179, 179, 179), + base_at(255, 255, 255), + base_iat(245, 245, 245), + base_dis(237, 237, 237), + window_at(245, 245, 245), + window_iat(237, 237, 237), + window_dis(230, 230, 230), + shadow_at(0, 0, 0, 16), + shadow_iat(0, 0, 0, 16), + shadow_dis(0, 0, 0, 21), +// shadow_at(214, 214, 214), +// shadow_iat(214, 214, 214), +// shadow_dis(201, 201, 201), + highLight_at(55, 144, 250), + highLight_iat(55, 144, 250), + highLight_dis(233, 233, 233), + highLightText_at(255, 255, 255), + highLightText_iat(255, 255, 255), + highLightText_dis(179, 179, 179), + link_at(55, 144, 250), + link_iat(55, 144, 250), + link_dis(55, 144, 250), + linkVisited_at(114, 46, 209), + linkVisited_iat(114, 46, 209), + linkVisited_dis(114, 46, 209), + alternateBase_at(245, 245, 245), + alternateBase_iat(245, 245, 245), + alternateBase_dis(245, 245, 245), + noRale_at(240, 240, 240), + noRole_iat(240, 240, 240), + noRole_dis(217, 217, 217), + toolTipBase_at(255, 255, 255), + toolTipBase_iat(255, 255, 255), + toolTipBase_dis(255, 255, 255), + toolTipText_at(38, 38, 38), + toolTipText_iat(38, 38, 38), + toolTipText_dis(38, 38, 38), + placeholderText_at(38, 38, 38), + placeholderText_iat(38, 38, 38), + placeholderText_dis(38, 38, 38); + + + + palette.setColor(QPalette::Active, QPalette::WindowText, windowText_at); + palette.setColor(QPalette::Inactive, QPalette::WindowText, windowText_iat); + palette.setColor(QPalette::Disabled, QPalette::WindowText, windowText_dis); + + palette.setColor(QPalette::Active, QPalette::Button, button_at); + palette.setColor(QPalette::Inactive, QPalette::Button, button_iat); + palette.setColor(QPalette::Disabled, QPalette::Button, button_dis); + + palette.setColor(QPalette::Active, QPalette::Light, light_at); + palette.setColor(QPalette::Inactive, QPalette::Light, light_iat); + palette.setColor(QPalette::Disabled, QPalette::Light, light_dis); + + palette.setColor(QPalette::Active, QPalette::Midlight, midlight_at); + palette.setColor(QPalette::Inactive, QPalette::Midlight, midlight_iat); + palette.setColor(QPalette::Disabled, QPalette::Midlight, midlight_dis); + + palette.setColor(QPalette::Active, QPalette::Dark, dark_at); + palette.setColor(QPalette::Inactive, QPalette::Dark, dark_iat); + palette.setColor(QPalette::Disabled, QPalette::Dark, dark_dis); + + palette.setColor(QPalette::Active, QPalette::Mid, mid_at); + palette.setColor(QPalette::Inactive, QPalette::Mid, mid_iat); + palette.setColor(QPalette::Disabled, QPalette::Mid, mid_dis); + + palette.setColor(QPalette::Active, QPalette::Text, text_at); + palette.setColor(QPalette::Inactive, QPalette::Text, text_iat); + palette.setColor(QPalette::Disabled, QPalette::Text, text_dis); + + palette.setColor(QPalette::Active, QPalette::BrightText, brightText_at); + palette.setColor(QPalette::Inactive, QPalette::BrightText, brightText_iat); + palette.setColor(QPalette::Disabled, QPalette::BrightText, brightText_dis); + + palette.setColor(QPalette::Active, QPalette::ButtonText, buttonText_at); + palette.setColor(QPalette::Inactive, QPalette::ButtonText, buttonText_iat); + palette.setColor(QPalette::Disabled, QPalette::ButtonText, buttonText_dis); + + palette.setColor(QPalette::Active, QPalette::Base, base_at); + palette.setColor(QPalette::Inactive, QPalette::Base, base_iat); + palette.setColor(QPalette::Disabled, QPalette::Base, base_dis); + + palette.setColor(QPalette::Active, QPalette::Window, window_at); + palette.setColor(QPalette::Inactive, QPalette::Window, window_iat); + palette.setColor(QPalette::Disabled, QPalette::Window, window_dis); + + palette.setColor(QPalette::Active, QPalette::Shadow, shadow_at); + palette.setColor(QPalette::Inactive, QPalette::Shadow, shadow_iat); + palette.setColor(QPalette::Disabled, QPalette::Shadow, shadow_dis); + + palette.setColor(QPalette::Active, QPalette::Highlight, highLight_at); + palette.setColor(QPalette::Inactive, QPalette::Highlight, highLight_iat); + palette.setColor(QPalette::Disabled, QPalette::Highlight, highLight_dis); + + palette.setColor(QPalette::Active, QPalette::HighlightedText, highLightText_at); + palette.setColor(QPalette::Inactive, QPalette::HighlightedText, highLightText_iat); + palette.setColor(QPalette::Disabled, QPalette::HighlightedText, highLightText_dis); + + palette.setColor(QPalette::Active, QPalette::Link, link_at); + palette.setColor(QPalette::Inactive, QPalette::Link, link_iat); + palette.setColor(QPalette::Disabled, QPalette::Link, link_dis); + + palette.setColor(QPalette::Active, QPalette::LinkVisited, linkVisited_at); + palette.setColor(QPalette::Inactive, QPalette::LinkVisited, linkVisited_iat); + palette.setColor(QPalette::Disabled, QPalette::LinkVisited, linkVisited_dis); + + palette.setColor(QPalette::Active, QPalette::AlternateBase, alternateBase_at); + palette.setColor(QPalette::Inactive, QPalette::AlternateBase, alternateBase_iat); + palette.setColor(QPalette::Disabled, QPalette::AlternateBase, alternateBase_dis); + + palette.setColor(QPalette::Active, QPalette::NoRole, noRale_at); + palette.setColor(QPalette::Inactive, QPalette::NoRole, noRole_iat); + palette.setColor(QPalette::Disabled, QPalette::NoRole, noRole_dis); + + palette.setColor(QPalette::Active, QPalette::ToolTipBase, toolTipBase_at); + palette.setColor(QPalette::Inactive, QPalette::ToolTipBase, toolTipBase_iat); + palette.setColor(QPalette::Disabled, QPalette::ToolTipBase, toolTipBase_dis); + + palette.setColor(QPalette::Active, QPalette::ToolTipText, toolTipText_at); + palette.setColor(QPalette::Inactive, QPalette::ToolTipText, toolTipText_iat); + palette.setColor(QPalette::Disabled, QPalette::ToolTipText, toolTipText_dis); + +#if (QT_VERSION >= QT_VERSION_CHECK(5,12,0)) + palette.setColor(QPalette::Active, QPalette::PlaceholderText, placeholderText_at); + palette.setColor(QPalette::Inactive, QPalette::PlaceholderText, placeholderText_iat); + palette.setColor(QPalette::Disabled, QPalette::PlaceholderText, placeholderText_dis); +#endif + + return palette; + +} + +#endif // CONINFO_H diff --git a/src/frontend/netdetails/creatnetpage.cpp b/src/frontend/netdetails/creatnetpage.cpp new file mode 100644 index 00000000..6034c50a --- /dev/null +++ b/src/frontend/netdetails/creatnetpage.cpp @@ -0,0 +1,227 @@ +#include "creatnetpage.h" +#include "math.h" + +#define MAX_NAME_LENGTH 32 + +CreatNetPage::CreatNetPage(QWidget *parent):QFrame(parent) +{ + initUI(); + initComponent(); +} + +void CreatNetPage::initUI() +{ + connNameEdit = new LineEdit(this); + connNameEdit->setMaxLength(MAX_NAME_LENGTH); + ipv4ConfigCombox = new QComboBox(this); + ipv4addressEdit = new LineEdit(this); + netMaskEdit = new LineEdit(this); + gateWayEdit = new LineEdit(this); + firstDnsEdit = new LineEdit(this); + secondDnsEdit = new LineEdit(this); + + m_connNameLabel = new QLabel(this); + m_configLabel = new QLabel(this); + m_addressLabel = new QLabel(this); + m_maskLabel = new QLabel(this); + m_gateWayLabel = new QLabel(this); + m_dnsLabel = new QLabel(this); + m_secDnsLabel = new QLabel(this); + + m_connNameLabel->setText(tr("Connection Name")); + m_configLabel->setText(tr("Ipv4Config")); + m_addressLabel->setText(tr("Address")); + m_maskLabel->setText(tr("Netmask")); + m_gateWayLabel->setText(tr("Default Gateway")); + m_dnsLabel->setText(tr("Prefs DNS")); + m_secDnsLabel->setText(tr("Alternative DNS")); + + m_detailLayout = new QFormLayout(this); + m_detailLayout->addRow(m_connNameLabel,connNameEdit); + m_detailLayout->addRow(m_configLabel,ipv4ConfigCombox); + m_detailLayout->addRow(m_addressLabel,ipv4addressEdit); + m_detailLayout->addRow(m_maskLabel,netMaskEdit); + m_detailLayout->addRow(m_gateWayLabel,gateWayEdit); + m_detailLayout->addRow(m_dnsLabel,firstDnsEdit); + m_detailLayout->addRow(m_secDnsLabel,secondDnsEdit); + + ipv4ConfigCombox->addItem(tr("Auto(DHCP)"), AUTO_CONFIG); //"自动(DHCP)" + ipv4ConfigCombox->addItem(tr("Manual"), MANUAL_CONFIG); //"手动" + + + // IP的正则格式限制 + QRegExp rx("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b"); + + ipv4addressEdit->setValidator(new QRegExpValidator(rx, this)); + gateWayEdit->setValidator(new QRegExpValidator(rx, this)); + netMaskEdit->setValidator(new QRegExpValidator(rx, this)); + firstDnsEdit->setValidator(new QRegExpValidator(rx, this)); + secondDnsEdit->setValidator(new QRegExpValidator(rx, this)); +} + +void CreatNetPage::initComponent() { + if (ipv4ConfigCombox->currentIndex() == AUTO_CONFIG) { + setLineEnabled(false); + } else if (ipv4ConfigCombox->currentIndex() == MANUAL_CONFIG) { + setLineEnabled(true); + } + connect(ipv4ConfigCombox, SIGNAL(currentIndexChanged(int)), this, SLOT(configChanged(int))); + + connect(connNameEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(ipv4ConfigCombox, SIGNAL(currentIndexChanged(int)), this, SLOT(setEnableOfSaveBtn())); + connect(netMaskEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(gateWayEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(firstDnsEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(secondDnsEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); +} + +bool CreatNetPage::checkConnectBtnIsEnabled() +{ + if (connNameEdit->text().isEmpty()) { + qDebug() << "create connName empty or invalid"; + return false; + } + qDebug() << "checkConnectBtnIsEnabled currentIndex" << ipv4ConfigCombox->currentIndex(); + if (ipv4ConfigCombox->currentIndex() == AUTO_CONFIG) { + return true; + } else { + if (ipv4addressEdit->text().isEmpty() || !getTextEditState(ipv4addressEdit->text())) { + qDebug() << "create ipv4address empty or invalid"; + return false; + } + + if (netMaskEdit->text().isEmpty() || !netMaskIsValide(netMaskEdit->text())) { + qDebug() << "create ipv4 netMask empty or invalid"; + return false; + } + + if (gateWayEdit->text().isEmpty() || !getTextEditState(gateWayEdit->text())) { + qDebug() << "create ipv4 gateway empty or invalid"; + return false; + } + + if (firstDnsEdit->text().isEmpty() && !secondDnsEdit->text().isEmpty()) { + qDebug() << "create ipv4 dns sort invalid"; + return false; + } + + if (!getTextEditState(firstDnsEdit->text())) { + qDebug() << "create ipv4 first dns invalid"; + return false; + } + + if (!getTextEditState(secondDnsEdit->text())) { + qDebug() << "create ipv4 second dns invalid"; + return false; + } + } + return true; +} + +void CreatNetPage::configChanged(int index) { + if (index == AUTO_CONFIG) { + setLineEnabled(false); + } + if (index == MANUAL_CONFIG) { + setLineEnabled(true); + } +} + +void CreatNetPage::setLineEnabled(bool check) { + + ipv4addressEdit->setEnabled(check); + netMaskEdit->setEnabled(check); + gateWayEdit->setEnabled(check); + firstDnsEdit->setEnabled(check); + secondDnsEdit->setEnabled(check); + + if (!check) { + ipv4addressEdit->clear(); + netMaskEdit->clear(); + gateWayEdit->clear(); + firstDnsEdit->clear(); + secondDnsEdit->clear(); + } +} + +void CreatNetPage::setEnableOfSaveBtn() { + emit setCreatePageState(checkConnectBtnIsEnabled()); +} + +bool CreatNetPage::getTextEditState(QString text) +{ + if (text.isEmpty()) { + return true; + } + QRegExp rx("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b"); + + bool match = false; + match = rx.exactMatch(text); + + return match; +} + +void CreatNetPage::constructIpv4Info(KyConnectSetting &setting) +{ + setting.m_connectName = connNameEdit->text(); + QString ipv4address =ipv4addressEdit->text(); + QString netMask = getNetMaskText(netMaskEdit->text()); + QString gateWay = gateWayEdit->text(); + qDebug() << "constructIpv4Info: " << "ipv4address " << ipv4address + << " netMask " << netMask + << " gateWay " << gateWay; + + QStringList dnsList; + dnsList.empty(); + if (!firstDnsEdit->text().isEmpty()) { + dnsList << firstDnsEdit->text(); + if (!secondDnsEdit->text().isEmpty()) { + dnsList << secondDnsEdit->text(); + } + } + if (ipv4ConfigCombox->currentData() == AUTO_CONFIG) { + setting.setIpConfigType(IPADDRESS_V4, CONFIG_IP_DHCP); + } else { + setting.setIpConfigType(IPADDRESS_V4, CONFIG_IP_MANUAL); + setting.ipv4AddressConstruct(ipv4address, netMask, gateWay, dnsList); + } + +} + +bool CreatNetPage::netMaskIsValide(QString text) +{ + if (getTextEditState(text)) { + return true; + } else { + if (text.length() > 0 && text.length() < 3) { + int num = text.toInt(); + if (num > 0 && num < 33) { + return true; + } + } + } + return false; +} + +QString CreatNetPage::getNetMaskText(QString text) +{ + if (text.length() > 2) { + return text; + } + + int num = text.toInt(); + QStringList list; + list << "0" << "0" << "0" << "0"; + int count = 0; + while (num - 8 >= 0) { + list[count] = "255"; + num = num - 8; + count ++; + } + if (num > 0) { + int size = pow(2, 8) - pow(2,(8-num)); + list[count] = QString::number(size); + } + return QString("%1.%2.%3.%4").arg(list[0],list[1],list[2],list[3]); +} + diff --git a/src/frontend/netdetails/creatnetpage.h b/src/frontend/netdetails/creatnetpage.h new file mode 100644 index 00000000..b0809ba0 --- /dev/null +++ b/src/frontend/netdetails/creatnetpage.h @@ -0,0 +1,62 @@ +#ifndef CREATNETPAGE_H +#define CREATNETPAGE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coninfo.h" + +class CreatNetPage : public QFrame +{ + Q_OBJECT +public: + CreatNetPage(QWidget *parent = nullptr); + + void constructIpv4Info(KyConnectSetting &setting); +private: + LineEdit *connNameEdit; + QComboBox *ipv4ConfigCombox; + LineEdit *ipv4addressEdit; + LineEdit *netMaskEdit; + LineEdit *gateWayEdit; + LineEdit *firstDnsEdit; + LineEdit *secondDnsEdit; + +private: + QFormLayout *m_detailLayout; + QVBoxLayout *mvBoxLayout; + QLabel *m_connNameLabel; + QLabel *m_configLabel; + QLabel *m_addressLabel; + QLabel *m_maskLabel; + QLabel *m_gateWayLabel; + QLabel *m_dnsLabel; + QLabel *m_secDnsLabel; +private: + void initUI(); + void initComponent(); + void setLineEnabled(bool check); + void configSave(); + bool getTextEditState(QString text); + bool checkConnectBtnIsEnabled(); + + bool netMaskIsValide(QString text); + QString getNetMaskText(QString text); + +private slots: + void setEnableOfSaveBtn(); + void configChanged(int index); +Q_SIGNALS: + void setCreatePageState(bool); + +}; + +#endif // CREATNETPAGE_H diff --git a/src/frontend/netdetails/customtabstyle.cpp b/src/frontend/netdetails/customtabstyle.cpp new file mode 100644 index 00000000..c8683cc8 --- /dev/null +++ b/src/frontend/netdetails/customtabstyle.cpp @@ -0,0 +1,73 @@ +#include "customtabstyle.h" + + +CustomTabStyle::CustomTabStyle() +{ + +} + +QSize CustomTabStyle::sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget) const +{ + QSize s = QProxyStyle::sizeFromContents(type, option, size, widget); + if (type == QStyle::CT_TabBarTab) { + s.transpose(); + s.rwidth() = 60; // 设置每个tabBar中item的大小 + s.rheight() = 36; + } + return s; +} + +void CustomTabStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const +{ + if (element == CE_TabBarTab) { + if (const QStyleOptionTab *tab = qstyleoption_cast(option)) { + QRect allRect = tab->rect; + if (tab->state & QStyle::State_Selected) { //选中状态:tab的Qlabel为矩形,底色为白色,边框淡灰色,文字为淡蓝色且加粗居中(具体颜色值由拾色器提取) + painter->save(); + painter->setPen(0x3790FA); //设置画笔颜色为淡灰色 + painter->setBrush(QBrush(0x3790FA)); //设置画刷为白色 + painter->drawRoundedRect(allRect.adjusted(0,0,0,0),6,6); //重绘tab的矩形边框 + painter->restore(); //还原为默认 + + painter->save(); + painter->setPen(0xffffff); //重新设置画笔颜色为淡蓝色 + QTextOption option; + option.setAlignment(Qt::AlignCenter); //设置文字居中 + painter->drawText(allRect, tab->text, option); //重绘文字 + painter->restore(); + } + else if(tab->state & QStyle::State_MouseOver) { //hover状态:tab的Qlabel为矩形,底色为灰色,边框仍未淡灰色,文字加粗居中 + painter->save(); + painter->setPen(tab->palette.color(QPalette::Window)); //设置画笔颜色为淡灰色 + painter->setBrush(tab->palette.color(QPalette::Window)); //设置画刷为灰色 + painter->drawRoundedRect(allRect.adjusted(0,0,0,0),6,6); //重绘tab的矩形边框 + painter->restore(); //还原 + + painter->save(); + QTextOption option; + option.setAlignment(Qt::AlignCenter); //设置文字居中 + painter->drawText(allRect, tab->text, option); //重绘文字 + painter->restore(); + } + else //其它的:tab的Qlabel为矩形,底色为灰色,边框为淡灰色不变,文字不加粗但居中 + { + painter->save(); + painter->setPen(tab->palette.color(QPalette::Button)); + painter->setBrush(tab->palette.color(QPalette::Button)); + painter->drawRoundedRect(allRect.adjusted(0,0,0,0),6,6); //重绘tab的矩形边框 + painter->restore(); + painter->save(); + QTextOption option; + option.setAlignment(Qt::AlignCenter); + painter->drawText(allRect, tab->text, option); + painter->restore(); + } + return; + } + } + + if (element == CE_TabBarTab) { + QProxyStyle::drawControl(element, option, painter, widget); + } +} + diff --git a/src/frontend/netdetails/customtabstyle.h b/src/frontend/netdetails/customtabstyle.h new file mode 100644 index 00000000..75a09825 --- /dev/null +++ b/src/frontend/netdetails/customtabstyle.h @@ -0,0 +1,19 @@ +#ifndef CUSTOMTABSTYLE_H +#define CUSTOMTABSTYLE_H + +#include +#include +#include +#include +#include +class CustomTabStyle : public QProxyStyle +{ +public: + CustomTabStyle(); + QSize sizeFromContents(ContentsType type, const QStyleOption *option, + const QSize &size, const QWidget *widget) const; + + void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const; +}; + +#endif // CUSTOMTABSTYLE_H diff --git a/src/frontend/netdetails/detailpage.cpp b/src/frontend/netdetails/detailpage.cpp new file mode 100644 index 00000000..381b913a --- /dev/null +++ b/src/frontend/netdetails/detailpage.cpp @@ -0,0 +1,346 @@ +#include "detailpage.h" +#include +#include + +#define MAX_NAME_LENGTH 32 +#define MAX_LABEL_WIDTH 250 +extern void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed); + +DetailPage::DetailPage(bool isWlan, bool isCreate, QWidget *parent) + : m_IsWlan(isWlan), m_IsCreate(isCreate), QFrame(parent) +{ + this->setFrameShape(QFrame::Shape::StyledPanel); + this->setMaximumWidth(960); + initUI(); + if (isCreate) { + connect(m_SSIDEdit, &LineEdit::textEdited, this, &DetailPage::setEnableOfSaveBtn); + } +} + +DetailPage::~DetailPage() +{ + if (m_copiedTip != nullptr) { + delete m_copiedTip; + m_copiedTip = nullptr; + } +} + +void DetailPage::setSSID(const QString &ssid) { + if (m_IsCreate) { + return; + } + m_formerSSID = ssid; + QFontMetrics fontMetrics(this->font()); + int fontSize = fontMetrics.width(ssid); + if (fontSize > this->width()) { + this->m_SSIDLabel->setText(fontMetrics.elidedText(ssid, Qt::ElideRight, this->width())); + this->setToolTip(ssid); + } else { + this->m_SSIDLabel->setText(ssid); + this->setToolTip(""); + } +} + +void DetailPage::setProtocol(const QString &protocol) { + this->m_Protocol->setText(protocol); +} + +void DetailPage::setSecType(const QString &secType) { + this->m_SecType->setText(secType); +} + +void DetailPage::setHz(const QString &hz) { + this->m_Hz->setText(hz); +} + +void DetailPage::setChan(const QString &chan) { + this->m_Chan->setText(chan); +} + +void DetailPage::setBandWidth(const QString &brandWidth) { + this->m_BandWidth->setText(brandWidth); +} + +void DetailPage::setIpv4(const QString &ipv4) { + this->m_IPV4->setText(ipv4); +} + +void DetailPage::setIpv4Dns(const QString &ipv4Dns) { + this->m_IPV4Dns->setText(ipv4Dns); +} + +void DetailPage::setIpv6(const QString &ipv6) { + m_formerIPV6 = ipv6; + this->m_IPV6->setText(ipv6); +} + +void DetailPage::setMac(const QString &mac) { + this->m_Mac->setText(mac); +} + +void DetailPage::setAutoConnect(bool flag) +{ + if (!m_IsWlan) { + return; + } + this->m_forgetNetBox->setChecked(flag); +} + +void DetailPage::getSsid(QString &ssid) +{ + if (m_IsCreate) { + ssid = m_SSIDEdit->text(); + } else { + ssid = m_SSIDLabel->text(); + } +} + +bool DetailPage::checkIsChanged(const ConInfo info) +{ + if (!m_IsWlan) { + return false; + } + if (info.isAutoConnect != m_forgetNetBox->isChecked()) { + return true; + } else { + return false; + } +} + +void DetailPage::addDetailItem(QListWidget *listWidget, QWidget *detailWidget) +{ + QListWidgetItem *listWidgetItem = new QListWidgetItem(listWidget); + listWidgetItem->setSizeHint(QSize(listWidget->width(),36)); + listWidgetItem->setFlags(Qt::NoItemFlags); + listWidget->addItem(listWidgetItem); + listWidget->setItemWidget(listWidgetItem, detailWidget); +} + +void DetailPage::newCopiedTip() +{ + //设置“复制成功”消息弹窗格式 + m_copiedTip = new KBallonTip(); + m_copiedTip->setTipType(Normal); + m_copiedTip->setFixedSize(158, 58); + m_copiedTip->setWindowFlags(Qt::FramelessWindowHint); + m_copiedTip->setAttribute(Qt::WA_TranslucentBackground, true); + m_copiedTip->setText(tr("Copied successfully!")); +} + +QPalette DetailPage::getTheme() +{ + //获取当前主题的颜色 + QPalette pal = qApp->palette(); + QGSettings * styleGsettings = nullptr; + const QByteArray style_id(THEME_SCHAME); + if (QGSettings::isSchemaInstalled(style_id)) { + styleGsettings = new QGSettings(style_id); + QString currentTheme = styleGsettings->get(COLOR_THEME).toString(); + if(currentTheme == "ukui-default"){ + pal = lightPalette(this); + } + } + if (styleGsettings != nullptr) { + delete styleGsettings; + styleGsettings = nullptr; + } + return pal; +} + +void DetailPage::initUI() { + m_layout = new QVBoxLayout(this); + m_layout->setContentsMargins(0,0,0,0); + + QWidget *mDetailFrame = new QFrame(this); + m_DetailLayout = new QVBoxLayout(mDetailFrame); + m_DetailLayout->setContentsMargins(0,0,0,0); + + m_listWidget = new QListWidget(mDetailFrame); + m_listWidget->setFrameShape(QFrame::Shape::NoFrame); + m_listWidget->setBackgroundRole(QPalette::Base); + m_listWidget->setFocusPolicy(Qt::FocusPolicy::NoFocus); + m_DetailLayout->addWidget(m_listWidget); + + if (!m_IsCreate) { + m_SSIDLabel = new QLabel(this); + m_SSIDLabel->adjustSize(); + m_SSIDLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + + m_netCopyButton = new QPushButton(this); + m_netCopyButton->setIcon(QIcon::fromTheme("edit-copy-symbolic")); + m_netCopyButton->setToolTip(tr("Copy all")); + //设置按钮背景颜色-透明 + QPalette btnPal = m_netCopyButton->palette(); + QColor color = qApp->palette().color(QPalette::Background); + color.setAlphaF(0); + btnPal.setColor(QPalette::Button, color); + m_netCopyButton->setPalette(btnPal); + + m_ssidWidget = new DetailWidget(qobject_cast(m_SSIDLabel), m_listWidget, m_netCopyButton); + connect(m_netCopyButton, &QPushButton::clicked, this, &DetailPage::on_btnCopyNetDetail_clicked); + newCopiedTip(); + +// mSSID->setStyleSheet("background:transparent;border-width:0px;border-style:none"); +// mSSID->setFocusPolicy(Qt::NoFocus); + } else { +// mSSID->setStyleSheet("border-width:1px;;border-style:solid;border-color:black;border-radius:2px"); + m_SSIDEdit = new LineEdit(this); + m_SSIDEdit->setMaximumWidth(MAX_LABEL_WIDTH); + m_SSIDEdit->setAlignment(Qt::AlignRight); + m_SSIDEdit->setStyleSheet("border-top:0px solid;border-bottom:1px solid;border-left:0px solid;border-right: 0px solid;background:transparent"); + m_SSIDEdit->setPlaceholderText(tr("Please input SSID:")); + m_SSIDEdit->setMaxLength(MAX_NAME_LENGTH); + m_ssidWidget = new DetailWidget(qobject_cast(m_SSIDEdit), m_listWidget); + } + + + m_ssidWidget->setKey(tr("SSID:")); + + m_Protocol = new QLabel(this); + m_protocolWidget = new DetailWidget(qobject_cast(m_Protocol), m_listWidget); + m_protocolWidget->setKey(tr("Protocol:")); + + m_SecType = new QLabel(this); + m_secTypeWidget = new DetailWidget(qobject_cast(m_SecType)); + m_secTypeWidget->setKey(tr("Security Type:")); + + m_Hz = new QLabel(this); + m_hzWidget = new DetailWidget(qobject_cast(m_Hz)); + m_hzWidget->setKey(tr("Hz:")); + + m_Chan = new QLabel(this); + m_chanelWidget = new DetailWidget(qobject_cast(m_Chan)); + m_chanelWidget->setKey(tr("Chan:")); + + m_BandWidth = new QLabel(this); + m_bandwidthWidget = new DetailWidget(qobject_cast(m_BandWidth), m_listWidget); + m_bandwidthWidget->setKey(tr("BandWidth:")); + + m_IPV4 = new QLabel(this); + m_ipv4Widget = new DetailWidget(qobject_cast(m_IPV4), m_listWidget); + m_ipv4Widget->setKey(tr("IPV4:")); + + m_IPV4Dns = new QLabel(this); + m_ipv4DnsWidget = new DetailWidget(qobject_cast(m_IPV4Dns), m_listWidget); + m_ipv4DnsWidget->setKey(tr("IPV4 Dns:")); + + m_IPV6 = new FixLabel(this); + m_IPV6->setFixedWidth(MAX_LABEL_WIDTH); + m_IPV6->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + m_ipv6Widget = new DetailWidget(qobject_cast(m_IPV6), m_listWidget); + m_ipv6Widget->setKey(tr("IPV6:")); + + m_Mac = new QLabel(this); + m_macWidget = new DetailWidget(qobject_cast(m_Mac), m_listWidget); + m_macWidget->setKey(tr("Mac:")); + + if (m_IsWlan) { + m_autoConnect = new QLabel(this); + m_forgetNetBox = new QCheckBox(this); + + m_autoConnect->setText(tr("Auto Connection")); + m_AutoLayout = new QHBoxLayout(this); + QSpacerItem *horizontalSpacer; + horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + m_AutoLayout->addSpacing(20); + m_AutoLayout->addWidget(m_forgetNetBox); + m_AutoLayout->addWidget(m_autoConnect); + m_AutoLayout->addSpacerItem(horizontalSpacer); + } + + this->addDetailItem(m_listWidget, m_ssidWidget); + this->addDetailItem(m_listWidget, m_protocolWidget); + if (m_IsWlan) { + this->addDetailItem(m_listWidget, m_secTypeWidget); + this->addDetailItem(m_listWidget, m_hzWidget); + this->addDetailItem(m_listWidget, m_chanelWidget); + } + this->addDetailItem(m_listWidget, m_bandwidthWidget); + this->addDetailItem(m_listWidget, m_ipv6Widget); + this->addDetailItem(m_listWidget, m_ipv4Widget); + this->addDetailItem(m_listWidget, m_ipv4DnsWidget); + this->addDetailItem(m_listWidget, m_macWidget); + + QPalette mpal(m_listWidget->palette()); + mpal.setColor(QPalette::Base, qApp->palette().base().color()); + mpal.setColor(QPalette::AlternateBase, qApp->palette().alternateBase().color()); + m_listWidget->setAlternatingRowColors(true); +// m_listWidget->setAutoFillBackground(true); + m_listWidget->setPalette(mpal); + + m_layout->addWidget(mDetailFrame); + if (m_IsWlan) { + m_layout->addLayout(m_AutoLayout); + } +} + +void DetailPage::setEnableOfSaveBtn() { + bool saveEnable = true; + if (m_IsCreate) { + saveEnable = !m_SSIDEdit->text().isEmpty(); + } + emit setDetailPageState(saveEnable); +} + +//获取列表信息 +void DetailPage::on_btnCopyNetDetail_clicked() +{ + QStringList netDetailList; + QString ssidCopy = tr("SSID:"); + QString protocolCopy = tr("Protocol:"); + QString securityCopy = tr("Security Type:"); + QString hzCopy= tr ("Hz:"); + QString chanCopy= tr ("Chan:"); + QString bandwithCopy = tr("BandWidth:"); + QString ipv4Copy = tr("IPV4:"); + QString ipv4dnsCopy = tr("IPV4 Dns:"); + QString ipv6Copy = tr("IPV6:"); + QString macCopy = tr("Mac:"); + QString netDetailCopyText; + + ssidCopy.append(m_formerSSID); + protocolCopy.append(this->m_Protocol->text()); + netDetailList.append(ssidCopy); + netDetailList.append(protocolCopy); + + if(m_IsWlan) + { + securityCopy.append(this->m_SecType->text()); + hzCopy.append(this->m_Hz->text()); + chanCopy.append(this->m_Chan->text()); + netDetailList.append(securityCopy); + netDetailList.append(hzCopy); + netDetailList.append(chanCopy); + } + + bandwithCopy += this->m_BandWidth->text(); + ipv6Copy += m_formerIPV6; + ipv4Copy += this->m_IPV4->text(); + ipv4dnsCopy += this->m_IPV4Dns->text(); + macCopy += this->m_Mac->text(); + netDetailList.append(bandwithCopy); + netDetailList.append(ipv4Copy); + netDetailList.append(ipv4dnsCopy); + netDetailList.append(ipv6Copy); + netDetailList.append(macCopy); +// qDebug() << netDetailList; + + //设置剪贴板内容 + netDetailCopyText = netDetailList.join("\n"); + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(netDetailCopyText); + + //设置“复制成功”弹窗位置并显示 + if (m_copiedTip != nullptr) { + delete m_copiedTip; + newCopiedTip(); + } + QPoint position = m_ssidWidget->mapToGlobal(this->pos()); + double x = 0.5 * (m_ssidWidget->width() - m_copiedTip->width()); + m_copiedTip->move(position.x() + x, position.y() + 150); + QPalette pal = getTheme(); + m_copiedTip->setPalette(pal); + m_copiedTip->showInfo(); +} + diff --git a/src/frontend/netdetails/detailpage.h b/src/frontend/netdetails/detailpage.h new file mode 100644 index 00000000..c7062629 --- /dev/null +++ b/src/frontend/netdetails/detailpage.h @@ -0,0 +1,102 @@ +#ifndef DETAILPAGE_H +#define DETAILPAGE_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "detailwidget.h" +#include "coninfo.h" +#include "kwidget.h" +#include "ktabbar.h" +#include "kballontip.h" +#define THEME_SCHAME "org.ukui.style" +#define COLOR_THEME "styleName" + +using namespace kdk; + +class DetailPage : public QFrame +{ + Q_OBJECT +public: + DetailPage(bool isWlan, bool isCreate = false, QWidget *parent = nullptr); + ~DetailPage(); + void setSSID(const QString &ssid); + void setProtocol(const QString &protocol); + void setSecType(const QString &secType); + void setHz(const QString &hz); + void setChan(const QString &chan); + void setBandWidth(const QString &brandWidth); + void setIpv4(const QString &ipv4); + void setIpv4Dns(const QString &ipv4Dns); + void setIpv6(const QString &ipv6); + void setMac(const QString &mac); + void setAutoConnect(bool flag); + + bool checkIsChanged(const ConInfo info); + + void getSsid(QString &ssid); + +private: + void initUI(); + void addDetailItem(QListWidget *listWidget, QWidget *detailWidget); + void newCopiedTip(); + QPalette getTheme(); + +public: + QListWidget *m_listWidget = nullptr; + DetailWidget *m_ssidWidget = nullptr; + DetailWidget *m_protocolWidget = nullptr; + DetailWidget *m_secTypeWidget = nullptr; + DetailWidget *m_hzWidget = nullptr; + DetailWidget *m_chanelWidget = nullptr; + DetailWidget *m_bandwidthWidget = nullptr; + DetailWidget *m_ipv4Widget = nullptr; + DetailWidget *m_ipv4DnsWidget = nullptr; + DetailWidget *m_ipv6Widget = nullptr; + DetailWidget *m_macWidget = nullptr; + + QPushButton *m_netCopyButton; + LineEdit *m_SSIDEdit; + QLabel *m_SSIDLabel; + QLabel *m_Protocol; + QLabel *m_SecType; + QLabel *m_Hz; + QLabel *m_Chan; + QLabel *m_BandWidth; + QLabel *m_IPV4; + QLabel *m_IPV4Dns; + FixLabel *m_IPV6; + QLabel *m_Mac; + QLabel *m_autoConnect; + KBallonTip *m_copiedTip = nullptr; + +// QWidget *autoFrame; +private: + QVBoxLayout *m_layout; + QVBoxLayout *m_DetailLayout; + QHBoxLayout *m_AutoLayout; + QCheckBox *m_forgetNetBox; + bool m_IsWlan; + bool m_IsCreate; + + QString m_formerSSID; + QString m_formerIPV6; + +private slots: + void setEnableOfSaveBtn(); + void on_btnCopyNetDetail_clicked(); + +signals: + void setDetailPageState(bool); + +}; + +#endif // DETAILPAGE_H diff --git a/src/frontend/netdetails/detailwidget.cpp b/src/frontend/netdetails/detailwidget.cpp new file mode 100644 index 00000000..0b4f4584 --- /dev/null +++ b/src/frontend/netdetails/detailwidget.cpp @@ -0,0 +1,82 @@ +#include "detailwidget.h" +#include +#include + +#define ITEM_HEIGHT 36 +#define ITEM_MARGINS 18,0,16,0 + +#define MAX_LABEL_WIDTH 138 + +FixLabel::FixLabel(QWidget *parent): + QLabel(parent) +{ + +} + +FixLabel::~FixLabel() +{ + +} + +void FixLabel::paintEvent(QPaintEvent *event) +{ + QFontMetrics fontMetrics(this->font()); + int fontSize = fontMetrics.width(mStr); + if (fontSize > this->width()) { + this->setText(fontMetrics.elidedText(mStr, Qt::ElideRight, this->width()), false); + this->setToolTip(mStr); + } else { + this->setText(mStr, false); + this->setToolTip(""); + } + QLabel::paintEvent(event); +} + +void FixLabel::setText(const QString & text, bool saveTextFlag) +{ + if (saveTextFlag) { + mStr = text; + } + QLabel::setText(text); +} + +DetailWidget::DetailWidget(QWidget *valueWidget, QWidget *parent, QWidget *buttonWidget) + : m_valueWidget(valueWidget) , QWidget(parent) , m_copyButton(buttonWidget) +{ +// m_valueWidget = valueWidget; + initUI(); +} + +DetailWidget::~DetailWidget() +{ + +} + +void DetailWidget::initUI() +{ + this->setFixedHeight(ITEM_HEIGHT); + m_mainLayout = new QHBoxLayout(this); + m_mainLayout->setContentsMargins(ITEM_MARGINS); + + m_keyLabel = new FixLabel(this); + m_keyLabel->setMaximumWidth(MAX_LABEL_WIDTH); + m_keyLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + + m_mainLayout->addWidget(m_keyLabel); + m_mainLayout->addStretch(); + if (m_copyButton != nullptr) { + m_copyButton->setMaximumWidth(ITEM_HEIGHT-8); + m_copyButton->setMaximumHeight(ITEM_HEIGHT-8); + m_mainLayout->addWidget(m_copyButton); + } + m_mainLayout->addWidget(m_valueWidget); + m_valueWidget->setMaximumWidth(100000000000); +} + +void DetailWidget::setKey(const QString &keyLabel) +{ + m_keyLabel->setText(keyLabel); +} + + + diff --git a/src/frontend/netdetails/detailwidget.h b/src/frontend/netdetails/detailwidget.h new file mode 100644 index 00000000..c97ac877 --- /dev/null +++ b/src/frontend/netdetails/detailwidget.h @@ -0,0 +1,45 @@ +#ifndef DetailWidget_H +#define DetailWidget_H + +#include +#include +#include + +//文本长自动省略并添加悬浮 +class FixLabel : public QLabel +{ + + Q_OBJECT +public: + FixLabel(QWidget *parent = nullptr); + ~FixLabel(); + void setText(const QString &text, bool saveTextFlag = true); +private: + void paintEvent(QPaintEvent *event); + +private: + QString mStr; +}; + +class DetailWidget : public QWidget +{ + Q_OBJECT +public: + explicit DetailWidget(QWidget *valueWidget = nullptr, QWidget *parent = nullptr, QWidget *buttonWidget = nullptr); + ~DetailWidget(); + + void setKey(const QString &keyLabel); + +private: + QHBoxLayout *m_mainLayout = nullptr; + FixLabel *m_keyLabel = nullptr; + QWidget *m_valueWidget = nullptr; + QWidget *m_copyButton; + + void initUI(); + +signals: + +}; + +#endif // DetailWidget_H diff --git a/src/frontend/netdetails/ipv4page.cpp b/src/frontend/netdetails/ipv4page.cpp new file mode 100644 index 00000000..7cc11f58 --- /dev/null +++ b/src/frontend/netdetails/ipv4page.cpp @@ -0,0 +1,280 @@ +#include "ipv4page.h" +#include "netdetail.h" +#include "math.h" + +Ipv4Page::Ipv4Page(QWidget *parent):QFrame(parent) +{ + initUI(); + initComponent(); +} + +void Ipv4Page::initUI() { + ipv4ConfigCombox = new QComboBox(this); + ipv4addressEdit = new LineEdit(this); + netMaskEdit = new LineEdit(this); + gateWayEdit = new LineEdit(this); + firstDnsEdit = new LineEdit(this); + secondDnsEdit = new LineEdit(this); + + m_configLabel = new QLabel(this); + m_addressLabel = new QLabel(this); + m_maskLabel = new QLabel(this); + m_gateWayLabel = new QLabel(this); + m_dnsLabel = new QLabel(this); + m_secDnsLabel = new QLabel(this); + + m_configLabel->setText(tr("Ipv4Config")); + m_addressLabel->setText(tr("Address")); + m_maskLabel->setText(tr("Netmask")); + m_gateWayLabel->setText(tr("Default Gateway")); + m_dnsLabel->setText(tr("Prefs DNS")); + m_secDnsLabel->setText(tr("Alternative DNS")); + + m_detailLayout = new QFormLayout(this); + m_detailLayout->addRow(m_configLabel,ipv4ConfigCombox); + m_detailLayout->addRow(m_addressLabel,ipv4addressEdit); + m_detailLayout->addRow(m_maskLabel,netMaskEdit); + m_detailLayout->addRow(m_gateWayLabel,gateWayEdit); + m_detailLayout->addRow(m_dnsLabel,firstDnsEdit); + m_detailLayout->addRow(m_secDnsLabel,secondDnsEdit); + + ipv4ConfigCombox->addItem(tr("Auto(DHCP)")); //"自动(DHCP)" + ipv4ConfigCombox->addItem(tr("Manual")); //"手动" + +// netMaskCombox->addItem(""); +// netMaskCombox->addItem("255.255.255.0"); //24 +// netMaskCombox->addItem("255.255.254.0"); //23 +// netMaskCombox->addItem("255.255.252.0"); //22 +// netMaskCombox->addItem("255.255.0.0"); //16 +// netMaskCombox->addItem("255.0.0.0"); //8 + + + // IP的正则格式限制 + QRegExp rx("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b"); + + ipv4addressEdit->setValidator(new QRegExpValidator(rx, this)); + gateWayEdit->setValidator(new QRegExpValidator(rx, this)); + netMaskEdit->setValidator(new QRegExpValidator(rx, this)); + firstDnsEdit->setValidator(new QRegExpValidator(rx, this)); + secondDnsEdit->setValidator(new QRegExpValidator(rx, this)); +} + +void Ipv4Page::initComponent() { + if (ipv4ConfigCombox->currentIndex() == AUTO_CONFIG) { + setLineEnabled(false); + } else if (ipv4ConfigCombox->currentIndex() == MANUAL_CONFIG) { + setLineEnabled(true); + } + connect(ipv4ConfigCombox, SIGNAL(currentIndexChanged(int)), this, SLOT(configChanged(int))); + + connect(ipv4ConfigCombox, SIGNAL(currentIndexChanged(int)), this, SLOT(setEnableOfSaveBtn())); + connect(ipv4addressEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(netMaskEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(gateWayEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(firstDnsEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(secondDnsEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); +} + +void Ipv4Page::setIpv4Config(KyIpConfigType ipv4Config) +{ + if (ipv4Config == CONFIG_IP_MANUAL) { + ipv4ConfigCombox->setCurrentIndex(MANUAL_CONFIG); + } else { + ipv4ConfigCombox->setCurrentIndex(AUTO_CONFIG); + } +} + +void Ipv4Page::setIpv4(const QString &ipv4) +{ + ipv4addressEdit->setText(ipv4); +} + +void Ipv4Page::setNetMask(const QString &netMask) +{ + netMaskEdit->setText(netMask); +} + +void Ipv4Page::setIpv4FirDns(const QString &ipv4FirDns) +{ + firstDnsEdit->setText(ipv4FirDns); +} + +void Ipv4Page::setIpv4SecDns(const QString &ipv4SecDns) +{ + secondDnsEdit->setText(ipv4SecDns); +} + +void Ipv4Page::setGateWay(const QString &gateWay) +{ + gateWayEdit->setText(gateWay); +} + +bool Ipv4Page::checkIsChanged(const ConInfo info, KyConnectSetting &setting) +{ + bool isChanged = false; + if (ipv4ConfigCombox->currentIndex() == AUTO_CONFIG) { + if (info.ipv4ConfigType != CONFIG_IP_DHCP) { + qDebug() << "ipv4ConfigType change to Auto"; + setting.setIpConfigType(IPADDRESS_V4, CONFIG_IP_DHCP); + QString ipv4address(""); + QString netMask(""); + QString gateWay(""); + QStringList dnsList; + dnsList.empty(); + qDebug() << ipv4address << netMask << gateWay; + setting.ipv4AddressConstruct(ipv4address, netMask, gateWay, dnsList); + isChanged = true; + } + } else { + if (info.ipv4ConfigType != CONFIG_IP_MANUAL) { + qDebug() << "ipv4ConfigType change to Manual"; + setting.setIpConfigType(IPADDRESS_V4, CONFIG_IP_MANUAL); + isChanged = true; + } + qDebug() << "ipv4 netmask " << getNetMaskText(netMaskEdit->text()); + if(info.strIPV4Address != ipv4addressEdit->text() + || info.strIPV4NetMask != /*netMaskEdit->text()*/getNetMaskText(netMaskEdit->text()) + || info.strIPV4GateWay != gateWayEdit->text() + || info.strIPV4FirDns != firstDnsEdit->text() + || info.strIPV4SecDns != secondDnsEdit->text()) { + + qDebug() << "ipv4 info changed"; + QStringList dnsList; + dnsList.empty(); + if (!firstDnsEdit->text().isEmpty()) { + dnsList << firstDnsEdit->text(); + if (!secondDnsEdit->text().isEmpty()) { + dnsList << secondDnsEdit->text(); + } + } + + QString ipv4address =ipv4addressEdit->text(); + QString netMask = getNetMaskText(netMaskEdit->text()); + QString gateWay = gateWayEdit->text(); + qDebug() << ipv4address << netMask << gateWay; + setting.ipv4AddressConstruct(ipv4address, netMask, gateWay, dnsList); + setting.dumpInfo(); + isChanged = true; + } + } + return isChanged; +} + +bool Ipv4Page::checkConnectBtnIsEnabled() +{ + qDebug() << "checkConnectBtnIsEnabled currentIndex" << ipv4ConfigCombox->currentIndex(); + if (ipv4ConfigCombox->currentIndex() == AUTO_CONFIG) { + return true; + } else { + if (ipv4addressEdit->text().isEmpty() || !getTextEditState(ipv4addressEdit->text())) { + qDebug() << "ipv4address empty or invalid"; + return false; + } + + if (netMaskEdit->text().isEmpty() || !netMaskIsValide(netMaskEdit->text())) { + qDebug() << "ipv4 netMask empty or invalid"; + return false; + } + +// if (gateWayEdit->text().isEmpty() || !getTextEditState(gateWayEdit->text())) { +// qDebug() << "ipv4 gateway empty or invalid"; +// return false; +// } + + if (firstDnsEdit->text().isEmpty() && !secondDnsEdit->text().isEmpty()) { + qDebug() << "ipv4 dns sort invalid"; + return false; + } + + if (!getTextEditState(firstDnsEdit->text())) { + qDebug() << "ipv4 first dns invalid"; + return false; + } + + if (!getTextEditState(secondDnsEdit->text())) { + qDebug() << "ipv4 second dns invalid"; + return false; + } + } + return true; +} + +void Ipv4Page::configChanged(int index) { + if (index == AUTO_CONFIG) { + setLineEnabled(false); + } + if (index == MANUAL_CONFIG) { + setLineEnabled(true); + } +} + +void Ipv4Page::setLineEnabled(bool check) { + + ipv4addressEdit->setEnabled(check); + netMaskEdit->setEnabled(check); + gateWayEdit->setEnabled(check); + firstDnsEdit->setEnabled(check); + secondDnsEdit->setEnabled(check); + + if (!check) { + ipv4addressEdit->clear(); + netMaskEdit->clear(); + gateWayEdit->clear(); + firstDnsEdit->clear(); + secondDnsEdit->clear(); + } +} + +void Ipv4Page::setEnableOfSaveBtn() { + emit setIpv4PageState(checkConnectBtnIsEnabled()); +} + +bool Ipv4Page::getTextEditState(QString text) +{ + if (text.isEmpty()) { + return true; + } + QRegExp rx("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b"); + + bool match = false; + match = rx.exactMatch(text); + + return match; +} + +bool Ipv4Page::netMaskIsValide(QString text) +{ + if (getTextEditState(text)) { + return true; + } else { + if (text.length() > 0 && text.length() < 3) { + int num = text.toInt(); + if (num > 0 && num < 33) { + return true; + } + } + } + return false; +} + +QString Ipv4Page::getNetMaskText(QString text) +{ + if (text.length() > 2) { + return text; + } + + int num = text.toInt(); + QStringList list; + list << "0" << "0" << "0" << "0"; + int count = 0; + while (num - 8 >= 0) { + list[count] = "255"; + num = num - 8; + count ++; + } + if (num > 0) { + int size = pow(2, 8) - pow(2,(8-num)); + list[count] = QString::number(size); + } + return QString("%1.%2.%3.%4").arg(list[0],list[1],list[2],list[3]); +} diff --git a/src/frontend/netdetails/ipv4page.h b/src/frontend/netdetails/ipv4page.h new file mode 100644 index 00000000..25ef07aa --- /dev/null +++ b/src/frontend/netdetails/ipv4page.h @@ -0,0 +1,67 @@ +#ifndef IPV4PAGE_H +#define IPV4PAGE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "kylinconnectsetting.h" +#include "coninfo.h" + +class Ipv4Page : public QFrame +{ + Q_OBJECT +public: + Ipv4Page(QWidget *parent = nullptr); + void setIpv4Config(KyIpConfigType ipv4Config); + void setIpv4(const QString &ipv4); + void setNetMask(const QString &netMask); + void setIpv4FirDns(const QString &ipv4FirDns); + void setIpv4SecDns(const QString &ipv4SecDns); + void setGateWay(const QString &gateWay); + + bool checkIsChanged(const ConInfo info, KyConnectSetting &setting); +private: + QComboBox *ipv4ConfigCombox; + LineEdit *ipv4addressEdit; + LineEdit *netMaskEdit; + LineEdit *gateWayEdit; + LineEdit *firstDnsEdit; + LineEdit *secondDnsEdit; + +private: + QFormLayout *m_detailLayout; + QVBoxLayout *mvBoxLayout; + QLabel *m_configLabel; + QLabel *m_addressLabel; + QLabel *m_maskLabel; + QLabel *m_gateWayLabel; + QLabel *m_dnsLabel; + QLabel *m_secDnsLabel; +private: + void initUI(); + void initComponent(); + void setLineEnabled(bool check); + void configSave(); + bool getTextEditState(QString text); + bool netMaskIsValide(QString text); + QString getNetMaskText(QString text); + bool checkConnectBtnIsEnabled(); + + +private slots: + void setEnableOfSaveBtn(); + void configChanged(int index); +Q_SIGNALS: + void setIpv4PageState(bool); + +}; + +#endif // IPV4PAGE_H diff --git a/src/frontend/netdetails/ipv6page.cpp b/src/frontend/netdetails/ipv6page.cpp new file mode 100644 index 00000000..d7453bec --- /dev/null +++ b/src/frontend/netdetails/ipv6page.cpp @@ -0,0 +1,257 @@ +#include "ipv6page.h" +#include "netdetail.h" + +Ipv6Page::Ipv6Page(QWidget *parent):QFrame(parent) +{ + initUI(); + initComponent(); +} + +void Ipv6Page::setIpv6Config(KyIpConfigType ipv6Config) +{ + if (ipv6Config == CONFIG_IP_MANUAL) { + ipv6ConfigCombox->setCurrentIndex(MANUAL_CONFIG); + } else { + ipv6ConfigCombox->setCurrentIndex(AUTO_CONFIG); + } +} + +void Ipv6Page::setIpv6(const QString &ipv6) +{ + ipv6AddressEdit->setText(ipv6); +} + +void Ipv6Page::setIpv6Perfix(const int &ipv6Perfix) +{ + lengthEdit->setText(QString::number(ipv6Perfix)); +} + +void Ipv6Page::setIpv6FirDns(const QString &ipv6FirDns) +{ + firstDnsEdit->setText(ipv6FirDns); +} + +void Ipv6Page::setIpv6SecDns(const QString &ipv6SecDns) +{ + secondDnsEdit->setText(ipv6SecDns); +} + +void Ipv6Page::setGateWay(const QString &gateWay) +{ + gateWayEdit->setText(gateWay); +} + +bool Ipv6Page::checkIsChanged(const ConInfo info, KyConnectSetting &setting) +{ + bool isChanged = false; + if (ipv6ConfigCombox->currentIndex() == AUTO_CONFIG) { + if (info.ipv6ConfigType != CONFIG_IP_DHCP) { + qDebug() << "ipv6ConfigType change to Auto"; + setting.setIpConfigType(IPADDRESS_V6, CONFIG_IP_DHCP); + QString ipv6address(""); + QString prefix(""); + QString gateWay(""); + QStringList dnsList; + dnsList.empty(); + setting.ipv6AddressConstruct(ipv6address, prefix, gateWay, dnsList); + isChanged = true; + } + } else { + if (info.ipv6ConfigType != CONFIG_IP_MANUAL) { + qDebug() << "ipv6ConfigType change to Manual"; + setting.setIpConfigType(IPADDRESS_V6, CONFIG_IP_MANUAL); + isChanged = true; + } + if(info.strIPV6Address != ipv6AddressEdit->text() + || info.iIPV6Prefix != lengthEdit->text().toInt() + || info.strIPV6GateWay != gateWayEdit->text() + || info.strIPV6FirDns != firstDnsEdit->text() + || info.strIPV6SecDns != secondDnsEdit->text()) { + + qDebug() << "ipv6 info changed"; + QStringList dnsList; + dnsList.empty(); + if (!firstDnsEdit->text().isEmpty()) { + dnsList << firstDnsEdit->text(); + if (!secondDnsEdit->text().isEmpty()) { + dnsList << secondDnsEdit->text(); + } + } + + QString ipv6address =ipv6AddressEdit->text(); + QString prefix = lengthEdit->text(); + QString gateWay = gateWayEdit->text(); + setting.ipv6AddressConstruct(ipv6address, prefix, gateWay, dnsList); + setting.dumpInfo(); + isChanged = true; + } + } + return isChanged; +} + +void Ipv6Page::initUI() { + ipv6ConfigCombox = new QComboBox(this); + ipv6AddressEdit = new LineEdit(this); + lengthEdit = new LineEdit(this); + gateWayEdit = new LineEdit(this); + firstDnsEdit = new LineEdit(this); + secondDnsEdit = new LineEdit(this); + + m_configLabel = new QLabel(this); + m_addressLabel = new QLabel(this); + m_subnetLabel = new QLabel(this); + m_gateWayLabel = new QLabel(this); + m_dnsLabel = new QLabel(this); + m_secDnsLabel = new QLabel(this); + + + m_configLabel->setText(tr("Ipv6Config")); + m_addressLabel->setText(tr("Address")); + m_subnetLabel->setText(tr("Subnet prefix Length")); + m_gateWayLabel->setText(tr("Default Gateway")); + m_dnsLabel->setText(tr("Prefs DNS")); + m_secDnsLabel->setText(tr("Alternative DNS")); + + + m_detailLayout = new QFormLayout(this); + m_detailLayout->addRow(m_configLabel,ipv6ConfigCombox); + m_detailLayout->addRow(m_addressLabel,ipv6AddressEdit); + m_detailLayout->addRow(m_subnetLabel,lengthEdit); + m_detailLayout->addRow(m_gateWayLabel,gateWayEdit); + m_detailLayout->addRow(m_dnsLabel,firstDnsEdit); + m_detailLayout->addRow(m_secDnsLabel,secondDnsEdit); + + ipv6ConfigCombox->addItem(tr("Auto(DHCP)")); //"自动(DHCP)" + ipv6ConfigCombox->addItem(tr("Manual")); //"手动" + + QRegExp ipv6_rx("^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$"); + ipv6AddressEdit->setValidator(new QRegExpValidator(ipv6_rx, this)); + gateWayEdit->setValidator(new QRegExpValidator(ipv6_rx, this)); + firstDnsEdit->setValidator(new QRegExpValidator(ipv6_rx, this)); + secondDnsEdit->setValidator(new QRegExpValidator(ipv6_rx, this)); + + QRegExp prefix_rx("\\b(?:(?:12[0-8]|1[0-1][0-9]|^[1-9][0-9]?$)\\.){3}(?:12[0-8]|1[0-1][0-9]|^[1-9][0-9]?$)\\b"); + lengthEdit->setValidator(new QRegExpValidator(prefix_rx,this)); +} + +void Ipv6Page::initComponent() { + if (ipv6ConfigCombox->currentIndex() == AUTO_CONFIG) { + setControlEnabled(false); + } else if (ipv6ConfigCombox->currentIndex() == MANUAL_CONFIG) { + setControlEnabled(true); + } + connect(ipv6ConfigCombox, SIGNAL(currentIndexChanged(int)), this, SLOT(configChanged(int))); + + connect(ipv6ConfigCombox, SIGNAL(currentIndexChanged(int)), this, SLOT(setEnableOfSaveBtn())); + connect(ipv6AddressEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(lengthEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(gateWayEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(firstDnsEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(secondDnsEdit, SIGNAL(textChanged(QString)), this, SLOT(setEnableOfSaveBtn())); +} + +void Ipv6Page::configChanged(int index) { + if (index == AUTO_CONFIG) { + setControlEnabled(false); + } + if (index == MANUAL_CONFIG) { + setControlEnabled(true); + } +} + +void Ipv6Page::setControlEnabled(bool check) +{ + ipv6AddressEdit->setEnabled(check); + lengthEdit->setEnabled(check); + gateWayEdit->setEnabled(check); + firstDnsEdit->setEnabled(check); + secondDnsEdit->setEnabled(check); + + if (!check) { + ipv6AddressEdit->clear(); + lengthEdit->clear(); + gateWayEdit->clear(); + firstDnsEdit->clear(); + secondDnsEdit->clear(); + } +} + +void Ipv6Page::setEnableOfSaveBtn() +{ + emit setIpv6PageState(checkConnectBtnIsEnabled()); +} + +bool Ipv6Page::checkConnectBtnIsEnabled() +{ + if (ipv6ConfigCombox->currentIndex() == AUTO_CONFIG) { + return true; + } else { + if (ipv6AddressEdit->text().isEmpty() || !getIpv6EditState(ipv6AddressEdit->text())) { + qDebug() << "ipv6address empty or invalid"; + return false; + } + + if (lengthEdit->text().isEmpty()) { + qDebug() << "ipv6 prefix length empty"; + return false; + } + + if (gateWayEdit->text().isEmpty() || !getIpv6EditState(gateWayEdit->text())) { + qDebug() << "ipv6 gateway empty or invalid"; + return false; + } + + if (firstDnsEdit->text().isEmpty() && !secondDnsEdit->text().isEmpty()) { + qDebug() << "ipv6 dns sort invalid"; + return false; + } + + if (!getIpv6EditState(firstDnsEdit->text())) { + qDebug() << "ipv6 first dns invalid"; + return false; + } + + if (!getIpv6EditState(secondDnsEdit->text())) { + qDebug() << "ipv6 second dns invalid"; + return false; + } + } + return true; +} + +bool Ipv6Page::getIpv6EditState(QString text) +{ + if (text.isEmpty()) { + return true; + } + QRegExp rx("^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$"); + + bool match = false; + match = rx.exactMatch(text); + + return match; +} + +int Ipv6Page::getPerfixLength(QString text) +{ + qDebug() << "getPerfixLength" << text; + int length = 0; + QStringList list= text.split(":"); + for (int i = 0; i < list.size(); ++i) { + QString temp = list.at(i); + if (temp.isEmpty()) { + continue; + } + bool ok; + unsigned int val = temp.toUInt(&ok, 16); + temp = temp.setNum(val,2); + for(int j = 0; j < temp.length(); ++j) { + if (temp.at(j) == "1") { + length++; + } + } + } + qDebug() << "getPerfixLength" << length; + return length; +} + diff --git a/src/frontend/netdetails/ipv6page.h b/src/frontend/netdetails/ipv6page.h new file mode 100644 index 00000000..5e8d430d --- /dev/null +++ b/src/frontend/netdetails/ipv6page.h @@ -0,0 +1,66 @@ +#ifndef IPV6PAGE_H +#define IPV6PAGE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "kylinconnectsetting.h" +#include "coninfo.h" + +class Ipv6Page : public QFrame +{ + Q_OBJECT +public: + Ipv6Page(QWidget *parent = nullptr); + void setIpv6Config(KyIpConfigType ipv6Config); + void setIpv6(const QString &ipv4); + void setIpv6Perfix(const int &ipv6Perfix); + void setIpv6FirDns(const QString &ipv6FirDns); + void setIpv6SecDns(const QString &ipv6SecDns); + void setGateWay(const QString &gateWay); + + bool checkIsChanged(const ConInfo info, KyConnectSetting &setting); + + int getPerfixLength(QString text); + +public: + QComboBox *ipv6ConfigCombox; + LineEdit *ipv6AddressEdit; + LineEdit *lengthEdit; + LineEdit *gateWayEdit; + LineEdit *firstDnsEdit; + LineEdit *secondDnsEdit; +private: + QFormLayout *m_detailLayout; + QLabel *m_configLabel; + QLabel *m_addressLabel; + QLabel *m_subnetLabel; + QLabel *m_gateWayLabel; + QLabel *m_dnsLabel; + QLabel *m_secDnsLabel; +private: + void initUI(); + void initComponent(); + void setControlEnabled(bool check); + + bool getIpv6EditState(QString text); + + bool checkConnectBtnIsEnabled(); + +private slots: + void configChanged(int index); + void setEnableOfSaveBtn(); + +signals: + void setIpv6PageState(bool); +}; + +#endif // IPV6PAGE_H diff --git a/src/frontend/netdetails/netdetail.cpp b/src/frontend/netdetails/netdetail.cpp new file mode 100644 index 00000000..2c276407 --- /dev/null +++ b/src/frontend/netdetails/netdetail.cpp @@ -0,0 +1,964 @@ +#include "netdetail.h" +#include "backend/kylinipv4arping.h" +#include "backend/kylinipv6arping.h" +//#include "xatom/xatom-helper.h" + + +#define THEME_SCHAME "org.ukui.style" +#define COLOR_THEME "styleName" + +#include +#include +#include + +#define WINDOW_WIDTH 520 +#define WINDOW_HEIGHT 590 +#define BUTTON_SIZE 30 +#define ICON_SIZE 22,22 +#define TITLE_LAYOUT_MARGINS 9,9,0,0 +#define LAYOUT_MARGINS 24,0,24,0 +#define BOTTOM_LAYOUT_SPACING 16 +#define PAGE_LAYOUT_SPACING 1 +#define DETAIL_PAGE_NUM 0 +#define IPV4_PAGE_NUM 1 +#define IPV6_PAGE_NUM 2 +#define SECURITY_PAGE_NUM 3 +#define CREATE_NET_PAGE_NUM 4 +#define PAGE_MIN_HEIGHT 40 + +//extern void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed); + +void NetDetail::showDesktopNotify(const QString &message) +{ + QDBusInterface iface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::sessionBus()); + QList args; + args<<(tr("Kylin NM")) + <<((unsigned int) 0) + <setEnabled(on); + ipv4Page->setEnabled(on); + ipv6Page->setEnabled(on); + securityPage->setEnabled(on); + createNetPage->setEnabled(on); + cancelBtn->setEnabled(on); + forgetBtn->setEnabled(on); + confimBtn->setEnabled(on); +} + +NetDetail::NetDetail(QString interface, QString name, QString uuid, bool isActive, bool isWlan, bool isCreateNet, QWidget *parent) + :m_deviceName(interface), + m_name(name), + m_uuid(uuid), + isActive(isActive), + isWlan(isWlan), + m_isCreateNet(isCreateNet), + QWidget(parent) +{ + //设置窗口无边框,阴影 +//#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +// MotifWmHints window_hints; +// window_hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; +// window_hints.functions = MWM_FUNC_ALL; +// window_hints.decorations = MWM_DECOR_BORDER; +// XAtomHelper::getInstance()->setWindowMotifHint(this->winId(), window_hints); +//#else +// this->setWindowFlags(Qt::Dialog /*| Qt::FramelessWindowHint*/); + this->setWindowFlag(Qt::Window); +//#endif +// this->setProperty("useStyleWindowManager", false); //禁用拖动 +// setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint ); +// setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_DeleteOnClose); + setFixedSize(WINDOW_WIDTH,WINDOW_HEIGHT); + centerToScreen(); + + qDebug() << m_isCreateNet << name; + if (!m_isCreateNet && name.isEmpty()) { + m_isCreateNet = true; + } + qDebug() << m_isCreateNet; + m_netDeviceResource = new KyNetworkDeviceResourse(this); + m_wirelessConnOpration = new KyWirelessConnectOperation(this); + m_resource = new KyWirelessNetResource(this); + m_connectOperation = new KyConnectOperation(this); + m_wiredConnOperation = new KyWiredConnectOperation(this); + initUI(); + loadPage(); + initComponent(); + getConInfo(m_info); + pagePadding(name,isWlan); + + connect(qApp, &QApplication::paletteChanged, this, &NetDetail::onPaletteChanged); + + isCreateOk = !(m_isCreateNet && !isWlan); + isDetailOk = !(m_name.isEmpty()); + isIpv4Ok = true; + isIpv6Ok = true; + isSecuOk = false; + if (!m_uuid.isEmpty() || (m_uuid.isEmpty() && m_info.secType == NONE)) { + isSecuOk = true; + } + + qDebug() << interface << name << uuid << "isWlan" << isWlan << "isCreateNet" <palette(); + + QGSettings * styleGsettings = nullptr; + const QByteArray style_id(THEME_SCHAME); + if (QGSettings::isSchemaInstalled(style_id)) { + styleGsettings = new QGSettings(style_id); + QString currentTheme = styleGsettings->get(COLOR_THEME).toString(); + if(currentTheme == "ukui-default"){ + pal = lightPalette(this); + } + } + + this->setPalette(pal); + + setFramePalette(detailPage, pal); + setFramePalette(ipv4Page, pal); + setFramePalette(ipv6Page, pal); + setFramePalette(securityPage, pal); + setFramePalette(createNetPage, pal); + QToolTip::setPalette(pal); + + QPalette listwidget_pal(detailPage->m_listWidget->palette()); + listwidget_pal.setColor(QPalette::Base, this->palette().base().color()); + listwidget_pal.setColor(QPalette::AlternateBase, this->palette().alternateBase().color()); + detailPage->m_listWidget->setAlternatingRowColors(true); + detailPage->m_listWidget->setPalette(listwidget_pal); + + if (styleGsettings != nullptr) { + delete styleGsettings; + styleGsettings = nullptr; + } +} + +void NetDetail::paintEvent(QPaintEvent *event) +{ + return QWidget::paintEvent(event); +} + +void NetDetail::closeEvent(QCloseEvent *event) +{ + emit this->detailPageClose(false); + emit this->createPageClose(m_deviceName); + return QWidget::closeEvent(event); +} + +void NetDetail::centerToScreen() +{ + QDesktopWidget* m = QApplication::desktop(); + QRect desk_rect = m->screenGeometry(m->screenNumber(QCursor::pos())); + int desk_x = desk_rect.width(); + int desk_y = desk_rect.height(); + int x = this->width(); + int y = this->height(); + this->move(desk_x / 2 - x / 2 + desk_rect.left(), desk_y / 2 - y / 2 + desk_rect.top()); +} + +void NetDetail::initUI() +{ + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(9,9,14,24); + + detailPage = new DetailPage(isWlan, m_name.isEmpty(), this); + + ipv4Page = new Ipv4Page(this); + ipv6Page = new Ipv6Page(this); + securityPage = new SecurityPage(this); + createNetPage = new CreatNetPage(this); + + this->installEventFilter(this); + + centerWidget = new QWidget(this); + bottomWidget = new QWidget(this); + + stackWidget = new QStackedWidget(centerWidget); + stackWidget->addWidget(detailPage); + stackWidget->addWidget(ipv4Page); + stackWidget->addWidget(ipv6Page); + stackWidget->addWidget(securityPage); + stackWidget->addWidget(createNetPage); + + mainLayout->addWidget(centerWidget); + mainLayout->addWidget(bottomWidget); + + bottomWidget->setMinimumHeight(PAGE_MIN_HEIGHT); + + pageFrame = new QFrame(this); + + QHBoxLayout *pageLayout = new QHBoxLayout(pageFrame); + pageLayout->setSpacing(PAGE_LAYOUT_SPACING); + + detailBtn = new QPushButton(this); + detailBtn->setText(tr("Detail")); + detailBtn->setCheckable(true); + detailBtn->setChecked(true); + + ipv4Btn = new QPushButton(this); + ipv4Btn->setText(tr("Ipv4")); + ipv4Btn->setCheckable(true); + + ipv6Btn = new QPushButton(this); + ipv6Btn->setText(tr("Ipv6")); + ipv6Btn->setCheckable(true); + + securityBtn = new QPushButton(this); + securityBtn->setText(tr("Security")); + securityBtn->setCheckable(true); + + m_group = new QButtonGroup(this); + m_group->addButton(detailBtn); + m_group->addButton(ipv4Btn); + m_group->addButton(ipv6Btn); + if (isWlan) { + m_group->addButton(securityBtn); + } + + pageLayout->addStretch(); + pageLayout->addWidget(detailBtn); + pageLayout->addWidget(ipv4Btn); + pageLayout->addWidget(ipv6Btn); + pageLayout->addWidget(securityBtn); + pageLayout->addStretch(); + + confimBtn = new QPushButton(this); + confimBtn->setText(tr("Confirm")); + + cancelBtn = new QPushButton(this); + cancelBtn->setText(tr("Cancel")); + + forgetBtn = new QPushButton(this); + forgetBtn->setText(tr("Forget this network")); + + this->setWindowIcon(QIcon::fromTheme("kylin-network")); + + QVBoxLayout *centerlayout = new QVBoxLayout(centerWidget); + centerlayout->setContentsMargins(LAYOUT_MARGINS); + centerlayout->addWidget(pageFrame); + centerlayout->addWidget(stackWidget); + + QHBoxLayout *bottomLayout = new QHBoxLayout(bottomWidget); + bottomLayout->setContentsMargins(LAYOUT_MARGINS); + bottomLayout->setSpacing(BOTTOM_LAYOUT_SPACING); + bottomLayout->addWidget(forgetBtn); + bottomLayout->addStretch(); + bottomLayout->addWidget(cancelBtn); + bottomLayout->addWidget(confimBtn); + +// QPalette pal(this->palette()); +// pal.setColor(QPalette::Background, qApp->palette().base().color()); + this->setAutoFillBackground(true); +// this->setPalette(pal); + onPaletteChanged(); +} + +void NetDetail::loadPage() +{ + //判断是否创建网络页面 + if (m_isCreateNet && !isWlan) { + pageFrame->hide(); + stackWidget->setCurrentIndex(CREATE_NET_PAGE_NUM); + this->setWindowTitle(tr("Add Lan Connect")); + } else { + stackWidget->setCurrentIndex(DETAIL_PAGE_NUM); + this->setWindowTitle(m_name); + if (!isWlan) { + securityBtn->hide(); + } else { + securityBtn->show(); + if (m_name.isEmpty()) { + this->setWindowTitle(tr("connect hiddin wlan")); + } + } + } +} + +void NetDetail::initComponent() +{ + connect(cancelBtn, &QPushButton::clicked, this, [=] { + close(); + }); + connect(detailBtn, &QPushButton::clicked, this, [=] { + stackWidget->setCurrentIndex(DETAIL_PAGE_NUM); + }); + connect(ipv4Btn, &QPushButton::clicked, this, [=] { + stackWidget->setCurrentIndex(IPV4_PAGE_NUM); + }); + connect(ipv6Btn, &QPushButton::clicked, this, [=] { + stackWidget->setCurrentIndex(IPV6_PAGE_NUM); + }); + connect(securityBtn, &QPushButton::clicked, this, [=] { + stackWidget->setCurrentIndex(SECURITY_PAGE_NUM); + }); + connect(confimBtn, SIGNAL(clicked()), this, SLOT(on_btnConfirm_clicked())); + if (isWlan && !m_uuid.isEmpty()) { + forgetBtn->show(); + connect(forgetBtn, SIGNAL(clicked()), this, SLOT(on_btnForget_clicked())); + } else { + forgetBtn->hide(); + } + + connect(createNetPage, &CreatNetPage::setCreatePageState, this, [=](bool status) { + isCreateOk = status; + setConfirmEnable(); + }); + + connect(detailPage, &DetailPage::setDetailPageState, this, [=](bool status) { + isDetailOk = status; + setConfirmEnable(); + }); + + connect(ipv4Page, &Ipv4Page::setIpv4PageState, this, [=](bool status) { + isIpv4Ok = status; + setConfirmEnable(); + }); + + connect(ipv6Page, &Ipv6Page::setIpv6PageState, this, [=](bool status) { + isIpv6Ok = status; + setConfirmEnable(); + }); + + connect(securityPage, &SecurityPage::setSecuPageState, this, [=](bool status) { + isSecuOk = status; + setConfirmEnable(); + }); +} + +void NetDetail::pagePadding(QString netName, bool isWlan) +{ + //网络详情页填充 + if(m_isCreateNet && !isWlan) { + return; + } + + detailPage->setSSID(netName); + detailPage->setProtocol(m_info.strConType); + detailPage->setSecType(m_info.strSecType); + detailPage->setHz(m_info.strHz); + detailPage->setChan(m_info.strChan); + detailPage->setIpv4(m_info.strDynamicIpv4); + detailPage->setIpv4Dns(m_info.strDynamicIpv4Dns); + detailPage->setIpv6(m_info.strDynamicIpv6); + detailPage->setMac(m_info.strMac); + detailPage->setBandWidth(m_info.strBandWidth); + detailPage->setAutoConnect(m_info.isAutoConnect); + + //ipv4页面填充 + if (m_info.ipv4ConfigType == CONFIG_IP_MANUAL) { + ipv4Page->setIpv4Config(m_info.ipv4ConfigType); + ipv4Page->setIpv4(m_info.strIPV4Address); + ipv4Page->setNetMask(m_info.strIPV4NetMask); + ipv4Page->setIpv4FirDns(m_info.strIPV4FirDns); + ipv4Page->setIpv4SecDns(m_info.strIPV4SecDns); + ipv4Page->setGateWay(m_info.strIPV4GateWay); + } else { + ipv4Page->setIpv4Config(m_info.ipv4ConfigType); + } + //ipv6页面填充 + if (m_info.ipv6ConfigType == CONFIG_IP_MANUAL) { + ipv6Page->setIpv6Config(m_info.ipv6ConfigType); + ipv6Page->setIpv6(m_info.strIPV6Address); + ipv6Page->setIpv6Perfix(m_info.iIPV6Prefix); + ipv6Page->setIpv6FirDns(m_info.strIPV6FirDns); + ipv6Page->setIpv6SecDns(m_info.strIPV6SecDns); + ipv6Page->setGateWay(m_info.strIPV6GateWay); + } else { + ipv6Page->setIpv6Config(m_info.ipv6ConfigType); + } + + //安全页面 + if (isWlan) { + securityPage->setSecurity(m_info.secType); + qDebug() << "setSecurity" << m_info.secType; + if (m_info.secType == WPA_AND_WPA2_ENTERPRISE) { + if (m_info.enterpriseType == TLS) { + securityPage->setTlsInfo(m_info.tlsInfo); + } else if (m_info.enterpriseType == PEAP) { + securityPage->setPeapInfo(m_info.peapInfo); + } else if (m_info.enterpriseType == TTLS) { + securityPage->setTtlsInfo(m_info.ttlsInfo); + } + } + } + +} + +//获取网路详情信息 +void NetDetail::getConInfo(ConInfo &conInfo) +{ + if (m_isCreateNet && !isWlan) { + return; + } + getBaseInfo(conInfo); + getDynamicIpInfo(conInfo, isActive); + getStaticIpInfo(conInfo,isActive); +} + + +//详情ssid 带宽 物理地址 无线额外(安全性 频带 通道) +void NetDetail::getBaseInfo(ConInfo &conInfo) +{ + //有线无线公有 + conInfo.strConName = m_name; + + QString hardAddress; + int bandWith; + m_netDeviceResource->getHardwareInfo(m_deviceName, hardAddress, bandWith); + + if (!hardAddress.isEmpty()) { + conInfo.strBandWidth = QString("%1").arg(bandWith/1000) + "Mbps"; + conInfo.strMac = hardAddress; + } + + if (!isWlan) { + conInfo.strConType = "802-3-ethernet"; + } else { + conInfo.strConType = "802-11-wireless"; + if (!isActive) { + KyWirelessNetItem item; + if (!m_resource->getWifiNetwork(m_deviceName, m_name, item)) { + qDebug() << "getWifiNetWork failed device:" << m_deviceName << " name:" << m_name; + return; + } else { + conInfo.strHz = QString::number(item.m_frequency); + conInfo.strChan = QString::number(item.m_channel); + //无线特有 + conInfo.strSecType = item.m_secuType; + if (conInfo.strSecType.isEmpty()) { + conInfo.strSecType = tr("None"); + } + + if (!item.m_isConfigured) { + conInfo.secType = item.m_kySecuType; + } + } + } else { + uint iHz,iChan; + QString strMac; + m_netDeviceResource->getDeviceActiveAPInfo(m_deviceName, strMac, iHz, iChan, conInfo.strSecType); + if (conInfo.strSecType.isEmpty()) { + conInfo.strSecType = tr("None"); + } + conInfo.strHz = QString::number(iHz); + conInfo.strChan = QString::number(iChan); + + } + + + + KyKeyMgmt type = m_wirelessConnOpration->getConnectKeyMgmt(m_uuid); + if (!m_uuid.isEmpty()) { + KyKeyMgmt type = m_wirelessConnOpration->getConnectKeyMgmt(m_uuid); + if (type == WpaNone || type == Unknown) { + conInfo.secType = NONE; + } else if (type == WpaPsk) { + conInfo.secType = WPA_AND_WPA2_PERSONAL; + } else if (type == SAE) { + conInfo.secType = WPA3_PERSONAL; + } else if (type == WpaEap) { + conInfo.secType = WPA_AND_WPA2_ENTERPRISE; + } else { + qDebug() << "KeyMgmt not support now " << type; + } + } + + initSecuData(); + } +} + +//详情ipv4 ipv6 ipv4Dns +void NetDetail::getDynamicIpInfo(ConInfo &conInfo, bool bActived) +{ + if (!bActived) { + return; + } + //已激活的网络 详情页显示动态ipv4 ipv6 dns + QString ipv4,ipv6; + QList ipv4Dns,ipv6Dns; + KyActiveConnectResourse *activeResourse = new KyActiveConnectResourse(this); + activeResourse->getActiveConnectIpInfo(m_uuid,ipv4,ipv6); + activeResourse->getActiveConnectDnsInfo(m_uuid,ipv4Dns,ipv6Dns); + + //Ipv6 + if (!ipv6.isEmpty()) { + conInfo.strDynamicIpv6 = ipv6; + } + + //IPv4 + if (!ipv4.isEmpty()) { + conInfo.strDynamicIpv4 = ipv4; + } + + if (!ipv4Dns.isEmpty()) { + conInfo.strDynamicIpv4Dns = ipv4Dns.at(0).toString(); + } +} + +//ipv4+ipv6页面 +void NetDetail::getStaticIpInfo(ConInfo &conInfo, bool bActived) +{ + KyConnectResourse *kyConnectResourse = new KyConnectResourse(this); + KyConnectSetting connetSetting; + kyConnectResourse->getConnectionSetting(m_uuid,connetSetting); + connetSetting.dumpInfo(); + + conInfo.ipv4ConfigType = connetSetting.m_ipv4ConfigIpType; + conInfo.ipv6ConfigType = connetSetting.m_ipv6ConfigIpType; + conInfo.isAutoConnect = connetSetting.m_isAutoConnect; + + if (connetSetting.m_ipv4ConfigIpType == CONFIG_IP_MANUAL) { + if (connetSetting.m_ipv4Address.size() > 0) { + conInfo.strIPV4Address = connetSetting.m_ipv4Address.at(0).ip().toString(); + conInfo.strIPV4NetMask = connetSetting.m_ipv4Address.at(0).netmask().toString(); + conInfo.strIPV4GateWay = connetSetting.m_ipv4Address.at(0).gateway().toString(); + } + if (connetSetting.m_ipv4Dns.size() == 1) { + conInfo.strIPV4FirDns = connetSetting.m_ipv4Dns.at(0).toString(); + } else if (connetSetting.m_ipv4Dns.size() > 1) { + conInfo.strIPV4FirDns = connetSetting.m_ipv4Dns.at(0).toString(); + conInfo.strIPV4SecDns = connetSetting.m_ipv4Dns.at(1).toString(); + } + } + + if (connetSetting.m_ipv6ConfigIpType == CONFIG_IP_MANUAL) { + if (connetSetting.m_ipv6Address.size() > 0) { + conInfo.strIPV6Address = connetSetting.m_ipv6Address.at(0).ip().toString(); + conInfo.iIPV6Prefix = ipv6Page->getPerfixLength(connetSetting.m_ipv6Address.at(0).netmask().toString()); + conInfo.strIPV6GateWay = connetSetting.m_ipv6Address.at(0).gateway().toString(); + } + + if (connetSetting.m_ipv6Dns.size() == 1) { + conInfo.strIPV6FirDns = connetSetting.m_ipv6Dns.at(0).toString(); + } else if (connetSetting.m_ipv4Dns.size() > 1) { + conInfo.strIPV6FirDns = connetSetting.m_ipv6Dns.at(0).toString(); + conInfo.strIPV6SecDns = connetSetting.m_ipv6Dns.at(1).toString(); + } + } + + if (!bActived) { + conInfo.strDynamicIpv4 = conInfo.strIPV4Address.isEmpty() ? tr("Auto") : conInfo.strIPV4Address; + conInfo.strDynamicIpv6 = conInfo.strIPV6Address.isEmpty() ? tr("Auto") : conInfo.strIPV6Address; + conInfo.strDynamicIpv4Dns = conInfo.strIPV4FirDns.isEmpty() ? tr("Auto") : conInfo.strIPV4FirDns; + } +} + +void NetDetail::initSecuData() +{ + QString password(""); + int type = m_info.secType; + switch (type) { + case NONE: + break; + case WPA_AND_WPA2_PERSONAL: + case WPA3_PERSONAL: + if (!m_uuid.isEmpty()) { + NetworkManager::Setting::SecretFlags flag; + if (m_wirelessConnOpration->getConnSecretFlags(m_uuid, flag)) { + if (!flag) { + password = m_wirelessConnOpration->getPsk(m_uuid); + } + } + } + m_info.strPassword = password; + securityPage->setPsk(password); + break; + case WPA_AND_WPA2_ENTERPRISE: + if (!m_wirelessConnOpration->getEnterpiseEapMethod(m_uuid, m_info.enterpriseType)) { + qDebug() << m_name << "not enterprise wifi"; + } else if (m_info.enterpriseType == TLS){ + initTlsInfo(m_info); + } else if (m_info.enterpriseType == PEAP){ + initPeapInfo(m_info); + } else { + initTtlsInfo(m_info); + } + break; + default: + break; + } +} + +void NetDetail::initTlsInfo(ConInfo &conInfo) +{ + m_resource->getEnterPriseInfoTls(m_uuid, conInfo.tlsInfo); +} + +void NetDetail::initPeapInfo(ConInfo &conInfo) +{ + m_resource->getEnterPriseInfoPeap(m_uuid, conInfo.peapInfo); +} + +void NetDetail::initTtlsInfo(ConInfo &conInfo) +{ + m_resource->getEnterPriseInfoTtls(m_uuid, conInfo.ttlsInfo); +} + +//点击了保存更改网络设置的按钮 +void NetDetail::on_btnConfirm_clicked() +{ + qDebug() << "on_btnConfirm_clicked"; + setNetdetailSomeEnable(false); + if (m_isCreateNet) { + if (!isWlan) { + //新建有线连接 + qDebug() << "Confirm create wired connect"; + if (!createWiredConnect()) { + setNetdetailSomeEnable(true); + return; + } + } else { + //新建无线连接 + qDebug() << "Confirm create wireless connect"; + if (!createWirelessConnect()) { + setNetdetailSomeEnable(true); + return; + } + } + } else { + //更新连接 + qDebug() << "Confirm update connect"; + if (!updateConnect()) { + setNetdetailSomeEnable(true); + return; + } + } + close(); +} + +//点击忘记网络 +void NetDetail::on_btnForget_clicked() +{ + qDebug() << "user choose forget connection uuid = " << m_uuid; + m_connectOperation->deleteConnect(m_uuid); + close(); +} + +void NetDetail::setConfirmEnable() +{ + if (m_isCreateNet && !isWlan) { + isConfirmBtnEnable = isCreateOk; + } else { + if (isDetailOk && isIpv4Ok && isIpv6Ok) { + if (isWlan && !isSecuOk) { + isConfirmBtnEnable = false; + } else { + isConfirmBtnEnable = true; + } + } else { + isConfirmBtnEnable = false; + } + } + qDebug() << "setConfirmEnable "<< isConfirmBtnEnable; + confimBtn->setEnabled(isConfirmBtnEnable); +} + +bool NetDetail::checkIpv4Conflict(QString ipv4Address) +{ + showDesktopNotify(tr("start check ipv4 address conflict")); + bool isConflict = false; + KyIpv4Arping* ipv4Arping = new KyIpv4Arping(m_deviceName, ipv4Address); + + if (ipv4Arping->ipv4ConflictCheck() >= 0) { + isConflict = ipv4Arping->ipv4IsConflict(); + } else { + qWarning() << "checkIpv4Conflict internal error"; + } + + delete ipv4Arping; + ipv4Arping = nullptr; + return isConflict; +} + +bool NetDetail::checkIpv6Conflict(QString ipv6address) +{ + showDesktopNotify(tr("start check ipv6 address conflict")); + bool isConflict = false; + KyIpv6Arping* ipv46rping = new KyIpv6Arping(m_deviceName, ipv6address); + + if (ipv46rping->ipv6ConflictCheck() >= 0) { + isConflict = ipv46rping->ipv6IsConflict(); + } else { + qWarning() << "checkIpv6Conflict internal error"; + } + + delete ipv46rping; + ipv46rping = nullptr; + return isConflict; +} + +void NetDetail::updateWirelessPersonalConnect() +{ + KyWirelessConnectSetting setting; + securityPage->updateSecurityChange(setting); + bool isPwdChanged = !(m_info.strPassword == setting.m_psk); + m_wirelessConnOpration->updateWirelessPersonalConnect(m_uuid, setting, isPwdChanged); +} + +void NetDetail::updateWirelessEnterPriseConnect(KyEapMethodType enterpriseType) +{ + if (enterpriseType == TLS) { + m_info.tlsInfo.devIfaceName = m_deviceName; + securityPage->updateTlsChange(m_info.tlsInfo); + m_wirelessConnOpration->updateWirelessEnterPriseTlsConnect(m_uuid, m_info.tlsInfo); + } else if (enterpriseType == PEAP) { + securityPage->updatePeapChange(m_info.peapInfo); + m_wirelessConnOpration->updateWirelessEnterPrisePeapConnect(m_uuid, m_info.peapInfo); + } else if (enterpriseType == TTLS) { + securityPage->updateTtlsChange(m_info.ttlsInfo); + m_wirelessConnOpration->updateWirelessEnterPriseTtlsConnect(m_uuid, m_info.ttlsInfo); + } +} + +bool NetDetail::createWiredConnect() +{ + KyWirelessConnectSetting connetSetting; + connetSetting.setIfaceName(m_deviceName); + createNetPage->constructIpv4Info(connetSetting); + if (connetSetting.m_ipv4ConfigIpType != CONFIG_IP_DHCP) { + if (checkIpv4Conflict(connetSetting.m_ipv4Address.at(0).ip().toString())) { + qDebug() << "ipv4 conflict"; + showDesktopNotify(tr("ipv4 address conflict!")); + return false; + } + } + m_wiredConnOperation->createWiredConnect(connetSetting); + return true; +} + +bool NetDetail::createWirelessConnect() +{ + KyWirelessConnectSetting connetSetting; + KySecuType secuType; + KyEapMethodType enterpriseType; + securityPage->getSecuType(secuType, enterpriseType); + //类型判断 + if (!m_name.isEmpty()) { + if (!checkWirelessSecurity(secuType)) { + return false; + } + } + + //基本信息 + QString ssid; + if (m_name.isEmpty()) { + detailPage->getSsid(ssid); + } else { + ssid = m_name; + } + connetSetting.setConnectName(ssid); + connetSetting.setIfaceName(m_deviceName); + if (detailPage->checkIsChanged(m_info)) { + connetSetting.isAutoConnect = !m_info.isAutoConnect; + } else { + connetSetting.isAutoConnect = m_info.isAutoConnect; + } + qDebug() << "isAutoConnect" << connetSetting.isAutoConnect; + connetSetting.m_ssid = ssid; +// connetSetting.m_secretFlag = NetworkManager::Setting::None; + //由于X.h的None与此处的None有歧义,此处直接使用值 + connetSetting.m_secretFlag = 0; + + //ipv4 & ipv6 + bool ipv4Change = ipv4Page->checkIsChanged(m_info, connetSetting); + bool ipv6Change = ipv6Page->checkIsChanged(m_info, connetSetting); + + connetSetting.dumpInfo(); + + qDebug() << "ipv4Changed" << ipv4Change << "ipv6Change" << ipv6Change; + if (ipv4Change && connetSetting.m_ipv4ConfigIpType == CONFIG_IP_MANUAL) { + if (checkIpv4Conflict(connetSetting.m_ipv4Address.at(0).ip().toString())) { + qDebug() << "ipv4 conflict"; + showDesktopNotify(tr("ipv4 address conflict!")); + return false; + } + } + + if (ipv6Change && connetSetting.m_ipv6ConfigIpType == CONFIG_IP_MANUAL) { + if (checkIpv6Conflict(connetSetting.m_ipv6Address.at(0).ip().toString())) { + qDebug() << "ipv6 conflict"; + showDesktopNotify(tr("ipv6 address conflict!")); + return false; + } + } + //wifi安全性 + if (secuType == WPA_AND_WPA2_ENTERPRISE) { + connetSetting.m_type = WpaEap; + if (enterpriseType == TLS) { + m_info.tlsInfo.devIfaceName = m_deviceName; + securityPage->updateTlsChange(m_info.tlsInfo); + if (!m_name.isEmpty()) { + qDebug() << "add new TLS connect"; + m_wirelessConnOpration->addTlsConnect(connetSetting, m_info.tlsInfo); + } else { + qDebug() << "addAndConnect TLS connect"; + m_wirelessConnOpration->addAndActiveWirelessEnterPriseTlsConnect(m_info.tlsInfo, connetSetting, m_deviceName, true); + } + } else if (enterpriseType == PEAP) { + securityPage->updatePeapChange(m_info.peapInfo); + if (!m_name.isEmpty()) { + qDebug() << "add new PEAP connect"; + m_wirelessConnOpration->addPeapConnect(connetSetting, m_info.peapInfo); + } else { + qDebug() << "addAndConnect PEAP connect"; + m_wirelessConnOpration->addAndActiveWirelessEnterPrisePeapConnect(m_info.peapInfo, connetSetting, m_deviceName, true); + } + } else if (enterpriseType == TTLS) { + securityPage->updateTtlsChange(m_info.ttlsInfo); + if (!m_name.isEmpty()) { + qDebug() << "add new TTLS connect"; + m_wirelessConnOpration->addTtlsConnect(connetSetting, m_info.ttlsInfo); + } else { + qDebug() << "addAndConnect TTLS connect"; + m_wirelessConnOpration->addAndActiveWirelessEnterPriseTtlsConnect(m_info.ttlsInfo, connetSetting, m_deviceName, true); + } + } + } else { + securityPage->updateSecurityChange(connetSetting); + if (!m_name.isEmpty()) { + qDebug() << "add new personal connect"; + m_wirelessConnOpration->addConnect(connetSetting); + } else { + qDebug() << "addAndConnect personal connect" << m_deviceName; + m_wirelessConnOpration->addAndActiveWirelessConnect(m_deviceName, connetSetting, true); + } + } + return true; +} + +bool NetDetail::updateConnect() +{ + KyConnectResourse *kyConnectResourse = new KyConnectResourse(this); + KyConnectSetting connetSetting; + KySecuType secuType; + KyEapMethodType enterpriseType; + kyConnectResourse->getConnectionSetting(m_uuid,connetSetting); + + bool securityChange = false; + if (isWlan) { + securityChange = securityPage->checkIsChanged(m_info); + if(securityChange) { + securityPage->getSecuType(secuType, enterpriseType); + if (!checkWirelessSecurity(secuType)) { + return false; + } + } + } + + if(!m_uuid.isEmpty() && detailPage->checkIsChanged(m_info)) { + m_wirelessConnOpration->setWirelessAutoConnect(m_uuid, !m_info.isAutoConnect); + } + + bool ipv4Change = ipv4Page->checkIsChanged(m_info, connetSetting); + bool ipv6Change = ipv6Page->checkIsChanged(m_info, connetSetting); + + qDebug() << "ipv4Changed" << ipv4Change << "ipv6Change" << ipv6Change; + + if (ipv4Change && connetSetting.m_ipv4ConfigIpType == CONFIG_IP_MANUAL) { + if (checkIpv4Conflict(connetSetting.m_ipv4Address.at(0).ip().toString())) { + qDebug() << "ipv4 conflict"; + showDesktopNotify(tr("ipv4 address conflict!")); + return false; + } + } + + if (ipv6Change && connetSetting.m_ipv6ConfigIpType == CONFIG_IP_MANUAL) { + if (checkIpv6Conflict(connetSetting.m_ipv6Address.at(0).ip().toString())) { + qDebug() << "ipv6 conflict"; + showDesktopNotify(tr("ipv6 address conflict!")); + return false; + } + } + + if (ipv4Change || ipv6Change) { + connetSetting.dumpInfo(); + m_wiredConnOperation->updateWiredConnect(m_uuid, connetSetting); + } + + qDebug() << "securityChange" << securityChange; + if (securityChange) { + if (secuType == WPA_AND_WPA2_ENTERPRISE) { + updateWirelessEnterPriseConnect(enterpriseType); + } else { + updateWirelessPersonalConnect(); + } + } + + if (ipv4Change || ipv6Change || securityChange) { + if (isActive) { + //信息变化 断开-重连 更新需要時間 不可以立即重連 +// sleep(1); + QEventLoop eventloop; + QTimer::singleShot(1000, &eventloop, SLOT(quit())); + eventloop.exec(); + m_wirelessConnOpration->activateConnection(m_uuid, m_deviceName); + } + } + return true; +} + +bool NetDetail::checkWirelessSecurity(KySecuType secuType) +{ + if (secuType == WPA_AND_WPA2_ENTERPRISE) { + if(m_info.strSecType.indexOf("802.1X") < 0) { + showDesktopNotify(tr("this wifi no support enterprise type")); + return false; + } + } else { + if (secuType == NONE && m_info.strSecType != tr("None")) { + showDesktopNotify(tr("this wifi no support None type")); + return false; + } else if (secuType == WPA_AND_WPA2_PERSONAL + && (m_info.strSecType.indexOf("WPA1") < 0 && + m_info.strSecType.indexOf("WPA2") < 0)) { + showDesktopNotify(tr("this wifi no support WPA2 type")); + return false; + } else if (secuType == WPA3_PERSONAL && m_info.strSecType.indexOf("WPA3") < 0) { + showDesktopNotify(tr("this wifi no support WPA3 type")); + return false; + } + } + return true; +} + +bool NetDetail::eventFilter(QObject *w, QEvent *event) +{ + // 回车键触发确定按钮点击事件 + if (event->type() == QEvent::KeyPress) { + QKeyEvent *mEvent = static_cast(event); + if (mEvent->key() == Qt::Key_Enter || mEvent->key() == Qt::Key_Return) { + if (confimBtn->isEnabled()) { + emit confimBtn->clicked(); + } + return true; + } else if (mEvent->key() == Qt::Key_Escape) { + close(); + return true; + } + } + return QWidget::eventFilter(w, event); +} diff --git a/src/frontend/netdetails/netdetail.h b/src/frontend/netdetails/netdetail.h new file mode 100644 index 00000000..c465527c --- /dev/null +++ b/src/frontend/netdetails/netdetail.h @@ -0,0 +1,141 @@ +#ifndef NETDETAIL_H +#define NETDETAIL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "detailpage.h" +#include "ipv4page.h" +#include "ipv6page.h" +#include "securitypage.h" +#include "creatnetpage.h" +#include "coninfo.h" +#include "tab-pages/tabpage.h" + +class NetDetail : public QWidget +{ + Q_OBJECT + +public: + NetDetail(QString interface, QString name, QString uuid, bool isActive, bool isWlan, bool isCreateNet, QWidget *parent = nullptr); + ~NetDetail(); +protected: + void paintEvent(QPaintEvent *event); + void closeEvent(QCloseEvent *event); + bool eventFilter(QObject *w, QEvent *event); + +private: + void initUI(); + void centerToScreen(); + void initComponent(); + void getConInfo(ConInfo &conInfo); + void loadPage(); + void pagePadding(QString netName, bool isWlan); + void initSecuData(); + + void initTlsInfo(ConInfo &conInfo); + void initPeapInfo(ConInfo &conInfo); + void initTtlsInfo(ConInfo &conInfo); + + void updateWirelessPersonalConnect(); + void updateWirelessEnterPriseConnect(KyEapMethodType enterpriseType); + + //详情ssid 带宽 物理地址 无线额外(安全性 频带 通道) + void getBaseInfo(ConInfo &conInfo); + //详情ipv4 ipv6 ipv4Dns + void getDynamicIpInfo(ConInfo &conInfo, bool bActived); + //ipv4+ipv6页面 + void getStaticIpInfo(ConInfo &conInfo, bool bActived); + + void setConfirmEnable(); + + bool checkIpv4Conflict(QString ipv4Address); + bool checkIpv6Conflict(QString ipv6Address); + + bool createWiredConnect(); + bool createWirelessConnect(); + bool updateConnect(); + + bool checkWirelessSecurity(KySecuType secuType); + + void showDesktopNotify(const QString &message); + + void setNetdetailSomeEnable(bool on); +private: + KyNetworkDeviceResourse *m_netDeviceResource = nullptr; + KyConnectOperation* m_connectOperation = nullptr; + KyWirelessConnectOperation *m_wirelessConnOpration = nullptr; + KyWiredConnectOperation *m_wiredConnOperation = nullptr; + KyWirelessNetResource *m_resource = nullptr; + + QStackedWidget * stackWidget; + + DetailPage * detailPage; + Ipv4Page * ipv4Page; + Ipv6Page * ipv6Page; + SecurityPage * securityPage; + CreatNetPage * createNetPage; + + QWidget * centerWidget; + QWidget * bottomWidget; + + QPushButton * cancelBtn; + QPushButton * forgetBtn; + QPushButton * confimBtn; + + QPushButton * detailBtn; + QPushButton * ipv4Btn; + QPushButton * ipv6Btn; + QPushButton * securityBtn; + QFrame * pageFrame; + + QString m_name; + QString m_uuid; + QString m_deviceName; + + bool isWlan; + bool m_isCreateNet; + bool isActive; + bool isHideWlan; + + bool isCreateOk; + bool isDetailOk; + bool isIpv4Ok; + bool isIpv6Ok; + bool isSecuOk; + bool isConfirmBtnEnable; + + ConInfo m_info; + + QButtonGroup *m_group; + +private slots: + void on_btnConfirm_clicked(); + void on_btnForget_clicked(); + void onPaletteChanged(); + +signals: + void detailPageClose(bool on); + void createPageClose(QString); +}; + + + +#endif // NETDETAIL_H diff --git a/src/frontend/netdetails/netdetails.pri b/src/frontend/netdetails/netdetails.pri new file mode 100644 index 00000000..720a66b8 --- /dev/null +++ b/src/frontend/netdetails/netdetails.pri @@ -0,0 +1,22 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/coninfo.h \ + $$PWD/creatnetpage.h \ + $$PWD/customtabstyle.h \ + $$PWD/detailpage.h \ + $$PWD/detailwidget.h \ + $$PWD/ipv4page.h \ + $$PWD/ipv6page.h \ + $$PWD/netdetail.h \ + $$PWD/securitypage.h + +SOURCES += \ + $$PWD/creatnetpage.cpp \ + $$PWD/customtabstyle.cpp \ + $$PWD/detailpage.cpp \ + $$PWD/detailwidget.cpp \ + $$PWD/ipv4page.cpp \ + $$PWD/ipv6page.cpp \ + $$PWD/netdetail.cpp \ + $$PWD/securitypage.cpp diff --git a/src/frontend/netdetails/securitypage.cpp b/src/frontend/netdetails/securitypage.cpp new file mode 100644 index 00000000..70845e28 --- /dev/null +++ b/src/frontend/netdetails/securitypage.cpp @@ -0,0 +1,834 @@ +#include "securitypage.h" +#include "netdetail.h" + +#include + +SecurityPage::SecurityPage(QWidget *parent) : QFrame(parent) +{ + initUI(); + initConnect(); +} + +void SecurityPage::initUI() +{ + secuTypeLabel = new QLabel(this); + pwdLabel = new QLabel(this); + secuTypeLabel = new QLabel(this); + pwdLabel = new QLabel(this); + //企业wifi共有 + eapTypeLabel = new QLabel(this); + //TLS + identityLable = new QLabel(this); + domainLable = new QLabel(this); + caCertPathLabel = new QLabel(this); + caNeedFlagLabel = new QLabel(this); + clientCertPathLabel = new QLabel(this); + clientPrivateKeyLabel = new QLabel(this); + clientPrivateKeyPwdLabel = new QLabel(this); + + //PEAP TTLS共有 + eapMethodLabel = new QLabel(this); + userNameLabel = new QLabel(this); + userPwdLabel = new QLabel(this); + userPwdFlagLabel = new QLabel(this); + + secuTypeCombox = new QComboBox(this); + pwdEdit = new LineEdit(this); + eapTypeCombox = new QComboBox(this); + //TLS + identityEdit = new LineEdit(this); + domainEdit = new LineEdit(this); + caCertPathCombox = new QComboBox(this); + caNeedBox = new QCheckBox(this); + clientCertPathCombox = new QComboBox(this); + clientPrivateKeyCombox = new QComboBox(this); + clientPrivateKeyPwdEdit = new LineEdit(this); + + //PEAP && TTLS + eapMethodCombox = new QComboBox(this); + userNameEdit = new LineEdit(this); + userPwdEdit = new LineEdit(this); + userPwdFlagBox = new QCheckBox(this); + + + mSecuLayout = new QFormLayout(this); + mSecuLayout->addRow(secuTypeLabel, secuTypeCombox); + mSecuLayout->addRow(pwdLabel, pwdEdit); + mSecuLayout->addRow(eapTypeLabel, eapTypeCombox); + mSecuLayout->addRow(identityLable, identityEdit); + mSecuLayout->addRow(domainLable, domainEdit); + mSecuLayout->addRow(caCertPathLabel, caCertPathCombox); + mSecuLayout->addRow(caNeedBox, caNeedFlagLabel); + mSecuLayout->addRow(clientCertPathLabel, clientCertPathCombox); + mSecuLayout->addRow(clientPrivateKeyLabel, clientPrivateKeyCombox); + mSecuLayout->addRow(clientPrivateKeyPwdLabel,clientPrivateKeyPwdEdit); + mSecuLayout->addRow(eapMethodLabel, eapMethodCombox); + mSecuLayout->addRow(userNameLabel, userNameEdit); + mSecuLayout->addRow(userPwdLabel, userPwdEdit); + mSecuLayout->addRow(userPwdFlagBox, userPwdFlagLabel); + + + secuTypeLabel->setText(tr("Security")); + pwdLabel->setText(tr("Password")); + //企业wifi共有 + eapTypeLabel->setText(tr("EAP type")); + //TLS + identityLable->setText(tr("Identity")); + domainLable->setText(tr("Domain")); + caCertPathLabel->setText(tr("CA certficate")); + caNeedFlagLabel->setText(tr("no need for CA certificate")); + clientCertPathLabel->setText(tr("User certificate")); + clientPrivateKeyLabel->setText(tr("User private key")); + clientPrivateKeyPwdLabel->setText(tr("User key password")); + + //PEAP TTLS共有 + eapMethodLabel->setText(tr("Ineer authentication")); + userNameLabel->setText(tr("Usename")); + userPwdLabel->setText(tr("Password")); + userPwdFlagLabel->setText(tr("Ask pwd each query")); + + secuTypeCombox->addItem(tr("None"),NONE); + secuTypeCombox->addItem(tr("WPA&WPA2 Personal"),WPA_AND_WPA2_PERSONAL); + secuTypeCombox->addItem(tr("WPA&WPA2 Enterprise"), WPA_AND_WPA2_ENTERPRISE); + secuTypeCombox->addItem(tr("WPA3 Personal"), WPA3_PERSONAL); + + eapTypeCombox->addItem("TLS", TLS); + eapTypeCombox->addItem("PEAP", PEAP); + eapTypeCombox->addItem("TTLS", TTLS); + eapTypeCombox->setCurrentIndex(TLS); + //TLS + caCertPathCombox->addItem(tr("None"), QString(tr("None"))); //无 + caCertPathCombox->addItem(tr("Choose from file..."), QString(tr("Choose from file..."))); //从文件中选择... + + clientCertPathCombox->addItem(tr("None"), QString(tr("None"))); //无 + clientCertPathCombox->addItem(tr("Choose from file..."), QString(tr("Choose from file..."))); //从文件中选择... + + clientPrivateKeyCombox->addItem(tr("None"), QString(tr("None"))); //无 + clientPrivateKeyCombox->addItem(tr("Choose from file..."), QString(tr("Choose from file..."))); //从文件中选择... + + pwdBox = new QCheckBox(this); + pwdBox->setStyleSheet("QCheckBox::indicator {width: 18px; height: 9px;}" + "QCheckBox::indicator:checked {image: url(:/res/h/show-pwd.png);}" + "QCheckBox::indicator:unchecked {image: url(:/res/h/hide-pwd.png);}"); + pwdBox->setCursor(Qt::PointingHandCursor); + pwdBox->setFixedSize(30, pwdEdit->height()); + //防止文本框输入内容位于按钮之下 + QMargins margins = pwdEdit->textMargins(); + pwdEdit->setTextMargins(margins.left(), margins.top(), pwdBox->width(), margins.bottom()); + QHBoxLayout *pPwdLayout = new QHBoxLayout(); + pPwdLayout->addStretch(); + pPwdLayout->addWidget(pwdBox); + pPwdLayout->setSpacing(0); + pPwdLayout->setContentsMargins(0, 0, 0, 0); + pwdEdit->setLayout(pPwdLayout); + pwdEdit->setEchoMode(QLineEdit::Password); + + userPwdBox = new QCheckBox(this); + userPwdBox->setStyleSheet("QCheckBox::indicator {width: 18px; height: 9px;}" + "QCheckBox::indicator:checked {image: url(:/res/h/show-pwd.png);}" + "QCheckBox::indicator:unchecked {image: url(:/res/h/hide-pwd.png);}"); + userPwdBox->setCursor(Qt::PointingHandCursor); + userPwdBox->setFixedSize(30, userPwdEdit->height()); + userPwdBox->setFocusPolicy(Qt::NoFocus); + //防止文本框输入内容位于按钮之下 + userPwdEdit->setTextMargins(margins.left(), margins.top(), userPwdBox->width(), margins.bottom()); + QHBoxLayout *puserPwdLayout = new QHBoxLayout(); + puserPwdLayout->addStretch(); + puserPwdLayout->addWidget(userPwdBox); + puserPwdLayout->setSpacing(0); + puserPwdLayout->setContentsMargins(0, 0, 0, 0); + userPwdEdit->setLayout(puserPwdLayout); + userPwdEdit->setEchoMode(QLineEdit::Password); + + privateKeyBox = new QCheckBox(this); + privateKeyBox->setStyleSheet("QCheckBox::indicator {width: 18px; height: 9px;}" + "QCheckBox::indicator:checked {image: url(:/res/h/show-pwd.png);}" + "QCheckBox::indicator:unchecked {image: url(:/res/h/hide-pwd.png);}"); + privateKeyBox->setCursor(Qt::PointingHandCursor); + privateKeyBox->setFixedSize(30, clientPrivateKeyPwdEdit->height()); + //防止文本框输入内容位于按钮之下 + clientPrivateKeyPwdEdit->setTextMargins(margins.left(), margins.top(), privateKeyBox->width(), margins.bottom()); + QHBoxLayout *pPrivateKeyPwdLayout = new QHBoxLayout(); + pPrivateKeyPwdLayout->addStretch(); + pPrivateKeyPwdLayout->addWidget(privateKeyBox); + pPrivateKeyPwdLayout->setSpacing(0); + pPrivateKeyPwdLayout->setContentsMargins(0, 0, 0, 0); + clientPrivateKeyPwdEdit->setLayout(pPrivateKeyPwdLayout); + clientPrivateKeyPwdEdit->setEchoMode(QLineEdit::Password); + + pwdEdit->setContextMenuPolicy(Qt::NoContextMenu); + clientPrivateKeyPwdEdit->setContextMenuPolicy(Qt::NoContextMenu); + userPwdEdit->setContextMenuPolicy(Qt::NoContextMenu); + + QRegExp rx("^[A-Za-z0-9`~!@#$%^&*()_-+=<>,.\\\/]+$"); + QRegExpValidator *latitude = new QRegExpValidator(rx, this); + pwdEdit->setValidator(latitude); + clientPrivateKeyPwdEdit->setValidator(latitude); + userPwdEdit->setValidator(latitude); + + showNone(); +} + +void SecurityPage::initConnect() +{ + //安全类型变化 +// connect(secuTypeCombox, &QComboBox::currentTextChanged, this, &SecurityPage::onSecuTypeComboxIndexChanged); + connect(secuTypeCombox, QOverload::of(&QComboBox::currentIndexChanged), this, &SecurityPage::onSecuTypeComboxIndexChanged); + //EAP方式变化 +// connect(eapTypeCombox, &QComboBox::currentTextChanged, this, &SecurityPage::onEapTypeComboxIndexChanged); + connect(eapTypeCombox, QOverload::of(&QComboBox::currentIndexChanged), this, &SecurityPage::onEapTypeComboxIndexChanged); + + connect(caNeedBox, &QCheckBox::clicked, this, &SecurityPage::onCaNeedBoxClicked); + + connect(pwdBox, &QCheckBox::clicked, this, &SecurityPage::onPwdBoxClicked); + connect(userPwdBox, &QCheckBox::clicked, this, &SecurityPage::onUserPwdBox); + connect(privateKeyBox, &QCheckBox::clicked, this, &SecurityPage::onPrivateKeyBoxClicked); + + connect(caCertPathCombox, static_cast(&QComboBox::currentIndexChanged), + this, &SecurityPage::onCaCertPathComboxIndexChanged); + + connect(clientCertPathCombox, static_cast(&QComboBox::currentIndexChanged), + this, &SecurityPage::onClientCertPathComboxIndexChanged); + + connect(clientPrivateKeyCombox, static_cast(&QComboBox::currentIndexChanged), + this, &SecurityPage::onClientPrivateKeyComboxIndexChanged); + + connect(secuTypeCombox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(pwdEdit, &LineEdit::textChanged, this, &SecurityPage::setEnableOfSaveBtn); + connect(eapTypeCombox, SIGNAL(currentIndexChanged(int)), this, SLOT(setEnableOfSaveBtn())); + connect(identityEdit, &LineEdit::textChanged, this, &SecurityPage::setEnableOfSaveBtn); + connect(caCertPathCombox, SIGNAL(currentTextChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(caNeedBox, &QCheckBox::stateChanged, this, &SecurityPage::setEnableOfSaveBtn); + connect(clientCertPathCombox, SIGNAL(currentTextChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(clientPrivateKeyCombox, SIGNAL(currentTextChanged(QString)), this, SLOT(setEnableOfSaveBtn())); + connect(clientPrivateKeyPwdEdit, &LineEdit::textChanged, this, &SecurityPage::setEnableOfSaveBtn); + connect(eapMethodCombox, SIGNAL(currentIndexChanged(int)), this, SLOT(setEnableOfSaveBtn())); + connect(userNameEdit, &LineEdit::textChanged, this, &SecurityPage::setEnableOfSaveBtn); + connect(userPwdEdit, &LineEdit::textChanged, this, &SecurityPage::setEnableOfSaveBtn); + +} + +void SecurityPage::setSecurity(KySecuType index) +{ + secuTypeCombox->setCurrentIndex(index); + onSecuTypeComboxIndexChanged(); +} + +void SecurityPage::setPsk(const QString &psk) +{ + pwdEdit->setText(psk); +} + +void SecurityPage::setTlsInfo(KyEapMethodTlsInfo &info) +{ + showTls(); + identityEdit->setText(info.identity); + domainEdit->setText(info.domain); + if (info.caCertPath.isEmpty()) { + caCertPathCombox->setItemText(0, QString(tr("None"))); + caNeedBox->setChecked(true); + caCertPathCombox->setEnabled(false); + } else { + caCertPathCombox->setItemText(0, info.caCertPath); + caNeedBox->setChecked(false); + caCertPathCombox->setEnabled(true); + } + + if (info.clientCertPath.isEmpty()) { + clientCertPathCombox->setItemText(0, ""); + } else { + clientCertPathCombox->setItemText(0, info.clientCertPath); + } + + if (info.clientPrivateKey.isEmpty()) { + clientPrivateKeyCombox->setItemText(0, ""); + } else { + clientPrivateKeyCombox->setItemText(0, info.clientPrivateKey); + } + + clientPrivateKeyPwdEdit->setText(info.clientPrivateKeyPWD); + +} + +void SecurityPage::setPeapInfo(KyEapMethodPeapInfo &info) +{ + showPeapOrTtls(); + eapTypeCombox->setCurrentIndex(PEAP); + onEapTypeComboxIndexChanged(); + if (info.phase2AuthMethod == KyAuthMethodMschapv2) { + eapMethodCombox->setCurrentIndex(MSCHAPV2_PEAP); + } else if (info.phase2AuthMethod == KyAuthMethodMd5){ + eapMethodCombox->setCurrentIndex(MD5_PEAP); + } else if (info.phase2AuthMethod == KyAuthMethodGtc) { + eapMethodCombox->setCurrentIndex(GTC_PEAP); + } + userNameEdit->setText(info.userName); + userPwdEdit->setText(info.userPWD); + if (info.m_passwdFlag) { + userPwdFlagBox->setChecked(true); + } else { + userPwdFlagBox->setChecked(false); + } +} + +void SecurityPage::setTtlsInfo(KyEapMethodTtlsInfo &info) +{ + showPeapOrTtls(); + eapTypeCombox->setCurrentIndex(TTLS); + onEapTypeComboxIndexChanged(); + + if (info.authType == AUTH_EAP) { + if (info.authEapMethod = KyAuthEapMethodMschapv2) { + eapMethodCombox->setCurrentIndex(MSCHAPV2_EAP); + } else if (info.authEapMethod = KyAuthEapMethodMd5) { + eapMethodCombox->setCurrentIndex(MD5_EAP); + } else if (info.authEapMethod = KyAuthEapMethodMd5) { + eapMethodCombox->setCurrentIndex(MD5_EAP); + } else { + qDebug() << "not support yet. AUTH_EAP method" << info.authEapMethod; + } + } else { + if (info.authNoEapMethod == KyAuthMethodPap) { + eapMethodCombox->setCurrentIndex(PAP); + } else if (info.authNoEapMethod == KyAuthMethodMschap) { + eapMethodCombox->setCurrentIndex(MSCHAP); + } else if (info.authNoEapMethod == KyAuthMethodMschapv2) { + eapMethodCombox->setCurrentIndex(MSCHAPV2); + } else if (info.authNoEapMethod == KyAuthMethodChap) { + eapMethodCombox->setCurrentIndex(CHAP); + } else { + qDebug() << "not support yet. AUTH_NO_EAP method" << info.authNoEapMethod; + } + } + userNameEdit->setText(info.userName); + userPwdEdit->setText(info.userPWD); + if (info.m_passwdFlag) { + userPwdFlagBox->setChecked(true); + } else { + userPwdFlagBox->setChecked(false); + } +} + +void SecurityPage::setSecurityVisible(const bool &visible) +{ + if (secuTypeLabel) { + secuTypeLabel->setVisible(visible); + } else { + qWarning() << "Set visible of secuTypeLabel failed because of null pointer" << Q_FUNC_INFO << __LINE__; + } + if (secuTypeCombox) { + secuTypeCombox->setVisible(visible); + } else { + qWarning() << "Set visible of secuTypeCombox failed because of null pointer" << Q_FUNC_INFO << __LINE__; + } +} + +void SecurityPage::updateTlsChange(KyEapMethodTlsInfo &info) +{ + KyEapMethodTlsInfo tlsInfo = assembleTlsInfo(); + if (tlsInfo.clientPrivateKeyPWD != info.clientPrivateKeyPWD) { + tlsInfo.bChanged = true; + } + tlsInfo.devIfaceName = info.devIfaceName; + info = tlsInfo; +} + +void SecurityPage::updatePeapChange(KyEapMethodPeapInfo &info) +{ + KyEapMethodPeapInfo peapInfo = assemblePeapInfo(); + if (peapInfo.userPWD != info.userPWD) { + peapInfo.bChanged = true; + } + info = peapInfo; +} + +void SecurityPage::updateTtlsChange(KyEapMethodTtlsInfo &info) +{ + KyEapMethodTtlsInfo ttlsInfo = assembleTtlsInfo(); + if (ttlsInfo.userPWD != info.userPWD) { + ttlsInfo.bChanged = true; + } + info = ttlsInfo; +} + +void SecurityPage::getSecuType(KySecuType &secuType, KyEapMethodType &enterpriseType) +{ + secuType = (KySecuType)secuTypeCombox->currentData().toInt(); + enterpriseType = (KyEapMethodType)eapTypeCombox->currentData().toInt(); +} + +bool SecurityPage::checkIsChanged(const ConInfo info) +{ + if (info.secType != secuTypeCombox->currentData().toInt()) { + return true; + } else { + if (info.secType == NONE) { + return false; + } else if (info.secType == WPA_AND_WPA2_PERSONAL || info.secType == WPA3_PERSONAL) { + return !(info.strPassword == pwdEdit->text()); + } else { + if (info.enterpriseType != eapTypeCombox->currentData().toInt()) { + return true; + } else { + if (info.enterpriseType == TLS) { + return !(info.tlsInfo == assembleTlsInfo()); + } else if (info.enterpriseType == PEAP) { + return !(info.peapInfo == assemblePeapInfo()); + } else if (info.enterpriseType == TTLS) { + return !(info.ttlsInfo == assembleTtlsInfo()); + } + } + } + } +} + +void SecurityPage::showNone() +{ + pwdEdit->hide(); + eapTypeCombox->hide(); + + identityEdit->hide(); + domainEdit->hide(); + caCertPathCombox->hide(); + caNeedBox->hide(); + clientCertPathCombox->hide(); + clientPrivateKeyCombox->hide(); + clientPrivateKeyPwdEdit->hide(); + + eapMethodCombox->hide(); + userNameEdit->hide(); + userPwdEdit->hide(); + userPwdFlagBox->hide(); + + pwdLabel->hide(); + //企业wifi共有 + eapTypeLabel->hide(); + //TLS + identityLable->hide(); + domainLable->hide(); + caCertPathLabel->hide(); + caNeedFlagLabel->hide(); + clientCertPathLabel->hide(); + clientPrivateKeyLabel->hide(); + clientPrivateKeyPwdLabel->hide(); + + //PEAP TTLS共有 + eapMethodLabel->hide(); + userNameLabel->hide(); + userPwdLabel->hide(); + userPwdFlagLabel->hide(); +} + +void SecurityPage::showPsk() +{ + pwdEdit->show(); + eapTypeCombox->hide(); + + identityEdit->hide(); + domainEdit->hide(); + caCertPathCombox->hide(); + caNeedBox->hide(); + clientCertPathCombox->hide(); + clientPrivateKeyCombox->hide(); + clientPrivateKeyPwdEdit->hide(); + + eapMethodCombox->hide(); + userNameEdit->hide(); + userPwdEdit->hide(); + userPwdFlagBox->hide(); + + pwdLabel->show(); + //企业wifi共有 + eapTypeLabel->hide(); + //TLS + identityLable->hide(); + domainLable->hide(); + caCertPathLabel->hide(); + caNeedFlagLabel->hide(); + clientCertPathLabel->hide(); + clientPrivateKeyLabel->hide(); + clientPrivateKeyPwdLabel->hide(); + + //PEAP TTLS共有 + eapMethodLabel->hide(); + userNameLabel->hide(); + userPwdLabel->hide(); + userPwdFlagLabel->hide(); +} + +void SecurityPage::showTls() +{ + pwdEdit->hide(); + eapTypeCombox->show(); + + identityEdit->show(); + domainEdit->show(); + caCertPathCombox->show(); + caNeedBox->show(); + clientCertPathCombox->show(); + clientPrivateKeyCombox->show(); + clientPrivateKeyPwdEdit->show(); + + eapMethodCombox->hide(); + userNameEdit->hide(); + userPwdEdit->hide(); + userPwdFlagBox->hide(); + + pwdLabel->hide(); + //企业wifi共有 + eapTypeLabel->show(); + //TLS + identityLable->show(); + domainLable->show(); + caCertPathLabel->show(); + caNeedFlagLabel->show(); + clientCertPathLabel->show(); + clientPrivateKeyLabel->show(); + clientPrivateKeyPwdLabel->show(); + + //PEAP TTLS共有 + eapMethodLabel->hide(); + userNameLabel->hide(); + userPwdLabel->hide(); + userPwdFlagLabel->hide(); +} + +void SecurityPage::showPeapOrTtls() +{ + pwdEdit->hide(); + eapTypeCombox->show(); + + identityEdit->hide(); + domainEdit->hide(); + caCertPathCombox->hide(); + caNeedBox->hide(); + clientCertPathCombox->hide(); + clientPrivateKeyCombox->hide(); + clientPrivateKeyPwdEdit->hide(); + + eapMethodCombox->show(); + userNameEdit->show(); + userPwdEdit->show(); + userPwdFlagBox->show(); + + pwdLabel->hide(); + //企业wifi共有 + eapTypeLabel->show(); + //TLS + identityLable->hide(); + domainLable->hide(); + caCertPathLabel->hide(); + caNeedFlagLabel->hide(); + clientCertPathLabel->hide(); + clientPrivateKeyLabel->hide(); + clientPrivateKeyPwdLabel->hide(); + + //PEAP TTLS共有 + eapMethodLabel->show(); + userNameLabel->show(); + userPwdLabel->show(); + userPwdFlagLabel->show(); +} + +KyEapMethodTlsInfo SecurityPage::assembleTlsInfo() +{ + KyEapMethodTlsInfo info; + info.identity = identityEdit->text(); + info.domain = domainEdit->text(); + info.caCertPath = caCertPathCombox->currentText(); + info.bNeedCa = !caNeedBox->isChecked(); + info.clientCertPath = clientCertPathCombox->currentText(); + info.clientPrivateKey = clientPrivateKeyCombox->currentText(); + info.clientPrivateKeyPWD = clientPrivateKeyPwdEdit->text(); + info.m_privateKeyPWDFlag = NetworkManager::Setting::None; + + return info; +} + +KyEapMethodPeapInfo SecurityPage::assemblePeapInfo() +{ + KyEapMethodPeapInfo info; +// info.phase2AuthMethod = (KyNoEapMethodAuth)eapMethodCombox->currentData().toInt(); + switch (eapMethodCombox->currentIndex()) { + case 0: + info.phase2AuthMethod = KyAuthMethodMschapv2; + break; + case 1: + info.phase2AuthMethod = KyAuthMethodMd5; + break; + case 2: + info.phase2AuthMethod = KyAuthMethodGtc; + break; + default: + break; + } + info.userName = userNameEdit->text(); + info.userPWD = userPwdEdit->text(); + info.m_passwdFlag = (userPwdFlagBox->isChecked() ? NetworkManager::Setting::NotSaved : NetworkManager::Setting::None); + + return info; +} +KyEapMethodTtlsInfo SecurityPage::assembleTtlsInfo() +{ + KyEapMethodTtlsInfo info; + switch (eapMethodCombox->currentIndex()) { + case PAP: + info.authType = AUTH_NO_EAP; + info.authNoEapMethod = KyAuthMethodPap; + break; + case MSCHAP: + info.authType = AUTH_NO_EAP; + info.authNoEapMethod = KyAuthMethodChap; + break; + case MSCHAPV2_EAP: + info.authType = AUTH_EAP; + info.authEapMethod = KyAuthEapMethodMschapv2; + break; + case MSCHAPV2: + info.authType = AUTH_NO_EAP; + info.authNoEapMethod = KyAuthMethodMschapv2; + break; + case CHAP: + info.authType = AUTH_NO_EAP; + info.authNoEapMethod = KyAuthMethodChap; + break; + case MD5_EAP: + info.authType = AUTH_EAP; + info.authEapMethod = KyAuthEapMethodMd5; + break; + case GTC_EAP: + info.authType = AUTH_EAP; + info.authEapMethod = KyAuthEapMethodGtc; + break; + default: + break; + } + info.userName = userNameEdit->text(); + info.userPWD = userPwdEdit->text(); + info.m_passwdFlag = (userPwdFlagBox->isChecked() ? NetworkManager::Setting::NotSaved : NetworkManager::Setting::None); + return info; +} + +void SecurityPage::updateSecurityChange(KyWirelessConnectSetting &setting) +{ + qDebug() << "secuTypeCombox->currentData()" << secuTypeCombox->currentData().toInt() << pwdEdit->text(); + if (secuTypeCombox->currentData().toInt() == NONE) { + setting.m_psk = ""; + } else { + setting.m_psk = pwdEdit->text(); + } + + if (secuTypeCombox->currentData().toInt() == NONE) { + setting.m_type = WpaNone; + } else if (secuTypeCombox->currentData().toInt() == WPA_AND_WPA2_PERSONAL) { + setting.m_type = WpaPsk; + } else if (secuTypeCombox->currentData().toInt() == WPA3_PERSONAL) { + setting.m_type = SAE; + } +} + +bool SecurityPage::checkConnectBtnIsEnabled() +{ + int index = secuTypeCombox->currentData().toInt(); + if (index == NONE) { + + } else if (index == WPA_AND_WPA2_PERSONAL || index == WPA3_PERSONAL) { + if (pwdEdit->text().isEmpty() || pwdEdit->text().length() < 8 ) { + qDebug() << "password is empty or length < 8"; + return false; + } + } else if (index == WPA_AND_WPA2_ENTERPRISE) { + int type = eapTypeCombox->currentData().toInt(); + if (type == TLS) { + if (identityEdit->text().isEmpty()) { + qDebug() << "tls identity is empty"; + return false; + } + QFile cafile(caCertPathCombox->currentText()); + if(!caNeedBox->isChecked() && !cafile.exists()) { + qDebug() << "ca cert filepath " << caCertPathCombox->currentText() << " is invalid"; + return false; + } + + QFile cliCafile(clientCertPathCombox->currentText()); + if(!cliCafile.exists()) { + qDebug() << "client cert filepath " << clientCertPathCombox->currentText() << " is invalid"; + return false; + } + + QFile cliKeyfile(clientPrivateKeyCombox->currentText()); + if(!cliKeyfile.exists()) { + qDebug() << "client private key filepath " << clientPrivateKeyCombox->currentText() << " is invalid"; + return false; + } + + if(clientPrivateKeyPwdEdit->text().isEmpty()) { + qDebug() << "client Private Key password is empty"; + return false; + } + } else if (type == PEAP || type == TTLS) { + if(userNameEdit->text().isEmpty() || userPwdEdit->text().isEmpty()) { + qDebug() << "user name or user password is empty"; + return false; + } + } + } + return true; +} + +void SecurityPage::setEnableOfSaveBtn() +{ + emit setSecuPageState(checkConnectBtnIsEnabled()); +} + +void SecurityPage::onSecuTypeComboxIndexChanged() +{ + int index = secuTypeCombox->currentData().toInt(); + if (index == WPA_AND_WPA2_PERSONAL || index == WPA3_PERSONAL) { + showPsk(); + } else if (index == WPA_AND_WPA2_ENTERPRISE) { + onEapTypeComboxIndexChanged(); + } else if (index == NONE) { + showNone(); + } +} + +void SecurityPage::onEapTypeComboxIndexChanged() +{ + qDebug() << "onEapTypeComboxIndexChanged"; + int index = eapTypeCombox->currentData().toInt(); + if (index == TLS) { + showTls(); + emit this->eapTypeChanged(TLS); + } else if (index == PEAP) { + showPeapOrTtls(); + eapMethodCombox->clear(); + eapMethodCombox->addItem("MSCHAPv2", MSCHAPV2_PEAP); + eapMethodCombox->addItem("MD5", MD5_PEAP); + eapMethodCombox->addItem("GTC", GTC_PEAP); + emit this->eapTypeChanged(PEAP); + } else if (index == TTLS) { + showPeapOrTtls(); + eapMethodCombox->clear(); + eapMethodCombox->addItem("pap", PAP); + eapMethodCombox->addItem("mschap", MSCHAP); + eapMethodCombox->addItem("mschapv2(eap)", MSCHAPV2_EAP); + eapMethodCombox->addItem("mschapv2", MSCHAPV2); + eapMethodCombox->addItem("chap", CHAP); + eapMethodCombox->addItem("md5(eap)", MD5_EAP); + eapMethodCombox->addItem("gtc(eap)", GTC_EAP); + emit this->eapTypeChanged(TTLS); + } +} + +void SecurityPage::onCaNeedBoxClicked() +{ + if (caNeedBox->isChecked()) { + caCertPathCombox->setItemText(0, QString(tr("None"))); + caCertPathCombox->setEnabled(false); + } else { + caCertPathCombox->setEnabled(true); + } +} + +void SecurityPage::onPwdBoxClicked() +{ + if (pwdEdit->echoMode() == QLineEdit::Password) { + pwdBox->setChecked(true); + pwdEdit->setEchoMode(QLineEdit::Normal); + } else { + pwdBox->setChecked(false); + pwdEdit->setEchoMode(QLineEdit::Password); + } +} + +void SecurityPage::onUserPwdBox() +{ + if (userPwdEdit->echoMode() == QLineEdit::Password) { + userPwdBox->setChecked(true); + userPwdEdit->setEchoMode(QLineEdit::Normal); + } else { + userPwdBox->setChecked(false); + userPwdEdit->setEchoMode(QLineEdit::Password); + } + +} + +void SecurityPage::onPrivateKeyBoxClicked() +{ + if (clientPrivateKeyPwdEdit->echoMode() == QLineEdit::Password) { + privateKeyBox->setChecked(true); + clientPrivateKeyPwdEdit->setEchoMode(QLineEdit::Normal); + } else { + privateKeyBox->setChecked(false); + clientPrivateKeyPwdEdit->setEchoMode(QLineEdit::Password); + } +} + +void SecurityPage::onCaCertPathComboxIndexChanged(QString str) +{ + if (str.contains("Choose from file...") || str.contains("从文件选择...")) + { + QString fileName = QFileDialog::getOpenFileName(this, tr("Choose a CA certificate"), "recent:///", + tr("CA Files (*.pem *.der *.p12 *.crt *.cer *.pfx)")); + if (!fileName.isNull()) { + QStringList nameList = fileName.split("/"); + caCertPathCombox->blockSignals(true); + caCertPathCombox->setItemText(0, fileName); + caCertPathCombox->setCurrentIndex(0); + caCertPathCombox->blockSignals(false); + } else { + caCertPathCombox->blockSignals(true); + caCertPathCombox->setItemText(0, tr("None")); + caCertPathCombox->setCurrentIndex(0); + caCertPathCombox->blockSignals(false); + } + } else { + qWarning() << "Choose file is null or unvalible"; + } +} + +void SecurityPage::onClientCertPathComboxIndexChanged(QString str) +{ + if (str.contains("Choose from file...") || str.contains("从文件选择...")) + { + QString fileName = QFileDialog::getOpenFileName(this, tr("Choose a CA certificate"), "recent:///", + tr("CA Files (*.pem *.der *.p12 *.crt *.cer *.pfx)")); + if (!fileName.isNull()) { + clientCertPathCombox->blockSignals(true); + clientCertPathCombox->setItemText(0, fileName); + clientCertPathCombox->setCurrentIndex(0); + clientCertPathCombox->blockSignals(false); + } else { + clientCertPathCombox->blockSignals(true); + clientCertPathCombox->setItemText(0, tr("None")); + clientCertPathCombox->setCurrentIndex(0); + clientCertPathCombox->blockSignals(false); + } + } else { + qWarning() << "Choose file is null or unvalible"; + } +} + +void SecurityPage::onClientPrivateKeyComboxIndexChanged(QString str) +{ + if (str.contains("Choose from file...") || str.contains("从文件选择...")) + { + QString fileName = QFileDialog::getOpenFileName(this, tr("Choose a CA certificate"), "recent:///", + tr("CA Files (*.pem *.der *.p12 *.crt *.cer *.pfx)")); + if (!fileName.isNull()) { + QStringList nameList = fileName.split("/"); + clientPrivateKeyCombox->blockSignals(true); + clientPrivateKeyCombox->setItemText(0, fileName); + clientPrivateKeyCombox->setCurrentIndex(0); + clientPrivateKeyCombox->blockSignals(false); + } else { + clientPrivateKeyCombox->blockSignals(true); + clientPrivateKeyCombox->setItemText(0, tr("None")); + clientPrivateKeyCombox->setCurrentIndex(0); + clientPrivateKeyCombox->blockSignals(false); + } + } else { + qWarning() << "Choose file is null or unvalible"; + } +} diff --git a/src/frontend/netdetails/securitypage.h b/src/frontend/netdetails/securitypage.h new file mode 100644 index 00000000..462b920d --- /dev/null +++ b/src/frontend/netdetails/securitypage.h @@ -0,0 +1,113 @@ +#ifndef SECURITYWIDGET_H +#define SECURITYWIDGET_H + +#include +#include +#include +#include +#include +#include + +#include "coninfo.h" + +class SecurityPage : public QFrame +{ + Q_OBJECT +public: + SecurityPage(QWidget *parent = nullptr); + + void setSecurity(KySecuType index); + void setPsk(const QString &psk); + void setTlsInfo(KyEapMethodTlsInfo &info); + void setPeapInfo(KyEapMethodPeapInfo &info); + void setTtlsInfo(KyEapMethodTtlsInfo &info); + void setSecurityVisible(const bool &visible); + + bool checkIsChanged(const ConInfo info); + void updateSecurityChange(KyWirelessConnectSetting &setting); + void updateTlsChange(KyEapMethodTlsInfo &info); + void updatePeapChange(KyEapMethodPeapInfo &info); + void updateTtlsChange(KyEapMethodTtlsInfo &info); + + void getSecuType(KySecuType &secuType, KyEapMethodType &enterpriseType); + +private: + QFormLayout *mSecuLayout; + +private: + + QLabel *secuTypeLabel; + QLabel *pwdLabel; + //企业wifi共有 + QLabel *eapTypeLabel; + //TLS + QLabel *identityLable; + QLabel *domainLable; + QLabel *caCertPathLabel; + QLabel *caNeedFlagLabel; + QLabel *clientCertPathLabel; + QLabel *clientPrivateKeyLabel; + QLabel *clientPrivateKeyPwdLabel; + + //PEAP TTLS共有 + QLabel *eapMethodLabel; + QLabel *userNameLabel; + QLabel *userPwdLabel; + QLabel *userPwdFlagLabel; + + QComboBox *secuTypeCombox; + LineEdit *pwdEdit; + QComboBox *eapTypeCombox; + //TLS + LineEdit *identityEdit; + LineEdit *domainEdit; + QComboBox *caCertPathCombox; + QCheckBox *caNeedBox; + QComboBox *clientCertPathCombox; + QComboBox *clientPrivateKeyCombox; + LineEdit *clientPrivateKeyPwdEdit; + + //PEAP && TTLS + QComboBox *eapMethodCombox; + LineEdit *userNameEdit; + LineEdit *userPwdEdit; + QCheckBox *userPwdFlagBox; + + QCheckBox *pwdBox; + QCheckBox *userPwdBox; + QCheckBox *privateKeyBox; + + void showNone(); + void showPsk(); + void showTls(); + void showPeapOrTtls(); + void initUI(); + void initConnect(); + + KyEapMethodTlsInfo assembleTlsInfo(); + KyEapMethodPeapInfo assemblePeapInfo(); + KyEapMethodTtlsInfo assembleTtlsInfo(); + + bool checkConnectBtnIsEnabled(); + + +private slots: + void onSecuTypeComboxIndexChanged(); + void onEapTypeComboxIndexChanged(); + void setEnableOfSaveBtn(); + + void onCaNeedBoxClicked(); + void onPwdBoxClicked(); + void onUserPwdBox(); + void onPrivateKeyBoxClicked(); + + void onCaCertPathComboxIndexChanged(QString str); + void onClientCertPathComboxIndexChanged(QString str); + void onClientPrivateKeyComboxIndexChanged(QString str); + +signals: + void setSecuPageState(bool); + void eapTypeChanged(const KyEapMethodType &type); +}; + +#endif // SECURITYWIDGET_H diff --git a/src/frontend/tab-pages/lanpage.cpp b/src/frontend/tab-pages/lanpage.cpp new file mode 100644 index 00000000..27799a92 --- /dev/null +++ b/src/frontend/tab-pages/lanpage.cpp @@ -0,0 +1,1257 @@ +#include "lanpage.h" +#include +#include + +#define MAIN_LAYOUT_MARGINS 0,0,0,0 +#define MAIN_LAYOUT_SPACING 0 +#define TITLE_FRAME_HEIGHT 52 +#define TITLE_LAYOUT_MARGINS 24,0,24,0 +#define LAN_LIST_SPACING 0 +#define TEXT_MARGINS 16,0,0,0 +#define SETTINGS_LAYOUT_MARGINS 24,16,24,16 +#define TRANSPARENT_COLOR QColor(0,0,0,0) +#define ITEM_HEIGHT 48 + +#define LOG_FLAG "[LanPage]" + +const QString EMPTY_CONNECT_UUID = "emptyconnect"; + +const QString WIRED_SWITCH = "wiredswitch"; + +LanPage::LanPage(QWidget *parent) : TabPage(parent) +{ + m_activeResourse = new KyActiveConnectResourse(this); + m_connectResourse = new KyConnectResourse(this); + m_deviceResource = new KyNetworkDeviceResourse(this); + m_wiredConnectOperation = new KyWiredConnectOperation(this); + + initUI(); + initLanDevice(); + initNetSwitch(); + initLanDeviceState(); + + initDeviceCombox(); + initLanArea(); + + connect(m_activeResourse, &KyActiveConnectResourse::stateChangeReason, this, &LanPage::onConnectionStateChange); + connect(m_activeResourse, &KyActiveConnectResourse::activeConnectRemove, this, [=] (QString activeConnectUuid) { + sendLanStateChangeSignal(activeConnectUuid,Deactivated); + } ); + + connect(m_connectResourse, &KyConnectResourse::connectionAdd, this, &LanPage::onAddConnection); + connect(m_connectResourse, &KyConnectResourse::connectionRemove, this, &LanPage::onRemoveConnection); + connect(m_connectResourse, &KyConnectResourse::connectionUpdate, this, &LanPage::onUpdateConnection); + + connect(m_deviceResource, &KyNetworkDeviceResourse::deviceAdd, this, &LanPage::onDeviceAdd); + connect(m_deviceResource, &KyNetworkDeviceResourse::deviceRemove, this, &LanPage::onDeviceRemove); + connect(m_deviceResource, &KyNetworkDeviceResourse::deviceNameUpdate, this, &LanPage::onDeviceNameUpdate); + + connect(m_deviceResource, &KyNetworkDeviceResourse::carrierChanage, this, &LanPage::onDeviceCarriered); + connect(m_deviceResource, &KyNetworkDeviceResourse::deviceActiveChanage, this, &LanPage::onDeviceActiveChanage); + + connect(m_wiredConnectOperation, &KyWiredConnectOperation::activateConnectionError, this, &LanPage::activateFailed); + connect(m_wiredConnectOperation, &KyWiredConnectOperation::deactivateConnectionError, this, &LanPage::deactivateFailed); + +} + +LanPage::~LanPage() +{ + +} + +void LanPage::initLanDevice() +{ + m_devList.clear(); + m_deviceResource->getNetworkDeviceList(NetworkManager::Device::Type::Ethernet, m_devList); + + m_currentDeviceName = getDefaultDeviceName(WIRED); + bool getDefault = !m_currentDeviceName.isEmpty(); + if (getDefault) { + if (m_deviceResource->wiredDeviceIsCarriered(m_currentDeviceName)) { + return; + } + } + + QList activedList; + for (int index = 0; index < m_devList.size(); ++index) { + m_activeResourse->getActiveConnectionList(m_devList.at(index), + NetworkManager::ConnectionSettings::Wired, activedList); + if (!activedList.isEmpty()) { + m_currentDeviceName = m_devList.at(index); + if (!getDefault) { + setDefaultDevice(WIRED, m_currentDeviceName); + } + return; + } + } + + for (int index = 0; index < m_devList.size(); ++index) { + if (m_deviceResource->wiredDeviceIsCarriered(m_devList.at(index))) { + m_currentDeviceName = m_devList.at(index); + if (!getDefault) { + setDefaultDevice(WIRED, m_currentDeviceName); + } + return; + } + } + return; +} + +void LanPage::initLanDeviceState() +{ + QMap deviceStateMap; + getDeviceEnableState(WIRED, deviceStateMap); + + QStringList disableDeviceList; + disableDeviceList.clear(); + m_enableDeviceList.clear(); + for (int index = 0; index < m_devList.count(); ++index) { + QString deviceName = m_devList.at(index); + if (deviceStateMap.contains(deviceName)) { + if (deviceStateMap[deviceName]) { + m_enableDeviceList<keys().contains(WIRED_SWITCH)) { + m_wiredSwitch = m_switchGsettings->get(WIRED_SWITCH).toBool(); + connect(m_switchGsettings, &QGSettings::changed, this, &LanPage::onSwithGsettingsChanged); + } + } else { + qDebug()<<"[LanPage] org.ukui.kylin-nm.switch is not installed!"; + } + + if (m_devList.count() == 0) { + qDebug() << "[wiredSwitch]:init not enable when no device"; + m_wiredSwitch = false; + m_netSwitch->setSwitchStatus(m_wiredSwitch); + m_netSwitch->setEnabled(false); + } + + qDebug() << "[wiredSwitch]:init state:" << m_wiredSwitch; + + m_netSwitch->setSwitchStatus(m_wiredSwitch); + + connect(m_netSwitch, &SwitchButton::clicked, this, &LanPage::onLanSwitchClicked); + + return; +} + +void LanPage::onSwithGsettingsChanged(const QString &key) +{ + if (key == WIRED_SWITCH) { + m_netSwitch->blockSignals(true); + + m_wiredSwitch = m_switchGsettings->get(WIRED_SWITCH).toBool(); + qDebug()<<"[LanPage] SwitchButton statue changed to:" << m_wiredSwitch; + + KyWiredConnectOperation wiredOperation; + if (m_wiredSwitch) { + for (int index = 0; index < m_enableDeviceList.size(); ++index) { + qDebug()<<"[LanPage] open wired device "<< m_enableDeviceList.at(index); + wiredOperation.openWiredNetworkWithDevice(m_enableDeviceList.at(index)); + } + } else { + for (int index = 0; index < m_enableDeviceList.size(); ++index) { + qDebug()<<"[LanPage] close wired device "<< m_enableDeviceList.at(index); + wiredOperation.closeWiredNetworkWithDevice(m_enableDeviceList.at(index)); + } + } + + initDeviceCombox(); + initLanArea(); + + m_netSwitch->setSwitchStatus(m_wiredSwitch); + + m_netSwitch->blockSignals(false); + } +} + +void LanPage::onLanSwitchClicked() +{ + qDebug()<<"[LanPage] On lan switch button clicked! Status:" <getSwitchStatus() + <<"devices count:"<showDesktopNotify(tr("No ethernet device avaliable")); + } else { + if (m_netSwitch->getSwitchStatus()) { + //qDebug() << "[wiredSwitch]set true after clicked"; + m_switchGsettings->set(WIRED_SWITCH, true); + } else { + //qDebug() << "[wiredSwitch]set false after clicked"; + m_switchGsettings->set(WIRED_SWITCH,false); + } + } +} + +void LanPage::getEnabledDevice(QStringList &enableDeviceList) +{ + int index = 0; + QMap deviceMap; + + if (m_devList.isEmpty()) { + qDebug()<<"[LanPage] there is not wired device."; + return; + } + + getDeviceEnableState(WIRED, deviceMap); + for (index = 0; index < m_devList.size(); ++index) { + if (deviceMap.contains(m_devList.at(index))) { + if (deviceMap[m_devList.at(index)]) { + enableDeviceList << m_devList.at(index); + } + } else { + saveDeviceEnableState(m_devList.at(index), true); + enableDeviceList << m_devList.at(index); + } + } + + return; +} + +void LanPage::getDisabledDevices(QStringList &disableDeviceList) +{ + int index = 0; + QMap deviceMap; + + if (m_devList.isEmpty()) { + qDebug()<<"[LanPage] there is not wired device."; + return; + } + + getDeviceEnableState(WIRED, deviceMap); + for (index = 0; index < m_devList.size(); ++index) { + if (deviceMap.contains(m_devList.at(index))) { + if (!deviceMap[m_devList.at(index)]) { + disableDeviceList << m_devList.at(index); + } + } + } + + return; +} + +void LanPage::initDeviceCombox() +{ + //TODO 获取设备列表,单设备时隐藏下拉框,多设备时添加到下拉框;m_devList记录插入的所有设备,deviceMap记录设备状态 + disconnect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &LanPage::onDeviceComboxIndexChanged); + + m_deviceComboBox->clear(); + + if (m_wiredSwitch) { + int enableDeviceCount = m_enableDeviceList.count(); + if (enableDeviceCount > 1) { + for (int index = 0; index < enableDeviceCount; ++index) { + m_deviceComboBox->addItem(m_enableDeviceList.at(index)); + } + + m_deviceFrame->show(); + m_tipsLabel->hide(); + m_deviceComboBox->show(); + + if (m_currentDeviceName != m_deviceComboBox->currentText()) { + if (m_enableDeviceList.contains(m_currentDeviceName)) { + m_deviceComboBox->setCurrentText(m_currentDeviceName); + } else { + m_currentDeviceName = m_deviceComboBox->currentText(); + } + } + + } else if (enableDeviceCount == 1) { + m_deviceFrame->hide(); + + if (m_currentDeviceName != m_enableDeviceList.at(0)) { + m_currentDeviceName = m_enableDeviceList.at(0); + } + } else { + m_deviceFrame->show(); + m_deviceComboBox->hide(); + m_tipsLabel->show(); + m_currentDeviceName = ""; + } + } else { + m_deviceFrame->hide(); + m_currentDeviceName = ""; + + } + + setDefaultDevice(WIRED, m_currentDeviceName); + connect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &LanPage::onDeviceComboxIndexChanged); + return; +} + +void LanPage::addEmptyConnectItem(QMap &connectMap, + QListWidget *lanListWidget) +{ + QListWidgetItem *p_listWidgetItem = addNewItem(nullptr, lanListWidget); + connectMap.insert(EMPTY_CONNECT_UUID, p_listWidgetItem); + + return; +} + + +void LanPage::deleteConnectionMapItem(QMap &connectMap, + QListWidget *lanListWidget, QString uuid) +{ + QListWidgetItem *p_listWidgetItem = connectMap.value(uuid); + if (p_listWidgetItem) { + connectMap.remove(uuid); + LanListItem *p_lanItem = (LanListItem *)lanListWidget->itemWidget(p_listWidgetItem); + lanListWidget->removeItemWidget(p_listWidgetItem); + + delete p_lanItem; + p_lanItem = nullptr; + + delete p_listWidgetItem; + p_listWidgetItem = nullptr; + } + + return; +} + +void LanPage::clearConnectionMap(QMap &connectMap, + QListWidget *lanListWidget) +{ + QMap::iterator iter; + + iter = connectMap.begin(); + while (iter != connectMap.end()) { + qDebug()<<"[LanPage] clear connection map item"<< iter.key(); + + QListWidgetItem *p_widgetItem = iter.value(); + LanListItem *p_lanItem = (LanListItem *)lanListWidget->itemWidget(p_widgetItem); + lanListWidget->removeItemWidget(p_widgetItem); + + delete p_lanItem; + p_lanItem = nullptr; + + delete p_widgetItem; + p_widgetItem = nullptr; + + iter = connectMap.erase(iter); + } + + return; +} + +void LanPage::constructActiveConnectionArea() +{ + QList activedList; + + activedList.clear(); + clearConnectionMap(m_activeConnectionMap, m_activatedLanListWidget); + + m_activeResourse->getActiveConnectionList(m_currentDeviceName, + NetworkManager::ConnectionSettings::Wired, activedList); //激活列表的显示 + qDebug() << "[LanPage] construct active connection area get Active list size:" << activedList.size(); + if (!activedList.isEmpty()) { + for (int index = 0; index < activedList.size(); index++) { + KyConnectItem *p_activeConnectionItem = activedList.at(index); + qDebug() << "[LanPage] construct active connection area. add active item" + << p_activeConnectionItem->m_connectName; + QListWidgetItem *p_listWidgetItem = addNewItem(p_activeConnectionItem, m_activatedLanListWidget); + m_activeConnectionMap.insert(p_activeConnectionItem->m_connectUuid, p_listWidgetItem); + + delete p_activeConnectionItem; + p_activeConnectionItem = nullptr; + } + } else { + qDebug()<<"[LanPage] there is not active"; + addEmptyConnectItem(m_activeConnectionMap, m_activatedLanListWidget); + } + + return; +} + +void LanPage::constructConnectionArea() +{ + QList deactivedList; + + deactivedList.clear(); + clearConnectionMap(m_inactiveConnectionMap, m_inactivatedLanListWidget); + + m_connectResourse->getConnectionList(m_currentDeviceName, NetworkManager::ConnectionSettings::Wired, deactivedList); //未激活列表的显示 + qDebug() << "[LanPage]construct connection area get connection list size:" << deactivedList.size(); + if (!deactivedList.isEmpty()) { + for (int index = 0; index < deactivedList.size(); index++) { + KyConnectItem *p_deactiveConnectionItem = deactivedList.at(index); + qDebug()<<"[LanPage] construct connection area add deactive item"<m_connectName; + QListWidgetItem *p_listWidgetItem = addNewItem(p_deactiveConnectionItem, m_inactivatedLanListWidget); + if (m_inactiveConnectionMap.contains(p_deactiveConnectionItem->m_connectUuid)) { + qDebug()<m_connectUuid; + } + m_inactiveConnectionMap.insert(p_deactiveConnectionItem->m_connectUuid, p_listWidgetItem); + + delete p_deactiveConnectionItem; + p_deactiveConnectionItem = nullptr; + + } + } + if (m_inactivatedLanListWidget->count() < MAX_ITEMS) { + m_inactivatedLanListWidget->setFixedWidth(MIN_WIDTH); + } else { + m_inactivatedLanListWidget->setFixedWidth(MAX_WIDTH); + } + return; +} + +void LanPage::initLanArea() +{ + if (!m_wiredSwitch || m_currentDeviceName.isEmpty()) { + m_activatedNetDivider->hide(); + m_activatedNetFrame->hide(); + + m_inactivatedNetFrame->hide(); + } else { + m_activatedNetDivider->show(); + m_activatedNetFrame->show(); + constructActiveConnectionArea(); + + m_inactivatedNetFrame->show(); + constructConnectionArea(); + } + + return; +} + +bool LanPage::removeConnectionItem(QMap &connectMap, + QListWidget *lanListWidget, QString path) +{ + QMap::iterator iter; + for (iter = connectMap.begin(); iter != connectMap.end(); ++iter) { + QListWidgetItem *p_listWidgetItem = iter.value(); + LanListItem *p_lanItem = (LanListItem*)lanListWidget->itemWidget(p_listWidgetItem); + if (p_lanItem->getConnectionPath() == path) { + qDebug()<<"[LanPage] Remove a connection from list"; + + lanListWidget->removeItemWidget(p_listWidgetItem); + + delete p_lanItem; + p_lanItem = nullptr; + + delete p_listWidgetItem; + p_listWidgetItem = nullptr; + + iter = connectMap.erase(iter); + if (m_inactivatedLanListWidget->count() < MAX_ITEMS) { + m_inactivatedLanListWidget->setFixedWidth(MIN_WIDTH); + } + return true; + } + } + + return false; +} + +void LanPage::onRemoveConnection(QString path) //删除时后端会自动断开激活,将其从未激活列表中删除 +{ + //for dbus + qDebug() << "[LanPage] emit lanRemove because onRemoveConnection " << path; + emit lanRemove(path); + + if (removeConnectionItem(m_inactiveConnectionMap, m_inactivatedLanListWidget, path)) { + return; + } else { + removeConnectionItem(m_activeConnectionMap, m_activatedLanListWidget, path); + if (m_activeConnectionMap.count() <= 0) { + addEmptyConnectItem(m_activeConnectionMap, m_activatedLanListWidget); + } + + return; + } +} + +void LanPage::onAddConnection(QString uuid) //新增一个有线连接,将其加入到激活列表 +{ + if (!m_connectResourse->isWiredConnection(uuid)) { + return; + } + + KyConnectItem *p_newItem = nullptr; + p_newItem = m_connectResourse->getConnectionItemByUuid(uuid); + if (nullptr == p_newItem) { + return; + } + + sendLanAddSignal(p_newItem); + + if (p_newItem->m_ifaceName == m_currentDeviceName || p_newItem->m_ifaceName == "") { + qDebug()<<"[LanPage] Add a new connection, name:"<m_connectName; + QListWidgetItem *p_listWidgetItem = insertNewItem(p_newItem, m_inactivatedLanListWidget); + if (m_inactiveConnectionMap.contains(p_newItem->m_connectUuid)) { + qDebug()<m_connectUuid; + } + m_inactiveConnectionMap.insert(p_newItem->m_connectUuid, p_listWidgetItem); + } + + delete p_newItem; + p_newItem = nullptr; + if (m_inactivatedLanListWidget->count() >= MAX_ITEMS) { + m_inactivatedLanListWidget->setFixedWidth(MAX_WIDTH); + } + return; +} + +void LanPage::addDeviceForCombox(QString deviceName) +{ + disconnect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &LanPage::onDeviceComboxIndexChanged); + + if (m_wiredSwitch) { + if (1 == m_enableDeviceList.count()) { + //1、从无到有添加第一块有线网卡 + //2、有多快网卡,但是没有使能 + m_deviceFrame->hide(); + m_currentDeviceName = deviceName; + setDefaultDevice(WIRED, m_currentDeviceName); + } else if (2 == m_enableDeviceList.count()) { + //3、现在有且只有一块网卡,并已使能 + //4、有多快网卡,且使能了其中一块 + m_deviceComboBox->addItem(m_currentDeviceName); + m_deviceComboBox->addItem(deviceName); + + m_deviceFrame->show(); + m_tipsLabel->hide(); + m_deviceComboBox->show(); + } else if (m_enableDeviceList.count() > 2) { + //5、有多快网卡且使能了多块网卡 + m_deviceComboBox->addItem(deviceName); + } + } + + connect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &LanPage::onDeviceComboxIndexChanged); + return; +} + +void LanPage::onDeviceAdd(QString deviceName, NetworkManager::Device::Type deviceType) +{ + if (!m_deviceResource->deviceIsWired(deviceName)) { + return; + } + + if (m_devList.contains(deviceName)) { + return; + } + + if (m_devList.count() == 0) {// 有线网卡从无到有,打开开关 + m_netSwitch->setEnabled(true); + m_wiredSwitch = m_switchGsettings->get(WIRED_SWITCH).toBool(); + m_netSwitch->setSwitchStatus(m_wiredSwitch); + } + + qDebug() << "[LanPage] Begin add device:" << deviceName; + + m_devList << deviceName; + saveDeviceEnableState(deviceName, true); + m_enableDeviceList<::of(&QComboBox::currentIndexChanged), + this, &LanPage::onDeviceComboxIndexChanged); + + if (m_wiredSwitch) { + if (0 == m_enableDeviceList.count()) { + //1、没有使能任何网卡 + goto l_out; + } else if (1 == m_enableDeviceList.count()) { + //2、使能了一个网卡,且当前网卡是要删除的网卡 + if (m_currentDeviceName == deviceName) { + m_deviceFrame->show(); + m_deviceComboBox->hide(); + m_tipsLabel->show(); + m_currentDeviceName = ""; + setDefaultDevice(WIRED, m_currentDeviceName); + } + } else if (2 == m_enableDeviceList.count()) { + //3、使能了两个网卡,且包括要删除的网卡,有可能是要删除的网卡 + if (m_deviceComboBox->findText(deviceName) != -1) { + for (int index = m_deviceComboBox->count() - 1; index >= 0; index--) { + if (m_currentDeviceName == deviceName + && m_deviceComboBox->itemText(index) != deviceName) { + m_currentDeviceName = m_deviceComboBox->itemText(index); + setDefaultDevice(WIRED, m_currentDeviceName); + } + m_deviceComboBox->removeItem(index); + } + + m_tipsLabel->hide(); + m_deviceFrame->hide(); + m_deviceComboBox->hide(); + } + } else { + //4、使能了多个网卡,且包括要删除的网卡,有可能是正要删除的网卡 + int index = m_deviceComboBox->findText(deviceName); + if (index != -1) { + m_deviceComboBox->removeItem(index); + if (m_currentDeviceName == deviceName) { + m_currentDeviceName = m_deviceComboBox->currentText(); + setDefaultDevice(WIRED, m_currentDeviceName); + } + } + } + } + +l_out: + connect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &LanPage::onDeviceComboxIndexChanged); + + return; +} + +void LanPage::onDeviceRemove(QString deviceName) +{ + if (!m_devList.contains(deviceName)) { + return; + } + + qDebug() << "[LanPage] deviceRemove:" << deviceName; + + m_devList.removeOne(deviceName); + if (m_devList.count() == 0) { + m_wiredSwitch = false; + m_netSwitch->setSwitchStatus(m_wiredSwitch); + m_netSwitch->setEnabled(false); + qDebug() << "[wiredSwitch]set not enable after device remove"; + } + + QString nowDevice = m_currentDeviceName; + deleteDeviceFromCombox(deviceName); + if (nowDevice == deviceName) { + setDefaultDevice(WIRED, m_currentDeviceName); + initLanArea(); + } + + if (m_enableDeviceList.contains(deviceName)) { + m_enableDeviceList.removeOne(deviceName); + } + deleteDeviceEnableState(deviceName); + + emit deviceStatusChanged(); + + return; +} + +void LanPage::updateDeviceCombox(QString oldDeviceName, QString newDeviceName) +{ + if (m_currentDeviceName == oldDeviceName) { + m_currentDeviceName = newDeviceName; + setDefaultDevice(WIRED, m_currentDeviceName); + } + + int index = m_deviceComboBox->findText(oldDeviceName); + if (index != -1) { + m_deviceComboBox->setItemText(index, newDeviceName); + } + + return; +} + +void LanPage::onDeviceNameUpdate(QString oldName, QString newName) +{ + if (m_devList.contains(oldName)) { + m_devList.removeOne(oldName); + m_devList.append(newName); + + if (m_enableDeviceList.contains(oldName)) { + m_enableDeviceList.removeOne(oldName); + m_enableDeviceList.append(newName); + } + qDebug() << "[LanPage] emit deviceNameUpdate " << oldName << newName; + + updateDeviceCombox(oldName, newName); + if (m_currentDeviceName == newName) { + initLanArea(); + } + + emit deviceNameChanged(oldName, newName, WIRED); + } +} + +void LanPage::onDeviceCarriered(QString deviceName, bool pluged) +{ + if (!pluged) { + return; + } + if (m_enableDeviceList.contains(deviceName)) { + m_wiredConnectOperation->openWiredNetworkWithDevice(deviceName); + updateCurrentDevice(deviceName); + } + return; +} + +void LanPage::onDeviceActiveChanage(QString deviceName, bool deviceActive) +{ + if (!m_devList.contains(deviceName)) { + return; + } + + if (deviceActive) { + if (!m_wiredSwitch || !m_enableDeviceList.contains(deviceName)) { + qDebug()<< LOG_FLAG << "close disabled device"; + m_wiredConnectOperation->closeWiredNetworkWithDevice(deviceName); + } + } + + return; +} + + +void LanPage::onDeviceComboxIndexChanged(int currentIndex) +{ + //TODO 设备变更时更新设备和列表 + m_currentDeviceName = m_deviceComboBox->currentText(); + qDebug() << "[LanPage]Current Device Changed to:" << m_currentDeviceName; + initLanArea(); +} + +void LanPage::onShowControlCenter() +{ + QProcess process; + process.startDetached("ukui-control-center -m netconnect"); +} + +void LanPage::initUI() +{ + m_titleLabel->setText(tr("LAN")); + + m_activatedNetLabel->setText(tr("Activated LAN")); + m_activatedLanListWidget = new QListWidget(m_activatedNetFrame); + m_activatedLanListWidget->setFrameShape(QFrame::Shape::NoFrame); + m_activatedLanListWidget->setSpacing(LAN_LIST_SPACING); + m_activatedLanListWidget->setFixedHeight(ITEM_HEIGHT); //active区域固定高度,只显示一个条目 + m_activatedLanListWidget->setFixedWidth(MIN_WIDTH); + m_activatedLanListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_activatedNetLayout->addWidget(m_activatedLanListWidget); + + m_inactivatedNetLabel->setText(tr("Inactivated LAN")); + m_inactivatedLanListWidget = new QListWidget(m_inactivatedNetListArea); + m_inactivatedLanListWidget->setFrameShape(QFrame::Shape::NoFrame); + m_inactivatedLanListWidget->setSpacing(LAN_LIST_SPACING); + m_inactivatedLanListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_inactivatedLanListWidget->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel); + m_inactivatedLanListWidget->verticalScrollBar()->setProperty("drawScrollBarGroove",false); //去除滚动条的外侧黑框 + m_inactivatedLanListWidget->verticalScrollBar()->setSingleStep(SCROLL_STEP); + m_inactivatedLanListWidget->verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu); + m_inactivatedAreaLayout->addWidget(m_inactivatedLanListWidget); + + QPalette pal = m_activatedLanListWidget->palette(); + pal.setBrush(QPalette::Base, QColor(0,0,0,0)); //背景透明 + m_activatedLanListWidget->setPalette(pal); + m_inactivatedLanListWidget->setPalette(pal); + + m_settingsLabel->installEventFilter(this); +} + +QListWidgetItem *LanPage::insertNewItem(KyConnectItem *itemData, QListWidget *listWidget) +{ + int index = 0; + + for(index = 0; index < m_inactivatedLanListWidget->count(); index++) { + QListWidgetItem *p_listWidgetItem = m_inactivatedLanListWidget->item(index); + LanListItem *p_lanItem = (LanListItem *)m_inactivatedLanListWidget->itemWidget(p_listWidgetItem); + QString name1 = p_lanItem->getConnectionName(); + QString name2 = itemData->m_connectName; + if (QString::compare(name1, name2, Qt::CaseInsensitive) > 0) { + break; + } + } + + QListWidgetItem *p_sortListWidgetItem = new QListWidgetItem(); + p_sortListWidgetItem->setFlags(p_sortListWidgetItem->flags() & (~Qt::ItemIsSelectable)); //设置不可被选中 + p_sortListWidgetItem->setSizeHint(QSize(listWidget->width(),ITEM_HEIGHT)); + + listWidget->insertItem(index, p_sortListWidgetItem); + + LanListItem *p_sortLanItem = nullptr; + p_sortLanItem = new LanListItem(itemData, m_currentDeviceName); + listWidget->setItemWidget(p_sortListWidgetItem, p_sortLanItem); + + return p_sortListWidgetItem; +} + +QListWidgetItem *LanPage::addNewItem(KyConnectItem *itemData, QListWidget *listWidget) +{ + QListWidgetItem *p_listWidgetItem = new QListWidgetItem(); + p_listWidgetItem->setFlags(p_listWidgetItem->flags() & (~Qt::ItemIsSelectable)); + p_listWidgetItem->setSizeHint(QSize(listWidget->width(), ITEM_HEIGHT)); + listWidget->addItem(p_listWidgetItem); + LanListItem *p_lanItem = nullptr; + if (itemData != nullptr) { + p_lanItem = new LanListItem(itemData, m_currentDeviceName); + qDebug() << "[LanPage] addNewItem, connection: " + << itemData->m_connectName << "deviceName: " << m_currentDeviceName; + } else { + p_lanItem = new LanListItem(); + qDebug() << "[LanPage] Add nullItem!"; + } + + listWidget->setItemWidget(p_listWidgetItem, p_lanItem); + return p_listWidgetItem; +} + +void LanPage::updateActivatedConnectionArea(KyConnectItem *p_newItem) +{ + if (m_activeConnectionMap.contains(p_newItem->m_connectUuid)) { + return; + } + + deleteConnectionMapItem(m_inactiveConnectionMap, m_inactivatedLanListWidget, p_newItem->m_connectUuid); + if (p_newItem->m_ifaceName == m_currentDeviceName) { + qDebug()<<"[LanPage]update active connection item"<m_connectName; + deleteConnectionMapItem(m_activeConnectionMap, m_activatedLanListWidget, EMPTY_CONNECT_UUID); + QListWidgetItem *p_listWidgetItem = addNewItem(p_newItem, m_activatedLanListWidget); + m_activeConnectionMap.insert(p_newItem->m_connectUuid, p_listWidgetItem); + this->showDesktopNotify(tr("LAN Connected Successfully")); + } + if (m_inactivatedLanListWidget->count() < MAX_ITEMS) { + m_inactivatedLanListWidget->setFixedWidth(MIN_WIDTH); + } + + return; +} + +void LanPage::updateConnectionArea(KyConnectItem *p_newItem) +{ + if (m_inactiveConnectionMap.contains(p_newItem->m_connectUuid)) { + return; + } + + deleteConnectionMapItem(m_activeConnectionMap, m_activatedLanListWidget, p_newItem->m_connectUuid); + if (m_activeConnectionMap.count() <= 0) { + addEmptyConnectItem(m_activeConnectionMap, m_activatedLanListWidget); + } + + if (p_newItem->m_ifaceName == m_currentDeviceName || p_newItem->m_ifaceName == "") { + qDebug()<<"[LanPage] update connection item"<m_connectName; + QListWidgetItem *p_listWidgetItem = insertNewItem(p_newItem, m_inactivatedLanListWidget); + m_inactiveConnectionMap.insert(p_newItem->m_connectUuid, p_listWidgetItem); + this->showDesktopNotify(tr("LAN Disconnected Successfully")); + } + if (m_inactivatedLanListWidget->count() < MAX_ITEMS) { + m_inactivatedLanListWidget->setFixedWidth(MIN_WIDTH); + } else { + m_inactivatedLanListWidget->setFixedWidth(MAX_WIDTH); + } + + return; +} + +void LanPage::updateConnectionState(QMap &connectMap, + QListWidget *lanListWidget, QString uuid, ConnectState state) +{ + qDebug() << LOG_FLAG << "update connection state"; + + QListWidgetItem *p_listWidgetItem = connectMap.value(uuid); + if (p_listWidgetItem) { + LanListItem *p_lanItem = (LanListItem *)lanListWidget->itemWidget(p_listWidgetItem); + p_lanItem->updateConnectionState(state); + } + + return; +} + +QString LanPage::getConnectionDevice(QString uuid) +{ + QString deviceName = ""; + + deviceName = m_activeResourse->getDeviceOfActivateConnect(uuid); + if (deviceName.isEmpty()) { + m_connectResourse->getInterfaceByUuid(deviceName, uuid); + } + + return deviceName; +} + +void LanPage::updateCurrentDevice(QString deviceName) +{ + if (m_currentDeviceName != deviceName) { + int index = m_deviceComboBox->findText(deviceName); + if (index < 0) { + index = 0; + } + m_deviceComboBox->setCurrentIndex(index); + return; + } + return; +} + +void LanPage::onConnectionStateChange(QString uuid, + NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason) +{ + //lanpage函数内持续监听连接状态的变化并记录供其他函数调用获取状态 + if (!m_connectResourse->isWiredConnection(uuid)) { + qDebug() << "[LanPage] connection state change signal but not wired"; + return; + } + + sendLanStateChangeSignal(uuid, (ConnectState)state); + + qDebug()<<"[LanPage] connection uuid"<< uuid + << "state change slot:"<< state; + + KyConnectItem *p_newItem = nullptr; + QString deviceName = ""; + + if (state == NetworkManager::ActiveConnection::State::Activated) { + p_newItem = m_activeResourse->getActiveConnectionByUuid(uuid); + if (nullptr == p_newItem) { + qWarning()<<"[LanPage] get active connection failed, connection uuid" << uuid; + return; + } + + deviceName = p_newItem->m_ifaceName; + updateActivatedConnectionArea(p_newItem); + updateConnectionState(m_activeConnectionMap, m_activatedLanListWidget, uuid, (ConnectState)state); + } else if (state == NetworkManager::ActiveConnection::State::Deactivated) { + p_newItem = m_connectResourse->getConnectionItemByUuid(uuid); + qDebug() << "[LanPage] deactivated reason" << reason; + if (nullptr == p_newItem) { + qWarning()<<"[LanPage] get active connection failed, connection uuid" << uuid; + return; + } + + deviceName = p_newItem->m_ifaceName; + updateConnectionArea(p_newItem); + updateConnectionState(m_inactiveConnectionMap, m_inactivatedLanListWidget, uuid, (ConnectState)state); + } else if (state == NetworkManager::ActiveConnection::State::Activating) { + deviceName = getConnectionDevice(uuid); + if (deviceName == m_currentDeviceName) { + updateConnectionState(m_inactiveConnectionMap, m_inactivatedLanListWidget, uuid, (ConnectState)state); + } + } else if (state == NetworkManager::ActiveConnection::State::Deactivating) { + deviceName = getConnectionDevice(uuid); + if (deviceName == m_currentDeviceName) { + updateConnectionState(m_activeConnectionMap, m_activatedLanListWidget, uuid, (ConnectState)state); + } + } + + emit lanActiveConnectionStateChanged(deviceName, uuid, state); + + if (p_newItem) { + delete p_newItem; + p_newItem = nullptr; + } + + return; +} + + +void LanPage::getWiredList(QMap > &map) +{ + QStringList devlist; + m_deviceResource->getNetworkDeviceList(NetworkManager::Device::Type::Ethernet, devlist); + if (devlist.isEmpty()) { + return; + } + + foreach (auto deviceName, devlist) { + QList activedList; + QList deactivedList; + QVector vector; + m_activeResourse->getActiveConnectionList(deviceName,NetworkManager::ConnectionSettings::Wired,activedList); + if (!activedList.isEmpty()) { + vector.append(QStringList() << activedList.at(0)->m_connectName << activedList.at(0)->m_connectUuid << activedList.at(0)->m_connectPath); + } else { + vector.append(QStringList()<<("--")); + } + + m_connectResourse->getConnectionList(deviceName, NetworkManager::ConnectionSettings::Wired, deactivedList); //未激活列表的显示 + if (!deactivedList.isEmpty()) { + for (int i = 0; i < deactivedList.size(); i++) { + vector.append(QStringList()<m_connectName<m_connectUuid << deactivedList.at(i)->m_connectPath); + } + } + map.insert(deviceName, vector); + } + return; +} + +void LanPage::sendLanUpdateSignal(KyConnectItem *p_connectItem) +{ + QStringList info; + info << p_connectItem->m_connectName << p_connectItem->m_connectUuid << p_connectItem->m_connectPath; + emit lanUpdate(p_connectItem->m_ifaceName, info); + + return; +} + +void LanPage::sendLanAddSignal(KyConnectItem *p_connectItem) +{ + QStringList info; + info << p_connectItem->m_connectName << p_connectItem->m_connectUuid << p_connectItem->m_connectPath; + qDebug() << "[LanPage] emit lanAdd because addConnection "; + emit lanAdd(p_connectItem->m_ifaceName, info); + + return; +} + +void LanPage::sendLanStateChangeSignal(QString uuid, ConnectState state) +{ + if (state == Activating || state == Deactivating) { + if (m_activeResourse->connectionIsVirtual(uuid)) { + return; + } + } + + emit this->lanConnectChanged(state); + + return; +} + +void LanPage::updateConnectionProperty(KyConnectItem *p_connectItem) +{ + QString newUuid = p_connectItem->m_connectUuid; + + if (m_inactiveConnectionMap.contains(newUuid)) { + QListWidgetItem *p_listWidgetItem = m_inactiveConnectionMap.value(newUuid); + LanListItem *p_lanItem = (LanListItem*)m_inactivatedLanListWidget->itemWidget(p_listWidgetItem); + if (p_connectItem->m_ifaceName != "" + && m_currentDeviceName != p_connectItem->m_ifaceName) { + m_inactivatedLanListWidget->removeItemWidget(p_listWidgetItem); + + delete p_listWidgetItem; + p_listWidgetItem = nullptr; + + delete p_lanItem; + p_lanItem = nullptr; + + m_inactiveConnectionMap.remove(newUuid); + } else { + if (p_connectItem->m_connectName != p_lanItem->getConnectionName()){ + //只要名字改变就要删除,重新插入,主要是为了排序 + deleteConnectionMapItem(m_inactiveConnectionMap, m_inactivatedLanListWidget, newUuid); + QListWidgetItem *p_sortListWidgetItem = insertNewItem(p_connectItem, m_inactivatedLanListWidget); + if (m_inactiveConnectionMap.contains(newUuid)) { + qDebug()<m_connectPath != p_lanItem->getConnectionPath()) { + p_lanItem->updateConnectionPath(p_connectItem->m_connectPath); + } + } + + } else if (!m_activeConnectionMap.contains(newUuid)){ + if (p_connectItem->m_ifaceName == m_currentDeviceName + || p_connectItem->m_ifaceName.isEmpty()) { + QListWidgetItem *p_listWidgetItem = insertNewItem(p_connectItem, m_inactivatedLanListWidget); + if (m_inactiveConnectionMap.contains(newUuid)) { + qDebug()<m_connectUuid; + + if (m_activeConnectionMap.contains(newUuid)) { + QListWidgetItem *p_listWidgetItem = m_activeConnectionMap.value(newUuid); + LanListItem *p_lanItem = (LanListItem *)m_activatedLanListWidget->itemWidget(p_listWidgetItem); + if (m_currentDeviceName != p_connectItem->m_ifaceName) { + m_activeConnectionMap.remove(newUuid); + int takeRow = m_activatedLanListWidget->row(p_listWidgetItem); + m_activatedLanListWidget->takeItem(takeRow); + + delete p_lanItem; + p_lanItem = nullptr; + + p_lanItem = new LanListItem(); + m_activatedLanListWidget->addItem(p_listWidgetItem); + m_activatedLanListWidget->setItemWidget(p_listWidgetItem, p_lanItem); + m_activeConnectionMap.insert(EMPTY_CONNECT_UUID, p_listWidgetItem); + } else { + if (p_lanItem->getConnectionName() != p_connectItem->m_connectName) { + p_lanItem->updateConnectionName(p_connectItem->m_connectName); + } + + if (p_lanItem->getConnectionName() != p_connectItem->m_connectPath) { + p_lanItem->updateConnectionPath(p_connectItem->m_connectPath); + } + + } + } + + return; +} + +void LanPage::onUpdateConnection(QString uuid) +{ + if (!m_connectResourse->isWiredConnection(uuid)) { + return; + } + + qDebug() << "[LanPage]:Connection property Changed." << Q_FUNC_INFO << __LINE__; + + KyConnectItem *p_newItem = nullptr; + if (m_connectResourse->isActivatedConnection(uuid)) { + p_newItem = m_activeResourse->getActiveConnectionByUuid(uuid); + if (nullptr == p_newItem) { + qWarning()<<"[LanPage] get item failed, when update activate connection." + <<"connection uuid" << uuid; + return; + } + + updateActiveConnectionProperty(p_newItem); + } else { + p_newItem = m_connectResourse->getConnectionItemByUuid(uuid); + if (nullptr == p_newItem) { + qWarning()<<"[LanPage] get item failed, when update connection." + <<"connection uuid"<type() == QEvent::MouseButtonRelease) { + onShowControlCenter(); + } + } + + return QWidget::eventFilter(watched, event); +} + +void LanPage::activateWired(const QString& devName, const QString& connUuid) +{ + qDebug() << "[LanPage] activateWired" << devName << connUuid; + if (!m_deviceResource->wiredDeviceIsCarriered(devName)) { + qDebug() << LOG_FLAG << devName << "is not carried, so can not activate connection"; + this->showDesktopNotify(tr("Wired Device not carried")); + } else { + m_wiredConnectOperation->activateConnection(connUuid, devName); + } +} + +void LanPage::deactivateWired(const QString& devName, const QString& connUuid) +{ + qDebug() << "[LanPage] deactivateWired" << devName << connUuid; + QString name(""); + m_wiredConnectOperation->deactivateWiredConnection(name, connUuid); +} + +void LanPage::showDetailPage(QString devName, QString uuid) +{ + KyConnectItem *p_item = nullptr; + bool isActive = true; + + if (m_connectResourse->isActivatedConnection(uuid)) { + p_item = m_activeResourse->getActiveConnectionByUuid(uuid); + isActive = true; + } else { + p_item = m_connectResourse->getConnectionItemByUuid(uuid); + isActive = false; + } + + if (nullptr == p_item) { + qWarning()<<"[LanPage] GetConnectionItemByUuid is empty when showDetailPage." + <<"device name"<m_connectName, uuid, isActive, false, false, this); + netDetail->show(); + + delete p_item; + p_item = nullptr; +} + +bool LanPage::lanIsConnected() +{ + if (m_activeResourse->wiredConnectIsActived()) { + return true; + } else { + return false; + } +} + diff --git a/src/frontend/tab-pages/lanpage.h b/src/frontend/tab-pages/lanpage.h new file mode 100644 index 00000000..ee2d6988 --- /dev/null +++ b/src/frontend/tab-pages/lanpage.h @@ -0,0 +1,135 @@ +#ifndef LANPAGE_H +#define LANPAGE_H + +#include "divider.h" +#include "switchbutton.h" +#include +#include +#include +#include +#include +#include +#include + +#include "list-items/listitem.h" +#include "list-items/lanlistitem.h" +#include "tab-pages/tabpage.h" + +class LanListItem; + +class LanPage : public TabPage +{ + Q_OBJECT +public: + explicit LanPage(QWidget *parent = nullptr); + ~LanPage(); + + //for dbus + void getWiredList(QMap > &map); + void activateWired(const QString& devName, const QString& connUuid); + void deactivateWired(const QString& devName, const QString& connUuid); + void showDetailPage(QString devName, QString uuid); + void setWiredDeviceEnable(const QString& devName, bool enable); + + bool lanIsConnected(); + +protected: + bool eventFilter(QObject *watched, QEvent *event); + +private: + void initLanDevice(); + void initUI(); + void initLanArea(); + void initNetSwitch(); + void initLanDeviceState(); + + void initDeviceCombox(); + void updateDeviceCombox(QString oldDeviceName, QString newDeviceName); + void deleteDeviceFromCombox(QString deviceName); + void addDeviceForCombox(QString deviceName); + + QListWidgetItem *insertNewItem(KyConnectItem *itemData, QListWidget *listWidget); + QListWidgetItem *addNewItem(KyConnectItem *itemData, QListWidget *listWidget); + bool removeConnectionItem(QMap &connectMap, + QListWidget *lanListWidget, QString path); + + void getEnabledDevice(QStringList &enableDeviceList); + void getDisabledDevices(QStringList &disableDeviceList); + + void constructConnectionArea(); + void constructActiveConnectionArea(); + + void updateConnectionArea(KyConnectItem *p_newItem); + void updateActivatedConnectionArea(KyConnectItem *p_newItem); + void updateConnectionState(QMap &connectMap, + QListWidget *lanListWidget, QString uuid, ConnectState state); + QString getConnectionDevice(QString uuid); + + void updateActiveConnectionProperty(KyConnectItem *p_connectItem); + void updateConnectionProperty(KyConnectItem *p_connectItem); + + void sendLanUpdateSignal(KyConnectItem *p_connectItem); + void sendLanAddSignal(KyConnectItem *p_connectItem); + void sendLanStateChangeSignal(QString uuid, ConnectState state); + + void addEmptyConnectItem(QMap &connectMap, + QListWidget *lanListWidget); + void clearConnectionMap(QMap &connectMap, + QListWidget *lanListWidget); + void deleteConnectionMapItem(QMap &connectMap, + QListWidget *lanListWidget, QString uuid); + + void updateCurrentDevice(QString deviceName); + +signals: + void lanAdd(QString devName, QStringList info); + void lanRemove(QString dbusPath); + void lanUpdate(QString devName, QStringList info); + + void lanActiveConnectionStateChanged(QString interface, QString uuid, int status); + void lanConnectChanged(int state); + +private slots: + void onConnectionStateChange(QString uuid, NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason); + + void onAddConnection(QString uuid); + void onRemoveConnection(QString path); + void onUpdateConnection(QString uuid); + + void onSwithGsettingsChanged(const QString &key); + void onLanSwitchClicked(); + + void onDeviceAdd(QString deviceName, NetworkManager::Device::Type deviceType); + void onDeviceRemove(QString deviceName); + void onDeviceNameUpdate(QString oldName, QString newName); + + void onDeviceCarriered(QString deviceName, bool pluged); + void onDeviceActiveChanage(QString deviceName, bool deviceActive); + + void onDeviceComboxIndexChanged(int currentIndex); + + void onShowControlCenter(); + +private: + QListWidget * m_activatedLanListWidget = nullptr; + QListWidget * m_inactivatedLanListWidget = nullptr; + + KyNetworkDeviceResourse *m_deviceResource = nullptr; + KyWiredConnectOperation *m_wiredConnectOperation = nullptr; + KyActiveConnectResourse *m_activeResourse = nullptr; //激活的连接 + KyConnectResourse *m_connectResourse = nullptr; //未激活的连接 + + QMap m_inactiveConnectionMap; + QMap m_activeConnectionMap; + + QString m_currentDeviceName; + QStringList m_devList; + QStringList m_enableDeviceList; + + QGSettings *m_switchGsettings = nullptr; + + bool m_wiredSwitch = true; +}; + +#endif // LANPAGE_H diff --git a/src/frontend/tab-pages/tab-pages.pri b/src/frontend/tab-pages/tab-pages.pri new file mode 100644 index 00000000..6d049be2 --- /dev/null +++ b/src/frontend/tab-pages/tab-pages.pri @@ -0,0 +1,12 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/lanpage.h \ + $$PWD/wlanpage.h \ + $$PWD/tabpage.h + +SOURCES += \ + $$PWD/lanpage.cpp \ + $$PWD/wlanpage.cpp \ + $$PWD/tabpage.cpp + diff --git a/src/frontend/tab-pages/tabpage.cpp b/src/frontend/tab-pages/tabpage.cpp new file mode 100644 index 00000000..b1d73308 --- /dev/null +++ b/src/frontend/tab-pages/tabpage.cpp @@ -0,0 +1,307 @@ +#include "tabpage.h" +#include +#include +#include +#include + +TabPage::TabPage(QWidget *parent) : QWidget(parent) +{ + initUI(); + connect(qApp, &QApplication::paletteChanged, this, &TabPage::onPaletteChanged); +} + +TabPage::~TabPage() +{ + delete m_titleDivider; + delete m_activatedNetDivider; + delete m_inactivatedNetDivider; +} + +void TabPage::initUI() +{ + m_mainLayout = new QVBoxLayout(this); + m_mainLayout->setContentsMargins(MAIN_LAYOUT_MARGINS); + m_mainLayout->setSpacing(MAIN_LAYOUT_SPACING); + this->setLayout(m_mainLayout); + + m_titleFrame = new QFrame(this); + m_titleFrame->setFixedHeight(TITLE_FRAME_HEIGHT); + m_titleLayout = new QHBoxLayout(m_titleFrame); + m_titleLayout->setContentsMargins(TITLE_LAYOUT_MARGINS); + m_titleLabel = new QLabel(m_titleFrame); + m_netSwitch = new SwitchButton(m_titleFrame); + m_titleLayout->addWidget(m_titleLabel); + m_titleLayout->addStretch(); + m_titleLayout->addWidget(m_netSwitch); + m_titleDivider = new Divider(this); + + //临时增加的下拉框选择网卡区域 + m_deviceFrame = new QFrame(this); + m_deviceFrame->setFixedHeight(TITLE_FRAME_HEIGHT); + m_deviceLayout = new QHBoxLayout(m_deviceFrame); + m_deviceLayout->setContentsMargins(DEVICE_LAYOUT_MARGINS); + m_deviceFrame->setLayout(m_deviceLayout); + m_deviceLabel = new QLabel(m_deviceFrame); + m_deviceLabel->setText(tr("Current Device")); + m_deviceComboBox = new QComboBox(m_deviceFrame); + m_deviceComboBox->setFixedWidth(DEVICE_COMBOBOX_WIDTH); + + m_tipsLabel = new QLabel(m_deviceFrame); + m_tipsLabel->setText(tr("Devices Closed!")); + m_deviceLayout->addWidget(m_deviceLabel); + m_deviceLayout->addStretch(); + m_deviceLayout->addWidget(m_deviceComboBox); + m_deviceLayout->addWidget(m_tipsLabel); + connect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &TabPage::onDeviceComboxIndexChanged); + + m_activatedNetFrame = new QFrame(this); + m_activatedNetFrame->setMaximumHeight(ACTIVE_AREA_MAX_HEIGHT); + m_activatedNetLayout = new QVBoxLayout(m_activatedNetFrame); + m_activatedNetLayout->setContentsMargins(ACTIVE_NET_LAYOUT_MARGINS); +// m_activatedNetLayout->setSpacing(NET_LAYOUT_SPACING); + m_activatedNetLabel = new QLabel(m_activatedNetFrame); + m_activatedNetLabel->setContentsMargins(TEXT_MARGINS); + m_activatedNetLayout->addWidget(m_activatedNetLabel); + m_activatedNetDivider = new Divider(this); + + m_inactivatedNetFrame = new QFrame(this); + m_inactivatedNetFrame->setMinimumHeight(INACTIVE_AREA_MIN_HEIGHT); + m_inactivatedNetLayout = new QVBoxLayout(m_inactivatedNetFrame); + m_inactivatedNetLayout->setContentsMargins(NET_LAYOUT_MARGINS); +// m_inactivatedNetLayout->setSpacing(NET_LAYOUT_SPACING); + m_inactivatedNetFrame->setLayout(m_inactivatedNetLayout); + + m_inactivatedNetLabel = new QLabel(m_inactivatedNetFrame); + m_inactivatedNetLabel->setContentsMargins(TEXT_MARGINS); + + m_inactivatedNetListArea = new QScrollArea(m_inactivatedNetFrame); + m_inactivatedNetListArea->setFrameShape(QFrame::Shape::NoFrame); + m_inactivatedNetListArea->setWidgetResizable(true); + m_inactivatedNetListArea->setBackgroundRole(QPalette::Base); + m_inactivatedNetListArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_inactivatedNetListArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_inactivatedAreaLayout = new QVBoxLayout(m_inactivatedNetListArea); + m_inactivatedAreaLayout->setSpacing(MAIN_LAYOUT_SPACING); + m_inactivatedAreaLayout->setContentsMargins(MAIN_LAYOUT_MARGINS); + m_inactivatedNetListArea->setLayout(m_inactivatedAreaLayout); + + m_inactivatedNetLayout->addWidget(m_inactivatedNetLabel); + m_inactivatedNetLayout->addWidget(m_inactivatedNetListArea); + + m_inactivatedNetDivider = new Divider(this); + m_settingsFrame = new QFrame(this); + m_settingsFrame->setFixedHeight(TITLE_FRAME_HEIGHT); + + m_settingsLayout = new QHBoxLayout(m_settingsFrame); + m_settingsLayout->setContentsMargins(SETTINGS_LAYOUT_MARGINS); + m_settingsLabel = new KyLable(m_settingsFrame); + m_settingsLabel->setCursor(Qt::PointingHandCursor); + m_settingsLabel->setText(tr("Settings")); + m_settingsLabel->setScaledContents(true); + + m_settingsLayout->addWidget(m_settingsLabel); + m_settingsLayout->addStretch(); + m_settingsFrame->setLayout(m_settingsLayout); + + m_mainLayout->addWidget(m_titleFrame); + //临时增加的设备选择区域 + m_mainLayout->addWidget(m_deviceFrame); + m_mainLayout->addWidget(m_titleDivider); + m_mainLayout->addWidget(m_activatedNetFrame); + m_mainLayout->addWidget(m_activatedNetDivider); + m_mainLayout->addWidget(m_inactivatedNetFrame); + m_mainLayout->addStretch(); + m_mainLayout->addWidget(m_inactivatedNetDivider); + m_mainLayout->addWidget(m_settingsFrame); + + QPalette pal = m_inactivatedNetListArea->palette(); + pal.setBrush(QPalette::Base, QColor(0,0,0,0)); //背景透明 + m_inactivatedNetListArea->setPalette(pal); + + onPaletteChanged(); +} + +void TabPage::onPaletteChanged() +{ + QPalette labPal = m_activatedNetLabel->palette(); + QColor color = qApp->palette().color(QPalette::PlaceholderText); + labPal.setColor(QPalette::WindowText, color); + m_activatedNetLabel->setPalette(labPal); + m_inactivatedNetLabel->setPalette(labPal); + + if (m_deviceComboBox->view()) { + QPalette view_pal = m_deviceComboBox->view()->palette(); + QColor view_color = qApp->palette().color(QPalette::Active, QPalette::Button); + view_pal.setColor(QPalette::Base, view_color); + m_deviceComboBox->setPalette(view_pal); + m_deviceComboBox->view()->setPalette(view_pal); + } +} + +void TabPage::showDesktopNotify(const QString &message) +{ + QDBusInterface iface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::sessionBus()); + QList args; + args<<(tr("Kylin NM")) + <<((unsigned int) 0) + <beginGroup("DEFAULTCARD"); + defaultDevice = m_settings->value(key).toString(); + m_settings->endGroup(); + + delete m_settings; + m_settings = nullptr; + + return defaultDevice; +} + +void setDefaultDevice(KyDeviceType deviceType, QString deviceName) +{ + QString key; + switch (deviceType) { + case WIRED: + key = "wired"; + break; + case WIRELESS: + key = "wireless"; + break; + default: + return; + } + + QSettings * m_settings = new QSettings(CONFIG_FILE_PATH, QSettings::IniFormat); + m_settings->beginGroup("DEFAULTCARD"); + m_settings->setValue(key, deviceName); + m_settings->endGroup(); + m_settings->sync(); + + delete m_settings; + m_settings = nullptr; + + return; +} + +bool checkDeviceExist(KyDeviceType deviceType, QString deviceName) +{ + NetworkManager::Device::Type type; + switch (deviceType) { + case WIRED: + type = NetworkManager::Device::Type::Ethernet; + break; + case WIRELESS: + type = NetworkManager::Device::Type::Wifi; + break; + default: + return false; + break; + } + + KyNetworkDeviceResourse * kdr = new KyNetworkDeviceResourse(); + QStringList devList; + devList.clear(); + + kdr->getNetworkDeviceList(type, devList); + delete kdr; + return devList.contains(deviceName); +} + +void saveDeviceEnableState(QString deviceName, bool enable) +{ + QSettings * m_settings = new QSettings(CONFIG_FILE_PATH, QSettings::IniFormat); + m_settings->beginGroup("CARDEABLE"); + m_settings->setValue(deviceName, enable); + m_settings->endGroup(); + m_settings->sync(); + delete m_settings; + m_settings = nullptr; + return; +} + +void deleteDeviceEnableState(QString deviceName) +{ + QSettings * m_settings = new QSettings(CONFIG_FILE_PATH, QSettings::IniFormat); + m_settings->beginGroup("CARDEABLE"); + m_settings->remove(deviceName); + m_settings->endGroup(); + m_settings->sync(); + delete m_settings; + m_settings = nullptr; + return; +} + +void getDeviceEnableState(int type, QMap &map) +{ + map.clear(); + if (!QFile::exists(CONFIG_FILE_PATH)) { + qDebug() << "CONFIG_FILE_PATH not exist"; + return; + } + if (type != WIRED && type != WIRELESS) { + qDebug() << "getDeviceEnableState but wrong type"; + return; + } + + KyNetworkDeviceResourse * kdr = new KyNetworkDeviceResourse(); + QStringList wiredDevList,wirelessDevList; + wiredDevList.clear(); + wirelessDevList.clear(); + + QSettings * m_settings = new QSettings(CONFIG_FILE_PATH, QSettings::IniFormat); + m_settings->beginGroup("CARDEABLE"); + + if (type == WIRED) { + kdr->getNetworkDeviceList(NetworkManager::Device::Type::Ethernet, wiredDevList); + if (!wiredDevList.isEmpty()) { + for (int i = 0; i < wiredDevList.size(); ++i) { + if (!m_settings->contains(wiredDevList.at(i))) { + saveDeviceEnableState(wiredDevList.at(i),true); + } + bool enable = m_settings->value(wiredDevList.at(i), true).toBool(); + map.insert(wiredDevList.at(i), enable); + } + } + } else if (type == WIRELESS) { + kdr->getNetworkDeviceList(NetworkManager::Device::Type::Wifi, wirelessDevList); + if (!wirelessDevList.isEmpty()) { + for (int i = 0; i < wirelessDevList.size(); ++i) { + bool enable = m_settings->value(wirelessDevList.at(i), true).toBool(); + map.insert(wirelessDevList.at(i), enable); + } + } + } + + m_settings->endGroup(); + delete m_settings; + m_settings = nullptr; + delete kdr; + kdr = nullptr; + return; +} diff --git a/src/frontend/tab-pages/tabpage.h b/src/frontend/tab-pages/tabpage.h new file mode 100644 index 00000000..d8bd8712 --- /dev/null +++ b/src/frontend/tab-pages/tabpage.h @@ -0,0 +1,131 @@ +#ifndef TABPAGE_H +#define TABPAGE_H + +#include "divider.h" +#include "switchbutton.h" +#include "kylable.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kylinnetworkdeviceresource.h" + +#define MAIN_LAYOUT_MARGINS 0,0,0,0 +#define MAIN_LAYOUT_SPACING 0 +#define TITLE_FRAME_HEIGHT 50 //TabWidget的tab和widget有间隙,和设计稿看起来一致就不能设为设计稿里的高度 +#define TITLE_LAYOUT_MARGINS 24,0,24,0 +#define DEVICE_LAYOUT_MARGINS 24,0,24,8 +#define DEVICE_COMBOBOX_WIDTH 180 +#define ACTIVE_NET_LAYOUT_MARGINS 8,8,8,8 +#define NET_LAYOUT_MARGINS 8,8,0,3 +#define NET_LAYOUT_SPACING 8 +#define NET_LIST_SPACING 0 +#define TEXT_MARGINS 16,0,0,0 +//#define SCROLL_AREA_HEIGHT 200 +#define SETTINGS_LAYOUT_MARGINS 24,0,24,0 +#define TRANSPARENT_COLOR QColor(0,0,0,0) +#define INACTIVE_AREA_MIN_HEIGHT 170 +#define ACTIVE_AREA_MAX_HEIGHT 92 + +#define MAX_ITEMS 4 +#define MAX_WIDTH 400 +#define MIN_WIDTH 383 + +#define SCROLL_STEP 4 + +enum KyDeviceType +{ + WIRED, + WIRELESS +}; + +const QString CONFIG_FILE_PATH = QDir::homePath() + "/.config/ukui/kylin-nm.conf"; +bool checkDeviceExist(KyDeviceType deviceType, QString deviceName); +QString getDefaultDeviceName(KyDeviceType deviceType); +void setDefaultDevice(KyDeviceType deviceType, QString deviceName); +void saveDeviceEnableState(QString deviceName, bool enable); +void deleteDeviceEnableState(QString deviceName); +void getDeviceEnableState(int type, QMap &map); + +class TabPage : public QWidget +{ + Q_OBJECT +public: + explicit TabPage(QWidget *parent = nullptr); + ~TabPage(); + +// void updateDefaultDevice(QString &deviceName); +// QString getDefaultDevice(); + static void showDesktopNotify(const QString &message); + + void hideSetting() { + if (nullptr != m_settingsFrame) { + m_settingsFrame->hide(); + m_inactivatedNetDivider->hide(); + m_inactivatedNetFrame->setMinimumHeight(INACTIVE_AREA_MIN_HEIGHT + 100); + } + } + void showSetting() { + if (nullptr != m_settingsFrame) { + m_inactivatedNetFrame->setMinimumHeight(INACTIVE_AREA_MIN_HEIGHT); + m_settingsFrame->show(); + m_inactivatedNetDivider->show(); + } + } + +signals: + void deviceStatusChanged(); + void deviceNameChanged(QString oldName, QString newName, int type); + void activateFailed(QString errorMessage); + void deactivateFailed(QString errorMessage); + +protected: + void initUI(); +// virtual void initDevice() = 0;//初始化默认设备 + virtual void initDeviceCombox() = 0;//初始化设备选择下拉框 + QVBoxLayout * m_mainLayout = nullptr; + QFrame * m_titleFrame = nullptr; + QHBoxLayout * m_titleLayout = nullptr; + QLabel * m_titleLabel = nullptr; + SwitchButton * m_netSwitch = nullptr; + Divider * m_titleDivider = nullptr; + + QFrame * m_activatedNetFrame = nullptr; + QVBoxLayout * m_activatedNetLayout = nullptr; + QLabel * m_activatedNetLabel = nullptr; + Divider * m_activatedNetDivider = nullptr; + + QFrame * m_inactivatedNetFrame = nullptr; + QVBoxLayout * m_inactivatedNetLayout = nullptr; + QLabel * m_inactivatedNetLabel = nullptr; + QScrollArea * m_inactivatedNetListArea = nullptr; + QVBoxLayout * m_inactivatedAreaLayout = nullptr; + + Divider * m_inactivatedNetDivider = nullptr; + + QFrame * m_settingsFrame = nullptr; + QHBoxLayout * m_settingsLayout = nullptr; + KyLable * m_settingsLabel = nullptr; + + //临时增加的下拉框选择网卡区域 + QFrame * m_deviceFrame = nullptr; + QHBoxLayout * m_deviceLayout = nullptr; + QLabel * m_deviceLabel = nullptr; + QComboBox * m_deviceComboBox = nullptr; + QLabel * m_tipsLabel = nullptr; + + + +public slots: + virtual void onDeviceComboxIndexChanged(int currentIndex) = 0; + void onPaletteChanged(); + +}; + +#endif // TABPAGE_H diff --git a/src/frontend/tab-pages/wlanpage.cpp b/src/frontend/tab-pages/wlanpage.cpp new file mode 100644 index 00000000..935c78fb --- /dev/null +++ b/src/frontend/tab-pages/wlanpage.cpp @@ -0,0 +1,1453 @@ +#include "wlanpage.h" +#include "kywirelessnetitem.h" +#include +#include +#include +#include +#include + +#define AP_SCAN_INTERVAL (20*1000) +#define ICON_REFRESH_INTERVAL (5*1000) +#define EMPTY_SSID "emptyssid" +#define LOG_FLAG "[WlanPage]" +#define LAN_PAGE_INDEX 0 + +const QString NotApConnection = "0"; +const QString IsApConnection = "1"; + +WlanPage::WlanPage(QWidget *parent) : TabPage(parent) +{ + m_wirelessNetResource = new KyWirelessNetResource(this); + m_activatedConnectResource = new KyActiveConnectResourse(this); + m_netDeviceResource=new KyNetworkDeviceResourse(this); + m_connectResource = new KyConnectResourse(this); + m_wirelessConnectOpreation = new KyWirelessConnectOperation(this); + + initDevice(); + initWlanUI(); + initWlanSwitchState(); + initDeviceCombox(); + initWlanArea(); + + initTimer(); + + connect(m_wirelessNetResource, &KyWirelessNetResource::wifiNetworkAdd, this, &WlanPage::onWlanAdded); + connect(m_wirelessNetResource, &KyWirelessNetResource::wifiNetworkRemove, this, &WlanPage::onWlanRemoved); + + connect(m_wirelessNetResource, &KyWirelessNetResource::signalStrengthChange, this, &WlanPage::signalStrengthChange); + connect(m_wirelessNetResource, &KyWirelessNetResource::secuTypeChange, this, &WlanPage::onSecurityTypeChange); + + connect(m_wirelessNetResource, &KyWirelessNetResource::connectionAdd, this, &WlanPage::onConnectionAdd); + connect(m_wirelessNetResource, &KyWirelessNetResource::connectionRemove, this, &WlanPage::onConnectionRemove); + + connect(m_activatedConnectResource, &KyActiveConnectResourse::stateChangeReason, + this, &WlanPage::onConnectionStateChanged); + + connect(m_netDeviceResource, &KyNetworkDeviceResourse::deviceAdd, this, &WlanPage::onDeviceAdd); + connect(m_netDeviceResource, &KyNetworkDeviceResourse::deviceRemove, this, &WlanPage::onDeviceRemove); + connect(m_netDeviceResource, &KyNetworkDeviceResourse::deviceNameUpdate, this, &WlanPage::onDeviceNameUpdate); + + connect(m_wirelessConnectOpreation, &KyWirelessConnectOperation::activateConnectionError, this, &WlanPage::activateFailed); + connect(m_wirelessConnectOpreation, &KyWirelessConnectOperation::addAndActivateConnectionError, this, &WlanPage::activateFailed); + connect(m_wirelessConnectOpreation, &KyWirelessConnectOperation::deactivateConnectionError, this, &WlanPage::deactivateFailed); + + connect(m_wirelessConnectOpreation, &KyWirelessConnectOperation::wifiEnabledChanged, this, &WlanPage::onWifiEnabledChanged); + + connect(m_connectResource, &KyConnectResourse::connectivityChanged, this, &WlanPage::connectivityChanged); +} + +bool WlanPage::eventFilter(QObject *w, QEvent *e) +{ + if (e->type() == QEvent::MouseButtonRelease) { + if (w == m_settingsLabel) { + //ZJP_TODO 打开控制面板 + qDebug() << LOG_FLAG <<"recive event show control center"; + showControlCenter(); + } + } + + return QWidget::eventFilter(w,e); +} + +void WlanPage::initWlanUI() +{ + m_titleLabel->setText(tr("WLAN")); + + m_activatedNetLabel->setText(tr("Activated WLAN")); + m_activatedNetListWidget = new QListWidget(m_activatedNetFrame); + m_activatedNetListWidget->setFrameShape(QFrame::Shape::NoFrame); + m_activatedNetListWidget->setContentsMargins(MAIN_LAYOUT_MARGINS); + m_activatedNetListWidget->setSpacing(NET_LIST_SPACING); + m_activatedNetListWidget->setFixedHeight(NORMAL_HEIGHT); + m_activatedNetListWidget->setFixedWidth(MIN_WIDTH); + m_activatedNetListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_activatedNetLayout->addWidget(m_activatedNetListWidget); + + m_inactivatedNetLabel->setText(tr("Other WLAN")); + m_inactivatedNetListWidget = new QListWidget(m_inactivatedNetListArea); + m_inactivatedNetListWidget->setContentsMargins(MAIN_LAYOUT_MARGINS); + m_inactivatedNetListWidget->setSpacing(NET_LIST_SPACING); + m_inactivatedNetListWidget->setFrameShape(QFrame::Shape::NoFrame); + m_inactivatedNetListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_inactivatedNetListWidget->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel); + m_inactivatedNetListWidget->verticalScrollBar()->setProperty("drawScrollBarGroove",false); //去除滚动条的外侧黑框 + m_inactivatedNetListWidget->verticalScrollBar()->setSingleStep(SCROLL_STEP); + m_inactivatedNetListWidget->verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu); + addWlanMoreItem(); + m_inactivatedAreaLayout->addWidget(m_inactivatedNetListWidget); + + QPalette pal = m_activatedNetListWidget->palette(); + pal.setBrush(QPalette::Base, QColor(0,0,0,0)); //背景透明 + m_activatedNetListWidget->setPalette(pal); + m_inactivatedNetListWidget->setPalette(pal); + + m_settingsLabel->installEventFilter(this); +} + + +void WlanPage::onWlanSwithGsettingsChanged(const QString &key) +{ + if (key == WIRELESS_SWITCH) { + m_wlanSwitchEnable = m_switchGsettings->get(WIRELESS_SWITCH).toBool(); + qDebug() << LOG_FLAG << "wlan switch state" << m_wlanSwitchEnable; + m_netSwitch->setSwitchStatus(m_wlanSwitchEnable); + initDeviceCombox(); + initWlanArea(); + } + + return; +} + +/** + * @brief WlanPage::onWlanSwitchClicked 点击无线网开关 + */ +void WlanPage::onWlanSwitchClicked() +{ + if (m_devList.isEmpty()) { + showDesktopNotify(tr("No wireless network card detected")); + //检测不到无线网卡不再触发click信号 + m_netSwitch->setSwitchStatus(false); + m_netSwitch->setEnabled(false); + } else { + bool switchEnable = m_netSwitch->getSwitchStatus(); + if (m_wlanSwitchEnable != switchEnable) { + qDebug()<< LOG_FLAG << "wlan switch state " << switchEnable; + m_wirelessConnectOpreation->setWirelessEnabled(switchEnable); + if (!switchEnable) { + m_netSwitch->setEnabled(false); + m_activatedNetFrame->hide(); + m_activatedNetDivider->hide(); + m_inactivatedNetFrame->hide(); + m_deviceFrame->hide(); + } + } + } + + return; +} + +void WlanPage::initWlanSwitchState() +{ + m_wlanSwitchEnable = m_wirelessConnectOpreation->getWirelessEnabled(); + + if (QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA)) { + m_switchGsettings = new QGSettings(GSETTINGS_SCHEMA); + if (m_switchGsettings->keys().contains(WIRELESS_SWITCH)) { + if (m_devList.isEmpty()) { + m_wlanSwitchEnable = false; + m_netSwitch->setSwitchStatus(m_wlanSwitchEnable); + m_netSwitch->setEnabled(false); + } else { + m_wlanSwitchEnable = m_switchGsettings->get(WIRELESS_SWITCH).toBool(); + } + connect(m_switchGsettings, &QGSettings::changed, this, &WlanPage::onWlanSwithGsettingsChanged); + } + } + m_netSwitch->setSwitchStatus(m_wlanSwitchEnable); + + + connect(m_netSwitch, &SwitchButton::clicked, this, &WlanPage::onWlanSwitchClicked); + + return; +} + +void WlanPage::initTimer() +{ + m_scanTimer = new QTimer(this); + connect(m_scanTimer, &QTimer::timeout, this, &WlanPage::requestScan); + + m_refreshIconTimer = new QTimer(this); + connect(m_refreshIconTimer, &QTimer::timeout, this, &WlanPage::onRefreshIconTimer); + m_refreshIconTimer->start(ICON_REFRESH_INTERVAL); +} + +/** + * @brief WlanPage::initDevice 初始化默认网卡 + */ +void WlanPage::initDevice() +{ + m_devList.clear(); + m_netDeviceResource->getNetworkDeviceList(NetworkManager::Device::Type::Wifi, m_devList); + + m_currentDevice = getDefaultDeviceName(WIRELESS); + return; +} + +void WlanPage::initDeviceCombox() +{ + //TODO 获取设备列表,单设备时隐藏下拉框,多设备时添加到下拉框 + disconnect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &WlanPage::onDeviceComboxIndexChanged); + m_deviceComboBox->clear(); + + if (m_wlanSwitchEnable) { + if (0 == m_devList.count()) { + m_deviceFrame->show(); + m_tipsLabel->show(); + m_deviceComboBox->hide(); + m_currentDevice = ""; + setDefaultDevice(WIRELESS, m_currentDevice); + } else if (1 == m_devList.count()) { + m_deviceFrame->hide(); + if (m_currentDevice != m_devList.at(0)) { + m_currentDevice = m_devList.at(0); + setDefaultDevice(WIRELESS, m_currentDevice); + } + } else { + m_deviceFrame->show(); + m_deviceComboBox->show(); + m_tipsLabel->hide(); + for (int index = 0; index < m_devList.count(); ++index) { + m_deviceComboBox->addItem(m_devList.at(index)); + } + + if (m_devList.contains(m_currentDevice)) { + m_deviceComboBox->setCurrentText(m_currentDevice); + } else { + m_currentDevice = m_deviceComboBox->currentText(); + setDefaultDevice(WIRELESS, m_currentDevice); + } + } + } else { + m_deviceFrame->hide(); + m_currentDevice = ""; + setDefaultDevice(WIRELESS, m_currentDevice); + } + + connect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &WlanPage::onDeviceComboxIndexChanged, Qt::DirectConnection); + return; +} + +QListWidgetItem *WlanPage::addEmptyItem(QListWidget *wirelessListWidget) +{ + WlanListItem *p_wlanItem = new WlanListItem(); + QListWidgetItem *p_listWidgetItem = new QListWidgetItem(); + p_listWidgetItem->setFlags(p_listWidgetItem->flags() & (~Qt::ItemIsSelectable)); //设置不可被选中 + p_listWidgetItem->setSizeHint(QSize(wirelessListWidget->width(), p_wlanItem->height())); + wirelessListWidget->addItem(p_listWidgetItem); + wirelessListWidget->setItemWidget(p_listWidgetItem, p_wlanItem); + + return p_listWidgetItem; +} + +QListWidgetItem *WlanPage::addNewItem(KyWirelessNetItem &wirelessNetItem, + QListWidget *wirelessListWidget) +{ + WlanListItem *p_wlanItem = new WlanListItem( + wirelessNetItem, + m_currentDevice, + m_connectResource->isApConnection(wirelessNetItem.m_connectUuid)); + connect(p_wlanItem, &WlanListItem::itemHeightChanged, this, &WlanPage::onItemHeightChanged); + + QListWidgetItem *p_listWidgetItem = new QListWidgetItem(); + p_listWidgetItem->setFlags(p_listWidgetItem->flags() & (~Qt::ItemIsSelectable)); + p_listWidgetItem->setSizeHint(QSize(wirelessListWidget->width(), p_wlanItem->height())); + wirelessListWidget->addItem(p_listWidgetItem); + wirelessListWidget->setItemWidget(p_listWidgetItem, p_wlanItem); + + return p_listWidgetItem; +} + +QListWidgetItem *WlanPage::insertNewItem(KyWirelessNetItem &wirelessNetItem, + QListWidget *wirelessListWidget, + int row) +{ + WlanListItem *p_wlanItem = new WlanListItem(wirelessNetItem, m_currentDevice); + connect(p_wlanItem, &WlanListItem::itemHeightChanged, this, &WlanPage::onItemHeightChanged); + + QListWidgetItem *p_listWidgetItem = new QListWidgetItem(); + p_listWidgetItem->setFlags(p_listWidgetItem->flags() & (~Qt::ItemIsSelectable)); + p_listWidgetItem->setSizeHint(QSize(wirelessListWidget->width(), p_wlanItem->height())); + wirelessListWidget->insertItem(row, p_listWidgetItem); + wirelessListWidget->setItemWidget(p_listWidgetItem, p_wlanItem); + + return p_listWidgetItem; +} + +QListWidgetItem *WlanPage::insertNewItemWithSort(KyWirelessNetItem &wirelessNetItem, + QListWidget *p_ListWidget) +{ + int row = 0; + + // qDebug()<< "insertNewItemWithSort" << wirelessNetItem.m_NetSsid + // <<"sort item config" << wirelessNetItem.m_isConfigured + // << "signal strength" << wirelessNetItem.m_signalStrength; + WlanListItem *p_sortWlanItem = new WlanListItem(wirelessNetItem, m_currentDevice); + connect(p_sortWlanItem, &WlanListItem::itemHeightChanged, this, &WlanPage::onItemHeightChanged); + + QListWidgetItem *p_sortListWidgetItem = new QListWidgetItem(); + p_sortListWidgetItem->setFlags(p_sortListWidgetItem->flags() & (~Qt::ItemIsSelectable)); + p_sortListWidgetItem->setSizeHint(QSize(p_ListWidget->width(), p_sortWlanItem->height())); + + // qDebug() << "insertNewItemWithSort, count" << p_ListWidget->count(); + for (row = 0; row < p_ListWidget->count() - 1; ++row) { + QListWidgetItem *p_listWidgetItem = p_ListWidget->item(row); + WlanListItem *p_wlanItem = (WlanListItem *)p_ListWidget->itemWidget(p_listWidgetItem); + + if (WMI_OB_NAME == p_wlanItem->objectName()) { + qDebug() << "insertNewItemWithSort" << "p_wlanItem is WlanMoreItem"; + continue; + } + + // qDebug()<< "insertNewItemWithSort" << p_wlanItem->getSsid() + // <<"item config" << p_wlanItem->isConfigured() + // << "signal strength" << p_wlanItem->getSignalStrength(); + + if (wirelessNetItem.m_isConfigured == p_wlanItem->isConfigured()) { + if (wirelessNetItem.m_signalStrength > p_wlanItem->getSignalStrength()) { + break; + } + } else { + if (wirelessNetItem.m_isConfigured) { + break; + } + } + } + + p_ListWidget->insertItem(row, p_sortListWidgetItem); + p_ListWidget->setItemWidget(p_sortListWidgetItem, p_sortWlanItem); + + //qDebug()<< "insertNewItemWithSort" << "insert sort item finished. row" << row; + return p_sortListWidgetItem; +} + +void WlanPage::clearWirelessNetItemMap(QMap &wirelessNetItem, + QListWidget *wirelessListWidget) +{ + QMap::iterator iter; + + iter = wirelessNetItem.begin(); + while (iter != wirelessNetItem.end()) { + QListWidgetItem *p_listWidgetItem = iter.value(); + if (p_listWidgetItem == m_hiddenItem) { + continue; + } + + WlanListItem *p_wlanItem = (WlanListItem *)wirelessListWidget->itemWidget(p_listWidgetItem); + wirelessListWidget->removeItemWidget(p_listWidgetItem); + + delete p_wlanItem; + p_wlanItem = nullptr; + + delete p_listWidgetItem; + p_listWidgetItem = nullptr; + + iter = wirelessNetItem.erase(iter); + } + + return; +} + +void WlanPage::deleteWirelessItemFormMap(QMap &wirelessNetItemMap, + QListWidget *wirelessListWidget, QString ssid) +{ + QListWidgetItem *p_listWidgetItem = wirelessNetItemMap.value(ssid); + if (nullptr == p_listWidgetItem) { + qWarning()<< LOG_FLAG <<"wireless item is not exsit, it's ssid is " << ssid; + return; + } + + WlanListItem *p_wlanItem = (WlanListItem *)wirelessListWidget->itemWidget(p_listWidgetItem); + if (nullptr == p_wlanItem) { + qWarning() << LOG_FLAG << "p_wlanItem is null"; + return; + } + + wirelessNetItemMap.remove(ssid); + + wirelessListWidget->takeItem(wirelessListWidget->row(p_listWidgetItem)); + + delete p_wlanItem; + p_wlanItem = nullptr; + + delete p_listWidgetItem; + p_listWidgetItem = nullptr; + + if (m_inactivatedNetListWidget->count() < MAX_ITEMS) { + m_inactivatedNetListWidget->setFixedWidth(MIN_WIDTH); + } + return; +} + +void WlanPage::updateWlanItemState(QListWidget *p_wirelessListWidget, + QListWidgetItem *p_listWidgetItem, + ConnectState state) +{ + WlanListItem *p_wlanItem = nullptr; + p_wlanItem = (WlanListItem *)p_wirelessListWidget->itemWidget(p_listWidgetItem); + if (nullptr != p_wlanItem) { + p_wlanItem->updateConnectState(state); + } + + return; +} + +void WlanPage::constructActivateConnectionArea() +{ + int height = 0; + clearWirelessNetItemMap(m_activateConnectionItemMap, m_activatedNetListWidget); + + if (!m_currentDevice.isEmpty()) { + KyWirelessNetItem wirelessNetItem; + bool ret = m_wirelessNetResource->getActiveWirelessNetItem(m_currentDevice, wirelessNetItem); + if (ret == true) { + QListWidgetItem *p_listWidgetItem = addNewItem(wirelessNetItem, m_activatedNetListWidget); + m_activateConnectionItemMap.insert(wirelessNetItem.m_NetSsid, p_listWidgetItem); + updateWlanItemState(m_activatedNetListWidget, p_listWidgetItem, Activated); + + height += p_listWidgetItem->sizeHint().height(); + } + } + + if (height == 0) { + QListWidgetItem *p_listWidgetItem = addEmptyItem(m_activatedNetListWidget); + m_activateConnectionItemMap.insert(EMPTY_SSID, p_listWidgetItem); + + height += p_listWidgetItem->sizeHint().height(); + } + + m_activatedNetListWidget->setFixedHeight(height); + + return; +} + +void WlanPage::constructWirelessNetArea() +{ + qDebug() << "[WlanPage] Started loading wireless net list!" + << QDateTime::currentDateTime().toString("hh:mm:ss.zzzz"); + + clearWirelessNetItemMap(m_wirelessNetItemMap, m_inactivatedNetListWidget); + m_expandedItem = nullptr; + + QList wirelessNetItemList; + if (!m_wirelessNetResource->getDeviceWifiNetwork(m_currentDevice, wirelessNetItemList)) { + qWarning()<<"[WlanPage] get wireless net item list failed."; + return; + } + + QString activateSsid = m_activateConnectionItemMap.firstKey(); + + foreach (auto wirelessNetItem, wirelessNetItemList) { + if (wirelessNetItem.m_NetSsid == activateSsid) { + continue; + } + + QListWidgetItem *p_listWidgetItem = addNewItem(wirelessNetItem, m_inactivatedNetListWidget); + m_wirelessNetItemMap.insert(wirelessNetItem.m_NetSsid, p_listWidgetItem); + updateWlanItemState(m_inactivatedNetListWidget, p_listWidgetItem, Deactivated); + } + + addWlanMoreItem(); + + qDebug() << "[WlanPage] Stopped loading wireless net list! time=" + << QDateTime::currentDateTime().toString("hh:mm:ss.zzzz"); + if (m_inactivatedNetListWidget->count() < MAX_ITEMS) { + m_inactivatedNetListWidget->setFixedWidth(MIN_WIDTH); + } else { + m_inactivatedNetListWidget->setFixedWidth(MAX_WIDTH); + } + return; +} + +void WlanPage::initWlanArea() +{ + if (m_wlanSwitchEnable) { + m_activatedNetFrame->show(); + m_activatedNetDivider->show(); + constructActivateConnectionArea(); + + m_inactivatedNetFrame->show(); + constructWirelessNetArea(); + } else { + m_activatedNetFrame->hide(); + m_activatedNetDivider->hide(); + + m_inactivatedNetFrame->hide(); + } + + return; +} + +void WlanPage::onWlanAdded(QString interface, KyWirelessNetItem &item) +{ + //for dbus + QStringList info; + info << item.m_NetSsid + << QString::number(item.m_signalStrength) + << item.m_secuType + << (m_connectResource->isApConnection(item.m_connectUuid) ? IsApConnection : NotApConnection); + emit wlanAdd(interface, info); + + if (interface != m_currentDevice) { + qDebug() << "[WlanPage] wlan add interface not equal defaultdevice"; + return; + } + + if (m_wirelessNetItemMap.contains(item.m_NetSsid)) { + return; + } + + qDebug() << "[WlanPage] A Wlan Added! interface = " + << interface << "; ssid = " << item.m_NetSsid << Q_FUNC_INFO <<__LINE__; + + QListWidgetItem *p_listWidgetItem = insertNewItemWithSort(item, m_inactivatedNetListWidget); + m_wirelessNetItemMap.insert(item.m_NetSsid, p_listWidgetItem); + updateWlanItemState(m_inactivatedNetListWidget, p_listWidgetItem, Deactivated); + + addWlanMoreItem(); + if (m_inactivatedNetListWidget->count() >= MAX_ITEMS) { + m_inactivatedNetListWidget->setFixedWidth(MAX_WIDTH); + } + return; +} + +void WlanPage::onWlanRemoved(QString interface, QString ssid) +{ + emit wlanRemove(interface, ssid); + + if (interface != m_currentDevice) { + qDebug()<<"[WlanPage] the device is not current device," + <<"current device" << m_currentDevice + << "remove wlan device" << interface; + return; + } + + if (!m_wirelessNetItemMap.contains(ssid) && !m_activateConnectionItemMap.contains(ssid)) { + return; + } + + if (m_expandedItem == m_wirelessNetItemMap.value(ssid)) { + m_expandedItem = nullptr; + } + + qDebug() << "[WlanPage] A Wlan Removed! interface = " << interface + << "; ssid = " << ssid << Q_FUNC_INFO <<__LINE__; + + + if (m_wirelessNetItemMap.contains(ssid)) { + deleteWirelessItemFormMap(m_wirelessNetItemMap, + m_inactivatedNetListWidget, ssid); + } else { + deleteWirelessItemFormMap(m_activateConnectionItemMap, + m_activatedNetListWidget, ssid); + showDesktopNotify(tr("WLAN Disconnected Successfully")); + + QListWidgetItem *p_listWidgetItem = addEmptyItem(m_activatedNetListWidget); + m_activateConnectionItemMap.insert(EMPTY_SSID, p_listWidgetItem); + } + + return; +} + +void WlanPage::updateWlanListItem(QString ssid) +{ + KyWirelessNetItem wirelessNetItem; + + qDebug() << LOG_FLAG << "update wlan list item " << ssid; + + bool ret = m_wirelessNetResource->getWifiNetwork(m_currentDevice, ssid, wirelessNetItem); + if (ret) { + QListWidgetItem *p_listWidgetItem = m_wirelessNetItemMap.value(ssid); + if (p_listWidgetItem) { + WlanListItem *p_wlanItem = (WlanListItem *)m_inactivatedNetListWidget->itemWidget(p_listWidgetItem); + p_wlanItem->updateWirelessNetItem(wirelessNetItem); + p_wlanItem->updateConnectState(Deactivated); + emit this->wlanConnectChanged(Deactivated); + } + } + + return; +} + +void WlanPage::onConnectionAdd(QString deviceName, QString ssid) +{ + qDebug() << LOG_FLAG << "one connection is added, it's ssid " << ssid << "device name"<< deviceName; + + if (deviceName == m_currentDevice) { + updateWlanListItem(ssid); + } + + return; +} + +void WlanPage::onConnectionRemove(QString deviceName, QString ssid) +{ + qDebug() << LOG_FLAG << "one connection is removed, it's ssid " << ssid << "device name"<< deviceName; + if (deviceName == m_currentDevice) { + if (m_activateConnectionItemMap.contains(ssid)) { + updateWirelessNetArea(nullptr, ssid, deviceName); + } + updateWlanListItem(ssid); + } + + return; +} + +void WlanPage::onSecurityTypeChange(QString devName, QString ssid, QString secuType) +{ + QListWidgetItem *p_listWidgetItem = nullptr; + WlanListItem *p_wlanItem = nullptr; + + qDebug()<< LOG_FLAG << "security type is chenged"; + + if (m_wirelessNetItemMap.contains(ssid)) { + p_listWidgetItem = m_wirelessNetItemMap.value(ssid); + p_wlanItem = (WlanListItem*)m_inactivatedNetListWidget->itemWidget(p_listWidgetItem); + } else if (m_activateConnectionItemMap.contains(ssid)) { + p_listWidgetItem = m_activateConnectionItemMap.value(ssid); + p_wlanItem = (WlanListItem*)m_activatedNetListWidget->itemWidget(p_listWidgetItem); + } + + if (nullptr != p_wlanItem) { + p_wlanItem->updateWirelessNetSecurity(ssid, secuType); + } + + emit secuTypeChange(devName, ssid, secuType); + + return; +} + + +void WlanPage::addDeviceToCombox(QString deviceName) +{ + disconnect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &WlanPage::onDeviceComboxIndexChanged); + if (m_wlanSwitchEnable) { + if (m_currentDevice.isEmpty()){ + m_deviceFrame->hide(); + m_currentDevice = deviceName; + setDefaultDevice(WIRELESS, m_currentDevice); + } else if (m_deviceComboBox->count() == 0) { + m_deviceComboBox->addItem(m_currentDevice); + m_deviceComboBox->addItem(deviceName); + m_deviceFrame->show(); + m_deviceComboBox->show(); + m_tipsLabel->hide(); + } else { + m_deviceComboBox->addItem(deviceName); + } + } + + connect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &WlanPage::onDeviceComboxIndexChanged); + return; +} + +void WlanPage::onDeviceAdd(QString deviceName, NetworkManager::Device::Type deviceType) +{ + qDebug() << LOG_FLAG << "currentDevice" << m_currentDevice << "deviceAdd" << deviceName; + if (deviceType != NetworkManager::Device::Type::Wifi) { + return; + } + + if (m_devList.contains(deviceName)) { + return; + } + + if (m_devList.isEmpty()) { + m_wlanSwitchEnable = m_switchGsettings->get(WIRELESS_SWITCH).toBool(); + m_netSwitch->setEnabled(true); + m_netSwitch->setSwitchStatus(m_wlanSwitchEnable); + } + + m_devList << deviceName; + addDeviceToCombox(deviceName); + if (m_currentDevice == deviceName) { + initWlanArea(); + } + + emit deviceStatusChanged(); + + return; +} + +void WlanPage::deleteDeviceFromCombox(QString deviceName) +{ + disconnect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &WlanPage::onDeviceComboxIndexChanged); + + if (m_wlanSwitchEnable) { + if (0 == m_devList.count()) { + m_deviceFrame->hide(); + //m_tipsLabel->show(); + //m_deviceComboBox->hide(); + m_currentDevice = ""; + setDefaultDevice(WIRELESS, m_currentDevice); + } else if (1 == m_devList.count()) { + m_deviceFrame->hide(); + m_deviceComboBox->clear(); + m_currentDevice = m_devList.at(0); + setDefaultDevice(WIRELESS, m_currentDevice); + } else { + int index = m_deviceComboBox->findData(deviceName); + if (-1 != index) { + m_deviceComboBox->removeItem(index); + m_currentDevice = m_deviceComboBox->currentText(); + setDefaultDevice(WIRELESS, m_currentDevice); + } + } + } + + connect(m_deviceComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &WlanPage::onDeviceComboxIndexChanged); + return; +} + +void WlanPage::onDeviceRemove(QString deviceName) +{ + if (!m_devList.contains(deviceName)) { + return; + } + + qDebug() << "deviceRemove" << deviceName; + + QString originalDeviceName = m_currentDevice; + + m_devList.removeOne(deviceName); + deleteDeviceFromCombox(deviceName); + + if (m_devList.isEmpty()) { + m_wlanSwitchEnable = false; + m_netSwitch->setSwitchStatus(m_wlanSwitchEnable); + m_netSwitch->setEnabled(false); + } + + if (originalDeviceName == deviceName) { + initWlanArea(); + } + + emit deviceStatusChanged(); +} + +void WlanPage::updateDeviceForCombox(QString oldDeviceName, QString newDeviceName) +{ + if (m_currentDevice == oldDeviceName) { + m_currentDevice = newDeviceName; + setDefaultDevice(WIRELESS, m_currentDevice); + } + + int index = m_deviceComboBox->findText(oldDeviceName); + if (index != -1) { + m_deviceComboBox->setItemText(index, newDeviceName); + } + + return; +} + +void WlanPage::onDeviceNameUpdate(QString oldName, QString newName) +{ + if (!m_devList.contains(oldName)) { + return; + } + + qDebug()<<"[WlanPage] update device name, old name" + << oldName << "new Name" << newName; + + m_devList.removeOne(oldName); + m_devList.append(newName); + + QString originalDeviceName = m_currentDevice; + + updateDeviceForCombox(oldName, newName); + if (originalDeviceName != m_currentDevice) { + initWlanArea(); + } + + emit deviceNameChanged(oldName, newName, WIRELESS); +} + +void WlanPage::sendApStateChangeSignal(QString uuid, + QString ssid, + QString deviceName, + NetworkManager::ActiveConnection::State state) +{ + bool ret = false; + ret = m_connectResource->isApConnection(uuid); + if (!ret) { + return; + } + + if (state == NetworkManager::ActiveConnection::State::Deactivated) { + qDebug() << "[WlanPage] hotspot Deactivated"; + emit hotspotDeactivated(deviceName, ssid); + } else if (state == NetworkManager::ActiveConnection::State::Activated) { + qDebug() << "[WlanPage] hotspot activated"; + emit hotspotActivated(deviceName, ssid, uuid); + } + + return; +} + +void WlanPage::wlanShowNotify(QString ssid, NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason) +{ + if (state == NetworkManager::ActiveConnection::State::Deactivated) { + showDesktopNotify(tr("WLAN Disconnected Successfully")); + qDebug() << "[WlanPage] wlan disconnected successfully" << Q_FUNC_INFO <<__LINE__; + } else if (state == NetworkManager::ActiveConnection::State::Activated) { + qDebug() << "[WlanPage] wlan connected successfully" << Q_FUNC_INFO <<__LINE__; + this->showDesktopNotify(tr("WLAN Connected Successfully")); + } + + return; +} + +void WlanPage::updateActivatedArea(QString uuid, QString ssid, QString devName) +{ + if (m_activateConnectionItemMap.contains(ssid)) { + return; + } + + if (m_expandedItem == m_wirelessNetItemMap.value(ssid)) { + m_expandedItem = nullptr; + } + deleteWirelessItemFormMap(m_wirelessNetItemMap, + m_inactivatedNetListWidget, ssid); + + KyWirelessNetItem wirelessNetItem; + bool ret = m_wirelessNetResource->getWifiNetwork(devName, ssid, wirelessNetItem); + if (!ret) { + qWarning()<<"[WlanPage] get wireless item failed, when update activated connection area."; + return; + } + + deleteWirelessItemFormMap(m_activateConnectionItemMap, m_activatedNetListWidget, EMPTY_SSID); + QListWidgetItem *p_listWidgetItem = addNewItem(wirelessNetItem, m_activatedNetListWidget); + m_activateConnectionItemMap.insert(wirelessNetItem.m_NetSsid, p_listWidgetItem); + + m_activatedNetListWidget->setFixedHeight(p_listWidgetItem->sizeHint().height()); + if (m_inactivatedNetListWidget->count() < MAX_ITEMS) { + m_inactivatedNetListWidget->setFixedWidth(MIN_WIDTH); + } + return; +} + +void WlanPage::updateWirelessNetArea(QString uuid, QString ssid, QString devName) +{ + if(m_wirelessNetItemMap.contains(ssid)) { + return; + } + + if (m_activateConnectionItemMap.contains(ssid)) { + deleteWirelessItemFormMap(m_activateConnectionItemMap, m_activatedNetListWidget, ssid); + QListWidgetItem *p_activeListWidgetItem = addEmptyItem(m_activatedNetListWidget); + m_activateConnectionItemMap.insert(EMPTY_SSID, p_activeListWidgetItem); + m_activatedNetListWidget->setFixedHeight(p_activeListWidgetItem->sizeHint().height()); + } else { + qDebug() << LOG_FLAG << ssid << "is not in activeconnection map"; + } + + KyWirelessNetItem wirelessNetItem; + bool ret = m_wirelessNetResource->getWifiNetwork(devName, ssid, wirelessNetItem); + if (!ret) { + qWarning()<<"[WlanPage] get wireless item failed, when update wireless area."; + return; + } + + QListWidgetItem *p_listWidgetItem = insertNewItemWithSort(wirelessNetItem, m_inactivatedNetListWidget); + m_wirelessNetItemMap.insert(wirelessNetItem.m_NetSsid, p_listWidgetItem); + + // 更新‘更多’条目,以保证其处于listwidget的最底部 + addWlanMoreItem(); + if (m_inactivatedNetListWidget->count() < MAX_ITEMS) { + m_inactivatedNetListWidget->setFixedWidth(MIN_WIDTH); + } else { + m_inactivatedNetListWidget->setFixedWidth(MAX_WIDTH); + } + return; +} + +void WlanPage::onConnectionStateChanged(QString uuid, + NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason) +{ + QString devName, ssid; + m_wirelessNetResource->getSsidByUuid(uuid, ssid); + m_wirelessNetResource->getDeviceByUuid(uuid, devName); + + qDebug()<< LOG_FLAG << "emit wlanActiveConnectionStateChanged" << devName << ssid << state; + emit wlanActiveConnectionStateChanged(devName, ssid, uuid, state); + + if (ssid.isEmpty() || devName.isEmpty()) { + qDebug()<< LOG_FLAG << "ssid or devicename is empty" + << "devicename"<< devName <<"ssid"<isWirelessConnection(uuid)) { + qDebug()<< LOG_FLAG << "it is not wireless connection" << uuid; + return; + } + + emit this->wlanConnectChanged(state); + + if (m_connectResource->isApConnection(uuid)) { + sendApStateChangeSignal(uuid, ssid, devName, state); + } else { + if (state == NetworkManager::ActiveConnection::State::Deactivated && + !m_activateConnectionItemMap.contains(ssid)) { + qDebug() << "wlan remove before deactivated"; + } else { + wlanShowNotify(ssid, state, reason); + } + } + + if (devName != m_currentDevice) { + return; + } + + qDebug()<< "[WlanPage] wlan state changed, ssid = " << ssid + << "; state = " << state << "; reason = " << reason << Q_FUNC_INFO <<__LINE__; + if (state == NetworkManager::ActiveConnection::State::Activated) { + m_updateStrength = true; + updateActivatedArea(uuid, ssid, devName); + if (m_activateConnectionItemMap.contains(ssid)) { + QListWidgetItem *p_listWidgetItem = m_activateConnectionItemMap.value(ssid); + updateWlanItemState(m_activatedNetListWidget, p_listWidgetItem, Activated); + } + } else if (state == NetworkManager::ActiveConnection::State::Deactivated) { + m_updateStrength = true; + updateWirelessNetArea(uuid, ssid, devName); + if (m_wirelessNetItemMap.contains(ssid)) { + QListWidgetItem *p_listWidgetItem = m_wirelessNetItemMap.value(ssid); + updateWlanItemState(m_inactivatedNetListWidget, p_listWidgetItem, Deactivated); + } + } else if (state == NetworkManager::ActiveConnection::State::Deactivating){ + m_updateStrength = false; + if (m_activateConnectionItemMap.contains(ssid)) { + QListWidgetItem *p_listWidgetItem = m_activateConnectionItemMap.value(ssid); + updateWlanItemState(m_activatedNetListWidget, p_listWidgetItem, Deactivating); + } + } else if (state == NetworkManager::ActiveConnection::State::Activating) { + m_updateStrength = false; + if (m_wirelessNetItemMap.contains(ssid)) { + QListWidgetItem *p_listWidgetItem = m_wirelessNetItemMap.value(ssid); + updateWlanItemState(m_inactivatedNetListWidget, p_listWidgetItem, Activating); + } + } + return; +} + +void WlanPage::onItemHeightChanged(const bool isExpanded, const QString &ssid) +{ + if (!m_wirelessNetItemMap.contains(ssid)) { + return; + } + + QListWidgetItem *p_listWidgetItem = m_wirelessNetItemMap.value(ssid); + + qDebug()<sizeHint().height(); + int width = p_listWidgetItem->sizeHint().width(); + + if (isExpanded) { + if (m_expandedItem != p_listWidgetItem) { + qDebug()<setSizeHint(expandedSize); + } + m_inactivatedNetListWidget->scrollToItem(p_listWidgetItem, QAbstractItemView::QAbstractItemView::EnsureVisible); + } else { + m_expandedItem = nullptr; + + if (height > NORMAL_HEIGHT) { + qDebug()<setSizeHint(normalSize); + } + } + + return; +} + +void WlanPage::onDeviceComboxIndexChanged(int currentIndex) +{ + qDebug() << "onDeviceComboxIndexChanged"; + if (!m_deviceComboBox || currentIndex < 0) { + return; + } + + m_currentDevice = m_deviceComboBox->itemText(currentIndex); + setDefaultDevice(WIRELESS, m_currentDevice); + + initWlanArea(); + + return; +} + +//申请触发扫描,初始化执行&定时执行 +void WlanPage::requestScan() +{ + if (!m_wirelessConnectOpreation) { + qWarning() << "[WlanPage]Scan failed! m_wirelessConnectOpreation is nullptr!" << Q_FUNC_INFO << __LINE__; + return; + } + + m_wirelessConnectOpreation->requestWirelessScan(); + + return; +} + +void WlanPage::onHiddenWlanClicked() +{ + qDebug() << "[wlanPage] AddHideWifi Clicked! " << Q_FUNC_INFO << __LINE__ ; + NetDetail *netDetail = new NetDetail(m_currentDevice, "", "", false, true, true, this); + netDetail->show(); +} + +void WlanPage::showControlCenter() +{ + qDebug() << LOG_FLAG <<"show control center"; + + QProcess process; + process.startDetached("ukui-control-center -m wlanconnect"); + + return; +} + +void WlanPage::onWifiEnabledChanged(bool isWifiOn) +{ + //监听外部命令导致wifi状态变化,更新界面 + qDebug() << "[WlanPage] onWifiEnabledChanged wifi state" << isWifiOn; + + //应该先检测是否有无线网卡可用,才改变开关状态 + if (m_devList.isEmpty()) { + qDebug() << "[WLanPage] have no device to use " << Q_FUNC_INFO << __LINE__; + return; + } + + if (m_wlanSwitchEnable == isWifiOn) { + return; + } else { + if (!m_netSwitch->getEnabled()) { + m_netSwitch->setEnabled(true); + } + m_switchGsettings->set(WIRELESS_SWITCH, isWifiOn); + } + + return; +} + +void WlanPage::refreshActiveConnectionIcon(QString ssid, const int &signal) +{ + QListWidgetItem *p_listWidgetItem = m_activateConnectionItemMap.value(ssid); + if (p_listWidgetItem) { + WlanListItem *p_wlanItem = (WlanListItem *)m_activatedNetListWidget->itemWidget(p_listWidgetItem); + if (nullptr != p_wlanItem) { + p_wlanItem->setSignalStrength(signal); + return; + } + } +} + +void WlanPage::onRefreshIconTimer() +{ + if (!m_updateStrength) { + return; + } + + emit timeToUpdate(); + + if(!this->isVisible()) { + return; + } + //qDebug() << "onRefreshIconTimer"; + + if (m_expandedItem) { + qDebug()<< LOG_FLAG << "Has expanded item and forbid refresh wifi strength" << Q_FUNC_INFO << __LINE__; + return; + } + + QList wlanList; + if (!m_wirelessNetResource->getDeviceWifiNetwork(m_currentDevice, wlanList)) { + return; + } + + + QString activateSsid = m_activateConnectionItemMap.firstKey(); + int sortRow = 0; + for (int index = 0; index < wlanList.length(); index++) { + KyWirelessNetItem sortItem = wlanList.at(index); + QString sortSsid = sortItem.m_NetSsid; //应该在第currentRow行的新的WiFi名称 + //qDebug()<< LOG_FLAG << "sort ssid"<< sortSsid << "active ssid" << activateSsid << "sort row"<< sortRow; + if (sortSsid == activateSsid) { //排除已连接WiFi + refreshActiveConnectionIcon(activateSsid, sortItem.m_signalStrength); + continue; + } + + QListWidgetItem *p_listWidgetItem = m_inactivatedNetListWidget->item(sortRow); + if (p_listWidgetItem) { + WlanListItem *p_wlanItem = (WlanListItem *)m_inactivatedNetListWidget->itemWidget(p_listWidgetItem); + + if (nullptr == p_wlanItem) { + qDebug() << LOG_FLAG << "p_wlanItem is null continue"; + continue;//暂时先保持continue,后续讨论是否使用break直接跳出循环 + } + + // 该item是‘更多’条目,不需要更新 + if (WMI_OB_NAME == p_wlanItem->objectName()) { + qDebug() << LOG_FLAG << "p_wlanItem is WlanMoreItem"; + continue; + } + + //qDebug()<< LOG_FLAG << "row" << sortRow << "item ssid" << p_wlanItem->getSsid(); + if (sortSsid == p_wlanItem->getSsid()) { + // qDebug()<< LOG_FLAG << "sort wlan set signal strength." << Q_FUNC_INFO << __LINE__; + p_wlanItem->setSignalStrength(sortItem.m_signalStrength); + sortRow++; + continue; + } + + QListWidgetItem *p_sortListWidgetItem = m_wirelessNetItemMap.value(sortSsid); + if (p_sortListWidgetItem) { + WlanListItem *p_wlanItem = (WlanListItem *)m_inactivatedNetListWidget->itemWidget(p_listWidgetItem); + if (p_wlanItem == nullptr) { + continue; + } + + if (Deactivated != p_wlanItem->getConnectionState()) { + continue; + } + // qDebug()<< LOG_FLAG << "sort wlan update position." << sortSsid << Q_FUNC_INFO << __LINE__; + int takeRow = m_inactivatedNetListWidget->row(p_sortListWidgetItem); + m_inactivatedNetListWidget->takeItem(takeRow); + + WlanListItem *p_sortWlanItem = new WlanListItem(sortItem, m_currentDevice); + connect(p_sortWlanItem, &WlanListItem::itemHeightChanged, this, &WlanPage::onItemHeightChanged); + m_inactivatedNetListWidget->insertItem(sortRow, p_sortListWidgetItem); + m_inactivatedNetListWidget->setItemWidget(p_sortListWidgetItem, p_sortWlanItem); + updateWlanItemState(m_inactivatedNetListWidget, p_sortListWidgetItem, Deactivated); + sortRow++; + continue; + } + } + + //qDebug()<< LOG_FLAG << "sort wlan add new item." << Q_FUNC_INFO << __LINE__; + QListWidgetItem *p_newListWidgetItem = insertNewItem(sortItem, m_inactivatedNetListWidget, sortRow); + m_wirelessNetItemMap.insert(sortItem.m_NetSsid, p_newListWidgetItem); + updateWlanItemState(m_inactivatedNetListWidget, p_listWidgetItem, Deactivated); + + sortRow++; + } + + // qDebug()<< LOG_FLAG << "sort wlan finished." << Q_FUNC_INFO << __LINE__; + + return; +} + +//for dbus +void WlanPage::getWirelessList(QMap > &map) +{ + QMap actMap; + m_wirelessNetResource->getWirelessActiveConnection(NetworkManager::ActiveConnection::State::Activated, actMap); + + QMap > wlanMap; + if (!m_wirelessNetResource->getAllDeviceWifiNetwork(wlanMap)) { + return; + } + + QMap >::iterator iter = wlanMap.begin(); + while (iter != wlanMap.end()) { + QVector vector; + QString activeSsid ; + //先是已连接 + if (actMap.contains(iter.key())) { + qDebug() << "find " <getSsidByUuid(actMap[iter.key()].at(0), ssid); + if (m_wirelessNetResource->getWifiNetwork(iter.key(), ssid, data)) { + vector.append(QStringList() << data.m_NetSsid + << QString::number(data.m_signalStrength) + << data.m_secuType + << data.m_connectUuid + << (m_connectResource->isApConnection(data.m_connectUuid) ? IsApConnection : NotApConnection)); + activeSsid = data.m_NetSsid; + } else { + vector.append(QStringList("--")); + } + } else { + vector.append(QStringList("--")); + } + //未连接 + foreach (auto itemData, iter.value()) { + if (itemData.m_NetSsid == activeSsid) { + continue; + } + vector.append(QStringList()<isApConnection(itemData.m_connectUuid) ? IsApConnection : NotApConnection)); + } + + map.insert(iter.key(), vector); + iter++; + } + + return; +} + +//开启热点 +void WlanPage::activeWirelessAp(const QString apName, const QString apPassword, const QString wirelessBand, const QString apDevice) +{ + QString uuid(""); + QList apConnectItemList; + m_connectResource->getApConnections(apConnectItemList); + if (!apConnectItemList.isEmpty()) { + uuid = apConnectItemList.at(0)->m_connectUuid; + } + + m_wirelessConnectOpreation->activeWirelessAp(uuid, apName, apPassword, apDevice, wirelessBand); +} + +//断开热点 +void WlanPage::deactiveWirelessAp(const QString apName, const QString uuid) +{ + if (!uuid.isEmpty()) { + m_wirelessConnectOpreation->deactiveWirelessAp(apName, uuid); + } +} + +void WlanPage::getStoredApInfo(QStringList &list) +{ + list.clear(); + QList apConnectItemList; + m_connectResource->getApConnections(apConnectItemList); + if (!apConnectItemList.isEmpty()) { + list << apConnectItemList.at(0)->m_connectSsid; + list << apConnectItemList.at(0)->m_password; + list << apConnectItemList.at(0)->m_ifaceName; + list << (apConnectItemList.at(0)->m_isActivated? "true":"false"); + list << apConnectItemList.at(0)->m_connectUuid; + list << apConnectItemList.at(0)->m_band; + } +} + +void WlanPage::getApInfoBySsid(QString devName, QString ssid, QStringList &list) +{ + list.clear(); + QList apConnectItemList; + m_connectResource->getApConnections(apConnectItemList); + for (int i = 0; i < apConnectItemList.size(); i++) { + if (apConnectItemList.at(i)->m_connectSsid == ssid + && apConnectItemList.at(i)->m_ifaceName == devName) { + list << apConnectItemList.at(i)->m_password; + list << apConnectItemList.at(i)->m_band; + } + } + + return; +} + +void WlanPage::activateWirelessConnection(const QString& devName, const QString& ssid) +{ + KyWirelessNetItem wirelessNetItem; + if (!m_wirelessNetResource->getWifiNetwork(devName, ssid, wirelessNetItem)) { + qDebug() << "[WlanPage] no such wifi " << ssid << " in " << devName; + return; + } + + if (devName != m_currentDevice) { + int index = m_deviceComboBox->findText(devName); + if (index >= 0) { + m_deviceComboBox->setCurrentIndex(index); + } else { + qDebug() << "[WlanPage]activateWirelessConnection no such " << devName; + return; + } + } + + QListWidgetItem *p_listWidgetItem = nullptr; + WlanListItem *p_wlanItem = nullptr; + + if (m_wirelessNetItemMap.contains(ssid)) { + p_listWidgetItem = m_wirelessNetItemMap.value(ssid); + p_wlanItem = (WlanListItem*)m_inactivatedNetListWidget->itemWidget(p_listWidgetItem); + + m_inactivatedNetListWidget->scrollToItem(p_listWidgetItem, QAbstractItemView::EnsureVisible); + + + QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, QPoint(0,0), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QApplication::postEvent(p_wlanItem, event); + emit showMainWindow(WLAN_PAGE_INDEX); + } else { + qDebug() << "[WlanPage]activateWirelessConnection no such " << ssid << "in" << devName; + } + return; +} + +void WlanPage::deactivateWirelessConnection(const QString& devName, const QString& ssid) +{ + KyWirelessNetItem wirelessNetItem; + if (!m_wirelessNetResource->getWifiNetwork(devName, ssid, wirelessNetItem)) { + qDebug() << "[WlanPage] no such wifi " << ssid << " in " << devName; + return; + } + + if (!m_connectResource->isActivatedConnection(wirelessNetItem.m_connectUuid)) { + qDebug()<<"[WlanPage]"<deActivateWirelessConnection(wirelessNetItem.m_connName, wirelessNetItem.m_connectUuid); + + return; +} + +void WlanPage::onMainWindowVisibleChanged(const bool &visible) +{ + qDebug() << "[WlanPage] Received signal of mainwindow visible changed. cur_state = " << visible << Q_FUNC_INFO << __LINE__; + if (visible) { + //打开页面时先触发一次扫描,然后定时扫描wifi热点和刷新icon + requestScan(); + m_scanTimer->start(AP_SCAN_INTERVAL); + onRefreshIconTimer(); +// m_refreshIconTimer->start(ICON_REFRESH_INTERVAL); + } else { + //界面关闭的时候,停止wifi扫描和刷新 + m_scanTimer->stop(); +// m_refreshIconTimer->stop(); + qDebug() << "wlanpage not visible"; + showNonePwd(); + } + + return; +} + +void WlanPage::onWlanPageVisibleChanged(int index) +{ + if (index == LAN_PAGE_INDEX) { + qDebug() << "wlanpage not visible"; + showNonePwd(); + } +} + +void WlanPage::showNonePwd() +{ + QMap::iterator iter; + for(iter=m_wirelessNetItemMap.begin();iter!= m_wirelessNetItemMap.end();iter++) + { + if(iter.value() != nullptr) { + QListWidgetItem *p_listWidgetItem = iter.value(); + WlanListItem *p_wlanItem = (WlanListItem*)m_inactivatedNetListWidget->itemWidget(p_listWidgetItem); + p_wlanItem->forgetPwd(); + p_wlanItem->setExpanded(false); + } + } + return; +} + +void WlanPage::showDetailPage(QString devName, QString ssid) +{ + KyWirelessNetItem wirelessNetItem; + if (!m_wirelessNetResource->getWifiNetwork(devName, ssid, wirelessNetItem)) { + qDebug()<<"[WlanPage] " << ssid << " is missing when showDetailPage"; + return; + } + + bool isActive = m_connectResource->isActivatedConnection(wirelessNetItem.m_connectUuid); + + NetDetail *netDetail = new NetDetail(devName, ssid, wirelessNetItem.m_connectUuid, isActive, true, !wirelessNetItem.m_isConfigured, this); + netDetail->show(); + + return; +} + +bool WlanPage::checkWlanStatus(NetworkManager::ActiveConnection::State state) +{ + if (m_activatedConnectResource->checkWirelessStatus(state)) { + return true; + } else { + return false; + } +} + +void WlanPage::setWirelessSwitchEnable(bool enable) +{ + qDebug() << "dbus setWirelessSwitchEnable = " << enable << __LINE__; + //应该先检测是否有无线网卡可用,才改变开关状态 + if (m_devList.isEmpty()) { + qDebug() << "have no device to use " << Q_FUNC_INFO << __LINE__; + //检测不到无线网卡不再触发click信号 + m_netSwitch->setSwitchStatus(false); + //m_netSwitch->setEnabled(false); + }else{ + m_wirelessConnectOpreation->setWirelessEnabled(enable); + if (!enable) { + m_netSwitch->setEnabled(false); + m_activatedNetFrame->hide(); + m_activatedNetDivider->hide(); + m_inactivatedNetFrame->hide(); + m_deviceFrame->hide(); + } + } +} + +void WlanPage::getWirelessDeviceCap(QMap &map) +{ + for (int i = 0; i < m_devList.size(); ++i) { + QString devName = m_devList.at(i); + map.insert(devName, m_netDeviceResource->getWirelessDeviceCapability(devName)); + } +} + +void WlanPage::getConnectivity(NetworkManager::Connectivity &connectivity) +{ + m_connectResource->getConnectivity(connectivity); +} + +/** + * @brief WlanPage::addWlanMoreItem + * 添加‘更多网络’的条目 + */ +void WlanPage::addWlanMoreItem() +{ + if (m_hiddenItem) { + m_inactivatedNetListWidget->takeItem(m_inactivatedNetListWidget->row(m_hiddenItem)); + delete m_hiddenItem; + m_hiddenItem = nullptr; + } + + if (m_hiddenWlanWidget) { +// disconnect(m_hiddenWlanWidget, &WlanMoreItem::hiddenWlanClicked, this, &WlanPage::onHiddenWlanClicked); + m_hiddenWlanWidget->setParent(nullptr); + delete m_hiddenWlanWidget; + m_hiddenWlanWidget = nullptr; + } + + m_hiddenWlanWidget = new WlanMoreItem(this); + connect(m_hiddenWlanWidget, &WlanMoreItem::hiddenWlanClicked, this, &WlanPage::onHiddenWlanClicked); + m_hiddenWlanWidget->setFixedHeight(NORMAL_HEIGHT); + + m_hiddenItem = new QListWidgetItem(m_inactivatedNetListWidget); + m_hiddenItem->setFlags(m_hiddenItem->flags() & (~Qt::ItemIsSelectable)); + m_hiddenItem->setSizeHint(QSize(m_inactivatedNetListWidget->width(), m_hiddenWlanWidget->height())); + m_inactivatedNetListWidget->addItem(m_hiddenItem); + m_inactivatedNetListWidget->setItemWidget(m_hiddenItem, m_hiddenWlanWidget); + return; +} diff --git a/src/frontend/tab-pages/wlanpage.h b/src/frontend/tab-pages/wlanpage.h new file mode 100644 index 00000000..270af216 --- /dev/null +++ b/src/frontend/tab-pages/wlanpage.h @@ -0,0 +1,186 @@ +#ifndef WLANPAGE_H +#define WLANPAGE_H + +#include "tabpage.h" +#include "kywirelessnetresource.h" +#include "kylinactiveconnectresource.h" +#include "kylinnetworkdeviceresource.h" +#include "kywirelessconnectoperation.h" +#include "wlanlistitem.h" +#include "wlanmoreitem.h" +#include "kylinconnectoperation.h" +#include +#include "netdetails/netdetail.h" +#include +#include "kylinactiveconnectresource.h" +#include "kywirelessnetresource.h" + +//#define SCROLLAREA_HEIGHT 150 +#define MORE_TEXT_MARGINS 16,0,0,0 +#define SCROLLAREA_HEIGHT 200 + +#define LAN_PAGE_INDEX 0 +#define WLAN_PAGE_INDEX 1 + +class WlanListItem; + +class WlanPage : public TabPage +{ + Q_OBJECT +public: + explicit WlanPage(QWidget *parent = nullptr); + ~WlanPage() = default; + + //for dbus + void getWirelessList(QMap > &map); + //开启热点 + void activeWirelessAp(const QString apName, const QString apPassword, const QString wirelessBand, const QString apDevice); + //断开热点 + void deactiveWirelessAp(const QString apName, const QString uuid); + //获取热点 + void getStoredApInfo(QStringList &list); + + void activateWirelessConnection(const QString& devName, const QString& ssid); + void deactivateWirelessConnection(const QString& devName, const QString& ssid); + + void showDetailPage(QString devName, QString uuid); + + bool checkWlanStatus(NetworkManager::ActiveConnection::State state); + + void getApInfoBySsid(QString devName, QString ssid, QStringList &list); + //无线总开关 + void setWirelessSwitchEnable(bool enable); + void getWirelessDeviceCap(QMap &map); + + void getConnectivity(NetworkManager::Connectivity &connectivity); + +signals: + void oneItemExpanded(const QString &ssid); + void wlanAdd(QString devName, QStringList info); + void wlanRemove(QString devName,QString ssid); + void wlanActiveConnectionStateChanged(QString interface, QString ssid, QString uuid, int status); + void hotspotDeactivated(QString devName, QString ssid); + void hotspotActivated(QString devName, QString ssid, QString uuid); + void signalStrengthChange(QString devName, QString ssid, int strength); + void secuTypeChange(QString devName, QString ssid, QString secuType); + void hiddenWlanClicked(); + void wlanConnectChanged(int state); + void timeToUpdate(); + + void showMainWindow(int type); + + void connectivityChanged(NetworkManager::Connectivity connectivity); + +public slots: + void onMainWindowVisibleChanged(const bool &visible); + void onSecurityTypeChange(QString devName, QString ssid, QString secuType); + void requestScan(); + void onWlanPageVisibleChanged(int index); + +private slots: + void onWlanAdded(QString interface, KyWirelessNetItem &item); + void onWlanRemoved(QString interface, QString ssid); + + void onConnectionAdd(QString deviceName, QString ssid); + void onConnectionRemove(QString deviceName, QString ssid); + + void onDeviceAdd(QString deviceName, NetworkManager::Device::Type deviceType); + void onDeviceRemove(QString deviceName); + void onDeviceNameUpdate(QString oldName, QString newName); + + void onConnectionStateChanged(QString uuid, + NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason); + void onItemHeightChanged(const bool isExpanded, const QString &ssid); + + void onWlanSwitchClicked(); + void onWlanSwithGsettingsChanged(const QString &key); + + void onDeviceComboxIndexChanged(int currentIndex); + void onHiddenWlanClicked(); + void showControlCenter(); + void onWifiEnabledChanged(bool isWifiOn); + void onRefreshIconTimer(); + +protected: + bool eventFilter(QObject *watched, QEvent *event); + +private: + //定时触发扫描的定时器 + void initTimer(); + void initWlanUI(); + void initConnections(); + void initDevice();//初始化默认设备 + void initDeviceCombox(); + void initWlanSwitchState(); + void initWlanArea(); + void addWlanMoreItem(); + + void showNonePwd(); + + QListWidgetItem *addEmptyItem(QListWidget *wirelessListWidget); + QListWidgetItem *addNewItem(KyWirelessNetItem &wirelessNetItem, + QListWidget *wirelessListWidget); + QListWidgetItem *insertNewItem(KyWirelessNetItem &wirelessNetItem, + QListWidget *wirelessListWidget, + int row); + QListWidgetItem *insertNewItemWithSort(KyWirelessNetItem &wirelessNetItem, + QListWidget *p_ListWidget); + + void clearWirelessNetItemMap(QMap &wirelessNetItem, + QListWidget *wirelessListWidget); + void deleteWirelessItemFormMap(QMap &wirelessNetItemMap, + QListWidget *wirelessListWidget, QString ssid); + + void updateWlanItemState(QListWidget *p_wirelessListWidget, + QListWidgetItem *p_listWidgetItem, + ConnectState state); + void updateWlanListItem(QString ssid); + void refreshActiveConnectionIcon(QString ssid, const int &signal); + + void constructWirelessNetArea(); + void constructActivateConnectionArea(); + + void updateActivatedArea(QString uuid, QString ssid, QString devName); + void updateWirelessNetArea(QString uuid, QString ssid, QString devName); + + void addDeviceToCombox(QString deviceName); + void deleteDeviceFromCombox(QString deviceName); + void updateDeviceForCombox(QString oldDeviceName, QString newDeviceName); + + void sendApStateChangeSignal(QString uuid, QString ssid, QString deviceName, + NetworkManager::ActiveConnection::State state); + void wlanShowNotify(QString ssid, NetworkManager::ActiveConnection::State state, + NetworkManager::ActiveConnection::Reason reason); + +private: + QMap m_wirelessNetItemMap; + QMap m_activateConnectionItemMap; + + QListWidgetItem *m_expandedItem = nullptr; + + WlanMoreItem * m_hiddenWlanWidget = nullptr; + QListWidgetItem *m_hiddenItem = nullptr; + + QListWidget * m_activatedNetListWidget = nullptr; + QListWidget * m_inactivatedNetListWidget = nullptr; + + QStringList m_devList; + QString m_currentDevice; + + KyWirelessNetResource *m_wirelessNetResource = nullptr; + KyActiveConnectResourse *m_activatedConnectResource = nullptr; + KyNetworkDeviceResourse *m_netDeviceResource = nullptr; + KyWirelessConnectOperation * m_wirelessConnectOpreation = nullptr; + KyConnectResourse * m_connectResource = nullptr; + + QGSettings *m_switchGsettings = nullptr; + bool m_wlanSwitchEnable = true; + + bool m_updateStrength = true; + + QTimer *m_scanTimer = nullptr; + QTimer *m_refreshIconTimer = nullptr; +}; + +#endif // WLANPAGE_H diff --git a/src/frontend/tools/divider.cpp b/src/frontend/tools/divider.cpp new file mode 100644 index 00000000..f82fba9a --- /dev/null +++ b/src/frontend/tools/divider.cpp @@ -0,0 +1,23 @@ +#include "divider.h" +#include +#include + +Divider::Divider(QWidget * parent) : QFrame(parent) +{ + this->setFixedHeight(1); +} + + + +void Divider::paintEvent(QPaintEvent * e) +{ + QPainter p(this); + QColor color = qApp->palette().color(QPalette::BrightText); + color.setAlphaF(0.08); + p.save(); + p.setBrush(color); + p.setPen(Qt::transparent); + p.drawRoundedRect(this->rect(), 6, 6); + p.restore(); + return QFrame::paintEvent(e); +} diff --git a/src/frontend/tools/divider.h b/src/frontend/tools/divider.h new file mode 100644 index 00000000..e22a6392 --- /dev/null +++ b/src/frontend/tools/divider.h @@ -0,0 +1,15 @@ +#ifndef DIVIDER_H +#define DIVIDER_H +#include + +class Divider : public QFrame +{ +public: + Divider(QWidget * parent = nullptr); + ~Divider() = default; + +protected: + void paintEvent(QPaintEvent *event); +}; + +#endif // DIVIDER_H diff --git a/src/frontend/tools/infobutton.cpp b/src/frontend/tools/infobutton.cpp new file mode 100644 index 00000000..0dd16cd8 --- /dev/null +++ b/src/frontend/tools/infobutton.cpp @@ -0,0 +1,93 @@ +#include "infobutton.h" +#include +#include +#include + +#define BUTTON_SIZE 36,36 +#define ICON_SIZE 16,16 +#define BACKGROUND_COLOR QColor(0,0,0,0) +#define FOREGROUND_COLOR_NORMAL qApp->palette().text().color() +#define FOREGROUND_COLOR_HOVER QColor(55,144,250,255) +#define FOREGROUND_COLOR_PRESS QColor(36,109,212,255) +#define OUTER_PATH 8,8,16,16 +#define INNER_PATH 9,9,14,14 +#define TEXT_POS 14,5,16,16,0 + +#define BUTTON_SIZE 36,36 + +InfoButton::InfoButton(QWidget *parent) : QPushButton(parent) +{ + this->setFixedSize(BUTTON_SIZE); + initUI(); + connect(qApp, &QApplication::paletteChanged, this, &InfoButton::onPaletteChanged); +} + +void InfoButton::initUI() +{ + this->setFixedSize(BUTTON_SIZE); + m_backgroundColor = BACKGROUND_COLOR; + m_foregroundColor = FOREGROUND_COLOR_NORMAL; +} + +void InfoButton::onPaletteChanged() +{ + m_foregroundColor = FOREGROUND_COLOR_NORMAL; + this->repaint(); +} + +void InfoButton::paintEvent(QPaintEvent *event) +{ + QPalette pal = this->palette(); + pal.setColor(QPalette::Base, m_backgroundColor); + pal.setColor(QPalette::Text, m_foregroundColor); + + QPainterPath cPath; + cPath.addRect(0, 0, ICON_SIZE); + cPath.addEllipse(0, 0, ICON_SIZE); + + QPainterPath outerPath; + outerPath.addEllipse(OUTER_PATH); + + QPainterPath innerPath; + innerPath.addEllipse(INNER_PATH); + outerPath -= innerPath; + + QPainter painter(this); + painter.setRenderHint(QPainter:: Antialiasing, true); //设置渲染,启动反锯齿 + painter.setPen(Qt::NoPen); + + painter.setBrush(pal.color(QPalette::Base)); + painter.drawPath(cPath); + + painter.fillPath(outerPath, pal.color(QPalette::Text)); + painter.setPen(m_foregroundColor); + QFont font("Noto Sans CJK SC", 11, QFont::Normal, false); + painter.setFont(font); + painter.drawText(TEXT_POS, "i"); +} + +void InfoButton::enterEvent(QEvent *event) +{ + m_foregroundColor = FOREGROUND_COLOR_HOVER; + this->update(); +} + +void InfoButton::leaveEvent(QEvent *event) +{ + m_foregroundColor = FOREGROUND_COLOR_NORMAL; + this->update(); +} + +void InfoButton::mousePressEvent(QMouseEvent *event) +{ + m_foregroundColor = FOREGROUND_COLOR_PRESS; + this->update(); + return QPushButton::mousePressEvent(event); +} + +void InfoButton::mouseReleaseEvent(QMouseEvent *event) +{ + m_foregroundColor = FOREGROUND_COLOR_HOVER; + this->update(); + return QPushButton::mouseReleaseEvent(event); +} diff --git a/src/frontend/tools/infobutton.h b/src/frontend/tools/infobutton.h new file mode 100644 index 00000000..523825ab --- /dev/null +++ b/src/frontend/tools/infobutton.h @@ -0,0 +1,31 @@ +#ifndef INFOBUTTON_H +#define INFOBUTTON_H +#include +#include + +class InfoButton : public QPushButton +{ + Q_OBJECT +public: + explicit InfoButton(QWidget * parent = nullptr); + ~InfoButton() = default; + +protected: + void paintEvent(QPaintEvent *event); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +private: + void initUI(); + +private: + QColor m_backgroundColor; + QColor m_foregroundColor; + +private slots: + void onPaletteChanged(); +}; + +#endif // INFOBUTTON_H diff --git a/src/frontend/tools/kylable.cpp b/src/frontend/tools/kylable.cpp new file mode 100644 index 00000000..0c428a5d --- /dev/null +++ b/src/frontend/tools/kylable.cpp @@ -0,0 +1,97 @@ +#include "kylable.h" +#include +#include +#include + +#define FOREGROUND_COLOR_NORMAL qApp->palette().text().color() + +static inline qreal mixQreal(qreal a, qreal b, qreal bias) +{ + return a + (b - a) * bias; +} + + +QColor mixColor(const QColor &c1, const QColor &c2, qreal bias) +{ + if (bias <= 0.0) { + return c1; + } + if (bias >= 1.0) { + return c2; + } + if (qIsNaN(bias)) { + return c1; + } + + qreal r = mixQreal(c1.redF(), c2.redF(), bias); + qreal g = mixQreal(c1.greenF(), c2.greenF(), bias); + qreal b = mixQreal(c1.blueF(), c2.blueF(), bias); + qreal a = mixQreal(c1.alphaF(), c2.alphaF(), bias); + + return QColor::fromRgbF(r, g, b, a); +} + +KyLable::KyLable(QWidget *parent) : QLabel(parent) +{ + connect(qApp, &QApplication::paletteChanged, this, &KyLable::onPaletteChanged); + onPaletteChanged(); +} + +void KyLable::onPaletteChanged() +{ + m_foregroundColor = FOREGROUND_COLOR_NORMAL; + this->repaint(); +} + +void KyLable::setPressColor() +{ + QColor hightlight = this->palette().color(QPalette::Active,QPalette::Highlight); + QColor mix = this->palette().color(QPalette::Active,QPalette::BrightText); + m_foregroundColor = mixColor(hightlight, mix, 0.05); +} + +void KyLable::setHoverColor() +{ + QColor hightlight = this->palette().color(QPalette::Active,QPalette::Highlight); + QColor mix = this->palette().color(QPalette::Active,QPalette::BrightText); + m_foregroundColor = mixColor(hightlight, mix, 0.2); +} + +void KyLable::setNormalColor() +{ + m_foregroundColor = FOREGROUND_COLOR_NORMAL; +} + +void KyLable::paintEvent(QPaintEvent *event) +{ + QPalette pal = this->palette(); + pal.setColor(QPalette::WindowText, m_foregroundColor); + this->setPalette(pal); + return QLabel::paintEvent(event); +} + +void KyLable::enterEvent(QEvent *event) +{ + setHoverColor(); + this->update(); +} + +void KyLable::leaveEvent(QEvent *event) +{ + setNormalColor(); + this->update(); +} + +void KyLable::mousePressEvent(QMouseEvent *event) +{ + setPressColor(); + this->update(); + return QLabel::mousePressEvent(event); +} + +void KyLable::mouseReleaseEvent(QMouseEvent *event) +{ + setHoverColor(); + this->update(); + return QLabel::mouseReleaseEvent(event); +} diff --git a/src/frontend/tools/kylable.h b/src/frontend/tools/kylable.h new file mode 100644 index 00000000..9b32dfe4 --- /dev/null +++ b/src/frontend/tools/kylable.h @@ -0,0 +1,33 @@ +#ifndef KYLABLE_H +#define KYLABLE_H + +#include +#include + +class KyLable : public QLabel +{ + Q_OBJECT +public: + explicit KyLable(QWidget *parent = nullptr); + ~KyLable() = default; + +protected: + void paintEvent(QPaintEvent *event); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +private: + QColor m_foregroundColor; + + void setPressColor(); + void setHoverColor(); + void setNormalColor(); + +private slots: + void onPaletteChanged(); + +}; + +#endif // KYLABLE_H diff --git a/src/frontend/tools/loadingdiv.cpp b/src/frontend/tools/loadingdiv.cpp new file mode 100644 index 00000000..ddaeef31 --- /dev/null +++ b/src/frontend/tools/loadingdiv.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +#define FRAMESPEED 60 //帧与帧之间的间隔时间(ms) +#define ALLTIME 40*1000 //等待动画持续总时间 + +//加载动画控件'loadingGif' +LoadingDiv::LoadingDiv(QWidget *parent) : QWidget(parent) +{ + this->resize(480, 538); + + this->loadingGif = new QLabel(this); + this->loadingGif->resize(96, 96); + this->loadingGif->move(this->width()/2 - 96/2 + 41/2 - 40, this->height()/2 + 20); + this->loadingGif->show(); + + this->switchTimer = new QTimer(this); //QTimer对象,控制等待动画播放 + connect(switchTimer, SIGNAL(timeout()), this, SLOT(switchAnimStep())); + + this->raise(); + this->hide(); +} + +//加载动画控件'loadingGif' +void LoadingDiv::switchAnimStep() +{ + //另外一种加载方法 + QString qpmQss = ":/res/s/conning-b/"; + qpmQss.append(QString::number(this->currentPage)); + qpmQss.append(".png"); + loadingGif->setPixmap(QPixmap(qpmQss)); + loadingGif->setProperty("useIconHighlightEffect", true); + loadingGif->setProperty("iconHighlightEffectMode", true); + + this->currentPage --; + + if (this->currentPage < 1) { + this->currentPage = 12; //循环播放 + } + this->countCurrentTime += FRAMESPEED; + if (this->countCurrentTime >= ALLTIME) { + emit this->toStopLoading(); //发出信号停止主界面和托盘区的等待动画 + } +} + +//开始播放动画 +void LoadingDiv::startLoading() +{ + this->currentPage = 12; + this->countCurrentTime = 0; + this->switchTimer->start(FRAMESPEED); + this->show(); +} + +//结束播放动画 +void LoadingDiv::stopLoading() +{ + this->switchTimer->stop(); + this->hide(); +} diff --git a/src/frontend/tools/loadingdiv.h b/src/frontend/tools/loadingdiv.h new file mode 100644 index 00000000..246e686e --- /dev/null +++ b/src/frontend/tools/loadingdiv.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include + +class LoadingDiv : public QWidget +{ + Q_OBJECT +public: + explicit LoadingDiv(QWidget *parent = nullptr); + +signals: + void toStopLoading(); + +public slots: + void switchAnimStep(); + void startLoading(); + void stopLoading(); + +private: + QLabel *loadingGif = nullptr; + QTimer *switchTimer = nullptr; + + int currentPage; + int countCurrentTime; +}; + +#endif // LOADINGDIV_H diff --git a/src/frontend/tools/radioitembutton.cpp b/src/frontend/tools/radioitembutton.cpp new file mode 100644 index 00000000..0efbf384 --- /dev/null +++ b/src/frontend/tools/radioitembutton.cpp @@ -0,0 +1,246 @@ +#include "radioitembutton.h" +#include +#include +#include +#include +#include +#define FLASH_SPEED 100 +#define TIMEOUT_TIMER 90*1000 +#define BUTTON_SIZE 36,36 +#define ICON_SIZE 16,16 +#define BACKGROUND_COLOR QColor(0,0,0,0) +#define FOREGROUND_COLOR_NORMAL_INACTIVE_LIGHT QColor(230,230,230,255) +#define FOREGROUND_COLOR_NORMAL_INACTIVE_DARK QColor(55,55,55,255) +#define FOREGROUND_COLOR_PRESS_INACTIVE_LIGHT QColor(217,217,217,255) +#define FOREGROUND_COLOR_PRESS_INACTIVE_DARK QColor(70,70,70,255) +#define FOREGROUND_COLOR_NORMAL_ACTIVE QColor(55,144,250,255) +#define FOREGROUND_COLOR_PRESS_ACTIVE QColor(36,109,212,255) + +RadioItemButton::RadioItemButton(QWidget *parent) : QPushButton(parent) +{ + this->setAutoFillBackground(false); + m_iconLabel = new QLabel(this); + + this->setFixedSize(BUTTON_SIZE); + m_iconLabel->setFixedSize(BUTTON_SIZE); + m_iconLabel->setAlignment(Qt::AlignCenter); + + setActive(false); + //JXJ_TODO loading动画 + connect(this, &RadioItemButton::requestStartLoading, this, &RadioItemButton::onLoadingStarted); + connect(this , &RadioItemButton::requestStopLoading, this, &RadioItemButton::onLoadingStopped); + connect(qApp, &QApplication::paletteChanged, this, &RadioItemButton::onPaletteChanged); +} + +RadioItemButton::~RadioItemButton() +{ + +} + +void RadioItemButton::startLoading() +{ + emit this->requestStartLoading(); +} + +void RadioItemButton::stopLoading() +{ + emit this->requestStopLoading(); +} +//设置图标 +void RadioItemButton::setButtonIcon(const QIcon &icon) +{ + if (icon.isNull()) { + return; + } + m_pixmap = icon.pixmap(ICON_SIZE); +// m_iconLabel->setPixmap(m_pixmap); +} +//显示默认图标 +void RadioItemButton::setDefaultPixmap() +{ + m_iconLabel->setPixmap(m_pixmap); +} +//根据连接状态更改图标颜色 +void RadioItemButton::setActive(const bool &isActive) +{ + m_isActivated = isActive; + refreshButtonIcon(); +} +void RadioItemButton::onLoadingStarted() +{ +#define ANIMATION_SPEED 0.5*1000 +#define START_ROTATION 0 +#define END_ROTATION 360 +#define ANIMATION_LOOP -1 //无限旋转 + if (!m_iconLabel) { + qWarning() << "Start loading failed, iconLabel is null pointer!" << Q_FUNC_INFO << __LINE__; + } + if (!m_animation) { + m_animation = new QVariantAnimation(m_iconLabel); + } + m_animation->setDuration(ANIMATION_SPEED); + m_animation->setStartValue(START_ROTATION); + m_animation->setEndValue(END_ROTATION); + m_animation->setLoopCount(ANIMATION_LOOP); + connect(m_animation, &QVariantAnimation::valueChanged, this, &RadioItemButton::onAnimationValueChanged); + m_animation->start(); +} + +void RadioItemButton::onLoadingStopped() +{ + if (!m_animation) { + qWarning() << "Stop loading failed, m_animation is null pointer!" << Q_FUNC_INFO << __LINE__; + return; + } else { + m_iconLabel->setPixmap(m_pixmap); + m_animation->stop(); + emit this->animationStoped(); + } + +} + +void RadioItemButton::onPaletteChanged() +{ + refreshButtonIcon(); +} + +void RadioItemButton::onAnimationValueChanged(const QVariant& value) +{ + if (!m_iconLabel) { + return; + } + QTransform t; + t.rotate(value.toReal()); + m_iconLabel->setPixmap(QIcon(":/res/s/conning-a/1.png").pixmap(ICON_SIZE).transformed(t)); +} + +void RadioItemButton::paintEvent(QPaintEvent *event) +{ + QPalette pal = this->palette(); + pal.setColor(QPalette::Base, BACKGROUND_COLOR); + pal.setColor(QPalette::Text, m_backgroundColor); + + QPainterPath cPath; + cPath.addRect(0, 0, this->width(), this->height()); + cPath.addEllipse(0, 0, this->width(), this->width()); + + QPainterPath innerPath; + innerPath.addEllipse(0, 0, this->width(), this->width()); + + QPainter painter(this); + painter.setRenderHint(QPainter:: Antialiasing, true); //设置渲染,启动反锯齿 + painter.setPen(Qt::NoPen); + + painter.setBrush(pal.color(QPalette::Base)); + painter.drawPath(cPath); + + painter.fillPath(innerPath, pal.color(QPalette::Text)); +} + +void RadioItemButton::mousePressEvent(QMouseEvent *event) +{ + if (m_isActivated) { + m_backgroundColor = qApp->palette().highlight().color(); + } else { + m_backgroundColor = qApp->palette().brightText().color(); + m_backgroundColor.setAlphaF(0.21); + } + this->update(); + return QPushButton::mousePressEvent(event); +} + +void RadioItemButton::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_isActivated) { + m_backgroundColor = qApp->palette().highlight().color(); + } else { + m_backgroundColor = qApp->palette().brightText().color(); + m_backgroundColor.setAlphaF(0.18); + } + this->update(); + return QPushButton::mouseReleaseEvent(event); +} + +void RadioItemButton::enterEvent(QEvent *event) +{ + if (m_isActivated) { + m_backgroundColor = qApp->palette().highlight().color(); + } else { + m_backgroundColor = qApp->palette().brightText().color(); + m_backgroundColor.setAlphaF(0.32); + } + this->update(); + return QPushButton::enterEvent(event); +} + +void RadioItemButton::leaveEvent(QEvent *event) +{ + if (m_isActivated) { + m_backgroundColor = qApp->palette().highlight().color(); + } else { + m_backgroundColor = qApp->palette().brightText().color(); + m_backgroundColor.setAlphaF(0.18); + } + this->update(); + return QPushButton::leaveEvent(event); +} + +void RadioItemButton::refreshButtonIcon() +{ + if (m_isActivated) { + m_backgroundColor = qApp->palette().highlight().color(); + m_iconLabel->setPixmap(loadSvg(m_pixmap, PixmapColor::WHITE)); + } else { + m_backgroundColor = qApp->palette().brightText().color(); + m_backgroundColor.setAlphaF(0.18); + if (qApp->palette().base().color().red() > MIDDLE_COLOR) { + m_iconLabel->setPixmap(m_pixmap); + } else { + m_iconLabel->setPixmap(loadSvg(m_pixmap, PixmapColor::WHITE)); + } + } + + return; +} + +const QPixmap RadioItemButton::loadSvg(const QPixmap &source, const PixmapColor &cgColor) +{ + QImage img = source.toImage(); + for (int x = 0; x < img.width(); x++) { + for (int y = 0; y < img.height(); y++) { + auto color = img.pixelColor(x, y); + if (color.alpha() > 0) { + switch (cgColor) { + case PixmapColor::WHITE: + color.setRed(255); + color.setGreen(255); + color.setBlue(255); + img.setPixelColor(x, y, color); + break; + case PixmapColor::BLACK: + color.setRed(0); + color.setGreen(0); + color.setBlue(0); + img.setPixelColor(x, y, color); + break; + case PixmapColor::GRAY: + color.setRed(152); + color.setGreen(163); + color.setBlue(164); + img.setPixelColor(x, y, color); + break; + case PixmapColor::BLUE: + color.setRed(61); + color.setGreen(107); + color.setBlue(229); + img.setPixelColor(x, y, color); + break; + default: + return source; + break; + } + } + } + } + return QPixmap::fromImage(img); +} diff --git a/src/frontend/tools/radioitembutton.h b/src/frontend/tools/radioitembutton.h new file mode 100644 index 00000000..89ba1a8d --- /dev/null +++ b/src/frontend/tools/radioitembutton.h @@ -0,0 +1,61 @@ +#ifndef NETBUTTON_H +#define NETBUTTON_H +#include +#include +#include +#include +#include + +#define MIDDLE_COLOR 178 + +class RadioItemButton : public QPushButton +{ + Q_OBJECT + +public: + RadioItemButton(QWidget * parent = nullptr); + ~RadioItemButton(); + void startLoading(); + void stopLoading(); + void setButtonIcon(const QIcon &icon); + void setDefaultPixmap(); + void setActive(const bool &isActive); + enum PixmapColor { + WHITE = 0, + BLACK, + GRAY, + BLUE, + }; + const QPixmap loadSvg(const QPixmap &source, const PixmapColor &color); + +signals: + void requestStartLoading(); + void requestStopLoading(); + void animationStoped(); + +protected: + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + +private: + bool m_isActivated = false; + QLabel * m_iconLabel = nullptr; + QColor m_backgroundColor; + QVariantAnimation * m_animation = nullptr; + + QPixmap m_pixmap; + + void refreshButtonIcon(); + +private slots: + void onLoadingStarted(); + void onLoadingStopped(); + void onPaletteChanged(); + void onAnimationValueChanged(const QVariant& value); +}; + +#endif // NETBUTTON_H + diff --git a/src/frontend/tools/switchbutton.cpp b/src/frontend/tools/switchbutton.cpp new file mode 100644 index 00000000..806ca05e --- /dev/null +++ b/src/frontend/tools/switchbutton.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see setInterval(5); + + if(m_bIsOn == 1) { + m_fCurrentValue = m_fWidth - 16 - 4; + } + else { + m_fCurrentValue = 4; + } + connect(m_cTimer, SIGNAL(timeout()), this, SLOT(startAnimation())); + + +} + +void SwitchButton::setSwitchStatus(bool check) { + if (!m_enabled) + return; + + if(check == true) { + m_bIsOn = 1; + } else { + m_bIsOn = 0; + } + emit this->switchStatusChanged(); + + m_cTimer->start(); //开始播放动画 +} + +bool SwitchButton::getSwitchStatus() +{ + if (m_bIsOn == 1) + return true; + return false; +} + +void SwitchButton::setEnabled(bool enabled) +{ + m_enabled = enabled; + update(); + + return; +} + +bool SwitchButton::getEnabled() +{ + return m_enabled; +} +/* 播放按钮开启关闭动画 */ +void SwitchButton::startAnimation() { //滑动按钮动作播放 + int pos = 4; + int size = m_fWidth - 16; + if(m_bIsOn) { + m_fCurrentValue ++; //往右滑动 + if(m_fCurrentValue >= size - pos) { //到达边界停下来 + m_fCurrentValue = size - pos; + m_cTimer->stop(); + } + } else { + m_fCurrentValue --; + if(m_fCurrentValue <= pos) { //到达最小值,停止继续前进 + m_fCurrentValue = pos; + m_cTimer->stop(); + } + } + update(); +} + +/* 按钮按下处理 */ +void SwitchButton::mousePressEvent(QMouseEvent *event) { + Q_UNUSED(event); + + if (m_enabled) { + m_bIsOn = !m_bIsOn; + } + Q_EMIT clicked(); + return QWidget::mousePressEvent(event); +} + +void SwitchButton::enterEvent(QEvent *event) +{ + if (m_enabled && m_bIsOn) { + m_colorActive = COLOR_ACTIVE_HOVER; + } else if (m_enabled && !m_bIsOn) { + m_colorInactive = COLOR_INACTIVE_HOVER; + } + + this->update(); + + return QWidget::enterEvent(event); +} + +void SwitchButton::leaveEvent(QEvent *event) +{ + if (m_enabled && m_bIsOn) { + m_colorActive = COLOR_ACTIVE; + } else if (m_enabled && !m_bIsOn) { + m_colorInactive = COLOR_INACTIVE; + } + + this->update(); + + return QWidget::leaveEvent(event); +} + +/* 绘制滑动按钮主体 */ +void SwitchButton::paintEvent(QPaintEvent *event) { + Q_UNUSED(event); + QPainter painter(this); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.setRenderHint(QPainter::Antialiasing); //抗锯齿效果 + painter.setPen(Qt::NoPen); + if(m_bIsOn && m_enabled) { + painter.save(); + painter.setBrush(m_colorActive); + QRectF active_rect = QRectF(0,0,m_fWidth,m_fHeight); + painter.drawRoundedRect(active_rect, 0.5 * m_fHeight, 0.5 * m_fHeight); //画开启状态 + } else { + painter.save(); + painter.setBrush(m_colorInactive); + QRectF inactive_rect = QRectF(0 ,0,m_fWidth,m_fHeight); + painter.drawRoundedRect(inactive_rect, 0.5 * m_fHeight, 0.5 * m_fHeight); //画关闭状态 + } + painter.restore(); + painter.save(); + if (!m_enabled) { + painter.setBrush(COLOR_UNABLE); +// QRectF enableRect = QRectF(30, 10, 8, 4); +// painter.drawRoundedRect(enableRect, 2, 2); + } else { + painter.setBrush(Qt::white); + } + painter.drawEllipse(m_fCurrentValue,4, 16, 16); + + painter.restore(); +} + diff --git a/src/frontend/tools/switchbutton.h b/src/frontend/tools/switchbutton.h new file mode 100644 index 00000000..fea1e480 --- /dev/null +++ b/src/frontend/tools/switchbutton.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include + +class SwitchButton : public QWidget +{ + Q_OBJECT +public: + explicit SwitchButton(QWidget *parent = nullptr); + void setSwitchStatus(bool check); + bool getSwitchStatus(); + void setEnabled(bool enabled); + bool getEnabled(); + +private: + QColor m_colorActive; + QColor m_colorInactive; + int m_bIsOn = 1; + QTimer *m_cTimer; + float m_fWidth; + float m_fHeight; + float m_fCurrentValue; + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + bool m_enabled = true; + +Q_SIGNALS: + void clicked(); + void switchStatusChanged(); + +private Q_SLOTS: + void startAnimation(); + +}; + +#endif // SWITCHBUTTON_H diff --git a/src/frontend/tools/tools.pri b/src/frontend/tools/tools.pri new file mode 100644 index 00000000..7863428b --- /dev/null +++ b/src/frontend/tools/tools.pri @@ -0,0 +1,18 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/divider.h \ + $$PWD/infobutton.h \ + $$PWD/loadingdiv.h \ + $$PWD/radioitembutton.h \ + $$PWD/switchbutton.h \ + $$PWD/kylable.h + + +SOURCES += \ + $$PWD/divider.cpp \ + $$PWD/infobutton.cpp \ + $$PWD/loadingdiv.cpp \ + $$PWD/radioitembutton.cpp \ + $$PWD/switchbutton.cpp \ + $$PWD/kylable.cpp diff --git a/src/frontend/wificonfigdialog.cpp b/src/frontend/wificonfigdialog.cpp new file mode 100644 index 00000000..23d2d24e --- /dev/null +++ b/src/frontend/wificonfigdialog.cpp @@ -0,0 +1,199 @@ +#include "wificonfigdialog.h" +#include "ui_wificonfigdialog.h" +#include "utils.h" + +#include +#include +#include +#include + +#include + +WiFiConfigDialog::WiFiConfigDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::WiFiConfigDialog) +{ + ui->setupUi(this); + + initTransparentState(); //初始化窗口透明度的GSetting方法 + + this->setWindowFlags(Qt::FramelessWindowHint); //Qt::WindowStaysOnTopHint + this->setWindowTitle(tr("WLAN Authentication"));//"Wifi 认证" + this->setAttribute(Qt::WA_TranslucentBackground); + this->setWindowIcon(QIcon::fromTheme("kylin-network", QIcon(":/res/x/setup.png")) ); + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + KWindowEffects::enableBlurBehind(this->winId(), true, QRegion(path.toFillPolygon().toPolygon())); + + ui->lbTitle->setText(tr("Input WLAN Information Please")); //输入Wi-Fi名称和密码后点击确定 + ui->lbWifiId->setText(tr("WLAN ID:")); //Wi-Fi连接名称: + ui->lbWifiName->setText(tr("WLAN Name:")); //Wi-Fi名称: + ui->lbWifiPassord->setText(tr("Password:")); //Wi-Fi密码: + ui->btnCancel->setText(tr("Cancl")); + ui->btnOk->setText(tr("Ok")); + ui->leWifiId->setContextMenuPolicy(Qt::NoContextMenu); + ui->leWiFiName->setContextMenuPolicy(Qt::NoContextMenu); + + ui->leWifiPassword->setContextMenuPolicy(Qt::NoContextMenu); + ui->leWifiPassword ->setEchoMode(QLineEdit::Password); + + ui->checkBoxPwd->setStyleSheet(checkBoxQss); + ui->checkBoxPwd->setFocusPolicy(Qt::NoFocus); + + ui->leWifiId->setContextMenuPolicy(Qt::NoContextMenu); //禁止LineEdit的右键菜单 + ui->leWiFiName->setContextMenuPolicy(Qt::NoContextMenu); + ui->leWifiPassword->setContextMenuPolicy(Qt::NoContextMenu); + + this->setEnableOfBtn(); + this->setFixedSize(474, 320); +} + +WiFiConfigDialog::~WiFiConfigDialog() +{ + delete ui; +} + +void WiFiConfigDialog::paintEvent(QPaintEvent *event) +{ + double trans = this->getTransparentData(); + + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + + QRect rect = this->rect(); + p.setRenderHint(QPainter::Antialiasing); // 反锯齿; + p.setBrush(opt.palette.color(QPalette::Base)); + p.setOpacity(trans); + p.setPen(Qt::NoPen); + p.drawRoundedRect(rect, 6, 6); + QWidget::paintEvent(event); +} + +//创建获取窗口透明度信息的GSetting的对象 +void WiFiConfigDialog::initTransparentState() +{ + if (QGSettings::isSchemaInstalled("org.ukui.control-center.personalise")) { + m_transparency_gsettings = new QGSettings("org.ukui.control-center.personalise"); + } +} + +//使用GSetting获取当前窗口应该使用的透明度 +double WiFiConfigDialog::getTransparentData() +{ + if (!m_transparency_gsettings) { + return 0.7; + } + + QStringList keys = m_transparency_gsettings->keys(); + if (keys.contains("transparency")) { + double tp = m_transparency_gsettings->get("transparency").toDouble(); + return tp; + } else { + return 0.7; + } +} + +void WiFiConfigDialog::on_btnCancel_clicked() +{ + ui->leWiFiName->setText(""); + ui->leWifiPassword->setText(""); + hide(); +} + +void WiFiConfigDialog::on_btnOk_clicked() +{ + QFuture < void > future1 = QtConcurrent::run([=](){ + this->toConfigWifi(); + }); + hide(); +} + +void WiFiConfigDialog::toConfigWifi() +{ + QString connName = ui->leWiFiName->text(); + QString password = ui->leWifiPassword->text(); + + QString strConntype = "nmcli connection modify '" + connName + "' wifi-sec.psk-flags 0"; + Utils::m_system(strConntype.toUtf8().data()); + + QString tmpPath = "/tmp/kylin-nm-btoutput-" + QDir::home().dirName(); + QString cmdStr = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli device wifi connect '" + connName + "' password '" + password + "' > " + tmpPath; + Utils::m_system(cmdStr.toUtf8().data()); + + QFile file(tmpPath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug()<<"Can't open the file /tmp/kylin-nm-btoutput in function toConfigWifi !"; + } + QString line = file.readLine(); + file.close(); + qDebug()<<"connect_wifi_result: "<< line; + + if (line.indexOf("successfully") != -1) { + QString strConntype = "nmcli connection modify '" + connName + "' wifi-sec.psk-flags 2"; + system(strConntype.toUtf8().data()); + } else { + QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli connection delete '" + ui->leWifiId->text() + "'"; + int status = system(cmd.toUtf8().data()); + qDebug()<<"executed cmd="< + + WiFiConfigDialog + + + + 0 + 0 + 474 + 320 + + + + Dialog + + + + + 60 + 30 + 290 + 30 + + + + + + + + + + 60 + 140 + 90 + 30 + + + + + + + + + + 60 + 200 + 90 + 30 + + + + + + + + + + 190 + 140 + 240 + 30 + + + + + + + 190 + 200 + 241 + 30 + + + + + + + 220 + 260 + 96 + 36 + + + + + + + + + + 330 + 260 + 96 + 36 + + + + + + + + + + 60 + 80 + 121 + 30 + + + + + + + + + + 190 + 80 + 240 + 30 + + + + + + + 400 + 211 + 18 + 9 + + + + + + + + + + diff --git a/src/frontend/xatom/xatom-helper.cpp b/src/frontend/xatom/xatom-helper.cpp new file mode 100644 index 00000000..cf7b2936 --- /dev/null +++ b/src/frontend/xatom/xatom-helper.cpp @@ -0,0 +1,210 @@ +/* + * KWin Style UKUI + * + * Copyright (C) 2020, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: Yue Lan + * + */ + +#include "xatom-helper.h" +#include +#include +#include +#include + +static XAtomHelper *global_instance = nullptr; + +XAtomHelper *XAtomHelper::getInstance() +{ + if(!global_instance) + global_instance = new XAtomHelper; + return global_instance; +} + +bool XAtomHelper::isFrameLessWindow(int winId) +{ + auto hints = getInstance()->getWindowMotifHint(winId); + if(hints.flags == MWM_HINTS_DECORATIONS && hints.functions == 1) { + return true; + } + return false; +} + +bool XAtomHelper::isWindowDecorateBorderOnly(int winId) +{ + return isWindowMotifHintDecorateBorderOnly(getInstance()->getWindowMotifHint(winId)); +} + +bool XAtomHelper::isWindowMotifHintDecorateBorderOnly(const MotifWmHints &hint) { + bool isDeco = false; + if(hint.flags & MWM_HINTS_DECORATIONS && hint.flags != MWM_HINTS_DECORATIONS) { + if(hint.decorations == MWM_DECOR_BORDER) + isDeco = true; + } + return isDeco; +} + +bool XAtomHelper::isUKUICsdSupported() +{ + // fixme: + return false; +} + +bool XAtomHelper::isUKUIDecorationWindow(int winId) +{ + if(m_ukuiDecorationAtion == None) + return false; + + Atom type; + int format; + ulong nitems; + ulong bytes_after; + uchar *data; + + bool isUKUIDecoration = false; + + XGetWindowProperty(QX11Info::display(), winId, m_ukuiDecorationAtion, + 0, LONG_MAX, false, + m_ukuiDecorationAtion, &type, + &format, &nitems, + &bytes_after, &data); + + if(type == m_ukuiDecorationAtion) { + if(nitems == 1) { + isUKUIDecoration = data[0]; + } + } + + return isUKUIDecoration; +} + +UnityCorners XAtomHelper::getWindowBorderRadius(int winId) +{ + UnityCorners corners; + + Atom type; + int format; + ulong nitems; + ulong bytes_after; + uchar *data; + + if(m_unityBorderRadiusAtom != None) { + XGetWindowProperty(QX11Info::display(), winId, m_unityBorderRadiusAtom, + 0, LONG_MAX, false, + XA_CARDINAL, &type, + &format, &nitems, + &bytes_after, &data); + + if(type == XA_CARDINAL) { + if(nitems == 4) { + corners.topLeft = static_cast(data[0]); + corners.topRight = static_cast(data[1 * sizeof(ulong)]); + corners.bottomLeft = static_cast(data[2 * sizeof(ulong)]); + corners.bottomRight = static_cast(data[3 * sizeof(ulong)]); + } + XFree(data); + } + } + + return corners; +} + +void XAtomHelper::setWindowBorderRadius(int winId, const UnityCorners &data) +{ + if(m_unityBorderRadiusAtom == None) + return; + + ulong corners[4] = {data.topLeft, data.topRight, data.bottomLeft, data.bottomRight}; + + XChangeProperty(QX11Info::display(), winId, m_unityBorderRadiusAtom, XA_CARDINAL, + 32, XCB_PROP_MODE_REPLACE, (const unsigned char *) &corners, sizeof(corners) / sizeof(corners[0])); +} + +void XAtomHelper::setWindowBorderRadius(int winId, int topLeft, int topRight, int bottomLeft, int bottomRight) +{ + if(m_unityBorderRadiusAtom == None) + return; + + ulong corners[4] = {(ulong)topLeft, (ulong)topRight, (ulong)bottomLeft, (ulong)bottomRight}; + + XChangeProperty(QX11Info::display(), winId, m_unityBorderRadiusAtom, XA_CARDINAL, + 32, XCB_PROP_MODE_REPLACE, (const unsigned char *) &corners, sizeof(corners) / sizeof(corners[0])); +} + +void XAtomHelper::setUKUIDecoraiontHint(int winId, bool set) +{ + if(m_ukuiDecorationAtion == None) + return; + + XChangeProperty(QX11Info::display(), winId, m_ukuiDecorationAtion, m_ukuiDecorationAtion, 32, XCB_PROP_MODE_REPLACE, (const unsigned char *) &set, 1); +} + +void XAtomHelper::setWindowMotifHint(int winId, const MotifWmHints &hints) +{ + if(m_unityBorderRadiusAtom == None) + return; + + XChangeProperty(QX11Info::display(), winId, m_motifWMHintsAtom, m_motifWMHintsAtom, + 32, XCB_PROP_MODE_REPLACE, (const unsigned char *)&hints, sizeof(MotifWmHints) / sizeof(ulong)); +} + +MotifWmHints XAtomHelper::getWindowMotifHint(int winId) +{ + MotifWmHints hints; + + if(m_unityBorderRadiusAtom == None) + return hints; + + uchar *data; + Atom type; + int format; + ulong nitems; + ulong bytes_after; + + XGetWindowProperty(QX11Info::display(), winId, m_motifWMHintsAtom, + 0, sizeof(MotifWmHints) / sizeof(long), false, AnyPropertyType, &type, + &format, &nitems, &bytes_after, &data); + + if(type == None) { + return hints; + } else { + hints = *(MotifWmHints *)data; + XFree(data); + } + return hints; +} + +XAtomHelper::XAtomHelper(QObject *parent) : QObject(parent) +{ + if(!QX11Info::isPlatformX11()) + return; + + m_motifWMHintsAtom = XInternAtom(QX11Info::display(), "_MOTIF_WM_HINTS", true); + m_unityBorderRadiusAtom = XInternAtom(QX11Info::display(), "_UNITY_GTK_BORDER_RADIUS", false); + m_ukuiDecorationAtion = XInternAtom(QX11Info::display(), "_KWIN_UKUI_DECORAION", false); +} + +Atom XAtomHelper::registerUKUICsdNetWmSupportAtom() +{ + // fixme: + return None; +} + +void XAtomHelper::unregisterUKUICsdNetWmSupportAtom() +{ + // fixme: +} diff --git a/src/frontend/xatom/xatom-helper.h b/src/frontend/xatom/xatom-helper.h new file mode 100644 index 00000000..87272454 --- /dev/null +++ b/src/frontend/xatom/xatom-helper.h @@ -0,0 +1,110 @@ +/* + * KWin Style UKUI + * + * Copyright (C) 2020, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: Yue Lan + * + */ + +#ifndef XATOMHELPER_H +#define XATOMHELPER_H + +#include +#include +#include + +struct UnityCorners { + ulong topLeft = 0; + ulong topRight = 0; + ulong bottomLeft = 0; + ulong bottomRight = 0; +}; + +typedef struct { + ulong flags = 0; + ulong functions = 0; + ulong decorations = 0; + long input_mode = 0; + ulong status = 0; +} MotifWmHints, MwmHints; + +#define MWM_HINTS_FUNCTIONS (1L << 0) +#define MWM_HINTS_DECORATIONS (1L << 1) +#define MWM_HINTS_INPUT_MODE (1L << 2) +#define MWM_HINTS_STATUS (1L << 3) + +#define MWM_FUNC_ALL (1L << 0) +#define MWM_FUNC_RESIZE (1L << 1) +#define MWM_FUNC_MOVE (1L << 2) +#define MWM_FUNC_MINIMIZE (1L << 3) +#define MWM_FUNC_MAXIMIZE (1L << 4) +#define MWM_FUNC_CLOSE (1L << 5) + +#define MWM_DECOR_ALL (1L << 0) +#define MWM_DECOR_BORDER (1L << 1) +#define MWM_DECOR_RESIZEH (1L << 2) +#define MWM_DECOR_TITLE (1L << 3) +#define MWM_DECOR_MENU (1L << 4) +#define MWM_DECOR_MINIMIZE (1L << 5) +#define MWM_DECOR_MAXIMIZE (1L << 6) + +#define MWM_INPUT_MODELESS 0 +#define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 +#define MWM_INPUT_SYSTEM_MODAL 2 +#define MWM_INPUT_FULL_APPLICATION_MODAL 3 +#define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL + +#define MWM_TEAROFF_WINDOW (1L<<0) + +namespace UKUI { +class Decoration; +} + +class XAtomHelper : public QObject +{ + // friend class UKUI::Decoration; + Q_OBJECT +public: + static XAtomHelper *getInstance(); + + static bool isFrameLessWindow(int winId); + + static bool isWindowDecorateBorderOnly(int winId); + static bool isWindowMotifHintDecorateBorderOnly(const MotifWmHints &hint); + bool isUKUICsdSupported(); + bool isUKUIDecorationWindow(int winId); + + UnityCorners getWindowBorderRadius(int winId); + void setWindowBorderRadius(int winId, const UnityCorners &data); + void setWindowBorderRadius(int winId, int topLeft, int topRight, int bottomLeft, int bottomRight); + void setUKUIDecoraiontHint(int winId, bool set = true); + + void setWindowMotifHint(int winId, const MotifWmHints &hints); + MotifWmHints getWindowMotifHint(int winId); + +private: + explicit XAtomHelper(QObject *parent = nullptr); + + Atom registerUKUICsdNetWmSupportAtom(); + void unregisterUKUICsdNetWmSupportAtom(); + + Atom m_motifWMHintsAtom = None; + Atom m_unityBorderRadiusAtom = None; + Atom m_ukuiDecorationAtion = None; +}; + +#endif // XATOMHELPER_H diff --git a/src/frontend/xatom/xatom.pri b/src/frontend/xatom/xatom.pri new file mode 100644 index 00000000..2d5b1409 --- /dev/null +++ b/src/frontend/xatom/xatom.pri @@ -0,0 +1,7 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/xatom-helper.h \ + +SOURCES += \ + $$PWD/xatom-helper.cpp \ diff --git a/src/kylin-nm.desktop b/src/kylin-nm.desktop new file mode 100644 index 00000000..849c402e --- /dev/null +++ b/src/kylin-nm.desktop @@ -0,0 +1,18 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Kylin NM +Name[zh_CN]=麒麟网络设置工具 +Name[zh_HK]=麒麟網路設置工具 +Name[zh_TW]=麒麟網路設置工具 +Icon=gnome-dev-ethernet +Comment=Beautiful Network Config Applet +Comment[zh_CN]=麒麟网络设置工具,提供查看和简单设置功能,拥有美观的界面和舒适的操作. +Keywords=applet;nm;network;network-manager; +Exec=/usr/bin/kylin-nm +StartupNotify=false +Terminal=false +Type=Application +OnlyShowIn=UKUI +X-UKUI-AutoRestart=true +NoDisplay=true +X-UKUI-Autostart-Phase=Application diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..836471f7 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include "qt-single-application.h" +#include +#include +#include +#include +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) +#include "xatom-helper.h" +#endif + +#define LOG_IDENT "ukui_kylin_nm" + +const QString QT_TRANSLATE_FILE = "/usr/share/qt5/translations/qt_zh_CN.qm"; + +void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + QByteArray localMsg = msg.toLocal8Bit(); + QByteArray currentDateTime = QDateTime::currentDateTime().toString().toLocal8Bit(); + + bool showDebug = true; + QString logFilePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.config/ukui/kylin-nm.log"; + //若不需要自动创建日志文件,请放开此注释 +// if (!QFile::exists(logFilePath)) { +// showDebug = false; +// } + FILE *log_file = nullptr; + + if (showDebug) { + log_file = fopen(logFilePath.toLocal8Bit().constData(), "a+"); + } + + const char *file = context.file ? context.file : ""; + const char *function = context.function ? context.function : ""; + switch (type) { + case QtDebugMsg: + if (!log_file) { + break; + } + fprintf(log_file, "Debug: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + case QtInfoMsg: + fprintf(log_file? log_file: stdout, "Info: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + case QtWarningMsg: + fprintf(log_file? log_file: stderr, "Warning: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + case QtCriticalMsg: + fprintf(log_file? log_file: stderr, "Critical: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + case QtFatalMsg: + fprintf(log_file? log_file: stderr, "Fatal: %s: %s (%s:%u, %s)\n", currentDateTime.constData(), localMsg.constData(), file, context.line, function); + break; + } + + if (log_file) + fclose(log_file); +} + +int main(int argc, char *argv[]) +{ + initUkuiLog4qt("kylin-nm"); + + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + +// QApplication a(argc, argv); + QString id = QString("kylin-nm"+ QLatin1String(getenv("DISPLAY"))); + QtSingleApplication a(id, argc, argv); + + QApplication::setQuitOnLastWindowClosed(false); + + + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", "kylinnm")); + parser.addHelpOption(); + parser.addVersionOption(); + + QCommandLineOption swOption(QStringLiteral("sw"),QCoreApplication::translate("main", "show kylin-nm wifi page")); + QCommandLineOption snOption(QStringLiteral("sn"),QCoreApplication::translate("main", "show kylin-nm lan page")); + + parser.addOptions({swOption,snOption}); + parser.process(a); + + QDBusInterface interface("com.kylin.network", + "/com/kylin/network", + "com.kylin.network", + QDBusConnection::sessionBus()); + if(interface.isValid()) { + if (parser.isSet(swOption)) + { + interface.call(QStringLiteral("showKylinNM"),1); + } else { + interface.call(QStringLiteral("showKylinNM"),0); + } + return 0; + } + + QThread thread; + KyNetworkResourceManager *p_networkResource = KyNetworkResourceManager::getInstance(); + p_networkResource->moveToThread(&thread); + QObject::connect(&thread, SIGNAL(started()), p_networkResource, SLOT(onInitNetwork())); + thread.start(); + + // Internationalization + QString locale = QLocale::system().name(); + QTranslator trans_global; + qDebug() << "QLocale " << QLocale(); + if (trans_global.load(QLocale(), "kylin-nm", "_", ":/translations/")) + { + a.installTranslator(&trans_global); + qDebug()<<"Translations load success"; + } else { + qWarning() << "Translations load fail"; + } + + QTranslator qtBaseTranslator; + if (qtBaseTranslator.load(QLocale(), "qt", "_", "/usr/share/qt5/translations/")) + { + a.installTranslator(&qtBaseTranslator); + qDebug()<<"QtBase Translations load success"; + } else { + qWarning() << "QtBase Translations load fail"; + } + + while (!p_networkResource->NetworkManagerIsInited()) { + ::usleep(1000); + } + + MainWindow w; + a.setActivationWindow(&w); + w.setProperty("useStyleWindowManager", false); //禁用拖动 + //设置窗口无边框,阴影 + + MotifWmHints window_hints; + window_hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; + window_hints.functions = MWM_FUNC_ALL; + window_hints.decorations = MWM_DECOR_BORDER; + XAtomHelper::getInstance()->setWindowMotifHint(w.winId(), window_hints); + +// w.setWindowFlags(Qt::CustomizeWindowHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); + + + DbusAdaptor adaptor(&w); + Q_UNUSED(adaptor); + + auto connection = QDBusConnection::sessionBus(); + if (!connection.registerService("com.kylin.network") || !connection.registerObject("/com/kylin/network", &w)) { + qCritical() << "QDbus register service failed reason:" << connection.lastError(); + } + + return a.exec(); +} diff --git a/src/org.ukui.kylin-nm.switch.gschema.xml b/src/org.ukui.kylin-nm.switch.gschema.xml new file mode 100644 index 00000000..544f2cd0 --- /dev/null +++ b/src/org.ukui.kylin-nm.switch.gschema.xml @@ -0,0 +1,14 @@ + + + + true +

wireless switch + Wireless switch.true is open,false is close. + + + true + wired switch + Wired switch.true is open,false is close. + + + diff --git a/src/singleapplication/qt-local-peer.cpp b/src/singleapplication/qt-local-peer.cpp new file mode 100644 index 00000000..4a62537c --- /dev/null +++ b/src/singleapplication/qt-local-peer.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** +****************************************************************************/ + + +#include "qt-local-peer.h" +#include +#include +#include + +#if defined(Q_OS_UNIX) +#include +#include +#include +#endif + +namespace QtLP_Private { +#include "qt-locked-file.cpp" +#include "qt-locked-file-unix.cpp" +} + +const char* QtLocalPeer::ack = "ack"; + +QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) + : QObject(parent), id(appId) { + QString prefix = id; + if(id.isEmpty()) { + id = QCoreApplication::applicationFilePath(); +#if defined(Q_OS_WIN) + id = id.toLower(); +#endif + prefix = id.section(QLatin1Char('/'), -1); //完整路径按‘/’分隔后取最后一个字段 + } + prefix.remove(QRegExp("[^a-zA-Z]")); //去掉名称中的非字母 + prefix.truncate(6); //取前六位 + + QByteArray idc = id.toUtf8(); + quint16 idNum = qChecksum(idc.constData(), idc.size()); + socketName = QLatin1String("qtsingleapp-") + prefix + + QLatin1Char('-') + QString::number(idNum, 16); + +#if defined(Q_OS_WIN) + if(!pProcessIdToSessionId) { + QLibrary lib("kernel32"); + pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); + } + if(pProcessIdToSessionId) { + DWORD sessionId = 0; + pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); + socketName += QLatin1Char('-') + QString::number(sessionId, 16); + } +#else + socketName += QLatin1Char('-') + QString::number(::getuid(), 16); +#endif + + server = new QLocalServer(this); + QString lockName = QDir(QDir::tempPath()).absolutePath() + + QLatin1Char('/') + socketName + + QLatin1String("-lockfile"); //tmp目录下的锁文件 + lockFile.setFileName(lockName); + lockFile.open(QIODevice::ReadWrite); +} + + + +bool QtLocalPeer::isClient() { + if(lockFile.isLocked()) + return false; + + if(!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) + return true; + + //由于文件锁的存在,仅当本进程第一次启动时能执行到此并使server进行监听和关联槽函数 + bool res = server->listen(socketName); +#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) + // ### Workaround + if(!res && server->serverError() == QAbstractSocket::AddressInUseError) { + QFile::remove(QDir::cleanPath(QDir::tempPath()) + QLatin1Char('/') + socketName); + res = server->listen(socketName); + } +#endif + if(!res) + qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); + QObject::connect(server, &QLocalServer::newConnection, this, &QtLocalPeer::receiveConnection); + return false; +} + + +bool QtLocalPeer::sendMessage(const QString &message, int timeout) { + if(!isClient()) + return false; + + QLocalSocket socket; + bool connOk = false; + for(int i = 0; i < 2; i++) { + // Try twice, in case the other instance is just starting up + socket.connectToServer(socketName); + connOk = socket.waitForConnected(timeout / 2); + if(connOk || i) + break; + int ms = 250; +#if defined(Q_OS_WIN) + Sleep(DWORD(ms)); +#else + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; + nanosleep(&ts, NULL); +#endif + } + if(!connOk) + return false; + + QByteArray uMsg(message.toUtf8()); + QDataStream ds(&socket); + ds.writeBytes(uMsg.constData(), uMsg.size()); + bool res = socket.waitForBytesWritten(timeout); + if(res) { + res &= socket.waitForReadyRead(timeout); // wait for ack + if(res) + res &= (socket.read(qstrlen(ack)) == ack); + } + return res; +} + +/** + * @brief QtLocalPeer::receiveConnection 当新进程启动时,会尝试连接此进程server,server接收到newConnection信号并触发此槽函数 + */ +void QtLocalPeer::receiveConnection() { + QLocalSocket* socket = server->nextPendingConnection(); //获取新进程的socket + if(!socket) + return; + + while(true) { + if(socket->state() == QLocalSocket::UnconnectedState) { + qWarning("QtLocalPeer: Peer disconnected"); + delete socket; + return; + } + if(socket->bytesAvailable() >= qint64(sizeof(quint32))) + break; + socket->waitForReadyRead(); + } + + QDataStream ds(socket); + QByteArray uMsg; + quint32 remaining; + ds >> remaining; + uMsg.resize(remaining); + int got = 0; + char* uMsgBuf = uMsg.data(); + do { + got = ds.readRawData(uMsgBuf, remaining); + remaining -= got; + uMsgBuf += got; + } while(remaining && got >= 0 && socket->waitForReadyRead(2000)); + if(got < 0) { + qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); + delete socket; + return; + } + QString message(QString::fromUtf8(uMsg)); + socket->write(ack, qstrlen(ack)); + socket->waitForBytesWritten(1000); + socket->waitForDisconnected(1000); // make sure client reads ack + delete socket; + Q_EMIT messageReceived(message); //获取新进程的启动信息并作为信号发送给前端 +} diff --git a/src/singleapplication/qt-local-peer.h b/src/singleapplication/qt-local-peer.h new file mode 100644 index 00000000..883aec2a --- /dev/null +++ b/src/singleapplication/qt-local-peer.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** +****************************************************************************/ + +#ifndef QTLOCALPEER_H +#define QTLOCALPEER_H + +#include +#include +#include + +#include "qt-locked-file.h" + +class QtLocalPeer : public QObject { + Q_OBJECT + +public: + QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); + bool isClient(); + bool sendMessage(const QString &message, int timeout); + QString applicationId() const { + return id; + } + +Q_SIGNALS: + void messageReceived(const QString &message); + +protected Q_SLOTS: + void receiveConnection(); + +protected: + QString id; + QString socketName; + QLocalServer* server; + QtLP_Private::QtLockedFile lockFile; + +private: + static const char* ack; +}; + +#endif // QTLOCALPEER_H diff --git a/src/singleapplication/qt-locked-file-unix.cpp b/src/singleapplication/qt-locked-file-unix.cpp new file mode 100644 index 00000000..51472520 --- /dev/null +++ b/src/singleapplication/qt-locked-file-unix.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "qt-locked-file.h" + +bool QtLockedFile::lock(LockMode mode, bool block) { + if(!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if(mode == NoLock) + return unlock(); + + if(mode == m_lock_mode) + return true; + + if(m_lock_mode != NoLock) + unlock(); + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; + int cmd = block ? F_SETLKW : F_SETLK; + int ret = fcntl(handle(), cmd, &fl); + + if(ret == -1) { + if(errno != EINTR && errno != EAGAIN) + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + + m_lock_mode = mode; + return true; +} + + +bool QtLockedFile::unlock() { + if(!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if(!isLocked()) + return true; + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = F_UNLCK; + int ret = fcntl(handle(), F_SETLKW, &fl); + + if(ret == -1) { + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + m_lock_mode = NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() { + if(isOpen()) + unlock(); +} + diff --git a/src/singleapplication/qt-locked-file.cpp b/src/singleapplication/qt-locked-file.cpp new file mode 100644 index 00000000..707b4cfa --- /dev/null +++ b/src/singleapplication/qt-locked-file.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt-locked-file.h" + +/*! + \class QtLockedFile + + \brief The QtLockedFile class extends QFile with advisory locking + functions. + + A file may be locked in read or write mode. Multiple instances of + \e QtLockedFile, created in multiple processes running on the same + machine, may have a file locked in read mode. Exactly one instance + may have it locked in write mode. A read and a write lock cannot + exist simultaneously on the same file. + + The file locks are advisory. This means that nothing prevents + another process from manipulating a locked file using QFile or + file system functions offered by the OS. Serialization is only + guaranteed if all processes that access the file use + QLockedFile. Also, while holding a lock on a file, a process + must not open the same file again (through any API), or locks + can be unexpectedly lost. + + The lock provided by an instance of \e QtLockedFile is released + whenever the program terminates. This is true even when the + program crashes and no destructors are called. +*/ + +/*! \enum QtLockedFile::LockMode + + This enum describes the available lock modes. + + \value ReadLock A read lock. + \value WriteLock A write lock. + \value NoLock Neither a read lock nor a write lock. +*/ + +/*! + Constructs an unlocked \e QtLockedFile object. This constructor + behaves in the same way as \e QFile::QFile(). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile() + : QFile() { +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Constructs an unlocked QtLockedFile object with file \a name. This + constructor behaves in the same way as \e QFile::QFile(const + QString&). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile(const QString &name) + : QFile(name) { +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Opens the file in OpenMode \a mode. + + This is identical to QFile::open(), with the one exception that the + Truncate mode flag is disallowed. Truncation would conflict with the + advisory file locking, since the file would be modified before the + write lock is obtained. If truncation is required, use resize(0) + after obtaining the write lock. + + Returns true if successful; otherwise false. + + \sa QFile::open(), QFile::resize() +*/ +bool QtLockedFile::open(OpenMode mode) { + if(mode & QIODevice::Truncate) { + qWarning("QtLockedFile::open(): Truncate mode not allowed."); + return false; + } + return QFile::open(mode); +} + +/*! + Returns \e true if this object has a in read or write lock; + otherwise returns \e false. + + \sa lockMode() +*/ +bool QtLockedFile::isLocked() const { + return m_lock_mode != NoLock; +} + +/*! + Returns the type of lock currently held by this object, or \e + QtLockedFile::NoLock. + + \sa isLocked() +*/ +QtLockedFile::LockMode QtLockedFile::lockMode() const { + return m_lock_mode; +} + +/*! + \fn bool QtLockedFile::lock(LockMode mode, bool block = true) + + Obtains a lock of type \a mode. The file must be opened before it + can be locked. + + If \a block is true, this function will block until the lock is + aquired. If \a block is false, this function returns \e false + immediately if the lock cannot be aquired. + + If this object already has a lock of type \a mode, this function + returns \e true immediately. If this object has a lock of a + different type than \a mode, the lock is first released and then a + new lock is obtained. + + This function returns \e true if, after it executes, the file is + locked by this object, and \e false otherwise. + + \sa unlock(), isLocked(), lockMode() +*/ + +/*! + \fn bool QtLockedFile::unlock() + + Releases a lock. + + If the object has no lock, this function returns immediately. + + This function returns \e true if, after it executes, the file is + not locked by this object, and \e false otherwise. + + \sa lock(), isLocked(), lockMode() +*/ + +/*! + \fn QtLockedFile::~QtLockedFile() + + Destroys the \e QtLockedFile object. If any locks were held, they + are released. +*/ diff --git a/src/singleapplication/qt-locked-file.h b/src/singleapplication/qt-locked-file.h new file mode 100644 index 00000000..332d648e --- /dev/null +++ b/src/singleapplication/qt-locked-file.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTLOCKEDFILE_H +#define QTLOCKEDFILE_H + +#include +#ifdef Q_OS_WIN +#include +#endif + +#if defined(Q_OS_WIN) +# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) +# define QT_QTLOCKEDFILE_EXPORT +# elif defined(QT_QTLOCKEDFILE_IMPORT) +# if defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# endif +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) +# elif defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTLOCKEDFILE_EXPORT +#endif + +namespace QtLP_Private { + +class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile { +public: + enum LockMode { NoLock = 0, ReadLock, WriteLock }; + + QtLockedFile(); + QtLockedFile(const QString &name); + ~QtLockedFile(); + + bool open(OpenMode mode); + + bool lock(LockMode mode, bool block = true); + bool unlock(); + bool isLocked() const; + LockMode lockMode() const; + +private: +#ifdef Q_OS_WIN + Qt::HANDLE wmutex; + Qt::HANDLE rmutex; + QVector rmutexes; + QString mutexname; + + Qt::HANDLE getMutexHandle(int idx, bool doCreate); + bool waitMutex(Qt::HANDLE mutex, bool doBlock); + +#endif + LockMode m_lock_mode; +}; +} +#endif diff --git a/src/singleapplication/qt-single-application.cpp b/src/singleapplication/qt-single-application.cpp new file mode 100644 index 00000000..b924430c --- /dev/null +++ b/src/singleapplication/qt-single-application.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** +****************************************************************************/ + + +#include "qt-single-application.h" +#include "qt-local-peer.h" +#include +#include +#include +#include +#include +#include +#include "../mainwindow.h" + + +/*! + \class QtSingleApplication qtsingleapplication.h + \brief The QtSingleApplication class provides an API to detect and + communicate with running instances of an application. + + This class allows you to create applications where only one + instance should be running at a time. I.e., if the user tries to + launch another instance, the already running instance will be + activated instead. Another usecase is a client-server system, + where the first started instance will assume the role of server, + and the later instances will act as clients of that server. + + By default, the full path of the executable file is used to + determine whether two processes are instances of the same + application. You can also provide an explicit identifier string + that will be compared instead. + + The application should create the QtSingleApplication object early + in the startup phase, and call isRunning() to find out if another + instance of this application is already running. If isRunning() + returns false, it means that no other instance is running, and + this instance has assumed the role as the running instance. In + this case, the application should continue with the initialization + of the application user interface before entering the event loop + with exec(), as normal. + + The messageReceived() signal will be emitted when the running + application receives messages from another instance of the same + application. When a message is received it might be helpful to the + user to raise the application so that it becomes visible. To + facilitate this, QtSingleApplication provides the + setActivationWindow() function and the activateWindow() slot. + + If isRunning() returns true, another instance is already + running. It may be alerted to the fact that another instance has + started by using the sendMessage() function. Also data such as + startup parameters (e.g. the name of the file the user wanted this + new instance to open) can be passed to the running instance with + this function. Then, the application should terminate (or enter + client mode). + + If isRunning() returns true, but sendMessage() fails, that is an + indication that the running instance is frozen. + + Here's an example that shows how to convert an existing + application to use QtSingleApplication. It is very simple and does + not make use of all QtSingleApplication's functionality (see the + examples for that). + + \code + // Original + int main(int argc, char **argv) + { + QApplication app(argc, argv); + + MyMainWidget mmw; + mmw.show(); + return app.exec(); + } + + // Single instance + int main(int argc, char **argv) + { + QtSingleApplication app(argc, argv); + + if (app.isRunning()) + return !app.sendMessage(someDataString); + + MyMainWidget mmw; + app.setActivationWindow(&mmw); + mmw.show(); + return app.exec(); + } + \endcode + + Once this QtSingleApplication instance is destroyed (normally when + the process exits or crashes), when the user next attempts to run the + application this instance will not, of course, be encountered. The + next instance to call isRunning() or sendMessage() will assume the + role as the new running instance. + + For console (non-GUI) applications, QtSingleCoreApplication may be + used instead of this class, to avoid the dependency on the QtGui + library. + + \sa QtSingleCoreApplication +*/ + + +void QtSingleApplication::sysInit(const QString &appId) { + m_activateWindow = 0; + m_peer = new QtLocalPeer(this, appId); + connect(m_peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::messageReceived); +} + + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a GUIenabled are passed on to the QAppliation constructor. + + If you are creating a console application (i.e. setting \a + GUIenabled to false), you may consider using + QtSingleCoreApplication instead. +*/ + +QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) + : QApplication(argc, argv, GUIenabled) { + sysInit(); +} + + +/*! + Creates a QtSingleApplication object with the application + identifier \a appId. \a argc and \a argv are passed on to the + QAppliation constructor. +*/ + +QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) + : QApplication(argc, argv) { + sysInit(appId); +} + +#if QT_VERSION < 0x050000 + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a type are passed on to the QAppliation constructor. +*/ +QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) + : QApplication(argc, argv, type) { + sysInit(); +} + + +# if defined(Q_WS_X11) +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, + and \a cmap are passed on to the QApplication constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, visual, cmap) { + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) { + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be \a appId. \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) { + sysInit(appId); +} +# endif // Q_WS_X11 +#endif // QT_VERSION < 0x050000 + + +/*! + Returns true if another instance of this application is running; + otherwise false. + + This function does not find instances of this application that are + being run by a different user (on Windows: that are running in + another session). + + \sa sendMessage() +*/ + +bool QtSingleApplication::isRunning() { + return m_peer->isClient(); +} + + +/*! + Tries to send the text \a message to the currently running + instance. The QtSingleApplication object in the running instance + will emit the messageReceived() signal when it receives the + message. + + This function returns true if the message has been sent to, and + processed by, the current instance. If there is no instance + currently running, or if the running instance fails to process the + message within \a timeout milliseconds, this function return false. + + \sa isRunning(), messageReceived() +*/ +bool QtSingleApplication::sendMessage(const QString &message, int timeout) { + return m_peer->sendMessage(message, timeout); +} + + +/*! + Returns the application identifier. Two processes with the same + identifier will be regarded as instances of the same application. +*/ +QString QtSingleApplication::id() const { + return m_peer->applicationId(); +} + + +/*! + Sets the activation window of this application to \a aw. The + activation window is the widget that will be activated by + activateWindow(). This is typically the application's main window. + + If \a activateOnMessage is true (the default), the window will be + activated automatically every time a message is received, just prior + to the messageReceived() signal being emitted. + + \sa activateWindow(), messageReceived() +*/ + +void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) { + m_activateWindow = aw; + if (activateOnMessage) + connect(m_peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); + else + disconnect(m_peer, &QtLocalPeer::messageReceived, this, &QtSingleApplication::activateWindow); +} + + +/*! + Returns the applications activation window if one has been set by + calling setActivationWindow(), otherwise returns 0. + + \sa setActivationWindow() +*/ +QWidget* QtSingleApplication::activationWindow() const { + return m_activateWindow; +} + + +/*! + De-minimizes, raises, and activates this application's activation window. + This function does nothing if no activation window has been set. + + This is a convenience function to show the user that this + application instance has been activated when he has tried to start + another instance. + + This function should typically be called in response to the + messageReceived() signal. By default, that will happen + automatically, if an activation window has been set. + + \sa setActivationWindow(), messageReceived(), initialize() +*/ +void QtSingleApplication::activateWindow() { + if (m_activateWindow) { + if(this->applicationState() & Qt::ApplicationInactive) + { + MainWindow* w=qobject_cast(m_activateWindow); + w->showMainwindow(); + m_activateWindow->setWindowState(m_activateWindow->windowState() & ~Qt::WindowMinimized); + m_activateWindow->raise(); + m_activateWindow->showNormal(); + m_activateWindow->activateWindow(); + } + else { + m_activateWindow->setWindowState(m_activateWindow->windowState() & Qt::WindowMinimized); + m_activateWindow->hide(); + } + } +} + + +/*! + \fn void QtSingleApplication::messageReceived(const QString& message) + + This signal is emitted when the current instance receives a \a + message from another instance of this application. + + \sa sendMessage(), setActivationWindow(), activateWindow() +*/ + + +/*! + \fn void QtSingleApplication::initialize(bool dummy = true) + + \obsolete +*/ diff --git a/src/singleapplication/qt-single-application.h b/src/singleapplication/qt-single-application.h new file mode 100644 index 00000000..084b9b7c --- /dev/null +++ b/src/singleapplication/qt-single-application.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies) +** 2020 KylinSoft Co., Ltd. +** Contact: http://www.qt-project.org/legal +** +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** +****************************************************************************/ + +#ifndef QTSINGLEAPPLICATION_H +#define QTSINGLEAPPLICATION_H + +#include + +class QtLocalPeer; + +#if defined(Q_OS_WIN) +# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) +# define QT_QTSINGLEAPPLICATION_EXPORT +# elif defined(QT_QTSINGLEAPPLICATION_IMPORT) +# if defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# endif +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) +# elif defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTSINGLEAPPLICATION_EXPORT +#endif + +class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication { + Q_OBJECT + +public: + QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); + QtSingleApplication(const QString &id, int &argc, char **argv); +#if QT_VERSION < 0x050000 + QtSingleApplication(int &argc, char **argv, Type type); +# if defined(Q_WS_X11) + QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); + QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0); + QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); +# endif // Q_WS_X11 +#endif // QT_VERSION < 0x050000 + + bool isRunning(); + QString id() const; + + void setActivationWindow(QWidget* aw, bool activateOnMessage = true); + QWidget* activationWindow() const; + + // Obsolete: + void initialize(bool dummy = true) { + isRunning(); + Q_UNUSED(dummy) + } + +public Q_SLOTS: + bool sendMessage(const QString &message, int timeout = 5000); + void activateWindow(); + + +Q_SIGNALS: + void messageReceived(const QString &message); + + +private: + void sysInit(const QString &appId = QString()); + QtLocalPeer *m_peer; + QWidget *m_activateWindow; +}; + +#endif // QTSINGLEAPPLICATION_H diff --git a/src/singleapplication/qt-single-application.pri b/src/singleapplication/qt-single-application.pri new file mode 100644 index 00000000..0a4ca9ae --- /dev/null +++ b/src/singleapplication/qt-single-application.pri @@ -0,0 +1,27 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +QT *= network +greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets + +qtsingleapplication-uselib:!qtsingleapplication-buildlib { + LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME +} else { + SOURCES += + HEADERS += +} + +win32 { + contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT + else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT +} + +HEADERS += \ + $$PWD/qt-local-peer.h \ + $$PWD/qt-locked-file.h \ + $$PWD/qt-single-application.h + +SOURCES += \ + $$PWD/qt-local-peer.cpp \ + $$PWD/qt-locked-file-unix.cpp \ + $$PWD/qt-single-application.cpp \ + $$PWD/qt-locked-file.cpp diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 00000000..603befdd --- /dev/null +++ b/src/src.pro @@ -0,0 +1,68 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-10-19T15:29:47 +# +#------------------------------------------------- + +QT += core gui x11extras dbus KWindowSystem svg concurrent network + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = kylin-nm +TEMPLATE = app + +CONFIG += c++14 qt warn_on link_pkgconfig +#CONFIG += release + +PKGCONFIG +=gio-2.0 glib-2.0 gio-unix-2.0 libnm libnma libsecret-1 gtk+-3.0 gsettings-qt libcap kysdk-qtwidgets + +INCLUDEPATH += /usr/include/KF5/NetworkManagerQt + +LIBS += -L/usr/lib/ -lgsettings-qt -lX11 -lKF5NetworkManagerQt -lukui-log4qt -lukui-com4c -lukui-com4cxx -lkysec +#LIBS += -lkysec +target.path = /usr/bin +target.source += $$TARGET +desktop.path = /etc/xdg/autostart/ +desktop.files = kylin-nm.desktop +gschema.files = org.ukui.kylin-nm.switch.gschema.xml +gschema.path = /usr/share/glib-2.0/schemas/ + +INSTALLS += target \ + desktop \ + gschema \ + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS WITHKYSEC + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +# QMAKE_CXXFLAGS += -Wno-unused-parameter +QMAKE_CPPFLAGS *= $(shell dpkg-buildflags --get CPPFLAGS) +QMAKE_CFLAGS *= $(shell dpkg-buildflags --get CFLAGS) +QMAKE_CXXFLAGS *= $(shell dpkg-buildflags --get CXXFLAGS) +QMAKE_LFLAGS *= $(shell dpkg-buildflags --get LDFLAGS) + +include(singleapplication/qt-single-application.pri) +include(backend/backend.pri) +include(frontend/frontend.pri) + +RESOURCES += \ + ../nmqrc.qrc + +SOURCES += \ + main.cpp + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} + +DISTFILES += \ + org.ukui.kylin-nm.switch.gschema.xml diff --git a/src/translations/kylin-nm_bo.qm b/src/translations/kylin-nm_bo.qm new file mode 100644 index 00000000..be651eed --- /dev/null +++ b/src/translations/kylin-nm_bo.qm @@ -0,0 +1 @@ +O30c@imYnTQm%qwf4zcwA$V7 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 epbz}+F~bv)K+u@P+hN?t + + + + CreatNetPage + + + Connection Name + + + + + Ipv4Config + + + + + Address + + + + + Netmask + + + + + Default Gateway + + + + + Prefs DNS + + + + + Alternative DNS + + + + + Auto(DHCP) + + + + + Manual + + + + + DetailPage + + + Auto Connection + + + + + + SSID: + + + + + Copied successfully! + + + + + Copy all + + + + + Please input SSID: + + + + + + Protocol: + + + + + + Security Type: + + + + + + Hz: + + + + + + Chan: + + + + + + BandWidth: + + + + + + IPV6: + + + + + + IPV4: + + + + + + IPV4 Dns: + + + + + + Mac: + + + + + EnterpriseWlanDialog + + + Connect Enterprise WLAN + + + + + Cancel + + + + + Connect + + + + + Ipv4Page + + + Ipv4Config + + + + + Address + + + + + Netmask + + + + + Default Gateway + + + + + Prefs DNS + + + + + Alternative DNS + + + + + Auto(DHCP) + + + + + Manual + + + + + Ipv6Page + + + Ipv6Config + + + + + Address + + + + + Subnet prefix Length + + + + + Default Gateway + + + + + Prefs DNS + + + + + Alternative DNS + + + + + Auto(DHCP) + + + + + Manual + + + + + LanListItem + + + Not connected + + + + + Wired Device not carried + + + + + + Disconnect + + + + + + Connect + + + + + LanPage + + + No ethernet device avaliable + + + + + LAN + + + + + Activated LAN + + + + + Inactivated LAN + + + + + LAN Connected Successfully + + + + + LAN Disconnected Successfully + + + + + Wired Device not carried + + + + + ListItem + + + Kylin NM + + + + + kylin network applet desktop message + + + + + MainWindow + + + + kylin-nm + + + + + LAN + + + + + WLAN + + + + + Settings + + + + + Show MainWindow + + + + + NetDetail + + + Kylin NM + + + + + kylin network desktop message + + + + + Detail + + + + + Ipv4 + + + + + Ipv6 + + + + + Security + + + + + Confirm + + + + + Cancel + + + + + Forget this network + + + + + Add Lan Connect + + + + + connect hiddin wlan + + + + + + + None + + + + + + + Auto + + + + + start check ipv4 address conflict + + + + + start check ipv6 address conflict + + + + + + + ipv4 address conflict! + + + + + + ipv6 address conflict! + + + + + this wifi no support enterprise type + + + + + this wifi no support None type + + + + + this wifi no support WPA2 type + + + + + this wifi no support WPA3 type + + + + + OneConnForm + + + Form + + + + + OneLancForm + + + Form + + + + + SecurityPage + + + Security + + + + + + Password + + + + + EAP type + + + + + Identity + + + + + Domain + + + + + CA certficate + + + + + no need for CA certificate + + + + + User certificate + + + + + User private key + + + + + User key password + + + + + Ineer authentication + + + + + Usename + + + + + Ask pwd each query + + + + + + + + + + + + + None + + + + + WPA&WPA2 Personal + + + + + WPA&WPA2 Enterprise + + + + + WPA3 Personal + + + + + + + Choose from file... + + + + + + + Choose a CA certificate + + + + + + + CA Files (*.pem *.der *.p12 *.crt *.cer *.pfx) + + + + + TabPage + + + Current Device + + + + + Devices Closed! + + + + + Settings + + + + + Kylin NM + + + + + kylin network applet desktop message + + + + + WiFiConfigDialog + + + Dialog + + + + + WLAN Authentication + + + + + Input WLAN Information Please + + + + + WLAN ID: + + + + + WLAN Name: + + + + + Password: + + + + + Cancl + + + + + Ok + + + + + WlanListItem + + + Not connected + + + + + + Disconnect + + + + + + + Connect + + + + + + Forget + + + + + Auto Connect + + + + + WlanMoreItem + + + Add Others... + + + + + WlanPage + + + WLAN + + + + + Activated WLAN + + + + + Other WLAN + + + + + No wireless network card detected + + + + + WLAN Connected Successfully + + + + + + WLAN Disconnected Successfully + + + + diff --git a/translations/kylin-nm_tr.qm b/translations/kylin-nm_tr.qm new file mode 100644 index 0000000000000000000000000000000000000000..07e2ca58f3d79cb9ff2f80155bf344fd312bf37c GIT binary patch literal 1958 zcmc&!O=uHA6n@)ow%atdRjha@xS~P}23japq#oL&KcQ(FXf;Sd9g}G~X0sc1Ck-hI z-ffSHiWgDOUW#}r)aUJJt%F^u)0A?Ov}ZC_fEU%kNqwB<%* zJOGox!GEE%;hmo^K$bJZDAphN4AX8>*u#7Y;IHtXr?a32uhZ*42ESGvLpkFxRS-E zC{9qivNZXIvkh?ZxHk};7scYr)lHJeb7hlRhDCYFc5((O6imvE40UtdE*N?0x_I)U z5`9Z2T`-6R5F||^sRe0#IylN$m;s}(E=6(8@2zcW&Mp}fgjp$OW$cpR3|6NVbEP1T z%n3hBO~nTjHPB!9^C7zIAZ+DpV5p#ZUiZPO@V5Xqqp72m6J| z!>JI4a1}LTJ5sh~(xMSOcrllJf54$B(mrk<-a%U)nmEuiKC>3J^SD)_($Ex57ahjS zzHj%T2N)&^7vwf2>9Gv8I3nw(?bu_j!bwyRev)=aH>IVcTzSYGRcEEL=d^0k?dM$T z7-{NoHp|k4%MGk0@2^WP?iGPKv?>8d$SIekkZ_fYZ&jK7QYJ^sMuE64+E(6_TAiM{ zI_t22jHb9e+Z3w`ULysE&0`Y=D%Ic6|8^IfrpXoG4MCX3R>W=3`}S#=%!!iQQ*4Bx iFK1ac%7|%ar8ocTTNX;0|NJql< + + + + BackThread + + Confirm your WLAN password + WLAN parolasını doğrula + + + + ConfForm + + edit network + Ağı düzenle + + + LAN name: + LAN adı: + + + Method: + Yöntem: + + + Address: + Adres: + + + Netmask: + Netmask: + + + DNS 1: + DNS 1: + + + DNS 2: + DNS 2: + + + Edit Conn + Bağ. Düzenle + + + Auto(DHCP) + Oto(DHCP) + + + Manual + Elle + + + Cancel + İptal + + + Save + Kaydet + + + Ok + Tamam + + + Can not create new wired network for without wired card + Kablolu kart olmadan yeni kablolu ağ oluşturulamıyor + + + New network already created + Yeni ağ zaten oluşturuldu + + + New network settings already finished + Yeni ağ ayarları zaten tamamlandı + + + kylin network applet desktop message + Kylin ağ uygulaması masaüstü mesajı + + + Edit Network + Ağı Düzenle + + + Add Wired Network + Kablolu Ağ Ekle + + + create wired network successfully + Başarıyla kablolu ağ oluşturuldu + + + change configuration of wired network successfully + Kablolu ağın yapılandırmasını başarıyla değiştirildi + + + New settings already effective + Yeni ayarlar zaten etkili + + + + CreatNetPage + + + Connection Name + + + + + Ipv4Config + + + + + Address + + + + + Netmask + + + + + Default Gateway + + + + + Prefs DNS + + + + + Alternative DNS + + + + + Auto(DHCP) + Oto(DHCP) + + + + Manual + Elle + + + + DetailPage + + + Auto Connection + + + + + + SSID: + + + + + Copied successfully! + + + + + Copy all + + + + + Please input SSID: + + + + + + Protocol: + + + + + + Security Type: + + + + + + Hz: + + + + + + Chan: + + + + + + BandWidth: + + + + + + IPV6: + + + + + + IPV4: + + + + + + IPV4 Dns: + + + + + + Mac: + + + + + DlgHideWifi + + Add Hidden WLAN + Gizli WLAN Ekle + + + Connection + Bağlantı + + + WLAN name + WLAN adı + + + WLAN security + WLAN güvenlik + + + Cancel + İptal + + + Connect + Bağlantı + + + C_reate… + Oluştur... + + + None + Yok + + + Conn WLAN Success + WLAN Bağlantısı Başarılı + + + Confirm your WLAN password or usable of wireless card + Kablosuz şifrenizi veya kablosuz kart kullanılabilirliğini onaylayın + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + WEP 128-bit Passphrase + WEP 128-bit Passphrase + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + + DlgHideWifiEapFast + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + Add hidden WLAN + Gizli WLAN Ekle + + + Connection + Bağlantı: + + + Network name + Ağ adı + + + WLAN security + WLAN güvenliği + + + Authentication + Kimlik Doğrulama + + + Anonymous identity + Anonim kimlik + + + Allow automatic PAC pro_visioning + Otomatik PAC pro_visioning'e izin ver + + + PAC file + PAC dosyası + + + Inner authentication + İç kimlik doğrulama: + + + Username + Kullanıcı adı + + + Password + Parola + + + Cancel + İptal + + + Connect + Bağlan + + + C_reate… + Oluştur... + + + None + Yok + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + WEP 128-bit Passphrase + WEP 128-bit Passphrase + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + Tunneled TLS + Tünelli TLS + + + Protected EAP (PEAP) + Korumalı EAP (PEAP) + + + Anonymous + Anonim + + + Authenticated + Doğrulanmış + + + Both + Her ikisi de + + + + DlgHideWifiEapLeap + + Connect to Hidden WLAN Network + Gizli WLAN ağına bağlan + + + Add hidden WLAN + Gizli WLAN ekle + + + Connection + Bağlantı: + + + Network name + Ağ adı: + + + WLAN security + WLAN güvenliği: + + + Authentication + Kimlik Doğrulama: + + + Username + Kullanıcı adı: + + + Password + Parola: + + + Cancel + 取消 + + + Connect + Bağlan + + + C_reate… + Oluştur... + + + None + Yok + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + WEP 128-bit Passphrase + WEP 128-bit Passphrase + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + Tunneled TLS + Tünelli TLS + + + Protected EAP (PEAP) + Korumalı EAP (PEAP) + + + + DlgHideWifiEapPeap + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + Add hidden WLAN + Gizli WLAN ekle + + + Connection + Bağlantı: + + + Network name + Ağ adı: + + + WLAN security + WLAN güvenliği: + + + Authentication + Kimlik Doğrulama: + + + Anonymous identity + Anonim kimlik: + + + Domain + Domain: + + + CA certificate + CA sertifikası: + + + CA certificate password + CA sertifika şifresi: + + + No CA certificate is required + CA sertifikası gerekmez + + + PEAP version + PEAP sürümü: + + + Inner authentication + İç kimlik doğrulama: + + + Username + Kullanıcı adı: + + + Password + Parola: + + + Cancel + İptal + + + Connect + Bağlan + + + None + Yok + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + WEP 128-bit Passphrase + WEP 128-bit Passphrase + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + Tunneled TLS + Tünelli TLS + + + Protected EAP (PEAP) + Korumalı EAP (PEAP) + + + Choose from file + Dosyadan seçin... + + + Automatic + Otomatik + + + Version 0 + Sürüm 0 + + + Version 1 + Sürüm 1 + + + + DlgHideWifiEapPwd + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + Add hidden WLAN + Gizli WLAN ekle + + + Connection + Bağlantı: + + + Network name + Ağ adı: + + + WLAN security + WLAN güvenliği: + + + Authentication + Kimlik Doğrulama: + + + Username + Kullanıcı adı: + + + Password + Parola: + + + Cancel + İptal + + + Connect + Bağlan + + + C_reate… + Oluştur... + + + None + Yok + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + WEP 128-bit Passphrase + WEP 128-bit Passphrase + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + Tunneled TLS + Tünelli TLS + + + Protected EAP (PEAP) + Korumalı EAP (PEAP) + + + + DlgHideWifiEapTTLS + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + Add hidden WLAN + Gizli WLAN ekle + + + Connection + Bağlantı: + + + Network name + Ağ adı: + + + WLAN security + WLAN Güvenliği: + + + Authentication + Kimlik Doğrulama: + + + Anonymous identity + Anonim kimlik: + + + Domain + Domain: + + + CA certificate + CA Sertifikası: + + + CA certificate password + CA sertifika şifresi: + + + No CA certificate is required + CA sertifikası gerekmez + + + Inner authentication + İç kimlik doğrulama: + + + Username + Kullanıcı adı: + + + Password + Parola: + + + Cancel + İptal + + + Connect + Bağlan + + + C_reate… + Oluştur... + + + None + Yok + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + WEP 128-bit Passphrase + WEP 128-bit Passphrase + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + Tunneled TLS + Tünelli TLS + + + Protected EAP (PEAP) + Korumalı EAP (PEAP) + + + Choose from file + Dosyadan seçiniz... + + + + DlgHideWifiEapTls + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + Add hidden WLAN + Gizli WLAN ekle + + + Connection + Bağlantı: + + + Network name + Ağ adı: + + + WLAN security + WLAN güvenliği: + + + Authentication + Kimlik Doğrulama: + + + Identity + Kimlik: + + + Domain + Domain: + + + CA certificate + CA sertifikası: + + + CA certificate password + CA sertifika şifresi: + + + No CA certificate is required + CA sertifikası gerekmez + + + User certificate + Kullanıcı sertifikası: + + + User certificate password + Kullanıcı sertifikası şifresi: + + + User private key + Kullanıcı özel anahtarı: + + + User key password + Kullanıcı anahtarı şifresi: + + + Cancel + İptal + + + Connect + Bağlan + + + C_reate… + Oluştur... + + + None + Yok + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + WEP 128-bit Passphrase + WEP 128-bit Passphrase + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + Tunneled TLS + Tünelli TLS + + + Protected EAP (PEAP) + Korumalı EAP (PEAP) + + + Choose from file + Dosyadan seç... + + + + DlgHideWifiLeap + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + Add hidden WLAN + Gizli WLAN Ekle + + + Connection + Bağlantı + + + Network name + Ağ adı + + + WLAN security + WLAN Güvenlik + + + Username + Kullanıcı adı + + + Password + Parola + + + Cancel + İptal + + + Connect + Bağlan + + + C_reate… + Oluştur... + + + None + Yok + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + WEP 128-bit Passphrase + WEP 128-bit Passphrase + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1X) + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + + DlgHideWifiWep + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + Add hidden WLAN + Gizli WLAN Ekle + + + Connection + Bağlantı: + + + Network name + Ağ adı: + + + WLAN security + WLAN Güvenliği: + + + Key + Anahtar + + + WEP index + WEP index + + + Authentication + Kimlik Doğrulama: + + + Cancel + İptal + + + Connect + Bağlan + + + C_reate… + Oluştur... + + + None + Yok + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + WEP 128-bit Passphrase + WEP 128-bit Passphrase + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + 1(default) + 1(default) + + + Open System + Sistemi aç + + + Shared Key + Paylaşılan Anahtar + + + + DlgHideWifiWpa + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + Add Hidden WLAN + Gizli WLAN Ekle + + + Connection + Bağlantı: + + + WLAN name + WLAN adı: + + + WLAN security + WLAN güvenlik: + + + Password + Parola: + + + Cancel + İptal + + + Connect + Bağlan + + + C_reate… + Oluştur... + + + None + Yok + + + Conn WLAN Success + WLAN Bağlantısı Başarılı + + + Confirm your WLAN password or usable of wireless card + Kablosuz şifrenizi veya kablosuz kart kullanılabilirliğini onaylayın + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + WPA & WPA2 Enterprise + WPA & WPA2 Enterprise + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128-bit Key (Hex veya ASCII) + + + Dynamic WEP (802.1X) + Dinamik WEP (802.1x) + + + + DlgHotspotCreate + + Create Hotspot + Etkin Nokta Oluştur + + + Network name + Ağ adı: + + + WLAN security + WLAN Güvenlik: + + + Password + Parola: + + + Cancel + İptal + + + Ok + Tamam + + + None + Yok + + + WPA & WPA2 Personal + WPA & WPA2 Kişisel + + + + EnterpriseWlanDialog + + + Connect Enterprise WLAN + + + + + Cancel + + + + + Connect + + + + + Ipv4Page + + + Ipv4Config + + + + + Address + + + + + Netmask + + + + + Default Gateway + + + + + Prefs DNS + + + + + Alternative DNS + + + + + Auto(DHCP) + Oto(DHCP) + + + + Manual + Elle + + + + Ipv6Page + + + Ipv6Config + + + + + Address + + + + + Subnet prefix Length + + + + + Default Gateway + + + + + Prefs DNS + + + + + Alternative DNS + + + + + Auto(DHCP) + Oto(DHCP) + + + + Manual + Elle + + + + KylinDBus + + kylin network applet desktop message + Kylin ağ uygulaması masaüstü mesajı + + + + LanListItem + + + Not connected + Bağlanamadı + + + + Wired Device not carried + + + + + + Disconnect + Bağlantıyı Kes + + + + + Connect + + + + + LanPage + + + No ethernet device avaliable + + + + + LAN + + + + + Activated LAN + + + + + Inactivated LAN + + + + + LAN Connected Successfully + + + + + LAN Disconnected Successfully + + + + + Wired Device not carried + + + + + ListItem + + + Kylin NM + + + + + kylin network applet desktop message + Kylin ağ uygulaması masaüstü mesajı + + + + MainWindow + + + + kylin-nm + + + + Network + + + + Advanced + Gelişmiş + + + Ethernet + Kablolu Ağ + + + Connect Hide Network + Gizli Ağı Bağlan + + + + LAN + + + + + WLAN + WLAN + + + + Settings + + + + Enabled + Aktif + + + Disabled + Pasif + + + HotSpot + HotSpot + + + + Show MainWindow + Ana Pencereyi Göster + + + Not connected + Bağlanamadı + + + Disconnected + Bağlantı Kesildi + + + No Other Wired Network Scheme + Başka Kablolu Ağ Düzeni Yok + + + No Other Wireless Network Scheme + Başka Kablosuz Ağ Düzeni Yok + + + Wired net is disconnected + Kablolu ağ bağlantısı kesildi + + + WLAN is disconnected + Kablosuz bağlantı kesildi + + + Confirm your WLAN password or usable of wireless card + Kablosuz şifrenizi veya kablosuz kart kullanılabilirliğini onaylayın + + + Confirm your WLAN password + WLAN parolasını doğrula + + + Ethernet Networks + Ethernet Ağları + + + New LAN + Yeni LAN + + + Hide WLAN + Gizli WLAN + + + No usable network in the list + Listede kullanılabilir ağ yok + + + WLAN Networks + WLAN Ağları + + + None + Yok + + + keep wired network switch is on before turning on wireless switch + Kablosuz anahtarı açmadan önce kablolu ağ anahtarını açık tut + + + please insert the wireless network adapter + Lütfen kablosuz ağ adaptörünü takın + + + Abnormal connection exist, program will delete it + Anormal bağlantı var, program onu ​​silecek + + + update WLAN list now, click again + Kablosuz listesini şimdi güncelle, tekrar tıkla + + + update WLAN list now + Kablosuz listesini şimdi güncelle + + + Conn Ethernet Success + Ethernet Bağlantısı Başarılı + + + Conn Ethernet Fail + Ethernet Bağlantısı Hatası + + + Conn WLAN Success + WLAN Bağlantısı Başarılı + + + + NetDetail + + + Kylin NM + + + + + kylin network desktop message + + + + + Detail + + + + + Ipv4 + + + + + Ipv6 + + + + + Security + + + + + Confirm + + + + + Cancel + + + + + Forget this network + + + + + Add Lan Connect + + + + + connect hiddin wlan + + + + + + + None + Yok + + + + + + Auto + Oto + + + + start check ipv4 address conflict + + + + + start check ipv6 address conflict + + + + + + + ipv4 address conflict! + + + + + + ipv6 address conflict! + + + + + this wifi no support enterprise type + + + + + this wifi no support None type + + + + + this wifi no support WPA2 type + + + + + this wifi no support WPA3 type + + + + + NotifySend + + Form + -- + + + + OldMainWindow + + Ethernet Networks + Ethernet Ağları + + + New LAN + Yeni LAN + + + WLAN Networks + WLAN Ağları + + + Hide WLAN + Gizli WLAN + + + No usable network in the list + Listede kullanılabilir ağ yok + + + Ethernet + Kablolu Ağ + + + HotSpot + HotSpot + + + Advanced + Gelişmiş + + + Show MainWindow + Ana Pencereyi Göster + + + Wired net is disconnected + Kablolu ağ bağlantısı kesildi + + + Not connected + Bağlanamadı + + + Disconnected + Bağlantı Kesildi + + + No Other Wired Network Scheme + Başka Kablolu Ağ Düzeni Yok + + + No Other Wireless Network Scheme + Başka Kablosuz Ağ Düzeni Yok + + + WLAN is disconnected + Kablosuz bağlantı kesildi + + + Conn Ethernet Success + Ethernet Bağlantısı Başarılı + + + Conn WLAN Success + WLAN Bağlantısı Başarılı + + + Confirm your WLAN password or usable of wireless card + Kablosuz şifrenizi veya kablosuz kart kullanılabilirliğini onaylayın + + + Confirm your WLAN password + WLAN parolasını doğrula + + + + OneConnForm + + + Form + -- + + + Input password + Parola gir + + + Config + Ayar + + + Connect + Bağlan + + + Disconnect + Bağlantıyı Kes + + + Input Password... + Parola gir... + + + Connect to Hidden WLAN Network + Gizli WLAN Ağına Bağlan + + + Public + Halka açık + + + Safe + Güvenli + + + Rate + Oran + + + None + Yok + + + WLAN Security: + WLAN güvenliği: + + + Sifnal: + Sinyal gücü: + + + MAC: + Fiziksel adres: + + + Conn WLAN Success + WLAN Bağlantısı Başarılı + + + Confirm your WLAN password or usable of wireless card + Kablosuz şifrenizi veya kablosuz kart kullanılabilirliğini onaylayın + + + Conn WLAN Failed + WLAN Bağlantısı Başarısız + + + + OneLancForm + + + Form + -- + + + Config + Ayar + + + Connect + Bağlan + + + Disconnect + Bağlantıyı Kes + + + Not connected + Bağlanamadı + + + Disconnected + Bağlantı Kesildi + + + Ethernet + Kablolu Ağ + + + No Configuration + Yapılandırma Yok + + + IPv4: + IPv4 adresi: + + + IPv6: + IPv6 adresi: + + + BandWidth: + Bant genişliği: + + + MAC: + Fiziksel adres: + + + Auto + Oto + + + + SecurityPage + + + Security + + + + + + Password + + + + + EAP type + + + + + Identity + Kimlik: + + + + Domain + Domain: + + + + CA certficate + + + + + no need for CA certificate + + + + + User certificate + Kullanıcı sertifikası: + + + + User private key + Kullanıcı özel anahtarı: + + + + User key password + Kullanıcı anahtarı şifresi: + + + + Ineer authentication + + + + + Usename + + + + + Ask pwd each query + + + + + + + + + + + + + None + Yok + + + + WPA&WPA2 Personal + + + + + WPA&WPA2 Enterprise + + + + + WPA3 Personal + + + + + + + Choose from file... + + + + + + + Choose a CA certificate + + + + + + + CA Files (*.pem *.der *.p12 *.crt *.cer *.pfx) + + + + + TabPage + + + Current Device + + + + + Devices Closed! + + + + + Settings + + + + + Kylin NM + + + + + kylin network applet desktop message + Kylin ağ uygulaması masaüstü mesajı + + + + Utils + + kylin network applet desktop message + Kylin ağ uygulaması masaüstü mesajı + + + + WiFiConfigDialog + + + Dialog + + + + + WLAN Authentication + + + + + Input WLAN Information Please + + + + + WLAN ID: + + + + + WLAN Name: + + + + + Password: + + + + + Cancl + + + + + Ok + Tamam + + + + WlanListItem + + + Not connected + Bağlanamadı + + + + + Disconnect + Bağlantıyı Kes + + + + + + Connect + + + + + + Forget + + + + + Auto Connect + + + + + WlanMoreItem + + + Add Others... + + + + + WlanPage + + + WLAN + WLAN + + + + Activated WLAN + + + + + Other WLAN + + + + + No wireless network card detected + + + + + WLAN Connected Successfully + + + + + + WLAN Disconnected Successfully + + + + + WpaWifiDialog + + Identity + Kimlik: + + + Domain + Domain: + + + None + Yok + + + diff --git a/translations/kylin-nm_zh_CN.qm b/translations/kylin-nm_zh_CN.qm new file mode 100644 index 0000000000000000000000000000000000000000..8164222b63120fa4b5e4da0f23e402b47488557a GIT binary patch literal 7744 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 literal 0 HcmV?d00001 diff --git a/translations/kylin-nm_zh_CN.ts b/translations/kylin-nm_zh_CN.ts new file mode 100644 index 00000000..6a9e870d --- /dev/null +++ b/translations/kylin-nm_zh_CN.ts @@ -0,0 +1,1180 @@ + + + + + ConfForm + + Manual + 手动 + + + Cancel + 取消 + + + kylin-nm + 网络工具 + + + kylin network applet desktop message + 网络提示消息 + + + Will check the IP address conflict + 正在检测ip地址冲突 + + + IPV4 address conflict, Please change IP + ip地址冲突,请更改ip + + + IPV6 address conflict, Please change IP + ip地址冲突,请更改ip {6 ?} + + + + CopyButton + + Copied successfully + 复制成功 + + + Copied successfully! + 复制成功! + + + Copy all + 复制全部 + + + + CreatNetPage + + + Connection Name + 网络名称 + + + + Ipv4Config + IPV4配置 + + + + Address + IPV4地址 + + + + Netmask + 子网掩码 + + + + Default Gateway + 默认网关 + + + + Prefs DNS + 首选DNS + + + + Alternative DNS + 备选DNS + + + + Auto(DHCP) + 自动(DHCP) + + + + Manual + 手动 + + + + DetailPage + + + Auto Connection + 自动连接 + + + + + SSID: + SSID: + + + + Copied successfully! + 复制成功! + + + + Copy all + 复制全部 + + + + Please input SSID: + 请输入SSID: + + + + + Protocol: + 协议: + + + + + Security Type: + 安全类型: + + + + + Hz: + 网络频带: + + + + + Chan: + 网络通道: + + + + + BandWidth: + 带宽: + + + + + IPV6: + 本地链接IPV6地址: + + + + + IPV4: + IPV4地址: + + + + + IPV4 Dns: + IPV4 DNS服务器: + + + + + Mac: + 物理地址: + + + + DlgHideWifi + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + DlgHideWifiEapFast + + Username + 用户名 + + + Password + 密钥 + + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + DlgHideWifiEapLeap + + Username + 用户名 + + + Password + 密钥 + + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + DlgHideWifiEapPeap + + Domain + + + + Username + 用户名 + + + Password + 密钥 + + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + DlgHideWifiEapPwd + + Username + 用户名 + + + Password + 密钥 + + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + DlgHideWifiEapTTLS + + Domain + + + + Username + 用户名 + + + Password + 密钥 + + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + DlgHideWifiEapTls + + Identity + 匿名身份 + + + Domain + + + + User certificate + 用户证书 + + + User private key + 用户私钥 + + + User key password + 用户密钥密码 + + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + DlgHideWifiLeap + + Username + 用户名 + + + Password + 密钥 + + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + DlgHideWifiWep + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + DlgHideWifiWpa + + Password + 密钥 + + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + + EnterpriseWlanDialog + + + Connect Enterprise WLAN + 连接企业网 + + + Close + 关闭 + + + + Cancel + 取消 + + + + Connect + 连接 + + + + Ipv4Page + + + Ipv4Config + Ipv4配置 + + + + Address + 地址 + + + + Netmask + 子网掩码 + + + + Default Gateway + 默认网关 + + + + Prefs DNS + 首选DNS + + + + Alternative DNS + 备选DNS + + + + Auto(DHCP) + 自动 + + + + Manual + 手动 + + + + Ipv6Page + + + Ipv6Config + Ipv6配置 + + + + Address + 地址 + + + + Subnet prefix Length + 子网前缀长度 + + + + Default Gateway + 默认网关 + + + + Prefs DNS + 首选DNS + + + + Alternative DNS + 备选DNS + + + + Auto(DHCP) + 自动 + + + + Manual + 手动 + + + + LanListItem + + + Not connected + 未连接 + + + + Wired Device not carried + 未插入网线 + + + + + Disconnect + 断开 + + + + + Connect + 连接 + + + + LanPage + + + No ethernet device avaliable + 未检测到有线设备 + + + + LAN + 有线网络 + + + + Activated LAN + 我的网络 + + + + Inactivated LAN + 其他网络 + + + + LAN Disconnected Successfully + 有线网络已断开 + + + + Wired Device not carried + 未插入网线 + + + + LAN Connected Successfully + 有线网络已连接 + + + + ListItem + + + Kylin NM + 麒麟网络设置工具 + + + + kylin network applet desktop message + 网络提示消息 + + + + MainWindow + + + + kylin-nm + 网络工具 + + + + LAN + 有线网络 + 有线网络 + + + + WLAN + 无线局域网 + 无线局域网 + + + + Show MainWindow + 打开网络工具 + + + + Settings + 设置网络项 + 设置网络项 + + + + NetDetail + + + Kylin NM + 麒麟网络设置工具 + + + + kylin network desktop message + 网络提示消息 + + + + Detail + 详情 + + + + Ipv4 + Ipv4 + + + + Ipv6 + Ipv6 + + + + Security + 安全 + + + Close + 关闭 + + + + Confirm + 确定 + + + + Cancel + 取消 + + + + Forget this network + 忘记此网络 + + + + Add Lan Connect + 添加有线网络 + + + + connect hiddin wlan + 连接到隐藏WLAN + + + + + + None + + + + + + + Auto + 自动 + + + + start check ipv4 address conflict + 开始检测ipv4地址冲突 + + + + start check ipv6 address conflict + 开始检测ipv6地址冲突 + + + + + + ipv4 address conflict! + ipv4地址冲突! + + + + + ipv6 address conflict! + ipv6地址冲突! + + + + this wifi no support enterprise type + 此wifi不支持企业网类型 + + + + this wifi no support None type + 此wifi不支持空类型 + + + + this wifi no support WPA2 type + 此wifi不支持WPA2类型 + + + + this wifi no support WPA3 type + 此wifi不支持WPA3类型 + + + SSID: + SSID: + + + Protocol: + 协议: + + + Hz: + 网络频带: + + + Chan: + 网络通道: + + + BandWidth: + 带宽: + + + IPV4: + IPV4地址: + + + IPV4 Dns: + IPV4 DNS服务器: + + + IPV6: + 本地链接IPV6地址: + + + Mac: + 物理地址: + + + + OldMainWindow + + kylin-nm + 网络工具 + + + Show MainWindow + 打开网络工具 + + + Not connected + 未连接 + + + + OneConnForm + + + Form + + + + Connect + 连接 + + + Disconnect + 断开 + + + Cancel + 取消 + + + Forget + 忘记此网络 + + + None + + + + + OneLancForm + + + Form + + + + Connect + 连接 + + + Disconnect + 断开 + + + Cancel + 取消 + + + Not connected + 未连接 + + + + SecurityPage + + + Security + 安全性 + + + + + Password + 密钥 + + + + EAP type + EAP方法 + + + + Identity + 匿名身份 + + + + Domain + + + + + CA certficate + CA 证书 + + + + no need for CA certificate + 不需要CA证书 + + + + User certificate + 用户证书 + + + + User private key + 用户私钥 + + + + User key password + 用户密钥密码 + + + + Ineer authentication + 内部认证 + + + + Usename + 用户名 + + + Username + Usename + 用户名 + + + + Ask pwd each query + 每次询问密码 + + + + + + + + + + + + None + + + + + WPA&WPA2 Personal + WPA&WPA2 个人 + + + + WPA&WPA2 Enterprise + WPA&WPA2 企业 + + + + WPA3 Personal + WPA3 个人 + + + + + + Choose from file... + 从文件选择... + + + + + + Choose a CA certificate + 选择一个CA证书 + + + + + + CA Files (*.pem *.der *.p12 *.crt *.cer *.pfx) + CA 证书 (*.pem *.der *.p12 *.crt *.cer *.pfx) + + + + TabPage + + + Current Device + 当前网卡 + + + + Devices Closed! + 设备关闭! + + + + Settings + 网络设置 + + + + Kylin NM + 麒麟网络设置工具 + + + + kylin network applet desktop message + 网络提示消息 + + + + WiFiConfigDialog + + + Dialog + + + + + WLAN Authentication + + + + + Input WLAN Information Please + + + + + WLAN ID: + + + + + WLAN Name: + + + + + Password: + + + + + Cancl + + + + + Ok + + + + + WlanListItem + + + Not connected + 未连接 + + + + + Disconnect + 断开 + + + + + + Connect + 连接 + + + + + Forget + 忘记此网络 + + + + Auto Connect + 自动加入该网络 + + + + WlanMoreItem + + More... + 更多... + + + + Add Others... + 加入其他网络... + + + + WlanPage + + + WLAN + 无线局域网 + + + + No wireless network card detected + 未检测到无线网卡 + + + + Activated WLAN + 我的网络 + + + + Other WLAN + 其他网络 + + + More... + 更多... + + + + WLAN Connected Successfully + 无线网络已连接 + + + + + WLAN Disconnected Successfully + 无线网络已断开 + + + + WpaWifiDialog + + EAP type + EAP方法 + + + Username + 用户名 + + + Password + 密钥 + + + Ask pwd each query + 每次询问密码 + + + Cancel + 取消 + + + Connect + 连接 + + + None + + + + Choose from file... + 从文件选择... + + + Choose a CA certificate + 选择一个CA证书 + + + CA Files (*.pem *.der *.p12 *.crt *.cer *.pfx) + CA 证书 (*.pem *.der *.p12 *.crt *.cer *.pfx) + + + Identity + 匿名身份 + + + Domain + + + + no need for CA certificate + 不需要CA证书 + + +