49 |
67 |
50 |
68 |
51 class PluginRepositoryWidget(QWidget, Ui_PluginRepositoryDialog): |
69 class PluginRepositoryWidget(QWidget, Ui_PluginRepositoryDialog): |
52 """ |
70 """ |
53 Class implementing a dialog showing the available plugins. |
71 Class implementing a dialog showing the available plugins. |
54 |
72 |
55 @signal closeAndInstall() emitted when the Close & Install button is |
73 @signal closeAndInstall() emitted when the Close & Install button is |
56 pressed |
74 pressed |
57 """ |
75 """ |
|
76 |
58 closeAndInstall = pyqtSignal() |
77 closeAndInstall = pyqtSignal() |
59 |
78 |
60 DescrRole = Qt.ItemDataRole.UserRole |
79 DescrRole = Qt.ItemDataRole.UserRole |
61 UrlRole = Qt.ItemDataRole.UserRole + 1 |
80 UrlRole = Qt.ItemDataRole.UserRole + 1 |
62 FilenameRole = Qt.ItemDataRole.UserRole + 2 |
81 FilenameRole = Qt.ItemDataRole.UserRole + 2 |
63 AuthorRole = Qt.ItemDataRole.UserRole + 3 |
82 AuthorRole = Qt.ItemDataRole.UserRole + 3 |
64 |
83 |
65 PluginStatusUpToDate = 0 |
84 PluginStatusUpToDate = 0 |
66 PluginStatusNew = 1 |
85 PluginStatusNew = 1 |
67 PluginStatusLocalUpdate = 2 |
86 PluginStatusLocalUpdate = 2 |
68 PluginStatusRemoteUpdate = 3 |
87 PluginStatusRemoteUpdate = 3 |
69 PluginStatusError = 4 |
88 PluginStatusError = 4 |
70 |
89 |
71 def __init__(self, pluginManager, integrated=False, parent=None): |
90 def __init__(self, pluginManager, integrated=False, parent=None): |
72 """ |
91 """ |
73 Constructor |
92 Constructor |
74 |
93 |
75 @param pluginManager reference to the plugin manager object |
94 @param pluginManager reference to the plugin manager object |
76 @type PluginManager |
95 @type PluginManager |
77 @param integrated flag indicating the integration into the sidebar |
96 @param integrated flag indicating the integration into the sidebar |
78 @type bool |
97 @type bool |
79 @param parent parent of this dialog |
98 @param parent parent of this dialog |
80 @type QWidget |
99 @type QWidget |
81 """ |
100 """ |
82 super().__init__(parent) |
101 super().__init__(parent) |
83 self.setupUi(self) |
102 self.setupUi(self) |
84 |
103 |
85 if pluginManager is None: |
104 if pluginManager is None: |
86 # started as external plug-in repository dialog |
105 # started as external plug-in repository dialog |
87 from .PluginManager import PluginManager |
106 from .PluginManager import PluginManager |
|
107 |
88 self.__pluginManager = PluginManager() |
108 self.__pluginManager = PluginManager() |
89 self.__external = True |
109 self.__external = True |
90 else: |
110 else: |
91 self.__pluginManager = pluginManager |
111 self.__pluginManager = pluginManager |
92 self.__external = False |
112 self.__external = False |
93 self.__integratedWidget = integrated |
113 self.__integratedWidget = integrated |
94 |
114 |
95 if integrated: |
115 if integrated: |
96 self.layout().setContentsMargins(0, 3, 0, 0) |
116 self.layout().setContentsMargins(0, 3, 0, 0) |
97 |
117 |
98 if self.__integratedWidget: |
118 if self.__integratedWidget: |
99 self.__actionButtonsLayout = QHBoxLayout() |
119 self.__actionButtonsLayout = QHBoxLayout() |
100 self.__actionButtonsLayout.addStretch() |
120 self.__actionButtonsLayout.addStretch() |
101 |
121 |
102 self.__updateButton = QToolButton(self) |
122 self.__updateButton = QToolButton(self) |
103 self.__updateButton.setIcon(UI.PixmapCache.getIcon("reload")) |
123 self.__updateButton.setIcon(UI.PixmapCache.getIcon("reload")) |
104 self.__updateButton.setToolTip(self.tr("Update")) |
124 self.__updateButton.setToolTip(self.tr("Update")) |
105 self.__updateButton.clicked.connect(self.__updateList) |
125 self.__updateButton.clicked.connect(self.__updateList) |
106 self.__actionButtonsLayout.addWidget(self.__updateButton) |
126 self.__actionButtonsLayout.addWidget(self.__updateButton) |
107 |
127 |
108 self.__downloadButton = QToolButton(self) |
128 self.__downloadButton = QToolButton(self) |
109 self.__downloadButton.setIcon(UI.PixmapCache.getIcon("download")) |
129 self.__downloadButton.setIcon(UI.PixmapCache.getIcon("download")) |
110 self.__downloadButton.setToolTip(self.tr("Download")) |
130 self.__downloadButton.setToolTip(self.tr("Download")) |
111 self.__downloadButton.clicked.connect(self.__downloadButtonClicked) |
131 self.__downloadButton.clicked.connect(self.__downloadButtonClicked) |
112 self.__actionButtonsLayout.addWidget(self.__downloadButton) |
132 self.__actionButtonsLayout.addWidget(self.__downloadButton) |
113 |
133 |
114 self.__downloadInstallButton = QToolButton(self) |
134 self.__downloadInstallButton = QToolButton(self) |
115 self.__downloadInstallButton.setIcon( |
135 self.__downloadInstallButton.setIcon(UI.PixmapCache.getIcon("downloadPlus")) |
116 UI.PixmapCache.getIcon("downloadPlus")) |
136 self.__downloadInstallButton.setToolTip(self.tr("Download & Install")) |
117 self.__downloadInstallButton.setToolTip( |
|
118 self.tr("Download & Install")) |
|
119 self.__downloadInstallButton.clicked.connect( |
137 self.__downloadInstallButton.clicked.connect( |
120 self.__downloadInstallButtonClicked) |
138 self.__downloadInstallButtonClicked |
|
139 ) |
121 self.__actionButtonsLayout.addWidget(self.__downloadInstallButton) |
140 self.__actionButtonsLayout.addWidget(self.__downloadInstallButton) |
122 |
141 |
123 self.__downloadCancelButton = QToolButton(self) |
142 self.__downloadCancelButton = QToolButton(self) |
124 self.__downloadCancelButton.setIcon( |
143 self.__downloadCancelButton.setIcon(UI.PixmapCache.getIcon("cancel")) |
125 UI.PixmapCache.getIcon("cancel")) |
|
126 self.__downloadCancelButton.setToolTip(self.tr("Cancel")) |
144 self.__downloadCancelButton.setToolTip(self.tr("Cancel")) |
127 self.__downloadCancelButton.clicked.connect(self.__downloadCancel) |
145 self.__downloadCancelButton.clicked.connect(self.__downloadCancel) |
128 self.__actionButtonsLayout.addWidget(self.__downloadCancelButton) |
146 self.__actionButtonsLayout.addWidget(self.__downloadCancelButton) |
129 |
147 |
130 self.__installButton = QToolButton(self) |
148 self.__installButton = QToolButton(self) |
131 self.__installButton.setIcon(UI.PixmapCache.getIcon("plus")) |
149 self.__installButton.setIcon(UI.PixmapCache.getIcon("plus")) |
132 self.__installButton.setToolTip(self.tr("Install")) |
150 self.__installButton.setToolTip(self.tr("Install")) |
133 self.__installButton.clicked.connect(self.__closeAndInstall) |
151 self.__installButton.clicked.connect(self.__closeAndInstall) |
134 self.__actionButtonsLayout.addWidget(self.__installButton) |
152 self.__actionButtonsLayout.addWidget(self.__installButton) |
135 |
153 |
136 self.__actionButtonsLayout.addStretch() |
154 self.__actionButtonsLayout.addStretch() |
137 |
155 |
138 self.layout().addLayout(self.__actionButtonsLayout) |
156 self.layout().addLayout(self.__actionButtonsLayout) |
139 self.buttonBox.hide() |
157 self.buttonBox.hide() |
140 else: |
158 else: |
141 self.__updateButton = self.buttonBox.addButton( |
159 self.__updateButton = self.buttonBox.addButton( |
142 self.tr("Update"), QDialogButtonBox.ButtonRole.ActionRole) |
160 self.tr("Update"), QDialogButtonBox.ButtonRole.ActionRole |
|
161 ) |
143 self.__downloadButton = self.buttonBox.addButton( |
162 self.__downloadButton = self.buttonBox.addButton( |
144 self.tr("Download"), QDialogButtonBox.ButtonRole.ActionRole) |
163 self.tr("Download"), QDialogButtonBox.ButtonRole.ActionRole |
|
164 ) |
145 self.__downloadInstallButton = self.buttonBox.addButton( |
165 self.__downloadInstallButton = self.buttonBox.addButton( |
146 self.tr("Download && Install"), |
166 self.tr("Download && Install"), QDialogButtonBox.ButtonRole.ActionRole |
147 QDialogButtonBox.ButtonRole.ActionRole) |
167 ) |
148 self.__downloadCancelButton = self.buttonBox.addButton( |
168 self.__downloadCancelButton = self.buttonBox.addButton( |
149 self.tr("Cancel"), QDialogButtonBox.ButtonRole.ActionRole) |
169 self.tr("Cancel"), QDialogButtonBox.ButtonRole.ActionRole |
|
170 ) |
150 self.__installButton = self.buttonBox.addButton( |
171 self.__installButton = self.buttonBox.addButton( |
151 self.tr("Close && Install"), |
172 self.tr("Close && Install"), QDialogButtonBox.ButtonRole.ActionRole |
152 QDialogButtonBox.ButtonRole.ActionRole) |
173 ) |
153 if not self.__integratedWidget: |
174 if not self.__integratedWidget: |
154 self.__closeButton = self.buttonBox.addButton( |
175 self.__closeButton = self.buttonBox.addButton( |
155 self.tr("Close"), QDialogButtonBox.ButtonRole.RejectRole) |
176 self.tr("Close"), QDialogButtonBox.ButtonRole.RejectRole |
|
177 ) |
156 self.__closeButton.setEnabled(True) |
178 self.__closeButton.setEnabled(True) |
157 |
179 |
158 self.__downloadButton.setEnabled(False) |
180 self.__downloadButton.setEnabled(False) |
159 self.__downloadInstallButton.setEnabled(False) |
181 self.__downloadInstallButton.setEnabled(False) |
160 self.__downloadCancelButton.setEnabled(False) |
182 self.__downloadCancelButton.setEnabled(False) |
161 self.__installButton.setEnabled(False) |
183 self.__installButton.setEnabled(False) |
162 |
184 |
163 self.repositoryUrlEdit.setText( |
185 self.repositoryUrlEdit.setText(Preferences.getUI("PluginRepositoryUrl7")) |
164 Preferences.getUI("PluginRepositoryUrl7")) |
186 |
165 |
|
166 if self.__integratedWidget: |
187 if self.__integratedWidget: |
167 self.repositoryList.setHeaderHidden(True) |
188 self.repositoryList.setHeaderHidden(True) |
168 else: |
189 else: |
169 self.repositoryList.headerItem().setText( |
190 self.repositoryList.headerItem().setText( |
170 self.repositoryList.columnCount(), "") |
191 self.repositoryList.columnCount(), "" |
|
192 ) |
171 self.repositoryList.header().setSortIndicator( |
193 self.repositoryList.header().setSortIndicator( |
172 0, Qt.SortOrder.AscendingOrder) |
194 0, Qt.SortOrder.AscendingOrder |
173 |
195 ) |
|
196 |
174 self.__pluginContextMenu = QMenu(self) |
197 self.__pluginContextMenu = QMenu(self) |
175 self.__hideAct = self.__pluginContextMenu.addAction( |
198 self.__hideAct = self.__pluginContextMenu.addAction( |
176 self.tr("Hide"), self.__hidePlugin) |
199 self.tr("Hide"), self.__hidePlugin |
|
200 ) |
177 self.__hideSelectedAct = self.__pluginContextMenu.addAction( |
201 self.__hideSelectedAct = self.__pluginContextMenu.addAction( |
178 self.tr("Hide Selected"), self.__hideSelectedPlugins) |
202 self.tr("Hide Selected"), self.__hideSelectedPlugins |
|
203 ) |
179 self.__pluginContextMenu.addSeparator() |
204 self.__pluginContextMenu.addSeparator() |
180 self.__showAllAct = self.__pluginContextMenu.addAction( |
205 self.__showAllAct = self.__pluginContextMenu.addAction( |
181 self.tr("Show All"), self.__showAllPlugins) |
206 self.tr("Show All"), self.__showAllPlugins |
|
207 ) |
182 self.__pluginContextMenu.addSeparator() |
208 self.__pluginContextMenu.addSeparator() |
183 self.__pluginContextMenu.addAction( |
209 self.__pluginContextMenu.addAction( |
184 self.tr("Cleanup Downloads"), self.__cleanupDownloads) |
210 self.tr("Cleanup Downloads"), self.__cleanupDownloads |
185 |
211 ) |
186 self.pluginRepositoryFile = os.path.join(Utilities.getConfigDir(), |
212 |
187 "PluginRepository") |
213 self.pluginRepositoryFile = os.path.join( |
188 |
214 Utilities.getConfigDir(), "PluginRepository" |
189 self.__pluginManager.pluginRepositoryFileDownloaded.connect( |
215 ) |
190 self.__populateList) |
216 |
191 |
217 self.__pluginManager.pluginRepositoryFileDownloaded.connect(self.__populateList) |
|
218 |
192 # attributes for the network objects |
219 # attributes for the network objects |
193 self.__networkManager = QNetworkAccessManager(self) |
220 self.__networkManager = QNetworkAccessManager(self) |
194 self.__networkManager.proxyAuthenticationRequired.connect( |
221 self.__networkManager.proxyAuthenticationRequired.connect( |
195 proxyAuthenticationRequired) |
222 proxyAuthenticationRequired |
|
223 ) |
196 if SSL_AVAILABLE: |
224 if SSL_AVAILABLE: |
197 self.__sslErrorHandler = EricSslErrorHandler(self) |
225 self.__sslErrorHandler = EricSslErrorHandler(self) |
198 self.__networkManager.sslErrors.connect(self.__sslErrors) |
226 self.__networkManager.sslErrors.connect(self.__sslErrors) |
199 self.__replies = [] |
227 self.__replies = [] |
200 |
228 |
201 if ( |
229 if Preferences.getUI("DynamicOnlineCheck") and QNetworkInformation.load( |
202 Preferences.getUI("DynamicOnlineCheck") and |
230 QNetworkInformation.Feature.Reachability |
203 QNetworkInformation.load(QNetworkInformation.Feature.Reachability) |
|
204 ): |
231 ): |
205 self.__reachabilityChanged( |
232 self.__reachabilityChanged(QNetworkInformation.instance().reachability()) |
206 QNetworkInformation.instance().reachability()) |
|
207 QNetworkInformation.instance().reachabilityChanged.connect( |
233 QNetworkInformation.instance().reachabilityChanged.connect( |
208 self.__reachabilityChanged) |
234 self.__reachabilityChanged |
|
235 ) |
209 else: |
236 else: |
210 # assume to be 'always online' if no backend could be loaded or |
237 # assume to be 'always online' if no backend could be loaded or |
211 # dynamic online check is switched of |
238 # dynamic online check is switched of |
212 self.__reachabilityChanged(QNetworkInformation.Reachability.Online) |
239 self.__reachabilityChanged(QNetworkInformation.Reachability.Online) |
213 |
240 |
214 self.__pluginsToDownload = [] |
241 self.__pluginsToDownload = [] |
215 self.__pluginsDownloaded = [] |
242 self.__pluginsDownloaded = [] |
216 self.__isDownloadInstall = False |
243 self.__isDownloadInstall = False |
217 self.__allDownloadedOk = False |
244 self.__allDownloadedOk = False |
218 |
245 |
219 self.__hiddenPlugins = Preferences.getPluginManager("HiddenPlugins") |
246 self.__hiddenPlugins = Preferences.getPluginManager("HiddenPlugins") |
220 |
247 |
221 self.__populateList() |
248 self.__populateList() |
222 |
249 |
223 def __reachabilityChanged(self, reachability): |
250 def __reachabilityChanged(self, reachability): |
224 """ |
251 """ |
225 Private slot handling reachability state changes. |
252 Private slot handling reachability state changes. |
226 |
253 |
227 @param reachability new reachability state |
254 @param reachability new reachability state |
228 @type QNetworkInformation.Reachability |
255 @type QNetworkInformation.Reachability |
229 """ |
256 """ |
230 online = reachability == QNetworkInformation.Reachability.Online |
257 online = reachability == QNetworkInformation.Reachability.Online |
231 self.__online = online |
258 self.__online = online |
232 |
259 |
233 self.__updateButton.setEnabled(online) |
260 self.__updateButton.setEnabled(online) |
234 self.on_repositoryList_itemSelectionChanged() |
261 self.on_repositoryList_itemSelectionChanged() |
235 |
262 |
236 if not self.__integratedWidget: |
263 if not self.__integratedWidget: |
237 msg = ( |
264 msg = ( |
238 self.tr("Internet Reachability Status: Reachable") |
265 self.tr("Internet Reachability Status: Reachable") |
239 if online else |
266 if online |
240 self.tr("Internet Reachability Status: Not Reachable") |
267 else self.tr("Internet Reachability Status: Not Reachable") |
241 ) |
268 ) |
242 self.statusLabel.setText(msg) |
269 self.statusLabel.setText(msg) |
243 |
270 |
244 @pyqtSlot(QAbstractButton) |
271 @pyqtSlot(QAbstractButton) |
245 def on_buttonBox_clicked(self, button): |
272 def on_buttonBox_clicked(self, button): |
246 """ |
273 """ |
247 Private slot to handle the click of a button of the button box. |
274 Private slot to handle the click of a button of the button box. |
248 |
275 |
249 @param button reference to the button pressed (QAbstractButton) |
276 @param button reference to the button pressed (QAbstractButton) |
250 """ |
277 """ |
251 if button == self.__updateButton: |
278 if button == self.__updateButton: |
252 self.__updateList() |
279 self.__updateList() |
253 elif button == self.__downloadButton: |
280 elif button == self.__downloadButton: |
256 self.__downloadInstallButtonClicked() |
283 self.__downloadInstallButtonClicked() |
257 elif button == self.__downloadCancelButton: |
284 elif button == self.__downloadCancelButton: |
258 self.__downloadCancel() |
285 self.__downloadCancel() |
259 elif button == self.__installButton: |
286 elif button == self.__installButton: |
260 self.__closeAndInstall() |
287 self.__closeAndInstall() |
261 |
288 |
262 @pyqtSlot() |
289 @pyqtSlot() |
263 def __downloadButtonClicked(self): |
290 def __downloadButtonClicked(self): |
264 """ |
291 """ |
265 Private slot to handle a click of the Download button. |
292 Private slot to handle a click of the Download button. |
266 """ |
293 """ |
267 self.__isDownloadInstall = False |
294 self.__isDownloadInstall = False |
268 self.__downloadPlugins() |
295 self.__downloadPlugins() |
269 |
296 |
270 @pyqtSlot() |
297 @pyqtSlot() |
271 def __downloadInstallButtonClicked(self): |
298 def __downloadInstallButtonClicked(self): |
272 """ |
299 """ |
273 Private slot to handle a click of the Download & Install button. |
300 Private slot to handle a click of the Download & Install button. |
274 """ |
301 """ |
275 self.__isDownloadInstall = True |
302 self.__isDownloadInstall = True |
276 self.__allDownloadedOk = True |
303 self.__allDownloadedOk = True |
277 self.__downloadPlugins() |
304 self.__downloadPlugins() |
278 |
305 |
279 def __formatDescription(self, lines): |
306 def __formatDescription(self, lines): |
280 """ |
307 """ |
281 Private method to format the description. |
308 Private method to format the description. |
282 |
309 |
283 @param lines lines of the description (list of strings) |
310 @param lines lines of the description (list of strings) |
284 @return formatted description (string) |
311 @return formatted description (string) |
285 """ |
312 """ |
286 # remove empty line at start and end |
313 # remove empty line at start and end |
287 newlines = lines[:] |
314 newlines = lines[:] |
288 if len(newlines) and newlines[0] == '': |
315 if len(newlines) and newlines[0] == "": |
289 del newlines[0] |
316 del newlines[0] |
290 if len(newlines) and newlines[-1] == '': |
317 if len(newlines) and newlines[-1] == "": |
291 del newlines[-1] |
318 del newlines[-1] |
292 |
319 |
293 # replace empty lines by newline character |
320 # replace empty lines by newline character |
294 index = 0 |
321 index = 0 |
295 while index < len(newlines): |
322 while index < len(newlines): |
296 if newlines[index] == '': |
323 if newlines[index] == "": |
297 newlines[index] = '\n' |
324 newlines[index] = "\n" |
298 index += 1 |
325 index += 1 |
299 |
326 |
300 # join lines by a blank |
327 # join lines by a blank |
301 return ' '.join(newlines) |
328 return " ".join(newlines) |
302 |
329 |
303 def __changeScheme(self, url, newScheme=""): |
330 def __changeScheme(self, url, newScheme=""): |
304 """ |
331 """ |
305 Private method to change the scheme of the given URL. |
332 Private method to change the scheme of the given URL. |
306 |
333 |
307 @param url URL to be modified |
334 @param url URL to be modified |
308 @type str |
335 @type str |
309 @param newScheme scheme to be set for the given URL |
336 @param newScheme scheme to be set for the given URL |
310 @return modified URL |
337 @return modified URL |
311 @rtype str |
338 @rtype str |
312 """ |
339 """ |
313 if not newScheme: |
340 if not newScheme: |
314 newScheme = self.repositoryUrlEdit.text().split("//", 1)[0] |
341 newScheme = self.repositoryUrlEdit.text().split("//", 1)[0] |
315 |
342 |
316 return newScheme + "//" + url.split("//", 1)[1] |
343 return newScheme + "//" + url.split("//", 1)[1] |
317 |
344 |
318 @pyqtSlot(QPoint) |
345 @pyqtSlot(QPoint) |
319 def on_repositoryList_customContextMenuRequested(self, pos): |
346 def on_repositoryList_customContextMenuRequested(self, pos): |
320 """ |
347 """ |
321 Private slot to show the context menu. |
348 Private slot to show the context menu. |
322 |
349 |
323 @param pos position to show the menu (QPoint) |
350 @param pos position to show the menu (QPoint) |
324 """ |
351 """ |
325 self.__hideAct.setEnabled( |
352 self.__hideAct.setEnabled( |
326 self.repositoryList.currentItem() is not None and |
353 self.repositoryList.currentItem() is not None |
327 len(self.__selectedItems()) == 1) |
354 and len(self.__selectedItems()) == 1 |
328 self.__hideSelectedAct.setEnabled( |
355 ) |
329 len(self.__selectedItems()) > 1) |
356 self.__hideSelectedAct.setEnabled(len(self.__selectedItems()) > 1) |
330 self.__showAllAct.setEnabled(bool(self.__hasHiddenPlugins())) |
357 self.__showAllAct.setEnabled(bool(self.__hasHiddenPlugins())) |
331 self.__pluginContextMenu.popup(self.repositoryList.mapToGlobal(pos)) |
358 self.__pluginContextMenu.popup(self.repositoryList.mapToGlobal(pos)) |
332 |
359 |
333 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
360 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
334 def on_repositoryList_currentItemChanged(self, current, previous): |
361 def on_repositoryList_currentItemChanged(self, current, previous): |
335 """ |
362 """ |
336 Private slot to handle the change of the current item. |
363 Private slot to handle the change of the current item. |
337 |
364 |
338 @param current reference to the new current item (QTreeWidgetItem) |
365 @param current reference to the new current item (QTreeWidgetItem) |
339 @param previous reference to the old current item (QTreeWidgetItem) |
366 @param previous reference to the old current item (QTreeWidgetItem) |
340 """ |
367 """ |
341 if self.__repositoryMissing or current is None: |
368 if self.__repositoryMissing or current is None: |
342 self.descriptionEdit.clear() |
369 self.descriptionEdit.clear() |
343 self.authorEdit.clear() |
370 self.authorEdit.clear() |
344 return |
371 return |
345 |
372 |
346 url = current.data(0, PluginRepositoryWidget.UrlRole) |
373 url = current.data(0, PluginRepositoryWidget.UrlRole) |
347 url = "" if url is None else self.__changeScheme(url) |
374 url = "" if url is None else self.__changeScheme(url) |
348 self.urlEdit.setText(url) |
375 self.urlEdit.setText(url) |
349 self.descriptionEdit.setPlainText( |
376 self.descriptionEdit.setPlainText( |
350 current.data(0, PluginRepositoryWidget.DescrRole) and |
377 current.data(0, PluginRepositoryWidget.DescrRole) |
351 self.__formatDescription( |
378 and self.__formatDescription( |
352 current.data(0, PluginRepositoryWidget.DescrRole)) or "") |
379 current.data(0, PluginRepositoryWidget.DescrRole) |
|
380 ) |
|
381 or "" |
|
382 ) |
353 self.authorEdit.setText( |
383 self.authorEdit.setText( |
354 current.data(0, PluginRepositoryWidget.AuthorRole) or "") |
384 current.data(0, PluginRepositoryWidget.AuthorRole) or "" |
355 |
385 ) |
|
386 |
356 def __selectedItems(self): |
387 def __selectedItems(self): |
357 """ |
388 """ |
358 Private method to get all selected items without the toplevel ones. |
389 Private method to get all selected items without the toplevel ones. |
359 |
390 |
360 @return list of selected items (list) |
391 @return list of selected items (list) |
361 """ |
392 """ |
362 ql = self.repositoryList.selectedItems() |
393 ql = self.repositoryList.selectedItems() |
363 for index in range(self.repositoryList.topLevelItemCount()): |
394 for index in range(self.repositoryList.topLevelItemCount()): |
364 ti = self.repositoryList.topLevelItem(index) |
395 ti = self.repositoryList.topLevelItem(index) |
365 if ti in ql: |
396 if ti in ql: |
366 ql.remove(ti) |
397 ql.remove(ti) |
367 return ql |
398 return ql |
368 |
399 |
369 @pyqtSlot() |
400 @pyqtSlot() |
370 def on_repositoryList_itemSelectionChanged(self): |
401 def on_repositoryList_itemSelectionChanged(self): |
371 """ |
402 """ |
372 Private slot to handle a change of the selection. |
403 Private slot to handle a change of the selection. |
373 """ |
404 """ |
374 enable = bool(self.__selectedItems()) |
405 enable = bool(self.__selectedItems()) |
375 self.__downloadButton.setEnabled(enable and self.__online) |
406 self.__downloadButton.setEnabled(enable and self.__online) |
376 self.__downloadInstallButton.setEnabled(enable and self.__online) |
407 self.__downloadInstallButton.setEnabled(enable and self.__online) |
377 self.__installButton.setEnabled(enable) |
408 self.__installButton.setEnabled(enable) |
378 |
409 |
379 def reloadList(self): |
410 def reloadList(self): |
380 """ |
411 """ |
381 Public method to reload the list of plugins. |
412 Public method to reload the list of plugins. |
382 """ |
413 """ |
383 self.__populateList() |
414 self.__populateList() |
384 |
415 |
385 @pyqtSlot() |
416 @pyqtSlot() |
386 def __updateList(self): |
417 def __updateList(self): |
387 """ |
418 """ |
388 Private slot to download a new list and display the contents. |
419 Private slot to download a new list and display the contents. |
389 """ |
420 """ |
390 url = self.repositoryUrlEdit.text() |
421 url = self.repositoryUrlEdit.text() |
391 self.__pluginManager.downLoadRepositoryFile(url=url) |
422 self.__pluginManager.downLoadRepositoryFile(url=url) |
392 |
423 |
393 def __downloadRepositoryFileDone(self, status, filename): |
424 def __downloadRepositoryFileDone(self, status, filename): |
394 """ |
425 """ |
395 Private method called after the repository file was downloaded. |
426 Private method called after the repository file was downloaded. |
396 |
427 |
397 @param status flaging indicating a successful download (boolean) |
428 @param status flaging indicating a successful download (boolean) |
398 @param filename full path of the downloaded file (string) |
429 @param filename full path of the downloaded file (string) |
399 """ |
430 """ |
400 self.__populateList() |
431 self.__populateList() |
401 |
432 |
402 def __downloadPluginDone(self, status, filename): |
433 def __downloadPluginDone(self, status, filename): |
403 """ |
434 """ |
404 Private method called, when the download of a plugin is finished. |
435 Private method called, when the download of a plugin is finished. |
405 |
436 |
406 @param status flag indicating a successful download (boolean) |
437 @param status flag indicating a successful download (boolean) |
407 @param filename full path of the downloaded file (string) |
438 @param filename full path of the downloaded file (string) |
408 """ |
439 """ |
409 if status: |
440 if status: |
410 self.__pluginsDownloaded.append(filename) |
441 self.__pluginsDownloaded.append(filename) |
411 if self.__isDownloadInstall: |
442 if self.__isDownloadInstall: |
412 self.__allDownloadedOk &= status |
443 self.__allDownloadedOk &= status |
413 |
444 |
414 if len(self.__pluginsToDownload): |
445 if len(self.__pluginsToDownload): |
415 self.__pluginsToDownload.pop(0) |
446 self.__pluginsToDownload.pop(0) |
416 |
447 |
417 if len(self.__pluginsToDownload): |
448 if len(self.__pluginsToDownload): |
418 self.__downloadPlugin() |
449 self.__downloadPlugin() |
419 else: |
450 else: |
420 self.__downloadPluginsDone() |
451 self.__downloadPluginsDone() |
421 |
452 |
422 def __downloadPlugin(self): |
453 def __downloadPlugin(self): |
423 """ |
454 """ |
424 Private method to download the next plugin. |
455 Private method to download the next plugin. |
425 """ |
456 """ |
426 self.__downloadFile(self.__pluginsToDownload[0][0], |
457 self.__downloadFile( |
427 self.__pluginsToDownload[0][1], |
458 self.__pluginsToDownload[0][0], |
428 self.__downloadPluginDone) |
459 self.__pluginsToDownload[0][1], |
429 |
460 self.__downloadPluginDone, |
|
461 ) |
|
462 |
430 def __downloadPlugins(self): |
463 def __downloadPlugins(self): |
431 """ |
464 """ |
432 Private slot to download the selected plugins. |
465 Private slot to download the selected plugins. |
433 """ |
466 """ |
434 self.__pluginsDownloaded = [] |
467 self.__pluginsDownloaded = [] |
435 self.__pluginsToDownload = [] |
468 self.__pluginsToDownload = [] |
436 self.__downloadButton.setEnabled(False) |
469 self.__downloadButton.setEnabled(False) |
437 self.__downloadInstallButton.setEnabled(False) |
470 self.__downloadInstallButton.setEnabled(False) |
438 self.__installButton.setEnabled(False) |
471 self.__installButton.setEnabled(False) |
439 |
472 |
440 newScheme = self.repositoryUrlEdit.text().split("//", 1)[0] |
473 newScheme = self.repositoryUrlEdit.text().split("//", 1)[0] |
441 for itm in self.repositoryList.selectedItems(): |
474 for itm in self.repositoryList.selectedItems(): |
442 if itm not in [self.__stableItem, self.__unstableItem, |
475 if itm not in [ |
443 self.__unknownItem, self.__obsoleteItem]: |
476 self.__stableItem, |
|
477 self.__unstableItem, |
|
478 self.__unknownItem, |
|
479 self.__obsoleteItem, |
|
480 ]: |
444 url = self.__changeScheme( |
481 url = self.__changeScheme( |
445 itm.data(0, PluginRepositoryWidget.UrlRole), |
482 itm.data(0, PluginRepositoryWidget.UrlRole), newScheme |
446 newScheme) |
483 ) |
447 filename = os.path.join( |
484 filename = os.path.join( |
448 Preferences.getPluginManager("DownloadPath"), |
485 Preferences.getPluginManager("DownloadPath"), |
449 itm.data(0, PluginRepositoryWidget.FilenameRole)) |
486 itm.data(0, PluginRepositoryWidget.FilenameRole), |
|
487 ) |
450 self.__pluginsToDownload.append((url, filename)) |
488 self.__pluginsToDownload.append((url, filename)) |
451 if self.__pluginsToDownload: |
489 if self.__pluginsToDownload: |
452 self.__downloadPlugin() |
490 self.__downloadPlugin() |
453 |
491 |
454 def __downloadPluginsDone(self): |
492 def __downloadPluginsDone(self): |
455 """ |
493 """ |
456 Private method called, when the download of the plugins is finished. |
494 Private method called, when the download of the plugins is finished. |
457 """ |
495 """ |
458 self.__downloadButton.setEnabled(len(self.__selectedItems())) |
496 self.__downloadButton.setEnabled(len(self.__selectedItems())) |
459 self.__downloadInstallButton.setEnabled(len(self.__selectedItems())) |
497 self.__downloadInstallButton.setEnabled(len(self.__selectedItems())) |
460 self.__installButton.setEnabled(len(self.__selectedItems())) |
498 self.__installButton.setEnabled(len(self.__selectedItems())) |
461 ui = (ericApp().getObject("UserInterface") |
499 ui = ericApp().getObject("UserInterface") if not self.__external else None |
462 if not self.__external else None) |
|
463 if ui is not None: |
500 if ui is not None: |
464 ui.showNotification( |
501 ui.showNotification( |
465 UI.PixmapCache.getPixmap("plugin48"), |
502 UI.PixmapCache.getPixmap("plugin48"), |
466 self.tr("Download Plugin Files"), |
503 self.tr("Download Plugin Files"), |
467 self.tr("""The requested plugins were downloaded.""")) |
504 self.tr("""The requested plugins were downloaded."""), |
468 |
505 ) |
|
506 |
469 if self.__isDownloadInstall: |
507 if self.__isDownloadInstall: |
470 self.closeAndInstall.emit() |
508 self.closeAndInstall.emit() |
471 else: |
509 else: |
472 if ui is None: |
510 if ui is None: |
473 EricMessageBox.information( |
511 EricMessageBox.information( |
474 self, |
512 self, |
475 self.tr("Download Plugin Files"), |
513 self.tr("Download Plugin Files"), |
476 self.tr("""The requested plugins were downloaded.""")) |
514 self.tr("""The requested plugins were downloaded."""), |
477 |
515 ) |
|
516 |
478 self.downloadProgress.setValue(0) |
517 self.downloadProgress.setValue(0) |
479 |
518 |
480 # repopulate the list to update the refresh icons |
519 # repopulate the list to update the refresh icons |
481 self.__populateList() |
520 self.__populateList() |
482 |
521 |
483 def __resortRepositoryList(self): |
522 def __resortRepositoryList(self): |
484 """ |
523 """ |
485 Private method to resort the tree. |
524 Private method to resort the tree. |
486 """ |
525 """ |
487 self.repositoryList.sortItems( |
526 self.repositoryList.sortItems( |
488 self.repositoryList.sortColumn(), |
527 self.repositoryList.sortColumn(), |
489 self.repositoryList.header().sortIndicatorOrder()) |
528 self.repositoryList.header().sortIndicatorOrder(), |
490 |
529 ) |
|
530 |
491 def __populateList(self): |
531 def __populateList(self): |
492 """ |
532 """ |
493 Private method to populate the list of available plugins. |
533 Private method to populate the list of available plugins. |
494 """ |
534 """ |
495 self.repositoryList.clear() |
535 self.repositoryList.clear() |
496 self.__stableItem = None |
536 self.__stableItem = None |
497 self.__unstableItem = None |
537 self.__unstableItem = None |
498 self.__unknownItem = None |
538 self.__unknownItem = None |
499 self.__obsoleteItem = None |
539 self.__obsoleteItem = None |
500 |
540 |
501 self.__newItems = 0 |
541 self.__newItems = 0 |
502 self.__updateLocalItems = 0 |
542 self.__updateLocalItems = 0 |
503 self.__updateRemoteItems = 0 |
543 self.__updateRemoteItems = 0 |
504 |
544 |
505 self.downloadProgress.setValue(0) |
545 self.downloadProgress.setValue(0) |
506 |
546 |
507 if os.path.exists(self.pluginRepositoryFile): |
547 if os.path.exists(self.pluginRepositoryFile): |
508 self.__repositoryMissing = False |
548 self.__repositoryMissing = False |
509 f = QFile(self.pluginRepositoryFile) |
549 f = QFile(self.pluginRepositoryFile) |
510 if f.open(QIODevice.OpenModeFlag.ReadOnly): |
550 if f.open(QIODevice.OpenModeFlag.ReadOnly): |
511 from EricXML.PluginRepositoryReader import ( |
551 from EricXML.PluginRepositoryReader import PluginRepositoryReader |
512 PluginRepositoryReader |
552 |
513 ) |
|
514 reader = PluginRepositoryReader(f, self.addEntry) |
553 reader = PluginRepositoryReader(f, self.addEntry) |
515 reader.readXML() |
554 reader.readXML() |
516 self.repositoryList.resizeColumnToContents(0) |
555 self.repositoryList.resizeColumnToContents(0) |
517 self.repositoryList.resizeColumnToContents(1) |
556 self.repositoryList.resizeColumnToContents(1) |
518 self.repositoryList.resizeColumnToContents(2) |
557 self.repositoryList.resizeColumnToContents(2) |
690 @param status status of the plugin (string [stable, unstable, unknown]) |
733 @param status status of the plugin (string [stable, unstable, unknown]) |
691 """ |
734 """ |
692 pluginName = filename.rsplit("-", 1)[0] |
735 pluginName = filename.rsplit("-", 1)[0] |
693 if pluginName in self.__hiddenPlugins: |
736 if pluginName in self.__hiddenPlugins: |
694 return |
737 return |
695 |
738 |
696 if status == "stable": |
739 if status == "stable": |
697 if self.__stableItem is None: |
740 if self.__stableItem is None: |
698 self.__stableItem = QTreeWidgetItem( |
741 self.__stableItem = QTreeWidgetItem( |
699 self.repositoryList, [self.tr("Stable")]) |
742 self.repositoryList, [self.tr("Stable")] |
|
743 ) |
700 self.__stableItem.setExpanded(True) |
744 self.__stableItem.setExpanded(True) |
701 parent = self.__stableItem |
745 parent = self.__stableItem |
702 elif status == "unstable": |
746 elif status == "unstable": |
703 if self.__unstableItem is None: |
747 if self.__unstableItem is None: |
704 self.__unstableItem = QTreeWidgetItem( |
748 self.__unstableItem = QTreeWidgetItem( |
705 self.repositoryList, [self.tr("Unstable")]) |
749 self.repositoryList, [self.tr("Unstable")] |
|
750 ) |
706 self.__unstableItem.setExpanded(True) |
751 self.__unstableItem.setExpanded(True) |
707 parent = self.__unstableItem |
752 parent = self.__unstableItem |
708 elif status == "obsolete": |
753 elif status == "obsolete": |
709 if self.__obsoleteItem is None: |
754 if self.__obsoleteItem is None: |
710 self.__obsoleteItem = QTreeWidgetItem( |
755 self.__obsoleteItem = QTreeWidgetItem( |
711 self.repositoryList, [self.tr("Obsolete")]) |
756 self.repositoryList, [self.tr("Obsolete")] |
|
757 ) |
712 self.__obsoleteItem.setExpanded(True) |
758 self.__obsoleteItem.setExpanded(True) |
713 parent = self.__obsoleteItem |
759 parent = self.__obsoleteItem |
714 else: |
760 else: |
715 if self.__unknownItem is None: |
761 if self.__unknownItem is None: |
716 self.__unknownItem = QTreeWidgetItem( |
762 self.__unknownItem = QTreeWidgetItem( |
717 self.repositoryList, [self.tr("Unknown")]) |
763 self.repositoryList, [self.tr("Unknown")] |
|
764 ) |
718 self.__unknownItem.setExpanded(True) |
765 self.__unknownItem.setExpanded(True) |
719 parent = self.__unknownItem |
766 parent = self.__unknownItem |
720 |
767 |
721 if self.__integratedWidget: |
768 if self.__integratedWidget: |
722 entryFormat = "<b>{0}</b> - Version: <i>{1}</i><br/>{2}" |
769 entryFormat = "<b>{0}</b> - Version: <i>{1}</i><br/>{2}" |
723 itm = QTreeWidgetItem(parent) |
770 itm = QTreeWidgetItem(parent) |
724 itm.setFirstColumnSpanned(True) |
771 itm.setFirstColumnSpanned(True) |
725 label = QLabel(entryFormat.format(name, version, short)) |
772 label = QLabel(entryFormat.format(name, version, short)) |
726 self.repositoryList.setItemWidget(itm, 0, label) |
773 self.repositoryList.setItemWidget(itm, 0, label) |
727 else: |
774 else: |
728 itm = QTreeWidgetItem(parent, [name, version, short]) |
775 itm = QTreeWidgetItem(parent, [name, version, short]) |
729 |
776 |
730 itm.setData(0, PluginRepositoryWidget.UrlRole, url) |
777 itm.setData(0, PluginRepositoryWidget.UrlRole, url) |
731 itm.setData(0, PluginRepositoryWidget.FilenameRole, filename) |
778 itm.setData(0, PluginRepositoryWidget.FilenameRole, filename) |
732 itm.setData(0, PluginRepositoryWidget.AuthorRole, author) |
779 itm.setData(0, PluginRepositoryWidget.AuthorRole, author) |
733 itm.setData(0, PluginRepositoryWidget.DescrRole, description) |
780 itm.setData(0, PluginRepositoryWidget.DescrRole, description) |
734 |
781 |
735 iconColumn = 0 if self.__integratedWidget else 1 |
782 iconColumn = 0 if self.__integratedWidget else 1 |
736 updateStatus = self.__updateStatus(filename, version) |
783 updateStatus = self.__updateStatus(filename, version) |
737 if updateStatus == PluginRepositoryWidget.PluginStatusUpToDate: |
784 if updateStatus == PluginRepositoryWidget.PluginStatusUpToDate: |
738 itm.setIcon(iconColumn, UI.PixmapCache.getIcon("empty")) |
785 itm.setIcon(iconColumn, UI.PixmapCache.getIcon("empty")) |
739 itm.setToolTip(iconColumn, self.tr("up-to-date")) |
786 itm.setToolTip(iconColumn, self.tr("up-to-date")) |
750 itm.setToolTip(iconColumn, self.tr("updated download available")) |
797 itm.setToolTip(iconColumn, self.tr("updated download available")) |
751 self.__updateRemoteItems += 1 |
798 self.__updateRemoteItems += 1 |
752 elif updateStatus == PluginRepositoryWidget.PluginStatusError: |
799 elif updateStatus == PluginRepositoryWidget.PluginStatusError: |
753 itm.setIcon(iconColumn, UI.PixmapCache.getIcon("warning")) |
800 itm.setIcon(iconColumn, UI.PixmapCache.getIcon("warning")) |
754 itm.setToolTip(iconColumn, self.tr("error determining status")) |
801 itm.setToolTip(iconColumn, self.tr("error determining status")) |
755 |
802 |
756 def __updateStatus(self, filename, version): |
803 def __updateStatus(self, filename, version): |
757 """ |
804 """ |
758 Private method to check the given archive update status. |
805 Private method to check the given archive update status. |
759 |
806 |
760 @param filename data for the filename field (string) |
807 @param filename data for the filename field (string) |
761 @param version data for the version field (string) |
808 @param version data for the version field (string) |
762 @return plug-in update status (integer, one of PluginStatusNew, |
809 @return plug-in update status (integer, one of PluginStatusNew, |
763 PluginStatusUpToDate, PluginStatusLocalUpdate, |
810 PluginStatusUpToDate, PluginStatusLocalUpdate, |
764 PluginStatusRemoteUpdate) |
811 PluginStatusRemoteUpdate) |
765 """ |
812 """ |
766 archive = os.path.join(Preferences.getPluginManager("DownloadPath"), |
813 archive = os.path.join(Preferences.getPluginManager("DownloadPath"), filename) |
767 filename) |
814 |
768 |
|
769 # check, if it is an update (i.e. we already have archives |
815 # check, if it is an update (i.e. we already have archives |
770 # with the same pattern) |
816 # with the same pattern) |
771 archivesPattern = archive.rsplit('-', 1)[0] + "-*.zip" |
817 archivesPattern = archive.rsplit("-", 1)[0] + "-*.zip" |
772 if len(glob.glob(archivesPattern)) == 0: |
818 if len(glob.glob(archivesPattern)) == 0: |
773 # Check against installed/loaded plug-ins |
819 # Check against installed/loaded plug-ins |
774 pluginName = filename.rsplit('-', 1)[0] |
820 pluginName = filename.rsplit("-", 1)[0] |
775 pluginDetails = self.__pluginManager.getPluginDetails(pluginName) |
821 pluginDetails = self.__pluginManager.getPluginDetails(pluginName) |
776 if ( |
822 if pluginDetails is None or pluginDetails["moduleName"] != pluginName: |
777 pluginDetails is None or |
|
778 pluginDetails["moduleName"] != pluginName |
|
779 ): |
|
780 return PluginRepositoryWidget.PluginStatusNew |
823 return PluginRepositoryWidget.PluginStatusNew |
781 if pluginDetails["error"]: |
824 if pluginDetails["error"]: |
782 return PluginRepositoryWidget.PluginStatusError |
825 return PluginRepositoryWidget.PluginStatusError |
783 pluginVersionTuple = Globals.versionToTuple( |
826 pluginVersionTuple = Globals.versionToTuple(pluginDetails["version"])[:3] |
784 pluginDetails["version"])[:3] |
|
785 versionTuple = Globals.versionToTuple(version)[:3] |
827 versionTuple = Globals.versionToTuple(version)[:3] |
786 if pluginVersionTuple < versionTuple: |
828 if pluginVersionTuple < versionTuple: |
787 return PluginRepositoryWidget.PluginStatusRemoteUpdate |
829 return PluginRepositoryWidget.PluginStatusRemoteUpdate |
788 else: |
830 else: |
789 return PluginRepositoryWidget.PluginStatusUpToDate |
831 return PluginRepositoryWidget.PluginStatusUpToDate |
790 |
832 |
791 # check, if the archive exists |
833 # check, if the archive exists |
792 if not os.path.exists(archive): |
834 if not os.path.exists(archive): |
793 return PluginRepositoryWidget.PluginStatusRemoteUpdate |
835 return PluginRepositoryWidget.PluginStatusRemoteUpdate |
794 |
836 |
795 # check, if the archive is a valid zip file |
837 # check, if the archive is a valid zip file |
796 if not zipfile.is_zipfile(archive): |
838 if not zipfile.is_zipfile(archive): |
797 return PluginRepositoryWidget.PluginStatusRemoteUpdate |
839 return PluginRepositoryWidget.PluginStatusRemoteUpdate |
798 |
840 |
799 zipFile = zipfile.ZipFile(archive, "r") |
841 zipFile = zipfile.ZipFile(archive, "r") |
800 try: |
842 try: |
801 aversion = zipFile.read("VERSION").decode("utf-8") |
843 aversion = zipFile.read("VERSION").decode("utf-8") |
802 except KeyError: |
844 except KeyError: |
803 aversion = "" |
845 aversion = "" |
804 zipFile.close() |
846 zipFile.close() |
805 |
847 |
806 if aversion == version: |
848 if aversion == version: |
807 # Check against installed/loaded plug-ins |
849 # Check against installed/loaded plug-ins |
808 pluginName = filename.rsplit('-', 1)[0] |
850 pluginName = filename.rsplit("-", 1)[0] |
809 pluginDetails = self.__pluginManager.getPluginDetails(pluginName) |
851 pluginDetails = self.__pluginManager.getPluginDetails(pluginName) |
810 if pluginDetails is None: |
852 if pluginDetails is None: |
811 return PluginRepositoryWidget.PluginStatusLocalUpdate |
853 return PluginRepositoryWidget.PluginStatusLocalUpdate |
812 if ( |
854 if ( |
813 Globals.versionToTuple(pluginDetails["version"])[:3] < |
855 Globals.versionToTuple(pluginDetails["version"])[:3] |
814 Globals.versionToTuple(version)[:3] |
856 < Globals.versionToTuple(version)[:3] |
815 ): |
857 ): |
816 return PluginRepositoryWidget.PluginStatusLocalUpdate |
858 return PluginRepositoryWidget.PluginStatusLocalUpdate |
817 else: |
859 else: |
818 return PluginRepositoryWidget.PluginStatusUpToDate |
860 return PluginRepositoryWidget.PluginStatusUpToDate |
819 else: |
861 else: |
820 return PluginRepositoryWidget.PluginStatusRemoteUpdate |
862 return PluginRepositoryWidget.PluginStatusRemoteUpdate |
821 |
863 |
822 def __sslErrors(self, reply, errors): |
864 def __sslErrors(self, reply, errors): |
823 """ |
865 """ |
824 Private slot to handle SSL errors. |
866 Private slot to handle SSL errors. |
825 |
867 |
826 @param reply reference to the reply object (QNetworkReply) |
868 @param reply reference to the reply object (QNetworkReply) |
827 @param errors list of SSL errors (list of QSslError) |
869 @param errors list of SSL errors (list of QSslError) |
828 """ |
870 """ |
829 ignored = self.__sslErrorHandler.sslErrorsReply(reply, errors)[0] |
871 ignored = self.__sslErrorHandler.sslErrorsReply(reply, errors)[0] |
830 if ignored == EricSslErrorState.NOT_IGNORED: |
872 if ignored == EricSslErrorState.NOT_IGNORED: |
831 self.__downloadCancel(reply) |
873 self.__downloadCancel(reply) |
832 |
874 |
833 def getDownloadedPlugins(self): |
875 def getDownloadedPlugins(self): |
834 """ |
876 """ |
835 Public method to get the list of recently downloaded plugin files. |
877 Public method to get the list of recently downloaded plugin files. |
836 |
878 |
837 @return list of plugin filenames (list of strings) |
879 @return list of plugin filenames (list of strings) |
838 """ |
880 """ |
839 return self.__pluginsDownloaded |
881 return self.__pluginsDownloaded |
840 |
882 |
841 @pyqtSlot(bool) |
883 @pyqtSlot(bool) |
842 def on_repositoryUrlEditButton_toggled(self, checked): |
884 def on_repositoryUrlEditButton_toggled(self, checked): |
843 """ |
885 """ |
844 Private slot to set the read only status of the repository URL line |
886 Private slot to set the read only status of the repository URL line |
845 edit. |
887 edit. |
846 |
888 |
847 @param checked state of the push button (boolean) |
889 @param checked state of the push button (boolean) |
848 """ |
890 """ |
849 self.repositoryUrlEdit.setReadOnly(not checked) |
891 self.repositoryUrlEdit.setReadOnly(not checked) |
850 |
892 |
851 def __closeAndInstall(self): |
893 def __closeAndInstall(self): |
852 """ |
894 """ |
853 Private method to close the dialog and invoke the install dialog. |
895 Private method to close the dialog and invoke the install dialog. |
854 """ |
896 """ |
855 if not self.__pluginsDownloaded and self.__selectedItems(): |
897 if not self.__pluginsDownloaded and self.__selectedItems(): |
856 for itm in self.__selectedItems(): |
898 for itm in self.__selectedItems(): |
857 filename = os.path.join( |
899 filename = os.path.join( |
858 Preferences.getPluginManager("DownloadPath"), |
900 Preferences.getPluginManager("DownloadPath"), |
859 itm.data(0, PluginRepositoryWidget.FilenameRole)) |
901 itm.data(0, PluginRepositoryWidget.FilenameRole), |
|
902 ) |
860 self.__pluginsDownloaded.append(filename) |
903 self.__pluginsDownloaded.append(filename) |
861 self.closeAndInstall.emit() |
904 self.closeAndInstall.emit() |
862 |
905 |
863 def __hidePlugin(self): |
906 def __hidePlugin(self): |
864 """ |
907 """ |
865 Private slot to hide the current plug-in. |
908 Private slot to hide the current plug-in. |
866 """ |
909 """ |
867 itm = self.__selectedItems()[0] |
910 itm = self.__selectedItems()[0] |
868 pluginName = (itm.data(0, PluginRepositoryWidget.FilenameRole) |
911 pluginName = itm.data(0, PluginRepositoryWidget.FilenameRole).rsplit("-", 1)[0] |
869 .rsplit("-", 1)[0]) |
|
870 self.__updateHiddenPluginsList([pluginName]) |
912 self.__updateHiddenPluginsList([pluginName]) |
871 |
913 |
872 def __hideSelectedPlugins(self): |
914 def __hideSelectedPlugins(self): |
873 """ |
915 """ |
874 Private slot to hide all selected plug-ins. |
916 Private slot to hide all selected plug-ins. |
875 """ |
917 """ |
876 hideList = [] |
918 hideList = [] |
877 for itm in self.__selectedItems(): |
919 for itm in self.__selectedItems(): |
878 pluginName = (itm.data(0, PluginRepositoryWidget.FilenameRole) |
920 pluginName = itm.data(0, PluginRepositoryWidget.FilenameRole).rsplit( |
879 .rsplit("-", 1)[0]) |
921 "-", 1 |
|
922 )[0] |
880 hideList.append(pluginName) |
923 hideList.append(pluginName) |
881 self.__updateHiddenPluginsList(hideList) |
924 self.__updateHiddenPluginsList(hideList) |
882 |
925 |
883 def __showAllPlugins(self): |
926 def __showAllPlugins(self): |
884 """ |
927 """ |
885 Private slot to show all plug-ins. |
928 Private slot to show all plug-ins. |
886 """ |
929 """ |
887 self.__hiddenPlugins = [] |
930 self.__hiddenPlugins = [] |
888 self.__updateHiddenPluginsList([]) |
931 self.__updateHiddenPluginsList([]) |
889 |
932 |
890 def __hasHiddenPlugins(self): |
933 def __hasHiddenPlugins(self): |
891 """ |
934 """ |
892 Private method to check, if there are any hidden plug-ins. |
935 Private method to check, if there are any hidden plug-ins. |
893 |
936 |
894 @return flag indicating the presence of hidden plug-ins (boolean) |
937 @return flag indicating the presence of hidden plug-ins (boolean) |
895 """ |
938 """ |
896 return bool(self.__hiddenPlugins) |
939 return bool(self.__hiddenPlugins) |
897 |
940 |
898 def __updateHiddenPluginsList(self, hideList): |
941 def __updateHiddenPluginsList(self, hideList): |
899 """ |
942 """ |
900 Private method to store the list of hidden plug-ins to the settings. |
943 Private method to store the list of hidden plug-ins to the settings. |
901 |
944 |
902 @param hideList list of plug-ins to add to the list of hidden ones |
945 @param hideList list of plug-ins to add to the list of hidden ones |
903 (list of string) |
946 (list of string) |
904 """ |
947 """ |
905 if hideList: |
948 if hideList: |
906 self.__hiddenPlugins.extend( |
949 self.__hiddenPlugins.extend( |
907 [p for p in hideList if p not in self.__hiddenPlugins]) |
950 [p for p in hideList if p not in self.__hiddenPlugins] |
|
951 ) |
908 Preferences.setPluginManager("HiddenPlugins", self.__hiddenPlugins) |
952 Preferences.setPluginManager("HiddenPlugins", self.__hiddenPlugins) |
909 self.__populateList() |
953 self.__populateList() |
910 |
954 |
911 def __cleanupDownloads(self): |
955 def __cleanupDownloads(self): |
912 """ |
956 """ |
913 Private slot to cleanup the plug-in downloads area. |
957 Private slot to cleanup the plug-in downloads area. |
914 """ |
958 """ |
915 PluginRepositoryDownloadCleanup() |
959 PluginRepositoryDownloadCleanup() |
917 |
961 |
918 class PluginRepositoryDialog(QDialog): |
962 class PluginRepositoryDialog(QDialog): |
919 """ |
963 """ |
920 Class for the dialog variant. |
964 Class for the dialog variant. |
921 """ |
965 """ |
|
966 |
922 def __init__(self, pluginManager, parent=None): |
967 def __init__(self, pluginManager, parent=None): |
923 """ |
968 """ |
924 Constructor |
969 Constructor |
925 |
970 |
926 @param pluginManager reference to the plugin manager object |
971 @param pluginManager reference to the plugin manager object |
927 @type PluginManager |
972 @type PluginManager |
928 @param parent reference to the parent widget |
973 @param parent reference to the parent widget |
929 @type QWidget |
974 @type QWidget |
930 """ |
975 """ |
931 super().__init__(parent) |
976 super().__init__(parent) |
932 self.setSizeGripEnabled(True) |
977 self.setSizeGripEnabled(True) |
933 |
978 |
934 self.__layout = QVBoxLayout(self) |
979 self.__layout = QVBoxLayout(self) |
935 self.__layout.setContentsMargins(0, 0, 0, 0) |
980 self.__layout.setContentsMargins(0, 0, 0, 0) |
936 self.setLayout(self.__layout) |
981 self.setLayout(self.__layout) |
937 |
982 |
938 self.cw = PluginRepositoryWidget(pluginManager, parent=self) |
983 self.cw = PluginRepositoryWidget(pluginManager, parent=self) |
939 size = self.cw.size() |
984 size = self.cw.size() |
940 self.__layout.addWidget(self.cw) |
985 self.__layout.addWidget(self.cw) |
941 self.resize(size) |
986 self.resize(size) |
942 self.setWindowTitle(self.cw.windowTitle()) |
987 self.setWindowTitle(self.cw.windowTitle()) |
943 |
988 |
944 self.cw.buttonBox.accepted.connect(self.accept) |
989 self.cw.buttonBox.accepted.connect(self.accept) |
945 self.cw.buttonBox.rejected.connect(self.reject) |
990 self.cw.buttonBox.rejected.connect(self.reject) |
946 self.cw.closeAndInstall.connect(self.__closeAndInstall) |
991 self.cw.closeAndInstall.connect(self.__closeAndInstall) |
947 |
992 |
948 def __closeAndInstall(self): |
993 def __closeAndInstall(self): |
949 """ |
994 """ |
950 Private slot to handle the closeAndInstall signal. |
995 Private slot to handle the closeAndInstall signal. |
951 """ |
996 """ |
952 self.done(QDialog.DialogCode.Accepted + 1) |
997 self.done(QDialog.DialogCode.Accepted + 1) |
953 |
998 |
954 def getDownloadedPlugins(self): |
999 def getDownloadedPlugins(self): |
955 """ |
1000 """ |
956 Public method to get the list of recently downloaded plugin files. |
1001 Public method to get the list of recently downloaded plugin files. |
957 |
1002 |
958 @return list of plugin filenames (list of strings) |
1003 @return list of plugin filenames (list of strings) |
959 """ |
1004 """ |
960 return self.cw.getDownloadedPlugins() |
1005 return self.cw.getDownloadedPlugins() |
961 |
1006 |
962 |
1007 |
963 class PluginRepositoryWindow(EricMainWindow): |
1008 class PluginRepositoryWindow(EricMainWindow): |
964 """ |
1009 """ |
965 Main window class for the standalone dialog. |
1010 Main window class for the standalone dialog. |
966 """ |
1011 """ |
|
1012 |
967 def __init__(self, parent=None): |
1013 def __init__(self, parent=None): |
968 """ |
1014 """ |
969 Constructor |
1015 Constructor |
970 |
1016 |
971 @param parent reference to the parent widget (QWidget) |
1017 @param parent reference to the parent widget (QWidget) |
972 """ |
1018 """ |
973 super().__init__(parent) |
1019 super().__init__(parent) |
974 self.cw = PluginRepositoryWidget(None, parent=self) |
1020 self.cw = PluginRepositoryWidget(None, parent=self) |
975 size = self.cw.size() |
1021 size = self.cw.size() |
976 self.setCentralWidget(self.cw) |
1022 self.setCentralWidget(self.cw) |
977 self.resize(size) |
1023 self.resize(size) |
978 self.setWindowTitle(self.cw.windowTitle()) |
1024 self.setWindowTitle(self.cw.windowTitle()) |
979 |
1025 |
980 self.setStyle(Preferences.getUI("Style"), |
1026 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) |
981 Preferences.getUI("StyleSheet")) |
1027 |
982 |
|
983 self.cw.buttonBox.accepted.connect(self.close) |
1028 self.cw.buttonBox.accepted.connect(self.close) |
984 self.cw.buttonBox.rejected.connect(self.close) |
1029 self.cw.buttonBox.rejected.connect(self.close) |
985 self.cw.closeAndInstall.connect(self.__startPluginInstall) |
1030 self.cw.closeAndInstall.connect(self.__startPluginInstall) |
986 |
1031 |
987 def __startPluginInstall(self): |
1032 def __startPluginInstall(self): |
988 """ |
1033 """ |
989 Private slot to start the eric plugin installation dialog. |
1034 Private slot to start the eric plugin installation dialog. |
990 """ |
1035 """ |
991 proc = QProcess() |
1036 proc = QProcess() |
992 applPath = os.path.join(getConfig("ericDir"), "eric7_plugininstall.py") |
1037 applPath = os.path.join(getConfig("ericDir"), "eric7_plugininstall.py") |
993 |
1038 |
994 args = [] |
1039 args = [] |
995 args.append(applPath) |
1040 args.append(applPath) |
996 args += self.cw.getDownloadedPlugins() |
1041 args += self.cw.getDownloadedPlugins() |
997 |
1042 |
998 if ( |
1043 if not os.path.isfile(applPath) or not proc.startDetached( |
999 not os.path.isfile(applPath) or |
1044 Globals.getPythonExecutable(), args |
1000 not proc.startDetached(Globals.getPythonExecutable(), args) |
|
1001 ): |
1045 ): |
1002 EricMessageBox.critical( |
1046 EricMessageBox.critical( |
1003 self, |
1047 self, |
1004 self.tr('Process Generation Error'), |
1048 self.tr("Process Generation Error"), |
1005 self.tr( |
1049 self.tr( |
1006 '<p>Could not start the process.<br>' |
1050 "<p>Could not start the process.<br>" |
1007 'Ensure that it is available as <b>{0}</b>.</p>' |
1051 "Ensure that it is available as <b>{0}</b>.</p>" |
1008 ).format(applPath), |
1052 ).format(applPath), |
1009 self.tr('OK')) |
1053 self.tr("OK"), |
1010 |
1054 ) |
|
1055 |
1011 self.close() |
1056 self.close() |
1012 |
1057 |
1013 |
1058 |
1014 def PluginRepositoryDownloadCleanup(quiet=False): |
1059 def PluginRepositoryDownloadCleanup(quiet=False): |
1015 """ |
1060 """ |
1016 Module function to clean up the plug-in downloads area. |
1061 Module function to clean up the plug-in downloads area. |
1017 |
1062 |
1018 @param quiet flag indicating quiet operations |
1063 @param quiet flag indicating quiet operations |
1019 @type bool |
1064 @type bool |
1020 """ |
1065 """ |
1021 pluginsRegister = [] # list of plug-ins contained in the repository |
1066 pluginsRegister = [] # list of plug-ins contained in the repository |
1022 |
1067 |
1023 def registerPlugin(name, short, description, url, author, version, |
1068 def registerPlugin( |
1024 filename, status): |
1069 name, short, description, url, author, version, filename, status |
|
1070 ): |
1025 """ |
1071 """ |
1026 Method to register a plug-in's data. |
1072 Method to register a plug-in's data. |
1027 |
1073 |
1028 @param name data for the name field (string) |
1074 @param name data for the name field (string) |
1029 @param short data for the short field (string) |
1075 @param short data for the short field (string) |
1030 @param description data for the description field (list of strings) |
1076 @param description data for the description field (list of strings) |
1031 @param url data for the url field (string) |
1077 @param url data for the url field (string) |
1032 @param author data for the author field (string) |
1078 @param author data for the author field (string) |
1061 # rsplit() returned just one entry, i.e. file name doesn't contain |
1105 # rsplit() returned just one entry, i.e. file name doesn't contain |
1062 # version info separated by '-' |
1106 # version info separated by '-' |
1063 # => assume version 0.0.0 |
1107 # => assume version 0.0.0 |
1064 pluginName = pluginFile.replace(".zip", "") |
1108 pluginName = pluginFile.replace(".zip", "") |
1065 pluginVersionList = [0, 0, 0] |
1109 pluginVersionList = [0, 0, 0] |
1066 |
1110 |
1067 if pluginName not in downloads: |
1111 if pluginName not in downloads: |
1068 downloads[pluginName] = [] |
1112 downloads[pluginName] = [] |
1069 downloads[pluginName].append((pluginFile, tuple(pluginVersionList))) |
1113 downloads[pluginName].append((pluginFile, tuple(pluginVersionList))) |
1070 |
1114 |
1071 # step 2: delete old entries |
1115 # step 2: delete old entries |
1072 hiddenPlugins = Preferences.getPluginManager("HiddenPlugins") |
1116 hiddenPlugins = Preferences.getPluginManager("HiddenPlugins") |
1073 for pluginName in downloads: |
1117 for pluginName in downloads: |
1074 downloads[pluginName].sort(key=lambda x: x[1]) |
1118 downloads[pluginName].sort(key=lambda x: x[1]) |
1075 |
1119 |
1076 removeFiles = ( |
1120 removeFiles = ( |
1077 [f[0] for f in downloads[pluginName]] |
1121 [f[0] for f in downloads[pluginName]] |
1078 if (pluginName in hiddenPlugins and |
1122 if ( |
1079 not Preferences.getPluginManager("KeepHidden")) else |
1123 pluginName in hiddenPlugins |
1080 [f[0] for f in downloads[pluginName][ |
1124 and not Preferences.getPluginManager("KeepHidden") |
1081 :-Preferences.getPluginManager("KeepGenerations")]] |
1125 ) |
|
1126 else [ |
|
1127 f[0] |
|
1128 for f in downloads[pluginName][ |
|
1129 : -Preferences.getPluginManager("KeepGenerations") |
|
1130 ] |
|
1131 ] |
1082 ) |
1132 ) |
1083 for removeFile in removeFiles: |
1133 for removeFile in removeFiles: |
1084 try: |
1134 try: |
1085 os.remove(os.path.join(downloadPath, removeFile)) |
1135 os.remove(os.path.join(downloadPath, removeFile)) |
1086 except OSError as err: |
1136 except OSError as err: |
1087 if not quiet: |
1137 if not quiet: |
1088 EricMessageBox.critical( |
1138 EricMessageBox.critical( |
1089 None, |
1139 None, |
1090 QCoreApplication.translate( |
1140 QCoreApplication.translate( |
1091 "PluginRepositoryWidget", |
1141 "PluginRepositoryWidget", "Cleanup of Plugin Downloads" |
1092 "Cleanup of Plugin Downloads"), |
1142 ), |
1093 QCoreApplication.translate( |
1143 QCoreApplication.translate( |
1094 "PluginRepositoryWidget", |
1144 "PluginRepositoryWidget", |
1095 """<p>The plugin download <b>{0}</b> could""" |
1145 """<p>The plugin download <b>{0}</b> could""" |
1096 """ not be deleted.</p><p>Reason: {1}</p>""") |
1146 """ not be deleted.</p><p>Reason: {1}</p>""", |
1097 .format(removeFile, str(err))) |
1147 ).format(removeFile, str(err)), |
1098 |
1148 ) |
|
1149 |
1099 # step 3: delete entries of obsolete plug-ins |
1150 # step 3: delete entries of obsolete plug-ins |
1100 pluginRepositoryFile = os.path.join(Utilities.getConfigDir(), |
1151 pluginRepositoryFile = os.path.join(Utilities.getConfigDir(), "PluginRepository") |
1101 "PluginRepository") |
|
1102 if os.path.exists(pluginRepositoryFile): |
1152 if os.path.exists(pluginRepositoryFile): |
1103 f = QFile(pluginRepositoryFile) |
1153 f = QFile(pluginRepositoryFile) |
1104 if f.open(QIODevice.OpenModeFlag.ReadOnly): |
1154 if f.open(QIODevice.OpenModeFlag.ReadOnly): |
1105 from EricXML.PluginRepositoryReader import PluginRepositoryReader |
1155 from EricXML.PluginRepositoryReader import PluginRepositoryReader |
|
1156 |
1106 reader = PluginRepositoryReader(f, registerPlugin) |
1157 reader = PluginRepositoryReader(f, registerPlugin) |
1107 reader.readXML() |
1158 reader.readXML() |
1108 |
1159 |
1109 for pluginName in downloads: |
1160 for pluginName in downloads: |
1110 if pluginName not in pluginsRegister: |
1161 if pluginName not in pluginsRegister: |
1111 removeFiles = [f[0] for f in downloads[pluginName]] |
1162 removeFiles = [f[0] for f in downloads[pluginName]] |
1112 for removeFile in removeFiles: |
1163 for removeFile in removeFiles: |
1113 try: |
1164 try: |