eric6/Network/IRC/IrcNetworkWidget.py

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

eric ide

mercurial