PageRenderTime 72ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/memos.py

https://github.com/602p/pesterchum
Python | 1289 lines | 1239 code | 44 blank | 6 comment | 74 complexity | bcb62b3bba861b4aa0e3a809abb35cb3 MD5 | raw file
Possible License(s): GPL-3.0, Cube
  1. from string import Template
  2. import re
  3. from copy import copy
  4. from PyQt4 import QtGui, QtCore
  5. from datetime import time, timedelta, datetime
  6. from mood import Mood
  7. from dataobjs import PesterProfile, PesterHistory
  8. from generic import PesterIcon, RightClickList, mysteryTime
  9. from convo import PesterConvo, PesterInput, PesterText, PesterTabWindow
  10. from parsetools import convertTags, addTimeInitial, timeProtocol, \
  11. lexMessage, colorBegin, colorEnd, mecmd, smiledict, oocre
  12. from logviewer import PesterLogViewer
  13. def delta2txt(d, format="pc"):
  14. if type(d) is mysteryTime:
  15. return "?"
  16. if format == "pc":
  17. sign = "+" if d >= timedelta(0) else "-"
  18. else:
  19. if d == timedelta(0):
  20. return "i"
  21. sign = "F" if d >= timedelta(0) else "P"
  22. d = abs(d)
  23. totalminutes = (d.days*86400 + d.seconds) // 60
  24. hours = totalminutes // 60
  25. leftovermins = totalminutes % 60
  26. if hours < 100:
  27. if format == "pc":
  28. return "%s%d:%02d" % (sign, hours, leftovermins)
  29. else:
  30. return "%s%02d:%02d" % (sign, hours, leftovermins)
  31. else:
  32. if format == "pc":
  33. return "%s%d" % (sign, hours)
  34. else:
  35. return "%s%02d:%02d" % (sign, hours, leftovermins)
  36. def txt2delta(txt):
  37. sign = 1
  38. if txt[0] == '?':
  39. return mysteryTime()
  40. if txt[0] == '+':
  41. txt = txt[1:]
  42. elif txt[0] == '-':
  43. sign = -1
  44. txt = txt[1:]
  45. l = txt.split(":")
  46. try:
  47. h = int(l[0])
  48. m = 0
  49. if len(l) > 1:
  50. m = int(l[1])
  51. timed = timedelta(0, h*3600+m*60)
  52. except ValueError:
  53. timed = timedelta(0)
  54. except OverflowError:
  55. if sign < 0:
  56. return timedelta(min)
  57. else:
  58. return timedelta(max)
  59. return sign*timed
  60. def pcfGrammar(td):
  61. if type(td) is mysteryTime:
  62. when = "???"
  63. temporal = "???"
  64. pcf = "?"
  65. elif td > timedelta(0):
  66. when = "FROM NOW"
  67. temporal = "FUTURE"
  68. pcf = "F"
  69. elif td < timedelta(0):
  70. when = "AGO"
  71. temporal = "PAST"
  72. pcf = "P"
  73. else:
  74. when = "RIGHT NOW"
  75. temporal = "CURRENT"
  76. pcf = "C"
  77. return (temporal, pcf, when)
  78. class TimeGrammar(object):
  79. def __init__(self, temporal, pcf, when, number="0"):
  80. self.temporal = temporal
  81. self.pcf = pcf
  82. self.when = when
  83. if number == "0" or number == 0:
  84. self.number = ""
  85. else:
  86. self.number = str(number)
  87. class TimeTracker(list):
  88. def __init__(self, time=None):
  89. self.timerecord = {"P": [], "F": []}
  90. self.open = {}
  91. if time is not None:
  92. self.append(time)
  93. self.current=0
  94. self.addRecord(time)
  95. self.open[time] = False
  96. else:
  97. self.current=-1
  98. def addTime(self, timed):
  99. try:
  100. i = self.index(timed)
  101. self.current = i
  102. return True
  103. except ValueError:
  104. self.current = len(self)
  105. self.append(timed)
  106. self.open[timed] = False
  107. self.addRecord(timed)
  108. return False
  109. def prevTime(self):
  110. i = self.current
  111. i = (i - 1) % len(self)
  112. return self[i]
  113. def nextTime(self):
  114. i = self.current
  115. i = (i + 1) % len(self)
  116. return self[i]
  117. def setCurrent(self, timed):
  118. self.current = self.index(timed)
  119. def addRecord(self, timed):
  120. try:
  121. (temporal, pcf, when) = pcfGrammar(timed - timedelta(0))
  122. except TypeError:
  123. (temporal, pcf, when) = pcfGrammar(mysteryTime())
  124. if pcf == "C" or pcf == "?":
  125. return
  126. if timed in self.timerecord[pcf]:
  127. return
  128. self.timerecord[pcf].append(timed)
  129. def getRecord(self, timed):
  130. try:
  131. (temporal, pcf, when) = pcfGrammar(timed - timedelta(0))
  132. except TypeError:
  133. (temporal, pcf, when) = pcfGrammar(mysteryTime())
  134. if pcf == "C" or pcf == "?":
  135. return 0
  136. if len(self.timerecord[pcf]) > 1:
  137. return self.timerecord[pcf].index(timed)+1
  138. else:
  139. return 0
  140. def removeTime(self, timed):
  141. try:
  142. self.pop(self.index(timed))
  143. self.current = len(self)-1
  144. del self.open[timed]
  145. return True
  146. except ValueError:
  147. return None
  148. def openTime(self, time):
  149. if self.open.has_key(time):
  150. self.open[time] = True
  151. def openCurrentTime(self):
  152. timed = self.getTime()
  153. self.openTime(timed)
  154. def isFirstTime(self):
  155. timed = self.getTime()
  156. return not self.open[timed]
  157. def getTime(self):
  158. if self.current >= 0:
  159. return self[self.current]
  160. else:
  161. return None
  162. def getGrammar(self):
  163. timed = self.getTime()
  164. return self.getGrammarTime(timed)
  165. def getGrammarTime(self, timed):
  166. mytime = timedelta(0)
  167. try:
  168. (temporal, pcf, when) = pcfGrammar(timed - mytime)
  169. except TypeError:
  170. (temporal, pcf, when) = pcfGrammar(mysteryTime())
  171. if timed == mytime:
  172. return TimeGrammar(temporal, pcf, when, 0)
  173. return TimeGrammar(temporal, pcf, when, self.getRecord(timed))
  174. class TimeInput(QtGui.QLineEdit):
  175. def __init__(self, timeslider, parent):
  176. QtGui.QLineEdit.__init__(self, parent)
  177. self.timeslider = timeslider
  178. self.setText("+0:00")
  179. self.connect(self.timeslider, QtCore.SIGNAL('valueChanged(int)'),
  180. self, QtCore.SLOT('setTime(int)'))
  181. self.connect(self, QtCore.SIGNAL('editingFinished()'),
  182. self, QtCore.SLOT('setSlider()'))
  183. @QtCore.pyqtSlot(int)
  184. def setTime(self, sliderval):
  185. self.setText(self.timeslider.getTime())
  186. @QtCore.pyqtSlot()
  187. def setSlider(self):
  188. value = unicode(self.text())
  189. timed = txt2delta(value)
  190. if type(timed) is mysteryTime:
  191. self.timeslider.setValue(0)
  192. self.setText("?")
  193. return
  194. sign = 1 if timed >= timedelta(0) else -1
  195. abstimed = abs(txt2delta(value))
  196. index = 50
  197. for i, td in enumerate(timedlist):
  198. if abstimed < td:
  199. index = i-1
  200. break
  201. self.timeslider.setValue(sign*index)
  202. text = delta2txt(timed)
  203. self.setText(text)
  204. class TimeSlider(QtGui.QSlider):
  205. def __init__(self, orientation, parent):
  206. QtGui.QSlider.__init__(self, orientation, parent)
  207. self.setTracking(True)
  208. self.setMinimum(-50)
  209. self.setMaximum(50)
  210. self.setValue(0)
  211. self.setPageStep(1)
  212. def getTime(self):
  213. time = timelist[abs(self.value())]
  214. sign = "+" if self.value() >= 0 else "-"
  215. return sign+time
  216. def mouseDoubleClickEvent(self, event):
  217. self.setValue(0)
  218. class MemoTabWindow(PesterTabWindow):
  219. def __init__(self, mainwindow, parent=None):
  220. PesterTabWindow.__init__(self, mainwindow, parent, "memos")
  221. def addChat(self, convo):
  222. self.convos[convo.channel] = convo
  223. # either addTab or setCurrentIndex will trigger changed()
  224. newindex = self.tabs.addTab(convo.channel)
  225. self.tabIndices[convo.channel] = newindex
  226. self.tabs.setCurrentIndex(newindex)
  227. self.tabs.setTabIcon(newindex, PesterIcon(self.mainwindow.theme["memos/memoicon"]))
  228. def updateBlocked(self):
  229. pass
  230. def updateMood(self):
  231. pass
  232. _ctag_begin = re.compile(r'<c=(.*?)>')
  233. class MemoText(PesterText):
  234. def __init__(self, theme, parent=None):
  235. QtGui.QTextEdit.__init__(self, parent)
  236. if hasattr(self.parent(), 'mainwindow'):
  237. self.mainwindow = self.parent().mainwindow
  238. else:
  239. self.mainwindow = self.parent()
  240. if type(parent.parent()) is PesterTabWindow:
  241. self.tabobject = parent.parent()
  242. self.hasTabs = True
  243. else:
  244. self.hasTabs = False
  245. self.initTheme(theme)
  246. self.setReadOnly(True)
  247. self.setMouseTracking(True)
  248. self.textSelected = False
  249. self.connect(self, QtCore.SIGNAL('copyAvailable(bool)'),
  250. self, QtCore.SLOT('textReady(bool)'))
  251. self.urls = {}
  252. for k in smiledict:
  253. self.addAnimation(QtCore.QUrl("smilies/%s" % (smiledict[k])), "smilies/%s" % (smiledict[k]))
  254. self.connect(self.mainwindow, QtCore.SIGNAL('animationSetting(bool)'),
  255. self, QtCore.SLOT('animateChanged(bool)'))
  256. def initTheme(self, theme):
  257. if theme.has_key("memos/scrollbar"):
  258. self.setStyleSheet("QTextEdit { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["memos/textarea/style"], theme["memos/scrollbar/style"], theme["memos/scrollbar/handle"], theme["memos/scrollbar/downarrow"], theme["memos/scrollbar/uparrow"], theme["memos/scrollbar/uarrowstyle"], theme["memos/scrollbar/darrowstyle"] ))
  259. else:
  260. self.setStyleSheet("QTextEdit { %s }" % theme["memos/textarea/style"])
  261. def addMessage(self, msg, chum):
  262. if type(msg) in [str, unicode]:
  263. lexmsg = lexMessage(msg)
  264. else:
  265. lexmsg = msg
  266. parent = self.parent()
  267. window = parent.mainwindow
  268. me = window.profile()
  269. if self.mainwindow.config.animations():
  270. for m in self.urls:
  271. if convertTags(lexmsg).find(self.urls[m].toString()) != -1:
  272. if m.state() == QtGui.QMovie.NotRunning:
  273. m.start()
  274. chumdb = window.chumdb
  275. if chum is not me: # SO MUCH WH1T3SP4C3 >:]
  276. if type(lexmsg[0]) is colorBegin: # get color tag
  277. colortag = lexmsg[0]
  278. try:
  279. color = QtGui.QColor(*[int(c) for c in colortag.color.split(",")])
  280. except ValueError:
  281. color = QtGui.QColor("black")
  282. else:
  283. chumdb.setColor(chum.handle, color)
  284. parent.updateColor(chum.handle, color)
  285. else:
  286. color = chumdb.getColor(chum.handle)
  287. else:
  288. color = me.color
  289. chum.color = color
  290. systemColor = QtGui.QColor(window.theme["memos/systemMsgColor"])
  291. if chum is not me:
  292. if parent.times.has_key(chum.handle):
  293. time = parent.times[chum.handle]
  294. if time.getTime() is None:
  295. # MY WAY OR THE HIGHWAY
  296. time.addTime(timedelta(0))
  297. else:
  298. # new chum! time current
  299. newtime = timedelta(0)
  300. time = TimeTracker(newtime)
  301. parent.times[handle] = time
  302. else:
  303. time = parent.time
  304. if time.isFirstTime():
  305. grammar = time.getGrammar()
  306. joinmsg = chum.memojoinmsg(systemColor, time.getTime(), grammar, window.theme["convo/text/joinmemo"])
  307. self.append(convertTags(joinmsg))
  308. parent.mainwindow.chatlog.log(parent.channel, joinmsg)
  309. time.openCurrentTime()
  310. def makeSafe(msg):
  311. if msg.count("<c") > msg.count("</c>"):
  312. for i in range(msg.count("<c") - msg.count("</c>")):
  313. msg = msg + "</c>"
  314. return "<span style=\"color:#000000\">" + msg + "</span>"
  315. if type(lexmsg[0]) is mecmd:
  316. memsg = chum.memsg(systemColor, lexmsg, time=time.getGrammar())
  317. window.chatlog.log(parent.channel, memsg)
  318. self.append(convertTags(memsg))
  319. else:
  320. self.append(makeSafe(convertTags(lexmsg)))
  321. window.chatlog.log(parent.channel, lexmsg)
  322. def changeTheme(self, theme):
  323. self.initTheme(theme)
  324. def submitLogTitle(self):
  325. return "[%s]" % (self.parent().title())
  326. class MemoInput(PesterInput):
  327. def __init__(self, theme, parent=None):
  328. QtGui.QLineEdit.__init__(self, parent)
  329. self.setStyleSheet(theme["memos/input/style"])
  330. def changeTheme(self, theme):
  331. self.setStyleSheet(theme["memos/input/style"])
  332. class PesterMemo(PesterConvo):
  333. def __init__(self, channel, timestr, mainwindow, parent=None):
  334. QtGui.QFrame.__init__(self, parent)
  335. self.setAttribute(QtCore.Qt.WA_QuitOnClose, False)
  336. self.channel = channel
  337. self.setObjectName(self.channel)
  338. self.mainwindow = mainwindow
  339. self.time = TimeTracker(txt2delta(timestr))
  340. self.setWindowTitle(channel)
  341. self.channelLabel = QtGui.QLabel(self)
  342. self.channelLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding))
  343. self.textArea = MemoText(self.mainwindow.theme, self)
  344. self.textInput = MemoInput(self.mainwindow.theme, self)
  345. self.textInput.setFocus()
  346. self.miniUserlist = QtGui.QPushButton(">\n>", self)
  347. #self.miniUserlist.setStyleSheet("border:1px solid #a68168; border-width: 2px 0px 2px 2px; height: 90px; width: 10px; color: #cd8f9d; font-family: 'Arial'; background: white; margin-left: 2px;")
  348. self.connect(self.miniUserlist, QtCore.SIGNAL('clicked()'),
  349. self, QtCore.SLOT('toggleUserlist()'))
  350. self.userlist = RightClickList(self)
  351. self.userlist.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Expanding))
  352. self.userlist.optionsMenu = QtGui.QMenu(self)
  353. self.addchumAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self)
  354. self.connect(self.addchumAction, QtCore.SIGNAL('triggered()'),
  355. self, QtCore.SLOT('addChumSlot()'))
  356. self.banuserAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/banuser"], self)
  357. self.connect(self.banuserAction, QtCore.SIGNAL('triggered()'),
  358. self, QtCore.SLOT('banSelectedUser()'))
  359. self.opAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/opuser"], self)
  360. self.connect(self.opAction, QtCore.SIGNAL('triggered()'),
  361. self, QtCore.SLOT('opSelectedUser()'))
  362. self.voiceAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/voiceuser"], self)
  363. self.connect(self.voiceAction, QtCore.SIGNAL('triggered()'),
  364. self, QtCore.SLOT('voiceSelectedUser()'))
  365. self.quirkDisableAction = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirkkill"], self)
  366. self.connect(self.quirkDisableAction, QtCore.SIGNAL('triggered()'),
  367. self, QtCore.SLOT('killQuirkUser()'))
  368. self.userlist.optionsMenu.addAction(self.addchumAction)
  369. # ban & op list added if we are op
  370. self.optionsMenu = QtGui.QMenu(self)
  371. self.oocToggle = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self)
  372. self.oocToggle.setCheckable(True)
  373. self.connect(self.oocToggle, QtCore.SIGNAL('toggled(bool)'),
  374. self, QtCore.SLOT('toggleOOC(bool)'))
  375. self.quirksOff = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self)
  376. self.quirksOff.setCheckable(True)
  377. self.connect(self.quirksOff, QtCore.SIGNAL('toggled(bool)'),
  378. self, QtCore.SLOT('toggleQuirks(bool)'))
  379. self.logchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self)
  380. self.connect(self.logchum, QtCore.SIGNAL('triggered()'),
  381. self, QtCore.SLOT('openChumLogs()'))
  382. self.invitechum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/invitechum"], self)
  383. self.connect(self.invitechum, QtCore.SIGNAL('triggered()'),
  384. self, QtCore.SLOT('inviteChums()'))
  385. self.optionsMenu.addAction(self.quirksOff)
  386. self.optionsMenu.addAction(self.oocToggle)
  387. self.optionsMenu.addAction(self.logchum)
  388. self.optionsMenu.addAction(self.invitechum)
  389. self.chanModeMenu = QtGui.QMenu(self.mainwindow.theme["main/menus/rclickchumlist/memosetting"], self)
  390. self.chanNoquirks = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memonoquirk"], self)
  391. self.chanNoquirks.setCheckable(True)
  392. self.connect(self.chanNoquirks, QtCore.SIGNAL('toggled(bool)'),
  393. self, QtCore.SLOT('noquirksChan(bool)'))
  394. self.chanHide = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memohidden"], self)
  395. self.chanHide.setCheckable(True)
  396. self.connect(self.chanHide, QtCore.SIGNAL('toggled(bool)'),
  397. self, QtCore.SLOT('hideChan(bool)'))
  398. self.chanInvite = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memoinvite"], self)
  399. self.chanInvite.setCheckable(True)
  400. self.connect(self.chanInvite, QtCore.SIGNAL('toggled(bool)'),
  401. self, QtCore.SLOT('inviteChan(bool)'))
  402. self.chanMod = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memomute"], self)
  403. self.chanMod.setCheckable(True)
  404. self.connect(self.chanMod, QtCore.SIGNAL('toggled(bool)'),
  405. self, QtCore.SLOT('modChan(bool)'))
  406. self.chanModeMenu.addAction(self.chanNoquirks)
  407. self.chanModeMenu.addAction(self.chanHide)
  408. self.chanModeMenu.addAction(self.chanInvite)
  409. self.chanModeMenu.addAction(self.chanMod)
  410. self.timeslider = TimeSlider(QtCore.Qt.Horizontal, self)
  411. self.timeinput = TimeInput(self.timeslider, self)
  412. self.timeinput.setText(timestr)
  413. self.timeinput.setSlider()
  414. self.timetravel = QtGui.QPushButton("GO", self)
  415. self.timeclose = QtGui.QPushButton("CLOSE", self)
  416. self.timeswitchl = QtGui.QPushButton(self)
  417. self.timeswitchr = QtGui.QPushButton(self)
  418. self.connect(self.timetravel, QtCore.SIGNAL('clicked()'),
  419. self, QtCore.SLOT('sendtime()'))
  420. self.connect(self.timeclose, QtCore.SIGNAL('clicked()'),
  421. self, QtCore.SLOT('smashclock()'))
  422. self.connect(self.timeswitchl, QtCore.SIGNAL('clicked()'),
  423. self, QtCore.SLOT('prevtime()'))
  424. self.connect(self.timeswitchr, QtCore.SIGNAL('clicked()'),
  425. self, QtCore.SLOT('nexttime()'))
  426. self.times = {}
  427. self.initTheme(self.mainwindow.theme)
  428. # connect
  429. self.connect(self.textInput, QtCore.SIGNAL('returnPressed()'),
  430. self, QtCore.SLOT('sentMessage()'))
  431. layout_0 = QtGui.QVBoxLayout()
  432. layout_0.addWidget(self.textArea)
  433. layout_0.addWidget(self.textInput)
  434. layout_1 = QtGui.QHBoxLayout()
  435. layout_1.addLayout(layout_0)
  436. layout_1.addWidget(self.miniUserlist)
  437. layout_1.addWidget(self.userlist)
  438. # layout_1 = QtGui.QGridLayout()
  439. # layout_1.addWidget(self.timeslider, 0, 1, QtCore.Qt.AlignHCenter)
  440. # layout_1.addWidget(self.timeinput, 1, 0, 1, 3)
  441. layout_2 = QtGui.QHBoxLayout()
  442. layout_2.addWidget(self.timeslider)
  443. layout_2.addWidget(self.timeinput)
  444. layout_2.addWidget(self.timetravel)
  445. layout_2.addWidget(self.timeclose)
  446. layout_2.addWidget(self.timeswitchl)
  447. layout_2.addWidget(self.timeswitchr)
  448. self.layout = QtGui.QVBoxLayout()
  449. self.layout.addWidget(self.channelLabel)
  450. self.layout.addLayout(layout_1)
  451. self.layout.addLayout(layout_2)
  452. self.layout.setSpacing(0)
  453. margins = self.mainwindow.theme["memos/margins"]
  454. self.layout.setContentsMargins(margins["left"], margins["top"],
  455. margins["right"], margins["bottom"])
  456. self.setLayout(self.layout)
  457. if parent:
  458. parent.addChat(self)
  459. p = self.mainwindow.profile()
  460. timeGrammar = self.time.getGrammar()
  461. systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
  462. msg = p.memoopenmsg(systemColor, self.time.getTime(), timeGrammar, self.mainwindow.theme["convo/text/openmemo"], self.channel)
  463. self.time.openCurrentTime()
  464. self.textArea.append(convertTags(msg))
  465. self.mainwindow.chatlog.log(self.channel, msg)
  466. self.op = False
  467. self.newmessage = False
  468. self.history = PesterHistory()
  469. self.applyquirks = True
  470. self.ooc = False
  471. @QtCore.pyqtSlot()
  472. def toggleUserlist(self):
  473. if self.userlist.isHidden():
  474. self.userlist.show()
  475. self.miniUserlist.setText(">\n>")
  476. self.miniUserlist.setStyleSheet("%s border-width: 2px 0px 2px 2px;" % self.miniUserlist.styleSheet())
  477. else:
  478. self.userlist.hide()
  479. self.miniUserlist.setText("<\n<")
  480. self.miniUserlist.setStyleSheet("%s border-width: 2px;" % self.miniUserlist.styleSheet())
  481. def title(self):
  482. return self.channel
  483. def icon(self):
  484. return PesterIcon(self.mainwindow.theme["memos/memoicon"])
  485. def sendTimeInfo(self, newChum=False):
  486. if newChum:
  487. self.messageSent.emit("PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server")+"i"),
  488. self.title())
  489. else:
  490. self.messageSent.emit("PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server")),
  491. self.title())
  492. def updateMood(self):
  493. pass
  494. def updateBlocked(self):
  495. pass
  496. def updateColor(self, handle, color):
  497. chums = self.userlist.findItems(handle, QtCore.Qt.MatchFlags(0))
  498. for c in chums:
  499. c.setTextColor(color)
  500. def addMessage(self, text, handle):
  501. if type(handle) is bool:
  502. chum = self.mainwindow.profile()
  503. else:
  504. chum = PesterProfile(handle)
  505. self.notifyNewMessage()
  506. self.textArea.addMessage(text, chum)
  507. def initTheme(self, theme):
  508. self.resize(*theme["memos/size"])
  509. self.setStyleSheet("QFrame#%s { %s }" % (self.channel, theme["memos/style"]))
  510. self.setWindowIcon(PesterIcon(theme["memos/memoicon"]))
  511. t = Template(theme["memos/label/text"])
  512. if self.mainwindow.advanced and hasattr(self, 'modes'):
  513. self.channelLabel.setText(t.safe_substitute(channel=self.channel) + "(%s)" % (self.modes))
  514. else:
  515. self.channelLabel.setText(t.safe_substitute(channel=self.channel))
  516. self.channelLabel.setStyleSheet(theme["memos/label/style"])
  517. self.channelLabel.setAlignment(self.aligndict["h"][theme["memos/label/align/h"]] | self.aligndict["v"][theme["memos/label/align/v"]])
  518. self.channelLabel.setMaximumHeight(theme["memos/label/maxheight"])
  519. self.channelLabel.setMinimumHeight(theme["memos/label/minheight"])
  520. self.userlist.optionsMenu.setStyleSheet(theme["main/defaultwindow/style"])
  521. scrolls = "width: 12px; height: 12px; border: 0; padding: 0;"
  522. if theme.has_key("main/chums/scrollbar"):
  523. self.userlist.setStyleSheet("QListWidget { %s } QScrollBar { %s } QScrollBar::handle { %s } QScrollBar::add-line { %s } QScrollBar::sub-line { %s } QScrollBar:up-arrow { %s } QScrollBar:down-arrow { %s }" % (theme["memos/userlist/style"], theme["main/chums/scrollbar/style"] + scrolls, theme["main/chums/scrollbar/handle"], theme["main/chums/scrollbar/downarrow"], theme["main/chums/scrollbar/uparrow"], theme["main/chums/scrollbar/uarrowstyle"], theme["main/chums/scrollbar/darrowstyle"] ))
  524. elif theme.has_key("convo/scrollbar"):
  525. self.userlist.setStyleSheet("QListWidget { %s } QScrollBar { %s } QScrollBar::handle { %s } QScrollBar::add-line { %s } QScrollBar::sub-line { %s } QScrollBar:up-arrow { %s } QScrollBar:down-arrow { %s }" % (theme["memos/userlist/style"], theme["convo/scrollbar/style"] + scrolls, theme["convo/scrollbar/handle"], "display:none;", "display:none;", "display:none;", "display:none;" ))
  526. else:
  527. self.userlist.setStyleSheet("QListWidget { %s } QScrollBar { %s } QScrollBar::handle { %s }" % (theme["memos/userlist/style"], scrolls, "background-color: black;"))
  528. self.userlist.setFixedWidth(theme["memos/userlist/width"])
  529. if self.userlist.isHidden():
  530. borders = "border-width: 2px;"
  531. else:
  532. borders = "border-width: 2px 0px 2px 2px;"
  533. self.miniUserlist.setStyleSheet("%s padding: 0px; margin: 0px; margin-left: 5px; width: 10px; height: 90px; %s" % (theme["memos/userlist/style"], borders))
  534. self.addchumAction.setText(theme["main/menus/rclickchumlist/addchum"])
  535. self.banuserAction.setText(theme["main/menus/rclickchumlist/banuser"])
  536. self.opAction.setText(theme["main/menus/rclickchumlist/opuser"])
  537. self.voiceAction.setText(theme["main/menus/rclickchumlist/voiceuser"])
  538. self.quirkDisableAction.setText(theme["main/menus/rclickchumlist/quirkkill"])
  539. self.quirksOff.setText(theme["main/menus/rclickchumlist/quirksoff"])
  540. self.logchum.setText(theme["main/menus/rclickchumlist/viewlog"])
  541. self.invitechum.setText(theme["main/menus/rclickchumlist/invitechum"])
  542. self.chanModeMenu.setTitle(theme["main/menus/rclickchumlist/memosetting"])
  543. self.chanNoquirks.setText(theme["main/menus/rclickchumlist/memonoquirk"])
  544. self.chanHide.setText(theme["main/menus/rclickchumlist/memohidden"])
  545. self.chanInvite.setText(theme["main/menus/rclickchumlist/memoinvite"])
  546. self.chanMod.setText(theme["main/menus/rclickchumlist/memomute"])
  547. self.timeinput.setFixedWidth(theme["memos/time/text/width"])
  548. self.timeinput.setStyleSheet(theme["memos/time/text/style"])
  549. slidercss = "QSlider { %s } QSlider::groove { %s } QSlider::handle { %s }" % (theme["memos/time/slider/style"], theme["memos/time/slider/groove"], theme["memos/time/slider/handle"])
  550. self.timeslider.setStyleSheet(slidercss)
  551. larrow = PesterIcon(self.mainwindow.theme["memos/time/arrows/left"])
  552. self.timeswitchl.setIcon(larrow)
  553. self.timeswitchl.setIconSize(larrow.realsize())
  554. self.timeswitchl.setStyleSheet(self.mainwindow.theme["memos/time/arrows/style"])
  555. self.timetravel.setStyleSheet(self.mainwindow.theme["memos/time/buttons/style"])
  556. self.timeclose.setStyleSheet(self.mainwindow.theme["memos/time/buttons/style"])
  557. rarrow = PesterIcon(self.mainwindow.theme["memos/time/arrows/right"])
  558. self.timeswitchr.setIcon(rarrow)
  559. self.timeswitchr.setIconSize(rarrow.realsize())
  560. self.timeswitchr.setStyleSheet(self.mainwindow.theme["memos/time/arrows/style"])
  561. def changeTheme(self, theme):
  562. self.initTheme(theme)
  563. self.textArea.changeTheme(theme)
  564. self.textInput.changeTheme(theme)
  565. margins = theme["memos/margins"]
  566. self.layout.setContentsMargins(margins["left"], margins["top"],
  567. margins["right"], margins["bottom"])
  568. for item in [self.userlist.item(i) for i in range(0,self.userlist.count())]:
  569. self.iconCrap(item)
  570. def addUser(self, handle):
  571. chumdb = self.mainwindow.chumdb
  572. defaultcolor = QtGui.QColor("black")
  573. founder = False
  574. op = False
  575. halfop = False
  576. admin = False
  577. voice = False
  578. if handle[0] == '@':
  579. op = True
  580. handle = handle[1:]
  581. if handle == self.mainwindow.profile().handle:
  582. self.userlist.optionsMenu.addAction(self.opAction)
  583. self.userlist.optionsMenu.addAction(self.banuserAction)
  584. self.optionsMenu.addMenu(self.chanModeMenu)
  585. self.op = True
  586. elif handle[0] == '%':
  587. halfop = True
  588. handle = handle[1:]
  589. if handle == self.mainwindow.profile().handle:
  590. self.userlist.optionsMenu.addAction(self.opAction)
  591. self.userlist.optionsMenu.addAction(self.banuserAction)
  592. self.optionsMenu.addMenu(self.chanModeMenu)
  593. self.halfop = True
  594. elif handle[0] == '+':
  595. voice = True
  596. handle = handle[1:]
  597. elif handle[0] == '~':
  598. founder = True
  599. handle = handle[1:]
  600. elif handle[0] == '&':
  601. admin = True
  602. handle = handle[1:]
  603. item = QtGui.QListWidgetItem(handle)
  604. if handle == self.mainwindow.profile().handle:
  605. color = self.mainwindow.profile().color
  606. else:
  607. color = chumdb.getColor(handle, defaultcolor)
  608. item.box = (handle == "evacipatedBox")
  609. item.setTextColor(color)
  610. item.founder = founder
  611. item.op = op
  612. item.halfop = halfop
  613. item.admin = admin
  614. item.voice = voice
  615. self.umodes = ["box", "founder", "admin", "op", "halfop", "voice"]
  616. self.iconCrap(item)
  617. self.userlist.addItem(item)
  618. self.sortUsers()
  619. def sortUsers(self):
  620. users = []
  621. listing = self.userlist.item(0)
  622. while listing is not None:
  623. users.append(self.userlist.takeItem(0))
  624. listing = self.userlist.item(0)
  625. users.sort(key=lambda x: ((-1 if x.box else (0 if x.founder else (1 if x.admin else (2 if x.op else (3 if x.halfop else (4 if x.voice else 5)))))), x.text()))
  626. for u in users:
  627. self.userlist.addItem(u)
  628. def updateChanModes(self, modes, op):
  629. if not hasattr(self, 'modes'): self.modes = ""
  630. chanmodes = list(str(self.modes))
  631. if chanmodes and chanmodes[0] == "+": chanmodes = chanmodes[1:]
  632. modes = str(modes)
  633. if op:
  634. systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
  635. chum = self.mainwindow.profile()
  636. opchum = PesterProfile(op)
  637. if self.times.has_key(op):
  638. opgrammar = self.times[op].getGrammar()
  639. elif op == self.mainwindow.profile().handle:
  640. opgrammar = self.time.getGrammar()
  641. else:
  642. opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW")
  643. if modes[0] == "+":
  644. for m in modes[1:]:
  645. if m not in chanmodes:
  646. chanmodes.extend(m)
  647. # Make +c (disable ANSI colours) disable quirks.
  648. if modes.find("c") >= 0:
  649. self.chanNoquirks.setChecked(True)
  650. self.quirksOff.setChecked(True)
  651. self.applyquirks = False
  652. if op:
  653. msg = chum.memomodemsg(opchum, opgrammar, systemColor, "A No-Quirk zone", True)
  654. self.textArea.append(convertTags(msg))
  655. self.mainwindow.chatlog.log(self.channel, msg)
  656. if modes.find("s") >= 0:
  657. self.chanHide.setChecked(True)
  658. if op:
  659. msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secret", True)
  660. self.textArea.append(convertTags(msg))
  661. self.mainwindow.chatlog.log(self.channel, msg)
  662. if modes.find("i") >= 0:
  663. self.chanInvite.setChecked(True)
  664. if op:
  665. msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Invite-Only", True)
  666. self.textArea.append(convertTags(msg))
  667. self.mainwindow.chatlog.log(self.channel, msg)
  668. if modes.find("m") >= 0:
  669. self.chanMod.setChecked(True)
  670. if op:
  671. msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Muted", True)
  672. self.textArea.append(convertTags(msg))
  673. self.mainwindow.chatlog.log(self.channel, msg)
  674. elif modes[0] == "-":
  675. for i in modes[1:]:
  676. try:
  677. chanmodes.remove(i)
  678. except ValueError:
  679. pass
  680. if modes.find("c") >= 0:
  681. self.chanNoquirks.setChecked(False)
  682. if op:
  683. msg = chum.memomodemsg(opchum, opgrammar, systemColor, "A No-Quirk zone", False)
  684. self.textArea.append(convertTags(msg))
  685. self.mainwindow.chatlog.log(self.channel, msg)
  686. if modes.find("s") >= 0:
  687. self.chanHide.setChecked(False)
  688. if op:
  689. msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secret", False)
  690. self.textArea.append(convertTags(msg))
  691. self.mainwindow.chatlog.log(self.channel, msg)
  692. if modes.find("i") >= 0:
  693. self.chanInvite.setChecked(False)
  694. if op:
  695. msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Invite-Only", False)
  696. self.textArea.append(convertTags(msg))
  697. self.mainwindow.chatlog.log(self.channel, msg)
  698. if modes.find("m") >= 0:
  699. self.chanMod.setChecked(False)
  700. if op:
  701. msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Muted", False)
  702. self.textArea.append(convertTags(msg))
  703. self.mainwindow.chatlog.log(self.channel, msg)
  704. chanmodes.sort()
  705. self.modes = "+" + "".join(chanmodes)
  706. if self.mainwindow.advanced:
  707. t = Template(self.mainwindow.theme["memos/label/text"])
  708. self.channelLabel.setText(t.safe_substitute(channel=self.channel) + "(%s)" % (self.modes))
  709. def timeUpdate(self, handle, cmd):
  710. window = self.mainwindow
  711. chum = PesterProfile(handle)
  712. systemColor = QtGui.QColor(window.theme["memos/systemMsgColor"])
  713. close = None
  714. # old TC command?
  715. try:
  716. secs = int(cmd)
  717. time = datetime.fromtimestamp(secs)
  718. timed = time - datetime.now()
  719. s = (timed.seconds // 60)*60
  720. timed = timedelta(timed.days, s)
  721. except ValueError:
  722. if cmd == "i":
  723. timed = timedelta(0)
  724. else:
  725. if cmd[len(cmd)-1] == 'c':
  726. close = timeProtocol(cmd)
  727. timed = None
  728. else:
  729. timed = timeProtocol(cmd)
  730. if self.times.has_key(handle):
  731. if close is not None:
  732. if close in self.times[handle]:
  733. self.times[handle].setCurrent(close)
  734. grammar = self.times[handle].getGrammar()
  735. self.times[handle].removeTime(close)
  736. msg = chum.memoclosemsg(systemColor, grammar, window.theme["convo/text/closememo"])
  737. self.textArea.append(convertTags(msg))
  738. self.mainwindow.chatlog.log(self.channel, msg)
  739. elif timed not in self.times[handle]:
  740. self.times[handle].addTime(timed)
  741. else:
  742. self.times[handle].setCurrent(timed)
  743. else:
  744. if timed is not None:
  745. ttracker = TimeTracker(timed)
  746. self.times[handle] = ttracker
  747. @QtCore.pyqtSlot()
  748. def sentMessage(self):
  749. text = unicode(self.textInput.text())
  750. if text == "" or text[0:11] == "PESTERCHUM:":
  751. return
  752. oocDetected = oocre.match(text.strip())
  753. if self.ooc and not oocDetected:
  754. text = "(( %s ))" % (text)
  755. self.history.add(text)
  756. if self.time.getTime() == None:
  757. self.sendtime()
  758. grammar = self.time.getGrammar()
  759. quirks = self.mainwindow.userprofile.quirks
  760. lexmsg = lexMessage(text)
  761. if type(lexmsg[0]) is not mecmd:
  762. if self.applyquirks and not (self.ooc or oocDetected):
  763. lexmsg = quirks.apply(lexmsg)
  764. initials = self.mainwindow.profile().initials()
  765. colorcmd = self.mainwindow.profile().colorcmd()
  766. clientMsg = [colorBegin("<c=%s>" % (colorcmd), colorcmd),
  767. "%s%s%s: " % (grammar.pcf, initials, grammar.number)] + lexmsg + [colorEnd("</c>")]
  768. # account for TC's parsing error
  769. serverMsg = [colorBegin("<c=%s>" % (colorcmd), colorcmd),
  770. "%s: " % (initials)] + lexmsg + [colorEnd("</c>"), " "]
  771. else:
  772. clientMsg = copy(lexmsg)
  773. serverMsg = copy(lexmsg)
  774. self.addMessage(clientMsg, True)
  775. serverText = convertTags(serverMsg, "ctag")
  776. self.messageSent.emit(serverText, self.title())
  777. self.textInput.setText("")
  778. @QtCore.pyqtSlot(QtCore.QString)
  779. def namesUpdated(self, channel):
  780. c = unicode(channel)
  781. if c.lower() != self.channel.lower(): return
  782. # get namesdb
  783. namesdb = self.mainwindow.namesdb
  784. # reload names
  785. self.userlist.clear()
  786. for n in self.mainwindow.namesdb[self.channel]:
  787. self.addUser(n)
  788. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
  789. def modesUpdated(self, channel, modes):
  790. c = unicode(channel)
  791. if c.lower() == self.channel.lower():
  792. self.updateChanModes(modes, None)
  793. @QtCore.pyqtSlot(QtCore.QString)
  794. def closeInviteOnly(self, channel):
  795. c = unicode(channel)
  796. if c.lower() == self.channel.lower():
  797. self.disconnect(self.mainwindow, QtCore.SIGNAL('inviteOnlyChan(QString)'),
  798. self, QtCore.SLOT('closeInviteOnly(QString)'))
  799. if self.parent():
  800. print self.channel
  801. i = self.parent().tabIndices[self.channel]
  802. self.parent().tabClose(i)
  803. else:
  804. self.close()
  805. msgbox = QtGui.QMessageBox()
  806. msgbox.setText("%s: Invites only!" % (c))
  807. msgbox.setInformativeText("This channel is invite-only. You must get an invitation from someone on the inside before entering.")
  808. msgbox.setStandardButtons(QtGui.QMessageBox.Ok)
  809. ret = msgbox.exec_()
  810. def quirkDisable(self, op, msg):
  811. chums = self.userlist.findItems(op, QtCore.Qt.MatchFlags(0))
  812. for c in chums:
  813. if c.op:
  814. if msg == self.mainwindow.profile().handle:
  815. self.quirksOff.setChecked(True)
  816. self.applyquirks = False
  817. systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
  818. chum = self.mainwindow.profile()
  819. opchum = PesterProfile(op)
  820. if self.times.has_key(op):
  821. opgrammar = self.times[op].getGrammar()
  822. elif op == self.mainwindow.profile().handle:
  823. opgrammar = self.time.getGrammar()
  824. else:
  825. opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW")
  826. msg = chum.memoquirkkillmsg(opchum, opgrammar, systemColor)
  827. self.textArea.append(convertTags(msg))
  828. self.mainwindow.chatlog.log(self.channel, msg)
  829. def chumOPstuff(self, h, op):
  830. chum = PesterProfile(h)
  831. if h == self.mainwindow.profile().handle:
  832. chum = self.mainwindow.profile()
  833. ttracker = self.time
  834. curtime = self.time.getTime()
  835. elif self.times.has_key(h):
  836. ttracker = self.times[h]
  837. else:
  838. ttracker = TimeTracker(timedelta(0))
  839. opchum = PesterProfile(op)
  840. if self.times.has_key(op):
  841. opgrammar = self.times[op].getGrammar()
  842. elif op == self.mainwindow.profile().handle:
  843. opgrammar = self.time.getGrammar()
  844. else:
  845. opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW")
  846. return (chum, opchum, opgrammar)
  847. def iconCrap(self, c, down=True):
  848. for m in (self.umodes if down else reversed(self.umodes)):
  849. if eval("c."+m):
  850. if m == "box":
  851. icon = PesterIcon("smilies/box.png")
  852. else:
  853. icon = PesterIcon(self.mainwindow.theme["memos/"+m+"/icon"])
  854. c.setIcon(icon)
  855. return
  856. icon = QtGui.QIcon()
  857. c.setIcon(icon)
  858. @QtCore.pyqtSlot()
  859. def dumpNetsplit(self):
  860. if (len(self.netsplit) > 0):
  861. chum = self.mainwindow.profile()
  862. systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
  863. msg = chum.memonetsplitmsg(systemColor, self.netsplit)
  864. self.textArea.append(convertTags(msg))
  865. self.mainwindow.chatlog.log(self.channel, msg)
  866. del self.netsplit
  867. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
  868. def userPresentChange(self, handle, channel, update):
  869. h = unicode(handle)
  870. c = unicode(channel)
  871. update = unicode(update)
  872. if update[0:4] == "kick": # yeah, i'm lazy.
  873. l = update.split(":")
  874. update = l[0]
  875. op = l[1]
  876. reason = ":".join(l[2:])
  877. if update == "nick":
  878. l = h.split(":")
  879. oldnick = l[0]
  880. newnick = l[1]
  881. h = oldnick
  882. if update[0:1] in ["+", "-"]:
  883. l = update.split(":")
  884. update = l[0]
  885. op = l[1]
  886. if (update in ["join","left", "kick", \
  887. "+q", "-q", "+o", "-o", "+h", "-h", \
  888. "+a", "-a", "+v", "-v"]) \
  889. and c.lower() != self.channel.lower():
  890. return
  891. chums = self.userlist.findItems(h, QtCore.Qt.MatchFlags(0))
  892. systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
  893. # print exit
  894. if update in ("quit", "left", "nick", "netsplit"):
  895. if update == "netsplit":
  896. if not hasattr(self, "netsplit"):
  897. self.netsplit = []
  898. QtCore.QTimer.singleShot(1500, self, QtCore.SLOT('dumpNetsplit()'))
  899. for c in chums:
  900. chum = PesterProfile(h)
  901. self.userlist.takeItem(self.userlist.row(c))
  902. if not self.times.has_key(h):
  903. self.times[h] = TimeTracker(timedelta(0))
  904. allinitials = []
  905. while self.times[h].getTime() is not None:
  906. t = self.times[h]
  907. grammar = t.getGrammar()
  908. allinitials.append("%s%s%s" % (grammar.pcf, chum.initials(), grammar.number))
  909. self.times[h].removeTime(t.getTime())
  910. if update == "netsplit":
  911. self.netsplit.extend(allinitials)
  912. else:
  913. msg = chum.memoclosemsg(systemColor, allinitials, self.mainwindow.theme["convo/text/closememo"])
  914. self.textArea.append(convertTags(msg))
  915. self.mainwindow.chatlog.log(self.channel, msg)
  916. if update == "nick":
  917. self.addUser(newnick)
  918. newchums = self.userlist.findItems(newnick, QtCore.Qt.MatchFlags(0))
  919. for nc in newchums:
  920. for c in chums:
  921. nc.founder = c.founder
  922. nc.op = c.op
  923. nc.halfop = c.halfop
  924. nc.admin = c.admin
  925. self.iconCrap(nc)
  926. self.sortUsers()
  927. elif update == "kick":
  928. if len(chums) == 0:
  929. return
  930. c = chums[0]
  931. chum = PesterProfile(h)
  932. if h == self.mainwindow.profile().handle:
  933. chum = self.mainwindow.profile()
  934. ttracker = self.time
  935. curtime = self.time.getTime()
  936. elif self.times.has_key(h):
  937. ttracker = self.times[h]
  938. else:
  939. ttracker = TimeTracker(timedelta(0))
  940. allinitials = []
  941. opchum = PesterProfile(op)
  942. if self.times.has_key(op):
  943. opgrammar = self.times[op].getGrammar()
  944. elif op == self.mainwindow.profile().handle:
  945. opgrammar = self.time.getGrammar()
  946. else:
  947. opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW")
  948. while ttracker.getTime() is not None:
  949. grammar = ttracker.getGrammar()
  950. allinitials.append("%s%s%s" % (grammar.pcf, chum.initials(), grammar.number))
  951. ttracker.removeTime(ttracker.getTime())
  952. msg = chum.memobanmsg(opchum, opgrammar, systemColor, allinitials, reason)
  953. self.textArea.append(convertTags(msg))
  954. self.mainwindow.chatlog.log(self.channel, msg)
  955. if chum is self.mainwindow.profile():
  956. # are you next?
  957. msgbox = QtGui.QMessageBox()
  958. msgbox.setText(self.mainwindow.theme["convo/text/kickedmemo"])
  959. msgbox.setInformativeText("press 0k to rec0nnect or cancel to absc0nd")
  960. msgbox.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
  961. ret = msgbox.exec_()
  962. if ret == QtGui.QMessageBox.Ok:
  963. self.userlist.clear()
  964. self.time = TimeTracker(curtime)
  965. self.resetSlider(curtime)
  966. self.mainwindow.joinChannel.emit(self.channel)
  967. me = self.mainwindow.profile()
  968. self.time.openCurrentTime()
  969. msg = me.memoopenmsg(systemColor, self.time.getTime(), self.time.getGrammar(), self.mainwindow.theme["convo/text/openmemo"], self.channel)
  970. self.textArea.append(convertTags(msg))
  971. self.mainwindow.chatlog.log(self.channel, msg)
  972. elif ret == QtGui.QMessageBox.Cancel:
  973. if self.parent():
  974. i = self.parent().tabIndices[self.channel]
  975. self.parent().tabClose(i)
  976. else:
  977. self.close()
  978. else:
  979. # i warned you about those stairs bro
  980. self.userlist.takeItem(self.userlist.row(c))
  981. elif update == "join":
  982. self.addUser(h)
  983. time = self.time.getTime()
  984. serverText = "PESTERCHUM:TIME>"+delta2txt(time, "server")
  985. self.messageSent.emit(serverText, self.title())
  986. elif update == "+q":
  987. for c in chums:
  988. c.founder = True
  989. self.iconCrap(c)
  990. self.sortUsers()
  991. elif update == "-q":
  992. for c in chums:
  993. c.founder = False
  994. self.iconCrap(c)
  995. self.sortUsers()
  996. elif update == "+o":
  997. if self.mainwindow.config.opvoiceMessages():
  998. (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
  999. msg = chum.memoopmsg(opchum, opgrammar, systemColor)
  1000. self.textArea.append(convertTags(msg))
  1001. self.mainwindow.chatlog.log(self.channel, msg)
  1002. for c in chums:
  1003. c.op = True
  1004. self.iconCrap(c)
  1005. if unicode(c.text()) == self.mainwindow.profile().handle:
  1006. self.userlist.optionsMenu.addAction(self.opAction)
  1007. self.userlist.optionsMenu.addAction(self.voiceAction)
  1008. self.userlist.optionsMenu.addAction(self.banuserAction)
  1009. self.userlist.optionsMenu.addAction(self.quirkDisableAction)
  1010. self.optionsMenu.addMenu(self.chanModeMenu)
  1011. self.sortUsers()
  1012. elif update == "-o":
  1013. self.mainwindow.channelNames.emit(self.channel)
  1014. if self.mainwindow.config.opvoiceMessages():
  1015. (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
  1016. msg = chum.memodeopmsg(opchum, opgrammar, systemColor)
  1017. self.textArea.append(convertTags(msg))
  1018. self.mainwindow.chatlog.log(self.channel, msg)
  1019. for c in chums:
  1020. c.op = False
  1021. self.iconCrap(c)
  1022. if unicode(c.text()) == self.mainwindow.profile().handle:
  1023. self.userlist.optionsMenu.removeAction(self.opAction)
  1024. self.userlist.optionsMenu.removeAction(self.voiceAction)
  1025. self.userlist.optionsMenu.removeAction(self.banuserAction)
  1026. self.userlist.optionsMenu.removeAction(self.quirkDisableAction)
  1027. self.optionsMenu.removeAction(self.chanModeMenu.menuAction())
  1028. self.sortUsers()
  1029. elif update == "+h":
  1030. if self.mainwindow.config.opvoiceMessages():
  1031. (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
  1032. msg = chum.memoopmsg(opchum, opgrammar, systemColor)
  1033. self.textArea.append(convertTags(msg))
  1034. self.mainwindow.chatlog.log(self.channel, msg)
  1035. for c in chums:
  1036. c.halfop = True
  1037. self.iconCrap(c)
  1038. if unicode(c.text()) == self.mainwindow.profile().handle:
  1039. self.userlist.optionsMenu.addAction(self.opAction)
  1040. self.userlist.optionsMenu.addAction(self.voiceAction)
  1041. self.userlist.optionsMenu.addAction(self.banuserAction)
  1042. self.userlist.optionsMenu.addAction(self.quirkDisableAction)
  1043. self.optionsMenu.addMenu(self.chanModeMenu)
  1044. self.sortUsers()
  1045. elif update == "-h":
  1046. self.mainwindow.channelNames.emit(self.channel)
  1047. if self.mainwindow.config.opvoiceMessages():
  1048. (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
  1049. msg = chum.memodeopmsg(opchum, opgrammar, systemColor)
  1050. self.textArea.append(convertTags(msg))
  1051. self.mainwindow.chatlog.log(self.channel, msg)
  1052. for c in chums:
  1053. c.halfop = False
  1054. self.iconCrap(c)
  1055. if unicode(c.text()) == self.mainwindow.profile().handle:
  1056. self.userlist.optionsMenu.removeAction(self.opAction)
  1057. self.userlist.optionsMenu.removeAction(self.voiceAction)
  1058. self.userlist.optionsMenu.removeAction(self.banuserAction)
  1059. self.userlist.optionsMenu.removeAction(self.quirkDisableAction)
  1060. self.optionsMenu.removeAction(self.chanModeMenu.menuAction())
  1061. self.sortUsers()
  1062. elif update == "+a":
  1063. for c in chums:
  1064. c.admin = True
  1065. self.iconCrap(c)
  1066. self.sortUsers()
  1067. elif update == "-a":
  1068. for c in chums:
  1069. c.admin = False
  1070. self.iconCrap(c)
  1071. self.sortUsers()
  1072. elif c.lower() == self.channel.lower() and h == "" and update[0] in ["+","-"]:
  1073. self.updateChanModes(update, op)
  1074. elif update == "+v":
  1075. if self.mainwindow.config.opvoiceMessages():
  1076. (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
  1077. msg = chum.memovoicemsg(opchum, opgrammar, systemColor)
  1078. self.textArea.append(convertTags(msg))
  1079. self.mainwindow.chatlog.log(self.channel, msg)
  1080. for c in chums:
  1081. c.voice = True
  1082. self.iconCrap(c)
  1083. self.sortUsers()
  1084. elif update == "-v":
  1085. if self.mainwindow.config.opvoiceMessages():
  1086. (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
  1087. msg = chum.memodevoicemsg(opchum, opgrammar, systemColor)
  1088. self.textArea.append(convertTags(msg))
  1089. self.mainwindow.chatlog.log(self.channel, msg)
  1090. for c in chums:
  1091. c.voice = False
  1092. self.iconCrap(c)
  1093. self.sortUsers()
  1094. elif c.lower() == self.channel.lower() and h == "" and update[0] in ["+","-"]:
  1095. self.updateChanModes(update, op)
  1096. @QtCore.pyqtSlot()
  1097. def addChumSlot(self):
  1098. if not self.userlist.currentItem():
  1099. return
  1100. currentChum = PesterProfile(unicode(self.userlist.currentItem().text()))
  1101. self.mainwindow.addChum(currentChum)
  1102. @QtCore.pyqtSlot()
  1103. def banSelectedUser(self):
  1104. if not self.userlist.currentItem():
  1105. return
  1106. currentHandle = unicode(self.userlist.currentItem().text())
  1107. (reason, ok) = QtGui.QInputDialog.getText(self, "Ban User", "Enter the reason you are banning this user (optional):")
  1108. if ok:
  1109. self.mainwindow.kickUser.emit("%s:%s" % (currentHandle, reason), self.channel)
  1110. @QtCore.pyqtSlot()
  1111. def opSelectedUser(self):
  1112. if not self.userlist.currentItem():
  1113. return
  1114. currentHandle = unicode(self.userlist.currentItem().text())
  1115. self.mainwindow.setChannelMode.emit(self.channel, "+o", currentHandle)
  1116. @QtCore.pyqtSlot()
  1117. def voiceSelectedUser(self):
  1118. if not self.userlist.currentItem():
  1119. return
  1120. currentHandle = unicode(self.userlist.currentItem().text())
  1121. self.mainwindow.setChannelMode.emit(self.channel, "+v", currentHandle)
  1122. @QtCore.pyqtSlot()
  1123. def killQuirkUser(self):
  1124. if not self.userlist.currentItem():
  1125. return
  1126. currentHandle = unicode(self.userlist.currentItem().text())
  1127. self.mainwindow.killSomeQuirks.emit(self.channel, currentHandle)
  1128. def resetSlider(self, time, send=True):
  1129. self.timeinput.setText(delta2txt(time))
  1130. self.timeinput.setSlider()
  1131. if send:
  1132. self.sendtime()
  1133. @QtCore.pyqtSlot()
  1134. def openChumLogs(self):
  1135. currentChum = self.channel
  1136. self.mainwindow.chumList.pesterlogviewer = PesterLogViewer(currentChum, self.mainwindow.config, self.mainwindow.theme, self.mainwindow)
  1137. self.connect(self.mainwindow.chumList.pesterlogviewer, QtCore.SIGNAL('rejected()'),
  1138. self.mainwindow.chumList, QtCore.SLOT('closeActiveLog()'))
  1139. self.mainwindow.chumList.pesterlogviewer.show()
  1140. self.mainwindow.chumList.pesterlogviewer.raise_()
  1141. self.mainwindow.chumList.pesterlogviewer.activateWindow()
  1142. @QtCore.pyqtSlot()
  1143. def inviteChums(self):
  1144. if not hasattr(self, 'invitechums'):
  1145. self.invitechums = None
  1146. if not self.invitechums:
  1147. (chum, ok) = QtGui.QInputDialog.getText(self, "Invite to Chat", "Enter the chumhandle of the user you'd like to invite:")
  1148. if ok:
  1149. chum = unicode(chum)
  1150. self.mainwindow.inviteChum.emit(chum, self.channel)
  1151. self.invitechums = None
  1152. @QtCore.pyqtSlot(bool)
  1153. def noquirksChan(self, on):
  1154. x = ["-","+"][on]
  1155. self.mainwindow.setChannelMode.emit(self.channel, x+"c", "")
  1156. @QtCore.pyqtSlot(bool)
  1157. def hideChan(self, on):
  1158. x = ["-","+"][on]
  1159. self.mainwindow.setChannelMode.emit(self.channel, x+"s", "")
  1160. @QtCore.pyqtSlot(bool)
  1161. def inviteChan(self, on):
  1162. x = ["-","+"][on]
  1163. self.mainwindow.setChannelMode.emit(self.channel, x+"i", "")
  1164. @QtCore.pyqtSlot(bool)
  1165. def modChan(self, on):
  1166. x = ["-","+"][on]
  1167. self.mainwindow.setChannelMode.emit(self.channel, x+"m", "")
  1168. @QtCore.pyqtSlot()
  1169. def sendtime(self):
  1170. me = self.mainwindow.profile()
  1171. systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
  1172. time = txt2delta(self.timeinput.text())
  1173. present = self.time.addTime(time)
  1174. serverText = "PESTERCHUM:TIME>"+delta2txt(time, "server")
  1175. self.messageSent.emit(serverText, self.title())
  1176. @QtCore.pyqtSlot()
  1177. def smashclock(self):
  1178. me = self.mainwindow.profile()
  1179. time = txt2delta(self.timeinput.text())
  1180. removed = self.time.removeTime(time)
  1181. if removed:
  1182. grammar = self.time.getGrammarTime(time)
  1183. systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
  1184. msg = me.memoclosemsg(systemColor, grammar, self.mainwindow.theme["convo/text/closememo"])
  1185. self.textArea.append(convertTags(msg))
  1186. self.mainwindow.chatlog.log(self.channel, msg)
  1187. newtime = self.time.getTime()
  1188. if newtime is None:
  1189. newtime = timedelta(0)
  1190. self.resetSlider(newtime, send=False)
  1191. else:
  1192. self.resetSlider(newtime)
  1193. @QtCore.pyqtSlot()
  1194. def prevtime(self):
  1195. time = self.time.prevTime()
  1196. self.time.setCurrent(time)
  1197. self.resetSlider(time)
  1198. self.textInput.setFocus()
  1199. @QtCore.pyqtSlot()
  1200. def nexttime(self):
  1201. time = self.time.nextTime()
  1202. self.time.setCurrent(time)
  1203. self.resetSlider(time)
  1204. self.textInput.setFocus()
  1205. def closeEvent(self, event):
  1206. self.mainwindow.waitingMessages.messageAnswered(self.channel)
  1207. self.windowClosed.emit(self.title())
  1208. windowClosed = QtCore.pyqtSignal(QtCore.QString)
  1209. timelist = ["0:00", "0:01", "0:02", "0:04", "0:06", "0:10", "0:14", "0:22", "0:30", "0:41", "1:00", "1:34", "2:16", "3:14", "4:13", "4:20", "5:25", "6:12", "7:30", "8:44", "10:25", "11:34", "14:13", "16:12", "17:44", "22:22", "25:10", "33:33", "42:00", "43:14", "50:00", "62:12", "75:00", "88:44", "100", "133", "143", "188", "200", "222", "250", "314", "333", "413", "420", "500", "600", "612", "888", "1000", "1025"]
  1210. timedlist = [timedelta(0), timedelta(0, 60), timedelta(0, 120), timedelta(0, 240), timedelta(0, 360), timedelta(0, 600), timedelta(0, 840), timedelta(0, 1320), timedelta(0, 1800), timedelta(0, 2460), timedelta(0, 3600), timedelta(0, 5640), timedelta(0, 8160), timedelta(0, 11640), timedelta(0, 15180), timedelta(0, 15600), timedelta(0, 19500), timedelta(0, 22320), timedelta(0, 27000), timedelta(0, 31440), timedelta(0, 37500), timedelta(0, 41640), timedelta(0, 51180), timedelta(0, 58320), timedelta(0, 63840), timedelta(0, 80520), timedelta(1, 4200), timedelta(1, 34380), timedelta(1, 64800), timedelta(1, 69240), timedelta(2, 7200), timedelta(2, 51120), timedelta(3, 10800), timedelta(3, 60240), timedelta(4, 14400), timedelta(5, 46800), timedelta(5, 82800), timedelta(7, 72000), timedelta(8, 28800), timedelta(9, 21600), timedelta(10, 36000), timedelta(13, 7200), timedelta(13, 75600), timedelta(17, 18000), timedelta(17, 43200), timedelta(20, 72000), timedelta(25), timedelta(25, 43200), timedelta(37), timedelta(41, 57600), timedelta(42, 61200)]