32 |
36 |
33 |
37 |
34 class DownloadManager(QDialog, Ui_DownloadManager): |
38 class DownloadManager(QDialog, Ui_DownloadManager): |
35 """ |
39 """ |
36 Class implementing the download manager. |
40 Class implementing the download manager. |
37 |
41 |
38 @signal downloadsCountChanged() emitted to indicate a change of the |
42 @signal downloadsCountChanged() emitted to indicate a change of the |
39 count of download items |
43 count of download items |
40 """ |
44 """ |
|
45 |
41 RemoveNever = 0 |
46 RemoveNever = 0 |
42 RemoveExit = 1 |
47 RemoveExit = 1 |
43 RemoveSuccessFullDownload = 2 |
48 RemoveSuccessFullDownload = 2 |
44 |
49 |
45 UpdateTimerTimeout = 1000 |
50 UpdateTimerTimeout = 1000 |
46 |
51 |
47 downloadsCountChanged = pyqtSignal() |
52 downloadsCountChanged = pyqtSignal() |
48 |
53 |
49 def __init__(self, parent=None): |
54 def __init__(self, parent=None): |
50 """ |
55 """ |
51 Constructor |
56 Constructor |
52 |
57 |
53 @param parent reference to the parent widget (QWidget) |
58 @param parent reference to the parent widget (QWidget) |
54 """ |
59 """ |
55 super().__init__(parent) |
60 super().__init__(parent) |
56 self.setupUi(self) |
61 self.setupUi(self) |
57 self.setWindowFlags(Qt.WindowType.Window) |
62 self.setWindowFlags(Qt.WindowType.Window) |
58 |
63 |
59 self.__winTaskbarButton = None |
64 self.__winTaskbarButton = None |
60 |
65 |
61 self.__saveTimer = AutoSaver(self, self.save) |
66 self.__saveTimer = AutoSaver(self, self.save) |
62 |
67 |
63 self.__model = DownloadModel(self) |
68 self.__model = DownloadModel(self) |
64 self.__manager = WebBrowserWindow.networkManager() |
69 self.__manager = WebBrowserWindow.networkManager() |
65 |
70 |
66 self.__iconProvider = None |
71 self.__iconProvider = None |
67 self.__downloads = [] |
72 self.__downloads = [] |
68 self.__downloadDirectory = "" |
73 self.__downloadDirectory = "" |
69 self.__loaded = False |
74 self.__loaded = False |
70 |
75 |
71 self.__rowHeightMultiplier = 1.1 |
76 self.__rowHeightMultiplier = 1.1 |
72 |
77 |
73 self.setDownloadDirectory(Preferences.getUI("DownloadPath")) |
78 self.setDownloadDirectory(Preferences.getUI("DownloadPath")) |
74 |
79 |
75 self.downloadsView.setShowGrid(False) |
80 self.downloadsView.setShowGrid(False) |
76 self.downloadsView.verticalHeader().hide() |
81 self.downloadsView.verticalHeader().hide() |
77 self.downloadsView.horizontalHeader().hide() |
82 self.downloadsView.horizontalHeader().hide() |
78 self.downloadsView.setAlternatingRowColors(True) |
83 self.downloadsView.setAlternatingRowColors(True) |
79 self.downloadsView.horizontalHeader().setStretchLastSection(True) |
84 self.downloadsView.horizontalHeader().setStretchLastSection(True) |
80 self.downloadsView.setModel(self.__model) |
85 self.downloadsView.setModel(self.__model) |
81 self.downloadsView.setContextMenuPolicy( |
86 self.downloadsView.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) |
82 Qt.ContextMenuPolicy.CustomContextMenu) |
|
83 self.downloadsView.customContextMenuRequested.connect( |
87 self.downloadsView.customContextMenuRequested.connect( |
84 self.__customContextMenuRequested) |
88 self.__customContextMenuRequested |
85 |
89 ) |
|
90 |
86 self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self) |
91 self.__clearShortcut = QShortcut(QKeySequence("Ctrl+L"), self) |
87 self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked) |
92 self.__clearShortcut.activated.connect(self.on_cleanupButton_clicked) |
88 |
93 |
89 self.__load() |
94 self.__load() |
90 |
95 |
91 self.__updateTimer = QBasicTimer() |
96 self.__updateTimer = QBasicTimer() |
92 |
97 |
93 def __customContextMenuRequested(self, pos): |
98 def __customContextMenuRequested(self, pos): |
94 """ |
99 """ |
95 Private slot to handle the context menu request for the bookmarks tree. |
100 Private slot to handle the context menu request for the bookmarks tree. |
96 |
101 |
97 @param pos position the context menu was requested (QPoint) |
102 @param pos position the context menu was requested (QPoint) |
98 """ |
103 """ |
99 menu = QMenu() |
104 menu = QMenu() |
100 |
105 |
101 selectedRowsCount = len( |
106 selectedRowsCount = len(self.downloadsView.selectionModel().selectedRows()) |
102 self.downloadsView.selectionModel().selectedRows()) |
107 |
103 |
|
104 if selectedRowsCount == 1: |
108 if selectedRowsCount == 1: |
105 row = self.downloadsView.selectionModel().selectedRows()[0].row() |
109 row = self.downloadsView.selectionModel().selectedRows()[0].row() |
106 itm = self.__downloads[row] |
110 itm = self.__downloads[row] |
107 if itm.downloadedSuccessfully(): |
111 if itm.downloadedSuccessfully(): |
108 menu.addAction( |
112 menu.addAction( |
109 UI.PixmapCache.getIcon("open"), |
113 UI.PixmapCache.getIcon("open"), |
110 self.tr("Open"), self.__contextMenuOpen) |
114 self.tr("Open"), |
|
115 self.__contextMenuOpen, |
|
116 ) |
111 elif itm.downloading(): |
117 elif itm.downloading(): |
112 menu.addAction( |
118 menu.addAction( |
113 UI.PixmapCache.getIcon("stopLoading"), |
119 UI.PixmapCache.getIcon("stopLoading"), |
114 self.tr("Cancel"), self.__contextMenuCancel) |
120 self.tr("Cancel"), |
|
121 self.__contextMenuCancel, |
|
122 ) |
115 menu.addSeparator() |
123 menu.addSeparator() |
116 menu.addAction( |
124 menu.addAction( |
117 self.tr("Open Containing Folder"), |
125 self.tr("Open Containing Folder"), self.__contextMenuOpenFolder |
118 self.__contextMenuOpenFolder) |
126 ) |
119 menu.addSeparator() |
127 menu.addSeparator() |
120 menu.addAction( |
128 menu.addAction(self.tr("Go to Download Page"), self.__contextMenuGotoPage) |
121 self.tr("Go to Download Page"), |
129 menu.addAction(self.tr("Copy Download Link"), self.__contextMenuCopyLink) |
122 self.__contextMenuGotoPage) |
|
123 menu.addAction( |
|
124 self.tr("Copy Download Link"), |
|
125 self.__contextMenuCopyLink) |
|
126 menu.addSeparator() |
130 menu.addSeparator() |
127 menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll) |
131 menu.addAction(self.tr("Select All"), self.__contextMenuSelectAll) |
128 if ( |
132 if selectedRowsCount > 1 or ( |
129 selectedRowsCount > 1 or |
133 selectedRowsCount == 1 |
130 (selectedRowsCount == 1 and |
134 and not self.__downloads[ |
131 not self.__downloads[ |
135 self.downloadsView.selectionModel().selectedRows()[0].row() |
132 self.downloadsView.selectionModel().selectedRows()[0].row()] |
136 ].downloading() |
133 .downloading()) |
|
134 ): |
137 ): |
135 menu.addSeparator() |
138 menu.addSeparator() |
136 menu.addAction( |
139 menu.addAction( |
137 self.tr("Remove From List"), |
140 self.tr("Remove From List"), self.__contextMenuRemoveSelected |
138 self.__contextMenuRemoveSelected) |
141 ) |
139 |
142 |
140 menu.exec(QCursor.pos()) |
143 menu.exec(QCursor.pos()) |
141 |
144 |
142 def shutdown(self): |
145 def shutdown(self): |
143 """ |
146 """ |
144 Public method to stop the download manager. |
147 Public method to stop the download manager. |
145 """ |
148 """ |
146 self.save() |
149 self.save() |
147 self.close() |
150 self.close() |
148 |
151 |
149 def activeDownloadsCount(self): |
152 def activeDownloadsCount(self): |
150 """ |
153 """ |
151 Public method to get the number of active downloads. |
154 Public method to get the number of active downloads. |
152 |
155 |
153 @return number of active downloads (integer) |
156 @return number of active downloads (integer) |
154 """ |
157 """ |
155 count = 0 |
158 count = 0 |
156 |
159 |
157 for download in self.__downloads: |
160 for download in self.__downloads: |
158 if download.downloading(): |
161 if download.downloading(): |
159 count += 1 |
162 count += 1 |
160 return count |
163 return count |
161 |
164 |
162 def allowQuit(self): |
165 def allowQuit(self): |
163 """ |
166 """ |
164 Public method to check, if it is ok to quit. |
167 Public method to check, if it is ok to quit. |
165 |
168 |
166 @return flag indicating allowance to quit (boolean) |
169 @return flag indicating allowance to quit (boolean) |
167 """ |
170 """ |
168 if self.activeDownloadsCount() > 0: |
171 if self.activeDownloadsCount() > 0: |
169 res = EricMessageBox.yesNo( |
172 res = EricMessageBox.yesNo( |
170 self, |
173 self, |
171 self.tr(""), |
174 self.tr(""), |
172 self.tr("""There are %n downloads in progress.\n""" |
175 self.tr( |
173 """Do you want to quit anyway?""", "", |
176 """There are %n downloads in progress.\n""" |
174 self.activeDownloadsCount()), |
177 """Do you want to quit anyway?""", |
175 icon=EricMessageBox.Warning) |
178 "", |
|
179 self.activeDownloadsCount(), |
|
180 ), |
|
181 icon=EricMessageBox.Warning, |
|
182 ) |
176 if not res: |
183 if not res: |
177 self.show() |
184 self.show() |
178 return False |
185 return False |
179 |
186 |
180 self.close() |
187 self.close() |
181 return True |
188 return True |
182 |
189 |
183 def __testWebBrowserView(self, view, url): |
190 def __testWebBrowserView(self, view, url): |
184 """ |
191 """ |
185 Private method to test a web browser view against an URL. |
192 Private method to test a web browser view against an URL. |
186 |
193 |
187 @param view reference to the web browser view to be tested |
194 @param view reference to the web browser view to be tested |
188 @type WebBrowserView |
195 @type WebBrowserView |
189 @param url URL to test against |
196 @param url URL to test against |
190 @type QUrl |
197 @type QUrl |
191 @return flag indicating, that the view is the one for the URL |
198 @return flag indicating, that the view is the one for the URL |
192 @rtype bool |
199 @rtype bool |
193 """ |
200 """ |
194 if view.tabWidget().count() < 2: |
201 if view.tabWidget().count() < 2: |
195 return False |
202 return False |
196 |
203 |
197 page = view.page() |
204 page = view.page() |
198 if page.history().count() != 0: |
205 if page.history().count() != 0: |
199 return False |
206 return False |
200 |
207 |
201 if ( |
208 if not page.url().isEmpty() and page.url().host() == url.host(): |
202 not page.url().isEmpty() and |
|
203 page.url().host() == url.host() |
|
204 ): |
|
205 return True |
209 return True |
206 |
210 |
207 requestedUrl = page.requestedUrl() |
211 requestedUrl = page.requestedUrl() |
208 if requestedUrl.isEmpty(): |
212 if requestedUrl.isEmpty(): |
209 requestedUrl = QUrl(view.tabWidget().urlBarForView(view).text()) |
213 requestedUrl = QUrl(view.tabWidget().urlBarForView(view).text()) |
210 return requestedUrl.isEmpty() or requestedUrl.host() == url.host() |
214 return requestedUrl.isEmpty() or requestedUrl.host() == url.host() |
211 |
215 |
212 def __closeDownloadTab(self, url): |
216 def __closeDownloadTab(self, url): |
213 """ |
217 """ |
214 Private method to close an empty tab, that was opened only for loading |
218 Private method to close an empty tab, that was opened only for loading |
215 the download URL. |
219 the download URL. |
216 |
220 |
217 @param url download URL |
221 @param url download URL |
218 @type QUrl |
222 @type QUrl |
219 """ |
223 """ |
220 if self.__testWebBrowserView( |
224 if self.__testWebBrowserView( |
221 WebBrowserWindow.getWindow().currentBrowser(), url): |
225 WebBrowserWindow.getWindow().currentBrowser(), url |
|
226 ): |
222 WebBrowserWindow.getWindow().closeCurrentBrowser() |
227 WebBrowserWindow.getWindow().closeCurrentBrowser() |
223 return |
228 return |
224 |
229 |
225 for window in WebBrowserWindow.mainWindows(): |
230 for window in WebBrowserWindow.mainWindows(): |
226 for browser in window.browsers(): |
231 for browser in window.browsers(): |
227 if self.__testWebBrowserView(browser, url): |
232 if self.__testWebBrowserView(browser, url): |
228 window.closeBrowser(browser) |
233 window.closeBrowser(browser) |
229 return |
234 return |
230 |
235 |
231 def download(self, downloadRequest): |
236 def download(self, downloadRequest): |
232 """ |
237 """ |
233 Public method to download a file. |
238 Public method to download a file. |
234 |
239 |
235 @param downloadRequest reference to the download object containing the |
240 @param downloadRequest reference to the download object containing the |
236 download data. |
241 download data. |
237 @type QWebEngineDownloadRequest |
242 @type QWebEngineDownloadRequest |
238 """ |
243 """ |
239 url = downloadRequest.url() |
244 url = downloadRequest.url() |
240 if url.isEmpty(): |
245 if url.isEmpty(): |
241 return |
246 return |
242 |
247 |
243 self.__closeDownloadTab(url) |
248 self.__closeDownloadTab(url) |
244 |
249 |
245 # Safe Browsing |
250 # Safe Browsing |
246 from WebBrowser.SafeBrowsing.SafeBrowsingManager import ( |
251 from WebBrowser.SafeBrowsing.SafeBrowsingManager import SafeBrowsingManager |
247 SafeBrowsingManager |
252 |
248 ) |
|
249 if SafeBrowsingManager.isEnabled(): |
253 if SafeBrowsingManager.isEnabled(): |
250 threatLists = ( |
254 threatLists = WebBrowserWindow.safeBrowsingManager().lookupUrl(url)[0] |
251 WebBrowserWindow.safeBrowsingManager().lookupUrl(url)[0] |
|
252 ) |
|
253 if threatLists: |
255 if threatLists: |
254 threatMessages = ( |
256 threatMessages = ( |
255 WebBrowserWindow.safeBrowsingManager() |
257 WebBrowserWindow.safeBrowsingManager().getThreatMessages( |
256 .getThreatMessages(threatLists) |
258 threatLists |
|
259 ) |
257 ) |
260 ) |
258 res = EricMessageBox.warning( |
261 res = EricMessageBox.warning( |
259 WebBrowserWindow.getWindow(), |
262 WebBrowserWindow.getWindow(), |
260 self.tr("Suspicuous URL detected"), |
263 self.tr("Suspicuous URL detected"), |
261 self.tr("<p>The URL <b>{0}</b> was found in the Safe" |
264 self.tr( |
262 " Browsing database.</p>{1}").format( |
265 "<p>The URL <b>{0}</b> was found in the Safe" |
263 url.toString(), "".join(threatMessages)), |
266 " Browsing database.</p>{1}" |
|
267 ).format(url.toString(), "".join(threatMessages)), |
264 EricMessageBox.Abort | EricMessageBox.Ignore, |
268 EricMessageBox.Abort | EricMessageBox.Ignore, |
265 EricMessageBox.Abort) |
269 EricMessageBox.Abort, |
|
270 ) |
266 if res == EricMessageBox.Abort: |
271 if res == EricMessageBox.Abort: |
267 downloadRequest.cancel() |
272 downloadRequest.cancel() |
268 return |
273 return |
269 |
274 |
270 window = WebBrowserWindow.getWindow() |
275 window = WebBrowserWindow.getWindow() |
271 pageUrl = window.currentBrowser().url() if window else QUrl() |
276 pageUrl = window.currentBrowser().url() if window else QUrl() |
272 from .DownloadItem import DownloadItem |
277 from .DownloadItem import DownloadItem |
273 itm = DownloadItem(downloadRequest=downloadRequest, pageUrl=pageUrl, |
278 |
274 parent=self) |
279 itm = DownloadItem( |
|
280 downloadRequest=downloadRequest, pageUrl=pageUrl, parent=self |
|
281 ) |
275 self.__addItem(itm) |
282 self.__addItem(itm) |
276 |
283 |
277 if Preferences.getWebBrowser("DownloadManagerAutoOpen"): |
284 if Preferences.getWebBrowser("DownloadManagerAutoOpen"): |
278 self.show() |
285 self.show() |
279 else: |
286 else: |
280 self.__startUpdateTimer() |
287 self.__startUpdateTimer() |
281 |
288 |
282 def show(self): |
289 def show(self): |
283 """ |
290 """ |
284 Public slot to show the download manager dialog. |
291 Public slot to show the download manager dialog. |
285 """ |
292 """ |
286 self.__startUpdateTimer() |
293 self.__startUpdateTimer() |
287 |
294 |
288 super().show() |
295 super().show() |
289 self.activateWindow() |
296 self.activateWindow() |
290 self.raise_() |
297 self.raise_() |
291 |
298 |
292 def __addItem(self, itm, append=False): |
299 def __addItem(self, itm, append=False): |
293 """ |
300 """ |
294 Private method to add a download to the list of downloads. |
301 Private method to add a download to the list of downloads. |
295 |
302 |
296 @param itm reference to the download item |
303 @param itm reference to the download item |
297 @type DownloadItem |
304 @type DownloadItem |
298 @param append flag indicating to append the item |
305 @param append flag indicating to append the item |
299 @type bool |
306 @type bool |
300 """ |
307 """ |
301 itm.statusChanged.connect(lambda: self.__updateRow(itm)) |
308 itm.statusChanged.connect(lambda: self.__updateRow(itm)) |
302 itm.downloadFinished.connect(self.__finished) |
309 itm.downloadFinished.connect(self.__finished) |
303 |
310 |
304 # insert at top of window |
311 # insert at top of window |
305 row = self.downloadsCount() if append else 0 |
312 row = self.downloadsCount() if append else 0 |
306 self.__model.beginInsertRows(QModelIndex(), row, row) |
313 self.__model.beginInsertRows(QModelIndex(), row, row) |
307 if append: |
314 if append: |
308 self.__downloads.append(itm) |
315 self.__downloads.append(itm) |
309 else: |
316 else: |
310 self.__downloads.insert(0, itm) |
317 self.__downloads.insert(0, itm) |
311 self.__model.endInsertRows() |
318 self.__model.endInsertRows() |
312 |
319 |
313 self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm) |
320 self.downloadsView.setIndexWidget(self.__model.index(row, 0), itm) |
314 icon = self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon) |
321 icon = self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon) |
315 itm.setIcon(icon) |
322 itm.setIcon(icon) |
316 self.downloadsView.setRowHeight( |
323 self.downloadsView.setRowHeight( |
317 row, int(itm.sizeHint().height() * self.__rowHeightMultiplier)) |
324 row, int(itm.sizeHint().height() * self.__rowHeightMultiplier) |
|
325 ) |
318 # just in case the download finished before the constructor returned |
326 # just in case the download finished before the constructor returned |
319 self.__updateRow(itm) |
327 self.__updateRow(itm) |
320 self.changeOccurred() |
328 self.changeOccurred() |
321 |
329 |
322 self.downloadsCountChanged.emit() |
330 self.downloadsCountChanged.emit() |
323 |
331 |
324 def __updateRow(self, itm): |
332 def __updateRow(self, itm): |
325 """ |
333 """ |
326 Private slot to update a download item. |
334 Private slot to update a download item. |
327 |
335 |
328 @param itm reference to the download item |
336 @param itm reference to the download item |
329 @type DownloadItem |
337 @type DownloadItem |
330 """ |
338 """ |
331 if itm not in self.__downloads: |
339 if itm not in self.__downloads: |
332 return |
340 return |
333 |
341 |
334 row = self.__downloads.index(itm) |
342 row = self.__downloads.index(itm) |
335 |
343 |
336 if self.__iconProvider is None: |
344 if self.__iconProvider is None: |
337 self.__iconProvider = QFileIconProvider() |
345 self.__iconProvider = QFileIconProvider() |
338 |
346 |
339 icon = self.__iconProvider.icon(QFileInfo(itm.fileName())) |
347 icon = self.__iconProvider.icon(QFileInfo(itm.fileName())) |
340 if icon.isNull(): |
348 if icon.isNull(): |
341 icon = self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon) |
349 icon = self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon) |
342 itm.setIcon(icon) |
350 itm.setIcon(icon) |
343 |
351 |
344 self.downloadsView.setRowHeight( |
352 self.downloadsView.setRowHeight( |
345 row, |
353 row, int(itm.minimumSizeHint().height() * self.__rowHeightMultiplier) |
346 int(itm.minimumSizeHint().height() * self.__rowHeightMultiplier)) |
354 ) |
347 |
355 |
348 remove = False |
356 remove = False |
349 |
357 |
350 if ( |
358 if ( |
351 itm.downloadedSuccessfully() and |
359 itm.downloadedSuccessfully() |
352 self.removePolicy() == DownloadManager.RemoveSuccessFullDownload |
360 and self.removePolicy() == DownloadManager.RemoveSuccessFullDownload |
353 ): |
361 ): |
354 remove = True |
362 remove = True |
355 |
363 |
356 if remove: |
364 if remove: |
357 self.__model.removeRow(row) |
365 self.__model.removeRow(row) |
358 |
366 |
359 self.cleanupButton.setEnabled( |
367 self.cleanupButton.setEnabled( |
360 (self.downloadsCount() - self.activeDownloadsCount()) > 0) |
368 (self.downloadsCount() - self.activeDownloadsCount()) > 0 |
361 |
369 ) |
|
370 |
362 # record the change |
371 # record the change |
363 self.changeOccurred() |
372 self.changeOccurred() |
364 |
373 |
365 def removePolicy(self): |
374 def removePolicy(self): |
366 """ |
375 """ |
367 Public method to get the remove policy. |
376 Public method to get the remove policy. |
368 |
377 |
369 @return remove policy (integer) |
378 @return remove policy (integer) |
370 """ |
379 """ |
371 return Preferences.getWebBrowser("DownloadManagerRemovePolicy") |
380 return Preferences.getWebBrowser("DownloadManagerRemovePolicy") |
372 |
381 |
373 def setRemovePolicy(self, policy): |
382 def setRemovePolicy(self, policy): |
374 """ |
383 """ |
375 Public method to set the remove policy. |
384 Public method to set the remove policy. |
376 |
385 |
377 @param policy policy to be set |
386 @param policy policy to be set |
378 (DownloadManager.RemoveExit, DownloadManager.RemoveNever, |
387 (DownloadManager.RemoveExit, DownloadManager.RemoveNever, |
379 DownloadManager.RemoveSuccessFullDownload) |
388 DownloadManager.RemoveSuccessFullDownload) |
380 """ |
389 """ |
381 if policy in (DownloadManager.RemoveExit, |
390 if policy in ( |
382 DownloadManager.RemoveNever, |
391 DownloadManager.RemoveExit, |
383 DownloadManager.RemoveSuccessFullDownload): |
392 DownloadManager.RemoveNever, |
384 |
393 DownloadManager.RemoveSuccessFullDownload, |
|
394 ): |
|
395 |
385 if policy == self.removePolicy(): |
396 if policy == self.removePolicy(): |
386 return |
397 return |
387 |
398 |
388 Preferences.setWebBrowser("DownloadManagerRemovePolicy", |
399 Preferences.setWebBrowser("DownloadManagerRemovePolicy", self.policy) |
389 self.policy) |
400 |
390 |
|
391 def save(self): |
401 def save(self): |
392 """ |
402 """ |
393 Public method to save the download settings. |
403 Public method to save the download settings. |
394 """ |
404 """ |
395 if not self.__loaded: |
405 if not self.__loaded: |
396 return |
406 return |
397 |
407 |
398 Preferences.setWebBrowser("DownloadManagerSize", self.size()) |
408 Preferences.setWebBrowser("DownloadManagerSize", self.size()) |
399 Preferences.setWebBrowser("DownloadManagerPosition", self.pos()) |
409 Preferences.setWebBrowser("DownloadManagerPosition", self.pos()) |
400 if self.removePolicy() == DownloadManager.RemoveExit: |
410 if self.removePolicy() == DownloadManager.RemoveExit: |
401 return |
411 return |
402 |
412 |
403 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
413 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
414 |
404 if WebBrowserWindow.isPrivate(): |
415 if WebBrowserWindow.isPrivate(): |
405 return |
416 return |
406 |
417 |
407 downloads = [] |
418 downloads = [] |
408 for download in self.__downloads: |
419 for download in self.__downloads: |
409 downloads.append(download.getData()) |
420 downloads.append(download.getData()) |
410 Preferences.setWebBrowser("DownloadManagerDownloads", downloads) |
421 Preferences.setWebBrowser("DownloadManagerDownloads", downloads) |
411 |
422 |
412 def __load(self): |
423 def __load(self): |
413 """ |
424 """ |
414 Private method to load the download settings. |
425 Private method to load the download settings. |
415 """ |
426 """ |
416 if self.__loaded: |
427 if self.__loaded: |
417 return |
428 return |
418 |
429 |
419 size = Preferences.getWebBrowser("DownloadManagerSize") |
430 size = Preferences.getWebBrowser("DownloadManagerSize") |
420 if size.isValid(): |
431 if size.isValid(): |
421 self.resize(size) |
432 self.resize(size) |
422 pos = Preferences.getWebBrowser("DownloadManagerPosition") |
433 pos = Preferences.getWebBrowser("DownloadManagerPosition") |
423 self.move(pos) |
434 self.move(pos) |
424 |
435 |
425 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
436 from WebBrowser.WebBrowserWindow import WebBrowserWindow |
|
437 |
426 if not WebBrowserWindow.isPrivate(): |
438 if not WebBrowserWindow.isPrivate(): |
427 downloads = Preferences.getWebBrowser("DownloadManagerDownloads") |
439 downloads = Preferences.getWebBrowser("DownloadManagerDownloads") |
428 for download in downloads: |
440 for download in downloads: |
429 if ( |
441 if not download["URL"].isEmpty() and bool(download["Location"]): |
430 not download["URL"].isEmpty() and |
|
431 bool(download["Location"]) |
|
432 ): |
|
433 from .DownloadItem import DownloadItem |
442 from .DownloadItem import DownloadItem |
|
443 |
434 itm = DownloadItem(parent=self) |
444 itm = DownloadItem(parent=self) |
435 itm.setData(download) |
445 itm.setData(download) |
436 self.__addItem(itm, append=True) |
446 self.__addItem(itm, append=True) |
437 self.cleanupButton.setEnabled( |
447 self.cleanupButton.setEnabled( |
438 (self.downloadsCount() - self.activeDownloadsCount()) > 0) |
448 (self.downloadsCount() - self.activeDownloadsCount()) > 0 |
439 |
449 ) |
|
450 |
440 self.__loaded = True |
451 self.__loaded = True |
441 |
452 |
442 self.downloadsCountChanged.emit() |
453 self.downloadsCountChanged.emit() |
443 |
454 |
444 def closeEvent(self, evt): |
455 def closeEvent(self, evt): |
445 """ |
456 """ |
446 Protected event handler for the close event. |
457 Protected event handler for the close event. |
447 |
458 |
448 @param evt reference to the close event |
459 @param evt reference to the close event |
449 @type QCloseEvent |
460 @type QCloseEvent |
450 """ |
461 """ |
451 self.save() |
462 self.save() |
452 |
463 |
453 def cleanup(self): |
464 def cleanup(self): |
454 """ |
465 """ |
455 Public slot to cleanup the downloads. |
466 Public slot to cleanup the downloads. |
456 """ |
467 """ |
457 self.on_cleanupButton_clicked() |
468 self.on_cleanupButton_clicked() |
458 |
469 |
459 @pyqtSlot() |
470 @pyqtSlot() |
460 def on_cleanupButton_clicked(self): |
471 def on_cleanupButton_clicked(self): |
461 """ |
472 """ |
462 Private slot to cleanup the downloads. |
473 Private slot to cleanup the downloads. |
463 """ |
474 """ |
464 if self.downloadsCount() == 0: |
475 if self.downloadsCount() == 0: |
465 return |
476 return |
466 |
477 |
467 self.__model.removeRows(0, self.downloadsCount()) |
478 self.__model.removeRows(0, self.downloadsCount()) |
468 if ( |
479 if self.downloadsCount() == 0 and self.__iconProvider is not None: |
469 self.downloadsCount() == 0 and |
|
470 self.__iconProvider is not None |
|
471 ): |
|
472 self.__iconProvider = None |
480 self.__iconProvider = None |
473 |
481 |
474 self.changeOccurred() |
482 self.changeOccurred() |
475 |
483 |
476 self.downloadsCountChanged.emit() |
484 self.downloadsCountChanged.emit() |
477 |
485 |
478 def __finished(self, success): |
486 def __finished(self, success): |
479 """ |
487 """ |
480 Private slot to handle a finished download. |
488 Private slot to handle a finished download. |
481 |
489 |
482 @param success flag indicating a successful download |
490 @param success flag indicating a successful download |
483 @type bool |
491 @type bool |
484 """ |
492 """ |
485 if self.isVisible(): |
493 if self.isVisible(): |
486 QApplication.alert(self) |
494 QApplication.alert(self) |
487 |
495 |
488 self.downloadsCountChanged.emit() |
496 self.downloadsCountChanged.emit() |
489 |
497 |
490 if self.activeDownloadsCount() == 0: |
498 if self.activeDownloadsCount() == 0: |
491 # all active downloads are done |
499 # all active downloads are done |
492 if success and ericApp().activeWindow() is not self: |
500 if success and ericApp().activeWindow() is not self: |
493 WebBrowserWindow.showNotification( |
501 WebBrowserWindow.showNotification( |
494 UI.PixmapCache.getPixmap("downloads48"), |
502 UI.PixmapCache.getPixmap("downloads48"), |
495 self.tr("Downloads finished"), |
503 self.tr("Downloads finished"), |
496 self.tr("All files have been downloaded.") |
504 self.tr("All files have been downloaded."), |
497 ) |
505 ) |
498 if not Preferences.getWebBrowser("DownloadManagerAutoClose"): |
506 if not Preferences.getWebBrowser("DownloadManagerAutoClose"): |
499 self.raise_() |
507 self.raise_() |
500 self.activateWindow() |
508 self.activateWindow() |
501 |
509 |
502 self.__stopUpdateTimer() |
510 self.__stopUpdateTimer() |
503 self.infoLabel.clear() |
511 self.infoLabel.clear() |
504 self.setWindowTitle(self.tr("Download Manager")) |
512 self.setWindowTitle(self.tr("Download Manager")) |
505 if Globals.isWindowsPlatform(): |
513 if Globals.isWindowsPlatform(): |
506 self.__taskbarButton().progress().hide() |
514 self.__taskbarButton().progress().hide() |
507 |
515 |
508 if Preferences.getWebBrowser("DownloadManagerAutoClose"): |
516 if Preferences.getWebBrowser("DownloadManagerAutoClose"): |
509 self.close() |
517 self.close() |
510 |
518 |
511 def setDownloadDirectory(self, directory): |
519 def setDownloadDirectory(self, directory): |
512 """ |
520 """ |
513 Public method to set the current download directory. |
521 Public method to set the current download directory. |
514 |
522 |
515 @param directory current download directory (string) |
523 @param directory current download directory (string) |
516 """ |
524 """ |
517 self.__downloadDirectory = directory |
525 self.__downloadDirectory = directory |
518 if self.__downloadDirectory != "": |
526 if self.__downloadDirectory != "": |
519 self.__downloadDirectory += "/" |
527 self.__downloadDirectory += "/" |
520 |
528 |
521 def downloadDirectory(self): |
529 def downloadDirectory(self): |
522 """ |
530 """ |
523 Public method to get the current download directory. |
531 Public method to get the current download directory. |
524 |
532 |
525 @return current download directory (string) |
533 @return current download directory (string) |
526 """ |
534 """ |
527 return self.__downloadDirectory |
535 return self.__downloadDirectory |
528 |
536 |
529 def downloadsCount(self): |
537 def downloadsCount(self): |
530 """ |
538 """ |
531 Public method to get the number of downloads. |
539 Public method to get the number of downloads. |
532 |
540 |
533 @return number of downloads |
541 @return number of downloads |
534 @rtype int |
542 @rtype int |
535 """ |
543 """ |
536 return len(self.__downloads) |
544 return len(self.__downloads) |
537 |
545 |
538 def downloads(self): |
546 def downloads(self): |
539 """ |
547 """ |
540 Public method to get a reference to the downloads. |
548 Public method to get a reference to the downloads. |
541 |
549 |
542 @return reference to the downloads (list of DownloadItem) |
550 @return reference to the downloads (list of DownloadItem) |
543 """ |
551 """ |
544 return self.__downloads |
552 return self.__downloads |
545 |
553 |
546 def changeOccurred(self): |
554 def changeOccurred(self): |
547 """ |
555 """ |
548 Public method to signal a change. |
556 Public method to signal a change. |
549 """ |
557 """ |
550 self.__saveTimer.changeOccurred() |
558 self.__saveTimer.changeOccurred() |
551 |
559 |
552 def __taskbarButton(self): |
560 def __taskbarButton(self): |
553 """ |
561 """ |
554 Private method to get a reference to the task bar button (Windows |
562 Private method to get a reference to the task bar button (Windows |
555 only). |
563 only). |
556 |
564 |
557 @return reference to the task bar button |
565 @return reference to the task bar button |
558 @rtype QWinTaskbarButton or None |
566 @rtype QWinTaskbarButton or None |
559 """ |
567 """ |
560 if Globals.isWindowsPlatform(): |
568 if Globals.isWindowsPlatform(): |
561 from PyQt6.QtWinExtras import QWinTaskbarButton |
569 from PyQt6.QtWinExtras import QWinTaskbarButton |
|
570 |
562 if self.__winTaskbarButton is None: |
571 if self.__winTaskbarButton is None: |
563 window = WebBrowserWindow.mainWindow() |
572 window = WebBrowserWindow.mainWindow() |
564 self.__winTaskbarButton = QWinTaskbarButton( |
573 self.__winTaskbarButton = QWinTaskbarButton(window.windowHandle()) |
565 window.windowHandle()) |
|
566 self.__winTaskbarButton.progress().setRange(0, 100) |
574 self.__winTaskbarButton.progress().setRange(0, 100) |
567 |
575 |
568 return self.__winTaskbarButton |
576 return self.__winTaskbarButton |
569 |
577 |
570 def timerEvent(self, evt): |
578 def timerEvent(self, evt): |
571 """ |
579 """ |
572 Protected event handler for timer events. |
580 Protected event handler for timer events. |
573 |
581 |
574 @param evt reference to the timer event |
582 @param evt reference to the timer event |
575 @type QTimerEvent |
583 @type QTimerEvent |
576 """ |
584 """ |
577 if evt.timerId() == self.__updateTimer.timerId(): |
585 if evt.timerId() == self.__updateTimer.timerId(): |
578 if self.activeDownloadsCount() == 0: |
586 if self.activeDownloadsCount() == 0: |
582 if Globals.isWindowsPlatform(): |
590 if Globals.isWindowsPlatform(): |
583 self.__taskbarButton().progress().hide() |
591 self.__taskbarButton().progress().hide() |
584 else: |
592 else: |
585 progresses = [] |
593 progresses = [] |
586 for itm in self.__downloads: |
594 for itm in self.__downloads: |
587 if ( |
595 if itm is None or itm.downloadCanceled() or not itm.downloading(): |
588 itm is None or |
|
589 itm.downloadCanceled() or |
|
590 not itm.downloading() |
|
591 ): |
|
592 continue |
596 continue |
593 |
597 |
594 progresses.append(( |
598 progresses.append( |
595 itm.downloadProgress(), |
599 ( |
596 itm.remainingTime(), |
600 itm.downloadProgress(), |
597 itm.currentSpeed() |
601 itm.remainingTime(), |
598 )) |
602 itm.currentSpeed(), |
599 |
603 ) |
|
604 ) |
|
605 |
600 if not progresses: |
606 if not progresses: |
601 return |
607 return |
602 |
608 |
603 remaining = 0 |
609 remaining = 0 |
604 progress = 0 |
610 progress = 0 |
605 speed = 0.0 |
611 speed = 0.0 |
606 |
612 |
607 for progressData in progresses: |
613 for progressData in progresses: |
608 if progressData[1] > remaining: |
614 if progressData[1] > remaining: |
609 remaining = progressData[1] |
615 remaining = progressData[1] |
610 progress += progressData[0] |
616 progress += progressData[0] |
611 speed += progressData[2] |
617 speed += progressData[2] |
612 progress /= len(progresses) |
618 progress /= len(progresses) |
613 |
619 |
614 if self.isVisible(): |
620 if self.isVisible(): |
615 self.infoLabel.setText(self.tr( |
621 self.infoLabel.setText( |
616 "{0}% of %n file(s) ({1}) {2}", "", |
622 self.tr( |
617 len(progresses)).format( |
623 "{0}% of %n file(s) ({1}) {2}", "", len(progresses) |
618 progress, |
624 ).format( |
619 speedString(speed), |
625 progress, |
620 timeString(remaining), |
626 speedString(speed), |
621 )) |
627 timeString(remaining), |
|
628 ) |
|
629 ) |
622 self.setWindowTitle(self.tr("{0}% - Download Manager")) |
630 self.setWindowTitle(self.tr("{0}% - Download Manager")) |
623 |
631 |
624 if Globals.isWindowsPlatform(): |
632 if Globals.isWindowsPlatform(): |
625 self.__taskbarButton().progress().show() |
633 self.__taskbarButton().progress().show() |
626 self.__taskbarButton().progress().setValue(progress) |
634 self.__taskbarButton().progress().setValue(progress) |
627 |
635 |
628 super().timerEvent(evt) |
636 super().timerEvent(evt) |
629 |
637 |
630 def __startUpdateTimer(self): |
638 def __startUpdateTimer(self): |
631 """ |
639 """ |
632 Private slot to start the update timer. |
640 Private slot to start the update timer. |
633 """ |
641 """ |
634 if self.activeDownloadsCount() and not self.__updateTimer.isActive(): |
642 if self.activeDownloadsCount() and not self.__updateTimer.isActive(): |
635 self.__updateTimer.start(DownloadManager.UpdateTimerTimeout, self) |
643 self.__updateTimer.start(DownloadManager.UpdateTimerTimeout, self) |
636 |
644 |
637 def __stopUpdateTimer(self): |
645 def __stopUpdateTimer(self): |
638 """ |
646 """ |
639 Private slot to stop the update timer. |
647 Private slot to stop the update timer. |
640 """ |
648 """ |
641 self.__updateTimer.stop() |
649 self.__updateTimer.stop() |
642 |
650 |
643 ########################################################################### |
651 ########################################################################### |
644 ## Context menu related methods below |
652 ## Context menu related methods below |
645 ########################################################################### |
653 ########################################################################### |
646 |
654 |
647 def __currentItem(self): |
655 def __currentItem(self): |
648 """ |
656 """ |
649 Private method to get a reference to the current item. |
657 Private method to get a reference to the current item. |
650 |
658 |
651 @return reference to the current item (DownloadItem) |
659 @return reference to the current item (DownloadItem) |
652 """ |
660 """ |
653 index = self.downloadsView.currentIndex() |
661 index = self.downloadsView.currentIndex() |
654 if index and index.isValid(): |
662 if index and index.isValid(): |
655 row = index.row() |
663 row = index.row() |
656 return self.__downloads[row] |
664 return self.__downloads[row] |
657 |
665 |
658 return None |
666 return None |
659 |
667 |
660 def __contextMenuOpen(self): |
668 def __contextMenuOpen(self): |
661 """ |
669 """ |
662 Private method to open the downloaded file. |
670 Private method to open the downloaded file. |
663 """ |
671 """ |
664 itm = self.__currentItem() |
672 itm = self.__currentItem() |
665 if itm is not None: |
673 if itm is not None: |
666 itm.openFile() |
674 itm.openFile() |
667 |
675 |
668 def __contextMenuOpenFolder(self): |
676 def __contextMenuOpenFolder(self): |
669 """ |
677 """ |
670 Private method to open the folder containing the downloaded file. |
678 Private method to open the folder containing the downloaded file. |
671 """ |
679 """ |
672 itm = self.__currentItem() |
680 itm = self.__currentItem() |
673 if itm is not None: |
681 if itm is not None: |
674 itm.openFolder() |
682 itm.openFolder() |
675 |
683 |
676 def __contextMenuCancel(self): |
684 def __contextMenuCancel(self): |
677 """ |
685 """ |
678 Private method to cancel the current download. |
686 Private method to cancel the current download. |
679 """ |
687 """ |
680 itm = self.__currentItem() |
688 itm = self.__currentItem() |
681 if itm is not None: |
689 if itm is not None: |
682 itm.cancelDownload() |
690 itm.cancelDownload() |
683 |
691 |
684 def __contextMenuGotoPage(self): |
692 def __contextMenuGotoPage(self): |
685 """ |
693 """ |
686 Private method to open the download page. |
694 Private method to open the download page. |
687 """ |
695 """ |
688 itm = self.__currentItem() |
696 itm = self.__currentItem() |
689 if itm is not None: |
697 if itm is not None: |
690 url = itm.getPageUrl() |
698 url = itm.getPageUrl() |
691 WebBrowserWindow.mainWindow().openUrl(url, "") |
699 WebBrowserWindow.mainWindow().openUrl(url, "") |
692 |
700 |
693 def __contextMenuCopyLink(self): |
701 def __contextMenuCopyLink(self): |
694 """ |
702 """ |
695 Private method to copy the download link to the clipboard. |
703 Private method to copy the download link to the clipboard. |
696 """ |
704 """ |
697 itm = self.__currentItem() |
705 itm = self.__currentItem() |
698 if itm is not None: |
706 if itm is not None: |
699 url = itm.getPageUrl().toDisplayString( |
707 url = itm.getPageUrl().toDisplayString( |
700 QUrl.ComponentFormattingOption.FullyDecoded) |
708 QUrl.ComponentFormattingOption.FullyDecoded |
|
709 ) |
701 QApplication.clipboard().setText(url) |
710 QApplication.clipboard().setText(url) |
702 |
711 |
703 def __contextMenuSelectAll(self): |
712 def __contextMenuSelectAll(self): |
704 """ |
713 """ |
705 Private method to select all downloads. |
714 Private method to select all downloads. |
706 """ |
715 """ |
707 self.downloadsView.selectAll() |
716 self.downloadsView.selectAll() |
708 |
717 |
709 def __contextMenuRemoveSelected(self): |
718 def __contextMenuRemoveSelected(self): |
710 """ |
719 """ |
711 Private method to remove the selected downloads from the list. |
720 Private method to remove the selected downloads from the list. |
712 """ |
721 """ |
713 self.downloadsView.removeSelected() |
722 self.downloadsView.removeSelected() |