1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2015 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing the feature permission dialog. 8""" 9 10from PyQt5.QtCore import pyqtSlot, Qt 11from PyQt5.QtWidgets import ( 12 QDialog, QTreeWidgetItem, QTreeWidget, QAbstractItemView 13) 14from PyQt5.QtWebEngineWidgets import QWebEnginePage 15 16import UI.PixmapCache 17 18from .Ui_FeaturePermissionsDialog import Ui_FeaturePermissionsDialog 19 20 21class FeaturePermissionsDialog(QDialog, Ui_FeaturePermissionsDialog): 22 """ 23 Class implementing the feature permission dialog. 24 """ 25 def __init__(self, featurePermissions, parent=None): 26 """ 27 Constructor 28 29 @param featurePermissions dictionary with remembered feature 30 permissions 31 @type dict of dict of list 32 @param parent reference to the parent widget 33 @type QWidget 34 """ 35 super().__init__(parent) 36 self.setupUi(self) 37 38 # add the various lists 39 40 if hasattr(QWebEnginePage, "Notifications"): 41 # this was re-added in Qt 5.13.0 42 self.notifList = QTreeWidget() 43 self.notifList.setAlternatingRowColors(True) 44 self.notifList.setSelectionMode( 45 QAbstractItemView.SelectionMode.ExtendedSelection) 46 self.notifList.setRootIsDecorated(False) 47 self.notifList.setItemsExpandable(False) 48 self.notifList.setAllColumnsShowFocus(True) 49 self.notifList.setObjectName("notifList") 50 self.notifList.setSortingEnabled(True) 51 self.notifList.headerItem().setText(0, self.tr("Host")) 52 self.notifList.headerItem().setText(1, self.tr("Permission")) 53 self.tabWidget.addTab( 54 self.notifList, 55 UI.PixmapCache.getIcon("notification"), 56 self.tr("Notifications")) 57 58 self.geoList = QTreeWidget() 59 self.geoList.setAlternatingRowColors(True) 60 self.geoList.setSelectionMode( 61 QAbstractItemView.SelectionMode.ExtendedSelection) 62 self.geoList.setRootIsDecorated(False) 63 self.geoList.setItemsExpandable(False) 64 self.geoList.setAllColumnsShowFocus(True) 65 self.geoList.setObjectName("geoList") 66 self.geoList.setSortingEnabled(True) 67 self.geoList.headerItem().setText(0, self.tr("Host")) 68 self.geoList.headerItem().setText(1, self.tr("Permission")) 69 self.tabWidget.addTab( 70 self.geoList, 71 UI.PixmapCache.getIcon("geolocation"), 72 self.tr("Geolocation")) 73 74 self.micList = QTreeWidget() 75 self.micList.setAlternatingRowColors(True) 76 self.micList.setSelectionMode( 77 QAbstractItemView.SelectionMode.ExtendedSelection) 78 self.micList.setRootIsDecorated(False) 79 self.micList.setItemsExpandable(False) 80 self.micList.setAllColumnsShowFocus(True) 81 self.micList.setObjectName("micList") 82 self.micList.setSortingEnabled(True) 83 self.micList.headerItem().setText(0, self.tr("Host")) 84 self.micList.headerItem().setText(1, self.tr("Permission")) 85 self.tabWidget.addTab( 86 self.micList, 87 UI.PixmapCache.getIcon("audiocapture"), 88 self.tr("Microphone")) 89 90 self.camList = QTreeWidget() 91 self.camList.setAlternatingRowColors(True) 92 self.camList.setSelectionMode( 93 QAbstractItemView.SelectionMode.ExtendedSelection) 94 self.camList.setRootIsDecorated(False) 95 self.camList.setItemsExpandable(False) 96 self.camList.setAllColumnsShowFocus(True) 97 self.camList.setObjectName("camList") 98 self.camList.setSortingEnabled(True) 99 self.camList.headerItem().setText(0, self.tr("Host")) 100 self.camList.headerItem().setText(1, self.tr("Permission")) 101 self.tabWidget.addTab( 102 self.camList, 103 UI.PixmapCache.getIcon("camera"), 104 self.tr("Camera")) 105 106 self.micCamList = QTreeWidget() 107 self.micCamList.setAlternatingRowColors(True) 108 self.micCamList.setSelectionMode( 109 QAbstractItemView.SelectionMode.ExtendedSelection) 110 self.micCamList.setRootIsDecorated(False) 111 self.micCamList.setItemsExpandable(False) 112 self.micCamList.setAllColumnsShowFocus(True) 113 self.micCamList.setObjectName("micCamList") 114 self.micCamList.setSortingEnabled(True) 115 self.micCamList.headerItem().setText(0, self.tr("Host")) 116 self.micCamList.headerItem().setText(1, self.tr("Permission")) 117 self.tabWidget.addTab( 118 self.micCamList, 119 UI.PixmapCache.getIcon("audio-video"), 120 self.tr("Microphone && Camera")) 121 122 self.mouseLockList = QTreeWidget() 123 self.mouseLockList.setAlternatingRowColors(True) 124 self.mouseLockList.setSelectionMode( 125 QAbstractItemView.SelectionMode.ExtendedSelection) 126 self.mouseLockList.setRootIsDecorated(False) 127 self.mouseLockList.setItemsExpandable(False) 128 self.mouseLockList.setAllColumnsShowFocus(True) 129 self.mouseLockList.setObjectName("mouseLockList") 130 self.mouseLockList.setSortingEnabled(True) 131 self.mouseLockList.headerItem().setText(0, self.tr("Host")) 132 self.mouseLockList.headerItem().setText(1, self.tr("Permission")) 133 self.tabWidget.addTab( 134 self.mouseLockList, 135 UI.PixmapCache.getIcon("mouse"), 136 self.tr("Mouse Lock")) 137 138 if hasattr(QWebEnginePage, "DesktopVideoCapture"): 139 # these are shown as of Qt 5.10.0/PyQt 5.10.0 140 self.deskVidList = QTreeWidget() 141 self.deskVidList.setAlternatingRowColors(True) 142 self.deskVidList.setSelectionMode( 143 QAbstractItemView.SelectionMode.ExtendedSelection) 144 self.deskVidList.setRootIsDecorated(False) 145 self.deskVidList.setItemsExpandable(False) 146 self.deskVidList.setAllColumnsShowFocus(True) 147 self.deskVidList.setObjectName("deskVidList") 148 self.deskVidList.setSortingEnabled(True) 149 self.deskVidList.headerItem().setText(0, self.tr("Host")) 150 self.deskVidList.headerItem().setText(1, self.tr("Permission")) 151 self.tabWidget.addTab( 152 self.deskVidList, 153 UI.PixmapCache.getIcon("desktopVideoCapture"), 154 self.tr("Desktop Video")) 155 156 self.deskAudVidList = QTreeWidget() 157 self.deskAudVidList.setAlternatingRowColors(True) 158 self.deskAudVidList.setSelectionMode( 159 QAbstractItemView.SelectionMode.ExtendedSelection) 160 self.deskAudVidList.setRootIsDecorated(False) 161 self.deskAudVidList.setItemsExpandable(False) 162 self.deskAudVidList.setAllColumnsShowFocus(True) 163 self.deskAudVidList.setObjectName("deskAudVidList") 164 self.deskAudVidList.setSortingEnabled(True) 165 self.deskAudVidList.headerItem().setText(0, self.tr("Host")) 166 self.deskAudVidList.headerItem().setText(1, self.tr("Permission")) 167 self.tabWidget.addTab( 168 self.deskAudVidList, 169 UI.PixmapCache.getIcon("desktopAudioVideoCapture"), 170 self.tr("Desktop Audio && Video")) 171 172 if hasattr(QWebEnginePage, "Notifications"): 173 self.setTabOrder(self.tabWidget, self.notifList) 174 self.setTabOrder(self.notifList, self.geoList) 175 else: 176 self.setTabOrder(self.tabWidget, self.geoList) 177 self.setTabOrder(self.geoList, self.micList) 178 self.setTabOrder(self.micList, self.camList) 179 self.setTabOrder(self.camList, self.micCamList) 180 self.setTabOrder(self.micCamList, self.mouseLockList) 181 if hasattr(QWebEnginePage, "DesktopVideoCapture"): 182 self.setTabOrder(self.mouseLockList, self.deskVidList) 183 self.setTabOrder(self.deskVidList, self.deskAudVidList) 184 self.setTabOrder(self.deskAudVidList, self.removeButton) 185 else: 186 self.setTabOrder(self.mouseLockList, self.removeButton) 187 self.setTabOrder(self.removeButton, self.removeAllButton) 188 189 self.__permissionStrings = { 190 QWebEnginePage.PermissionPolicy.PermissionGrantedByUser: 191 self.tr("Allow"), 192 QWebEnginePage.PermissionPolicy.PermissionDeniedByUser: 193 self.tr("Deny"), 194 } 195 196 self.__permissionsLists = { 197 QWebEnginePage.Feature.Geolocation: self.geoList, 198 QWebEnginePage.Feature.MediaAudioCapture: self.micList, 199 QWebEnginePage.Feature.MediaVideoCapture: self.camList, 200 QWebEnginePage.Feature.MediaAudioVideoCapture: self.micCamList, 201 QWebEnginePage.Feature.MouseLock: self.mouseLockList, 202 } 203 if hasattr(QWebEnginePage, "DesktopVideoCapture"): 204 self.__permissionsLists.update({ 205 QWebEnginePage.Feature.DesktopVideoCapture: 206 self.deskVidList, 207 QWebEnginePage.Feature.DesktopAudioVideoCapture: 208 self.deskAudVidList, 209 }) 210 if hasattr(QWebEnginePage, "Notifications"): 211 self.__permissionsLists[QWebEnginePage.Feature.Notifications] = ( 212 self.notifList 213 ) 214 215 for feature, permissionsList in self.__permissionsLists.items(): 216 for permission in featurePermissions[feature]: 217 for host in featurePermissions[feature][permission]: 218 itm = QTreeWidgetItem( 219 permissionsList, 220 [host, self.__permissionStrings[permission]]) 221 itm.setData(0, Qt.ItemDataRole.UserRole, permission) 222 223 self.__previousCurrent = -1 224 self.tabWidget.currentChanged.connect(self.__currentTabChanged) 225 self.tabWidget.setCurrentIndex(0) 226 227 @pyqtSlot(int) 228 def __currentTabChanged(self, index): 229 """ 230 Private slot handling changes of the selected tab. 231 232 @param index index of the current tab 233 @type int 234 """ 235 if self.__previousCurrent >= 0: 236 previousList = self.tabWidget.widget(self.__previousCurrent) 237 previousList.itemSelectionChanged.disconnect( 238 self.__itemSelectionChanged) 239 240 self.__updateButtons() 241 242 currentList = self.tabWidget.currentWidget() 243 currentList.itemSelectionChanged.connect(self.__itemSelectionChanged) 244 self.__previousCurrent = index 245 246 def __updateButtons(self): 247 """ 248 Private method to update the buttons. 249 """ 250 currentList = self.tabWidget.currentWidget() 251 self.removeAllButton.setEnabled( 252 currentList.topLevelItemCount() > 0) 253 self.removeButton.setEnabled( 254 len(currentList.selectedItems()) > 0) 255 256 @pyqtSlot() 257 def __itemSelectionChanged(self): 258 """ 259 Private slot handling changes in the current list of selected items. 260 """ 261 self.__updateButtons() 262 263 @pyqtSlot() 264 def on_removeButton_clicked(self): 265 """ 266 Private slot to remove selected entries. 267 """ 268 currentList = self.tabWidget.currentWidget() 269 for itm in currentList.selectedItems(): 270 row = currentList.indexOfTopLevelItem(itm) 271 itm = currentList.takeTopLevelItem(row) 272 del itm 273 self.__updateButtons() 274 275 @pyqtSlot() 276 def on_removeAllButton_clicked(self): 277 """ 278 Private slot to remove all entries. 279 """ 280 currentList = self.tabWidget.currentWidget() 281 while currentList.topLevelItemCount() > 0: 282 itm = currentList.takeTopLevelItem(0) # __IGNORE_WARNING__ 283 del itm 284 self.__updateButtons() 285 286 def getData(self): 287 """ 288 Public method to retrieve the dialog contents. 289 290 @return new feature permission settings 291 @rtype dict of dict of list 292 """ 293 featurePermissions = {} 294 for feature, permissionsList in self.__permissionsLists.items(): 295 featurePermissions[feature] = { 296 QWebEnginePage.PermissionPolicy.PermissionGrantedByUser: [], 297 QWebEnginePage.PermissionPolicy.PermissionDeniedByUser: [], 298 } 299 for row in range(permissionsList.topLevelItemCount()): 300 itm = permissionsList.topLevelItem(row) 301 host = itm.text(0) 302 permission = itm.data(0, Qt.ItemDataRole.UserRole) 303 featurePermissions[feature][permission].append(host) 304 305 return featurePermissions 306