]> jfr.im git - irc/weechat/qweechat.git/blobdiff - src/qweechat/qweechat.py
Set variable "uncompressed" in WeeChat decoded message when compression is off (using...
[irc/weechat/qweechat.git] / src / qweechat / qweechat.py
index 91512afffa87ed6c5783a8bec60eea385b7c7398..604503c7998e4a0b227bdc7c0c587284b1e7064c 100755 (executable)
@@ -1,7 +1,9 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 #
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2011 Sebastien Helleu <flashcode@flashtux.org>
+# qweechat.py - WeeChat remote GUI using Qt toolkit
+#
+# Copyright (C) 2011-2013 Sebastien Helleu <flashcode@flashtux.org>
 #
 # This file is part of QWeeChat, a Qt remote GUI for WeeChat.
 #
 #
 # 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:
 #
 #
 # History:
 #
 #     start dev
 #
 
 #     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 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
 
 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/'
 
 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):
 
 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.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.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)
         # 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 = {}
                        '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])
             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'),
         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()
                                          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)
         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 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):
     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():
 
     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
                 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.network.send_to_weechat(text + '\n')
-            self.debug_dialog.chat.display(0, '<==', text, color='red')
 
     def debug_dialog_closed(self, result):
 
     def debug_dialog_closed(self, result):
-        self.debug_dialog.hide()
+        self.debug_dialog = None
 
     def open_about_dialog(self):
         messages = ['<b>%s</b> %s' % (NAME, VERSION),
 
     def open_about_dialog(self):
         messages = ['<b>%s</b> %s' % (NAME, VERSION),
-                    '&copy; 2011 %s &lt;<a href="mailto:%s">%s</a>&gt;' % (AUTHOR, AUTHOR_MAIL, AUTHOR_MAIL),
+                    '&copy; 2011-2013 %s &lt;<a href="mailto:%s">%s</a>&gt;' % (AUTHOR, AUTHOR_MAIL, AUTHOR_MAIL),
+                    '',
+                    'Running with %s' % ('PySide' if qt_compat.uses_pyside else 'PyQt4'),
                     '',
                     'WeeChat site: <a href="%s">%s</a>' % (WEECHAT_SITE, WEECHAT_SITE),
                     '']
                     '',
                     'WeeChat site: <a href="%s">%s</a>' % (WEECHAT_SITE, WEECHAT_SITE),
                     '']
@@ -184,7 +199,7 @@ class MainWindow(QtGui.QMainWindow):
 
     def open_connection_dialog(self):
         values = {}
 
     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)
             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(),
     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.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()
         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'))
             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.setPalette(pal)
         icon = self.network.status_icon(status)
         if icon:
-            self.network_status.setText('<img src="data/icons/%s"> %s' % (icon, status.capitalize()))
+            self.network_status.setText('<img src="data/icons/%s"> %s' % (icon, status.capitalize() + ssl))
         else:
             self.network_status.setText(status.capitalize())
         if status == self.network.status_disconnected:
         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.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'):
 
     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:
             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.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()
                     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']:
             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:
             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']:
                     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()
 
     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'))
         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_())
 main = MainWindow()
 sys.exit(app.exec_())