ratchet 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/gem_bin/ratchet +23 -0
  2. data/lib/ratchet.rb +613 -0
  3. data/lib/ratchet/aliases.rb +106 -0
  4. data/lib/ratchet/bufferparser.rb +409 -0
  5. data/lib/ratchet/commandbuffer.rb +66 -0
  6. data/lib/ratchet/commandparser.rb +668 -0
  7. data/lib/ratchet/configuration.rb +278 -0
  8. data/lib/ratchet/connections.rb +403 -0
  9. data/lib/ratchet/constants.rb +111 -0
  10. data/lib/ratchet/contrib/instance_exec.rb +21 -0
  11. data/lib/ratchet/eventparser.rb +486 -0
  12. data/lib/ratchet/gtk/bufferlistview.rb +514 -0
  13. data/lib/ratchet/gtk/bufferview.rb +167 -0
  14. data/lib/ratchet/gtk/configwindow.rb +229 -0
  15. data/lib/ratchet/gtk/connectionwindow.rb +218 -0
  16. data/lib/ratchet/gtk/keybinding.rb +356 -0
  17. data/lib/ratchet/gtk/linkwindow.rb +137 -0
  18. data/lib/ratchet/gtk/mainwindow.rb +504 -0
  19. data/lib/ratchet/gtk/networkpresenceconf.rb +567 -0
  20. data/lib/ratchet/gtk/pluginconfig.rb +94 -0
  21. data/lib/ratchet/gtk/pluginwindow.rb +146 -0
  22. data/lib/ratchet/gtk/userlistview.rb +161 -0
  23. data/lib/ratchet/help.rb +64 -0
  24. data/lib/ratchet/items.rb +271 -0
  25. data/lib/ratchet/lines.rb +63 -0
  26. data/lib/ratchet/networks.rb +652 -0
  27. data/lib/ratchet/plugins.rb +616 -0
  28. data/lib/ratchet/queue.rb +47 -0
  29. data/lib/ratchet/ratchet-version.rb +21 -0
  30. data/lib/ratchet/replies.rb +134 -0
  31. data/lib/ratchet/replyparser.rb +441 -0
  32. data/lib/ratchet/tabcomplete.rb +98 -0
  33. data/lib/ratchet/users.rb +237 -0
  34. data/lib/ratchet/utils.rb +178 -0
  35. data/share/defaults.yaml +169 -0
  36. data/share/glade/config.glade +2634 -0
  37. data/share/glade/connect.glade +950 -0
  38. data/share/glade/keybindings.glade +109 -0
  39. data/share/glade/linkwindow.glade +188 -0
  40. data/share/glade/mainwindow.glade +335 -0
  41. data/share/glade/network-presences.glade +1373 -0
  42. data/share/glade/pluginconf.glade +97 -0
  43. data/share/glade/plugins.glade +360 -0
  44. data/share/plugins/colorewrite.rb +193 -0
  45. data/share/plugins/highlighter.rb +115 -0
  46. data/share/plugins/mpdplay.rb +123 -0
  47. data/share/plugins/numberswitcher.rb +30 -0
  48. data/share/plugins/sysinfo.rb +82 -0
  49. metadata +96 -0
