291 lines
13 KiB
Python
Executable File
291 lines
13 KiB
Python
Executable File
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2011 Sebastien Helleu <flashcode@flashtux.org>
|
|
#
|
|
# This file is part of QWeeChat, a Qt remote GUI for WeeChat.
|
|
#
|
|
# QWeeChat is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# QWeeChat is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with QWeeChat. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
#
|
|
# QWeeChat - WeeChat remote GUI using Qt toolkit.
|
|
# (this script requires WeeChat 0.3.7 or newer, running on local or remote host)
|
|
#
|
|
# History:
|
|
#
|
|
# 2011-05-27, Sebastien Helleu <flashcode@flashtux.org>:
|
|
# start dev
|
|
#
|
|
|
|
import sys, struct
|
|
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'
|
|
AUTHOR = 'Sébastien Helleu'
|
|
AUTHOR_MAIL= 'flashcode@flashtux.org'
|
|
WEECHAT_SITE = 'http://www.weechat.org/'
|
|
|
|
|
|
class MainWindow(QtGui.QMainWindow):
|
|
"""Main window."""
|
|
|
|
def __init__(self, *args):
|
|
apply(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)
|
|
|
|
# network
|
|
self.network = Network()
|
|
self.network.statusChanged.connect(self.network_status_changed)
|
|
self.network.messageFromWeechat.connect(self.network_message_from_weechat)
|
|
|
|
# list of buffers
|
|
self.list_buffers = BufferListWidget()
|
|
self.list_buffers.currentRowChanged.connect(self.buffer_switch)
|
|
|
|
# default buffer
|
|
self.buffers = [Buffer()]
|
|
self.stacked_buffers = QtGui.QStackedWidget()
|
|
self.stacked_buffers.addWidget(self.buffers[0].widget)
|
|
|
|
# splitter with buffers + chat/input
|
|
splitter = QtGui.QSplitter()
|
|
splitter.addWidget(self.list_buffers)
|
|
splitter.addWidget(self.stacked_buffers)
|
|
|
|
self.setCentralWidget(splitter)
|
|
|
|
if self.config.getboolean('look', 'statusbar'):
|
|
self.statusBar().visible = True
|
|
|
|
# actions for menu and toolbar
|
|
actions_def = {'connect' : ['network-connect.png', 'Connect to WeeChat', 'Ctrl+O', self.open_connection_dialog],
|
|
'disconnect' : ['network-disconnect.png', 'Disconnect from WeeChat', 'Ctrl+D', self.network.disconnect_weechat],
|
|
'debug' : ['edit-find.png', 'Debug console window', 'Ctrl+B', self.open_debug_dialog],
|
|
'preferences': ['preferences-other.png', 'Preferences', 'Ctrl+P', self.open_preferences_dialog],
|
|
'about' : ['help-about.png', 'About', 'Ctrl+H', self.open_about_dialog],
|
|
'quit' : ['application-exit.png', 'Quit application', 'Ctrl+Q', self.close],
|
|
}
|
|
self.actions = {}
|
|
for name, action in actions_def.iteritems():
|
|
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].triggered.connect(action[3])
|
|
|
|
# menu
|
|
self.menu = self.menuBar()
|
|
menu_file = self.menu.addMenu('&File')
|
|
menu_file.addActions([self.actions['connect'], self.actions['disconnect'],
|
|
self.actions['preferences'], self.actions['quit']])
|
|
menu_window = self.menu.addMenu('&Window')
|
|
menu_window.addAction(self.actions['debug'])
|
|
menu_help = self.menu.addMenu('&Help')
|
|
menu_help.addAction(self.actions['about'])
|
|
self.network_status = QtGui.QLabel()
|
|
self.network_status.setFixedHeight(20)
|
|
self.network_status.setFixedWidth(200)
|
|
self.network_status.setContentsMargins(0, 0, 10, 0)
|
|
self.network_status.setAlignment(QtCore.Qt.AlignRight)
|
|
if hasattr(self.menu, 'setCornerWidget'):
|
|
self.menu.setCornerWidget(self.network_status, QtCore.Qt.TopRightCorner)
|
|
self.network_status_set(self.network.status_disconnected, None)
|
|
|
|
# toolbar
|
|
toolbar = self.addToolBar('toolBar')
|
|
toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
|
|
toolbar.addActions([self.actions['connect'], self.actions['disconnect'],
|
|
self.actions['debug'], self.actions['preferences'],
|
|
self.actions['about'], self.actions['quit']])
|
|
|
|
self.buffers[0].widget.input.setFocus()
|
|
|
|
# open debug dialog
|
|
if self.config.getboolean('look', 'debug'):
|
|
self.open_debug_dialog()
|
|
|
|
# auto-connect to relay
|
|
if self.config.getboolean('relay', 'autoconnect'):
|
|
self.network.connect_weechat(self.config.get('relay', 'server'),
|
|
self.config.get('relay', 'port'),
|
|
self.config.get('relay', 'password'))
|
|
|
|
self.show()
|
|
|
|
def buffer_switch(self, index):
|
|
if index >= 0:
|
|
self.stacked_buffers.setCurrentIndex(index)
|
|
self.stacked_buffers.widget(index).input.setFocus()
|
|
|
|
def buffer_input(self, full_name, text):
|
|
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')
|
|
|
|
def open_preferences_dialog(self):
|
|
pass # TODO
|
|
|
|
def open_debug_dialog(self):
|
|
self.debug_dialog.chat.scroll_bottom()
|
|
self.debug_dialog.show()
|
|
|
|
def debug_input_text_sent(self, text):
|
|
if self.network.is_connected():
|
|
text = str(text)
|
|
pos = text.find(')')
|
|
if text.startswith('(') and pos >= 0:
|
|
text = '(debug_%s)%s' % (text[1:pos], text[pos+1:])
|
|
else:
|
|
text = '(debug) %s' % text
|
|
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()
|
|
|
|
def open_about_dialog(self):
|
|
messages = ['<b>%s</b> %s' % (NAME, VERSION),
|
|
'© 2011 %s <<a href="mailto:%s">%s</a>>' % (AUTHOR, AUTHOR_MAIL, AUTHOR_MAIL),
|
|
'',
|
|
'WeeChat site: <a href="%s">%s</a>' % (WEECHAT_SITE, WEECHAT_SITE),
|
|
'']
|
|
self.about_dialog = AboutDialog(NAME, messages, self)
|
|
|
|
def open_connection_dialog(self):
|
|
values = {}
|
|
for option in ('server', 'port', 'password'):
|
|
values[option] = self.config.get('relay', option)
|
|
self.connection_dialog = ConnectionDialog(values, self)
|
|
self.connection_dialog.dialog_buttons.accepted.connect(self.connect_weechat)
|
|
|
|
def connect_weechat(self):
|
|
self.network.connect_weechat(self.connection_dialog.fields['server'].text(),
|
|
self.connection_dialog.fields['port'].text(),
|
|
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.network_status_set(status, extra)
|
|
|
|
def network_status_set(self, status, extra):
|
|
pal = self.network_status.palette()
|
|
if self.network.is_connected():
|
|
pal.setColor(self.network_status.foregroundRole(), QtGui.QColor('green'))
|
|
else:
|
|
pal.setColor(self.network_status.foregroundRole(), QtGui.QColor('#aa0000'))
|
|
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()))
|
|
else:
|
|
self.network_status.setText(status.capitalize())
|
|
if status == self.network.status_disconnected:
|
|
self.actions['connect'].setEnabled(True)
|
|
self.actions['disconnect'].setEnabled(False)
|
|
else:
|
|
self.actions['connect'].setEnabled(False)
|
|
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)
|
|
|
|
def parse_message(self, message):
|
|
if message.msgid.startswith('debug'):
|
|
self.debug_dialog.chat.display(0, '', '(debug message, ignored)')
|
|
return
|
|
if message.msgid == 'listbuffers':
|
|
for obj in message.objects:
|
|
if obj.objtype == 'hda' and obj.value['path'][-1] == 'buffer':
|
|
self.list_buffers.clear()
|
|
while self.stacked_buffers.count() > 0:
|
|
buf = self.stacked_buffers.widget(0)
|
|
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)
|
|
self.list_buffers.setCurrentRow(0)
|
|
self.buffers[0].widget.input.setFocus()
|
|
elif message.msgid == 'listlines':
|
|
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':
|
|
for obj in message.objects:
|
|
if obj.objtype == 'hda' and obj.value['path'][-1] == 'nick_group':
|
|
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'])
|
|
|
|
def closeEvent(self, event):
|
|
self.network.disconnect_weechat()
|
|
self.debug_dialog.close()
|
|
config.write(self.config)
|
|
QtGui.QMainWindow.closeEvent(self, event)
|
|
|
|
|
|
app = QtGui.QApplication(sys.argv)
|
|
app.setStyle(QtGui.QStyleFactory.create('Cleanlooks'))
|
|
main = MainWindow()
|
|
sys.exit(app.exec_())
|