1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2005 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing the base class of the VCS project browser helper. 8""" 9 10import os 11 12from PyQt5.QtCore import QObject, QCoreApplication 13from PyQt5.QtWidgets import QDialog 14 15from E5Gui.E5Application import e5App 16 17from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog 18 19from Project.ProjectBrowserModel import ( 20 ProjectBrowserSimpleDirectoryItem, ProjectBrowserFileItem, 21 ProjectBrowserDirectoryItem 22) 23 24import Preferences 25 26 27class VcsProjectBrowserHelper(QObject): 28 """ 29 Class implementing the base class of the VCS project browser helper. 30 """ 31 def __init__(self, vcsObject, browserObject, projectObject, 32 isTranslationsBrowser, parent=None, name=None): 33 """ 34 Constructor 35 36 @param vcsObject reference to the vcs object 37 @param browserObject reference to the project browser object 38 @param projectObject reference to the project object 39 @param isTranslationsBrowser flag indicating, the helper is requested 40 for the translations browser (this needs some special treatment) 41 @param parent parent widget (QWidget) 42 @param name name of this object (string) 43 """ 44 super().__init__(parent) 45 if name: 46 self.setObjectName(name) 47 48 self.vcs = vcsObject 49 self.browser = browserObject 50 self.isTranslationsBrowser = isTranslationsBrowser 51 self.project = projectObject 52 53 def addVCSMenus(self, mainMenu, multiMenu, backMenu, dirMenu, 54 dirMultiMenu): 55 """ 56 Public method to add the VCS entries to the various project browser 57 menus. 58 59 @param mainMenu reference to the main menu (QPopupMenu) 60 @param multiMenu reference to the multiple selection menu (QPopupMenu) 61 @param backMenu reference to the background menu (QPopupMenu) 62 @param dirMenu reference to the directory menu (QPopupMenu) 63 @param dirMultiMenu reference to the multiple selection directory 64 menu (QPopupMenu) 65 """ 66 self._addVCSMenu(mainMenu) 67 self._addVCSMenuMulti(multiMenu) 68 self._addVCSMenuBack(backMenu) 69 self._addVCSMenuDir(dirMenu) 70 self._addVCSMenuDirMulti(dirMultiMenu) 71 72 def showContextMenu(self, menu, standardItems): 73 """ 74 Public slot called before the context menu is shown. 75 76 It enables/disables the VCS menu entries depending on the overall 77 VCS status and the file status. 78 79 @param menu reference to the menu to be shown 80 @param standardItems array of standard items that need 81 activation/deactivation depending on the overall VCS status 82 @exception RuntimeError to indicate that this method must be 83 implemented by a subclass 84 """ 85 raise RuntimeError('Not implemented') 86 87 def showContextMenuMulti(self, menu, standardItems): 88 """ 89 Public slot called before the context menu (multiple selections) is 90 shown. 91 92 It enables/disables the VCS menu entries depending on the overall 93 VCS status and the files status. 94 95 @param menu reference to the menu to be shown 96 @param standardItems array of standard items that need 97 activation/deactivation depending on the overall VCS status 98 @exception RuntimeError to indicate that this method must be 99 implemented by a subclass 100 """ 101 raise RuntimeError('Not implemented') 102 103 def showContextMenuDir(self, menu, standardItems): 104 """ 105 Public slot called before the context menu is shown. 106 107 It enables/disables the VCS menu entries depending on the overall 108 VCS status and the directory status. 109 110 @param menu reference to the menu to be shown 111 @param standardItems array of standard items that 112 need activation/deactivation depending on the overall VCS status 113 @exception RuntimeError to indicate that this method must be 114 implemented by a subclass 115 """ 116 raise RuntimeError('Not implemented') 117 118 def showContextMenuDirMulti(self, menu, standardItems): 119 """ 120 Public slot called before the context menu is shown. 121 122 It enables/disables the VCS menu entries depending on the overall 123 VCS status and the directory status. 124 125 @param menu reference to the menu to be shown 126 @param standardItems array of standard items that need 127 activation/deactivation depending on the overall VCS status 128 @exception RuntimeError to indicate that this method must be 129 implemented by a subclass 130 """ 131 raise RuntimeError('Not implemented') 132 133 ########################################################################### 134 ## General menu handling methods below 135 ########################################################################### 136 137 def _VCSUpdate(self): 138 """ 139 Protected slot called by the context menu to update a file from the 140 VCS repository. 141 """ 142 if self.isTranslationsBrowser: 143 names = [itm.dirName() 144 for itm in self.browser.getSelectedItems( 145 [ProjectBrowserSimpleDirectoryItem])] 146 if not names: 147 names = [itm.fileName() 148 for itm in self.browser.getSelectedItems( 149 [ProjectBrowserFileItem])] 150 else: 151 names = [] 152 for itm in self.browser.getSelectedItems(): 153 try: 154 name = itm.fileName() 155 except AttributeError: 156 name = itm.dirName() 157 names.append(name) 158 self.vcs.vcsUpdate(names) 159 160 def _VCSCommit(self): 161 """ 162 Protected slot called by the context menu to commit the changes to the 163 VCS repository. 164 """ 165 if self.isTranslationsBrowser: 166 names = [itm.dirName() 167 for itm in self.browser.getSelectedItems( 168 [ProjectBrowserSimpleDirectoryItem])] 169 if not names: 170 names = [itm.fileName() 171 for itm in self.browser.getSelectedItems( 172 [ProjectBrowserFileItem])] 173 else: 174 names = [] 175 for itm in self.browser.getSelectedItems(): 176 try: 177 name = itm.fileName() 178 except AttributeError: 179 name = itm.dirName() 180 names.append(name) 181 if Preferences.getVCS("AutoSaveFiles"): 182 vm = e5App().getObject("ViewManager") 183 for name in names: 184 vm.saveEditor(name) 185 self.vcs.vcsCommit(names, '') 186 187 def _VCSAdd(self): 188 """ 189 Protected slot called by the context menu to add the selected file to 190 the VCS repository. 191 """ 192 if self.isTranslationsBrowser: 193 items = self.browser.getSelectedItems( 194 [ProjectBrowserSimpleDirectoryItem]) 195 if items: 196 names = [itm.dirName() for itm in items] 197 qnames = [] 198 else: 199 items = self.browser.getSelectedItems([ProjectBrowserFileItem]) 200 names = [] 201 qnames = [] 202 for itm in items: 203 name = itm.fileName() 204 if name.endswith('.qm'): 205 qnames.append(name) 206 else: 207 names.append(name) 208 else: 209 names = [] 210 for itm in self.browser.getSelectedItems(): 211 try: 212 name = itm.fileName() 213 except AttributeError: 214 name = itm.dirName() 215 names.append(name) 216 qnames = [] 217 218 if not len(names + qnames): 219 return 220 221 if len(names + qnames) == 1: 222 if names: 223 self.vcs.vcsAdd(names[0], os.path.isdir(names[0])) 224 else: 225 if self.vcs.canDetectBinaries: 226 self.vcs.vcsAdd(qnames) 227 else: 228 self.vcs.vcsAddBinary(qnames) 229 else: 230 if self.vcs.canDetectBinaries: 231 self.vcs.vcsAdd(names + qnames) 232 else: 233 self.vcs.vcsAdd(names) 234 if len(qnames): 235 self.vcs.vcsAddBinary(qnames) 236 for fn in names + qnames: 237 self._updateVCSStatus(fn) 238 239 def _VCSAddTree(self): 240 """ 241 Protected slot called by the context menu. 242 243 It is used to add the selected 244 directory tree to the VCS repository. 245 """ 246 names = [] 247 for itm in self.browser.getSelectedItems(): 248 try: 249 name = itm.fileName() 250 except AttributeError: 251 name = itm.dirName() 252 names.append(name) 253 self.vcs.vcsAddTree(names) 254 for fn in names: 255 self._updateVCSStatus(fn) 256 257 def _VCSRemove(self): 258 """ 259 Protected slot called by the context menu to remove the selected file 260 from the VCS repository. 261 """ 262 if self.isTranslationsBrowser: 263 items = self.browser.getSelectedItems( 264 [ProjectBrowserSimpleDirectoryItem]) 265 if items: 266 return # not supported 267 268 isRemoveDirs = False 269 items = self.browser.getSelectedItems([ProjectBrowserFileItem]) 270 names = [itm.fileName() for itm in items] 271 272 dlg = DeleteFilesConfirmationDialog( 273 self.parent(), 274 QCoreApplication.translate( 275 "VcsProjectBrowserHelper", 276 "Remove from repository (and disk)"), 277 QCoreApplication.translate( 278 "VcsProjectBrowserHelper", 279 "Do you really want to remove these translation files from" 280 " the repository (and disk)?"), 281 names) 282 else: 283 items = self.browser.getSelectedItems() 284 isRemoveDirs = ( 285 len(items) == self.browser.getSelectedItemsCount( 286 [ProjectBrowserSimpleDirectoryItem, 287 ProjectBrowserDirectoryItem]) 288 ) 289 if isRemoveDirs: 290 names = [itm.dirName() for itm in items] 291 else: 292 names = [itm.fileName() for itm in items] 293 files = [self.browser.project.getRelativePath(name) 294 for name in names] 295 296 dlg = DeleteFilesConfirmationDialog( 297 self.parent(), 298 QCoreApplication.translate( 299 "VcsProjectBrowserHelper", 300 "Remove from repository (and disk)"), 301 QCoreApplication.translate( 302 "VcsProjectBrowserHelper", 303 "Do you really want to remove these files/directories" 304 " from the repository (and disk)?"), 305 files) 306 307 if dlg.exec() == QDialog.DialogCode.Accepted: 308 status = self.vcs.vcsRemove(names) 309 if status: 310 if isRemoveDirs: 311 self.browser._removeDir() 312 # remove directories from Project 313 else: 314 self.browser._removeFile() # remove file(s) from project 315 316 def _VCSLog(self): 317 """ 318 Protected slot called by the context menu to show the VCS log of a 319 file/directory. 320 """ 321 # kept for backward compatibility for plug-ins 322 self._VCSLogBrowser() 323 324 def _VCSLogBrowser(self): 325 """ 326 Protected slot called by the context menu to show the log browser for a 327 file. 328 """ 329 itm = self.browser.currentItem() 330 try: 331 fn = itm.fileName() 332 isFile = True 333 except AttributeError: 334 fn = itm.dirName() 335 isFile = False 336 self.vcs.vcsLogBrowser(fn, isFile=isFile) 337 338 def _VCSDiff(self): 339 """ 340 Protected slot called by the context menu to show the difference of a 341 file/directory to the repository. 342 """ 343 names = [] 344 for itm in self.browser.getSelectedItems(): 345 try: 346 name = itm.fileName() 347 except AttributeError: 348 name = itm.dirName() 349 names.append(name) 350 self.vcs.vcsDiff(names) 351 352 def _VCSStatus(self): 353 """ 354 Protected slot called by the context menu to show the status of a file. 355 """ 356 if self.isTranslationsBrowser: 357 items = self.browser.getSelectedItems( 358 [ProjectBrowserSimpleDirectoryItem]) 359 if items: 360 names = [itm.dirName() for itm in items] 361 else: 362 items = self.browser.getSelectedItems([ProjectBrowserFileItem]) 363 names = [itm.fileName() for itm in items] 364 else: 365 names = [] 366 for itm in self.browser.getSelectedItems(): 367 try: 368 name = itm.fileName() 369 except AttributeError: 370 name = itm.dirName() 371 names.append(name) 372 self.vcs.vcsStatus(names) 373 374 def _VCSRevert(self): 375 """ 376 Protected slot called by the context menu to revert changes made to a 377 file. 378 """ 379 names = [] 380 for itm in self.browser.getSelectedItems(): 381 try: 382 name = itm.fileName() 383 except AttributeError: 384 name = itm.dirName() 385 names.append(name) 386 self.vcs.vcsRevert(names) 387 388 def _VCSMerge(self): 389 """ 390 Protected slot called by the context menu to merge changes into to a 391 file. 392 """ 393 itm = self.browser.currentItem() 394 try: 395 name = itm.fileName() 396 except AttributeError: 397 name = itm.dirName() 398 self.vcs.vcsMerge(name) 399 400 def _VCSInfoDisplay(self): 401 """ 402 Protected slot called to show some vcs information. 403 """ 404 from .RepositoryInfoDialog import VcsRepositoryInfoDialog 405 info = self.vcs.vcsRepositoryInfos(self.project.ppath) 406 dlg = VcsRepositoryInfoDialog(None, info) 407 dlg.exec() 408 409 def _updateVCSStatus(self, name): 410 """ 411 Protected method to update the VCS status of an item. 412 413 @param name filename or directoryname of the item to be updated 414 (string) 415 """ 416 self.project.getModel().updateVCSStatus(name) 417