PageRenderTime 53ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/pesterchum.py

https://github.com/602p/pesterchum
Python | 3030 lines | 2959 code | 37 blank | 34 comment | 184 complexity | 47506ef152ea949f18334b2aa8c08a2e MD5 | raw file
Possible License(s): GPL-3.0, Cube
  1. # pesterchum
  2. import os, shutil, sys, getopt
  3. if os.path.dirname(sys.argv[0]):
  4. os.chdir(os.path.dirname(sys.argv[0]))
  5. import version
  6. version.pcVerCalc()
  7. import logging
  8. from datetime import *
  9. import random
  10. import re
  11. from time import time
  12. import threading, Queue
  13. reqmissing = []
  14. optmissing = []
  15. try:
  16. from PyQt4 import QtGui, QtCore
  17. except ImportError, e:
  18. module = str(e)
  19. if module.startswith("No module named ") or \
  20. module.startswith("cannot import name "):
  21. reqmissing.append(module[module.rfind(" ")+1:])
  22. else: print e
  23. try:
  24. import pygame
  25. except ImportError, e:
  26. pygame = None
  27. module = str(e)
  28. if module[:16] == "No module named ": optmissing.append(module[16:])
  29. else: print e
  30. if reqmissing:
  31. print "ERROR: The following modules are required for Pesterchum to run and are missing on your system:"
  32. for m in reqmissing: print "* "+m
  33. exit()
  34. vnum = QtCore.qVersion()
  35. major = int(vnum[:vnum.find(".")])
  36. if vnum.find(".", vnum.find(".")+1) != -1:
  37. minor = int(vnum[vnum.find(".")+1:vnum.find(".", vnum.find(".")+1)])
  38. else:
  39. minor = int(vnum[vnum.find(".")+1:])
  40. if not ((major > 4) or (major == 4 and minor >= 6)):
  41. print "ERROR: Pesterchum requires Qt version >= 4.6"
  42. print "You currently have version " + vnum + ". Please ungrade Qt"
  43. exit()
  44. import ostools
  45. # Placed here before importing the rest of pesterchum, since bits of it need
  46. # OSX's data directory and it doesn't hurt to have everything set up before
  47. # plowing on. :o)
  48. # ~Lex
  49. _datadir = ostools.getDataDir()
  50. # See, what I've done here is that _datadir is '' if we're not on OSX, so the
  51. # concatination is the same as if it wasn't there.
  52. # UPDATE 2011-11-28 <Kiooeht>:
  53. # Now using data directory as defined by QDesktopServices on all platforms
  54. # (on Linux, same as using xdg). To stay safe with older versions, copy any
  55. # data (profiles, logs, etc) from old location to new data directory.
  56. if _datadir:
  57. if not os.path.exists(_datadir):
  58. os.makedirs(_datadir)
  59. if not os.path.exists(_datadir+"profiles/") and os.path.exists("profiles/"):
  60. shutil.move("profiles/", _datadir+"profiles/")
  61. if not os.path.exists(_datadir+"pesterchum.js") and os.path.exists("pesterchum.js"):
  62. shutil.move("pesterchum.js", _datadir+"pesterchum.js")
  63. if not os.path.exists(_datadir+"logs/") and os.path.exists("logs/"):
  64. shutil.move("logs/", _datadir+"logs/")
  65. if not os.path.exists(_datadir+"profiles"):
  66. os.mkdir(_datadir+"profiles")
  67. if not os.path.exists(_datadir+"pesterchum.js"):
  68. f = open(_datadir+"pesterchum.js", 'w')
  69. f.write("{}")
  70. f.close()
  71. if not os.path.exists(_datadir+"logs"):
  72. os.mkdir(_datadir+"logs")
  73. from menus import PesterChooseQuirks, PesterChooseTheme, \
  74. PesterChooseProfile, PesterOptions, PesterUserlist, PesterMemoList, \
  75. LoadingScreen, AboutPesterchum, UpdatePesterchum, AddChumDialog
  76. from mood import Mood, PesterMoodAction, PesterMoodHandler, PesterMoodButton
  77. from dataobjs import PesterProfile, pesterQuirk, pesterQuirks
  78. from generic import PesterIcon, RightClickList, RightClickTree, \
  79. MultiTextDialog, PesterList, CaseInsensitiveDict, MovingWindow, \
  80. NoneSound, WMButton
  81. from convo import PesterTabWindow, PesterText, PesterInput, PesterConvo
  82. from parsetools import convertTags, addTimeInitial, themeChecker, ThemeException
  83. from memos import PesterMemo, MemoTabWindow, TimeTracker
  84. from irc import PesterIRC
  85. from logviewer import PesterLogUserSelect, PesterLogViewer
  86. from bugreport import BugReporter
  87. from randomer import RandomHandler
  88. # Rawr, fuck you OSX leopard
  89. if not ostools.isOSXLeopard():
  90. from updatecheck import MSPAChecker
  91. from toast import PesterToastMachine, PesterToast
  92. from libs import pytwmn
  93. from profile import *
  94. canon_handles = ["apocalypseArisen", "arsenicCatnip", "arachnidsGrip", "adiosToreador", \
  95. "caligulasAquarium", "cuttlefishCuller", "carcinoGeneticist", "centaursTesticle", \
  96. "grimAuxiliatrix", "gallowsCalibrator", "gardenGnostic", "ectoBiologist", \
  97. "twinArmageddons", "terminallyCapricious", "turntechGodhead", "tentacleTherapist"]
  98. class waitingMessageHolder(object):
  99. def __init__(self, mainwindow, **msgfuncs):
  100. self.mainwindow = mainwindow
  101. self.funcs = msgfuncs
  102. self.queue = msgfuncs.keys()
  103. if len(self.queue) > 0:
  104. self.mainwindow.updateSystemTray()
  105. def waitingHandles(self):
  106. return self.queue
  107. def answerMessage(self):
  108. func = self.funcs[self.queue[0]]
  109. func()
  110. def messageAnswered(self, handle):
  111. if handle not in self.queue:
  112. return
  113. self.queue = [q for q in self.queue if q != handle]
  114. del self.funcs[handle]
  115. if len(self.queue) == 0:
  116. self.mainwindow.updateSystemTray()
  117. def addMessage(self, handle, func):
  118. if not self.funcs.has_key(handle):
  119. self.queue.append(handle)
  120. self.funcs[handle] = func
  121. if len(self.queue) > 0:
  122. self.mainwindow.updateSystemTray()
  123. def __len__(self):
  124. return len(self.queue)
  125. class chumListing(QtGui.QTreeWidgetItem):
  126. def __init__(self, chum, window):
  127. QtGui.QTreeWidgetItem.__init__(self, [chum.handle])
  128. self.mainwindow = window
  129. self.chum = chum
  130. self.handle = chum.handle
  131. self.setMood(Mood("offline"))
  132. self.status = None
  133. self.setToolTip(0, "%s: %s" % (chum.handle, window.chumdb.getNotes(chum.handle)))
  134. def setMood(self, mood):
  135. if hasattr(self.mainwindow, "chumList") and self.mainwindow.chumList.notify:
  136. #print "%s -> %s" % (self.chum.mood.name(), mood.name())
  137. if self.mainwindow.config.notifyOptions() & self.mainwindow.config.SIGNOUT and \
  138. mood.name() == "offline" and self.chum.mood.name() != "offline":
  139. #print "OFFLINE NOTIFY: " + self.handle
  140. uri = self.mainwindow.theme["toasts/icon/signout"]
  141. n = self.mainwindow.tm.Toast(self.mainwindow.tm.appName,
  142. "%s is Offline" % (self.handle), uri)
  143. n.show()
  144. elif self.mainwindow.config.notifyOptions() & self.mainwindow.config.SIGNIN and \
  145. mood.name() != "offline" and self.chum.mood.name() == "offline":
  146. #print "ONLINE NOTIFY: " + self.handle
  147. uri = self.mainwindow.theme["toasts/icon/signin"]
  148. n = self.mainwindow.tm.Toast(self.mainwindow.tm.appName,
  149. "%s is Online" % (self.handle), uri)
  150. n.show()
  151. login = False
  152. logout = False
  153. if mood.name() == "offline" and self.chum.mood.name() != "offline":
  154. logout = True
  155. elif mood.name() != "offline" and self.chum.mood.name() == "offline":
  156. login = True
  157. self.chum.mood = mood
  158. self.updateMood(login=login, logout=logout)
  159. def setColor(self, color):
  160. self.chum.color = color
  161. def updateMood(self, unblock=False, login=False, logout=False):
  162. mood = self.chum.mood
  163. self.mood = mood
  164. icon = self.mood.icon(self.mainwindow.theme)
  165. if login:
  166. self.login()
  167. elif logout:
  168. self.logout()
  169. else:
  170. self.setIcon(0, icon)
  171. try:
  172. self.setTextColor(0, QtGui.QColor(self.mainwindow.theme["main/chums/moods"][self.mood.name()]["color"]))
  173. except KeyError:
  174. self.setTextColor(0, QtGui.QColor(self.mainwindow.theme["main/chums/moods/chummy/color"]))
  175. def changeTheme(self, theme):
  176. icon = self.mood.icon(theme)
  177. self.setIcon(0, icon)
  178. try:
  179. self.setTextColor(0, QtGui.QColor(self.mainwindow.theme["main/chums/moods"][self.mood.name()]["color"]))
  180. except KeyError:
  181. self.setTextColor(0, QtGui.QColor(self.mainwindow.theme["main/chums/moods/chummy/color"]))
  182. def login(self):
  183. self.setIcon(0, PesterIcon("themes/arrow_right.png"))
  184. self.status = "in"
  185. QtCore.QTimer.singleShot(5000, self.doneLogin)
  186. def doneLogin(self):
  187. icon = self.mood.icon(self.mainwindow.theme)
  188. self.setIcon(0, icon)
  189. def logout(self):
  190. self.setIcon(0, PesterIcon("themes/arrow_left.png"))
  191. self.status = "out"
  192. QtCore.QTimer.singleShot(5000, self.doneLogout)
  193. def doneLogout(self):
  194. hideoff = self.mainwindow.config.hideOfflineChums()
  195. icon = self.mood.icon(self.mainwindow.theme)
  196. self.setIcon(0, icon)
  197. if hideoff and self.status and self.status == "out":
  198. self.mainwindow.chumList.takeItem(self)
  199. def __lt__(self, cl):
  200. h1 = self.handle.lower()
  201. h2 = cl.handle.lower()
  202. return (h1 < h2)
  203. class chumArea(RightClickTree):
  204. def __init__(self, chums, parent=None):
  205. QtGui.QTreeWidget.__init__(self, parent)
  206. self.notify = False
  207. QtCore.QTimer.singleShot(30000, self, QtCore.SLOT('beginNotify()'))
  208. self.mainwindow = parent
  209. theme = self.mainwindow.theme
  210. self.chums = chums
  211. gTemp = self.mainwindow.config.getGroups()
  212. self.groups = [g[0] for g in gTemp]
  213. self.openGroups = [g[1] for g in gTemp]
  214. self.showAllGroups(True)
  215. if not self.mainwindow.config.hideOfflineChums():
  216. self.showAllChums()
  217. if not self.mainwindow.config.showEmptyGroups():
  218. self.hideEmptyGroups()
  219. self.groupMenu = QtGui.QMenu(self)
  220. self.canonMenu = QtGui.QMenu(self)
  221. self.optionsMenu = QtGui.QMenu(self)
  222. self.pester = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/pester"], self)
  223. self.connect(self.pester, QtCore.SIGNAL('triggered()'),
  224. self, QtCore.SLOT('activateChum()'))
  225. self.removechum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/removechum"], self)
  226. self.connect(self.removechum, QtCore.SIGNAL('triggered()'),
  227. self, QtCore.SLOT('removeChum()'))
  228. self.blockchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/blockchum"], self)
  229. self.connect(self.blockchum, QtCore.SIGNAL('triggered()'),
  230. self, QtCore.SLOT('blockChum()'))
  231. self.logchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self)
  232. self.connect(self.logchum, QtCore.SIGNAL('triggered()'),
  233. self, QtCore.SLOT('openChumLogs()'))
  234. self.reportchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/report"], self)
  235. self.connect(self.reportchum, QtCore.SIGNAL('triggered()'),
  236. self, QtCore.SLOT('reportChum()'))
  237. self.findalts = QtGui.QAction("Find Alts", self)
  238. self.connect(self.findalts, QtCore.SIGNAL('triggered()'),
  239. self, QtCore.SLOT('findAlts()'))
  240. self.removegroup = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/removegroup"], self)
  241. self.connect(self.removegroup, QtCore.SIGNAL('triggered()'),
  242. self, QtCore.SLOT('removeGroup()'))
  243. self.renamegroup = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/renamegroup"], self)
  244. self.connect(self.renamegroup, QtCore.SIGNAL('triggered()'),
  245. self, QtCore.SLOT('renameGroup()'))
  246. self.notes = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/notes"], self)
  247. self.connect(self.notes, QtCore.SIGNAL('triggered()'),
  248. self, QtCore.SLOT('editNotes()'))
  249. self.optionsMenu.addAction(self.pester)
  250. self.optionsMenu.addAction(self.logchum)
  251. self.optionsMenu.addAction(self.notes)
  252. self.optionsMenu.addAction(self.blockchum)
  253. self.optionsMenu.addAction(self.removechum)
  254. self.moveMenu = QtGui.QMenu(self.mainwindow.theme["main/menus/rclickchumlist/movechum"], self)
  255. self.optionsMenu.addMenu(self.moveMenu)
  256. self.optionsMenu.addAction(self.reportchum)
  257. self.moveGroupMenu()
  258. self.groupMenu.addAction(self.renamegroup)
  259. self.groupMenu.addAction(self.removegroup)
  260. self.canonMenu.addAction(self.pester)
  261. self.canonMenu.addAction(self.logchum)
  262. self.canonMenu.addAction(self.blockchum)
  263. self.canonMenu.addAction(self.removechum)
  264. self.canonMenu.addMenu(self.moveMenu)
  265. self.canonMenu.addAction(self.reportchum)
  266. self.canonMenu.addAction(self.findalts)
  267. self.initTheme(theme)
  268. #self.sortItems()
  269. #self.sortItems(1, QtCore.Qt.AscendingOrder)
  270. self.setSortingEnabled(False)
  271. self.header().hide()
  272. self.setDropIndicatorShown(True)
  273. self.setIndentation(4)
  274. self.setDragEnabled(True)
  275. self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
  276. self.setAnimated(True)
  277. self.setRootIsDecorated(False)
  278. self.connect(self, QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem *, int)'),
  279. self, QtCore.SLOT('expandGroup()'))
  280. @QtCore.pyqtSlot()
  281. def beginNotify(self):
  282. print "BEGIN NOTIFY"
  283. self.notify = True
  284. def getOptionsMenu(self):
  285. if not self.currentItem():
  286. return None
  287. text = unicode(self.currentItem().text(0))
  288. if text.rfind(" (") != -1:
  289. text = text[0:text.rfind(" (")]
  290. if text == "Chums":
  291. return None
  292. elif text in self.groups:
  293. return self.groupMenu
  294. else:
  295. currenthandle = self.currentItem().chum.handle
  296. if currenthandle in canon_handles:
  297. return self.canonMenu
  298. else:
  299. return self.optionsMenu
  300. def startDrag(self, dropAction):
  301. # create mime data object
  302. mime = QtCore.QMimeData()
  303. mime.setData('application/x-item', '???')
  304. # start drag
  305. drag = QtGui.QDrag(self)
  306. drag.setMimeData(mime)
  307. drag.start(QtCore.Qt.MoveAction)
  308. def dragMoveEvent(self, event):
  309. if event.mimeData().hasFormat("application/x-item"):
  310. event.setDropAction(QtCore.Qt.MoveAction)
  311. event.accept()
  312. else:
  313. event.ignore()
  314. def dragEnterEvent(self, event):
  315. if (event.mimeData().hasFormat('application/x-item')):
  316. event.accept()
  317. else:
  318. event.ignore()
  319. def dropEvent(self, event):
  320. if (event.mimeData().hasFormat('application/x-item')):
  321. event.acceptProposedAction()
  322. else:
  323. event.ignore()
  324. return
  325. thisitem = str(event.source().currentItem().text(0))
  326. if thisitem.rfind(" (") != -1:
  327. thisitem = thisitem[0:thisitem.rfind(" (")]
  328. # Drop item is a group
  329. thisitem = unicode(event.source().currentItem().text(0))
  330. if thisitem.rfind(" (") != -1:
  331. thisitem = thisitem[0:thisitem.rfind(" (")]
  332. if thisitem == "Chums" or thisitem in self.groups:
  333. droppos = self.itemAt(event.pos())
  334. if not droppos: return
  335. droppos = unicode(droppos.text(0))
  336. if droppos.rfind(" ") != -1:
  337. droppos = droppos[0:droppos.rfind(" ")]
  338. if droppos == "Chums" or droppos in self.groups:
  339. saveOpen = event.source().currentItem().isExpanded()
  340. saveDrop = self.itemAt(event.pos())
  341. saveItem = self.takeTopLevelItem(self.indexOfTopLevelItem(event.source().currentItem()))
  342. self.insertTopLevelItems(self.indexOfTopLevelItem(saveDrop)+1, [saveItem])
  343. if saveOpen:
  344. saveItem.setExpanded(True)
  345. gTemp = []
  346. for i in range(self.topLevelItemCount()):
  347. text = unicode(self.topLevelItem(i).text(0))
  348. if text.rfind(" (") != -1:
  349. text = text[0:text.rfind(" (")]
  350. gTemp.append([unicode(text), self.topLevelItem(i).isExpanded()])
  351. self.mainwindow.config.saveGroups(gTemp)
  352. # Drop item is a chum
  353. else:
  354. item = self.itemAt(event.pos())
  355. if item:
  356. text = unicode(item.text(0))
  357. # Figure out which group to drop into
  358. if text.rfind(" (") != -1:
  359. text = text[0:text.rfind(" (")]
  360. if text == "Chums" or text in self.groups:
  361. group = text
  362. gitem = item
  363. else:
  364. ptext = unicode(item.parent().text(0))
  365. if ptext.rfind(" ") != -1:
  366. ptext = ptext[0:ptext.rfind(" ")]
  367. group = ptext
  368. gitem = item.parent()
  369. chumLabel = event.source().currentItem()
  370. chumLabel.chum.group = group
  371. self.mainwindow.chumdb.setGroup(chumLabel.chum.handle, group)
  372. self.takeItem(chumLabel)
  373. # Using manual chum reordering
  374. if self.mainwindow.config.sortMethod() == 2:
  375. insertIndex = gitem.indexOfChild(item)
  376. if insertIndex == -1:
  377. insertIndex = 0
  378. gitem.insertChild(insertIndex, chumLabel)
  379. chums = self.mainwindow.config.chums()
  380. if item == gitem:
  381. item = gitem.child(0)
  382. inPos = chums.index(str(item.text(0)))
  383. if chums.index(thisitem) < inPos:
  384. inPos -= 1
  385. chums.remove(thisitem)
  386. chums.insert(inPos, unicode(thisitem))
  387. self.mainwindow.config.setChums(chums)
  388. else:
  389. self.addItem(chumLabel)
  390. if self.mainwindow.config.showOnlineNumbers():
  391. self.showOnlineNumbers()
  392. def moveGroupMenu(self):
  393. currentGroup = self.currentItem()
  394. if currentGroup:
  395. if currentGroup.parent():
  396. text = unicode(currentGroup.parent().text(0))
  397. else:
  398. text = unicode(currentGroup.text(0))
  399. if text.rfind(" (") != -1:
  400. text = text[0:text.rfind(" (")]
  401. currentGroup = text
  402. self.moveMenu.clear()
  403. actGroup = QtGui.QActionGroup(self)
  404. groups = self.groups[:]
  405. for gtext in groups:
  406. if gtext == currentGroup:
  407. continue
  408. movegroup = self.moveMenu.addAction(gtext)
  409. actGroup.addAction(movegroup)
  410. self.connect(actGroup, QtCore.SIGNAL('triggered(QAction *)'),
  411. self, QtCore.SLOT('moveToGroup(QAction *)'))
  412. def addChum(self, chum):
  413. if len([c for c in self.chums if c.handle == chum.handle]) != 0:
  414. return
  415. self.chums.append(chum)
  416. if not (self.mainwindow.config.hideOfflineChums() and
  417. chum.mood.name() == "offline"):
  418. chumLabel = chumListing(chum, self.mainwindow)
  419. self.addItem(chumLabel)
  420. #self.topLevelItem(0).addChild(chumLabel)
  421. #self.topLevelItem(0).sortChildren(0, QtCore.Qt.AscendingOrder)
  422. def getChums(self, handle):
  423. chums = self.findItems(handle, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive)
  424. return chums
  425. def showAllChums(self):
  426. for c in self.chums:
  427. chandle = c.handle
  428. if not len(self.findItems(chandle, QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive)):
  429. chumLabel = chumListing(c, self.mainwindow)
  430. self.addItem(chumLabel)
  431. self.sort()
  432. def hideOfflineChums(self):
  433. for j in range(self.topLevelItemCount()):
  434. i = 0
  435. listing = self.topLevelItem(j).child(i)
  436. while listing is not None:
  437. if listing.chum.mood.name() == "offline":
  438. self.topLevelItem(j).takeChild(i)
  439. else:
  440. i += 1
  441. listing = self.topLevelItem(j).child(i)
  442. self.sort()
  443. def showAllGroups(self, first=False):
  444. if first:
  445. for i,g in enumerate(self.groups):
  446. child_1 = QtGui.QTreeWidgetItem(["%s" % (g)])
  447. self.addTopLevelItem(child_1)
  448. if self.openGroups[i]:
  449. child_1.setExpanded(True)
  450. return
  451. curgroups = []
  452. for i in range(self.topLevelItemCount()):
  453. text = unicode(self.topLevelItem(i).text(0))
  454. if text.rfind(" (") != -1:
  455. text = text[0:text.rfind(" (")]
  456. curgroups.append(text)
  457. for i,g in enumerate(self.groups):
  458. if g not in curgroups:
  459. child_1 = QtGui.QTreeWidgetItem(["%s" % (g)])
  460. j = 0
  461. for h in self.groups:
  462. if h == g:
  463. self.insertTopLevelItem(j, child_1)
  464. break
  465. if h in curgroups:
  466. j += 1
  467. if self.openGroups[i]:
  468. child_1.setExpanded(True)
  469. if self.mainwindow.config.showOnlineNumbers():
  470. self.showOnlineNumbers()
  471. def showOnlineNumbers(self):
  472. if hasattr(self, 'groups'):
  473. self.hideOnlineNumbers()
  474. totals = {'Chums': 0}
  475. online = {'Chums': 0}
  476. for g in self.groups:
  477. totals[unicode(g)] = 0
  478. online[unicode(g)] = 0
  479. for c in self.chums:
  480. yes = c.mood.name() != "offline"
  481. if c.group == "Chums":
  482. totals[unicode(c.group)] = totals[unicode(c.group)]+1
  483. if yes:
  484. online[unicode(c.group)] = online[unicode(c.group)]+1
  485. elif c.group in totals:
  486. totals[unicode(c.group)] = totals[unicode(c.group)]+1
  487. if yes:
  488. online[unicode(c.group)] = online[unicode(c.group)]+1
  489. else:
  490. totals["Chums"] = totals["Chums"]+1
  491. if yes:
  492. online["Chums"] = online["Chums"]+1
  493. for i in range(self.topLevelItemCount()):
  494. text = unicode(self.topLevelItem(i).text(0))
  495. if text.rfind(" (") != -1:
  496. text = text[0:text.rfind(" (")]
  497. if text in online:
  498. self.topLevelItem(i).setText(0, "%s (%i/%i)" % (text, online[text], totals[text]))
  499. def hideOnlineNumbers(self):
  500. for i in range(self.topLevelItemCount()):
  501. text = unicode(self.topLevelItem(i).text(0))
  502. if text.rfind(" (") != -1:
  503. text = text[0:text.rfind(" (")]
  504. self.topLevelItem(i).setText(0, "%s" % (text))
  505. def hideEmptyGroups(self):
  506. i = 0
  507. listing = self.topLevelItem(i)
  508. while listing is not None:
  509. if listing.childCount() == 0:
  510. self.takeTopLevelItem(i)
  511. else:
  512. i += 1
  513. listing = self.topLevelItem(i)
  514. @QtCore.pyqtSlot()
  515. def expandGroup(self):
  516. item = self.currentItem()
  517. text = unicode(item.text(0))
  518. if text.rfind(" (") != -1:
  519. text = text[0:text.rfind(" (")]
  520. if text in self.groups:
  521. expand = item.isExpanded()
  522. self.mainwindow.config.expandGroup(text, not expand)
  523. def addItem(self, chumLabel):
  524. if hasattr(self, 'groups'):
  525. if chumLabel.chum.group not in self.groups:
  526. chumLabel.chum.group = "Chums"
  527. if "Chums" not in self.groups:
  528. self.mainwindow.config.addGroup("Chums")
  529. curgroups = []
  530. for i in range(self.topLevelItemCount()):
  531. text = unicode(self.topLevelItem(i).text(0))
  532. if text.rfind(" (") != -1:
  533. text = text[0:text.rfind(" (")]
  534. curgroups.append(text)
  535. if not self.findItems(chumLabel.handle, QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive):
  536. if chumLabel.chum.group not in curgroups:
  537. child_1 = QtGui.QTreeWidgetItem(["%s" % (chumLabel.chum.group)])
  538. i = 0
  539. for g in self.groups:
  540. if g == chumLabel.chum.group:
  541. self.insertTopLevelItem(i, child_1)
  542. break
  543. if g in curgroups:
  544. i += 1
  545. if self.openGroups[self.groups.index("%s" % (chumLabel.chum.group))]:
  546. child_1.setExpanded(True)
  547. for i in range(self.topLevelItemCount()):
  548. text = unicode(self.topLevelItem(i).text(0))
  549. if text.rfind(" (") != -1:
  550. text = text[0:text.rfind(" (")]
  551. if text == chumLabel.chum.group:
  552. break
  553. # Manual sorting
  554. if self.mainwindow.config.sortMethod() == 2:
  555. chums = self.mainwindow.config.chums()
  556. if chumLabel.chum.handle in chums:
  557. fi = chums.index(chumLabel.chum.handle)
  558. else:
  559. fi = 0
  560. c = 1
  561. # TODO: Rearrange chums list on drag-n-drop
  562. bestj = 0
  563. bestname = ""
  564. if fi > 0:
  565. while not bestj:
  566. for j in xrange(self.topLevelItem(i).childCount()):
  567. if chums[fi-c] == str(self.topLevelItem(i).child(j).text(0)):
  568. bestj = j
  569. bestname = chums[fi-c]
  570. break
  571. c += 1
  572. if fi-c < 0:
  573. break
  574. if bestname:
  575. self.topLevelItem(i).insertChild(bestj+1, chumLabel)
  576. else:
  577. self.topLevelItem(i).insertChild(bestj, chumLabel)
  578. #sys.exit(0)
  579. self.topLevelItem(i).addChild(chumLabel)
  580. else: # All other sorting
  581. self.topLevelItem(i).addChild(chumLabel)
  582. self.sort()
  583. if self.mainwindow.config.showOnlineNumbers():
  584. self.showOnlineNumbers()
  585. else: # usually means this is now the trollslum
  586. if not self.findItems(chumLabel.handle, QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive):
  587. self.topLevelItem(0).addChild(chumLabel)
  588. self.topLevelItem(0).sortChildren(0, QtCore.Qt.AscendingOrder)
  589. def takeItem(self, chumLabel):
  590. r = None
  591. if not hasattr(chumLabel, 'chum'):
  592. return r
  593. for i in range(self.topLevelItemCount()):
  594. for j in range(self.topLevelItem(i).childCount()):
  595. if self.topLevelItem(i).child(j).text(0) == chumLabel.chum.handle:
  596. r = self.topLevelItem(i).takeChild(j)
  597. break
  598. if not self.mainwindow.config.showEmptyGroups():
  599. self.hideEmptyGroups()
  600. if self.mainwindow.config.showOnlineNumbers():
  601. self.showOnlineNumbers()
  602. return r
  603. def updateMood(self, handle, mood):
  604. hideoff = self.mainwindow.config.hideOfflineChums()
  605. chums = self.getChums(handle)
  606. oldmood = None
  607. if hideoff:
  608. if mood.name() != "offline" and \
  609. len(chums) == 0 and \
  610. handle in [p.handle for p in self.chums]:
  611. newLabel = chumListing([p for p in self.chums if p.handle == handle][0], self.mainwindow)
  612. self.addItem(newLabel)
  613. #self.sortItems()
  614. chums = [newLabel]
  615. elif mood.name() == "offline" and \
  616. len(chums) > 0:
  617. for c in chums:
  618. if (hasattr(c, 'mood')):
  619. c.setMood(mood)
  620. #self.takeItem(c)
  621. chums = []
  622. for c in chums:
  623. if (hasattr(c, 'mood')):
  624. oldmood = c.mood
  625. c.setMood(mood)
  626. if self.mainwindow.config.sortMethod() == 1:
  627. for i in range(self.topLevelItemCount()):
  628. saveCurrent = self.currentItem()
  629. self.moodSort(i)
  630. self.setCurrentItem(saveCurrent)
  631. if self.mainwindow.config.showOnlineNumbers():
  632. self.showOnlineNumbers()
  633. return oldmood
  634. def updateColor(self, handle, color):
  635. chums = self.findItems(handle, QtCore.Qt.MatchFlags(0))
  636. for c in chums:
  637. c.setColor(color)
  638. def initTheme(self, theme):
  639. self.resize(*theme["main/chums/size"])
  640. self.move(*theme["main/chums/loc"])
  641. if theme.has_key("main/chums/scrollbar"):
  642. self.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["main/chums/style"], theme["main/chums/scrollbar/style"], 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"] ))
  643. else:
  644. self.setStyleSheet(theme["main/chums/style"])
  645. self.pester.setText(theme["main/menus/rclickchumlist/pester"])
  646. self.removechum.setText(theme["main/menus/rclickchumlist/removechum"])
  647. self.blockchum.setText(theme["main/menus/rclickchumlist/blockchum"])
  648. self.logchum.setText(theme["main/menus/rclickchumlist/viewlog"])
  649. self.reportchum.setText(theme["main/menus/rclickchumlist/report"])
  650. self.notes.setText(theme["main/menus/rclickchumlist/notes"])
  651. self.removegroup.setText(theme["main/menus/rclickchumlist/removegroup"])
  652. self.renamegroup.setText(theme["main/menus/rclickchumlist/renamegroup"])
  653. self.moveMenu.setTitle(theme["main/menus/rclickchumlist/movechum"])
  654. def changeTheme(self, theme):
  655. self.initTheme(theme)
  656. chumlistings = []
  657. for i in range(self.topLevelItemCount()):
  658. for j in range(self.topLevelItem(i).childCount()):
  659. chumlistings.append(self.topLevelItem(i).child(j))
  660. #chumlistings = [self.item(i) for i in range(0, self.count())]
  661. for c in chumlistings:
  662. c.changeTheme(theme)
  663. def count(self):
  664. c = 0
  665. for i in range(self.topLevelItemCount()):
  666. c = c + self.topLevelItem(i).childCount()
  667. return c
  668. def sort(self):
  669. if self.mainwindow.config.sortMethod() == 2:
  670. pass # Do nothing!!!!! :OOOOOOO It's manual, bitches
  671. elif self.mainwindow.config.sortMethod() == 1:
  672. for i in range(self.topLevelItemCount()):
  673. self.moodSort(i)
  674. else:
  675. for i in range(self.topLevelItemCount()):
  676. self.topLevelItem(i).sortChildren(0, QtCore.Qt.AscendingOrder)
  677. def moodSort(self, group):
  678. scrollPos = self.verticalScrollBar().sliderPosition()
  679. chums = []
  680. listing = self.topLevelItem(group).child(0)
  681. while listing is not None:
  682. chums.append(self.topLevelItem(group).takeChild(0))
  683. listing = self.topLevelItem(group).child(0)
  684. chums.sort(key=lambda x: ((999 if x.chum.mood.value() == 2 else x.chum.mood.value()), x.chum.handle), reverse=False)
  685. for c in chums:
  686. self.topLevelItem(group).addChild(c)
  687. self.verticalScrollBar().setSliderPosition(scrollPos)
  688. @QtCore.pyqtSlot()
  689. def activateChum(self):
  690. self.itemActivated.emit(self.currentItem(), 0)
  691. @QtCore.pyqtSlot()
  692. def removeChum(self, handle = None):
  693. if handle:
  694. clistings = self.getChums(handle)
  695. if len(clistings) <= 0: return
  696. for c in clistings:
  697. self.setCurrentItem(c)
  698. if not self.currentItem():
  699. return
  700. currentChum = self.currentItem().chum
  701. self.chums = [c for c in self.chums if c.handle != currentChum.handle]
  702. self.removeChumSignal.emit(self.currentItem().chum.handle)
  703. oldlist = self.takeItem(self.currentItem())
  704. del oldlist
  705. @QtCore.pyqtSlot()
  706. def blockChum(self):
  707. currentChum = self.currentItem()
  708. if not currentChum:
  709. return
  710. self.blockChumSignal.emit(self.currentItem().chum.handle)
  711. @QtCore.pyqtSlot()
  712. def reportChum(self):
  713. currentChum = self.currentItem()
  714. if not currentChum:
  715. return
  716. self.mainwindow.reportChum(self.currentItem().chum.handle)
  717. @QtCore.pyqtSlot()
  718. def findAlts(self):
  719. currentChum = self.currentItem()
  720. if not currentChum:
  721. return
  722. self.mainwindow.sendMessage.emit("ALT %s" % (currentChum.chum.handle) , "calSprite")
  723. @QtCore.pyqtSlot()
  724. def openChumLogs(self):
  725. currentChum = self.currentItem()
  726. if not currentChum:
  727. return
  728. currentChum = currentChum.text(0)
  729. self.pesterlogviewer = PesterLogViewer(currentChum, self.mainwindow.config, self.mainwindow.theme, self.mainwindow)
  730. self.connect(self.pesterlogviewer, QtCore.SIGNAL('rejected()'),
  731. self, QtCore.SLOT('closeActiveLog()'))
  732. self.pesterlogviewer.show()
  733. self.pesterlogviewer.raise_()
  734. self.pesterlogviewer.activateWindow()
  735. @QtCore.pyqtSlot()
  736. def closeActiveLog(self):
  737. self.pesterlogviewer.close()
  738. self.pesterlogviewer = None
  739. @QtCore.pyqtSlot()
  740. def editNotes(self):
  741. currentChum = self.currentItem()
  742. if not currentChum:
  743. return
  744. (notes, ok) = QtGui.QInputDialog.getText(self, "Notes", "Enter your notes...")
  745. if ok:
  746. notes = unicode(notes)
  747. self.mainwindow.chumdb.setNotes(currentChum.handle, notes)
  748. currentChum.setToolTip(0, "%s: %s" % (currentChum.handle, notes))
  749. @QtCore.pyqtSlot()
  750. def renameGroup(self):
  751. if not hasattr(self, 'renamegroupdialog'):
  752. self.renamegroupdialog = None
  753. if not self.renamegroupdialog:
  754. (gname, ok) = QtGui.QInputDialog.getText(self, "Rename Group", "Enter a new name for the group:")
  755. if ok:
  756. gname = unicode(gname)
  757. if re.search("[^A-Za-z0-9_\s]", gname) is not None:
  758. msgbox = QtGui.QMessageBox()
  759. msgbox.setInformativeText("THIS IS NOT A VALID GROUP NAME")
  760. msgbox.setStandardButtons(QtGui.QMessageBox.Ok)
  761. ret = msgbox.exec_()
  762. self.addgroupdialog = None
  763. return
  764. currentGroup = self.currentItem()
  765. if not currentGroup:
  766. return
  767. index = self.indexOfTopLevelItem(currentGroup)
  768. if index != -1:
  769. expanded = currentGroup.isExpanded()
  770. text = unicode(currentGroup.text(0))
  771. if text.rfind(" (") != -1:
  772. text = text[0:text.rfind(" (")]
  773. self.mainwindow.config.delGroup(text)
  774. self.mainwindow.config.addGroup(gname, expanded)
  775. gTemp = self.mainwindow.config.getGroups()
  776. self.groups = [g[0] for g in gTemp]
  777. self.openGroups = [g[1] for g in gTemp]
  778. for i in range(currentGroup.childCount()):
  779. currentGroup.child(i).chum.group = gname
  780. self.mainwindow.chumdb.setGroup(currentGroup.child(i).chum.handle, gname)
  781. currentGroup.setText(0, gname)
  782. if self.mainwindow.config.showOnlineNumbers():
  783. self.showOnlineNumbers()
  784. self.renamegroupdialog = None
  785. @QtCore.pyqtSlot()
  786. def removeGroup(self):
  787. currentGroup = self.currentItem()
  788. if not currentGroup:
  789. return
  790. text = unicode(currentGroup.text(0))
  791. if text.rfind(" (") != -1:
  792. text = text[0:text.rfind(" (")]
  793. self.mainwindow.config.delGroup(text)
  794. gTemp = self.mainwindow.config.getGroups()
  795. self.groups = [g[0] for g in gTemp]
  796. self.openGroups = [g[1] for g in gTemp]
  797. for c in self.chums:
  798. if c.group == text:
  799. c.group = "Chums"
  800. self.mainwindow.chumdb.setGroup(c.handle, "Chums")
  801. for i in range(self.topLevelItemCount()):
  802. if self.topLevelItem(i).text(0) == currentGroup.text(0):
  803. break
  804. while self.topLevelItem(i) and self.topLevelItem(i).child(0):
  805. chumLabel = self.topLevelItem(i).child(0)
  806. self.takeItem(chumLabel)
  807. self.addItem(chumLabel)
  808. self.takeTopLevelItem(i)
  809. @QtCore.pyqtSlot(QtGui.QAction)
  810. def moveToGroup(self, item):
  811. if not item:
  812. return
  813. group = unicode(item.text())
  814. chumLabel = self.currentItem()
  815. if not chumLabel:
  816. return
  817. chumLabel.chum.group = group
  818. self.mainwindow.chumdb.setGroup(chumLabel.chum.handle, group)
  819. self.takeItem(chumLabel)
  820. self.addItem(chumLabel)
  821. removeChumSignal = QtCore.pyqtSignal(QtCore.QString)
  822. blockChumSignal = QtCore.pyqtSignal(QtCore.QString)
  823. class trollSlum(chumArea):
  824. def __init__(self, trolls, mainwindow, parent=None):
  825. QtGui.QListWidget.__init__(self, parent)
  826. self.mainwindow = mainwindow
  827. theme = self.mainwindow.theme
  828. self.setStyleSheet(theme["main/trollslum/chumroll/style"])
  829. self.chums = trolls
  830. child_1 = QtGui.QTreeWidgetItem([""])
  831. self.addTopLevelItem(child_1)
  832. child_1.setExpanded(True)
  833. for c in self.chums:
  834. chandle = c.handle
  835. if not self.findItems(chandle, QtCore.Qt.MatchFlags(0)):
  836. chumLabel = chumListing(c, self.mainwindow)
  837. self.addItem(chumLabel)
  838. self.setSortingEnabled(False)
  839. self.header().hide()
  840. self.setDropIndicatorShown(False)
  841. self.setIndentation(0)
  842. self.optionsMenu = QtGui.QMenu(self)
  843. self.unblockchum = QtGui.QAction(self.mainwindow.theme["main/menus/rclickchumlist/unblockchum"], self)
  844. self.connect(self.unblockchum, QtCore.SIGNAL('triggered()'),
  845. self, QtCore.SIGNAL('unblockChumSignal()'))
  846. self.optionsMenu.addAction(self.unblockchum)
  847. #self.sortItems()
  848. def contextMenuEvent(self, event):
  849. #fuckin Qt
  850. if event.reason() == QtGui.QContextMenuEvent.Mouse:
  851. listing = self.itemAt(event.pos())
  852. self.setCurrentItem(listing)
  853. if self.currentItem().text(0) != "":
  854. self.optionsMenu.popup(event.globalPos())
  855. def changeTheme(self, theme):
  856. self.setStyleSheet(theme["main/trollslum/chumroll/style"])
  857. self.removechum.setText(theme["main/menus/rclickchumlist/removechum"])
  858. self.unblockchum.setText(theme["main/menus/rclickchumlist/blockchum"])
  859. chumlistings = [self.item(i) for i in range(0, self.count())]
  860. for c in chumlistings:
  861. c.changeTheme(theme)
  862. unblockChumSignal = QtCore.pyqtSignal(QtCore.QString)
  863. class TrollSlumWindow(QtGui.QFrame):
  864. def __init__(self, trolls, mainwindow, parent=None):
  865. QtGui.QFrame.__init__(self, parent)
  866. self.mainwindow = mainwindow
  867. theme = self.mainwindow.theme
  868. self.slumlabel = QtGui.QLabel(self)
  869. self.initTheme(theme)
  870. self.trollslum = trollSlum(trolls, self.mainwindow, self)
  871. self.connect(self.trollslum, QtCore.SIGNAL('unblockChumSignal()'),
  872. self, QtCore.SLOT('removeCurrentTroll()'))
  873. layout_1 = QtGui.QHBoxLayout()
  874. self.addButton = QtGui.QPushButton("ADD", self)
  875. self.connect(self.addButton, QtCore.SIGNAL('clicked()'),
  876. self, QtCore.SLOT('addTrollWindow()'))
  877. self.removeButton = QtGui.QPushButton("REMOVE", self)
  878. self.connect(self.removeButton, QtCore.SIGNAL('clicked()'),
  879. self, QtCore.SLOT('removeCurrentTroll()'))
  880. layout_1.addWidget(self.addButton)
  881. layout_1.addWidget(self.removeButton)
  882. layout_0 = QtGui.QVBoxLayout()
  883. layout_0.addWidget(self.slumlabel)
  884. layout_0.addWidget(self.trollslum)
  885. layout_0.addLayout(layout_1)
  886. self.setLayout(layout_0)
  887. def initTheme(self, theme):
  888. self.resize(*theme["main/trollslum/size"])
  889. self.setStyleSheet(theme["main/trollslum/style"])
  890. self.slumlabel.setText(theme["main/trollslum/label/text"])
  891. self.slumlabel.setStyleSheet(theme["main/trollslum/label/style"])
  892. if not self.parent():
  893. self.setWindowTitle(theme["main/menus/profile/block"])
  894. self.setWindowIcon(self.mainwindow.windowIcon())
  895. def changeTheme(self, theme):
  896. self.initTheme(theme)
  897. self.trollslum.changeTheme(theme)
  898. # move unblocked trolls from slum to chumarea
  899. def closeEvent(self, event):
  900. self.mainwindow.closeTrollSlum()
  901. def updateMood(self, handle, mood):
  902. self.trollslum.updateMood(handle, mood)
  903. def addTroll(self, chum):
  904. self.trollslum.addChum(chum)
  905. def removeTroll(self, handle):
  906. self.trollslum.removeChum(handle)
  907. @QtCore.pyqtSlot()
  908. def removeCurrentTroll(self):
  909. currentListing = self.trollslum.currentItem()
  910. if not currentListing or not hasattr(currentListing, 'chum'):
  911. return
  912. self.unblockChumSignal.emit(currentListing.chum.handle)
  913. @QtCore.pyqtSlot()
  914. def addTrollWindow(self):
  915. if not hasattr(self, 'addtrolldialog'):
  916. self.addtrolldialog = None
  917. if self.addtrolldialog:
  918. return
  919. self.addtrolldialog = QtGui.QInputDialog(self)
  920. (handle, ok) = self.addtrolldialog.getText(self, "Add Troll", "Enter Troll Handle:")
  921. if ok:
  922. handle = unicode(handle)
  923. if not (PesterProfile.checkLength(handle) and
  924. PesterProfile.checkValid(handle)[0]):
  925. errormsg = QtGui.QErrorMessage(self)
  926. errormsg.showMessage("THIS IS NOT A VALID CHUMTAG!")
  927. self.addchumdialog = None
  928. return
  929. self.blockChumSignal.emit(handle)
  930. self.addtrolldialog = None
  931. blockChumSignal = QtCore.pyqtSignal(QtCore.QString)
  932. unblockChumSignal = QtCore.pyqtSignal(QtCore.QString)
  933. class PesterWindow(MovingWindow):
  934. def __init__(self, options, parent=None):
  935. MovingWindow.__init__(self, parent,
  936. (QtCore.Qt.CustomizeWindowHint |
  937. QtCore.Qt.FramelessWindowHint))
  938. self.convos = CaseInsensitiveDict()
  939. self.memos = CaseInsensitiveDict()
  940. self.tabconvo = None
  941. self.tabmemo = None
  942. if "advanced" in options:
  943. self.advanced = options["advanced"]
  944. else: self.advanced = False
  945. if "server" in options:
  946. self.serverOverride = options["server"]
  947. if "port" in options:
  948. self.portOverride = options["port"]
  949. if "honk" in options:
  950. self.honk = options["honk"]
  951. else: self.honk = True
  952. self.setAutoFillBackground(True)
  953. self.setObjectName("main")
  954. self.config = userConfig(self)
  955. if self.config.defaultprofile():
  956. self.userprofile = userProfile(self.config.defaultprofile())
  957. self.theme = self.userprofile.getTheme()
  958. else:
  959. self.userprofile = userProfile(PesterProfile("pesterClient%d" % (random.randint(100,999)), QtGui.QColor("black"), Mood(0)))
  960. self.theme = self.userprofile.getTheme()
  961. self.modes = ""
  962. self.randhandler = RandomHandler(self)
  963. try:
  964. themeChecker(self.theme)
  965. except ThemeException, (inst):
  966. print "Caught: "+inst.parameter
  967. themeWarning = QtGui.QMessageBox(self)
  968. themeWarning.setText("Theme Error: %s" % (inst))
  969. themeWarning.exec_()
  970. self.theme = pesterTheme("pesterchum")
  971. extraToasts = {'default': PesterToast}
  972. if pytwmn.confExists():
  973. extraToasts['twmn'] = pytwmn.Notification
  974. self.tm = PesterToastMachine(self, lambda: self.theme["main/windowtitle"], on=self.config.notify(),
  975. type=self.config.notifyType(), extras=extraToasts)
  976. self.tm.run()
  977. self.chatlog = PesterLog(self.profile().handle, self)
  978. self.move(100, 100)
  979. talk = QtGui.QAction(self.theme["main/menus/client/talk"], self)
  980. self.talk = talk
  981. self.connect(talk, QtCore.SIGNAL('triggered()'),
  982. self, QtCore.SLOT('openChat()'))
  983. logv = QtGui.QAction(self.theme["main/menus/client/logviewer"], self)
  984. self.logv = logv
  985. self.connect(logv, QtCore.SIGNAL('triggered()'),
  986. self, QtCore.SLOT('openLogv()'))
  987. grps = QtGui.QAction(self.theme["main/menus/client/addgroup"], self)
  988. self.grps = grps
  989. self.connect(grps, QtCore.SIGNAL('triggered()'),
  990. self, QtCore.SLOT('addGroupWindow()'))
  991. self.rand = QtGui.QAction(self.theme["main/menus/client/randen"], self)
  992. self.connect(self.rand, QtCore.SIGNAL('triggered()'),
  993. self.randhandler, QtCore.SLOT('getEncounter()'))
  994. opts = QtGui.QAction(self.theme["main/menus/client/options"], self)
  995. self.opts = opts
  996. self.connect(opts, QtCore.SIGNAL('triggered()'),
  997. self, QtCore.SLOT('openOpts()'))
  998. exitaction = QtGui.QAction(self.theme["main/menus/client/exit"], self)
  999. self.exitaction = exitaction
  1000. self.connect(exitaction, QtCore.SIGNAL('triggered()'),
  1001. self, QtCore.SLOT('close()'))
  1002. userlistaction = QtGui.QAction(self.theme["main/menus/client/userlist"], self)
  1003. self.userlistaction = userlistaction
  1004. self.connect(userlistaction, QtCore.SIGNAL('triggered()'),
  1005. self, QtCore.SLOT('showAllUsers()'))
  1006. memoaction = QtGui.QAction(self.theme["main/menus/client/memos"], self)
  1007. self.memoaction = memoaction
  1008. self.connect(memoaction, QtCore.SIGNAL('triggered()'),
  1009. self, QtCore.SLOT('showMemos()'))
  1010. self.importaction = QtGui.QAction(self.theme["main/menus/client/import"], self)
  1011. self.connect(self.importaction, QtCore.SIGNAL('triggered()'),
  1012. self, QtCore.SLOT('importExternalConfig()'))
  1013. self.idleaction = QtGui.QAction(self.theme["main/menus/client/idle"], self)
  1014. self.idleaction.setCheckable(True)
  1015. self.connect(self.idleaction, QtCore.SIGNAL('toggled(bool)'),
  1016. self, QtCore.SLOT('toggleIdle(bool)'))
  1017. self.reconnectAction = QtGui.QAction(self.theme["main/menus/client/reconnect"], self)
  1018. self.connect(self.reconnectAction, QtCore.SIGNAL('triggered()'),
  1019. self, QtCore.SIGNAL('reconnectIRC()'))
  1020. self.menu = QtGui.QMenuBar(self)
  1021. self.menu.setNativeMenuBar(False)
  1022. filemenu = self.menu.addMenu(self.theme["main/menus/client/_name"])
  1023. self.filemenu = filemenu
  1024. filemenu.addAction(opts)
  1025. filemenu.addAction(memoaction)
  1026. filemenu.addAction(logv)
  1027. filemenu.addAction(self.rand)
  1028. if not self.randhandler.running:
  1029. self.rand.setEnabled(False)
  1030. filemenu.addAction(userlistaction)
  1031. filemenu.addAction(talk)
  1032. filemenu.addAction(self.idleaction)
  1033. filemenu.addAction(grps)
  1034. filemenu.addAction(self.importaction)
  1035. filemenu.addAction(self.reconnectAction)
  1036. filemenu.addAction(exitaction)
  1037. changequirks = QtGui.QAction(self.theme["main/menus/profile/quirks"], self)
  1038. self.changequirks = changequirks
  1039. self.connect(changequirks, QtCore.SIGNAL('triggered()'),
  1040. self, QtCore.SLOT('openQuirks()'))
  1041. loadslum = QtGui.QAction(self.theme["main/menus/profile/block"], self)
  1042. self.loadslum = loadslum
  1043. self.connect(loadslum, QtCore.SIGNAL('triggered()'),
  1044. self, QtCore.SLOT('showTrollSlum()'))
  1045. changecoloraction = QtGui.QAction(self.theme["main/menus/profile/color"], self)
  1046. self.changecoloraction = changecoloraction
  1047. self.connect(changecoloraction, QtCore.SIGNAL('triggered()'),
  1048. self, QtCore.SLOT('changeMyColor()'))
  1049. switch = QtGui.QAction(self.theme["main/menus/profile/switch"], self)
  1050. self.switch = switch
  1051. self.connect(switch, QtCore.SIGNAL('triggered()'),
  1052. self, QtCore.SLOT('switchProfile()'))
  1053. profilemenu = self.menu.addMenu(self.theme["main/menus/profile/_name"])
  1054. self.profilemenu = profilemenu
  1055. profilemenu.addAction(changequirks)
  1056. profilemenu.addAction(loadslum)
  1057. profilemenu.addAction(changecoloraction)
  1058. profilemenu.addAction(switch)
  1059. self.helpAction = QtGui.QAction(self.theme["main/menus/help/help"], self)
  1060. self.connect(self.helpAction, QtCore.SIGNAL('triggered()'),
  1061. self, QtCore.SLOT('launchHelp()'))
  1062. self.botAction = QtGui.QAction(self.theme["main/menus/help/calsprite"], self)
  1063. self.connect(self.botAction, QtCore.SIGNAL('triggered()'),
  1064. self, QtCore.SLOT('loadCalsprite()'))
  1065. self.nickServAction = QtGui.QAction(self.theme["main/menus/help/nickserv"], self)
  1066. self.connect(self.nickServAction, QtCore.SIGNAL('triggered()'),
  1067. self, QtCore.SLOT('loadNickServ()'))
  1068. self.chanServAction = QtGui.QAction(self.theme["main/menus/help/chanserv"], self)
  1069. self.connect(self.chanServAction, QtCore.SIGNAL('triggered()'),
  1070. self, QtCore.SLOT('loadChanServ()'))
  1071. self.aboutAction = QtGui.QAction(self.theme["main/menus/help/about"], self)
  1072. self.connect(self.aboutAction, QtCore.SIGNAL('triggered()'),
  1073. self, QtCore.SLOT('aboutPesterchum()'))
  1074. self.reportBugAction = QtGui.QAction("REPORT BUG", self)
  1075. self.connect(self.reportBugAction, QtCore.SIGNAL('triggered()'),
  1076. self, QtCore.SLOT('reportBug()'))
  1077. helpmenu = self.menu.addMenu(self.theme["main/menus/help/_name"])
  1078. self.helpmenu = helpmenu
  1079. self.helpmenu.addAction(self.helpAction)
  1080. self.helpmenu.addAction(self.botAction)
  1081. self.helpmenu.addAction(self.chanServAction)
  1082. self.helpmenu.addAction(self.nickServAction)
  1083. self.helpmenu.addAction(self.aboutAction)
  1084. self.helpmenu.addAction(self.reportBugAction)
  1085. self.closeButton = WMButton(PesterIcon(self.theme["main/close/image"]), self)
  1086. self.setButtonAction(self.closeButton, self.config.closeAction(), -1)
  1087. self.miniButton = WMButton(PesterIcon(self.theme["main/minimize/image"]), self)
  1088. self.setButtonAction(self.miniButton, self.config.minimizeAction(), -1)
  1089. self.namesdb = CaseInsensitiveDict()
  1090. self.chumdb = PesterProfileDB()
  1091. chums = [PesterProfile(c, chumdb=self.chumdb) for c in set(self.config.chums())]
  1092. self.chumList = chumArea(chums, self)
  1093. self.connect(self.chumList,
  1094. QtCore.SIGNAL('itemActivated(QTreeWidgetItem *, int)'),
  1095. self,
  1096. QtCore.SLOT('pesterSelectedChum()'))
  1097. self.connect(self.chumList,
  1098. QtCore.SIGNAL('removeChumSignal(QString)'),
  1099. self,
  1100. QtCore.SLOT('removeChum(QString)'))
  1101. self.connect(self.chumList,
  1102. QtCore.SIGNAL('blockChumSignal(QString)'),
  1103. self,
  1104. QtCore.SLOT('blockChum(QString)'))
  1105. self.addChumButton = QtGui.QPushButton(self.theme["main/addchum/text"], self)
  1106. self.connect(self.addChumButton, QtCore.SIGNAL('clicked()'),
  1107. self, QtCore.SLOT('addChumWindow()'))
  1108. self.pesterButton = QtGui.QPushButton(self.theme["main/pester/text"], self)
  1109. self.connect(self.pesterButton, QtCore.SIGNAL('clicked()'),
  1110. self, QtCore.SLOT('pesterSelectedChum()'))
  1111. self.blockButton = QtGui.QPushButton(self.theme["main/block/text"], self)
  1112. self.connect(self.blockButton, QtCore.SIGNAL('clicked()'),
  1113. self, QtCore.SLOT('blockSelectedChum()'))
  1114. self.moodsLabel = QtGui.QLabel(self.theme["main/moodlabel/text"], self)
  1115. self.mychumhandleLabel = QtGui.QLabel(self.theme["main/mychumhandle/label/text"], self)
  1116. self.mychumhandle = QtGui.QPushButton(self.profile().handle, self)
  1117. self.mychumhandle.setFlat(True)
  1118. self.connect(self.mychumhandle, QtCore.SIGNAL('clicked()'),
  1119. self, QtCore.SLOT('switchProfile()'))
  1120. self.mychumcolor = QtGui.QPushButton(self)
  1121. self.connect(self.mychumcolor, QtCore.SIGNAL('clicked()'),
  1122. self, QtCore.SLOT('changeMyColor()'))
  1123. self.initTheme(self.theme)
  1124. self.waitingMessages = waitingMessageHolder(self)
  1125. self.autoidle = False
  1126. self.idlethreshold = 60*self.config.idleTime()
  1127. self.idletimer = QtCore.QTimer(self)
  1128. self.idleposition = QtGui.QCursor.pos()
  1129. self.idletime = 0
  1130. self.connect(self.idletimer, QtCore.SIGNAL('timeout()'),
  1131. self, QtCore.SLOT('checkIdle()'))
  1132. self.idletimer.start(1000)
  1133. if not self.config.defaultprofile():
  1134. self.changeProfile()
  1135. # Fuck you some more OSX leopard! >:(
  1136. if not ostools.isOSXLeopard():
  1137. QtCore.QTimer.singleShot(1000, self, QtCore.SLOT('mspacheck()'))
  1138. self.connect(self, QtCore.SIGNAL('pcUpdate(QString, QString)'),
  1139. self, QtCore.SLOT('updateMsg(QString, QString)'))
  1140. self.pingtimer = QtCore.QTimer()
  1141. self.connect(self.pingtimer, QtCore.SIGNAL('timeout()'),
  1142. self, QtCore.SLOT('checkPing()'))
  1143. self.lastping = int(time())
  1144. self.pingtimer.start(1000*90)
  1145. @QtCore.pyqtSlot()
  1146. def mspacheck(self):
  1147. # Fuck you EVEN more OSX leopard! >:((((
  1148. if not ostools.isOSXLeopard():
  1149. checker = MSPAChecker(self)
  1150. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
  1151. def updateMsg(self, ver, url):
  1152. if not hasattr(self, 'updatemenu'):
  1153. self.updatemenu = None
  1154. if not self.updatemenu:
  1155. self.updatemenu = UpdatePesterchum(ver, url, self)
  1156. self.connect(self.updatemenu, QtCore.SIGNAL('accepted()'),
  1157. self, QtCore.SLOT('updatePC()'))
  1158. self.connect(self.updatemenu, QtCore.SIGNAL('rejected()'),
  1159. self, QtCore.SLOT('noUpdatePC()'))
  1160. self.updatemenu.show()
  1161. self.updatemenu.raise_()
  1162. self.updatemenu.activateWindow()
  1163. @QtCore.pyqtSlot()
  1164. def updatePC(self):
  1165. version.updateDownload(unicode(self.updatemenu.url))
  1166. self.updatemenu = None
  1167. @QtCore.pyqtSlot()
  1168. def noUpdatePC(self):
  1169. self.updatemenu = None
  1170. @QtCore.pyqtSlot()
  1171. def checkPing(self):
  1172. curtime = int(time())
  1173. if curtime - self.lastping > 600:
  1174. self.pingServer.emit()
  1175. def profile(self):
  1176. return self.userprofile.chat
  1177. def closeConversations(self, switch=False):
  1178. if not hasattr(self, 'tabconvo'):
  1179. self.tabconvo = None
  1180. if self.tabconvo:
  1181. self.tabconvo.close()
  1182. else:
  1183. for c in self.convos.values():
  1184. c.close()
  1185. if self.tabmemo:
  1186. if not switch:
  1187. self.tabmemo.close()
  1188. else:
  1189. for m in self.tabmemo.convos:
  1190. self.tabmemo.convos[m].sendtime()
  1191. else:
  1192. for m in self.memos.values():
  1193. if not switch:
  1194. m.close()
  1195. else:
  1196. m.sendtime()
  1197. def paintEvent(self, event):
  1198. palette = QtGui.QPalette()
  1199. palette.setBrush(QtGui.QPalette.Window, QtGui.QBrush(self.backgroundImage))
  1200. self.setPalette(palette)
  1201. @QtCore.pyqtSlot()
  1202. def closeToTray(self):
  1203. self.hide()
  1204. self.closeToTraySignal.emit()
  1205. def closeEvent(self, event):
  1206. self.closeConversations()
  1207. if hasattr(self, 'trollslum') and self.trollslum:
  1208. self.trollslum.close()
  1209. self.closeSignal.emit()
  1210. event.accept()
  1211. def newMessage(self, handle, msg):
  1212. if handle in self.config.getBlocklist():
  1213. #yeah suck on this
  1214. self.sendMessage.emit("PESTERCHUM:BLOCKED", handle)
  1215. return
  1216. # notify
  1217. if self.config.notifyOptions() & self.config.NEWMSG:
  1218. if not self.convos.has_key(handle):
  1219. t = self.tm.Toast("New Conversation", "From: %s" % handle)
  1220. t.show()
  1221. elif not self.config.notifyOptions() & self.config.NEWCONVO:
  1222. if msg[:11] != "PESTERCHUM:":
  1223. t = self.tm.Toast("From: %s" % handle, re.sub("</?c(=.*?)?>", "", msg))
  1224. t.show()
  1225. else:
  1226. if msg == "PESTERCHUM:CEASE":
  1227. t = self.tm.Toast("Closed Conversation", handle)
  1228. t.show()
  1229. elif msg == "PESTERCHUM:BLOCK":
  1230. t = self.tm.Toast("Blocked", handle)
  1231. t.show()
  1232. elif msg == "PESTERCHUM:UNBLOCK":
  1233. t = self.tm.Toast("Unblocked", handle)
  1234. t.show()
  1235. if not self.convos.has_key(handle):
  1236. if msg == "PESTERCHUM:CEASE": # ignore cease after we hang up
  1237. return
  1238. matchingChums = [c for c in self.chumList.chums if c.handle == handle]
  1239. if len(matchingChums) > 0:
  1240. mood = matchingChums[0].mood
  1241. else:
  1242. mood = Mood(0)
  1243. chum = PesterProfile(handle, mood=mood, chumdb=self.chumdb)
  1244. self.newConversation(chum, False)
  1245. if len(matchingChums) == 0:
  1246. self.moodRequest.emit(chum)
  1247. convo = self.convos[handle]
  1248. convo.addMessage(msg, False)
  1249. # play sound here
  1250. if self.config.soundOn():
  1251. if self.config.chatSound():
  1252. if msg in ["PESTERCHUM:CEASE", "PESTERCHUM:BLOCK"]:
  1253. self.ceasesound.play()
  1254. else:
  1255. self.alarm.play()
  1256. def newMemoMsg(self, chan, handle, msg):
  1257. if not self.memos.has_key(chan):
  1258. # silently ignore in case we forgot to /part
  1259. return
  1260. memo = self.memos[chan]
  1261. msg = unicode(msg)
  1262. if not memo.times.has_key(handle):
  1263. # new chum! time current
  1264. newtime = timedelta(0)
  1265. time = TimeTracker(newtime)
  1266. memo.times[handle] = time
  1267. if msg[0:3] != "/me" and msg[0:13] != "PESTERCHUM:ME":
  1268. msg = addTimeInitial(msg, memo.times[handle].getGrammar())
  1269. if handle == "ChanServ":
  1270. systemColor = QtGui.QColor(self.theme["memos/systemMsgColor"])
  1271. msg = "<c=%s>%s</c>" % (systemColor.name(), msg)
  1272. memo.addMessage(msg, handle)
  1273. if self.config.soundOn():
  1274. if self.config.memoSound():
  1275. if self.config.nameSound():
  1276. m = convertTags(msg, "text")
  1277. if m.find(":") <= 3:
  1278. m = m[m.find(":"):]
  1279. for search in self.userprofile.getMentions():
  1280. if re.search(search, m):
  1281. if self.config.notifyOptions() & self.config.INITIALS:
  1282. t = self.tm.Toast(chan, re.sub("</?c(=.*?)?>", "", msg))
  1283. t.show()
  1284. self.namesound.play()
  1285. return
  1286. if self.honk and re.search(r"\bhonk\b", convertTags(msg, "text"), re.I):
  1287. self.honksound.play()
  1288. elif self.config.memoPing():
  1289. self.memosound.play()
  1290. def changeColor(self, handle, color):
  1291. # pesterconvo and chumlist
  1292. self.chumList.updateColor(handle, color)
  1293. if self.convos.has_key(handle):
  1294. self.convos[handle].updateColor(color)
  1295. self.chumdb.setColor(handle, color)
  1296. def updateMood(self, handle, mood):
  1297. # updates OTHER chums' moods
  1298. oldmood = self.chumList.updateMood(handle, mood)
  1299. if self.convos.has_key(handle):
  1300. self.convos[handle].updateMood(mood, old=oldmood)
  1301. if hasattr(self, 'trollslum') and self.trollslum:
  1302. self.trollslum.updateMood(handle, mood)
  1303. def newConversation(self, chum, initiated=True):
  1304. if type(chum) in [str, unicode]:
  1305. matchingChums = [c for c in self.chumList.chums if c.handle == chum]
  1306. if len(matchingChums) > 0:
  1307. mood = matchingChums[0].mood
  1308. else:
  1309. mood = Mood(2)
  1310. chum = PesterProfile(chum, mood=mood, chumdb=self.chumdb)
  1311. if len(matchingChums) == 0:
  1312. self.moodRequest.emit(chum)
  1313. if self.convos.has_key(chum.handle):
  1314. self.convos[chum.handle].showChat()
  1315. return
  1316. if self.config.tabs():
  1317. if not self.tabconvo:
  1318. self.createTabWindow()
  1319. convoWindow = PesterConvo(chum, initiated, self, self.tabconvo)
  1320. self.tabconvo.show()
  1321. else:
  1322. convoWindow = PesterConvo(chum, initiated, self)
  1323. self.connect(convoWindow, QtCore.SIGNAL('messageSent(QString, QString)'),
  1324. self, QtCore.SIGNAL('sendMessage(QString, QString)'))
  1325. self.connect(convoWindow, QtCore.SIGNAL('windowClosed(QString)'),
  1326. self, QtCore.SLOT('closeConvo(QString)'))
  1327. self.convos[chum.handle] = convoWindow
  1328. if unicode(chum.handle).upper() == "CHANSERV" or \
  1329. unicode(chum.handle).upper() == "NICKSERV" or \
  1330. unicode(chum.handle).upper() == "MEMOSERV" or \
  1331. unicode(chum.handle).upper() == "OPERSERV" or \
  1332. unicode(chum.handle).upper() == "HELPSERV":
  1333. convoWindow.toggleQuirks(True)
  1334. convoWindow.quirksOff.setChecked(True)
  1335. else:
  1336. if unicode(chum.handle).upper() == "CALSPRITE" or \
  1337. unicode(chum.handle).upper() == "RANDOMENCOUNTER":
  1338. convoWindow.toggleQuirks(True)
  1339. convoWindow.quirksOff.setChecked(True)
  1340. self.newConvoStarted.emit(QtCore.QString(chum.handle), initiated)
  1341. convoWindow.show()
  1342. def createTabWindow(self):
  1343. self.tabconvo = PesterTabWindow(self)
  1344. self.connect(self.tabconvo, QtCore.SIGNAL('windowClosed()'),
  1345. self, QtCore.SLOT('tabsClosed()'))
  1346. def createMemoTabWindow(self):
  1347. self.tabmemo = MemoTabWindow(self)
  1348. self.connect(self.tabmemo, QtCore.SIGNAL('windowClosed()'),
  1349. self, QtCore.SLOT('memoTabsClosed()'))
  1350. def newMemo(self, channel, timestr, secret=False, invite=False):
  1351. if channel == "#pesterchum":
  1352. return
  1353. if self.memos.has_key(channel):
  1354. self.memos[channel].showChat()
  1355. return
  1356. # do slider dialog then set
  1357. if self.config.tabMemos():
  1358. if not self.tabmemo:
  1359. self.createMemoTabWindow()
  1360. memoWindow = PesterMemo(channel, timestr, self, self.tabmemo)
  1361. self.tabmemo.show()
  1362. else:
  1363. memoWindow = PesterMemo(channel, timestr, self, None)
  1364. # connect signals
  1365. self.connect(self, QtCore.SIGNAL('inviteOnlyChan(QString)'),
  1366. memoWindow, QtCore.SLOT('closeInviteOnly(QString)'))
  1367. self.connect(memoWindow, QtCore.SIGNAL('messageSent(QString, QString)'),
  1368. self, QtCore.SIGNAL('sendMessage(QString, QString)'))
  1369. self.connect(memoWindow, QtCore.SIGNAL('windowClosed(QString)'),
  1370. self, QtCore.SLOT('closeMemo(QString)'))
  1371. self.connect(self, QtCore.SIGNAL('namesUpdated(QString)'),
  1372. memoWindow, QtCore.SLOT('namesUpdated(QString)'))
  1373. self.connect(self, QtCore.SIGNAL('modesUpdated(QString, QString)'),
  1374. memoWindow, QtCore.SLOT('modesUpdated(QString, QString)'))
  1375. self.connect(self,
  1376. QtCore.SIGNAL('userPresentSignal(QString, QString, QString)'),
  1377. memoWindow, QtCore.SLOT('userPresentChange(QString, QString, QString)'))
  1378. # chat client send memo open
  1379. self.memos[channel] = memoWindow
  1380. self.joinChannel.emit(channel) # race condition?
  1381. self.secret = secret
  1382. if self.secret:
  1383. self.secret = True
  1384. self.setChannelMode.emit(channel, "+s", "")
  1385. if invite:
  1386. self.setChannelMode.emit(channel, "+i", "")
  1387. memoWindow.sendTimeInfo()
  1388. memoWindow.show()
  1389. def addChum(self, chum):
  1390. self.chumList.addChum(chum)
  1391. self.config.addChum(chum)
  1392. self.moodRequest.emit(chum)
  1393. def addGroup(self, gname):
  1394. self.config.addGroup(gname)
  1395. gTemp = self.config.getGroups()
  1396. self.chumList.groups = [g[0] for g in gTemp]
  1397. self.chumList.openGroups = [g[1] for g in gTemp]
  1398. self.chumList.moveGroupMenu()
  1399. self.chumList.showAllGroups()
  1400. if not self.config.showEmptyGroups():
  1401. self.chumList.hideEmptyGroups()
  1402. if self.config.showOnlineNumbers():
  1403. self.chumList.showOnlineNumbers()
  1404. def changeProfile(self, collision=None):
  1405. if not hasattr(self, 'chooseprofile'):
  1406. self.chooseprofile = None
  1407. if not self.chooseprofile:
  1408. self.chooseprofile = PesterChooseProfile(self.userprofile, self.config, self.theme, self, collision=collision)
  1409. self.chooseprofile.exec_()
  1410. def themePicker(self):
  1411. if not hasattr(self, 'choosetheme'):
  1412. self.choosetheme = None
  1413. if not self.choosetheme:
  1414. self.choosetheme = PesterChooseTheme(self.config, self.theme, self)
  1415. self.choosetheme.exec_()
  1416. def initTheme(self, theme):
  1417. self.resize(*theme["main/size"])
  1418. self.setWindowIcon(PesterIcon(theme["main/icon"]))
  1419. self.setWindowTitle(theme["main/windowtitle"])
  1420. self.setStyleSheet("QFrame#main { %s }" % (theme["main/style"]))
  1421. self.backgroundImage = QtGui.QPixmap(theme["main/background-image"])
  1422. self.backgroundMask = self.backgroundImage.mask()
  1423. self.setMask(self.backgroundMask)
  1424. self.menu.setStyleSheet("QMenuBar { background: transparent; %s } QMenuBar::item { background: transparent; %s } " % (theme["main/menubar/style"], theme["main/menu/menuitem"]) + "QMenu { background: transparent; %s } QMenu::item::selected { %s } QMenu::item::disabled { %s }" % (theme["main/menu/style"], theme["main/menu/selected"], theme["main/menu/disabled"]))
  1425. newcloseicon = PesterIcon(theme["main/close/image"])
  1426. self.closeButton.setIcon(newcloseicon)
  1427. self.closeButton.setIconSize(newcloseicon.realsize())
  1428. self.closeButton.resize(newcloseicon.realsize())
  1429. self.closeButton.move(*theme["main/close/loc"])
  1430. newminiicon = PesterIcon(theme["main/minimize/image"])
  1431. self.miniButton.setIcon(newminiicon)
  1432. self.miniButton.setIconSize(newminiicon.realsize())
  1433. self.miniButton.resize(newminiicon.realsize())
  1434. self.miniButton.move(*theme["main/minimize/loc"])
  1435. # menus
  1436. self.menu.move(*theme["main/menu/loc"])
  1437. self.talk.setText(theme["main/menus/client/talk"])
  1438. self.logv.setText(theme["main/menus/client/logviewer"])
  1439. self.grps.setText(theme["main/menus/client/addgroup"])
  1440. self.rand.setText(self.theme["main/menus/client/randen"])
  1441. self.opts.setText(theme["main/menus/client/options"])
  1442. self.exitaction.setText(theme["main/menus/client/exit"])
  1443. self.userlistaction.setText(theme["main/menus/client/userlist"])
  1444. self.memoaction.setText(theme["main/menus/client/memos"])
  1445. self.importaction.setText(theme["main/menus/client/import"])
  1446. self.idleaction.setText(theme["main/menus/client/idle"])
  1447. self.reconnectAction.setText(theme["main/menus/client/reconnect"])
  1448. self.filemenu.setTitle(theme["main/menus/client/_name"])
  1449. self.changequirks.setText(theme["main/menus/profile/quirks"])
  1450. self.loadslum.setText(theme["main/menus/profile/block"])
  1451. self.changecoloraction.setText(theme["main/menus/profile/color"])
  1452. self.switch.setText(theme["main/menus/profile/switch"])
  1453. self.profilemenu.setTitle(theme["main/menus/profile/_name"])
  1454. self.aboutAction.setText(self.theme["main/menus/help/about"])
  1455. self.helpAction.setText(self.theme["main/menus/help/help"])
  1456. self.botAction.setText(self.theme["main/menus/help/calsprite"])
  1457. self.chanServAction.setText(self.theme["main/menus/help/chanserv"])
  1458. self.nickServAction.setText(self.theme["main/menus/help/nickserv"])
  1459. self.helpmenu.setTitle(self.theme["main/menus/help/_name"])
  1460. # moods
  1461. self.moodsLabel.setText(theme["main/moodlabel/text"])
  1462. self.moodsLabel.move(*theme["main/moodlabel/loc"])
  1463. self.moodsLabel.setStyleSheet(theme["main/moodlabel/style"])
  1464. if hasattr(self, 'moods'):
  1465. self.moods.removeButtons()
  1466. mood_list = theme["main/moods"]
  1467. mood_list = [dict([(str(k),v) for (k,v) in d.iteritems()])
  1468. for d in mood_list]
  1469. self.moods = PesterMoodHandler(self, *[PesterMoodButton(self, **d) for d in mood_list])
  1470. self.moods.showButtons()
  1471. # chum
  1472. addChumStyle = "QPushButton { %s }" % (theme["main/addchum/style"])
  1473. if theme.has_key("main/addchum/pressed"):
  1474. addChumStyle += "QPushButton:pressed { %s }" % (theme["main/addchum/pressed"])
  1475. pesterButtonStyle = "QPushButton { %s }" % (theme["main/pester/style"])
  1476. if theme.has_key("main/pester/pressed"):
  1477. pesterButtonStyle += "QPushButton:pressed { %s }" % (theme["main/pester/pressed"])
  1478. blockButtonStyle = "QPushButton { %s }" % (theme["main/block/style"])
  1479. if theme.has_key("main/block/pressed"):
  1480. pesterButtonStyle += "QPushButton:pressed { %s }" % (theme["main/block/pressed"])
  1481. self.addChumButton.setText(theme["main/addchum/text"])
  1482. self.addChumButton.resize(*theme["main/addchum/size"])
  1483. self.addChumButton.move(*theme["main/addchum/loc"])
  1484. self.addChumButton.setStyleSheet(addChumStyle)
  1485. self.pesterButton.setText(theme["main/pester/text"])
  1486. self.pesterButton.resize(*theme["main/pester/size"])
  1487. self.pesterButton.move(*theme["main/pester/loc"])
  1488. self.pesterButton.setStyleSheet(pesterButtonStyle)
  1489. self.blockButton.setText(theme["main/block/text"])
  1490. self.blockButton.resize(*theme["main/block/size"])
  1491. self.blockButton.move(*theme["main/block/loc"])
  1492. self.blockButton.setStyleSheet(blockButtonStyle)
  1493. # buttons
  1494. self.mychumhandleLabel.setText(theme["main/mychumhandle/label/text"])
  1495. self.mychumhandleLabel.move(*theme["main/mychumhandle/label/loc"])
  1496. self.mychumhandleLabel.setStyleSheet(theme["main/mychumhandle/label/style"])
  1497. self.mychumhandle.setText(self.profile().handle)
  1498. self.mychumhandle.move(*theme["main/mychumhandle/handle/loc"])
  1499. self.mychumhandle.resize(*theme["main/mychumhandle/handle/size"])
  1500. self.mychumhandle.setStyleSheet(theme["main/mychumhandle/handle/style"])
  1501. self.mychumcolor.resize(*theme["main/mychumhandle/colorswatch/size"])
  1502. self.mychumcolor.move(*theme["main/mychumhandle/colorswatch/loc"])
  1503. self.mychumcolor.setStyleSheet("background: %s" % (self.profile().colorhtml()))
  1504. if self.theme.has_key("main/mychumhandle/currentMood"):
  1505. moodicon = self.profile().mood.icon(theme)
  1506. if hasattr(self, 'currentMoodIcon') and self.currentMoodIcon:
  1507. self.currentMoodIcon.hide()
  1508. self.currentMoodIcon = None
  1509. self.currentMoodIcon = QtGui.QLabel(self)
  1510. self.currentMoodIcon.setPixmap(moodicon.pixmap(moodicon.realsize()))
  1511. self.currentMoodIcon.move(*theme["main/mychumhandle/currentMood"])
  1512. self.currentMoodIcon.show()
  1513. else:
  1514. if hasattr(self, 'currentMoodIcon') and self.currentMoodIcon:
  1515. self.currentMoodIcon.hide()
  1516. self.currentMoodIcon = None
  1517. if theme["main/mychumhandle/colorswatch/text"]:
  1518. self.mychumcolor.setText(theme["main/mychumhandle/colorswatch/text"])
  1519. else:
  1520. self.mychumcolor.setText("")
  1521. # sounds
  1522. if not pygame or not pygame.mixer:
  1523. self.alarm = NoneSound()
  1524. self.memosound = NoneSound()
  1525. self.namesound = NoneSound()
  1526. self.ceasesound = NoneSound()
  1527. self.honksound = NoneSound()
  1528. else:
  1529. try:
  1530. self.alarm = pygame.mixer.Sound(theme["main/sounds/alertsound"])
  1531. self.memosound = pygame.mixer.Sound(theme["main/sounds/memosound"])
  1532. self.namesound = pygame.mixer.Sound("themes/namealarm.wav")
  1533. self.ceasesound = pygame.mixer.Sound(theme["main/sounds/ceasesound"])
  1534. self.honksound = pygame.mixer.Sound("themes/honk.wav")
  1535. except Exception, e:
  1536. self.alarm = NoneSound()
  1537. self.memosound = NoneSound()
  1538. self.namesound = NoneSound()
  1539. self.ceasesound = NoneSound()
  1540. self.honksound = NoneSound()
  1541. self.setVolume(self.config.volume())
  1542. def setVolume(self, vol):
  1543. vol = vol/100.0
  1544. self.alarm.set_volume(vol)
  1545. self.memosound.set_volume(vol)
  1546. self.namesound.set_volume(vol)
  1547. self.ceasesound.set_volume(vol)
  1548. self.honksound.set_volume(vol)
  1549. def changeTheme(self, theme):
  1550. # check theme
  1551. try:
  1552. themeChecker(theme)
  1553. except ThemeException, (inst):
  1554. themeWarning = QtGui.QMessageBox(self)
  1555. themeWarning.setText("Theme Error: %s" % (inst))
  1556. themeWarning.exec_()
  1557. theme = pesterTheme("pesterchum")
  1558. return
  1559. self.theme = theme
  1560. # do self
  1561. self.initTheme(theme)
  1562. # set mood
  1563. self.moods.updateMood(theme['main/defaultmood'])
  1564. # chum area
  1565. self.chumList.changeTheme(theme)
  1566. # do open windows
  1567. if self.tabconvo:
  1568. self.tabconvo.changeTheme(theme)
  1569. if self.tabmemo:
  1570. self.tabmemo.changeTheme(theme)
  1571. for c in self.convos.values():
  1572. c.changeTheme(theme)
  1573. for m in self.memos.values():
  1574. m.changeTheme(theme)
  1575. if hasattr(self, 'trollslum') and self.trollslum:
  1576. self.trollslum.changeTheme(theme)
  1577. if hasattr(self, 'allusers') and self.allusers:
  1578. self.allusers.changeTheme(theme)
  1579. if self.config.ghostchum():
  1580. self.theme["main"]["icon"] = "themes/pesterchum/pesterdunk.png"
  1581. self.theme["main"]["newmsgicon"] = "themes/pesterchum/ghostchum.png"
  1582. self.setWindowIcon(PesterIcon(self.theme["main/icon"]))
  1583. # system tray icon
  1584. self.updateSystemTray()
  1585. def updateSystemTray(self):
  1586. if len(self.waitingMessages) == 0:
  1587. self.trayIconSignal.emit(0)
  1588. else:
  1589. self.trayIconSignal.emit(1)
  1590. def systemTrayFunction(self):
  1591. if len(self.waitingMessages) == 0:
  1592. if self.isMinimized():
  1593. self.showNormal()
  1594. elif self.isHidden():
  1595. self.show()
  1596. else:
  1597. if self.isActiveWindow():
  1598. self.closeToTray()
  1599. else:
  1600. self.raise_()
  1601. self.activateWindow()
  1602. else:
  1603. self.waitingMessages.answerMessage()
  1604. @QtCore.pyqtSlot()
  1605. def connected(self):
  1606. if self.loadingscreen:
  1607. self.loadingscreen.done(QtGui.QDialog.Accepted)
  1608. self.loadingscreen = None
  1609. @QtCore.pyqtSlot()
  1610. def blockSelectedChum(self):
  1611. curChumListing = self.chumList.currentItem()
  1612. if curChumListing:
  1613. curChum = curChumListing.chum
  1614. self.blockChum(curChum.handle)
  1615. @QtCore.pyqtSlot()
  1616. def pesterSelectedChum(self):
  1617. curChum = self.chumList.currentItem()
  1618. if curChum:
  1619. text = unicode(curChum.text(0))
  1620. if text.rfind(" (") != -1:
  1621. text = text[0:text.rfind(" (")]
  1622. if text not in self.chumList.groups and \
  1623. text != "Chums":
  1624. self.newConversationWindow(curChum)
  1625. @QtCore.pyqtSlot(QtGui.QListWidgetItem)
  1626. def newConversationWindow(self, chumlisting):
  1627. # check chumdb
  1628. chum = chumlisting.chum
  1629. color = self.chumdb.getColor(chum)
  1630. if color:
  1631. chum.color = color
  1632. self.newConversation(chum)
  1633. @QtCore.pyqtSlot(QtCore.QString)
  1634. def closeConvo(self, handle):
  1635. h = unicode(handle)
  1636. try:
  1637. chum = self.convos[h].chum
  1638. except KeyError:
  1639. chum = self.convos[h.lower()].chum
  1640. try:
  1641. chumopen = self.convos[h].chumopen
  1642. except KeyError:
  1643. chumopen = self.convos[h.lower()].chumopen
  1644. if chumopen:
  1645. self.chatlog.log(chum.handle, self.profile().pestermsg(chum, QtGui.QColor(self.theme["convo/systemMsgColor"]), self.theme["convo/text/ceasepester"]))
  1646. self.convoClosed.emit(handle)
  1647. self.chatlog.finish(h)
  1648. del self.convos[h]
  1649. @QtCore.pyqtSlot(QtCore.QString)
  1650. def closeMemo(self, channel):
  1651. c = unicode(channel)
  1652. self.chatlog.finish(c)
  1653. self.leftChannel.emit(channel)
  1654. try:
  1655. del self.memos[c]
  1656. except KeyError:
  1657. del self.memos[c.lower()]
  1658. @QtCore.pyqtSlot()
  1659. def tabsClosed(self):
  1660. del self.tabconvo
  1661. self.tabconvo = None
  1662. @QtCore.pyqtSlot()
  1663. def memoTabsClosed(self):
  1664. del self.tabmemo
  1665. self.tabmemo = None
  1666. @QtCore.pyqtSlot(QtCore.QString, Mood)
  1667. def updateMoodSlot(self, handle, mood):
  1668. h = unicode(handle)
  1669. self.updateMood(h, mood)
  1670. @QtCore.pyqtSlot(QtCore.QString, QtGui.QColor)
  1671. def updateColorSlot(self, handle, color):
  1672. h = unicode(handle)
  1673. self.changeColor(h, color)
  1674. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
  1675. def deliverMessage(self, handle, msg):
  1676. h = unicode(handle)
  1677. m = unicode(msg)
  1678. self.newMessage(h, m)
  1679. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
  1680. def deliverMemo(self, chan, handle, msg):
  1681. (c, h, m) = (unicode(chan), unicode(handle), unicode(msg))
  1682. self.newMemoMsg(c,h,m)
  1683. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
  1684. def deliverNotice(self, handle, msg):
  1685. h = unicode(handle)
  1686. m = unicode(msg)
  1687. if m.startswith("Your nickname is now being changed to"):
  1688. changedto = m[39:-1]
  1689. msgbox = QtGui.QMessageBox()
  1690. msgbox.setText("This chumhandle has been registered; you may not use it.")
  1691. msgbox.setInformativeText("Your handle is now being changed to %s." % (changedto))
  1692. msgbox.setStandardButtons(QtGui.QMessageBox.Ok)
  1693. ret = msgbox.exec_()
  1694. elif h == self.randhandler.randNick:
  1695. self.randhandler.incoming(msg)
  1696. elif self.convos.has_key(h):
  1697. self.newMessage(h, m)
  1698. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
  1699. def deliverInvite(self, handle, channel):
  1700. msgbox = QtGui.QMessageBox()
  1701. msgbox.setText("You're invited!")
  1702. msgbox.setInformativeText("%s has invited you to the memo: %s\nWould you like to join them?" % (handle, channel))
  1703. msgbox.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
  1704. ret = msgbox.exec_()
  1705. if ret == QtGui.QMessageBox.Ok:
  1706. self.newMemo(unicode(channel), "+0:00")
  1707. @QtCore.pyqtSlot(QtCore.QString)
  1708. def chanInviteOnly(self, channel):
  1709. self.inviteOnlyChan.emit(channel)
  1710. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
  1711. def cannotSendToChan(self, channel, msg):
  1712. self.deliverMemo(channel, "ChanServ", msg)
  1713. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
  1714. def modesUpdated(self, channel, modes):
  1715. self.modesUpdated.emit(channel, modes)
  1716. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
  1717. def timeCommand(self, chan, handle, command):
  1718. (c, h, cmd) = (unicode(chan), unicode(handle), unicode(command))
  1719. if self.memos[c]:
  1720. self.memos[c].timeUpdate(h, cmd)
  1721. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
  1722. def quirkDisable(self, channel, msg, op):
  1723. (c, msg, op) = (unicode(channel), unicode(msg), unicode(op))
  1724. if not self.memos.has_key(c):
  1725. return
  1726. memo = self.memos[c]
  1727. memo.quirkDisable(op, msg)
  1728. @QtCore.pyqtSlot(QtCore.QString, PesterList)
  1729. def updateNames(self, channel, names):
  1730. c = unicode(channel)
  1731. # update name DB
  1732. self.namesdb[c] = names
  1733. # warn interested party of names
  1734. self.namesUpdated.emit(c)
  1735. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString, QtCore.QString)
  1736. def userPresentUpdate(self, handle, channel, update):
  1737. c = unicode(channel)
  1738. n = unicode(handle)
  1739. if update == "nick":
  1740. l = n.split(":")
  1741. oldnick = l[0]
  1742. newnick = l[1]
  1743. if update in ("quit", "netsplit"):
  1744. for c in self.namesdb.keys():
  1745. try:
  1746. i = self.namesdb[c].index(n)
  1747. self.namesdb[c].pop(i)
  1748. except ValueError:
  1749. pass
  1750. except KeyError:
  1751. self.namesdb[c] = []
  1752. elif update == "left":
  1753. try:
  1754. i = self.namesdb[c].index(n)
  1755. self.namesdb[c].pop(i)
  1756. except ValueError:
  1757. pass
  1758. except KeyError:
  1759. self.namesdb[c] = []
  1760. elif update == "nick":
  1761. for c in self.namesdb.keys():
  1762. try:
  1763. i = self.namesdb[c].index(oldnick)
  1764. self.namesdb[c].pop(i)
  1765. self.namesdb[c].append(newnick)
  1766. except ValueError:
  1767. pass
  1768. except KeyError:
  1769. pass
  1770. elif update == "join":
  1771. try:
  1772. i = self.namesdb[c].index(n)
  1773. except ValueError:
  1774. self.namesdb[c].append(n)
  1775. except KeyError:
  1776. self.namesdb[c] = [n]
  1777. self.userPresentSignal.emit(handle, channel, update)
  1778. @QtCore.pyqtSlot()
  1779. def addChumWindow(self):
  1780. if not hasattr(self, 'addchumdialog'):
  1781. self.addchumdialog = None
  1782. if not self.addchumdialog:
  1783. available_groups = [g[0] for g in self.config.getGroups()]
  1784. self.addchumdialog = AddChumDialog(available_groups, self)
  1785. ok = self.addchumdialog.exec_()
  1786. handle = unicode(self.addchumdialog.chumBox.text()).strip()
  1787. newgroup = unicode(self.addchumdialog.newgroup.text()).strip()
  1788. selectedGroup = self.addchumdialog.groupBox.currentText()
  1789. group = newgroup if newgroup else selectedGroup
  1790. if ok:
  1791. handle = unicode(handle)
  1792. if handle in [h.handle for h in self.chumList.chums]:
  1793. self.addchumdialog = None
  1794. return
  1795. if not (PesterProfile.checkLength(handle) and
  1796. PesterProfile.checkValid(handle)[0]):
  1797. errormsg = QtGui.QErrorMessage(self)
  1798. errormsg.showMessage("THIS IS NOT A VALID CHUMTAG!")
  1799. self.addchumdialog = None
  1800. return
  1801. if re.search("[^A-Za-z0-9_\s]", group) is not None:
  1802. errormsg = QtGui.QErrorMessage(self)
  1803. errormsg.showMessage("THIS IS NOT A VALID GROUP NAME")
  1804. self.addchumdialog = None
  1805. return
  1806. if newgroup:
  1807. # make new group
  1808. self.addGroup(group)
  1809. chum = PesterProfile(handle, chumdb=self.chumdb, group=group)
  1810. self.chumdb.setGroup(handle, group)
  1811. self.addChum(chum)
  1812. self.addchumdialog = None
  1813. @QtCore.pyqtSlot(QtCore.QString)
  1814. def removeChum(self, chumlisting):
  1815. self.config.removeChum(chumlisting)
  1816. def reportChum(self, handle):
  1817. (reason, ok) = QtGui.QInputDialog.getText(self, "Report User", "Enter the reason you are reporting this user (optional):")
  1818. if ok:
  1819. self.sendMessage.emit("REPORT %s %s" % (handle, reason) , "calSprite")
  1820. @QtCore.pyqtSlot(QtCore.QString)
  1821. def blockChum(self, handle):
  1822. h = unicode(handle)
  1823. self.config.addBlocklist(h)
  1824. self.config.removeChum(h)
  1825. if self.convos.has_key(h):
  1826. convo = self.convos[h]
  1827. msg = self.profile().pestermsg(convo.chum, QtGui.QColor(self.theme["convo/systemMsgColor"]), self.theme["convo/text/blocked"])
  1828. convo.textArea.append(convertTags(msg))
  1829. self.chatlog.log(convo.chum.handle, msg)
  1830. convo.updateBlocked()
  1831. self.chumList.removeChum(h)
  1832. if hasattr(self, 'trollslum') and self.trollslum:
  1833. newtroll = PesterProfile(h)
  1834. self.trollslum.addTroll(newtroll)
  1835. self.moodRequest.emit(newtroll)
  1836. self.blockedChum.emit(handle)
  1837. @QtCore.pyqtSlot(QtCore.QString)
  1838. def unblockChum(self, handle):
  1839. h = unicode(handle)
  1840. self.config.delBlocklist(h)
  1841. if self.convos.has_key(h):
  1842. convo = self.convos[h]
  1843. msg = self.profile().pestermsg(convo.chum, QtGui.QColor(self.theme["convo/systemMsgColor"]), self.theme["convo/text/unblocked"])
  1844. convo.textArea.append(convertTags(msg))
  1845. self.chatlog.log(convo.chum.handle, msg)
  1846. convo.updateMood(convo.chum.mood, unblocked=True)
  1847. chum = PesterProfile(h, chumdb=self.chumdb)
  1848. if hasattr(self, 'trollslum') and self.trollslum:
  1849. self.trollslum.removeTroll(handle)
  1850. self.config.addChum(chum)
  1851. self.chumList.addChum(chum)
  1852. self.moodRequest.emit(chum)
  1853. self.unblockedChum.emit(handle)
  1854. @QtCore.pyqtSlot(bool)
  1855. def toggleIdle(self, idle):
  1856. if idle:
  1857. self.setAway.emit(True)
  1858. self.randhandler.setIdle(True)
  1859. sysColor = QtGui.QColor(self.theme["convo/systemMsgColor"])
  1860. verb = self.theme["convo/text/idle"]
  1861. for (h, convo) in self.convos.iteritems():
  1862. if convo.chumopen:
  1863. msg = self.profile().idlemsg(sysColor, verb)
  1864. convo.textArea.append(convertTags(msg))
  1865. self.chatlog.log(h, msg)
  1866. self.sendMessage.emit("PESTERCHUM:IDLE", h)
  1867. else:
  1868. self.setAway.emit(False)
  1869. self.randhandler.setIdle(False)
  1870. self.idletime = 0
  1871. @QtCore.pyqtSlot()
  1872. def checkIdle(self):
  1873. newpos = QtGui.QCursor.pos()
  1874. if newpos == self.idleposition:
  1875. self.idletime += 1
  1876. else:
  1877. self.idletime = 0
  1878. if self.idletime >= self.idlethreshold:
  1879. if not self.idleaction.isChecked():
  1880. self.idleaction.toggle()
  1881. self.autoidle = True
  1882. else:
  1883. if self.autoidle:
  1884. if self.idleaction.isChecked():
  1885. self.idleaction.toggle()
  1886. self.autoidle = False
  1887. self.idleposition = newpos
  1888. @QtCore.pyqtSlot()
  1889. def importExternalConfig(self):
  1890. f = QtGui.QFileDialog.getOpenFileName(self)
  1891. if f == "":
  1892. return
  1893. fp = open(f, 'r')
  1894. regexp_state = None
  1895. for l in fp.xreadlines():
  1896. # import chumlist
  1897. l = l.rstrip()
  1898. chum_mo = re.match("handle: ([A-Za-z0-9]+)", l)
  1899. if chum_mo is not None:
  1900. chum = PesterProfile(chum_mo.group(1))
  1901. self.addChum(chum)
  1902. continue
  1903. if regexp_state is not None:
  1904. replace_mo = re.match("replace: (.+)", l)
  1905. if replace_mo is not None:
  1906. replace = replace_mo.group(1)
  1907. try:
  1908. re.compile(regexp_state)
  1909. except re.error, e:
  1910. continue
  1911. newquirk = pesterQuirk({"type": "regexp",
  1912. "from": regexp_state,
  1913. "to": replace})
  1914. qs = self.userprofile.quirks
  1915. qs.addQuirk(newquirk)
  1916. self.userprofile.setQuirks(qs)
  1917. regexp_state = None
  1918. continue
  1919. search_mo = re.match("search: (.+)", l)
  1920. if search_mo is not None:
  1921. regexp_state = search_mo.group(1)
  1922. continue
  1923. other_mo = re.match("(prefix|suffix): (.+)", l)
  1924. if other_mo is not None:
  1925. newquirk = pesterQuirk({"type": other_mo.group(1),
  1926. "value": other_mo.group(2)})
  1927. qs = self.userprofile.quirks
  1928. qs.addQuirk(newquirk)
  1929. self.userprofile.setQuirks(qs)
  1930. @QtCore.pyqtSlot()
  1931. def showMemos(self, channel=""):
  1932. if not hasattr(self, 'memochooser'):
  1933. self.memochooser = None
  1934. if self.memochooser:
  1935. return
  1936. self.memochooser = PesterMemoList(self, channel)
  1937. self.connect(self.memochooser, QtCore.SIGNAL('accepted()'),
  1938. self, QtCore.SLOT('joinSelectedMemo()'))
  1939. self.connect(self.memochooser, QtCore.SIGNAL('rejected()'),
  1940. self, QtCore.SLOT('memoChooserClose()'))
  1941. self.requestChannelList.emit()
  1942. self.memochooser.show()
  1943. @QtCore.pyqtSlot()
  1944. def joinSelectedMemo(self):
  1945. newmemo = self.memochooser.newmemoname()
  1946. selectedmemo = self.memochooser.selectedmemo()
  1947. time = unicode(self.memochooser.timeinput.text())
  1948. secret = self.memochooser.secretChannel.isChecked()
  1949. invite = self.memochooser.inviteChannel.isChecked()
  1950. if newmemo:
  1951. channel = "#"+unicode(newmemo).replace(" ", "_")
  1952. channel = re.sub(r"[^A-Za-z0-9#_]", "", channel)
  1953. self.newMemo(channel, time, secret=secret, invite=invite)
  1954. elif selectedmemo:
  1955. channel = "#"+unicode(selectedmemo.target)
  1956. self.newMemo(channel, time)
  1957. self.memochooser = None
  1958. @QtCore.pyqtSlot()
  1959. def memoChooserClose(self):
  1960. self.memochooser = None
  1961. @QtCore.pyqtSlot(PesterList)
  1962. def updateChannelList(self, channels):
  1963. if hasattr(self, 'memochooser') and self.memochooser:
  1964. self.memochooser.updateChannels(channels)
  1965. @QtCore.pyqtSlot()
  1966. def showAllUsers(self):
  1967. if not hasattr(self, 'allusers'):
  1968. self.allusers = None
  1969. if not self.allusers:
  1970. self.allusers = PesterUserlist(self.config, self.theme, self)
  1971. self.connect(self.allusers, QtCore.SIGNAL('accepted()'),
  1972. self, QtCore.SLOT('userListClose()'))
  1973. self.connect(self.allusers, QtCore.SIGNAL('rejected()'),
  1974. self, QtCore.SLOT('userListClose()'))
  1975. self.connect(self.allusers, QtCore.SIGNAL('addChum(QString)'),
  1976. self, QtCore.SLOT('userListAdd(QString)'))
  1977. self.connect(self.allusers, QtCore.SIGNAL('pesterChum(QString)'),
  1978. self, QtCore.SLOT('userListPester(QString)'))
  1979. self.requestNames.emit("#pesterchum")
  1980. self.allusers.show()
  1981. @QtCore.pyqtSlot(QtCore.QString)
  1982. def userListAdd(self, handle):
  1983. h = unicode(handle)
  1984. chum = PesterProfile(h, chumdb=self.chumdb)
  1985. self.addChum(chum)
  1986. @QtCore.pyqtSlot(QtCore.QString)
  1987. def userListPester(self, handle):
  1988. h = unicode(handle)
  1989. self.newConversation(h)
  1990. @QtCore.pyqtSlot()
  1991. def userListClose(self):
  1992. self.allusers = None
  1993. @QtCore.pyqtSlot()
  1994. def openQuirks(self):
  1995. if not hasattr(self, 'quirkmenu'):
  1996. self.quirkmenu = None
  1997. if not self.quirkmenu:
  1998. self.quirkmenu = PesterChooseQuirks(self.config, self.theme, self)
  1999. self.connect(self.quirkmenu, QtCore.SIGNAL('accepted()'),
  2000. self, QtCore.SLOT('updateQuirks()'))
  2001. self.connect(self.quirkmenu, QtCore.SIGNAL('rejected()'),
  2002. self, QtCore.SLOT('closeQuirks()'))
  2003. self.quirkmenu.show()
  2004. self.quirkmenu.raise_()
  2005. self.quirkmenu.activateWindow()
  2006. @QtCore.pyqtSlot()
  2007. def updateQuirks(self):
  2008. for i in range(self.quirkmenu.quirkList.topLevelItemCount()):
  2009. curgroup = unicode(self.quirkmenu.quirkList.topLevelItem(i).text(0))
  2010. for j in range(self.quirkmenu.quirkList.topLevelItem(i).childCount()):
  2011. item = self.quirkmenu.quirkList.topLevelItem(i).child(j)
  2012. item.quirk.quirk["on"] = item.quirk.on = (item.checkState(0) == QtCore.Qt.Checked)
  2013. item.quirk.quirk["group"] = item.quirk.group = curgroup
  2014. quirks = pesterQuirks(self.quirkmenu.quirks())
  2015. self.userprofile.setQuirks(quirks)
  2016. if hasattr(self.quirkmenu, 'quirktester') and self.quirkmenu.quirktester:
  2017. self.quirkmenu.quirktester.close()
  2018. self.quirkmenu = None
  2019. @QtCore.pyqtSlot()
  2020. def closeQuirks(self):
  2021. if hasattr(self.quirkmenu, 'quirktester') and self.quirkmenu.quirktester:
  2022. self.quirkmenu.quirktester.close()
  2023. self.quirkmenu = None
  2024. @QtCore.pyqtSlot()
  2025. def openChat(self):
  2026. if not hasattr(self, "openchatdialog"):
  2027. self.openchatdialog = None
  2028. if not self.openchatdialog:
  2029. (chum, ok) = QtGui.QInputDialog.getText(self, "Pester Chum", "Enter a handle to pester:")
  2030. try:
  2031. if ok:
  2032. self.newConversation(unicode(chum))
  2033. except:
  2034. pass
  2035. finally:
  2036. self.openchatdialog = None
  2037. @QtCore.pyqtSlot()
  2038. def openLogv(self):
  2039. if not hasattr(self, 'logusermenu'):
  2040. self.logusermenu = None
  2041. if not self.logusermenu:
  2042. self.logusermenu = PesterLogUserSelect(self.config, self.theme, self)
  2043. self.connect(self.logusermenu, QtCore.SIGNAL('accepted()'),
  2044. self, QtCore.SLOT('closeLogUsers()'))
  2045. self.connect(self.logusermenu, QtCore.SIGNAL('rejected()'),
  2046. self, QtCore.SLOT('closeLogUsers()'))
  2047. self.logusermenu.show()
  2048. self.logusermenu.raise_()
  2049. self.logusermenu.activateWindow()
  2050. @QtCore.pyqtSlot()
  2051. def closeLogUsers(self):
  2052. self.logusermenu.close()
  2053. self.logusermenu = None
  2054. @QtCore.pyqtSlot()
  2055. def addGroupWindow(self):
  2056. if not hasattr(self, 'addgroupdialog'):
  2057. self.addgroupdialog = None
  2058. if not self.addgroupdialog:
  2059. (gname, ok) = QtGui.QInputDialog.getText(self, "Add Group", "Enter a name for the new group:")
  2060. if ok:
  2061. gname = unicode(gname)
  2062. if re.search("[^A-Za-z0-9_\s]", gname) is not None:
  2063. msgbox = QtGui.QMessageBox()
  2064. msgbox.setInformativeText("THIS IS NOT A VALID GROUP NAME")
  2065. msgbox.setStandardButtons(QtGui.QMessageBox.Ok)
  2066. ret = msgbox.exec_()
  2067. self.addgroupdialog = None
  2068. return
  2069. self.addGroup(gname)
  2070. self.addgroupdialog = None
  2071. @QtCore.pyqtSlot()
  2072. def openOpts(self):
  2073. if not hasattr(self, 'optionmenu'):
  2074. self.optionmenu = None
  2075. if not self.optionmenu:
  2076. self.optionmenu = PesterOptions(self.config, self.theme, self)
  2077. self.connect(self.optionmenu, QtCore.SIGNAL('accepted()'),
  2078. self, QtCore.SLOT('updateOptions()'))
  2079. self.connect(self.optionmenu, QtCore.SIGNAL('rejected()'),
  2080. self, QtCore.SLOT('closeOptions()'))
  2081. self.optionmenu.show()
  2082. self.optionmenu.raise_()
  2083. self.optionmenu.activateWindow()
  2084. @QtCore.pyqtSlot()
  2085. def closeOptions(self):
  2086. self.optionmenu.close()
  2087. self.optionmenu = None
  2088. @QtCore.pyqtSlot()
  2089. def updateOptions(self):
  2090. try:
  2091. # tabs
  2092. curtab = self.config.tabs()
  2093. tabsetting = self.optionmenu.tabcheck.isChecked()
  2094. if curtab and not tabsetting:
  2095. # split tabs into windows
  2096. windows = []
  2097. if self.tabconvo:
  2098. windows = list(self.tabconvo.convos.values())
  2099. for w in windows:
  2100. w.setParent(None)
  2101. w.show()
  2102. w.raiseChat()
  2103. if self.tabconvo:
  2104. self.tabconvo.closeSoft()
  2105. # save options
  2106. self.config.set("tabs", tabsetting)
  2107. elif tabsetting and not curtab:
  2108. # combine
  2109. self.createTabWindow()
  2110. newconvos = {}
  2111. for (h,c) in self.convos.iteritems():
  2112. c.setParent(self.tabconvo)
  2113. self.tabconvo.addChat(c)
  2114. self.tabconvo.show()
  2115. newconvos[h] = c
  2116. self.convos = newconvos
  2117. # save options
  2118. self.config.set("tabs", tabsetting)
  2119. # tabs memos
  2120. curtabmemo = self.config.tabMemos()
  2121. tabmemosetting = self.optionmenu.tabmemocheck.isChecked()
  2122. if curtabmemo and not tabmemosetting:
  2123. # split tabs into windows
  2124. windows = []
  2125. if self.tabmemo:
  2126. windows = list(self.tabmemo.convos.values())
  2127. for w in windows:
  2128. w.setParent(None)
  2129. w.show()
  2130. w.raiseChat()
  2131. if self.tabmemo:
  2132. self.tabmemo.closeSoft()
  2133. # save options
  2134. self.config.set("tabmemos", tabmemosetting)
  2135. elif tabmemosetting and not curtabmemo:
  2136. # combine
  2137. newmemos = {}
  2138. self.createMemoTabWindow()
  2139. for (h,m) in self.memos.iteritems():
  2140. m.setParent(self.tabmemo)
  2141. self.tabmemo.addChat(m)
  2142. self.tabmemo.show()
  2143. newmemos[h] = m
  2144. self.memos = newmemos
  2145. # save options
  2146. self.config.set("tabmemos", tabmemosetting)
  2147. # hidden chums
  2148. chumsetting = self.optionmenu.hideOffline.isChecked()
  2149. curchum = self.config.hideOfflineChums()
  2150. if curchum and not chumsetting:
  2151. self.chumList.showAllChums()
  2152. elif chumsetting and not curchum:
  2153. self.chumList.hideOfflineChums()
  2154. self.config.set("hideOfflineChums", chumsetting)
  2155. # sorting method
  2156. sortsetting = self.optionmenu.sortBox.currentIndex()
  2157. cursort = self.config.sortMethod()
  2158. self.config.set("sortMethod", sortsetting)
  2159. if sortsetting != cursort:
  2160. self.chumList.sort()
  2161. # sound
  2162. soundsetting = self.optionmenu.soundcheck.isChecked()
  2163. self.config.set("soundon", soundsetting)
  2164. chatsoundsetting = self.optionmenu.chatsoundcheck.isChecked()
  2165. curchatsound = self.config.chatSound()
  2166. if chatsoundsetting != curchatsound:
  2167. self.config.set('chatSound', chatsoundsetting)
  2168. memosoundsetting = self.optionmenu.memosoundcheck.isChecked()
  2169. curmemosound = self.config.memoSound()
  2170. if memosoundsetting != curmemosound:
  2171. self.config.set('memoSound', memosoundsetting)
  2172. memopingsetting = self.optionmenu.memopingcheck.isChecked()
  2173. curmemoping = self.config.memoPing()
  2174. if memopingsetting != curmemoping:
  2175. self.config.set('pingSound', memopingsetting)
  2176. namesoundsetting = self.optionmenu.namesoundcheck.isChecked()
  2177. curnamesound = self.config.nameSound()
  2178. if namesoundsetting != curnamesound:
  2179. self.config.set('nameSound', namesoundsetting)
  2180. volumesetting = self.optionmenu.volume.value()
  2181. curvolume = self.config.volume()
  2182. if volumesetting != curvolume:
  2183. self.config.set('volume', volumesetting)
  2184. self.setVolume(volumesetting)
  2185. # timestamps
  2186. timestampsetting = self.optionmenu.timestampcheck.isChecked()
  2187. self.config.set("showTimeStamps", timestampsetting)
  2188. timeformatsetting = unicode(self.optionmenu.timestampBox.currentText())
  2189. if timeformatsetting == "12 hour":
  2190. self.config.set("time12Format", True)
  2191. else:
  2192. self.config.set("time12Format", False)
  2193. secondssetting = self.optionmenu.secondscheck.isChecked()
  2194. self.config.set("showSeconds", secondssetting)
  2195. # groups
  2196. #groupssetting = self.optionmenu.groupscheck.isChecked()
  2197. #self.config.set("useGroups", groupssetting)
  2198. emptygroupssetting = self.optionmenu.showemptycheck.isChecked()
  2199. curemptygroup = self.config.showEmptyGroups()
  2200. if curemptygroup and not emptygroupssetting:
  2201. self.chumList.hideEmptyGroups()
  2202. elif emptygroupssetting and not curemptygroup:
  2203. self.chumList.showAllGroups()
  2204. self.config.set("emptyGroups", emptygroupssetting)
  2205. # online numbers
  2206. onlinenumsetting = self.optionmenu.showonlinenumbers.isChecked()
  2207. curonlinenum = self.config.showOnlineNumbers()
  2208. if onlinenumsetting and not curonlinenum:
  2209. self.chumList.showOnlineNumbers()
  2210. elif curonlinenum and not onlinenumsetting:
  2211. self.chumList.hideOnlineNumbers()
  2212. self.config.set("onlineNumbers", onlinenumsetting)
  2213. # logging
  2214. logpesterssetting = 0
  2215. if self.optionmenu.logpesterscheck.isChecked():
  2216. logpesterssetting = logpesterssetting | self.config.LOG
  2217. if self.optionmenu.stamppestercheck.isChecked():
  2218. logpesterssetting = logpesterssetting | self.config.STAMP
  2219. curlogpesters = self.config.logPesters()
  2220. if logpesterssetting != curlogpesters:
  2221. self.config.set('logPesters', logpesterssetting)
  2222. logmemossetting = 0
  2223. if self.optionmenu.logmemoscheck.isChecked():
  2224. logmemossetting = logmemossetting | self.config.LOG
  2225. if self.optionmenu.stampmemocheck.isChecked():
  2226. logmemossetting = logmemossetting | self.config.STAMP
  2227. curlogmemos = self.config.logMemos()
  2228. if logmemossetting != curlogmemos:
  2229. self.config.set('logMemos', logmemossetting)
  2230. # memo and user links
  2231. linkssetting = self.optionmenu.userlinkscheck.isChecked()
  2232. curlinks = self.config.disableUserLinks()
  2233. if linkssetting != curlinks:
  2234. self.config.set('userLinks', not linkssetting)
  2235. # idle time
  2236. idlesetting = self.optionmenu.idleBox.value()
  2237. curidle = self.config.idleTime()
  2238. if idlesetting != curidle:
  2239. self.config.set('idleTime', idlesetting)
  2240. self.idlethreshold = 60*idlesetting
  2241. # theme
  2242. ghostchumsetting = self.optionmenu.ghostchum.isChecked()
  2243. curghostchum = self.config.ghostchum()
  2244. self.config.set('ghostchum', ghostchumsetting)
  2245. self.themeSelected(ghostchumsetting != curghostchum)
  2246. # randoms
  2247. if self.randhandler.running:
  2248. self.randhandler.setRandomer(self.optionmenu.randomscheck.isChecked())
  2249. # button actions
  2250. minisetting = self.optionmenu.miniBox.currentIndex()
  2251. curmini = self.config.minimizeAction()
  2252. if minisetting != curmini:
  2253. self.config.set('miniAction', minisetting)
  2254. self.setButtonAction(self.miniButton, minisetting, curmini)
  2255. closesetting = self.optionmenu.closeBox.currentIndex()
  2256. curclose = self.config.closeAction()
  2257. if closesetting != curclose:
  2258. self.config.set('closeAction', closesetting)
  2259. self.setButtonAction(self.closeButton, closesetting, curclose)
  2260. # op and voice messages
  2261. opvmesssetting = self.optionmenu.memomessagecheck.isChecked()
  2262. curopvmess = self.config.opvoiceMessages()
  2263. if opvmesssetting != curopvmess:
  2264. self.config.set('opvMessages', opvmesssetting)
  2265. # animated smiles
  2266. if ostools.isOSXBundle():
  2267. animatesetting = False;
  2268. else:
  2269. animatesetting = self.optionmenu.animationscheck.isChecked()
  2270. curanimate = self.config.animations()
  2271. if animatesetting != curanimate:
  2272. self.config.set('animations', animatesetting)
  2273. self.animationSetting.emit(animatesetting)
  2274. # update checked
  2275. updatechecksetting = self.optionmenu.updateBox.currentIndex()
  2276. curupdatecheck = self.config.checkForUpdates()
  2277. if updatechecksetting != curupdatecheck:
  2278. self.config.set('checkUpdates', updatechecksetting)
  2279. # mspa update check
  2280. if ostools.isOSXLeopard():
  2281. mspachecksetting = false
  2282. else:
  2283. mspachecksetting = self.optionmenu.mspaCheck.isChecked()
  2284. curmspacheck = self.config.checkMSPA()
  2285. if mspachecksetting != curmspacheck:
  2286. self.config.set('mspa', mspachecksetting)
  2287. # Taskbar blink
  2288. blinksetting = 0
  2289. if self.optionmenu.pesterBlink.isChecked():
  2290. blinksetting |= self.config.PBLINK
  2291. if self.optionmenu.memoBlink.isChecked():
  2292. blinksetting |= self.config.MBLINK
  2293. curblink = self.config.blink()
  2294. if blinksetting != curblink:
  2295. self.config.set('blink', blinksetting)
  2296. # toast notifications
  2297. self.tm.setEnabled(self.optionmenu.notifycheck.isChecked())
  2298. self.tm.setCurrentType(unicode(self.optionmenu.notifyOptions.currentText()))
  2299. notifysetting = 0
  2300. if self.optionmenu.notifySigninCheck.isChecked():
  2301. notifysetting |= self.config.SIGNIN
  2302. if self.optionmenu.notifySignoutCheck.isChecked():
  2303. notifysetting |= self.config.SIGNOUT
  2304. if self.optionmenu.notifyNewMsgCheck.isChecked():
  2305. notifysetting |= self.config.NEWMSG
  2306. if self.optionmenu.notifyNewConvoCheck.isChecked():
  2307. notifysetting |= self.config.NEWCONVO
  2308. if self.optionmenu.notifyMentionsCheck.isChecked():
  2309. notifysetting |= self.config.INITIALS
  2310. curnotify = self.config.notifyOptions()
  2311. if notifysetting != curnotify:
  2312. self.config.set('notifyOptions', notifysetting)
  2313. # low bandwidth
  2314. bandwidthsetting = self.optionmenu.bandwidthcheck.isChecked()
  2315. curbandwidth = self.config.lowBandwidth()
  2316. if bandwidthsetting != curbandwidth:
  2317. self.config.set('lowBandwidth', bandwidthsetting)
  2318. if bandwidthsetting:
  2319. self.leftChannel.emit("#pesterchum")
  2320. else:
  2321. self.joinChannel.emit("#pesterchum")
  2322. # advanced
  2323. ## user mode
  2324. if self.advanced:
  2325. newmodes = self.optionmenu.modechange.text()
  2326. if newmodes:
  2327. self.setChannelMode.emit(self.profile().handle, newmodes, "")
  2328. except Exception, e:
  2329. logging.error(e)
  2330. finally:
  2331. self.optionmenu = None
  2332. def setButtonAction(self, button, setting, old):
  2333. if old == 0: # minimize to taskbar
  2334. self.disconnect(button, QtCore.SIGNAL('clicked()'),
  2335. self, QtCore.SLOT('showMinimized()'));
  2336. elif old == 1: # minimize to tray
  2337. self.disconnect(button, QtCore.SIGNAL('clicked()'),
  2338. self, QtCore.SLOT('closeToTray()'));
  2339. elif old == 2: # quit
  2340. self.disconnect(button, QtCore.SIGNAL('clicked()'),
  2341. self, QtCore.SLOT('close()'));
  2342. if setting == 0: # minimize to taskbar
  2343. self.connect(button, QtCore.SIGNAL('clicked()'),
  2344. self, QtCore.SLOT('showMinimized()'));
  2345. elif setting == 1: # minimize to tray
  2346. self.connect(button, QtCore.SIGNAL('clicked()'),
  2347. self, QtCore.SLOT('closeToTray()'));
  2348. elif setting == 2: # quit
  2349. self.connect(button, QtCore.SIGNAL('clicked()'),
  2350. self, QtCore.SLOT('close()'));
  2351. @QtCore.pyqtSlot()
  2352. def themeSelectOverride(self):
  2353. self.themeSelected(self.theme.name)
  2354. @QtCore.pyqtSlot()
  2355. def themeSelected(self, override=False):
  2356. if not override:
  2357. themename = unicode(self.optionmenu.themeBox.currentText())
  2358. else:
  2359. themename = override
  2360. if override or themename != self.theme.name:
  2361. try:
  2362. self.changeTheme(pesterTheme(themename))
  2363. except ValueError, e:
  2364. themeWarning = QtGui.QMessageBox(self)
  2365. themeWarning.setText("Theme Error: %s" % (e))
  2366. themeWarning.exec_()
  2367. self.choosetheme = None
  2368. return
  2369. # update profile
  2370. self.userprofile.setTheme(self.theme)
  2371. self.choosetheme = None
  2372. @QtCore.pyqtSlot()
  2373. def closeTheme(self):
  2374. self.choosetheme = None
  2375. @QtCore.pyqtSlot()
  2376. def profileSelected(self):
  2377. if self.chooseprofile.profileBox and \
  2378. self.chooseprofile.profileBox.currentIndex() > 0:
  2379. handle = unicode(self.chooseprofile.profileBox.currentText())
  2380. if handle == self.profile().handle:
  2381. self.chooseprofile = None
  2382. return
  2383. self.userprofile = userProfile(handle)
  2384. self.changeTheme(self.userprofile.getTheme())
  2385. else:
  2386. handle = unicode(self.chooseprofile.chumHandle.text())
  2387. if handle == self.profile().handle:
  2388. self.chooseprofile = None
  2389. return
  2390. profile = PesterProfile(handle,
  2391. self.chooseprofile.chumcolor)
  2392. self.userprofile = userProfile.newUserProfile(profile)
  2393. self.changeTheme(self.userprofile.getTheme())
  2394. self.chatlog.close()
  2395. self.chatlog = PesterLog(handle, self)
  2396. # is default?
  2397. if self.chooseprofile.defaultcheck.isChecked():
  2398. self.config.set("defaultprofile", self.userprofile.chat.handle)
  2399. if hasattr(self, 'trollslum') and self.trollslum:
  2400. self.trollslum.close()
  2401. self.chooseprofile = None
  2402. self.profileChanged.emit()
  2403. @QtCore.pyqtSlot()
  2404. def showTrollSlum(self):
  2405. if not hasattr(self, 'trollslum'):
  2406. self.trollslum = None
  2407. if self.trollslum:
  2408. return
  2409. trolls = [PesterProfile(h) for h in self.config.getBlocklist()]
  2410. self.trollslum = TrollSlumWindow(trolls, self)
  2411. self.connect(self.trollslum, QtCore.SIGNAL('blockChumSignal(QString)'),
  2412. self, QtCore.SLOT('blockChum(QString)'))
  2413. self.connect(self.trollslum,
  2414. QtCore.SIGNAL('unblockChumSignal(QString)'),
  2415. self, QtCore.SLOT('unblockChum(QString)'))
  2416. self.moodsRequest.emit(PesterList(trolls))
  2417. self.trollslum.show()
  2418. @QtCore.pyqtSlot()
  2419. def closeTrollSlum(self):
  2420. self.trollslum = None
  2421. @QtCore.pyqtSlot()
  2422. def changeMyColor(self):
  2423. if not hasattr(self, 'colorDialog'):
  2424. self.colorDialog = None
  2425. if self.colorDialog:
  2426. return
  2427. self.colorDialog = QtGui.QColorDialog(self)
  2428. color = self.colorDialog.getColor(initial=self.profile().color)
  2429. if not color.isValid():
  2430. color = self.profile().color
  2431. self.mychumcolor.setStyleSheet("background: %s" % color.name())
  2432. self.userprofile.setColor(color)
  2433. self.mycolorUpdated.emit()
  2434. self.colorDialog = None
  2435. @QtCore.pyqtSlot()
  2436. def closeProfile(self):
  2437. self.chooseprofile = None
  2438. @QtCore.pyqtSlot()
  2439. def switchProfile(self):
  2440. if self.convos:
  2441. closeWarning = QtGui.QMessageBox()
  2442. closeWarning.setText("WARNING: CHANGING PROFILES WILL CLOSE ALL CONVERSATION WINDOWS!")
  2443. closeWarning.setInformativeText("i warned you about windows bro!!!! i told you dog!")
  2444. closeWarning.setStandardButtons(QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok)
  2445. closeWarning.setDefaultButton(QtGui.QMessageBox.Ok)
  2446. ret = closeWarning.exec_()
  2447. if ret == QtGui.QMessageBox.Cancel:
  2448. return
  2449. self.changeProfile()
  2450. @QtCore.pyqtSlot()
  2451. def aboutPesterchum(self):
  2452. if hasattr(self, 'aboutwindow') and self.aboutwindow:
  2453. return
  2454. self.aboutwindow = AboutPesterchum(self)
  2455. self.aboutwindow.exec_()
  2456. self.aboutwindow = None
  2457. @QtCore.pyqtSlot()
  2458. def loadCalsprite(self):
  2459. self.newConversation("calSprite")
  2460. @QtCore.pyqtSlot()
  2461. def loadChanServ(self):
  2462. self.newConversation("chanServ")
  2463. @QtCore.pyqtSlot()
  2464. def loadNickServ(self):
  2465. self.newConversation("nickServ")
  2466. @QtCore.pyqtSlot()
  2467. def launchHelp(self):
  2468. QtGui.QDesktopServices.openUrl(QtCore.QUrl("http://nova.xzibition.com/~illuminatedwax/help.html", QtCore.QUrl.TolerantMode))
  2469. @QtCore.pyqtSlot()
  2470. def reportBug(self):
  2471. if hasattr(self, 'bugreportwindow') and self.bugreportwindow:
  2472. return
  2473. self.bugreportwindow = BugReporter(self)
  2474. self.bugreportwindow.exec_()
  2475. self.bugreportwindow = None
  2476. @QtCore.pyqtSlot(QtCore.QString, QtCore.QString)
  2477. def nickCollision(self, handle, tmphandle):
  2478. self.mychumhandle.setText(tmphandle)
  2479. self.userprofile = userProfile(PesterProfile("pesterClient%d" % (random.randint(100,999)), QtGui.QColor("black"), Mood(0)))
  2480. self.changeTheme(self.userprofile.getTheme())
  2481. if not hasattr(self, 'chooseprofile'):
  2482. self.chooseprofile = None
  2483. if not self.chooseprofile:
  2484. h = unicode(handle)
  2485. self.changeProfile(collision=h)
  2486. @QtCore.pyqtSlot(QtCore.QString)
  2487. def myHandleChanged(self, handle):
  2488. if self.profile().handle == handle:
  2489. return
  2490. else:
  2491. self.nickCollision(self.profile().handle, handle)
  2492. @QtCore.pyqtSlot()
  2493. def pickTheme(self):
  2494. self.themePicker()
  2495. @QtCore.pyqtSlot(QtGui.QSystemTrayIcon.ActivationReason)
  2496. def systemTrayActivated(self, reason):
  2497. if reason == QtGui.QSystemTrayIcon.Trigger:
  2498. self.systemTrayFunction()
  2499. elif reason == QtGui.QSystemTrayIcon.Context:
  2500. pass
  2501. # show context menu i guess
  2502. #self.showTrayContext.emit()
  2503. @QtCore.pyqtSlot()
  2504. def tooManyPeeps(self):
  2505. msg = QtGui.QMessageBox(self)
  2506. msg.setText("D: TOO MANY PEOPLE!!!")
  2507. msg.setInformativeText("The server has hit max capacity. Please try again later.")
  2508. msg.show()
  2509. pcUpdate = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
  2510. closeToTraySignal = QtCore.pyqtSignal()
  2511. newConvoStarted = QtCore.pyqtSignal(QtCore.QString, bool, name="newConvoStarted")
  2512. sendMessage = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
  2513. sendNotice = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
  2514. convoClosed = QtCore.pyqtSignal(QtCore.QString)
  2515. profileChanged = QtCore.pyqtSignal()
  2516. animationSetting = QtCore.pyqtSignal(bool)
  2517. moodRequest = QtCore.pyqtSignal(PesterProfile)
  2518. moodsRequest = QtCore.pyqtSignal(PesterList)
  2519. moodUpdated = QtCore.pyqtSignal()
  2520. requestChannelList = QtCore.pyqtSignal()
  2521. requestNames = QtCore.pyqtSignal(QtCore.QString)
  2522. namesUpdated = QtCore.pyqtSignal(QtCore.QString)
  2523. modesUpdated = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
  2524. userPresentSignal = QtCore.pyqtSignal(QtCore.QString,QtCore.QString,QtCore.QString)
  2525. mycolorUpdated = QtCore.pyqtSignal()
  2526. trayIconSignal = QtCore.pyqtSignal(int)
  2527. blockedChum = QtCore.pyqtSignal(QtCore.QString)
  2528. unblockedChum = QtCore.pyqtSignal(QtCore.QString)
  2529. kickUser = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
  2530. joinChannel = QtCore.pyqtSignal(QtCore.QString)
  2531. leftChannel = QtCore.pyqtSignal(QtCore.QString)
  2532. setChannelMode = QtCore.pyqtSignal(QtCore.QString, QtCore.QString, QtCore.QString)
  2533. channelNames = QtCore.pyqtSignal(QtCore.QString)
  2534. inviteChum = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
  2535. inviteOnlyChan = QtCore.pyqtSignal(QtCore.QString)
  2536. closeSignal = QtCore.pyqtSignal()
  2537. reconnectIRC = QtCore.pyqtSignal()
  2538. gainAttention = QtCore.pyqtSignal(QtGui.QWidget)
  2539. pingServer = QtCore.pyqtSignal()
  2540. setAway = QtCore.pyqtSignal(bool)
  2541. killSomeQuirks = QtCore.pyqtSignal(QtCore.QString, QtCore.QString)
  2542. class PesterTray(QtGui.QSystemTrayIcon):
  2543. def __init__(self, icon, mainwindow, parent):
  2544. QtGui.QSystemTrayIcon.__init__(self, icon, parent)
  2545. self.mainwindow = mainwindow
  2546. @QtCore.pyqtSlot(int)
  2547. def changeTrayIcon(self, i):
  2548. if i == 0:
  2549. self.setIcon(PesterIcon(self.mainwindow.theme["main/icon"]))
  2550. else:
  2551. self.setIcon(PesterIcon(self.mainwindow.theme["main/newmsgicon"]))
  2552. @QtCore.pyqtSlot()
  2553. def mainWindowClosed(self):
  2554. self.hide()
  2555. class MainProgram(QtCore.QObject):
  2556. def __init__(self):
  2557. QtCore.QObject.__init__(self)
  2558. self.app = QtGui.QApplication(sys.argv)
  2559. self.app.setApplicationName("Pesterchum 3.14")
  2560. options = self.oppts(sys.argv[1:])
  2561. if pygame and pygame.mixer:
  2562. # we could set the frequency higher but i love how cheesy it sounds
  2563. try:
  2564. pygame.mixer.init()
  2565. pygame.mixer.init()
  2566. except pygame.error, e:
  2567. print "Warning: No sound! %s" % (e)
  2568. else:
  2569. print "Warning: No sound!"
  2570. self.widget = PesterWindow(options)
  2571. self.widget.show()
  2572. self.trayicon = PesterTray(PesterIcon(self.widget.theme["main/icon"]), self.widget, self.app)
  2573. self.traymenu = QtGui.QMenu()
  2574. moodMenu = self.traymenu.addMenu("SET MOOD")
  2575. moodCategories = {}
  2576. for k in Mood.moodcats:
  2577. moodCategories[k] = moodMenu.addMenu(k.upper())
  2578. self.moodactions = {}
  2579. for (i,m) in enumerate(Mood.moods):
  2580. maction = QtGui.QAction(m.upper(), self)
  2581. mobj = PesterMoodAction(i, self.widget.moods.updateMood)
  2582. self.trayicon.connect(maction, QtCore.SIGNAL('triggered()'),
  2583. mobj, QtCore.SLOT('updateMood()'))
  2584. self.moodactions[i] = mobj
  2585. moodCategories[Mood.revmoodcats[m]].addAction(maction)
  2586. miniAction = QtGui.QAction("MINIMIZE", self)
  2587. self.trayicon.connect(miniAction, QtCore.SIGNAL('triggered()'),
  2588. self.widget, QtCore.SLOT('showMinimized()'))
  2589. exitAction = QtGui.QAction("EXIT", self)
  2590. self.trayicon.connect(exitAction, QtCore.SIGNAL('triggered()'),
  2591. self.widget, QtCore.SLOT('close()'))
  2592. self.traymenu.addAction(miniAction)
  2593. self.traymenu.addAction(exitAction)
  2594. self.trayicon.setContextMenu(self.traymenu)
  2595. self.trayicon.show()
  2596. self.trayicon.connect(self.trayicon,
  2597. QtCore.SIGNAL('activated(QSystemTrayIcon::ActivationReason)'),
  2598. self.widget,
  2599. QtCore.SLOT('systemTrayActivated(QSystemTrayIcon::ActivationReason)'))
  2600. self.trayicon.connect(self.widget,
  2601. QtCore.SIGNAL('trayIconSignal(int)'),
  2602. self.trayicon,
  2603. QtCore.SLOT('changeTrayIcon(int)'))
  2604. self.trayicon.connect(self.widget,
  2605. QtCore.SIGNAL('closeToTraySignal()'),
  2606. self,
  2607. QtCore.SLOT('trayiconShow()'))
  2608. self.trayicon.connect(self.widget,
  2609. QtCore.SIGNAL('closeSignal()'),
  2610. self.trayicon,
  2611. QtCore.SLOT('mainWindowClosed()'))
  2612. self.connect(self.trayicon,
  2613. QtCore.SIGNAL('messageClicked()'),
  2614. self,
  2615. QtCore.SLOT('trayMessageClick()'))
  2616. self.attempts = 0
  2617. self.irc = PesterIRC(self.widget.config, self.widget)
  2618. self.connectWidgets(self.irc, self.widget)
  2619. self.connect(self.widget, QtCore.SIGNAL('gainAttention(QWidget*)'),
  2620. self, QtCore.SLOT('alertWindow(QWidget*)'))
  2621. # 0 Once a day
  2622. # 1 Once a week
  2623. # 2 Only on start
  2624. # 3 Never
  2625. check = self.widget.config.checkForUpdates()
  2626. if check == 2:
  2627. self.runUpdateSlot()
  2628. elif check == 0:
  2629. seconds = 60 * 60 * 24
  2630. if int(time()) - self.widget.config.lastUCheck() < seconds:
  2631. seconds -= int(time()) - self.widget.config.lastUCheck()
  2632. if seconds < 0: seconds = 0
  2633. QtCore.QTimer.singleShot(1000*seconds, self, QtCore.SLOT('runUpdateSlot()'))
  2634. elif check == 1:
  2635. seconds = 60 * 60 * 24 * 7
  2636. if int(time()) - self.widget.config.lastUCheck() < seconds:
  2637. seconds -= int(time()) - self.widget.config.lastUCheck()
  2638. if seconds < 0: seconds = 0
  2639. QtCore.QTimer.singleShot(1000*seconds, self, QtCore.SLOT('runUpdateSlot()'))
  2640. @QtCore.pyqtSlot()
  2641. def runUpdateSlot(self):
  2642. q = Queue.Queue(1)
  2643. s = threading.Thread(target=version.updateCheck, args=(q,))
  2644. w = threading.Thread(target=self.showUpdate, args=(q,))
  2645. w.start()
  2646. s.start()
  2647. self.widget.config.set('lastUCheck', int(time()))
  2648. check = self.widget.config.checkForUpdates()
  2649. if check == 0:
  2650. seconds = 60 * 60 * 24
  2651. elif check == 1:
  2652. seconds = 60 * 60 * 24 * 7
  2653. else:
  2654. return
  2655. QtCore.QTimer.singleShot(1000*seconds, self, QtCore.SLOT('runUpdateSlot()'))
  2656. @QtCore.pyqtSlot(QtGui.QWidget)
  2657. def alertWindow(self, widget):
  2658. self.app.alert(widget)
  2659. @QtCore.pyqtSlot()
  2660. def trayiconShow(self):
  2661. self.trayicon.show()
  2662. if self.widget.config.trayMessage():
  2663. self.trayicon.showMessage("Pesterchum", "Pesterchum is still running in the system tray.\n\
  2664. Right click to close it.\n\
  2665. Click this message to never see this again.")
  2666. @QtCore.pyqtSlot()
  2667. def trayMessageClick(self):
  2668. self.widget.config.set('traymsg', False)
  2669. widget2irc = [('sendMessage(QString, QString)',
  2670. 'sendMessage(QString, QString)'),
  2671. ('sendNotice(QString, QString)',
  2672. 'sendNotice(QString, QString)'),
  2673. ('newConvoStarted(QString, bool)',
  2674. 'startConvo(QString, bool)'),
  2675. ('convoClosed(QString)',
  2676. 'endConvo(QString)'),
  2677. ('profileChanged()',
  2678. 'updateProfile()'),
  2679. ('moodRequest(PyQt_PyObject)',
  2680. 'getMood(PyQt_PyObject)'),
  2681. ('moodsRequest(PyQt_PyObject)',
  2682. 'getMoods(PyQt_PyObject)'),
  2683. ('moodUpdated()', 'updateMood()'),
  2684. ('mycolorUpdated()','updateColor()'),
  2685. ('blockedChum(QString)', 'blockedChum(QString)'),
  2686. ('unblockedChum(QString)', 'unblockedChum(QString)'),
  2687. ('requestNames(QString)','requestNames(QString)'),
  2688. ('requestChannelList()', 'requestChannelList()'),
  2689. ('joinChannel(QString)', 'joinChannel(QString)'),
  2690. ('leftChannel(QString)', 'leftChannel(QString)'),
  2691. ('kickUser(QString, QString)',
  2692. 'kickUser(QString, QString)'),
  2693. ('setChannelMode(QString, QString, QString)',
  2694. 'setChannelMode(QString, QString, QString)'),
  2695. ('channelNames(QString)',
  2696. 'channelNames(QString)'),
  2697. ('inviteChum(QString, QString)',
  2698. 'inviteChum(QString, QString)'),
  2699. ('pingServer()', 'pingServer()'),
  2700. ('setAway(bool)', 'setAway(bool)'),
  2701. ('killSomeQuirks(QString, QString)',
  2702. 'killSomeQuirks(QString, QString)'),
  2703. ('reconnectIRC()', 'reconnectIRC()')
  2704. ]
  2705. # IRC --> Main window
  2706. irc2widget = [('connected()', 'connected()'),
  2707. ('moodUpdated(QString, PyQt_PyObject)',
  2708. 'updateMoodSlot(QString, PyQt_PyObject)'),
  2709. ('colorUpdated(QString, QColor)',
  2710. 'updateColorSlot(QString, QColor)'),
  2711. ('messageReceived(QString, QString)',
  2712. 'deliverMessage(QString, QString)'),
  2713. ('memoReceived(QString, QString, QString)',
  2714. 'deliverMemo(QString, QString, QString)'),
  2715. ('noticeReceived(QString, QString)',
  2716. 'deliverNotice(QString, QString)'),
  2717. ('inviteReceived(QString, QString)',
  2718. 'deliverInvite(QString, QString)'),
  2719. ('nickCollision(QString, QString)',
  2720. 'nickCollision(QString, QString)'),
  2721. ('myHandleChanged(QString)',
  2722. 'myHandleChanged(QString)'),
  2723. ('namesReceived(QString, PyQt_PyObject)',
  2724. 'updateNames(QString, PyQt_PyObject)'),
  2725. ('userPresentUpdate(QString, QString, QString)',
  2726. 'userPresentUpdate(QString, QString, QString)'),
  2727. ('channelListReceived(PyQt_PyObject)',
  2728. 'updateChannelList(PyQt_PyObject)'),
  2729. ('timeCommand(QString, QString, QString)',
  2730. 'timeCommand(QString, QString, QString)'),
  2731. ('chanInviteOnly(QString)',
  2732. 'chanInviteOnly(QString)'),
  2733. ('modesUpdated(QString, QString)',
  2734. 'modesUpdated(QString, QString)'),
  2735. ('cannotSendToChan(QString, QString)',
  2736. 'cannotSendToChan(QString, QString)'),
  2737. ('tooManyPeeps()',
  2738. 'tooManyPeeps()'),
  2739. ('quirkDisable(QString, QString, QString)',
  2740. 'quirkDisable(QString, QString, QString)')
  2741. ]
  2742. def connectWidgets(self, irc, widget):
  2743. self.connect(irc, QtCore.SIGNAL('finished()'),
  2744. self, QtCore.SLOT('restartIRC()'))
  2745. self.connect(irc, QtCore.SIGNAL('connected()'),
  2746. self, QtCore.SLOT('connected()'))
  2747. for c in self.widget2irc:
  2748. self.connect(widget, QtCore.SIGNAL(c[0]),
  2749. irc, QtCore.SLOT(c[1]))
  2750. for c in self.irc2widget:
  2751. self.connect(irc, QtCore.SIGNAL(c[0]),
  2752. widget, QtCore.SLOT(c[1]))
  2753. def disconnectWidgets(self, irc, widget):
  2754. for c in self.widget2irc:
  2755. self.disconnect(widget, QtCore.SIGNAL(c[0]),
  2756. irc, QtCore.SLOT(c[1]))
  2757. for c in self.irc2widget:
  2758. self.disconnect(irc, QtCore.SIGNAL(c[0]),
  2759. widget, QtCore.SLOT(c[1]))
  2760. self.disconnect(irc, QtCore.SIGNAL('connected()'),
  2761. self, QtCore.SLOT('connected()'))
  2762. self.disconnect(self.irc, QtCore.SIGNAL('finished()'),
  2763. self, QtCore.SLOT('restartIRC()'))
  2764. def showUpdate(self, q):
  2765. new_url = q.get()
  2766. if new_url[0]:
  2767. self.widget.pcUpdate.emit(new_url[0], new_url[1])
  2768. q.task_done()
  2769. def showLoading(self, widget, msg="CONN3CT1NG"):
  2770. self.widget.show()
  2771. if len(msg) > 60:
  2772. newmsg = []
  2773. while len(msg) > 60:
  2774. s = msg.rfind(" ", 0, 60)
  2775. if s == -1:
  2776. break
  2777. newmsg.append(msg[:s])
  2778. newmsg.append("\n")
  2779. msg = msg[s+1:]
  2780. newmsg.append(msg)
  2781. msg = "".join(newmsg)
  2782. if hasattr(self.widget, 'loadingscreen') and widget.loadingscreen:
  2783. widget.loadingscreen.loadinglabel.setText(msg)
  2784. if self.reconnectok:
  2785. widget.loadingscreen.showReconnect()
  2786. else:
  2787. widget.loadingscreen.hideReconnect()
  2788. else:
  2789. widget.loadingscreen = LoadingScreen(widget)
  2790. widget.loadingscreen.loadinglabel.setText(msg)
  2791. self.connect(widget.loadingscreen, QtCore.SIGNAL('rejected()'),
  2792. widget, QtCore.SLOT('close()'))
  2793. self.connect(self.widget.loadingscreen, QtCore.SIGNAL('tryAgain()'),
  2794. self, QtCore.SLOT('tryAgain()'))
  2795. if hasattr(self, 'irc') and self.irc.registeredIRC:
  2796. return
  2797. if self.reconnectok:
  2798. widget.loadingscreen.showReconnect()
  2799. else:
  2800. widget.loadingscreen.hideReconnect()
  2801. status = widget.loadingscreen.exec_()
  2802. if status == QtGui.QDialog.Rejected:
  2803. sys.exit(0)
  2804. else:
  2805. if self.widget.tabmemo:
  2806. for c in self.widget.tabmemo.convos:
  2807. self.irc.joinChannel(c)
  2808. else:
  2809. for c in self.widget.memos.values():
  2810. self.irc.joinChannel(c.channel)
  2811. return True
  2812. @QtCore.pyqtSlot()
  2813. def connected(self):
  2814. self.attempts = 0
  2815. @QtCore.pyqtSlot()
  2816. def tryAgain(self):
  2817. if not self.reconnectok:
  2818. return
  2819. if self.widget.loadingscreen:
  2820. self.widget.loadingscreen.done(QtGui.QDialog.Accepted)
  2821. self.widget.loadingscreen = None
  2822. self.attempts += 1
  2823. if hasattr(self, 'irc') and self.irc:
  2824. self.irc.reconnectIRC()
  2825. self.irc.quit()
  2826. else:
  2827. self.restartIRC()
  2828. @QtCore.pyqtSlot()
  2829. def restartIRC(self):
  2830. if hasattr(self, 'irc') and self.irc:
  2831. self.disconnectWidgets(self.irc, self.widget)
  2832. stop = self.irc.stopIRC
  2833. del self.irc
  2834. else:
  2835. stop = None
  2836. if stop is None:
  2837. self.irc = PesterIRC(self.widget.config, self.widget)
  2838. self.connectWidgets(self.irc, self.widget)
  2839. self.irc.start()
  2840. if self.attempts == 1:
  2841. msg = "R3CONN3CT1NG"
  2842. elif self.attempts > 1:
  2843. msg = "R3CONN3CT1NG %d" % (self.attempts)
  2844. else:
  2845. msg = "CONN3CT1NG"
  2846. self.reconnectok = False
  2847. self.showLoading(self.widget, msg)
  2848. else:
  2849. self.reconnectok = True
  2850. self.showLoading(self.widget, "F41L3D: %s" % stop)
  2851. def oppts(self, argv):
  2852. options = {}
  2853. try:
  2854. opts, args = getopt.getopt(argv, "s:p:", ["server=", "port=", "advanced", "no-honk"])
  2855. except getopt.GetoptError:
  2856. return options
  2857. for opt, arg in opts:
  2858. if opt in ("-s", "--server"):
  2859. options["server"] = arg
  2860. elif opt in ("-p", "--port"):
  2861. options["port"] = arg
  2862. elif opt in ("--advanced"):
  2863. options["advanced"] = True
  2864. elif opt in ("--no-honk"):
  2865. options["honk"] = False
  2866. return options
  2867. def run(self):
  2868. self.irc.start()
  2869. self.reconnectok = False
  2870. self.showLoading(self.widget)
  2871. sys.exit(self.app.exec_())
  2872. pesterchum = MainProgram()
  2873. pesterchum.run()