eric7/Network/IRC/IrcNetworkWidget.py

branch
eric7
changeset 8312
800c432b34c8
parent 8259
2bbec88047dd
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2012 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the network part of the IRC widget.
8 """
9
10 from PyQt5.QtCore import pyqtSlot, pyqtSignal, QPoint, QFileInfo, QUrl, QThread
11 from PyQt5.QtGui import QDesktopServices
12 from PyQt5.QtWidgets import QWidget, QApplication, QMenu
13
14 from E5Gui import E5MessageBox, E5FileDialog
15
16 from .Ui_IrcNetworkWidget import Ui_IrcNetworkWidget
17
18 from .IrcUtilities import ircFilter, ircTimestamp
19
20 import UI.PixmapCache
21 import Preferences
22 import Utilities
23
24
25 class IrcNetworkWidget(QWidget, Ui_IrcNetworkWidget):
26 """
27 Class implementing the network part of the IRC widget.
28
29 @signal connectNetwork(str,bool,bool) emitted to connect or disconnect from
30 a network
31 @signal editNetwork(str) emitted to edit a network configuration
32 @signal joinChannel(str) emitted to join a channel
33 @signal nickChanged(str) emitted to change the nick name
34 @signal sendData(str) emitted to send a message to the channel
35 @signal away(bool) emitted to indicate the away status
36 @signal autoConnected() emitted after an automatic connection was initiated
37 """
38 connectNetwork = pyqtSignal(str, bool, bool)
39 editNetwork = pyqtSignal(str)
40 joinChannel = pyqtSignal(str)
41 nickChanged = pyqtSignal(str)
42 sendData = pyqtSignal(str)
43 away = pyqtSignal(bool)
44 autoConnected = pyqtSignal()
45
46 def __init__(self, parent=None):
47 """
48 Constructor
49
50 @param parent reference to the parent widget (QWidget)
51 """
52 super().__init__(parent)
53 self.setupUi(self)
54
55 self.connectButton.setIcon(UI.PixmapCache.getIcon("ircConnect"))
56 self.editButton.setIcon(UI.PixmapCache.getIcon("ircConfigure"))
57 self.joinButton.setIcon(UI.PixmapCache.getIcon("ircJoinChannel"))
58 self.awayButton.setIcon(UI.PixmapCache.getIcon("ircUserPresent"))
59
60 self.joinButton.setEnabled(False)
61 self.nickCombo.setEnabled(False)
62 self.awayButton.setEnabled(False)
63
64 self.channelCombo.lineEdit().returnPressed.connect(
65 self.on_joinButton_clicked)
66 self.nickCombo.lineEdit().returnPressed.connect(
67 self.on_nickCombo_currentIndexChanged)
68
69 self.setConnected(False)
70
71 self.__initMessagesMenu()
72
73 self.__manager = None
74 self.__connected = False
75 self.__registered = False
76 self.__away = False
77
78 def initialize(self, manager):
79 """
80 Public method to initialize the widget.
81
82 @param manager reference to the network manager (IrcNetworkManager)
83 """
84 self.__manager = manager
85
86 self.networkCombo.addItems(self.__manager.getNetworkNames())
87
88 self.__manager.networksChanged.connect(self.__refreshNetworks)
89 self.__manager.identitiesChanged.connect(self.__refreshNetworks)
90
91 def autoConnect(self):
92 """
93 Public method to perform the IRC auto connection.
94 """
95 self.__autoConnect()
96
97 def __autoConnect(self):
98 """
99 Private method to perform the IRC auto connection.
100 """
101 for networkName in self.__manager.getNetworkNames():
102 if self.__manager.getNetwork(networkName).autoConnect():
103 row = self.networkCombo.findText(networkName)
104 self.networkCombo.setCurrentIndex(row)
105 self.on_connectButton_clicked()
106 self.autoConnected.emit()
107 break
108
109 @pyqtSlot(bool)
110 def __onlineStateChanged(self, online):
111 """
112 Private slot handling online state changes.
113
114 @param online online state
115 @type bool
116 """
117 self.connectButton.setEnabled(online)
118 if online:
119 # delay a bit because the signal seems to be sent before the
120 # network interface is fully up
121 QThread.msleep(200)
122 self.__autoConnect()
123 else:
124 network = self.networkCombo.currentText()
125 self.connectNetwork.emit(network, online, True)
126
127 @pyqtSlot()
128 def __refreshNetworks(self):
129 """
130 Private slot to refresh all network related widgets.
131 """
132 currentNetwork = self.networkCombo.currentText()
133 currentNick = self.nickCombo.currentText()
134 currentChannel = self.channelCombo.currentText()
135 blocked = self.networkCombo.blockSignals(True)
136 self.networkCombo.clear()
137 self.networkCombo.addItems(self.__manager.getNetworkNames())
138 self.networkCombo.blockSignals(blocked)
139 row = self.networkCombo.findText(currentNetwork)
140 if row == -1:
141 row = 0
142 blocked = self.nickCombo.blockSignals(True)
143 self.networkCombo.setCurrentIndex(row)
144 self.nickCombo.setEditText(currentNick)
145 self.nickCombo.blockSignals(blocked)
146 self.channelCombo.setEditText(currentChannel)
147
148 @pyqtSlot()
149 def on_connectButton_clicked(self):
150 """
151 Private slot to connect to a network.
152 """
153 network = self.networkCombo.currentText()
154 self.connectNetwork.emit(network, not self.__connected, False)
155
156 @pyqtSlot()
157 def on_awayButton_clicked(self):
158 """
159 Private slot to toggle the away status.
160 """
161 if self.__away:
162 self.handleAwayCommand("")
163 else:
164 networkName = self.networkCombo.currentText()
165 identityName = (
166 self.__manager.getNetwork(networkName).getIdentityName()
167 )
168 identity = self.__manager.getIdentity(identityName)
169 if identity:
170 awayMessage = identity.getAwayMessage()
171 else:
172 awayMessage = ""
173 self.handleAwayCommand(awayMessage)
174
175 @pyqtSlot(str)
176 def handleAwayCommand(self, awayMessage):
177 """
178 Public slot to process an away command.
179
180 @param awayMessage message to be set for being away
181 @type str
182 """
183 if awayMessage and not self.__away:
184 # set being away
185 # don't send away, if the status is already set
186 self.sendData.emit("AWAY :" + awayMessage)
187 self.awayButton.setIcon(UI.PixmapCache.getIcon("ircUserAway"))
188 self.__away = True
189 self.away.emit(self.__away)
190 elif not awayMessage and self.__away:
191 # cancel being away
192 self.sendData.emit("AWAY")
193 self.awayButton.setIcon(
194 UI.PixmapCache.getIcon("ircUserPresent"))
195 self.__away = False
196 self.away.emit(self.__away)
197
198 @pyqtSlot()
199 def on_editButton_clicked(self):
200 """
201 Private slot to edit a network.
202 """
203 network = self.networkCombo.currentText()
204 self.editNetwork.emit(network)
205
206 @pyqtSlot(str)
207 def on_channelCombo_editTextChanged(self, txt):
208 """
209 Private slot to react upon changes of the channel.
210
211 @param txt current text of the channel combo (string)
212 """
213 on = bool(txt) and self.__registered
214 self.joinButton.setEnabled(on)
215
216 @pyqtSlot()
217 def on_joinButton_clicked(self):
218 """
219 Private slot to join a channel.
220 """
221 channel = self.channelCombo.currentText()
222 self.joinChannel.emit(channel)
223
224 @pyqtSlot(int)
225 def on_networkCombo_currentIndexChanged(self, index):
226 """
227 Private slot to handle selections of a network.
228
229 @param index index of the selected entry
230 @type int
231 """
232 networkName = self.networkCombo.itemText(index)
233 network = self.__manager.getNetwork(networkName)
234 self.nickCombo.clear()
235 self.channelCombo.clear()
236 if network:
237 channels = network.getChannelNames()
238 self.channelCombo.addItems(channels)
239 self.channelCombo.setEnabled(True)
240 identity = self.__manager.getIdentity(
241 network.getIdentityName())
242 if identity:
243 self.nickCombo.addItems(identity.getNickNames())
244 else:
245 self.channelCombo.setEnabled(False)
246
247 def getNetworkChannels(self):
248 """
249 Public method to get the list of channels associated with the
250 selected network.
251
252 @return associated channels (list of IrcChannel)
253 """
254 networkName = self.networkCombo.currentText()
255 network = self.__manager.getNetwork(networkName)
256 return network.getChannels()
257
258 @pyqtSlot(int)
259 @pyqtSlot()
260 def on_nickCombo_currentIndexChanged(self, nick=0):
261 """
262 Private slot to use another nick name.
263
264 @param nick index of the selected nick name (unused)
265 """
266 if self.__connected:
267 self.nickChanged.emit(self.nickCombo.currentText())
268
269 def getNickname(self):
270 """
271 Public method to get the currently selected nick name.
272
273 @return selected nick name (string)
274 """
275 return self.nickCombo.currentText()
276
277 def setNickName(self, nick):
278 """
279 Public slot to set the nick name in use.
280
281 @param nick nick name in use (string)
282 """
283 self.nickCombo.blockSignals(True)
284 self.nickCombo.setEditText(nick)
285 self.nickCombo.blockSignals(False)
286
287 def addMessage(self, msg):
288 """
289 Public method to add a message.
290
291 @param msg message to be added (string)
292 """
293 s = '<font color="{0}">{1} {2}</font>'.format(
294 Preferences.getIrc("NetworkMessageColour"),
295 ircTimestamp(),
296 msg
297 )
298 self.messages.append(s)
299
300 def addServerMessage(self, msgType, msg, filterMsg=True):
301 """
302 Public method to add a server message.
303
304 @param msgType txpe of the message (string)
305 @param msg message to be added (string)
306 @param filterMsg flag indicating to filter the message (boolean)
307 """
308 if filterMsg:
309 msg = ircFilter(msg)
310 s = '<font color="{0}">{1} <b>[</b>{2}<b>]</b> {3}</font>'.format(
311 Preferences.getIrc("ServerMessageColour"),
312 ircTimestamp(),
313 msgType,
314 msg
315 )
316 self.messages.append(s)
317
318 def addErrorMessage(self, msgType, msg):
319 """
320 Public method to add an error message.
321
322 @param msgType txpe of the message (string)
323 @param msg message to be added (string)
324 """
325 s = '<font color="{0}">{1} <b>[</b>{2}<b>]</b> {3}</font>'.format(
326 Preferences.getIrc("ErrorMessageColour"),
327 ircTimestamp(),
328 msgType,
329 msg
330 )
331 self.messages.append(s)
332
333 def setConnected(self, connected):
334 """
335 Public slot to set the connection state.
336
337 @param connected flag indicating the connection state (boolean)
338 """
339 self.__connected = connected
340 if self.__connected:
341 self.connectButton.setIcon(
342 UI.PixmapCache.getIcon("ircDisconnect"))
343 self.connectButton.setToolTip(
344 self.tr("Press to disconnect from the network"))
345 else:
346 self.connectButton.setIcon(
347 UI.PixmapCache.getIcon("ircConnect"))
348 self.connectButton.setToolTip(
349 self.tr("Press to connect to the selected network"))
350
351 def isConnected(self):
352 """
353 Public method to check, if the network is connected.
354
355 @return flag indicating a connected network (boolean)
356 """
357 return self.__connected
358
359 def setRegistered(self, registered):
360 """
361 Public slot to set the registered state.
362
363 @param registered flag indicating the registration state (boolean)
364 """
365 self.__registered = registered
366 on = bool(self.channelCombo.currentText()) and self.__registered
367 self.joinButton.setEnabled(on)
368 self.nickCombo.setEnabled(registered)
369 self.awayButton.setEnabled(registered)
370 if registered:
371 self.awayButton.setIcon(
372 UI.PixmapCache.getIcon("ircUserPresent"))
373 self.__away = False
374
375 def __clearMessages(self):
376 """
377 Private slot to clear the contents of the messages display.
378 """
379 self.messages.clear()
380
381 def __copyMessages(self):
382 """
383 Private slot to copy the selection of the messages display to
384 the clipboard.
385 """
386 self.messages.copy()
387
388 def __copyAllMessages(self):
389 """
390 Private slot to copy the contents of the messages display to
391 the clipboard.
392 """
393 txt = self.messages.toPlainText()
394 if txt:
395 cb = QApplication.clipboard()
396 cb.setText(txt)
397
398 def __cutAllMessages(self):
399 """
400 Private slot to cut the contents of the messages display to
401 the clipboard.
402 """
403 txt = self.messages.toPlainText()
404 if txt:
405 cb = QApplication.clipboard()
406 cb.setText(txt)
407 self.messages.clear()
408
409 def __saveMessages(self):
410 """
411 Private slot to save the contents of the messages display.
412 """
413 hasText = not self.messages.document().isEmpty()
414 if hasText:
415 if Utilities.isWindowsPlatform():
416 htmlExtension = "htm"
417 else:
418 htmlExtension = "html"
419 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
420 self,
421 self.tr("Save Messages"),
422 "",
423 self.tr(
424 "HTML Files (*.{0});;Text Files (*.txt);;All Files (*)")
425 .format(htmlExtension),
426 None,
427 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
428 if fname:
429 ext = QFileInfo(fname).suffix()
430 if not ext:
431 ex = selectedFilter.split("(*")[1].split(")")[0]
432 if ex:
433 fname += ex
434 ext = QFileInfo(fname).suffix()
435 if QFileInfo(fname).exists():
436 res = E5MessageBox.yesNo(
437 self,
438 self.tr("Save Messages"),
439 self.tr("<p>The file <b>{0}</b> already exists."
440 " Overwrite it?</p>").format(fname),
441 icon=E5MessageBox.Warning)
442 if not res:
443 return
444 fname = Utilities.toNativeSeparators(fname)
445
446 try:
447 txt = (
448 self.messages.toHtml()
449 if ext.lower() in ["htm", "html"] else
450 self.messages.toPlainText()
451 )
452 with open(fname, "w", encoding="utf-8") as f:
453 f.write(txt)
454 except OSError as err:
455 E5MessageBox.critical(
456 self,
457 self.tr("Error saving Messages"),
458 self.tr(
459 """<p>The messages contents could not be written"""
460 """ to <b>{0}</b></p><p>Reason: {1}</p>""")
461 .format(fname, str(err)))
462
463 def __initMessagesMenu(self):
464 """
465 Private slot to initialize the context menu of the messages pane.
466 """
467 self.__messagesMenu = QMenu(self)
468 self.__copyMessagesAct = self.__messagesMenu.addAction(
469 UI.PixmapCache.getIcon("editCopy"),
470 self.tr("Copy"), self.__copyMessages)
471 self.__messagesMenu.addSeparator()
472 self.__cutAllMessagesAct = self.__messagesMenu.addAction(
473 UI.PixmapCache.getIcon("editCut"),
474 self.tr("Cut all"), self.__cutAllMessages)
475 self.__copyAllMessagesAct = self.__messagesMenu.addAction(
476 UI.PixmapCache.getIcon("editCopy"),
477 self.tr("Copy all"), self.__copyAllMessages)
478 self.__messagesMenu.addSeparator()
479 self.__clearMessagesAct = self.__messagesMenu.addAction(
480 UI.PixmapCache.getIcon("editDelete"),
481 self.tr("Clear"), self.__clearMessages)
482 self.__messagesMenu.addSeparator()
483 self.__saveMessagesAct = self.__messagesMenu.addAction(
484 UI.PixmapCache.getIcon("fileSave"),
485 self.tr("Save"), self.__saveMessages)
486
487 self.on_messages_copyAvailable(False)
488
489 @pyqtSlot(bool)
490 def on_messages_copyAvailable(self, yes):
491 """
492 Private slot to react to text selection/deselection of the
493 messages edit.
494
495 @param yes flag signaling the availability of selected text (boolean)
496 """
497 self.__copyMessagesAct.setEnabled(yes)
498
499 @pyqtSlot(QPoint)
500 def on_messages_customContextMenuRequested(self, pos):
501 """
502 Private slot to show the context menu of the messages pane.
503
504 @param pos position the menu should be opened at (QPoint)
505 """
506 enable = not self.messages.document().isEmpty()
507 self.__cutAllMessagesAct.setEnabled(enable)
508 self.__copyAllMessagesAct.setEnabled(enable)
509 self.__saveMessagesAct.setEnabled(enable)
510 self.__messagesMenu.popup(self.messages.mapToGlobal(pos))
511
512 @pyqtSlot(QUrl)
513 def on_messages_anchorClicked(self, url):
514 """
515 Private slot to open links in the default browser.
516
517 @param url URL to be opened (QUrl)
518 """
519 QDesktopServices.openUrl(url)

eric ide

mercurial