2 # -*- coding: utf-8 -*-
4 # Copyright (C) 2011 Sebastien Helleu <flashcode@flashtux.org>
6 # This file is part of QWeeChat, a Qt remote GUI for WeeChat.
8 # QWeeChat is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # QWeeChat is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with QWeeChat. If not, see <http://www.gnu.org/licenses/>.
23 # QWeeChat - WeeChat remote GUI using Qt toolkit.
24 # (this script requires WeeChat 0.3.7 or newer, running on local or remote host)
28 # 2011-05-27, Sebastien Helleu <flashcode@flashtux.org>:
34 QtCore
= qt_compat
.import_module('QtCore')
35 QtGui
= qt_compat
.import_module('QtGui')
37 import weechat
.protocol
as protocol
38 import weechat
.color
as color
39 from network
import Network
40 from connection
import ConnectionDialog
41 from buffer import BufferListWidget
, Buffer
42 from debug
import DebugDialog
43 from about
import AboutDialog
47 AUTHOR
= 'Sébastien Helleu'
48 AUTHOR_MAIL
= 'flashcode@flashtux.org'
49 WEECHAT_SITE
= 'http://www.weechat.org/'
52 class MainWindow(QtGui
.QMainWindow
):
55 def __init__(self
, *args
):
56 apply(QtGui
.QMainWindow
.__init
__, (self
,) + args
)
58 self
.config
= config
.read()
60 self
.debug_dialog
= DebugDialog(self
)
61 self
.debug_dialog
.input.textSent
.connect(self
.debug_input_text_sent
)
62 self
.debug_dialog
.finished
.connect(self
.debug_dialog_closed
)
64 self
.resize(1000, 600)
65 self
.setWindowTitle(NAME
)
68 self
.network
= Network()
69 self
.network
.statusChanged
.connect(self
.network_status_changed
)
70 self
.network
.messageFromWeechat
.connect(self
.network_message_from_weechat
)
73 self
.list_buffers
= BufferListWidget()
74 self
.list_buffers
.currentRowChanged
.connect(self
.buffer_switch
)
77 self
.buffers
= [Buffer()]
78 self
.stacked_buffers
= QtGui
.QStackedWidget()
79 self
.stacked_buffers
.addWidget(self
.buffers
[0].widget
)
81 # splitter with buffers + chat/input
82 splitter
= QtGui
.QSplitter()
83 splitter
.addWidget(self
.list_buffers
)
84 splitter
.addWidget(self
.stacked_buffers
)
86 self
.setCentralWidget(splitter
)
88 if self
.config
.getboolean('look', 'statusbar'):
89 self
.statusBar().visible
= True
91 # actions for menu and toolbar
92 actions_def
= {'connect' : ['network-connect.png', 'Connect to WeeChat', 'Ctrl+O', self
.open_connection_dialog
],
93 'disconnect' : ['network-disconnect.png', 'Disconnect from WeeChat', 'Ctrl+D', self
.network
.disconnect_weechat
],
94 'debug' : ['edit-find.png', 'Debug console window', 'Ctrl+B', self
.open_debug_dialog
],
95 'preferences': ['preferences-other.png', 'Preferences', 'Ctrl+P', self
.open_preferences_dialog
],
96 'about' : ['help-about.png', 'About', 'Ctrl+H', self
.open_about_dialog
],
97 'quit' : ['application-exit.png', 'Quit application', 'Ctrl+Q', self
.close
],
100 for name
, action
in actions_def
.iteritems():
101 self
.actions
[name
] = QtGui
.QAction(QtGui
.QIcon('data/icons/%s' % action
[0]), name
.capitalize(), self
)
102 self
.actions
[name
].setStatusTip(action
[1])
103 self
.actions
[name
].setShortcut(action
[2])
104 self
.actions
[name
].triggered
.connect(action
[3])
107 self
.menu
= self
.menuBar()
108 menu_file
= self
.menu
.addMenu('&File')
109 menu_file
.addActions([self
.actions
['connect'], self
.actions
['disconnect'],
110 self
.actions
['preferences'], self
.actions
['quit']])
111 menu_window
= self
.menu
.addMenu('&Window')
112 menu_window
.addAction(self
.actions
['debug'])
113 menu_help
= self
.menu
.addMenu('&Help')
114 menu_help
.addAction(self
.actions
['about'])
115 self
.network_status
= QtGui
.QLabel()
116 self
.network_status
.setFixedHeight(20)
117 self
.network_status
.setFixedWidth(200)
118 self
.network_status
.setContentsMargins(0, 0, 10, 0)
119 self
.network_status
.setAlignment(QtCore
.Qt
.AlignRight
)
120 if hasattr(self
.menu
, 'setCornerWidget'):
121 self
.menu
.setCornerWidget(self
.network_status
, QtCore
.Qt
.TopRightCorner
)
122 self
.network_status_set(self
.network
.status_disconnected
, None)
125 toolbar
= self
.addToolBar('toolBar')
126 toolbar
.setToolButtonStyle(QtCore
.Qt
.ToolButtonTextUnderIcon
)
127 toolbar
.addActions([self
.actions
['connect'], self
.actions
['disconnect'],
128 self
.actions
['debug'], self
.actions
['preferences'],
129 self
.actions
['about'], self
.actions
['quit']])
131 self
.buffers
[0].widget
.input.setFocus()
134 if self
.config
.getboolean('look', 'debug'):
135 self
.open_debug_dialog()
137 # auto-connect to relay
138 if self
.config
.getboolean('relay', 'autoconnect'):
139 self
.network
.connect_weechat(self
.config
.get('relay', 'server'),
140 self
.config
.get('relay', 'port'),
141 self
.config
.get('relay', 'password'))
145 def buffer_switch(self
, index
):
147 self
.stacked_buffers
.setCurrentIndex(index
)
148 self
.stacked_buffers
.widget(index
).input.setFocus()
150 def buffer_input(self
, full_name
, text
):
151 if self
.network
.is_connected():
152 message
= 'input %s %s\n' % (full_name
, text
)
153 self
.network
.send_to_weechat(message
)
154 self
.debug_dialog
.chat
.display(0, '<==', message
, color
='red')
156 def open_preferences_dialog(self
):
159 def open_debug_dialog(self
):
160 self
.debug_dialog
.chat
.scroll_bottom()
161 self
.debug_dialog
.show()
163 def debug_input_text_sent(self
, text
):
164 if self
.network
.is_connected():
167 if text
.startswith('(') and pos
>= 0:
168 text
= '(debug_%s)%s' % (text
[1:pos
], text
[pos
+1:])
170 text
= '(debug) %s' % text
171 self
.network
.send_to_weechat(text
+ '\n')
172 self
.debug_dialog
.chat
.display(0, '<==', text
, color
='red')
174 def debug_dialog_closed(self
, result
):
175 self
.debug_dialog
.hide()
177 def open_about_dialog(self
):
178 messages
= ['<b>%s</b> %s' % (NAME
, VERSION
),
179 '© 2011 %s <<a href="mailto:%s">%s</a>>' % (AUTHOR
, AUTHOR_MAIL
, AUTHOR_MAIL
),
181 'WeeChat site: <a href="%s">%s</a>' % (WEECHAT_SITE
, WEECHAT_SITE
),
183 self
.about_dialog
= AboutDialog(NAME
, messages
, self
)
185 def open_connection_dialog(self
):
187 for option
in ('server', 'port', 'password'):
188 values
[option
] = self
.config
.get('relay', option
)
189 self
.connection_dialog
= ConnectionDialog(values
, self
)
190 self
.connection_dialog
.dialog_buttons
.accepted
.connect(self
.connect_weechat
)
192 def connect_weechat(self
):
193 self
.network
.connect_weechat(self
.connection_dialog
.fields
['server'].text(),
194 self
.connection_dialog
.fields
['port'].text(),
195 self
.connection_dialog
.fields
['password'].text())
196 self
.connection_dialog
.close()
198 def network_status_changed(self
, status
, extra
):
199 if self
.config
.getboolean('look', 'statusbar'):
200 self
.statusBar().showMessage(status
)
201 self
.debug_dialog
.chat
.display(0, '', status
, color
='blue')
202 self
.network_status_set(status
, extra
)
204 def network_status_set(self
, status
, extra
):
205 pal
= self
.network_status
.palette()
206 if self
.network
.is_connected():
207 pal
.setColor(self
.network_status
.foregroundRole(), QtGui
.QColor('green'))
209 pal
.setColor(self
.network_status
.foregroundRole(), QtGui
.QColor('#aa0000'))
210 self
.network_status
.setPalette(pal
)
211 icon
= self
.network
.status_icon(status
)
213 self
.network_status
.setText('<img src="data/icons/%s"> %s' % (icon
, status
.capitalize()))
215 self
.network_status
.setText(status
.capitalize())
216 if status
== self
.network
.status_disconnected
:
217 self
.actions
['connect'].setEnabled(True)
218 self
.actions
['disconnect'].setEnabled(False)
220 self
.actions
['connect'].setEnabled(False)
221 self
.actions
['disconnect'].setEnabled(True)
223 def network_message_from_weechat(self
, message
):
224 self
.debug_dialog
.chat
.display(0, '==>',
225 'message (%d bytes):\n%s'
226 % (len(message
), protocol
.hex_and_ascii(message
, 20)),
228 proto
= protocol
.Protocol()
229 message
= proto
.decode(str(message
))
230 if message
.uncompressed
:
231 self
.debug_dialog
.chat
.display(0, '==>',
232 'message uncompressed (%d bytes):\n%s'
233 % (message
.size_uncompressed
,
234 protocol
.hex_and_ascii(message
.uncompressed
, 20)),
236 self
.debug_dialog
.chat
.display(0, '', 'Message: %s' % message
)
237 self
.parse_message(message
)
239 def parse_message(self
, message
):
240 if message
.msgid
.startswith('debug'):
241 self
.debug_dialog
.chat
.display(0, '', '(debug message, ignored)')
243 if message
.msgid
== 'listbuffers':
244 for obj
in message
.objects
:
245 if obj
.objtype
== 'hda' and obj
.value
['path'][-1] == 'buffer':
246 self
.list_buffers
.clear()
247 while self
.stacked_buffers
.count() > 0:
248 buf
= self
.stacked_buffers
.widget(0)
249 self
.stacked_buffers
.removeWidget(buf
)
251 for item
in obj
.value
['items']:
252 self
.list_buffers
.addItem('%d. %s' % (item
['number'], item
['full_name']))
253 self
.buffers
.append(Buffer(item
))
254 self
.stacked_buffers
.addWidget(self
.buffers
[-1].widget
)
255 self
.buffers
[-1].bufferInput
.connect(self
.buffer_input
)
256 self
.buffers
[-1].widget
.input.bufferSwitchPrev
.connect(self
.list_buffers
.switch_prev_buffer
)
257 self
.buffers
[-1].widget
.input.bufferSwitchNext
.connect(self
.list_buffers
.switch_next_buffer
)
258 self
.list_buffers
.setCurrentRow(0)
259 self
.buffers
[0].widget
.input.setFocus()
260 elif message
.msgid
== 'listlines':
261 for obj
in message
.objects
:
262 if obj
.objtype
== 'hda' and obj
.value
['path'][-1] == 'line_data':
263 for item
in obj
.value
['items']:
264 pointer
= item
['__path'][0]
265 buf
= filter(lambda b
: b
.pointer() == pointer
, self
.buffers
)
267 buf
[0].widget
.chat
.display(item
['date'],
268 color
.remove(item
['prefix']),
269 color
.remove(item
['message']))
270 elif message
.msgid
== 'nicklist':
271 for obj
in message
.objects
:
272 if obj
.objtype
== 'hda' and obj
.value
['path'][-1] == 'nick_group':
273 for item
in obj
.value
['items']:
274 if not item
['group'] and item
['visible']:
275 pointer
= item
['__path'][0]
276 buf
= filter(lambda b
: b
.pointer() == pointer
, self
.buffers
)
278 buf
[0].add_nick(item
['prefix'], item
['name'])
280 def closeEvent(self
, event
):
281 self
.network
.disconnect_weechat()
282 self
.debug_dialog
.close()
283 config
.write(self
.config
)
284 QtGui
.QMainWindow
.closeEvent(self
, event
)
287 app
= QtGui
.QApplication(sys
.argv
)
288 app
.setStyle(QtGui
.QStyleFactory
.create('Cleanlooks'))
290 sys
.exit(app
.exec_())