X-Git-Url: https://jfr.im/git/irc/weechat/qweechat.git/blobdiff_plain/7dcf23b19547e37dbadd98aaed99ce4656471dbe..9587cb0afb338b7f7a56b6c2eedada509c96f102:/src/qweechat/qweechat.py diff --git a/src/qweechat/qweechat.py b/src/qweechat/qweechat.py index 91512af..604503c 100755 --- a/src/qweechat/qweechat.py +++ b/src/qweechat/qweechat.py @@ -1,7 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # -# Copyright (C) 2011 Sebastien Helleu +# qweechat.py - WeeChat remote GUI using Qt toolkit +# +# Copyright (C) 2011-2013 Sebastien Helleu # # This file is part of QWeeChat, a Qt remote GUI for WeeChat. # @@ -20,8 +22,7 @@ # # -# QWeeChat - WeeChat remote GUI using Qt toolkit. -# (this script requires WeeChat 0.3.7 or newer, running on local or remote host) +# This script requires WeeChat 0.3.7 or newer, running on local or remote host. # # History: # @@ -29,41 +30,42 @@ # start dev # -import sys, struct +import sys, struct, traceback import qt_compat QtCore = qt_compat.import_module('QtCore') QtGui = qt_compat.import_module('QtGui') import config import weechat.protocol as protocol -import weechat.color as color from network import Network from connection import ConnectionDialog from buffer import BufferListWidget, Buffer from debug import DebugDialog from about import AboutDialog -NAME = 'qweechat' -VERSION = '0.1-dev' +NAME = 'QWeeChat' +VERSION = '0.0.1-dev' AUTHOR = 'Sébastien Helleu' AUTHOR_MAIL= 'flashcode@flashtux.org' WEECHAT_SITE = 'http://www.weechat.org/' +# number of lines in buffer for debug window +DEBUG_NUM_LINES = 50 + class MainWindow(QtGui.QMainWindow): """Main window.""" def __init__(self, *args): - apply(QtGui.QMainWindow.__init__, (self,) + args) + QtGui.QMainWindow.__init__(*(self,) + args) self.config = config.read() - self.debug_dialog = DebugDialog(self) - self.debug_dialog.input.textSent.connect(self.debug_input_text_sent) - self.debug_dialog.finished.connect(self.debug_dialog_closed) - self.resize(1000, 600) self.setWindowTitle(NAME) + self.debug_dialog = None + self.debug_lines = [] + # network self.network = Network() self.network.statusChanged.connect(self.network_status_changed) @@ -97,7 +99,7 @@ class MainWindow(QtGui.QMainWindow): 'quit' : ['application-exit.png', 'Quit application', 'Ctrl+Q', self.close], } self.actions = {} - for name, action in actions_def.iteritems(): + for name, action in list(actions_def.items()): self.actions[name] = QtGui.QAction(QtGui.QIcon('data/icons/%s' % action[0]), name.capitalize(), self) self.actions[name].setStatusTip(action[1]) self.actions[name].setShortcut(action[2]) @@ -138,6 +140,7 @@ class MainWindow(QtGui.QMainWindow): if self.config.getboolean('relay', 'autoconnect'): self.network.connect_weechat(self.config.get('relay', 'server'), self.config.get('relay', 'port'), + self.config.get('relay', 'ssl') == 'on', self.config.get('relay', 'password')) self.show() @@ -151,14 +154,24 @@ class MainWindow(QtGui.QMainWindow): if self.network.is_connected(): message = 'input %s %s\n' % (full_name, text) self.network.send_to_weechat(message) - self.debug_dialog.chat.display(0, '<==', message, color='red') + self.debug_display(0, '<==', message, forcecolor='#AA0000') def open_preferences_dialog(self): pass # TODO + def debug_display(self, *args, **kwargs): + self.debug_lines.append((args, kwargs)) + self.debug_lines = self.debug_lines[-DEBUG_NUM_LINES:] + if self.debug_dialog: + self.debug_dialog.chat.display(*args, **kwargs) + def open_debug_dialog(self): - self.debug_dialog.chat.scroll_bottom() - self.debug_dialog.show() + if not self.debug_dialog: + self.debug_dialog = DebugDialog(self) + self.debug_dialog.input.textSent.connect(self.debug_input_text_sent) + self.debug_dialog.finished.connect(self.debug_dialog_closed) + self.debug_dialog.display_lines(self.debug_lines) + self.debug_dialog.chat.scroll_bottom() def debug_input_text_sent(self, text): if self.network.is_connected(): @@ -168,15 +181,17 @@ class MainWindow(QtGui.QMainWindow): text = '(debug_%s)%s' % (text[1:pos], text[pos+1:]) else: text = '(debug) %s' % text + self.debug_display(0, '<==', text, forcecolor='#AA0000') self.network.send_to_weechat(text + '\n') - self.debug_dialog.chat.display(0, '<==', text, color='red') def debug_dialog_closed(self, result): - self.debug_dialog.hide() + self.debug_dialog = None def open_about_dialog(self): messages = ['%s %s' % (NAME, VERSION), - '© 2011 %s <%s>' % (AUTHOR, AUTHOR_MAIL, AUTHOR_MAIL), + '© 2011-2013 %s <%s>' % (AUTHOR, AUTHOR_MAIL, AUTHOR_MAIL), + '', + 'Running with %s' % ('PySide' if qt_compat.uses_pyside else 'PyQt4'), '', 'WeeChat site: %s' % (WEECHAT_SITE, WEECHAT_SITE), ''] @@ -184,7 +199,7 @@ class MainWindow(QtGui.QMainWindow): def open_connection_dialog(self): values = {} - for option in ('server', 'port', 'password'): + for option in ('server', 'port', 'ssl', 'password'): values[option] = self.config.get('relay', option) self.connection_dialog = ConnectionDialog(values, self) self.connection_dialog.dialog_buttons.accepted.connect(self.connect_weechat) @@ -192,25 +207,27 @@ class MainWindow(QtGui.QMainWindow): def connect_weechat(self): self.network.connect_weechat(self.connection_dialog.fields['server'].text(), self.connection_dialog.fields['port'].text(), + self.connection_dialog.fields['ssl'].isChecked(), self.connection_dialog.fields['password'].text()) self.connection_dialog.close() def network_status_changed(self, status, extra): if self.config.getboolean('look', 'statusbar'): self.statusBar().showMessage(status) - self.debug_dialog.chat.display(0, '', status, color='blue') + self.debug_display(0, '', status, forcecolor='#0000AA') self.network_status_set(status, extra) def network_status_set(self, status, extra): pal = self.network_status.palette() - if self.network.is_connected(): + if status == self.network.status_connected: pal.setColor(self.network_status.foregroundRole(), QtGui.QColor('green')) else: pal.setColor(self.network_status.foregroundRole(), QtGui.QColor('#aa0000')) + ssl = ' (SSL)' if status != self.network.status_disconnected and self.network.is_ssl() else '' self.network_status.setPalette(pal) icon = self.network.status_icon(status) if icon: - self.network_status.setText(' %s' % (icon, status.capitalize())) + self.network_status.setText(' %s' % (icon, status.capitalize() + ssl)) else: self.network_status.setText(status.capitalize()) if status == self.network.status_disconnected: @@ -221,24 +238,28 @@ class MainWindow(QtGui.QMainWindow): self.actions['disconnect'].setEnabled(True) def network_message_from_weechat(self, message): - self.debug_dialog.chat.display(0, '==>', - 'message (%d bytes):\n%s' - % (len(message), protocol.hex_and_ascii(message, 20)), - color='green') - proto = protocol.Protocol() - message = proto.decode(str(message)) - if message.uncompressed: - self.debug_dialog.chat.display(0, '==>', - 'message uncompressed (%d bytes):\n%s' - % (message.size_uncompressed, - protocol.hex_and_ascii(message.uncompressed, 20)), - color='green') - self.debug_dialog.chat.display(0, '', 'Message: %s' % message) - self.parse_message(message) + self.debug_display(0, '==>', + 'message (%d bytes):\n%s' + % (len(message), protocol.hex_and_ascii(message, 20)), + forcecolor='#008800') + try: + proto = protocol.Protocol() + message = proto.decode(str(message)) + if message.uncompressed: + self.debug_display(0, '==>', + 'message uncompressed (%d bytes):\n%s' + % (message.size_uncompressed, + protocol.hex_and_ascii(message.uncompressed, 20)), + forcecolor='#008800') + self.debug_display(0, '', 'Message: %s' % message) + self.parse_message(message) + except: + print('Error while decoding message from WeeChat:\n%s' % traceback.format_exc()) + self.network.disconnect_weechat() def parse_message(self, message): if message.msgid.startswith('debug'): - self.debug_dialog.chat.display(0, '', '(debug message, ignored)') + self.debug_display(0, '', '(debug message, ignored)') return if message.msgid == 'listbuffers': for obj in message.objects: @@ -249,42 +270,138 @@ class MainWindow(QtGui.QMainWindow): self.stacked_buffers.removeWidget(buf) self.buffers = [] for item in obj.value['items']: - self.list_buffers.addItem('%d. %s' % (item['number'], item['full_name'])) - self.buffers.append(Buffer(item)) - self.stacked_buffers.addWidget(self.buffers[-1].widget) - self.buffers[-1].bufferInput.connect(self.buffer_input) - self.buffers[-1].widget.input.bufferSwitchPrev.connect(self.list_buffers.switch_prev_buffer) - self.buffers[-1].widget.input.bufferSwitchNext.connect(self.list_buffers.switch_next_buffer) + buf = self.create_buffer(item) + self.insert_buffer(len(self.buffers), buf) self.list_buffers.setCurrentRow(0) self.buffers[0].widget.input.setFocus() - elif message.msgid == 'listlines': + elif message.msgid in ('listlines', '_buffer_line_added'): for obj in message.objects: if obj.objtype == 'hda' and obj.value['path'][-1] == 'line_data': for item in obj.value['items']: - pointer = item['__path'][0] - buf = filter(lambda b: b.pointer() == pointer, self.buffers) - if buf: - buf[0].widget.chat.display(item['date'], - color.remove(item['prefix']), - color.remove(item['message'])) - elif message.msgid == 'nicklist': + if message.msgid == 'listlines': + ptrbuf = item['__path'][0] + else: + ptrbuf = item['buffer'] + index = [i for i, b in enumerate(self.buffers) if b.pointer() == ptrbuf] + if index: + self.buffers[index[0]].widget.chat.display(item['date'], + item['prefix'], + item['message']) + elif message.msgid in ('_nicklist', 'nicklist'): + buffer_refresh = {} + for obj in message.objects: + if obj.objtype == 'hda' and obj.value['path'][-1] == 'nicklist_item': + group = '__root' + for item in obj.value['items']: + index = [i for i, b in enumerate(self.buffers) if b.pointer() == item['__path'][0]] + if index: + if not index[0] in buffer_refresh: + self.buffers[index[0]].nicklist = {} + buffer_refresh[index[0]] = True + if item['group']: + group = item['name'] + self.buffers[index[0]].nicklist_add_item(group, item['group'], item['prefix'], item['name'], item['visible']) + for index in buffer_refresh: + self.buffers[index].nicklist_refresh() + elif message.msgid == '_nicklist_diff': + buffer_refresh = {} + for obj in message.objects: + if obj.objtype == 'hda' and obj.value['path'][-1] == 'nicklist_item': + group = '__root' + for item in obj.value['items']: + index = [i for i, b in enumerate(self.buffers) if b.pointer() == item['__path'][0]] + if index: + buffer_refresh[index[0]] = True + if item['_diff'] == ord('^'): + group = item['name'] + elif item['_diff'] == ord('+'): + self.buffers[index[0]].nicklist_add_item(group, item['group'], item['prefix'], item['name'], item['visible']) + elif item['_diff'] == ord('-'): + self.buffers[index[0]].nicklist_remove_item(group, item['group'], item['name']) + elif item['_diff'] == ord('*'): + self.buffers[index[0]].nicklist_update_item(group, item['group'], item['prefix'], item['name'], item['visible']) + for index in buffer_refresh: + self.buffers[index].nicklist_refresh() + elif message.msgid == '_buffer_opened': + for obj in message.objects: + if obj.objtype == 'hda' and obj.value['path'][-1] == 'buffer': + for item in obj.value['items']: + buf = self.create_buffer(item) + index = self.find_buffer_index_for_insert(item['next_buffer']) + self.insert_buffer(index, buf) + elif message.msgid.startswith('_buffer_'): for obj in message.objects: - if obj.objtype == 'hda' and obj.value['path'][-1] == 'nick_group': + if obj.objtype == 'hda' and obj.value['path'][-1] == 'buffer': for item in obj.value['items']: - if not item['group'] and item['visible']: - pointer = item['__path'][0] - buf = filter(lambda b: b.pointer() == pointer, self.buffers) - if buf: - buf[0].add_nick(item['prefix'], item['name']) + index = [i for i, b in enumerate(self.buffers) if b.pointer() == item['__path'][0]] + if index: + index = index[0] + if message.msgid == '_buffer_type_changed': + self.buffers[index].data['type'] = item['type'] + elif message.msgid in ('_buffer_moved', '_buffer_merged', '_buffer_unmerged'): + buf = self.buffers[index] + buf.data['number'] = item['number'] + self.remove_buffer(index) + index2 = self.find_buffer_index_for_insert(item['next_buffer']) + self.insert_buffer(index2, buf) + elif message.msgid == '_buffer_renamed': + self.buffers[index].data['full_name'] = item['full_name'] + self.buffers[index].data['short_name'] = item['short_name'] + elif message.msgid == '_buffer_title_changed': + self.buffers[index].data['title'] = item['title'] + self.buffers[index].update_title() + elif message.msgid.startswith('_buffer_localvar_'): + self.buffers[index].data['local_variables'] = item['local_variables'] + self.buffers[index].update_prompt() + elif message.msgid == '_buffer_closing': + self.remove_buffer(index) + elif message.msgid == '_upgrade': + self.network.desync_weechat() + elif message.msgid == '_upgrade_ended': + self.network.sync_weechat() + + def create_buffer(self, item): + buf = Buffer(item) + buf.bufferInput.connect(self.buffer_input) + buf.widget.input.bufferSwitchPrev.connect(self.list_buffers.switch_prev_buffer) + buf.widget.input.bufferSwitchNext.connect(self.list_buffers.switch_next_buffer) + return buf + + def insert_buffer(self, index, buf): + self.buffers.insert(index, buf) + self.list_buffers.insertItem(index, '%d. %s' % (buf.data['number'], buf.data['full_name'])) + self.stacked_buffers.insertWidget(index, buf.widget) + + def remove_buffer(self, index): + if self.list_buffers.currentRow == index and index > 0: + self.list_buffers.setCurrentRow(index - 1) + self.list_buffers.takeItem(index) + self.stacked_buffers.removeWidget(self.stacked_buffers.widget(index)) + self.buffers.pop(index) + + def find_buffer_index_for_insert(self, next_buffer): + index = -1 + if next_buffer == '0x0': + index = len(self.buffers) + else: + index = [i for i, b in enumerate(self.buffers) if b.pointer() == next_buffer] + if index: + index = index[0] + if index < 0: + print('Warning: unable to find position for buffer, using end of list by default') + index = len(self.buffers) + return index def closeEvent(self, event): self.network.disconnect_weechat() - self.debug_dialog.close() + if self.debug_dialog: + self.debug_dialog.close() config.write(self.config) QtGui.QMainWindow.closeEvent(self, event) app = QtGui.QApplication(sys.argv) app.setStyle(QtGui.QStyleFactory.create('Cleanlooks')) +app.setWindowIcon(QtGui.QIcon('data/icons/weechat_icon_32.png')) main = MainWindow() sys.exit(app.exec_())