|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2024 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a file dialog showing the file system of the eric-ide server. |
|
8 """ |
|
9 |
|
10 import enum |
|
11 import fnmatch |
|
12 import re |
|
13 |
|
14 from PyQt6.QtCore import QLocale, Qt, pyqtSlot, QPoint |
|
15 from PyQt6.QtWidgets import ( |
|
16 QDialog, QTreeWidgetItem, QAbstractItemView, QCompleter, QLineEdit, QInputDialog, |
|
17 QMenu |
|
18 ) |
|
19 |
|
20 from eric7.EricGui import EricPixmapCache |
|
21 from eric7.EricGui.EricFileIconProvider import EricFileIconProvider |
|
22 from eric7.EricWidgets import EricMessageBox |
|
23 from eric7.EricWidgets.EricApplication import ericApp |
|
24 from eric7.Globals import dataString |
|
25 from eric7.SystemUtilities import FileSystemUtilities |
|
26 |
|
27 from .Ui_EricServerFileDialog import Ui_EricServerFileDialog |
|
28 |
|
29 |
|
30 class AcceptMode(enum.Enum): |
|
31 """ |
|
32 Class defining the dialog accept modes. |
|
33 """ |
|
34 AcceptOpen = 0 |
|
35 AcceptSave = 1 |
|
36 |
|
37 |
|
38 class FileMode(enum.Enum): |
|
39 """ |
|
40 Class defining what the user may select in the file dialog. |
|
41 """ |
|
42 AnyFile = 0 |
|
43 ExistingFile = 1 |
|
44 Directory = 2 |
|
45 ExistingFiles = 3 |
|
46 |
|
47 class EricServerFileDialog(QDialog, Ui_EricServerFileDialog): |
|
48 """ |
|
49 Class implementing a file dialog showing the file system of the eric-ide server. |
|
50 """ |
|
51 |
|
52 IsDirectoryRole = Qt.ItemDataRole.UserRole |
|
53 |
|
54 def __init__(self, parent=None, caption="", directory="", filter=""): |
|
55 """ |
|
56 Constructor |
|
57 |
|
58 @param parent reference to the parent widget (defaults to None) |
|
59 @type QWidget (optional) |
|
60 @param caption dialog title (defaults to "") |
|
61 @type str (optional) |
|
62 @param directory initial directory (defaults to "") |
|
63 @type str (optional) |
|
64 @param filter Qt file filter string (defaults to "") |
|
65 @type str (optional) |
|
66 """ |
|
67 super().__init__(parent) |
|
68 self.setupUi(self) |
|
69 |
|
70 # finish UI setup |
|
71 self.backButton.setIcon(EricPixmapCache.getIcon("1leftarrow")) |
|
72 self.forwardButton.setIcon(EricPixmapCache.getIcon("1rightarrow")) |
|
73 self.upButton.setIcon(EricPixmapCache.getIcon("1uparrow")) |
|
74 self.newDirButton.setIcon(EricPixmapCache.getIcon("dirNew")) |
|
75 self.reloadButton.setIcon(EricPixmapCache.getIcon("reload")) |
|
76 self.cancelButton.setIcon(EricPixmapCache.getIcon("dialog-cancel")) |
|
77 |
|
78 self.setWindowTitle(caption) |
|
79 |
|
80 self.__iconProvider = EricFileIconProvider() |
|
81 |
|
82 self.__nameCompleter = QCompleter() |
|
83 self.__nameCompleter.setModel(self.listing.model()) |
|
84 self.__nameCompleter.setCompletionColumn(0) |
|
85 self.__nameCompleter.activated.connect(self.__nameCompleterActivated) |
|
86 self.nameEdit.setCompleter(self.__nameCompleter) |
|
87 |
|
88 self.__contextMenu = QMenu(self) |
|
89 |
|
90 self.__fsInterface = ericApp().getObject("EricServer").getServiceInterface( |
|
91 "FileSystem" |
|
92 ) |
|
93 |
|
94 # set some default values |
|
95 self.__fileMode = FileMode.ExistingFile |
|
96 self.__dirsOnly = False |
|
97 self.__acceptMode = AcceptMode.AcceptOpen |
|
98 self.__showHidden = False |
|
99 self.__sep = "/" |
|
100 self.__filters = [] |
|
101 |
|
102 self.__history = [] |
|
103 self.__currentHistoryIndex = -1 # empty history |
|
104 self.__updateHistoryButtons() |
|
105 |
|
106 self.__filenameCache = [] |
|
107 self.__directoryCache = [] |
|
108 self.__selectedDirectory = None |
|
109 |
|
110 self.setNameFilters(filter.split(";;")) |
|
111 |
|
112 self.reloadButton.clicked.connect(self.__reload) |
|
113 self.cancelButton.clicked.connect(self.reject) |
|
114 |
|
115 self.treeCombo.currentTextChanged.connect(self.setDirectory) |
|
116 |
|
117 self.setDirectory(FileSystemUtilities.plainFileName(directory)) |
|
118 |
|
119 def acceptMode(self): |
|
120 """ |
|
121 Public method to get the accept mode of the dialog. |
|
122 |
|
123 @return accept mode |
|
124 @rtype AcceptMode |
|
125 """ |
|
126 return self.__acceptMode |
|
127 |
|
128 def setAcceptMode(self, mode): |
|
129 """ |
|
130 Public method to set the accept mode of the dialog. |
|
131 |
|
132 @param mode accept mode |
|
133 @type AcceptMode |
|
134 """ |
|
135 self.__acceptMode = mode |
|
136 |
|
137 self.__updateOkButton() |
|
138 |
|
139 def fileMode(self): |
|
140 """ |
|
141 Public method to get the current file mode of the dialog. |
|
142 |
|
143 @return file mode |
|
144 @rtype FileMode |
|
145 """ |
|
146 return self.__fileMode |
|
147 |
|
148 def setFileMode(self, mode): |
|
149 """ |
|
150 Public method to set the file mode of the dialog. |
|
151 |
|
152 @param mode file mode |
|
153 @type FileMode |
|
154 """ |
|
155 self.__fileMode = mode |
|
156 |
|
157 self.listing.clearSelection() |
|
158 if mode == FileMode.ExistingFiles: |
|
159 self.listing.setSelectionMode( |
|
160 QAbstractItemView.SelectionMode.ExtendedSelection |
|
161 ) |
|
162 else: |
|
163 self.listing.setSelectionMode( |
|
164 QAbstractItemView.SelectionMode.SingleSelection |
|
165 ) |
|
166 |
|
167 self.__updateOkButton() |
|
168 |
|
169 def setNameFilters(self, filters): |
|
170 """ |
|
171 Public method to set the list of file/directory name filters. |
|
172 |
|
173 @param filters list of filter expressions |
|
174 ("filter_name (pattern1 ... patternN)") |
|
175 @type list of str |
|
176 """ |
|
177 self.__filters = [ |
|
178 f.split(" (", 1)[1].split(")", 1)[0].split() for f in filters |
|
179 ] |
|
180 |
|
181 self.filterCombo.clear() |
|
182 self.filterCombo.addItems(filters) |
|
183 |
|
184 def setNameFilter(self, filter): |
|
185 """ |
|
186 Public method to set the current name filter. |
|
187 |
|
188 @param filter filter text to make current |
|
189 @type str |
|
190 """ |
|
191 self.filterCombo.setCurrentText(filter) |
|
192 |
|
193 def setDirectoriesOnly(self, dirsOnly): |
|
194 """ |
|
195 Public method to set a flag to just show directories. |
|
196 |
|
197 @param dirsOnly flag indicating to just show directories |
|
198 @type bool |
|
199 """ |
|
200 self.__dirsOnly = dirsOnly |
|
201 |
|
202 filters = self.__filters[self.filterCombo.currentIndex()] |
|
203 self.__filterList(filters) |
|
204 |
|
205 def __addToHistory(self, entry): |
|
206 """ |
|
207 Private method to add a directory to the history list. |
|
208 |
|
209 @param entry name of the directory to be added |
|
210 @type str |
|
211 """ |
|
212 try: |
|
213 # is in the history already? |
|
214 index = self.__history.index(entry) |
|
215 self.__currentHistoryIndex = index |
|
216 except ValueError: |
|
217 # new entry |
|
218 self.__history.append(entry) |
|
219 self.__currentHistoryIndex = len(self.__history) - 1 |
|
220 |
|
221 self.__updateHistoryButtons() |
|
222 |
|
223 @pyqtSlot() |
|
224 def __updateHistoryButtons(self): |
|
225 """ |
|
226 Private method to update the enabled state of the back and forward buttons. |
|
227 """ |
|
228 if not self.__history: |
|
229 self.backButton.setEnabled(False) |
|
230 self.forwardButton.setEnabled(False) |
|
231 else: |
|
232 self.backButton.setEnabled(self.__currentHistoryIndex > 0) |
|
233 self.forwardButton.setEnabled( |
|
234 self.__currentHistoryIndex < len(self.__history) - 1 |
|
235 ) |
|
236 |
|
237 @pyqtSlot() |
|
238 def on_backButton_clicked(self): |
|
239 """ |
|
240 Private slot to move back in history of visited directories. |
|
241 """ |
|
242 self.setDirectory(self.__history[self.__currentHistoryIndex - 1]) |
|
243 |
|
244 @pyqtSlot() |
|
245 def on_forwardButton_clicked(self): |
|
246 """ |
|
247 Private slot to move forward in history of visited directories. |
|
248 """ |
|
249 self.setDirectory(self.__history[self.__currentHistoryIndex + 1]) |
|
250 |
|
251 @pyqtSlot() |
|
252 def __updateUpButton(self): |
|
253 """ |
|
254 Private slot to update the enabled state of the 'Up' button. |
|
255 """ |
|
256 self.upButton.setEnabled( |
|
257 self.treeCombo.currentIndex() < self.treeCombo.count() - 1 |
|
258 ) |
|
259 |
|
260 @pyqtSlot() |
|
261 def on_upButton_clicked(self): |
|
262 """ |
|
263 Private slot to move up one level in the hierarchy. |
|
264 """ |
|
265 self.treeCombo.setCurrentIndex(self.treeCombo.currentIndex() + 1) |
|
266 |
|
267 @pyqtSlot() |
|
268 def on_newDirButton_clicked(self): |
|
269 """ |
|
270 Private slot to create a new directory. |
|
271 """ |
|
272 newDir, ok = QInputDialog.getText( |
|
273 self, |
|
274 self.tr("New Directory"), |
|
275 self.tr("Enter the name for the new directory:"), |
|
276 QLineEdit.EchoMode.Normal, |
|
277 ) |
|
278 if ok and newDir: |
|
279 if newDir in self.__directoryCache or newDir in self.__filenameCache: |
|
280 EricMessageBox.warning( |
|
281 self, |
|
282 self.tr("New Directory"), |
|
283 self.tr( |
|
284 "<p>A file or directory with the name <b>{0}</b> exists" |
|
285 " already. Aborting...</p>" |
|
286 ).format(newDir), |
|
287 ) |
|
288 return |
|
289 |
|
290 ok, error = self.__fsInterface.mkdir(self.__getFullPath(newDir)) |
|
291 if ok: |
|
292 # refresh |
|
293 self.__reload() |
|
294 else: |
|
295 EricMessageBox.critical( |
|
296 self, |
|
297 self.tr("New Directory"), |
|
298 self.tr( |
|
299 "<p>The directory <b>{0}</b> could not be created.</p>" |
|
300 "<p>Reason: {1}</p>" |
|
301 ).format( |
|
302 self.__getFullPath(newDir), |
|
303 error if error else self.tr("Unknown"), |
|
304 ), |
|
305 ) |
|
306 |
|
307 @pyqtSlot() |
|
308 def __reload(self): |
|
309 """ |
|
310 Private slot to reload the directory listing. |
|
311 """ |
|
312 self.setDirectory(self.treeCombo.currentText()) |
|
313 |
|
314 @pyqtSlot(QTreeWidgetItem, int) |
|
315 def on_listing_itemActivated(self, item, column): |
|
316 """ |
|
317 Private slot to handle the activation of an item in the list. |
|
318 |
|
319 @param item reference to the activated item |
|
320 @type QTreeWidgetItem |
|
321 @param column column number (unused) |
|
322 @type int |
|
323 """ |
|
324 if ( |
|
325 item.data(0, EricServerFileDialog.IsDirectoryRole) |
|
326 and self.__fileMode != FileMode.Directory |
|
327 ): |
|
328 self.setDirectory(self.__getFullPath(item.text(0))) |
|
329 else: |
|
330 self.accept() |
|
331 |
|
332 @pyqtSlot() |
|
333 def on_listing_itemSelectionChanged(self): |
|
334 """ |
|
335 Private slot to handle the selection of listed items. |
|
336 """ |
|
337 for itm in self.listing.selectedItems(): |
|
338 if itm.data(0, EricServerFileDialog.IsDirectoryRole): |
|
339 self.__selectedDirectory = itm.text(0) |
|
340 break |
|
341 else: |
|
342 self.__selectedDirectory = None |
|
343 |
|
344 selected = [] |
|
345 for itm in self.listing.selectedItems(): |
|
346 isDir = itm.data(0, EricServerFileDialog.IsDirectoryRole) |
|
347 if self.__fileMode == FileMode.Directory and isDir: |
|
348 selected.append(itm.text(0)) |
|
349 elif not isDir: |
|
350 selected.append(itm.text(0)) |
|
351 |
|
352 if len(selected) == 1: |
|
353 self.nameEdit.setText(selected[0]) |
|
354 elif len(selected) > 1: |
|
355 self.nameEdit.setText( |
|
356 '"{0}"'.format('" "'.join(selected)) |
|
357 ) |
|
358 |
|
359 self.__updateOkButton() |
|
360 |
|
361 @pyqtSlot() |
|
362 def __nameCompleterActivated(self): |
|
363 """ |
|
364 Private slot handling the activation of the completer. |
|
365 """ |
|
366 if self.okButton.isEnabled(): |
|
367 self.okButton.animateClick() |
|
368 |
|
369 @pyqtSlot(str) |
|
370 def on_nameEdit_textChanged(self, name): |
|
371 """ |
|
372 Private slot handling the editing of a file or directory name. |
|
373 |
|
374 @param name current text of the name edit |
|
375 @type str |
|
376 """ |
|
377 self.listing.clearSelection() |
|
378 items = self.listing.findItems(name, Qt.MatchFlag.MatchExactly) |
|
379 for itm in items: |
|
380 itm.setSelected(True) |
|
381 |
|
382 self.__updateOkButton() |
|
383 |
|
384 def __getNames(self): |
|
385 """ |
|
386 Private method to get the selected names list. |
|
387 |
|
388 @return list containing the selected names |
|
389 @rtype list of str |
|
390 """ |
|
391 namesStr = self.nameEdit.text() |
|
392 if namesStr.startswith('"'): |
|
393 namesStr = namesStr[1:] |
|
394 if namesStr.endswith('"'): |
|
395 namesStr = namesStr[:-1] |
|
396 names = re.split(r'"\s+"', namesStr) |
|
397 return names |
|
398 |
|
399 def __getFullPath(self, name): |
|
400 """ |
|
401 Private method to get the full path for a given file or directory name. |
|
402 |
|
403 @param name name of the file or directory |
|
404 @type str |
|
405 @return full path of the file or directory |
|
406 @rtype str |
|
407 """ |
|
408 return "{0}{1}{2}".format(self.treeCombo.currentText(), self.__sep, name) |
|
409 |
|
410 @pyqtSlot() |
|
411 def __updateOkButton(self): |
|
412 """ |
|
413 Private slot to set the 'OK' button state, icon and label. |
|
414 """ |
|
415 # 1. adjust icon and label |
|
416 if ( |
|
417 self.__acceptMode == AcceptMode.AcceptOpen |
|
418 or self.__selectedDirectory is not None |
|
419 ): |
|
420 self.okButton.setIcon(EricPixmapCache.getIcon("dialog-ok")) |
|
421 self.okButton.setText(self.tr("Open")) |
|
422 else: |
|
423 self.okButton.setIcon(EricPixmapCache.getIcon("fileSave")) |
|
424 self.okButton.setText(self.tr("Save")) |
|
425 |
|
426 # 2. adjust enabled state |
|
427 if self.__selectedDirectory and self.__fileMode != FileMode.Directory: |
|
428 self.okButton.setEnabled(True) |
|
429 elif self.__fileMode == FileMode.AnyFile: |
|
430 self.okButton.setEnabled(bool(self.nameEdit.text())) |
|
431 elif self.__fileMode == FileMode.ExistingFile: |
|
432 self.okButton.setEnabled( |
|
433 self.nameEdit.text() in self.__filenameCache |
|
434 ) |
|
435 elif self.__fileMode == FileMode.ExistingFiles: |
|
436 names = self.__getNames() |
|
437 self.okButton.setEnabled( |
|
438 all(n in self.__filenameCache for n in names) |
|
439 ) |
|
440 elif self.__fileMode == FileMode.Directory: |
|
441 self.okButton.setEnabled( |
|
442 self.nameEdit.text() in self.__directoryCache |
|
443 ) |
|
444 else: |
|
445 self.okButton.setEnabled(False) |
|
446 |
|
447 @pyqtSlot() |
|
448 def on_okButton_clicked(self): |
|
449 """ |
|
450 Private slot handling the press of the OK button. |
|
451 """ |
|
452 if self.__selectedDirectory and self.__fileMode != FileMode.Directory: |
|
453 self.setDirectory(self.__getFullPath(self.__selectedDirectory)) |
|
454 else: |
|
455 self.accept() |
|
456 |
|
457 @pyqtSlot(int) |
|
458 def on_filterCombo_currentIndexChanged(self, index): |
|
459 """ |
|
460 Slot documentation goes here. |
|
461 |
|
462 @param index DESCRIPTION |
|
463 @type int |
|
464 """ |
|
465 filters = self.__filters[index] |
|
466 self.__filterList(filters) |
|
467 |
|
468 @pyqtSlot(str) |
|
469 def setDirectory(self, directory): |
|
470 """ |
|
471 Public slot to set the current directory and populate the tree list. |
|
472 |
|
473 @param directory directory to be set as current. An empty directory sets the |
|
474 server's current directory. |
|
475 @type str |
|
476 """ |
|
477 self.__filenameCache.clear() |
|
478 self.__directoryCache.clear() |
|
479 |
|
480 |
|
481 directory, sep, dirListing = self.__fsInterface.listdir(directory) |
|
482 |
|
483 self.__sep = sep |
|
484 |
|
485 # 1. populate the directory tree combo box |
|
486 self.treeCombo.blockSignals(True) |
|
487 self.treeCombo.clear() |
|
488 if len(directory) > 1 and directory.endswith(sep): |
|
489 directory = directory[:-1] |
|
490 if len(directory) > 2 and directory[1] == ":": |
|
491 # starts with a Windows drive letter |
|
492 directory = directory[2:] |
|
493 directoryParts = directory.split(sep) |
|
494 while directoryParts: |
|
495 if directoryParts[-1]: |
|
496 self.treeCombo.addItem(sep.join(directoryParts)) |
|
497 directoryParts.pop() |
|
498 self.treeCombo.addItem(sep) |
|
499 self.treeCombo.blockSignals(False) |
|
500 |
|
501 # 2. populate the directory listing |
|
502 self.listing.clear() |
|
503 for dirEntry in sorted( |
|
504 dirListing, |
|
505 key=lambda d: " " + d['name'].lower() if d["is_dir"] else d["name"].lower(), |
|
506 ): |
|
507 if dirEntry["is_dir"]: |
|
508 type_ = self.tr("Directory") |
|
509 iconName = "dirClosed" |
|
510 sizeStr = "" |
|
511 self.__directoryCache.append(dirEntry["name"]) |
|
512 else: |
|
513 type_ = self.tr("File") |
|
514 iconName = self.__iconProvider.fileIconName(dirEntry["name"]) |
|
515 sizeStr = dataString(dirEntry["size"], QLocale.system()) |
|
516 self.__filenameCache.append(dirEntry["name"]) |
|
517 itm = QTreeWidgetItem( |
|
518 self.listing, |
|
519 [dirEntry["name"], sizeStr, type_, dirEntry["mtime"]] |
|
520 ) |
|
521 itm.setIcon(0, EricPixmapCache.getIcon(iconName)) |
|
522 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignRight) |
|
523 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignHCenter) |
|
524 itm.setData(0, EricServerFileDialog.IsDirectoryRole, dirEntry["is_dir"]) |
|
525 |
|
526 |
|
527 filters = self.__filters[self.filterCombo.currentIndex()] |
|
528 self.__filterList(filters) |
|
529 |
|
530 # 3. add the directory to the history |
|
531 self.__addToHistory(directory) |
|
532 |
|
533 # 4. update some dependent states |
|
534 self.nameEdit.clear() |
|
535 self.__updateUpButton() |
|
536 |
|
537 @pyqtSlot(QPoint) |
|
538 def on_listing_customContextMenuRequested(self, pos): |
|
539 """ |
|
540 Priovate slot to show a context menu. |
|
541 |
|
542 @param pos mouse pointer position to show the menu at |
|
543 @type QPoint |
|
544 """ |
|
545 self.__contextMenu.clear() |
|
546 |
|
547 itm = self.listing.itemAt(pos) |
|
548 if itm is not None: |
|
549 self.__contextMenu.addAction( |
|
550 self.tr("Rename"), lambda: self.__renameItem(itm) |
|
551 ) |
|
552 self.__contextMenu.addAction( |
|
553 self.tr("Delete"), lambda: self.__deleteItem(itm) |
|
554 ) |
|
555 self.__contextMenu.addSeparator() |
|
556 act = self.__contextMenu.addAction(self.tr("Show Hidden Files")) |
|
557 act.setCheckable(True) |
|
558 act.setChecked(self.__showHidden) |
|
559 act.toggled.connect(self.__showHiddenToggled) |
|
560 self.__contextMenu.addAction( |
|
561 self.tr("New Directory"), self.on_newDirButton_clicked |
|
562 ) |
|
563 |
|
564 self.__contextMenu.popup(self.listing.mapToGlobal(pos)) |
|
565 |
|
566 @pyqtSlot(QTreeWidgetItem) |
|
567 def __renameItem(self, item): |
|
568 """ |
|
569 Private slot to rename the given file/directory item. |
|
570 |
|
571 @param item reference to the item to be renamed |
|
572 @type QTreeWidgetItem |
|
573 """ |
|
574 title = ( |
|
575 self.tr("Rename Directory") |
|
576 if item.data(0, EricServerFileDialog.IsDirectoryRole) |
|
577 else self.tr("Rename File") |
|
578 ) |
|
579 |
|
580 newName, ok = QInputDialog.getText( |
|
581 self, |
|
582 title, |
|
583 self.tr("<p>Enter the new name <b>{0}</b>:</p>").format(item.text(0)), |
|
584 QLineEdit.EchoMode.Normal, |
|
585 item.text(0), |
|
586 ) |
|
587 if ok and newName: |
|
588 if newName in self.__directoryCache or newName in self.__filenameCache: |
|
589 EricMessageBox.warning( |
|
590 self, |
|
591 title, |
|
592 self.tr( |
|
593 "<p>A file or directory with the name <b>{0}</b> exists" |
|
594 " already. Aborting...</p>" |
|
595 ).format(newName), |
|
596 ) |
|
597 return |
|
598 |
|
599 ok, error = self.__fsInterface.replace( |
|
600 self.__getFullPath(item.text(0)), self.__getFullPath(newName) |
|
601 ) |
|
602 if ok: |
|
603 # refresh |
|
604 self.__reload() |
|
605 else: |
|
606 EricMessageBox.critical( |
|
607 self, |
|
608 title, |
|
609 self.tr( |
|
610 "<p>The renaming operation failed.</p>" |
|
611 "<p>Reason: {0}</p>" |
|
612 ).format(error if error else self.tr("Unknown")), |
|
613 ) |
|
614 |
|
615 @pyqtSlot(QTreeWidgetItem) |
|
616 def __deleteItem(self, item): |
|
617 """ |
|
618 Private slot to delete the given file/directory item. |
|
619 |
|
620 @param item reference to the item to be deleted |
|
621 @type QTreeWidgetItem |
|
622 """ |
|
623 isDir = item.data(0, EricServerFileDialog.IsDirectoryRole) |
|
624 if isDir: |
|
625 title = self.tr("Delete Directory") |
|
626 itemType = self.tr("directory") |
|
627 else: |
|
628 title = self.tr("Delete File") |
|
629 itemType = self.tr("file") |
|
630 |
|
631 yes = EricMessageBox.yesNo( |
|
632 self, |
|
633 title, |
|
634 self.tr("Shall the selected {0} really be deleted?").format(itemType), |
|
635 ) |
|
636 if yes: |
|
637 ok, error = ( |
|
638 self.__fsInterface.rmdir(self.__getFullPath(item.text(0))) |
|
639 if isDir |
|
640 else self.__fsInterface.remove(self.__getFullPath(item.text(0))) |
|
641 ) |
|
642 if ok: |
|
643 # refresh |
|
644 self.__reload() |
|
645 else: |
|
646 EricMessageBox.critical( |
|
647 self, |
|
648 title, |
|
649 self.tr( |
|
650 "<p>The deletion operation failed.</p>" |
|
651 "<p>Reason: {0}</p>" |
|
652 ).format(error if error else self.tr("Unknown")), |
|
653 ) |
|
654 |
|
655 @pyqtSlot(bool) |
|
656 def __showHiddenToggled(self, on): |
|
657 """ |
|
658 Private slot to handle toggling the display of hidden files/directories. |
|
659 |
|
660 @param on flag indicating to show hidden files and directories |
|
661 @type bool |
|
662 """ |
|
663 self.__showHidden = on |
|
664 filters = self.__filters[self.filterCombo.currentIndex()] |
|
665 self.__filterList(filters) |
|
666 |
|
667 def selectedFiles(self): |
|
668 """ |
|
669 Public method to get the selected files or the current viewport path. |
|
670 |
|
671 @return selected files or current viewport path |
|
672 @rtype str |
|
673 """ |
|
674 return [self.__getFullPath(n) for n in self.__getNames()] |
|
675 |
|
676 def selectedNameFilter(self): |
|
677 """ |
|
678 Public method to get the selected name filter. |
|
679 |
|
680 @return selected name filter |
|
681 @rtype str |
|
682 """ |
|
683 return self.filterCombo.currentText() |
|
684 |
|
685 def __isHidden(self, name): |
|
686 """ |
|
687 Private method to check, if the given name is indicating a hidden file or |
|
688 directory. |
|
689 |
|
690 @param name name of the file or directory |
|
691 @type str |
|
692 @return flag indicating a hidden file or directory |
|
693 @rtype bool |
|
694 """ |
|
695 return name.startswith(".") or name.endswith("~") |
|
696 |
|
697 def __filterList(self, filters): |
|
698 """ |
|
699 Private method to filter the files and directories list based on the given |
|
700 filters and whether hidden files/directories should be shown. |
|
701 |
|
702 @param filters list of filter patterns (only applied to files |
|
703 @type list of str |
|
704 """ |
|
705 self.listing.clearSelection() |
|
706 for row in range(self.listing.topLevelItemCount()): |
|
707 itm = self.listing.topLevelItem(row) |
|
708 name = itm.text(0) |
|
709 if ( |
|
710 self.__dirsOnly |
|
711 and not itm.data(0, EricServerFileDialog.IsDirectoryRole) |
|
712 ): |
|
713 itm.setHidden(True) |
|
714 elif not self.__showHidden and self.__isHidden(name): |
|
715 # applies to files and directories |
|
716 itm.setHidden(True) |
|
717 elif not itm.data(0, EricServerFileDialog.IsDirectoryRole): |
|
718 # it is a file item, apply the filter |
|
719 itm.setHidden(not any(fnmatch.fnmatch(name, pat) for pat in filters)) |
|
720 else: |
|
721 itm.setHidden(False) |
|
722 |
|
723 # resize the columns |
|
724 for column in range(4): |
|
725 self.listing.resizeColumnToContents(column) |
|
726 |
|
727 ########################################################################### |
|
728 ## Module functions mimicing the interface of EricFileDialog/QFileDialog |
|
729 ########################################################################### |
|
730 |
|
731 |
|
732 def getOpenFileName( |
|
733 parent=None, caption="", directory="", filterStr="", initialFilter="", |
|
734 withRemote=True |
|
735 ): |
|
736 """ |
|
737 Module function to get the name of a file for opening it. |
|
738 |
|
739 @param parent parent widget of the dialog (defaults to None) |
|
740 @type QWidget (optional) |
|
741 @param caption window title of the dialog (defaults to "") |
|
742 @type str (optional) |
|
743 @param directory working directory of the dialog (defaults to "") |
|
744 @type str (optional) |
|
745 @param filterStr filter string for the dialog (defaults to "") |
|
746 @type str (optional) |
|
747 @param initialFilter initial filter for the dialog (defaults to "") |
|
748 @type str (optional) |
|
749 @param withRemote flag indicating to create the file names with the remote |
|
750 indicator (defaults to True) |
|
751 @type bool (optional) |
|
752 @return name of file to be opened |
|
753 @rtype str |
|
754 """ |
|
755 return getOpenFileNameAndFilter( |
|
756 parent, caption, directory, filterStr, initialFilter, withRemote |
|
757 )[0] |
|
758 |
|
759 |
|
760 def getOpenFileNameAndFilter( |
|
761 parent=None, caption="", directory="", filterStr="", initialFilter="", |
|
762 withRemote=True |
|
763 ): |
|
764 """ |
|
765 Module function to get the name of a file for opening it and the selected |
|
766 file name filter. |
|
767 |
|
768 @param parent parent widget of the dialog (defaults to None) |
|
769 @type QWidget (optional) |
|
770 @param caption window title of the dialog (defaults to "") |
|
771 @type str (optional) |
|
772 @param directory working directory of the dialog (defaults to "") |
|
773 @type str (optional) |
|
774 @param filterStr filter string for the dialog (defaults to "") |
|
775 @type str (optional) |
|
776 @param initialFilter initial filter for the dialog (defaults to "") |
|
777 @type str (optional) |
|
778 @param withRemote flag indicating to create the file names with the remote |
|
779 indicator (defaults to True) |
|
780 @type bool (optional) |
|
781 @return tuple containing the list of file names to be opened and the |
|
782 selected file name filter |
|
783 @rtype tuple of (list of str, str) |
|
784 """ |
|
785 dlg = EricServerFileDialog( |
|
786 parent=parent, caption=caption, directory=directory, filter=filterStr |
|
787 ) |
|
788 dlg.setFileMode(FileMode.ExistingFile) |
|
789 dlg.setNameFilter(initialFilter) |
|
790 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
791 if withRemote: |
|
792 fileName = FileSystemUtilities.remoteFileName(dlg.selectedFiles()[0]) |
|
793 else: |
|
794 fileName = dlg.selectedFiles()[0] |
|
795 selectedFilter = dlg.selectedNameFilter() |
|
796 else: |
|
797 fileName = "" |
|
798 selectedFilter = "" |
|
799 |
|
800 return fileName, selectedFilter |
|
801 |
|
802 |
|
803 def getOpenFileNames( |
|
804 parent=None, caption="", directory="", filterStr="", initialFilter="", |
|
805 withRemote=True |
|
806 ): |
|
807 """ |
|
808 Module function to get a list of names of files for opening. |
|
809 |
|
810 @param parent parent widget of the dialog (defaults to None) |
|
811 @type QWidget (optional) |
|
812 @param caption window title of the dialog (defaults to "") |
|
813 @type str (optional) |
|
814 @param directory working directory of the dialog (defaults to "") |
|
815 @type str (optional) |
|
816 @param filterStr filter string for the dialog (defaults to "") |
|
817 @type str (optional) |
|
818 @param initialFilter initial filter for the dialog (defaults to "") |
|
819 @type str (optional) |
|
820 @param withRemote flag indicating to create the file names with the remote |
|
821 indicator (defaults to True) |
|
822 @type bool (optional) |
|
823 @return list of file names to be opened |
|
824 @rtype list of str |
|
825 """ |
|
826 return getOpenFileNamesAndFilter( |
|
827 parent, caption, directory, filterStr, initialFilter, withRemote |
|
828 )[0] |
|
829 |
|
830 |
|
831 def getOpenFileNamesAndFilter( |
|
832 parent=None, caption="", directory="", filterStr="", initialFilter="", |
|
833 withRemote=True |
|
834 ): |
|
835 """ |
|
836 Module function to get a list of names of files for opening and the |
|
837 selected file name filter. |
|
838 |
|
839 @param parent parent widget of the dialog (defaults to None) |
|
840 @type QWidget (optional) |
|
841 @param caption window title of the dialog (defaults to "") |
|
842 @type str (optional) |
|
843 @param directory working directory of the dialog (defaults to "") |
|
844 @type str (optional) |
|
845 @param filterStr filter string for the dialog (defaults to "") |
|
846 @type str (optional) |
|
847 @param initialFilter initial filter for the dialog (defaults to "") |
|
848 @type str (optional) |
|
849 @param withRemote flag indicating to create the file names with the remote |
|
850 indicator (defaults to True) |
|
851 @type bool (optional) |
|
852 @return tuple containing the list of file names to be opened and the |
|
853 selected file name filter |
|
854 @rtype tuple of (list of str, str) |
|
855 """ |
|
856 dlg = EricServerFileDialog( |
|
857 parent=parent, caption=caption, directory=directory, filter=filterStr |
|
858 ) |
|
859 dlg.setFileMode(FileMode.ExistingFiles) |
|
860 dlg.setNameFilter(initialFilter) |
|
861 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
862 if withRemote: |
|
863 filesList = [ |
|
864 FileSystemUtilities.remoteFileName(f) for f in dlg.selectedFiles() |
|
865 ] |
|
866 else: |
|
867 filesList = dlg.selectedFiles() |
|
868 selectedFilter = dlg.selectedNameFilter() |
|
869 else: |
|
870 filesList = [] |
|
871 selectedFilter = "" |
|
872 |
|
873 return filesList, selectedFilter |
|
874 |
|
875 |
|
876 def getSaveFileName( |
|
877 parent=None, caption="", directory="", filterStr="", initialFilter="", |
|
878 withRemote=True |
|
879 ): |
|
880 """ |
|
881 Module function to get the name of a file for saving. |
|
882 |
|
883 @param parent parent widget of the dialog (defaults to None) |
|
884 @type QWidget (optional) |
|
885 @param caption window title of the dialog (defaults to "") |
|
886 @type str (optional) |
|
887 @param directory working directory of the dialog (defaults to "") |
|
888 @type str (optional) |
|
889 @param filterStr filter string for the dialog (defaults to "") |
|
890 @type str (optional) |
|
891 @param initialFilter initial filter for the dialog (defaults to "") |
|
892 @type str (optional) |
|
893 @param withRemote flag indicating to create the file names with the remote |
|
894 indicator (defaults to True) |
|
895 @type bool (optional) |
|
896 @return name of file to be saved |
|
897 @rtype str |
|
898 """ |
|
899 return getSaveFileNameAndFilter( |
|
900 parent, caption, directory, filterStr, initialFilter, withRemote |
|
901 )[0] |
|
902 |
|
903 |
|
904 def getSaveFileNameAndFilter( |
|
905 parent=None, caption="", directory="", filterStr="", initialFilter="", |
|
906 withRemote=True |
|
907 ): |
|
908 """ |
|
909 Module function to get the name of a file for saving and the selected file name |
|
910 filter. |
|
911 |
|
912 @param parent parent widget of the dialog (defaults to None) |
|
913 @type QWidget (optional) |
|
914 @param caption window title of the dialog (defaults to "") |
|
915 @type str (optional) |
|
916 @param directory working directory of the dialog (defaults to "") |
|
917 @type str (optional) |
|
918 @param filterStr filter string for the dialog (defaults to "") |
|
919 @type str (optional) |
|
920 @param initialFilter initial filter for the dialog (defaults to "") |
|
921 @type str (optional) |
|
922 @param withRemote flag indicating to create the file names with the remote |
|
923 indicator (defaults to True) |
|
924 @type bool (optional) |
|
925 @return name of file to be saved and selected filte |
|
926 @rtype tuple of (str, str) |
|
927 """ |
|
928 dlg = EricServerFileDialog( |
|
929 parent=parent, caption=caption, directory=directory, filter=filterStr |
|
930 ) |
|
931 dlg.setFileMode(FileMode.AnyFile) |
|
932 dlg.setNameFilter(initialFilter) |
|
933 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
934 if withRemote: |
|
935 fileName = FileSystemUtilities.remoteFileName(dlg.selectedFiles()[0]) |
|
936 else: |
|
937 fileName = dlg.selectedFiles()[0] |
|
938 selectedFilter = dlg.selectedNameFilter() |
|
939 else: |
|
940 fileName = "" |
|
941 selectedFilter = "" |
|
942 |
|
943 return fileName, selectedFilter |
|
944 |
|
945 |
|
946 def getExistingDirectory( |
|
947 parent=None, caption="", directory="", dirsOnly=True, withRemote=True |
|
948 ): |
|
949 """ |
|
950 Module function to get the name of a directory. |
|
951 |
|
952 @param parent parent widget of the dialog (defaults to None) |
|
953 @type QWidget (optional) |
|
954 @param caption window title of the dialog (defaults to "") |
|
955 @type str (optional) |
|
956 @param directory working directory of the dialog (defaults to "") |
|
957 @type str (optional) |
|
958 @param dirsOnly flag indicating to just show directories (defaults to True) |
|
959 @type bool (optional) |
|
960 @param withRemote flag indicating to create the file names with the remote |
|
961 indicator (defaults to True) |
|
962 @type bool (optional) |
|
963 @return name of selected directory |
|
964 @rtype str |
|
965 """ |
|
966 dlg = EricServerFileDialog(parent=parent, caption=caption, directory=directory) |
|
967 dlg.setFileMode(FileMode.Directory) |
|
968 dlg.setDirectoriesOnly(dirsOnly) |
|
969 if dlg.exec() == QDialog.DialogCode.Accepted: |
|
970 if withRemote: |
|
971 dirName = FileSystemUtilities.remoteFileName(dlg.selectedFiles()[0]) |
|
972 else: |
|
973 dirName = dlg.selectedFiles()[0] |
|
974 else: |
|
975 dirName = "" |
|
976 |
|
977 return dirName |