@@ -0,0 +1,504 @@
1
+ =begin
2
+ This file is part of the Ratchet project, a client for Icecap.
3
+ Copyright (C) 2005-6 Andrew Thompson
4
+
5
+ This program is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program; if not, write to the Free Software
17
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ =end
19
+
20
+ #### mainwindow.rb ####
21
+ # The main window, a LOT of stuff is in here...
22
+ ####
23
+
24
+ #TODO: move all but the GTK specific stuff into a more appropiate place
25
+
26
+ module Ratchet
27
+ class MainWindow
28
+ attr_reader :currentbuffer, :buffers, :main, :config
29
+ include KeyBind
30
+ def initialize(main, confighash)
31
+ @main = main
32
+ @confighash = confighash
33
+ @config = @main.config
34
+
35
+ @glade = GladeXML.new("#{DATADIR}/glade/mainwindow.glade") {|handler| method(handler)}
36
+
37
+ @usernamebutton = @glade["username"]
38
+ @topic = @glade["topic"]
39
+ @messageinput = @glade["message_input"]
40
+ @messagescroll = @glade['message_scroll']
41
+ @messagevadjustment = @messagescroll.vadjustment
42
+
43
+ @tooltips = Gtk::Tooltips.new
44
+
45
+ @messageinput.grab_focus
46
+ @messageinput.signal_connect("key_press_event"){|widget, event| input_buttons(widget, event)}
47
+
48
+ @userlist = @glade['userlist']
49
+ @panel = @glade['hpaned1']
50
+ @vpanel = @glade['vpaned1']
51
+ @vpanel.hide
52
+ @mainbox = @glade['mainbox']
53
+ @messagebox = @glade['vbox2']
54
+ @preferencesbar = @glade['preferencesbar']
55
+ @usercount = @glade['usercount']
56
+ @currentbuffer = @serverlist
57
+
58
+ @glade['window1'].default_width = @confighash['width']
59
+ @glade['window1'].default_height = @confighash['height']
60
+
61
+ @usernamebutton.hide
62
+ @topic.hide
63
+
64
+ @messagescroll.set_size_request(0, -1)#magical diamond skill 7 hack to stop window resizing
65
+ args = [self]
66
+ if @confighash['console']
67
+ args.push(@main.console)
68
+ end
69
+ @buffers = BufferListController.new(*args)
70
+ redraw_channellist(true)
71
+
72
+ @glade['window1'].signal_connect('key_press_event') { |widget, event| window_buttons(widget, event)}
73
+
74
+ @me = self
75
+
76
+ @last = nil
77
+
78
+ @highlighted = []
79
+
80
+ @linkcursor = Gdk::Cursor.new(Gdk::Cursor::HAND2)
81
+ @normalcursor = Gdk::Cursor.new(Gdk::Cursor::LEFT_PTR)
82
+
83
+ @defaultmenu = Gtk::Menu.new
84
+ @defaultmenu.append(Gtk::MenuItem.new("thing1"))
85
+ @defaultmenu.append(Gtk::MenuItem.new("thing2"))
86
+
87
+ @bindable_functions = []
88
+ @bindable_functions.push({'name' => 'switchtab', 'arguments' => 1})
89
+ @bindable_functions.push({'name' => 'open_linkwindow', 'arguments' => 0})
90
+ @bindable_functions.push({'name' => 'open_preferences', 'arguments' => 0})
91
+ @bindable_functions.push({'name' => 'open_networks', 'arguments' => 0})
92
+ @bindable_functions.push({'name' => 'open_keybindings', 'arguments' => 0})
93
+ @bindable_functions.push({'name' => 'next_buffer', 'arguments' => 0})
94
+ @bindable_functions.push({'name' => 'prev_buffer', 'arguments' => 0})
95
+ #@keyintmap = {'q' => 11, 'w' => 12, 'e' => 13, 'r' => 14, 't'=> 15, 'y' => 16, 'u' => 17, 'i' => 18, 'o' => 19, 'p' => 20}
96
+ end
97
+
98
+ def draw_from_config(unhide=true)
99
+ puts 'drawing from config'
100
+ #~ return if $main.quitting
101
+ #~ #@serverlist.redraw
102
+ #~ redraw_channellist
103
+ #~ #resize the window if we have some saved sizes...
104
+ #~ x = -1
105
+ #~ y = -1
106
+
107
+ #~ x = $config['windowwidth'].to_i if $config['windowwidth']
108
+ #~ y = $config['windowheight'].to_i if $config['windowheight']
109
+
110
+ #~ @glade['window1'].default_width = x
111
+ #~ @glade['window1'].default_height = y
112
+ #~ @glade['window1'].resize(x, y)
113
+
114
+ #~ @panel.position = $config['panelposition'].to_i if $config['panelposition']
115
+
116
+ @font = Pango::FontDescription.new(@config['main_font'])
117
+
118
+ #~ update_view(@serverlist.view.view)
119
+ #~ @serverlist.servers.each do |server|
120
+ #~ update_view(server.view.view)
121
+ #~ server.channels.each {|channel| update_view(channel.view.view)}
122
+ #~ server.chats.each {|chat| update_view(chat.view.view)}
123
+ #~ end
124
+
125
+ #TODO - figure out how to set the cursor-color style var (its undocumented, might not be in ruby-gtk2)
126
+ #maybe use parse_string like for scwview
127
+ #@glade['window1'].resize(@confighash['width'], @confighash['height'])
128
+ @glade['window1'].move(@confighash['xpos'], @confighash['ypos'])
129
+ redraw_channellist
130
+ @glade['window1'].show_all
131
+ @glade['window1'].move(@confighash['xpos'], @confighash['ypos'])
132
+ @panel.position = @confighash['panelposition']
133
+ #@glade['window1'].resize(@confighash['width'], @confighash['height'])
134
+ @topic.hide unless @currentbuffer.respond_to? :topic
135
+ @usernamebutton.hide unless @currentbuffer.respond_to? :username
136
+ @messageinput.grab_focus
137
+ end
138
+
139
+ def redraw_channellist(force=false)
140
+ oldview = @buffers.view
141
+ @buffers.recreate
142
+ puts "switch channellist? #{oldview != @buffers.view} #{oldview} #{@buffers.view}"
143
+ if oldview != @buffers.view or force
144
+ @buffers.view.widget.show_all
145
+
146
+ if @config['tablisttype'] == 'button'
147
+ @vpanel.remove(@vpanel.child2)
148
+ @glade['v_top'].pack_start(@buffers.view.widget, false, false, 5)
149
+ elsif @config['tablisttype'] == 'treeview'
150
+ @glade['v_top'].remove(oldview.widget)
151
+ @vpanel.pack2(@buffers.view.widget, false, true)
152
+ end
153
+ end
154
+
155
+ # return
156
+
157
+ # if @buffers
158
+ # if @glade['h_top'].children.include?(@buffers.view.widget)
159
+ # @glade['h_top'].remove(@buffers.view.widget)
160
+ # elsif @glade['v_top'].children.include?(@buffers.view.widget)
161
+ # @glade['v_top'].remove(@buffers.view.widget)
162
+ # elsif @glade['u_pane'].children.include?(@buffers.view.widget)
163
+ # @glade['u_pane'].remove(@buffers.view.widget)
164
+ # end
165
+ # end
166
+
167
+ # if $config['tablisttype'] == 'treeview'
168
+ # if $config['tablistposition'] != 'right' and $config['tablistposition'] != 'left' and $config['tablistposition'] != 'underuserlist'
169
+ # $config['tablistposition'] = 'left'
170
+ # end
171
+ # unless @tablist.class == TreeTabList
172
+ # $main.tabmodel.delete_observer(@tablist)
173
+ # @tablist = TreeTabList.new($main.tabmodel)
174
+ # end
175
+ # else
176
+ # if $config['tablistposition'] == 'right' or $config['tablistposition'] == 'left' or $config['tablistposition'] == 'underuserlist'
177
+ # unless @tablist.class == VBoxTabList
178
+ # $main.tabmodel.delete_observer(@tablist)
179
+ # @tablist = VBoxTabList.new($main.tabmodel)
180
+ # end
181
+ # else
182
+ # unless @tablist.class == HBoxTabList
183
+ # $main.tabmodel.delete_observer(@tablist)
184
+ # @tablist = HBoxTabList.new($main.tabmodel)
185
+ # end
186
+ # end
187
+ # end
188
+
189
+ # $main.tabmodel.set_sort_and_structure(*$config.gettabmodelconfig)
190
+ # @tablist.renumber
191
+
192
+ # if $config['tablistposition'] == 'right'
193
+ # @glade['h_top'].pack_start(@tablist.widget, false, false, 5)
194
+ # elsif $config['tablistposition'] == 'left'
195
+ # @glade['h_top'].pack_start(@tablist.widget, false, false, 5)
196
+ # @glade['h_top'].reorder_child(@tablist.widget, 0)
197
+ # elsif $config['tablistposition'] == 'top'
198
+ # @glade['v_top'].pack_start(@tablist.widget, false, false, 5)
199
+ # @glade['v_top'].reorder_child(@tablist.widget, 0)
200
+ # elsif $config['tablistposition'] == 'bottom'
201
+ # @glade['v_top'].pack_start(@tablist.widget, false, false, 5)
202
+ # elsif $config['tablistposition'] == 'underuserlist'
203
+ # @glade['u_pane'].pack2(@tablist.widget, false, true)
204
+ # end
205
+ #@tablist.widget.show_all
206
+ end
207
+
208
+ def prev_buffer
209
+ @buffers.prev_buffer
210
+ end
211
+
212
+ def next_buffer
213
+ @buffers.next_buffer
214
+ end
215
+
216
+ def set_username
217
+ x = nil
218
+ label = Gtk::Label.new("New username")
219
+ entry = Gtk::Entry.new
220
+ entry.text = @currentbuffer.network.username
221
+ dialog = Gtk::Dialog.new("Username", nil,
222
+ Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
223
+ [Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT],
224
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_REJECT])
225
+ dialog.vbox.add(label)
226
+ dialog.vbox.add(entry)
227
+ dialog.show_all
228
+ dialog.run do |response|
229
+ case response
230
+ when Gtk::Dialog::RESPONSE_ACCEPT
231
+ x = entry.text
232
+ end
233
+ dialog.destroy
234
+ end
235
+ @main.send_command('nick'+x, "presence change;#{@currentbuffer.network.identifier_string};name=#{x}") if x
236
+ end
237
+
238
+ def topic_change(widget)
239
+ if widget.text != @currentbuffer.topic and @currentbuffer.class == ChannelBuffer
240
+ @main.send_command('topicchange', 'channel change;network='+@currentbuffer.network.name+';mypresence='+@currentbuffer.presence+';channel='+@currentbuffer.name+';topic='+escape(widget.text))
241
+ end
242
+ end
243
+
244
+ #get the substring to use for tab completion.
245
+ def get_completion_substr
246
+ string = @messageinput.text
247
+ position = @messageinput.position
248
+ string = string[0, position]
249
+
250
+ #get the string between the end of the string and the last space (the fragment we use for matching)
251
+ name, whatever = string.reverse.split(' ', 2)
252
+
253
+ return nil unless name
254
+
255
+ name = name.reverse
256
+
257
+ return name
258
+ end
259
+
260
+ #function to do the nick replace for tab completion
261
+ def replace_completion_substr(substr, match)
262
+ string = @messageinput.text.strip
263
+ position = @messageinput.position
264
+
265
+ index = string.rindex(substr, position)
266
+ endindex = index+substr.length
267
+
268
+ replacement = match
269
+
270
+ #append the tabcompletionsuffix if we're at the start of the line and it's not a command
271
+ if index == 0 and string[0, 1] != '/'
272
+ replacement += @config['tabcompletesuffix']
273
+ end
274
+ #pad the replacement string with a space if appropiate
275
+ if string[endindex, 1] != ' '
276
+ replacement += ' '
277
+ end
278
+
279
+ #update the content of the entry
280
+ string[index, substr.length] = replacement
281
+ @messageinput.text = string
282
+ @messageinput.position = index+replacement.length
283
+ end
284
+
285
+ def switch_buffer(obj)
286
+ update_dimensions
287
+ @messagescroll.remove(@messagescroll.child) if @messagescroll.child
288
+ @vpanel.remove(@vpanel.child1) if @vpanel.child1
289
+ @commandbuffer.currentcommand = @messageinput.text if @commandbuffer
290
+ @currentbuffer.buffer.marklastread if @currentbuffer and @currentbuffer.buffer
291
+ @currentbuffer = obj
292
+ @commandbuffer = @currentbuffer.commandbuffer
293
+ @messageinput.text = @commandbuffer.currentcommand
294
+ @messageinput.position = -1 #puts the cursor position at the end
295
+
296
+ if @currentbuffer.respond_to? :username
297
+ @usernamebutton.label = @currentbuffer.username.gsub('_', '__')
298
+ @usernamebutton.show
299
+ else
300
+ @usernamebutton.hide
301
+ end
302
+ if @currentbuffer.respond_to? :topic
303
+ update_topic
304
+ @topic.show
305
+ else
306
+ @topic.hide
307
+ end
308
+
309
+ if @currentbuffer.respond_to? :userlistview
310
+ @vpanel.show_all
311
+ @currentbuffer.userlistview.widget.show_all
312
+ @vpanel.pack1(@currentbuffer.userlistview.widget, false, false)
313
+ @panel.position = @confighash['panelposition'].to_i
314
+ elsif @config['tablisttype'] == 'treeview'
315
+ @panel.position = @confighash['panelposition'].to_i
316
+ @vpanel.remove(@vpanel.child1)
317
+ else
318
+ @vpanel.hide
319
+ end
320
+
321
+ @messagescroll.child = @currentbuffer.buffer.view
322
+ @currentbuffer.buffer.view.show
323
+ @currentbuffer.buffer.view.scroll_to_end
324
+ set_title
325
+ @messageinput.grab_focus
326
+ end
327
+
328
+ def set_title
329
+ if @currentbuffer.respond_to? :network and @currentbuffer.network != @currentbuffer
330
+ if @currentbuffer.kind_of? ChatBuffer
331
+ @glade['window1'].title= "Query with #{@currentbuffer.name} on #{@currentbuffer.network.name} - Ratchet"
332
+ else
333
+ @glade['window1'].title= "#{@currentbuffer.name} on #{@currentbuffer.network.name} - Ratchet"
334
+ end
335
+ else
336
+ @glade['window1'].title = "#{@currentbuffer.name} - Ratchet"
337
+ end
338
+ end
339
+
340
+ def message_inputted
341
+ return if @messageinput.text.length == 0
342
+ @commandbuffer.add_command(@messageinput.text)
343
+
344
+ if @currentbuffer.respond_to? 'parent'
345
+ network = @currentbuffer.parent
346
+ presence = @currentbuffer.parent.presence
347
+ elsif @currentbuffer.respond_to? 'presence'
348
+ network = @currentbuffer
349
+ presence = @currentbuffer.presence
350
+ else
351
+
352
+ end
353
+
354
+ message = @messageinput.text
355
+ @main.queue_input([message, @currentbuffer])
356
+ @messageinput.text = ''
357
+ end
358
+
359
+ def get_username
360
+ @usernamebutton.label = @currentbuffer.username.gsub('_', '__')
361
+ end
362
+
363
+ def show_username
364
+ @usernamebutton.show
365
+ end
366
+
367
+ def update_dimensions
368
+ # puts 'updating dimensions'
369
+ width, height = @glade['window1'].size
370
+ xpos, ypos = @glade['window1'].position
371
+ @confighash['panelposition'] = @panel.position if @panel.child2.visible?
372
+ @confighash['width']= width if width
373
+ @confighash['height'] = height if height
374
+ @confighash['xpos'] = xpos if xpos
375
+ @confighash['ypos'] = ypos if ypos
376
+ end
377
+
378
+ def update_topic
379
+ @topic.text = @currentbuffer.topic
380
+ @tooltips.set_tip(@topic, @currentbuffer.topic, '')
381
+ end
382
+
383
+ def whois(user)
384
+ return unless @currentbuffer.respond_to? :users
385
+ @main.send_command('whois'+user, "presence status;#{@currentbuffer.network.identifier_string};presence=#{user}")
386
+ end
387
+
388
+ def commandbuffer
389
+ @commandbuffer
390
+ end
391
+
392
+ def input_buttons(widget, event)
393
+ return unless event.class == Gdk::EventKey #ack, another guard against non EventKey events
394
+ if event.keyval == Gdk::Keyval.from_name('Tab')
395
+ substr = get_completion_substr
396
+ nick = @currentbuffer.tabcomplete(substr) if substr
397
+ replace_completion_substr(substr, nick) if nick
398
+ return true #block the signal
399
+ else
400
+ #if @currentbuffer.class == ChannelBuffer || @currentbuffer.class == ChatBuffer
401
+ @currentbuffer.clear_tabcomplete
402
+ end
403
+
404
+ if event.keyval == Gdk::Keyval.from_name('Up')
405
+ @messageinput.text = commandbuffer.last_command if commandbuffer
406
+ return true
407
+ elsif event.keyval == Gdk::Keyval.from_name('Down')
408
+ @messageinput.text = commandbuffer.next_command if commandbuffer
409
+ return true
410
+ end
411
+ false
412
+ end
413
+
414
+ def window_buttons(widget, event)
415
+ return unless event.class == Gdk::EventKey #make sure we're only dealing with EventKeys
416
+ x = event_to_string(event)
417
+ unless x
418
+ key = Gdk::Keyval.to_name(event.keyval)
419
+ if key == "Page_Up"
420
+ scroll_up
421
+ elsif key == "Page_Down"
422
+ scroll_down
423
+ end
424
+ end
425
+ return unless x and @config['keybindings'][x]
426
+ command, args = @config['keybindings'][x].split('(', 2)
427
+ args ||= ''
428
+ args.chomp!(')')
429
+ args = args.split(',').map{|e| e.downcase}
430
+ if command and self.respond_to?(command)
431
+ self.send(command, *args)
432
+ return true #block any futher things
433
+ else
434
+ return false
435
+ end
436
+ #eval($config['keybindings'][x])
437
+ #~ if (event.state & Gdk::Window::MOD1_MASK) != 0
438
+ #~ puts 'pressed alt-'+Gdk::Keyval.to_name(event.keyval) if $args['debug']
439
+ #~ key = Gdk::Keyval.to_name(event.keyval)
440
+ #~ if key =~ /\d/
441
+ #~ key = 10 if key.to_i == 0
442
+ #~ tab = @serverlist.number2tab(key.to_i)
443
+ #~ switchchannel(tab)
444
+ #~ return true
445
+ #~ elsif key =~ /[qwertyuiop]+/
446
+ #~ tab = @serverlist.number2tab(@keyintmap[key].to_i)
447
+ #~ switchchannel(tab)
448
+ #~ return true
449
+ #~ elsif key == 'l'
450
+ #~ LinkWindow.new(@currentbuffer.links)
451
+ #~ end
452
+ #~ end
453
+ end
454
+
455
+ def on_window_focus(*args)
456
+ #puts 'focused window'
457
+ @messageinput.grab_focus# unless @messageinput.focus?
458
+ false
459
+ end
460
+
461
+ def scroll_up
462
+ adjustment = @messagescroll.vadjustment
463
+ adjustment.value = adjustment.value - adjustment.page_increment
464
+ end
465
+
466
+ def scroll_down
467
+ adjustment = @messagescroll.vadjustment
468
+ x = adjustment.value + adjustment.page_increment
469
+ x= adjustment.upper-adjustment.page_size if x > adjustment.upper-adjustment.page_size
470
+ adjustment.value = x
471
+ end
472
+
473
+ def open_linkwindow
474
+ # LinkWindow.new(@currentbuffer.links)
475
+ end
476
+
477
+ def open_preferences
478
+ update_dimensions
479
+ configwindow = ConfigWindow.new(@main)
480
+ configwindow.show_all
481
+ end
482
+
483
+ def do_disconnect
484
+ @main.disconnect
485
+ end
486
+
487
+ def open_networks
488
+ @networkpresence = NetworkPresenceConf.new(@main, @main.networks, @main.protocols).show
489
+ end
490
+
491
+ def open_plugins
492
+ @pluginwindow = PluginWindow.new(@main).show
493
+ end
494
+
495
+ def open_keybindings
496
+ @keybindingwindow = KeyBindingWindow.new(@main, @config['keybindings'], @bindable_functions).show
497
+ end
498
+
499
+ def quit(notifymain=true)
500
+ update_dimensions
501
+ @main.quit if notifymain
502
+ end
503
+ end
504
+ end