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,616 @@
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
+ #### plugins.rb ####
21
+ # Provides a pluginAPI mixin for addding plugin support to classes
22
+ # provides a base plugin class which all other plugins must derive from
23
+ # provides some configuration automation for plugins
24
+ ####
25
+
26
+ #include this in any classes where you want plugins
27
+ module Ratchet
28
+ module PluginAPI
29
+ @@cb_hash = {}
30
+ @@cb_hash_after = {}
31
+ #calls all the callback blocks associated with method, should exit if a block returns true
32
+ def callback(method, *args)
33
+
34
+ #lookup the hash
35
+ # cb_hash = self.class.cb_hashes
36
+ # cb_hash ||= resolve_cb_hashes
37
+
38
+ #return if we get nothing
39
+ # callbacks = self.class.find_callbacks(method.to_sym)
40
+ # unless cb_hash
41
+ # puts cb_hash
42
+ # puts 'empty callback hash for '+self.class.to_s
43
+ # return *args
44
+ # end
45
+
46
+ #check if the callback is in the hash
47
+ # if cb_hash.has_key?(method.to_sym)
48
+ #initialize the return value as nil
49
+ ret = nil
50
+
51
+ #loop through the callbacks
52
+ # cb_hash[method.to_sym].each do |callback|
53
+ self.class.find_callbacks(method.to_sym).each do |callback|
54
+
55
+ #call the block and get the return value
56
+ #what about instance_eval here?
57
+ ret = self.instance_exec(*args, &callback)
58
+ # ret = callback.call(self, *args)
59
+
60
+ #if return value is true, break off calling any more callbacks (like GTK's system)
61
+ if ret === true
62
+ break
63
+ #if its an array, store the results so they can be passed to sucessive callbacks
64
+ elsif ret.kind_of? Array
65
+ ret.each_with_index { |z, i| args[i]=z}
66
+
67
+ #if a single value, assume first argument is returned
68
+ elsif ret
69
+ args[0] = ret
70
+ end
71
+ end
72
+
73
+ #if it returned true, let the calling function know
74
+ if ret === true
75
+ puts "returned true, disabling further callbacks or functions for #{method}"
76
+ return true
77
+ end
78
+
79
+ #otherwise return the arguments, possibly modified by callbacks.
80
+ return args
81
+ end
82
+
83
+ #calls all the callback_after blocks associated with method, should exit if a block returns true
84
+ def callback_after(method, *args)
85
+ #lookup the hash
86
+ # cb_hash_after = self.class.cb_hashes_after
87
+ # cb_hash_after ||= resolve_cb_hashes_after
88
+
89
+ # #return if nothing
90
+ # unless cb_hash_after
91
+ # puts 'empty callback_after hash for '+self.class.to_s
92
+ # return *args
93
+ # end
94
+
95
+ #check if the callback is in the hash
96
+ # if cb_hash_after.has_key?(method.to_sym)
97
+
98
+ #init the return value to nil
99
+ ret = nil
100
+
101
+ #loop through the callbacks
102
+ # cb_hash_after[method.to_sym].each do |callback|
103
+ self.class.find_callbacks_after(method.to_sym).each do |callback|
104
+ #call the callback, and get the return stuff
105
+ ret = self.instance_exec(*args, &callback)
106
+ # ret = callback.call(self, *args)
107
+
108
+ #if callback returns true, break off calling any other callbacks like GTK's signals
109
+ if ret === true
110
+ break
111
+
112
+ #if its an array, update the original arguments (assume they're passed in order)
113
+ elsif ret.kind_of? Array
114
+ ret.each_with_index { |z, i| args[i]=z}
115
+
116
+ #if single return, assume first argument
117
+ elsif ret
118
+ args[0] = ret
119
+ end
120
+ end
121
+
122
+ #let the calling function know it returned true
123
+ if ret === true
124
+ puts 'returned true, disabling further callbacks or functions for '+method
125
+ return true
126
+ end
127
+
128
+ #otherwise, return the arguments, possibly modified by callbacks
129
+ return args
130
+ end
131
+
132
+ #resolves the method name of the current method (is this still needed?)
133
+ def get_method_name()
134
+ /\`([^\']+)\'/.match(caller(1).first)[1].to_sym # we need a ' to close the damn thing :)
135
+ end
136
+
137
+ #load a plugin
138
+ def plugin_load(name)
139
+ #expand the name
140
+ return unless file = Plugin.find_plugin(name)
141
+ @config['plugins'] = [] unless @config['plugins'].class == Array
142
+ @config['plugins'].push(name) unless @config['plugins'].include?(name)
143
+
144
+ #check if it exists, if so, load it
145
+ if File.exists?(file)
146
+ begin
147
+ load(file, true)
148
+ rescue Exception => details
149
+ puts 'Error loading plugin '+name+' : '+details.message
150
+ puts details.backtrace
151
+ @config['plugins'].delete(name)
152
+ return false
153
+ end
154
+
155
+ #no plugin found
156
+ else
157
+ puts 'plugin file '+file+' not found'
158
+ @config['plugins'].delete(name)
159
+ return false
160
+ end
161
+ Plugin[name]
162
+ end
163
+
164
+ #wrapper function for class method
165
+ # def resolve_cb_hash
166
+ # puts 'resolving cb_hash'
167
+ # return self.class.resolve_cb_hash
168
+ # end
169
+
170
+ # #wrapper function for class method
171
+ # def resolve_cb_hash_after
172
+ # puts 'resolving cb_hash_after'
173
+ # return self.class.resolve_cb_hash_after
174
+ # end
175
+
176
+ #hyperextend!, these methods are added as class methods
177
+ module Plugins
178
+ #define a callback
179
+ def add_callback(name, &block)
180
+ #initialize the hash if it doesn't exist
181
+ @cb_hash ||= Hash.new
182
+
183
+ #make sure there's a function to attach a callback to
184
+ if self.private_instance_methods.include?(name.to_s) or self.instance_methods.include?(name.to_s)
185
+
186
+ #make a new array for the callback if need be, this allows multiple callbacks per function, executed in the order they were added
187
+ @cb_hash[name.to_sym] ||= Array.new
188
+ if block_given?
189
+
190
+ #add the block to the array
191
+ @cb_hash[name.to_sym].push(block)
192
+ puts "added callback for #{name}"
193
+ puts @cb_hash.inspect
194
+ # ObjectSpace.each_object(self) do |klass|
195
+ #puts klass
196
+ # klass.class.resolve_cb_hash
197
+ # end
198
+ #return the name of the callback..?
199
+ return name
200
+ end
201
+ else
202
+ #no function to attach to...
203
+ puts "no event function called #{name}, not adding callback"
204
+ return nil
205
+ end
206
+ end
207
+
208
+ #add a callback to be executed after a function
209
+ def add_callback_after(name, &block)
210
+ #again, make the hash if it doesn't exist
211
+ @cb_hash_after ||= Hash.new
212
+
213
+ #check to see if there's a function to attach to
214
+ if self.private_instance_methods.include?(name.to_s) or self.instance_methods.include?(name.to_s)
215
+ #make an array for the callbacks if there isn't one
216
+ @cb_hash_after[name.to_sym] ||= Array.new
217
+ if block_given?
218
+ @cb_hash_after[name.to_sym].push(block)
219
+ puts "added callback_after for #{name}"
220
+ puts @cb_hash_after.inspect
221
+ # ObjectSpace.each_object(self) do |klass|
222
+ #puts klass.class
223
+ # klass.class.resolve_cb_hash_after
224
+ # end
225
+ #return the name, not sure why...
226
+ return name
227
+ end
228
+ else
229
+ #no function!
230
+ puts "no event function called #{name}, not adding callback"
231
+ return nil
232
+ end
233
+ end
234
+
235
+ #remove a callback
236
+ def del_callback(name)
237
+ #puts self.class
238
+ puts @cb_hash.delete(name.to_sym)
239
+ puts @cb_hash.inspect
240
+ # resolve_cb_hashes
241
+ end
242
+
243
+ #remove a callback_after
244
+ def del_callback_after(name)
245
+ @cb_hash_after.delete(name.to_sym)
246
+ puts @cb_hash_after.inspect
247
+ # resolve_cb_hashes_after
248
+ puts @cb_hashes_after.inspect
249
+ end
250
+
251
+ #remove a method
252
+ def del_method(name)
253
+ self.send(:remove_method, name.to_sym)
254
+ # puts self.methods.inspect
255
+ end
256
+
257
+ #add a new method
258
+ def add_method(name, &block)
259
+ #make sure its not already defined....
260
+ if self.private_instance_methods.include?(name.to_s) or self.instance_methods.include?(name.to_s)
261
+ puts 'method '+name+' already defined, not redefining'
262
+ return nil
263
+ end
264
+
265
+ #call define method and add it.
266
+ self.send(:define_method, name, &block)
267
+ #puts 'added '+name
268
+ return name
269
+ end
270
+
271
+ #reader for cb_hash...?
272
+ def cb_hash
273
+ @cb_hash ||= Hash.new
274
+ return @cb_hash
275
+ end
276
+
277
+ def cb_hashes
278
+ resolve_cb_hashes unless @cb_hashes
279
+ @cb_hashes
280
+ end
281
+
282
+ #lookup parent classes for callbacks
283
+ #TODO is there a way to do this less then once per callback call without messing up plugin unloading?
284
+ def resolve_cb_hashes
285
+ #a new hash to store the resulting callbacks
286
+ hashes = [cb_hash]
287
+
288
+ #loop through the parent classes, and build an array of them....
289
+ c = self
290
+ classes = Array.new
291
+
292
+ while c != Object || nil
293
+ classes.push(c)
294
+ c = c.superclass
295
+ end
296
+
297
+ #reverse the array of classes, so callbacks are overridden in child classes
298
+ classes.reverse!
299
+
300
+ #loop through, and add callbacks
301
+ classes.each do |klass|
302
+ if klass.respond_to? :cb_hash
303
+ hashes << klass.cb_hash# if klass.cb_hash
304
+ end
305
+ end
306
+
307
+ #return
308
+ #return temphash
309
+ @cb_hashes = hashes
310
+ end
311
+
312
+ def find_callbacks(method)
313
+ ret = []
314
+ x = cb_hashes.select{|x| x.has_key? method}
315
+ # puts x.inspect, @cb_hashes.inspect
316
+ x.each do |y|
317
+ ret += y[method]
318
+ end
319
+ # puts "found callbacks: #{ret.inspect}"
320
+ ret
321
+ end
322
+
323
+ def find_callbacks_after(method)
324
+ # puts method
325
+ ret = []
326
+ x = cb_hashes_after.select{|x| x.has_key? method}
327
+ # puts x.inspect, @cb_hashes_after.inspect
328
+ x.each do |y|
329
+ ret += y[method]
330
+ end
331
+ # puts "found callbacks after: #{ret.inspect}"
332
+ ret
333
+ end
334
+
335
+ #reader for cb_hash_after
336
+ def cb_hash_after
337
+ @cb_hash_after ||= Hash.new
338
+ return @cb_hash_after
339
+ end
340
+
341
+ def cb_hashes_after
342
+ resolve_cb_hashes_after unless @cb_hashes_after
343
+ @cb_hashes_after
344
+ end
345
+
346
+ #lookup parent classes for callback_after
347
+ def resolve_cb_hashes_after
348
+
349
+ #new hash to store results
350
+ hashes = [cb_hash_after]
351
+
352
+ #loop through parents and build an array
353
+ c = self
354
+ classes = Array.new
355
+
356
+ while c != Object || nil
357
+ classes.push(c)
358
+ c = c.superclass
359
+ end
360
+
361
+ #invert the array, so child callbacks override parents
362
+ classes.reverse!
363
+
364
+ #build hash of callback_after
365
+ classes.each do |klass|
366
+ # puts klass, klass.respond_to?(:cb_hash_after)
367
+ if klass.respond_to? :cb_hash_after
368
+ # puts "#{klass} has cb_hash_after"
369
+ # puts klass.cb_hash_after.inspect
370
+ hashes << klass.cb_hash_after# if klass.cb_hash_after
371
+ end
372
+ end
373
+
374
+ #return
375
+ #return temphash
376
+ # puts "resolved cb_hashes_after to: #{hashes.inspect} for class #{self}"
377
+ @cb_hashes_after = hashes
378
+ end
379
+ end
380
+
381
+ extend Plugins
382
+
383
+ def self.append_features(klass)
384
+ super
385
+ klass.extend(Plugins)
386
+ end
387
+
388
+ def method_missing(symbol, *args)
389
+ plugin = Plugin.find_plugin_method(symbol)
390
+ if plugin
391
+ plugin[0].send(symbol, *args)
392
+ else
393
+ puts "cannot find method #{symbol} in plugins"
394
+ nil
395
+ # raise NoMethodError
396
+ end
397
+ end
398
+ end
399
+
400
+ #the plugin class, all plugins are derivatives of this class
401
+ class Plugin
402
+ include PluginAPI
403
+
404
+ def self.main=(main)
405
+ @@main= main
406
+ # puts "set main to #{main}"
407
+ end
408
+
409
+ #register a plugin
410
+ def self.register(plugin)
411
+ # puts 'registeting'
412
+ if /([\w\-]+)\.rb/.match(caller[0])
413
+ unless find_plugin($1)
414
+ raise SystemCallError, 'No such file '+$1+' to load as plugin', caller
415
+ end
416
+ name = $1
417
+ else
418
+ raise SystemCallError, 'Plugin does not have a name', caller
419
+ end
420
+
421
+ #plugin must be a child of Plugin class..
422
+ unless plugin.class.superclass == Plugin
423
+ puts 'Plugin must be subclass of Plugin'
424
+ return
425
+ end
426
+
427
+ #make sure its not already defined
428
+ if self[name]
429
+ puts "a plugin called #{name} is already registered"
430
+ return
431
+ end
432
+
433
+ #init the plugin hash if it doesn't exist
434
+ @@plugins ||= {}
435
+
436
+ #stuff the data in it
437
+ @@plugins[plugin] = {:name => name, :callbacks => Array.new, :callbacks_after => Array.new, :methods => Array.new} if plugin and !@@plugins[plugin]
438
+
439
+ #call the plugins load() method
440
+ @@main.console.send_user_event({'msg' => 'Loading Plugin '+name}, EVENT_NOTICE)
441
+ plugin.load
442
+ # puts self, self.class
443
+ end
444
+
445
+ def help(a, b)
446
+ self.class.help(a, b)
447
+ end
448
+
449
+ #TODO - what if the function we lookup is shared between multiple plugins?
450
+ def self.find_plugin_method(method)
451
+ plugin = @@plugins.detect do |p, info|
452
+ p.respond_to? method
453
+ end
454
+ plugin
455
+ end
456
+
457
+ #unload a plugin and remove all methods/callbacks added by it...
458
+ def self.unregister(plugin)
459
+
460
+ #make sure the plugin is registered
461
+ return false unless @@plugins and @@plugins[plugin]
462
+
463
+ #remove all the callbacks
464
+ @@plugins[plugin][:callbacks].each do |c|
465
+ c[:class].del_callback(c[:callback])
466
+ puts "removed callback #{c[:callback]} for class #{c[:class]}"
467
+ end
468
+
469
+ #remove all the callback_afters
470
+ @@plugins[plugin][:callbacks_after].each do |c|
471
+ c[:class].del_callback_after(c[:callback])
472
+ puts "removed callback_after #{c[:callback]} for class #{c[:class]}"
473
+ end
474
+
475
+ #remove all the methods
476
+ @@plugins[plugin][:methods].each do |c|
477
+ c[:class].del_method(c[:method])
478
+ puts "removed method #{c[:method]} for class #{c[:class]}"
479
+ end
480
+
481
+ @@main.config['plugins'].delete(@@plugins[plugin][:name])
482
+
483
+ #delete the plugin from the hash
484
+ @@plugins.delete(plugin)
485
+
486
+ #call the unload function if the plugin wants to do any additional cleanup
487
+ plugin.unload
488
+ true
489
+ end
490
+
491
+ #list registered plugins.
492
+ def self.list
493
+ @@plugins ||= {}
494
+ return @@plugins
495
+ end
496
+
497
+ #lookup a string as a plugin name
498
+ def self.[](name)
499
+ # puts "looking up #{name}"
500
+ @@plugins ||= {}
501
+ x = @@plugins.detect{|p| p[1][:name] == name}
502
+ x = x[0] if x
503
+ return x
504
+ end
505
+
506
+ #wrapper function for adding a callback to a class
507
+ def add_callback(plugin, classname, name, &block)
508
+
509
+ #make sure the plugin is registered
510
+ if @@plugins[plugin]
511
+ #add the callback, make sure its not already defined for this plugin and then define it iof add is sucessful
512
+ if callback = classname.add_callback(name, &block) and !@@plugins[plugin][:callbacks].include?({:callback=>callback, :class=>classname})
513
+ @@plugins[plugin][:callbacks].push({:callback=>callback, :class=>classname})
514
+ end
515
+
516
+ #error
517
+ else
518
+ puts 'plugin not registered'
519
+ end
520
+ end
521
+
522
+ def add_callback_after(plugin, classname, name, &block)
523
+
524
+ #make sure the plugin is registered
525
+ if @@plugins[plugin]
526
+ #add the callback, make sure its not already defined for this plugin and then define it if add is sucessful
527
+ if callback = classname.add_callback_after(name, &block) and !@@plugins[plugin][:callbacks_after].include?({:callback=>callback, :class=>classname})
528
+ @@plugins[plugin][:callbacks_after].push({:callback=>callback, :class=>classname})
529
+ end
530
+
531
+ #error
532
+ else
533
+ puts 'plugin not registered'
534
+ end
535
+ end
536
+
537
+ def add_method(plugin, classname, name, &block)
538
+ #make sure the plugin is registered
539
+ if @@plugins[plugin]
540
+ #add the callback, make sure its not already defined for this plugin and then define it if add is sucessful
541
+ if method = classname.add_method(name, &block) and !@@plugins[plugin][:methods].include?({:method=>method, :class=>classname})
542
+ @@plugins[plugin][:methods].push({:method=>method, :class=>classname})
543
+ end
544
+
545
+ #error
546
+ else
547
+ puts 'plugin not registered'
548
+ end
549
+ end
550
+
551
+ def self.find_plugin(name)
552
+ return unless name
553
+ name += '.rb'
554
+ dir1 = File.join($ratchetfolder, 'plugins')
555
+ dir2 = "#{DATADIR}/plugins"
556
+ if File.directory?(dir1) and Dir.entries(dir1).include?(name)
557
+ return File.join(dir1, name)
558
+ elsif File.directory?(dir2) and Dir.entries(dir2).include?(name)
559
+ return File.join(dir2, name)
560
+ else
561
+ return false
562
+ end
563
+ end
564
+
565
+ #stub for unload function to allow plugins to do additional cleanup
566
+ def unload
567
+ end
568
+
569
+ #stub for configure function
570
+ # def configure
571
+ # []
572
+ # end
573
+ end
574
+
575
+ class PluginConfig
576
+ #The GTK specific part of this has been moved to gtk/pluginconfig.rb
577
+
578
+ def bool_changed(widget)
579
+ change_setting(widget, widget.active?)
580
+ end
581
+
582
+ def color_changed(widget)
583
+ change_setting(widget, Color.new(*widget.color.to_a))
584
+ end
585
+
586
+ def text_changed(widget)
587
+ change_setting(widget, widget.text)
588
+ end
589
+
590
+ def integer_changed(widget)
591
+ change_setting(widget, widget.text.to_i)
592
+ end
593
+
594
+ def array_changed(widget)
595
+ change_setting(widget, widget.text.split(',').uniq)
596
+ end
597
+
598
+ def change_setting(widget, setting)
599
+ #puts 'changed setting of '+widget.name+' to '+setting.to_s
600
+ @configarray[widget]['value'] = setting
601
+ end
602
+
603
+ def update_config
604
+ @configarray.each do |k, v|
605
+ #puts v['name']+' = '+v['value'].to_s
606
+ @config[v['name']] = v['value']
607
+ end
608
+ destroy
609
+ @main.send_command('sendconfig', @config.changes)
610
+ end
611
+
612
+ def destroy
613
+ @window.destroy
614
+ end
615
+ end
616
+ end