Network/IRC/IrcChannelWidget.py

changeset 6514
f11a703e4664
parent 6513
e1fcd71fbda3
child 6532
f253f0f9ea7f
equal deleted inserted replaced
6513:e1fcd71fbda3 6514:f11a703e4664
7 Module implementing the IRC channel widget. 7 Module implementing the IRC channel widget.
8 """ 8 """
9 9
10 from __future__ import unicode_literals 10 from __future__ import unicode_literals
11 11
12 try:
13 from itertools import izip_longest as zip_longest # __IGNORE_EXCEPTION__
14 except ImportError:
15 from itertools import zip_longest
16
12 import re 17 import re
13 18
14 from PyQt5.QtCore import pyqtSlot, pyqtSignal, QDateTime, QPoint, QFileInfo, \ 19 from PyQt5.QtCore import pyqtSlot, pyqtSignal, QDateTime, QPoint, QFileInfo, \
15 QTimer, QUrl 20 QTimer, QUrl, QCoreApplication
16 from PyQt5.QtGui import QIcon, QPainter, QTextCursor, QDesktopServices 21 from PyQt5.QtGui import QIcon, QPainter, QTextCursor, QDesktopServices
17 from PyQt5.QtWidgets import QWidget, QListWidgetItem, QMenu, QApplication, \ 22 from PyQt5.QtWidgets import QWidget, QListWidgetItem, QMenu, QApplication, \
18 QInputDialog, QLineEdit 23 QInputDialog, QLineEdit
19 24
20 from E5Gui import E5MessageBox, E5FileDialog 25 from E5Gui import E5MessageBox, E5FileDialog
61 """ 66 """
62 super(IrcUserItem, self).__init__(name, parent) 67 super(IrcUserItem, self).__init__(name, parent)
63 68
64 self.__privilege = IrcUserItem.Normal 69 self.__privilege = IrcUserItem.Normal
65 self.__name = name 70 self.__name = name
66 71 self.__ignored = False
72
73 self.__setText()
67 self.__setIcon() 74 self.__setIcon()
68 75
69 def name(self): 76 def name(self):
70 """ 77 """
71 Public method to get the user name. 78 Public method to get the user name.
79 Public method to set a new nick name. 86 Public method to set a new nick name.
80 87
81 @param name new nick name for the user (string) 88 @param name new nick name for the user (string)
82 """ 89 """
83 self.__name = name 90 self.__name = name
84 self.setText(name) 91 self.__setText()
85 92
86 def changePrivilege(self, privilege): 93 def changePrivilege(self, privilege):
87 """ 94 """
88 Public method to set or unset a user privilege. 95 Public method to set or unset a user privilege.
89 96
103 """ 110 """
104 Public method to clear the user privileges. 111 Public method to clear the user privileges.
105 """ 112 """
106 self.__privilege = IrcUserItem.Normal 113 self.__privilege = IrcUserItem.Normal
107 self.__setIcon() 114 self.__setIcon()
115
116 def __setText(self):
117 """
118 Private method to set the user item text.
119 """
120 if self.__ignored:
121 self.setText(QCoreApplication.translate(
122 "IrcUserItem",
123 "{0} (ignored)").format(self.__name))
124 else:
125 self.setText(self.__name)
108 126
109 def __setIcon(self): 127 def __setIcon(self):
110 """ 128 """
111 Private method to set the icon dependent on user privileges. 129 Private method to set the icon dependent on user privileges.
112 """ 130 """
174 @return flag indicating that the topic can be changed (boolean) 192 @return flag indicating that the topic can be changed (boolean)
175 """ 193 """
176 return(bool(self.__privilege & IrcUserItem.Operator) or 194 return(bool(self.__privilege & IrcUserItem.Operator) or
177 bool(self.__privilege & IrcUserItem.Admin) or 195 bool(self.__privilege & IrcUserItem.Admin) or
178 bool(self.__privilege & IrcUserItem.Owner)) 196 bool(self.__privilege & IrcUserItem.Owner))
197
198 def setIgnored(self, ignored):
199 """
200 Public method to set the user status to ignored.
201
202 @param ignored flag indicating the new ignored status
203 @type bool
204 """
205 self.__ignored = ignored
206 self.__setText()
207
208 def isIgnored(self):
209 """
210 Public method to check, if this user is ignored.
211
212 @return flag indicating the ignored status
213 @rtype bool
214 """
215 return self.__ignored
179 216
180 217
181 class IrcChannelWidget(QWidget, Ui_IrcChannelWidget): 218 class IrcChannelWidget(QWidget, Ui_IrcChannelWidget):
182 """ 219 """
183 Class implementing the IRC channel widget. 220 Class implementing the IRC channel widget.
184 221
185 @signal sendData(str) emitted to send a message to the channel 222 @signal sendData(str) emitted to send a message to the channel
223 @signal sendCtcpRequest(str, str, str) emitted to send a CTCP request
186 @signal sendCtcpReply(str, str) emitted to send a CTCP reply 224 @signal sendCtcpReply(str, str) emitted to send a CTCP reply
187 @signal channelClosed(str) emitted after the user has left the channel 225 @signal channelClosed(str) emitted after the user has left the channel
188 @signal openPrivateChat(str) emitted to open a "channel" for private 226 @signal openPrivateChat(str) emitted to open a "channel" for private
189 messages 227 messages
228 @signal awayCommand(str) emitted to set the away status via the /away
229 command
230 @signal leaveChannels(list) emitted to leave a list of channels
231 @signal leaveAllChannels() emitted to leave all channels
190 """ 232 """
191 sendData = pyqtSignal(str) 233 sendData = pyqtSignal(str)
234 sendCtcpRequest = pyqtSignal(str, str, str)
192 sendCtcpReply = pyqtSignal(str, str) 235 sendCtcpReply = pyqtSignal(str, str)
193 channelClosed = pyqtSignal(str) 236 channelClosed = pyqtSignal(str)
194 openPrivateChat = pyqtSignal(str) 237 openPrivateChat = pyqtSignal(str)
238 awayCommand = pyqtSignal(str)
239 leaveChannels = pyqtSignal(list)
240 leaveAllChannels = pyqtSignal()
195 241
196 UrlRe = re.compile( 242 UrlRe = re.compile(
197 r"""((?:http|ftp|https):\/\/[\w\-_]+(?:\.[\w\-_]+)+""" 243 r"""((?:http|ftp|https):\/\/[\w\-_]+(?:\.[\w\-_]+)+"""
198 r"""(?:[\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?)""") 244 r"""(?:[\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?)""")
199 245
356 '<b>&gt;</b> {4}</font>'.format( 402 '<b>&gt;</b> {4}</font>'.format(
357 Preferences.getIrc("ChannelMessageColour"), 403 Preferences.getIrc("ChannelMessageColour"),
358 Preferences.getIrc("OwnNickColour"), 404 Preferences.getIrc("OwnNickColour"),
359 ircTimestamp(), self.__userName, 405 ircTimestamp(), self.__userName,
360 Utilities.html_encode(msg))) 406 Utilities.html_encode(msg)))
407
361 if msg.startswith("/"): 408 if msg.startswith("/"):
362 if self.__private: 409 if self.__private:
363 E5MessageBox.information( 410 E5MessageBox.information(
364 self, 411 self,
365 self.tr("Send Message"), 412 self.tr("Send Message"),
366 self.tr( 413 self.tr(
367 """Messages starting with a '/' are not allowed""" 414 """Messages starting with a '/' are not allowed"""
368 """ in private chats.""")) 415 """ in private chats."""))
369 else: 416 else:
417 sendData = True
418 # flag set to False, if command was handled
419
370 msgList = msg.split() 420 msgList = msg.split()
371 cmd = msgList[0][1:].upper() 421 cmd = msgList[0][1:].upper()
372 if cmd == "MSG": 422 if cmd in ["MSG", "QUERY"]:
373 cmd = "PRIVMSG" 423 cmd = "PRIVMSG"
374 if len(msgList) > 1: 424 if len(msgList) > 1:
375 if msgList[1].strip().lower() in \ 425 if msgList[1].strip().lower() in \
376 self.__serviceNamesLower: 426 self.__serviceNamesLower:
377 msg = "PRIVMSG " + \ 427 msg = "PRIVMSG " + \
381 msg = "PRIVMSG {0} :{1}".format( 431 msg = "PRIVMSG {0} :{1}".format(
382 msgList[1], " ".join(msgList[2:])) 432 msgList[1], " ".join(msgList[2:]))
383 else: 433 else:
384 msgList[0] = cmd 434 msgList[0] = cmd
385 msg = " ".join(msgList) 435 msg = " ".join(msgList)
436 elif cmd == "NOTICE":
437 if len(msgList) > 2:
438 msg = "NOTICE {0} :{1}".format(
439 msgList[1], " ".join(msgList[2:]))
440 else:
441 msg = "NOTICE {0}".format(" ".join(msgList[1:]))
442 elif cmd == "PING":
443 receiver = msgList[1]
444 msg = "PING {0} "
445 self.sendCtcpRequest.emit(receiver, "PING", "")
446 sendData = False
447 elif cmd == "IGNORE":
448 sendData = False
449 if len(msgList) > 1:
450 if msgList[1] == "-r":
451 ignored = False
452 userNamesList = msgList[2:]
453 else:
454 ignored = True
455 userNamesList = msgList[1:]
456 else:
457 userNamesList = []
458 userNames = ",".join(
459 u.rstrip(",") for u in userNamesList).split(",")
460 for userName in userNames:
461 itm = self.__findUser(userName)
462 if itm:
463 itm.setIgnored(ignored)
464 elif cmd == "UNIGNORE":
465 sendData = False
466 if len(msgList) > 1:
467 userNamesList = msgList[1:]
468 else:
469 userNamesList = []
470 userNames = ",".join(
471 u.rstrip(",") for u in userNamesList).split(",")
472 for userName in userNames:
473 itm = self.__findUser(userName)
474 if itm:
475 itm.setIgnored(False)
476 elif cmd == "AWAY":
477 sendData = False
478 if len(msgList) > 1:
479 msg = " ".join(msgList[1:])
480 else:
481 msg = ""
482 self.awayCommand.emit(msg)
483 elif cmd == "JOIN":
484 sendData = False
485 if len(msgList) > 1:
486 channels = msgList[1].split(",")
487 if len(msgList) > 2:
488 keys = msgList[2].split(",")
489 else:
490 keys = []
491 for channel, key in zip_longest(
492 channels, keys, fillvalue=""):
493 self.__ircWidget.joinChannel(channel, key)
494 elif cmd == "PART":
495 sendData = False
496 if len(msgList) == 1:
497 self.leaveChannel()
498 else:
499 self.leaveChannels.emit(msgList[1:])
500 elif cmd == "PARTALL":
501 sendData = False
502 self.leaveAllChannels.emit()
386 else: 503 else:
387 msg = msg[1:] 504 msg = msg[1:]
388 self.sendData.emit(msg) 505 if sendData:
506 self.sendData.emit(msg)
389 else: 507 else:
390 if self.__private: 508 if self.__private:
391 self.sendData.emit( 509 self.sendData.emit(
392 "PRIVMSG " + self.__privatePartner + " :" + msg) 510 "PRIVMSG " + self.__privatePartner + " :" + msg)
393 else: 511 else:
394 self.sendData.emit( 512 self.sendData.emit(
395 "PRIVMSG " + self.__name + " :" + msg) 513 "PRIVMSG " + self.__name + " :" + msg)
514
396 self.messageEdit.clear() 515 self.messageEdit.clear()
397 self.unsetMarkerLine() 516 self.unsetMarkerLine()
398 517
399 def requestLeave(self): 518 def requestLeave(self):
400 """ 519 """
402 """ 521 """
403 ok = E5MessageBox.yesNo( 522 ok = E5MessageBox.yesNo(
404 self, 523 self,
405 self.tr("Leave IRC channel"), 524 self.tr("Leave IRC channel"),
406 self.tr( 525 self.tr(
407 """Do you really want to leave the IRC channel <b>{0}</b>?""") 526 """Do you really want to leave the IRC channel"""
408 .format(self.__name)) 527 """ <b>{0}</b>?""").format(self.__name))
409 if ok: 528 if ok:
410 if not self.__private: 529 self.leaveChannel()
411 self.sendData.emit( 530
412 "PART " + self.__name + " :" + self.__partMessage) 531 def leaveChannel(self):
413 self.channelClosed.emit(self.__name) 532 """
533 Public slot to leave the channel.
534 """
535 if not self.__private:
536 self.sendData.emit(
537 "PART " + self.__name + " :" + self.__partMessage)
538 self.channelClosed.emit(self.__name)
414 539
415 def name(self): 540 def name(self):
416 """ 541 """
417 Public method to get the name of the channel. 542 Public method to get the name of the channel.
418 543
513 # group(1) sender user name 638 # group(1) sender user name
514 # group(2) sender user@host 639 # group(2) sender user@host
515 # group(3) target nick 640 # group(3) target nick
516 # group(4) message 641 # group(4) message
517 if match.group(3).lower() == self.__name.lower(): 642 if match.group(3).lower() == self.__name.lower():
643 senderName = match.group(1)
644 itm = self.__findUser(senderName)
645 if itm and itm.isIgnored():
646 # user should be ignored
647 return True
648
518 if match.group(4).startswith("\x01"): 649 if match.group(4).startswith("\x01"):
519 return self.__handleCtcp(match) 650 return self.__handleCtcp(match)
520 651
521 self.addMessage(match.group(1), match.group(4)) 652 self.addMessage(senderName, match.group(4))
522 if self.__private and not self.topicLabel.text(): 653 if self.__private and not self.topicLabel.text():
523 self.setPrivateInfo( 654 self.setPrivateInfo(
524 "{0} - {1}".format(match.group(1), match.group(2))) 655 "{0} - {1}".format(match.group(1), match.group(2)))
525 return True 656 return True
526 657

eric ide

mercurial