cheri 0.0.4 → 0.0.5

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.
data/README CHANGED
@@ -6,7 +6,7 @@ based on the framework, as well as a builder-builder tool for easily creating
6
6
  simple builders. Cheri also comes with a demo application, Cheri::JRuby::Explorer,
7
7
  that is built using two of the supplied builders (Cheri::Swing and Cheri::Html).
8
8
 
9
- This version (0.0.4) is an early beta release. Some features are not yet fully
9
+ This version (0.0.5) is an early beta release. Some features are not yet fully
10
10
  developed, in particular the HTML/XML components. On the other hand, Cheri::Swing,
11
11
  which is the successor to JRBuilder (http://www2.webng.com/bdortch/jrbuilder), is
12
12
  relatively mature, in an adolescent sort of way, though many features could be added.
@@ -40,8 +40,13 @@ Cheri gem):
40
40
 
41
41
  Cheri::JRuby::Explorer.run
42
42
 
43
+ Alternatively, you can load and run it in one step:
44
+
45
+ require 'rubygems'
46
+ require 'cheri/cjx'
47
+
43
48
  This will take several seconds to load and start -- performance will be one area
44
- of improvement for versions 0.0.5 and beyond. Once it loads, it should be fairly
49
+ of improvement for versions 0.0.6 and beyond. Once it loads, it should be fairly
45
50
  clear what to do.
46
51
 
47
52
  Some known issues:
@@ -49,22 +54,13 @@ Some known issues:
49
54
  * Browsing the class hierarchy is very slow right now -- this actually slowed down
50
55
  in the past couple of days when I switched from HTML to straight Swing layout, the
51
56
  opposite of what I expected to happen.
52
-
53
- * ObjectSpace searches that return large numbers of objects sometimes result in a
54
- NullPointerException (JList seems to get overwhelmed), requiring a restart of
55
- CJX. I had a fix in that did a GC on the target instance before each search,
56
- which seemed to help, but also seemed a little extreme. This will be a search
57
- option in 0.0.5.
58
-
57
+
59
58
  * There are lots of layout issues; neither HTML (JEditorPane) nor BoxLayout provide
60
59
  exactly what I'm looking for. Will probably have to bite the bullet and go to
61
60
  GridBagLayout. Ugh.
62
-
63
- * Tab management (and navigation in general) is not yet quite right.
64
- Will have history/back buttons and better tab management in 0.0.5.
65
-
61
+
66
62
  * Global variables are currently shown, um, globally, when many of them should be shown
67
- per thread. This will be fixed in 0.0.5, which will include a Thread section with
63
+ per thread. This will be fixed in 0.0.6, which will include a Thread section with
68
64
  other goodies as well (thread-local vars, status, etc.).
69
65
 
70
66
  To install the CJX DRb server component in an instance (assuming the Cheri gem is
@@ -81,7 +81,7 @@ class TypeConnecter
81
81
  t = @t
82
82
  bld_anc = (class<<builder.object;self;end).ancestors
83
83
  anc ||= (class<<obj;self;end).ancestors
84
- key = [bld_anc,anc,sym]
84
+ key = [sym,bld_anc,anc]
85
85
  if (ctr = @c[key])
86
86
  return ctr.prepare(ctx,builder,obj,sym,props)
87
87
  end
@@ -557,6 +557,11 @@ class Context
557
557
  # alias_method :check, :ck
558
558
  #
559
559
 
560
+ # Overrides the default Object#inspect to prevent mind-boggling circular displays in IRB.
561
+ def inspect
562
+ "#<#{self.class}:instance>"
563
+ end
564
+
560
565
  # We want to prevent built objects passed as parameters from
561
566
  # being dynamically connected, since presumably the method/ctor
562
567
  # being called will do whatever is needed (except for cherify,
@@ -28,6 +28,9 @@ module Swing
28
28
 
29
29
  # note: inherits connection types from AWTConnecter
30
30
  SwingConnecter = Cheri::Builder::TypeConnecter.new(Cheri::AWT::AWTConnecter) do
31
+ jver = ENV_JAVA['java.version']
32
+ Java5 = String === jver && jver >= '1.5'
33
+ Java6 = String === jver && jver >= '1.6'
31
34
  S = java.lang.String.new ''
32
35
  TreePath = javax.swing.tree.TreePath
33
36
 
@@ -81,13 +84,13 @@ SwingConnecter = Cheri::Builder::TypeConnecter.new(Cheri::AWT::AWTConnecter) do
81
84
  # TODO: close button (:default or user JButton)
82
85
  connect java.awt.Component do |pane,cmp,sym,props|
83
86
  if props
84
- if (tc = props[:tab])
87
+ if Java6 && (tc = props[:tab])
85
88
  pane.addTab(S,cmp)
86
89
  i = pane.indexOfComponent(cmp)
87
90
  pane.setTabComponentAt(i,tc)
88
91
  # TODO: tooltip allowed for custom tab?
89
92
  else
90
- title = props[:title] || cmp.name || ''
93
+ title = props[:title] || cmp.name || S
91
94
  icon = props[:icon]
92
95
  tooltip = props[:tooltip]
93
96
  pane.addTab(title,icon,cmp,tooltip)
data/lib/cheri/cheri.rb CHANGED
@@ -29,10 +29,12 @@ module Cheri
29
29
  module VERSION #:nodoc:
30
30
  MAJOR = 0
31
31
  MINOR = 0
32
- TINY = 4
32
+ TINY = 5
33
33
  STRING = [MAJOR, MINOR, TINY].join('.').freeze
34
34
  end
35
- PathExp = Regexp.new "cheri-#{VERSION::STRING}\\/lib$" #:nodoc:
35
+ #:stopdoc:
36
+ PathExp = Regexp.new "cheri-#{VERSION::STRING}\\/lib$"
37
+ #:startdoc:
36
38
  class CheriException < StandardError; end
37
39
  # global threadsafe; set if all Cheri module instances in a system
38
40
  # must be threadsafe
@@ -79,6 +79,9 @@ module Explorer
79
79
  end
80
80
 
81
81
  class ObjectRec
82
+ # TODO: make this configurable (or settable in search dialog)
83
+ MaxDataLength = 100000
84
+
82
85
  def initialize(obj)
83
86
  @z = obj.class.to_s rescue nil
84
87
  @i = obj.__id__ rescue nil
@@ -95,6 +98,8 @@ class ObjectRec
95
98
  end
96
99
  @u = (obj.superclass.to_s rescue nil) if obj.respond_to?(:superclass)
97
100
  @s = obj.inspect rescue nil
101
+ # we're going allow a maximum of ~ 64K for value
102
+ @s = @s[0,MaxDataLength] << '...' if @s && @s.length > MaxDataLength
98
103
  end
99
104
  def clazz
100
105
  @z
@@ -186,7 +191,7 @@ class NameValue
186
191
  raise Cheri.type_error(name,String) unless String === name
187
192
  @n = name
188
193
  @i = value.__id__ rescue '?'
189
- @v = String === value ? value[0,100] : (value.to_s[0,100] rescue '?')
194
+ @v = String === value ? value[0,128] : (value.to_s[0,128] rescue '?')
190
195
  end
191
196
  def name
192
197
  @n
@@ -229,6 +234,7 @@ class NameTypeValue < NameValue
229
234
  end #NameTypeValue
230
235
 
231
236
  class SearchArgs
237
+ attr :gc, true
232
238
  def initialize(clazz,vars=nil,any=nil)
233
239
  raise Cheri.type_error(clazz,String) unless String === clazz
234
240
  raise ArgumentError,"Invalid class "+ clazz unless /^([A-Z])((\w|::[A-Z])*)$/.match clazz
@@ -237,7 +243,7 @@ class SearchArgs
237
243
  raise Cheri.type_error(vars,Array) unless Array === vars
238
244
  vars.each do |v|
239
245
  raise Cheri.type_error(v,SearchNameValue) unless SearchNameValue === v
240
- raise Cheri.type_error(v.name,Symbol) unless Symbol === v.name
246
+ raise Cheri.type_error(v.name,Symbol) if v.name && !(Symbol === v.name)
241
247
  end
242
248
  @v = vars
243
249
  @a = !(any == nil || any == false)
@@ -402,7 +408,9 @@ class RubyExplorer
402
408
  def module_methods(name,id=nil)
403
409
  if id && defined?ObjectSpace
404
410
  raise Cheri.type_error(id,Fixnum) unless Fixnum === id
405
- if Module === (mod = ObjectSpace._id2ref(id) rescue nil)
411
+ # doing this in two steps to work around JRuby bug (JRUBY-1125)
412
+ mod = ObjectSpace._id2ref(id)
413
+ if Module === mod
406
414
  return MethodRec.new(mod)
407
415
  end
408
416
  elsif name
@@ -420,7 +428,9 @@ class RubyExplorer
420
428
  def object(id)
421
429
  return unless id && defined?ObjectSpace
422
430
  raise Cheri.type_error(id,Fixnum) unless Fixnum === id
423
- if (obj = ObjectSpace._id2ref(id) rescue nil) && !(Fixnum === obj)
431
+ # doing this in two steps to work around JRuby bug (JRUBY-1125)
432
+ obj = ObjectSpace._id2ref(id)
433
+ if obj && !(Fixnum === obj)
424
434
  if Module === obj && (name = obj.name rescue nil)
425
435
  if (ix = name.rindex('::'))
426
436
  parent = name[0,ix]
@@ -440,18 +450,22 @@ class RubyExplorer
440
450
  def object_methods(id)
441
451
  return unless id && defined?ObjectSpace
442
452
  raise Cheri.type_error(id,Fixnum) unless Fixnum === id
443
- if (obj = ObjectSpace._id2ref(id) rescue nil) && !(Fixnum === obj)
453
+ # doing this in two steps to work around JRuby bug (JRUBY-1125)
454
+ obj = ObjectSpace._id2ref(id)
455
+ if obj && !(Fixnum === obj)
444
456
  return MethodRec.new(obj)
445
457
  end
446
458
  end
447
459
 
448
- def simple_value(id)
460
+ def simple_value(id,maxlen=80)
449
461
  return unless id && defined?ObjectSpace
450
462
  raise Cheri.type_error(id,Fixnum) unless Fixnum === id
451
- if (obj = ObjectSpace._id2ref(id)) && !(Fixnum === obj)
452
- obj.to_s[0,40] rescue 'Unreadable'
463
+ # doing this in two steps to work around JRuby bug (JRUBY-1125)
464
+ obj = ObjectSpace._id2ref(id)
465
+ if obj && !(Fixnum === obj)
466
+ obj.to_s[0,maxlen] rescue 'Unreadable'
453
467
  else
454
- 'Unavailable'
468
+ nil
455
469
  end
456
470
  end
457
471
 
@@ -463,30 +477,67 @@ class RubyExplorer
463
477
  # make sure we don't have anything executable before we eval it
464
478
  raise ArgumentError,"Invalid class "+ sclazz unless /^([A-Z])((\w|::[A-Z])*)$/.match sclazz
465
479
  if Module === (clazz = (eval("::#{sclazz}") rescue nil))
466
- #GC.start
480
+ GC.start if sargs.gc
467
481
  result = []
468
- vars = sargs.vars
469
- len = vars.length if vars
470
- any = sargs.any
471
- ObjectSpace.each_object(clazz) do |o|
472
- if (id = o.__id__ rescue nil)
473
- if vars
474
- match = 0
475
- vars.each do |v|
476
- if (iv = o.instance_variable_get(v.name) rescue nil) &&
477
- (v.rx? ? (iv.to_s =~ v.value rescue nil) : (iv.to_s.index(v.value) rescue nil))
478
- if any
479
- match = 1
480
- break
481
- else
482
- match += 1
482
+ if (vars = sargs.vars) && !vars.empty?
483
+ # just supporting one var name and/or value for now
484
+ sname = vars[0].name
485
+ raise ArgumentError,"Invalid instance variable" if sname && !(Symbol === sname)
486
+ sval = vars[0].value
487
+ sval = nil if String === sval && sval.empty?
488
+ rx = Regexp === sval
489
+ else
490
+ sname = nil
491
+ sval = nil
492
+ end
493
+ if sname && sval
494
+ ObjectSpace.each_object(clazz) do |o|
495
+ if Fixnum === (id = o.__id__ rescue nil) &&
496
+ #(o.respond_to?(:instance_variable_get) rescue nil) &&
497
+ (iv = o.instance_variable_get(sname) rescue nil) &&
498
+ (String === iv || String === (iv = iv.to_s rescue nil)) &&
499
+ (rx ? (iv =~ sval rescue nil) : (iv.index(sval) rescue nil))
500
+ result << id
501
+ end
502
+ end
503
+ elsif sname
504
+ sname = sname.to_s
505
+ ObjectSpace.each_object(clazz) do |o|
506
+ if Fixnum === (id = o.__id__ rescue nil) &&
507
+ #(o.respond_to?(:instance_variables) rescue nil) &&
508
+ Array === (ivs = o.instance_variables rescue nil) &&
509
+ (ivs.include?(sname) rescue nil)
510
+ result << id
511
+ end
512
+ end
513
+ elsif sval
514
+ # note: _not_ calling inspect here to avoid circular reference trap;
515
+ # examining (potentially) each instance var instead
516
+ ObjectSpace.each_object(clazz) do |o|
517
+ if Fixnum === (id = o.__id__ rescue nil)
518
+ if (String === (os = o) || String === (os = o.to_s rescue nil)) &&
519
+ (rx ? (os =~ sval rescue nil) : (os.index(sval) rescue nil))
520
+ result << id
521
+ elsif Array === (ivs = o.instance_variables rescue nil)
522
+ hit = nil
523
+ ivs.each do |ivn|
524
+ if (iv = o.instance_variable_get(ivn.to_sym) rescue nil) &&
525
+ (String === iv || String === (iv = iv.to_s rescue nil)) &&
526
+ (rx ? (iv =~ sval rescue nil) : (iv.index(sval) rescue nil))
527
+ result << id unless hit
528
+ hit = true
529
+ # this doesn't appear to be breaking?
530
+ break # TODO: check for LocalJumpError
483
531
  end
484
532
  end
485
533
  end
486
- result << id if (any && match == 1) || match == len
487
- else
488
- result << id
489
- end
534
+ end
535
+ end
536
+ else
537
+ ObjectSpace.each_object(clazz) do |o|
538
+ if Fixnum === (id = o.__id__ rescue nil)
539
+ result << id
540
+ end
490
541
  end
491
542
  end
492
543
  result
Binary file
Binary file
@@ -165,8 +165,6 @@ class SearchDialog
165
165
  @object_id = text_field do
166
166
  columns 16
167
167
  fixed_size 100,24
168
- #maximum_size dimension(100,24)
169
- #minimum_size dimension(100,24)
170
168
  end
171
169
  y_glue
172
170
  end
@@ -182,6 +180,7 @@ class SearchDialog
182
180
  'Search Error', JOptionPane::ERROR_MESSAGE)
183
181
  else
184
182
  @dialog.visible = false
183
+ @object_id.text = ''
185
184
  @main.new_find(@instances[@instance_list.selected_index],id)
186
185
  end
187
186
  end
@@ -192,11 +191,12 @@ class SearchDialog
192
191
 
193
192
  y_glue
194
193
 
195
- x_panel do
194
+ y_panel do
196
195
  compound_border(
197
196
  titled_border('Search for instances by type and optional variable/value') { etched_border :LOWERED },
198
197
  empty_border(4,8,4,8)
199
198
  )
199
+ x_panel do
200
200
  y_box do
201
201
  y_glue
202
202
  label 'Class or Module:'
@@ -217,22 +217,16 @@ class SearchDialog
217
217
  @clazz = text_field do
218
218
  columns 20
219
219
  fixed_size 200,24
220
- #maximum_size dimension(200,24)
221
- #minimum_size dimension(200,24)
222
220
  end
223
221
  y_glue
224
222
  @name1 = text_field do
225
223
  columns 20
226
224
  fixed_size 200,24
227
- #maximum_size dimension(200,24)
228
- #minimum_size dimension(200,24)
229
225
  end
230
226
  y_glue
231
227
  @value1 = text_field do
232
228
  columns 20
233
229
  fixed_size 200,24
234
- #maximum_size dimension(200,24)
235
- #minimum_size dimension(200,24)
236
230
  end
237
231
  y_glue
238
232
  end
@@ -253,24 +247,38 @@ class SearchDialog
253
247
  elsif name && name !~ /^([A-Za-z_])(\w*)$/
254
248
  JOptionPane.show_message_dialog(@dialog,"Invalid variable name, please re-enter",
255
249
  'Search Error', JOptionPane::ERROR_MESSAGE)
256
- elsif value && !name && !value.empty?
257
- JOptionPane.show_message_dialog(@dialog,"Please enter a variable name (or omit value)",
258
- 'Search Error', JOptionPane::ERROR_MESSAGE)
259
250
  else
260
- @dialog.visible = false
261
- @main.new_search(@instances[@instance_list.selected_index],clazz,name,value)
251
+ if value && value.strip.length != value.length
252
+ rsp = JOptionPane.show_confirm_dialog(@main_frame,
253
+ "Value contains leading/trailing whitespace -- continue?")
254
+ else
255
+ rsp = nil
256
+ end
257
+ unless rsp && rsp != JOptionPane::YES_OPTION
258
+ @dialog.visible = false
259
+ gc = @gc.selected
260
+ reset_fields
261
+ @main.new_search(@instances[@instance_list.selected_index],clazz,name,value,gc)
262
+ end
262
263
  end
263
264
  end
264
265
  end
265
266
  end
267
+ end
268
+ x_panel do
269
+ @gc = check_box 'GC target instance before search'
270
+ end
266
271
  end
267
272
  y_glue
273
+ x_box do
274
+ x_glue
268
275
  button 'Cancel' do
269
276
  on_click do
270
277
  @dialog.visible = false
271
278
  end
272
279
  end
273
-
280
+ x_glue
281
+ end
274
282
  y_glue
275
283
  end
276
284
 
@@ -285,6 +293,14 @@ class SearchDialog
285
293
  val
286
294
  end
287
295
 
296
+ def reset_fields
297
+ @clazz.text = ''
298
+ @name1.text = ''
299
+ @value1.text = ''
300
+ @gc.selected = false
301
+ end
302
+ private :reset_fields
303
+
288
304
  def value
289
305
  @dialog
290
306
  end
@@ -345,7 +361,7 @@ class AboutDialog
345
361
  table(:width=>'97%') {
346
362
  tr{td(:align=>:left) {
347
363
  p 'Cheri::JRuby::Explorer (CJX) demonstrates some of the features ',
348
- 'of the Cheri builder framework, and with any luck may prove useful ',
364
+ 'of the Cheri builder framework, and may even prove useful ',
349
365
  'in its own right. CJX is built using the Cheri::Swing and Cheri::Html ',
350
366
  'components of the framework.'
351
367
  p
@@ -72,6 +72,9 @@ class Main
72
72
  SearchIcon = CJava.get_icon('Search24.gif')
73
73
  RefreshIcon = CJava.get_icon('Refresh24.gif')
74
74
  CloseIcon = CJava.get_icon('Delete24.gif')
75
+ # TODO: I *hate* these next two -- find replacements!
76
+ NextIcon = CJava.get_icon('Forward24.gif')
77
+ PrevIcon = CJava.get_icon('Back24.gif')
75
78
  ClassIcon = CJava.get_icon('class_16x16.png')
76
79
  ModuleIcon = CJava.get_icon('mod_tree.png')
77
80
  Viewers = Cheri::JRuby::Explorer
@@ -106,9 +109,10 @@ class Main
106
109
  @instances = {}
107
110
  @instance_listeners = []
108
111
  @main_tabs = {}
109
- @search_tabs = {}
112
+ @result_tabs = {}
113
+ @main_tab_history = []
110
114
  @main_tabs[:global] = {}
111
- @search_tabs[:global] = {}
115
+ @result_tabs[:global] = {}
112
116
  @local_instance = JRubyInstance.new(RubyExplorer.new,'Local','local')
113
117
  add_instance(@local_instance)
114
118
  end
@@ -122,14 +126,14 @@ class Main
122
126
  @main_frame.set_location(Math.max(0,(bounds.width - 800)/2), Math.max(0,(bounds.height - 760)/2))
123
127
  @main_frame.show
124
128
  @main_frame.to_front
125
- sleep 2
129
+ sleep 1.5
126
130
  @main_tree.selection_row = 0
127
131
  @main_frame.content_pane.remove(splash_screen)
128
132
  @main_menu.visible = true
129
133
  @main_tool_bar.visible = true
130
134
  @main_panel.visible = true
131
135
  @footer_panel.visible = true
132
- @search_tab_pane.visible = false
136
+ @result_tab_pane.visible = false
133
137
  end
134
138
 
135
139
  def release_resources
@@ -157,7 +161,7 @@ class Main
157
161
  right_pane do
158
162
  right_splitter do
159
163
  main_tab_pane
160
- search_tab_pane
164
+ result_tab_pane
161
165
  end
162
166
  end
163
167
  end
@@ -212,10 +216,10 @@ class Main
212
216
  def view_menu(&block)
213
217
  @view_menu ||= menu 'View' do |mn|
214
218
  mnemonic :VK_V
215
- @search_results_select = check_box_menu_item 'Search Results pane' do
219
+ @results_select = check_box_menu_item 'Search Results pane' do
216
220
  selected true
217
221
  on_click do |e|
218
- if (@search_tab_pane.visible = e.source.selected = !@search_tab_pane.visible)
222
+ if (@result_tab_pane.visible = e.source.selected = !@result_tab_pane.visible)
219
223
  adjust_right_splitter
220
224
  end
221
225
  end
@@ -264,7 +268,12 @@ class Main
264
268
  on_click do
265
269
  show_search_dialog
266
270
  end
267
-
271
+ end
272
+ menu_item 'Repeat last search' do
273
+ mnemonic :VK_R
274
+ on_click do
275
+ repeat_search
276
+ end
268
277
  end
269
278
  end
270
279
  cheri_yield(@search_menu,&block) if block
@@ -304,9 +313,24 @@ class Main
304
313
  show_search_dialog
305
314
  end
306
315
  end
307
- button FindAgainIcon
308
- button SearchIcon
316
+ button FindAgainIcon do
317
+ tool_tip_text 'Repeat last search'
318
+ on_click do
319
+ repeat_search
320
+ end
321
+ end
322
+ button SearchIcon do
323
+ tool_tip_text "We don't know what this does yet"
324
+ end
309
325
  x_glue
326
+ button PrevIcon do
327
+ tool_tip_text 'Previous tab view'
328
+ on_click {prev_view}
329
+ end
330
+ button NextIcon do
331
+ tool_tip_text 'Next tab view'
332
+ on_click {next_view}
333
+ end
310
334
  button CloseActiveIcon do
311
335
  tool_tip_text 'Close active view'
312
336
  on_click do
@@ -372,15 +396,15 @@ class Main
372
396
  @main_tab_pane
373
397
  end
374
398
 
375
- def search_tab_pane(&block)
376
- unless @search_tab_pane
377
- @search_tab_pane = tabbed_pane do
399
+ def result_tab_pane(&block)
400
+ unless @result_tab_pane
401
+ @result_tab_pane = tabbed_pane do
378
402
  align :LEFT
379
403
  end
380
- @search_tab_pane.extend TabbedPaneMethods
404
+ @result_tab_pane.extend TabbedPaneMethods
381
405
  end
382
- cheri_yield(@search_tab_pane,&block) if block
383
- @search_tab_pane
406
+ cheri_yield(@result_tab_pane,&block) if block
407
+ @result_tab_pane
384
408
  end
385
409
 
386
410
  def main_panel(&block)
@@ -469,32 +493,51 @@ class Main
469
493
  tabs = main_tab_pane
470
494
  unless tabs.include?(view)
471
495
  set_view_viewer(view,viewer)
472
- instance = viewer.instance || :global
496
+ if instance = viewer.instance
497
+ tab_group = instance.__id__
498
+ else
499
+ tab_group = :global
500
+ end
473
501
  ntype = viewer.type.type
474
502
  tab_type = TypeTabMap[ntype] || ntype
475
- current_viewer = @main_tabs[instance][tab_type]
476
- if current_viewer && (ix = tabs.index(current_viewer.view))
477
- tabs[ix] = view
478
- if Java6 && (tab = viewer.tab)
479
- tabs[ix,:tab] = tab
480
- else
481
- tabs[ix,:title] = viewer.title_tab
482
- tabs[ix,:icon] = viewer.icon_tab
483
- end
503
+ current_viewer = @main_tabs[tab_group][tab_type]
504
+ if current_viewer && (index = tabs.index(current_viewer.view))
505
+ replace_view_tab(tabs,view,viewer,index)
484
506
  else
485
- cheri_yield tabs do
486
- if Java6 && (tab = viewer.tab)
487
- cherify(view, :tab => tab)
488
- else
489
- cherify(view, :title => viewer.title_tab, :icon => viewer.icon_tab)
490
- end
491
- end
507
+ add_view_tab(tabs,view,viewer)
492
508
  end
493
- @main_tabs[instance][tab_type] = viewer
509
+ @main_tabs[tab_group][tab_type] = viewer
494
510
  end
495
511
  tabs.set_selected_component(view)
496
512
  end
497
513
 
514
+ def add_view_tab(tabs,view,viewer)
515
+ cheri_yield tabs do
516
+ cherify(view, :tab => viewer.tab, :title => viewer.title_tab, :icon => viewer.icon_tab)
517
+ end
518
+ if tabs == @main_tab_pane
519
+ index = tabs.index(view)
520
+ @main_tab_history[index] = TabHistory.new(viewer)
521
+ end
522
+ end
523
+
524
+ def replace_view_tab(tabs,view,viewer,index)
525
+ tabs[index] = view
526
+ if Java6 && (tab = viewer.tab)
527
+ tabs[index,:tab] = tab
528
+ else
529
+ tabs[index,:title] = viewer.title_tab
530
+ tabs[index,:icon] = viewer.icon_tab
531
+ end
532
+ if tabs == @main_tab_pane
533
+ if hist = @main_tab_history[index]
534
+ hist << viewer
535
+ else
536
+ @main_tab_history[index] = TabHistory.new(viewer)
537
+ end
538
+ end
539
+ end
540
+
498
541
  def prepare_for_node_expansion(node)
499
542
  unless node.child_count > 0
500
543
  vwr = node.user_object
@@ -533,6 +576,36 @@ class Main
533
576
  view.get_client_property(:viewer)
534
577
  end
535
578
 
579
+ def next_view
580
+ tabs = main_tab_pane
581
+ if (oldvwr = tabs.active_viewer) &&
582
+ (ix = tabs.index(oldvwr.view)) &&
583
+ (hist = @main_tab_history[ix]) &&
584
+ (newvwr = hist.next!)
585
+ replace_view(tabs,ix,newvwr)
586
+ end
587
+ end
588
+
589
+ def prev_view
590
+ tabs = main_tab_pane
591
+ if (oldvwr = tabs.active_viewer) &&
592
+ (ix = tabs.index(oldvwr.view)) &&
593
+ (hist = @main_tab_history[ix]) &&
594
+ (newvwr = hist.prev!)
595
+ replace_view(tabs,ix,newvwr)
596
+ end
597
+ end
598
+
599
+ def replace_view(tabs,index,viewer)
600
+ tabs[index] = viewer.view
601
+ if Java6 && (tab = viewer.tab)
602
+ tabs[index,:tab] = tab
603
+ else
604
+ tabs[index,:title] = viewer.title_tab
605
+ tabs[index,:icon] = viewer.icon_tab
606
+ end
607
+ end
608
+
536
609
  def refresh_active_view
537
610
  if (viewer = main_tab_pane.active_viewer)
538
611
  viewer.refresh if viewer.respond_to?(:refresh)
@@ -546,29 +619,39 @@ class Main
546
619
  end
547
620
 
548
621
  def close_active_search_view
549
- if (viewer = search_tab_pane.active_viewer)
550
- close_search_view(viewer)
622
+ if (viewer = result_tab_pane.active_viewer)
623
+ close_results_view(viewer)
551
624
  end
552
625
  end
553
626
 
554
627
  def close_view(viewer)
555
628
  view = viewer.view
556
- main_tab_pane.remove(view)
557
- instance = viewer.instance || :global
629
+ index = (tabs = main_tab_pane).index(view)
630
+ tabs.remove(view)
631
+ @main_tab_history.delete_at(index)
632
+ if instance = viewer.instance
633
+ tab_group = instance.__id__
634
+ else
635
+ tab_group = :global
636
+ end
558
637
  ntype = viewer.type.type
559
638
  tab_type = TypeTabMap[ntype] || ntype
560
- @main_tabs[instance].delete(tab_type) if @main_tabs[instance][tab_type] == viewer
561
- @main_tabs[instance].delete(viewer.__id__)
639
+ @main_tabs[tab_group].delete(tab_type) if @main_tabs[tab_group][tab_type] == viewer
640
+ @main_tabs[tab_group].delete(viewer.__id__)
562
641
  end
563
642
 
564
- def close_search_view(viewer)
643
+ def close_results_view(viewer)
565
644
  view = viewer.view
566
- search_tab_pane.remove(view)
567
- instance = viewer.instance || :global
645
+ result_tab_pane.remove(view)
646
+ if instance = viewer.instance
647
+ tab_group = instance.__id__
648
+ else
649
+ tab_group = :global
650
+ end
568
651
  ntype = viewer.type.type
569
652
  tab_type = TypeTabMap[ntype] || ntype
570
- @search_tabs[instance].delete(tab_type) if @search_tabs[instance][tab_type] == viewer
571
- @search_tabs[instance].delete(viewer.__id__)
653
+ @result_tabs[tab_group].delete(tab_type) if @result_tabs[tab_group][tab_type] == viewer
654
+ @result_tabs[tab_group].delete(viewer.__id__)
572
655
  end
573
656
 
574
657
  def close_views_for_instance(inst)
@@ -577,41 +660,39 @@ class Main
577
660
  views.each do |view|
578
661
  close_view(view_viewer(view))
579
662
  end
580
- views = search_tab_pane.select {|view| view_viewer(view).instance == inst}
663
+ views = result_tab_pane.select {|view| view_viewer(view).instance == inst}
581
664
  views.each do |view|
582
- close_search_view(view_viewer(view))
665
+ close_results_view(view_viewer(view))
583
666
  end
584
667
  end
585
668
 
586
- def open_search_view(viewer)
669
+ def open_results_view(viewer)
587
670
  view = viewer.view
588
- tabs = search_tab_pane
671
+ tabs = result_tab_pane
589
672
  set_view_viewer(view,viewer)
590
- instance = viewer.instance || :global
673
+ if instance = viewer.instance
674
+ tab_group = instance.__id__
675
+ else
676
+ tab_group = :global
677
+ end
591
678
  ntype = viewer.type.type
592
679
  tab_type = TypeTabMap[ntype] || ntype
593
- cheri_yield tabs do
594
- if Java6 && (tab = viewer.tab)
595
- cherify(view, :tab => tab)
596
- else
597
- cherify(view, :title => viewer.title_tab, :icon => viewer.icon_tab)
598
- end
599
- end
600
- @search_tabs[instance][tab_type] = viewer
680
+ add_view_tab(tabs,view,viewer)
681
+ @result_tabs[tab_group][tab_type] = viewer
601
682
  tabs.set_selected_component(view)
602
683
  show_search_results
603
684
  end
604
685
 
605
686
  def show_search_results
606
- @search_tab_pane.visible = true
607
- @search_results_select.selected = true
687
+ @result_tab_pane.visible = true
688
+ @results_select.selected = true
608
689
  adjust_right_splitter
609
690
  end
610
691
 
611
692
  def add_instance(instance)
612
693
  @instances[instance.address] = instance
613
- @main_tabs[instance] = {}
614
- @search_tabs[instance] = {}
694
+ @main_tabs[instance.__id__] = {}
695
+ @result_tabs[instance.__id__] = {}
615
696
  @instance_listeners.each do |lst|
616
697
  lst.instance_added(instance)
617
698
  end
@@ -621,8 +702,8 @@ class Main
621
702
  close_views_for_instance(inst)
622
703
  main_tree_model.remove_node_from_parent(inst.tree_node) if inst.tree_node
623
704
  @instances.delete(inst.address)
624
- @main_tabs.delete(inst)
625
- @search_tabs.delete(inst)
705
+ @main_tabs.delete(inst.__id__)
706
+ @result_tabs.delete(inst.__id__)
626
707
  @instance_listeners.each do |lst|
627
708
  lst.instance_removed(inst)
628
709
  end
@@ -684,64 +765,137 @@ class Main
684
765
  @connection_dialog ||= ConnectionDialog.new(self)
685
766
  end
686
767
 
687
- def new_search(instance,clazz,var,value)
768
+ def new_search(instance,clazz,var,value,gc=nil)
688
769
  raise Cheri.type_error(clazz,RubyInstance) unless RubyInstance === instance
689
770
  raise ArgumentError, "invalid class name #{clazz}" unless clazz =~ /^([A-Z])((\w|::[A-Z])*)$/
690
771
  raise ArgumentError, "invalid variable name #{var}" if var && var !~ /^([A-Za-z_])(\w*)$/
691
- if var
692
- var = ('@' + var).to_sym rescue nil
693
- if value
694
- if value =~ /^(\/)(.*)(\/)$/
695
- unless (val = Regexp.new($2) rescue nil)
696
- JOptionPane.show_message_dialog(@main_frame,"Invalid regular expression #{value}")
697
- return
698
- end
699
- else
700
- val = value
772
+ var = ('@' + var).to_sym rescue nil if var
773
+ if value
774
+ if value =~ /^(\/)(.*)(\/)$/
775
+ unless (val = Regexp.new($2) rescue nil)
776
+ JOptionPane.show_message_dialog(@main_frame,"Invalid regular expression #{value}")
777
+ return
701
778
  end
702
779
  else
703
- val = ''
780
+ val = value
704
781
  end
782
+ end
783
+ if var || value
705
784
  nv = Cheri::Explorer::SearchNameValue.new(var,val)
706
785
  args = Cheri::Explorer::SearchArgs.new(clazz,[nv])
707
786
  else
708
787
  args = Cheri::Explorer::SearchArgs.new(clazz)
709
788
  end
710
- res = instance.proxy.find(args)
789
+ args.gc = true if gc
790
+ @last_search = [instance,args]
711
791
  if (res = instance.proxy.find(args))
712
792
  results = Cheri::Explorer::SearchResults.new(args,res)
713
793
  vwr = viewer(:results).new(NodeTypes[:results],self,instance,results)
714
- open_search_view(vwr)
794
+ open_results_view(vwr)
715
795
  end
716
796
  end
717
797
 
718
- def new_find(instance,id)
719
- if rec = instance.proxy.object(id)
720
- ntv = case rec.clazz
721
- when 'Class' : NodeTypeValue.new(Class,rec.value,rec)
722
- when 'Module' : NodeTypeValue.new(Module,rec.value,rec)
723
- else NodeTypeValue.new(:object,rec.clazz,rec)
724
- end
725
- if (clazz = viewer(ntv.type,ntv.subtype))
726
- vwr = clazz.new(ntv,self,instance,ntv.value)
727
- view = vwr.view
728
- set_view_viewer(view,vwr)
729
- tabs = main_tab_pane
730
- cheri_yield tabs do
731
- if Java6 && (tab = vwr.tab)
732
- cherify(view, :tab => tab)
733
- else
734
- cherify(view, :title => vwr.title_tab, :icon => vwr.icon_tab)
735
- end
736
- end
737
- @main_tabs[instance][vwr.__id__] = vwr
738
- tabs.set_selected_component(view)
798
+ def repeat_search
799
+ if (last = @last_search)
800
+ instance = last[0]
801
+ args = last[1]
802
+ if (res = instance.proxy.find(args))
803
+ results = Cheri::Explorer::SearchResults.new(args,res)
804
+ vwr = viewer(:results).new(NodeTypes[:results],self,instance,results)
805
+ open_results_view(vwr)
806
+ end
807
+ end
808
+ end
809
+
810
+ # def new_find(instance,id,new_tab=nil)
811
+ # if rec = instance.proxy.object(id)
812
+ # ntv = case rec.clazz
813
+ # when 'Class' : NodeTypeValue.new(Class,rec.value,rec)
814
+ # when 'Module' : NodeTypeValue.new(Module,rec.value,rec)
815
+ # else NodeTypeValue.new(:object,rec.clazz,rec)
816
+ # end
817
+ # if (clazz = viewer(ntv.type,ntv.subtype))
818
+ # vwr = clazz.new(ntv,self,instance,ntv.value)
819
+ # view = vwr.view
820
+ # set_view_viewer(view,vwr)
821
+ # tabs = main_tab_pane
822
+ # add_view_tab(tabs,view,vwr)
823
+ # @main_tabs[instance.__id__][vwr.__id__] = vwr
824
+ # tabs.set_selected_component(view)
825
+ # else
826
+ # JOptionPane.show_message_dialog(@main_frame,"Unable to display object #{id} (no viewer)")
827
+ # end
828
+ # else
829
+ # JOptionPane.show_message_dialog(@main_frame,"Unable to retrieve object #{id} (garbage-collected?)")
830
+ # end
831
+ # end
832
+ #
833
+ def show_linked_object(curvwr,id,new_tab=nil)
834
+ instance = curvwr.instance
835
+ unless rec = instance.proxy.object(id)
836
+ JOptionPane.show_message_dialog(@main_frame,"Unable to retrieve object #{id} (garbage-collected?)")
837
+ return
838
+ end
839
+ ntv = case rec.clazz
840
+ when 'Class' : NodeTypeValue.new(Class,rec.value,rec)
841
+ when 'Module' : NodeTypeValue.new(Module,rec.value,rec)
842
+ else NodeTypeValue.new(:object,rec.clazz,rec)
843
+ end
844
+ unless clazz = viewer(ntv.type,ntv.subtype)
845
+ JOptionPane.show_message_dialog(@main_frame,"Unable to display object #{id} (no viewer)")
846
+ end
847
+ vwr = clazz.new(ntv,self,instance,ntv.value)
848
+ view = vwr.view
849
+ set_view_viewer(view,vwr)
850
+ tabs = main_tab_pane
851
+ if new_tab
852
+ add_view_tab(tabs,view,vwr)
853
+ @main_tabs[instance.__id__][vwr.__id__] = vwr
854
+ else
855
+ ntype = vwr.type.type
856
+ tab_type = TypeTabMap[ntype] || ntype
857
+ current_viewer = @main_tabs[instance.__id__][tab_type]
858
+ if current_viewer && (index = tabs.index(current_viewer.view))
859
+ replace_view_tab(tabs,view,vwr,index)
739
860
  else
740
- JOptionPane.show_message_dialog(@main_frame,"Unable to display object #{id}")
861
+ add_view_tab(tabs,view,vwr)
741
862
  end
863
+ @main_tabs[instance.__id__][tab_type] = vwr
864
+ end
865
+ tabs.set_selected_component(view)
866
+ end
867
+
868
+ def show_result_object(resvwr,id,new_tab=nil)
869
+ instance = resvwr.instance
870
+ unless rec = instance.proxy.object(id)
871
+ JOptionPane.show_message_dialog(@main_frame,"Unable to retrieve object #{id} (garbage-collected?)")
872
+ return
873
+ end
874
+ ntv = case rec.clazz
875
+ when 'Class' : NodeTypeValue.new(Class,rec.value,rec)
876
+ when 'Module' : NodeTypeValue.new(Module,rec.value,rec)
877
+ else NodeTypeValue.new(:object,rec.clazz,rec)
878
+ end
879
+ unless clazz = viewer(ntv.type,ntv.subtype)
880
+ JOptionPane.show_message_dialog(@main_frame,"Unable to display object #{id} (no viewer)")
881
+ end
882
+ vwr = clazz.new(ntv,self,instance,ntv.value)
883
+ view = vwr.view
884
+ set_view_viewer(view,vwr)
885
+ tabs = main_tab_pane
886
+ if new_tab
887
+ add_view_tab(tabs,view,vwr)
888
+ @main_tabs[instance.__id__][vwr.__id__] = vwr
742
889
  else
743
- JOptionPane.show_message_dialog(@main_frame,"Unable to retrieve object #{id}")
890
+ current_viewer = @main_tabs[instance.__id__][resvwr.__id__]
891
+ if current_viewer && (index = tabs.index(current_viewer.view))
892
+ replace_view_tab(tabs,view,vwr,index)
893
+ else
894
+ add_view_tab(tabs,view,vwr)
895
+ end
896
+ @main_tabs[instance.__id__][resvwr.__id__] = vwr
744
897
  end
898
+ tabs.set_selected_component(view)
745
899
  end
746
900
 
747
901
  def simple_value(instance,id)
@@ -898,6 +1052,45 @@ module TabbedPaneMethods
898
1052
  end
899
1053
  end #TabbedPaneMethods
900
1054
 
1055
+ class TabHistory
1056
+ def initialize(viewer)
1057
+ @h = [viewer]
1058
+ @c = 1
1059
+ end
1060
+ def <<(viewer)
1061
+ if (len = @h.length) > @c
1062
+ @h = @h[0,@c]
1063
+ end
1064
+ @h << viewer
1065
+ @c = @h.length
1066
+ end
1067
+ def curr
1068
+ @h[@c-1]
1069
+ end
1070
+ def next
1071
+ @h[@c]
1072
+ end
1073
+ def prev
1074
+ if @c >= 2
1075
+ @h[@c-2]
1076
+ end
1077
+ end
1078
+ def next!
1079
+ if @c < @h.length
1080
+ v = @h[@c]
1081
+ @c += 1
1082
+ v
1083
+ end
1084
+ end
1085
+ def prev!
1086
+ if @c > 1
1087
+ @c -= 1
1088
+ @h[@c-1]
1089
+ end
1090
+ end
1091
+ end #TabHistory
1092
+
1093
+
901
1094
  # TODO: may want JComponent version that uses get/put client property?
902
1095
  # TODO: had to do this, see note at set_view_viewer
903
1096
  #module ComponentViewMethods
@@ -436,6 +436,11 @@ module NavViewerConstants
436
436
  IFont = Font.new('Monospaced',Font::BOLD,14)
437
437
  CFont = Font.new('Dialog',Font::PLAIN,14)
438
438
  ResFont = Font.new('Monospaced',Font::PLAIN,12)
439
+ MouseEvent = ::Java::JavaAwtEvent::MouseEvent
440
+ BUTTON1 = MouseEvent::BUTTON1
441
+ BUTTON2 = MouseEvent::BUTTON2
442
+ BUTTON3 = MouseEvent::BUTTON3
443
+ SHIFT = MouseEvent::SHIFT_DOWN_MASK
439
444
  HdrName = 'Name'.freeze
440
445
  HdrVarName = 'Variable name'.freeze
441
446
  HdrType = 'Type'.freeze
@@ -566,15 +571,15 @@ module NavViewer
566
571
  set_font col.dfont || VFont
567
572
  foreground col.dfg if col.dfg
568
573
  if (click_val = col.clk)
569
- on_mouse_clicked do
570
- mouse_clicked(click_val)
571
- end
572
574
  on_mouse_entered do |e|
573
575
  e.source.parent.background = HColor
574
576
  end
575
577
  on_mouse_exited do |e|
576
578
  e.source.parent.background = VColor
577
579
  end
580
+ on_mouse_clicked {|e| mouse_clicked(e,click_val) }
581
+ on_mouse_pressed {|e| mouse_action(e,click_val) }
582
+ on_mouse_released {|e| mouse_action(e,click_val) }
578
583
  end
579
584
  end
580
585
  end
@@ -599,7 +604,10 @@ module NavViewer
599
604
  y_glue if glue
600
605
  end
601
606
 
602
- def mouse_clicked(val)
607
+ def mouse_clicked(e,val=nil)
608
+ end
609
+
610
+ def mouse_action(e,val=nil)
603
611
  end
604
612
 
605
613
  end
@@ -550,29 +550,42 @@ end
550
550
  class ResultListViewer < Viewer
551
551
  include ValueViewerInterface
552
552
  include NavViewerConstants
553
-
553
+ #:stopdoc:
554
+ Empty = '(empty)'
555
+ #:startdoc:
556
+
554
557
  class ResultListItem
555
558
  def initialize(id,value)
556
559
  @i = id
557
- @v = value || 'Unavailable'
560
+ @v = value
558
561
  end
559
562
  def id
560
563
  @i
561
564
  end
562
- def value
565
+ def v
563
566
  @v
564
567
  end
568
+ alias_method :value,:v
569
+ def ==(other)
570
+ @v == other.v
571
+ end
572
+ def eql?(other)
573
+ @v.eql?(other.v)
574
+ end
575
+ def <=>(other)
576
+ @v <=> other.v
577
+ end
565
578
  def to_s
566
579
  @v
567
580
  end
568
581
  alias_method :to_str, :to_s
569
- #alias_method :toString, :to_s
570
- end
571
-
572
- def item_value(id)
573
- @main.simple_value(@instance,id)
574
- end
582
+
583
+ end #ResultListItem
575
584
 
585
+ # def item_value(id)
586
+ # @main.simple_value(@instance,id)
587
+ # end
588
+ #
576
589
  def initialize(*r,&k)
577
590
  super
578
591
  create_object_list
@@ -581,19 +594,16 @@ class ResultListViewer < Viewer
581
594
  def create_object_list
582
595
  res = @value.results
583
596
  proxy = @instance.proxy
584
- len = res.length;
585
- list = @obj_list = ::Java::JavaLang::Object[len].new
586
- len.times do |i|
597
+ items = []
598
+ res.length.times do |i|
587
599
  id = res[i]
588
- val = nil
589
- begin
590
- val = proxy.simple_value(id)
591
- rescue
592
- val = 'Unavailable'
600
+ if (val = proxy.simple_value(id) rescue nil)
601
+ val.strip!
602
+ val = Empty if val.empty?
603
+ items << ResultListItem.new(id,val)
593
604
  end
594
- list[i] = ResultListItem.new(id,val)
595
605
  end
596
- list
606
+ @obj_list = items.sort.to_java
597
607
  end
598
608
 
599
609
  def title
@@ -647,8 +657,13 @@ class ResultListViewer < Viewer
647
657
  name_value_row('Class/Module',@value.args.clazz,CFont)
648
658
  if (vars = @value.args.vars) && !vars.empty?
649
659
  vname = vars[0].name.to_s
650
- vval = vars[0].value || ''
651
- vval = vval.inspect if Regexp === vval
660
+ vname = ' ' if vname.empty?
661
+ vval = vars[0].value || ' '
662
+ if Regexp === vval
663
+ vval = vval.inspect
664
+ elsif String === vval
665
+ vval = ' ' if vval.empty?
666
+ end
652
667
  name_value_row('Variable',vname)
653
668
  name_value_row('Value',vval)
654
669
  end
@@ -682,12 +697,64 @@ class ResultListViewer < Viewer
682
697
  super
683
698
  end
684
699
 
685
- def item_selected
700
+ def set_selection
686
701
  if (ix = @list.selected_index) >= 0
687
- @main.new_find(@instance,@value.results[ix])
702
+ @pending = ix
703
+ end
704
+ end
705
+ def send_selection(new_tab=nil)
706
+ if ix = @pending
707
+ @pending = nil
708
+ @main.show_result_object(self,@obj_list[ix].id,new_tab)
709
+ end
710
+ end
711
+ def list_clicked(e)
712
+ list = @list
713
+ point = e.point
714
+ if BUTTON1 == e.button &&
715
+ (ix = list.location_to_index(point)) >= 0 &&
716
+ list.get_cell_bounds(ix,ix).contains(point)
717
+ send_selection((e.modifiers_ex & SHIFT) == SHIFT)
718
+ end
719
+ end
720
+
721
+ def list_keyed(e)
722
+ if @pending && e.action_key?
723
+ send_selection((e.modifiers_ex & SHIFT) == SHIFT)
724
+ end
725
+ end
726
+
727
+ def mouse_action(e)
728
+ if e.popup_trigger?
729
+ list = @list
730
+ point = e.point
731
+ if (ix = list.location_to_index(point)) >= 0 &&
732
+ list.get_cell_bounds(ix,ix).contains(point)
733
+ @menu_pending = ix
734
+ selection_popup.show(e.component,e.x,e.y)
735
+ end
688
736
  end
689
737
  end
690
738
 
739
+ def menu_selection(new_tab)
740
+ if ix = @menu_pending
741
+ @menu_pending = nil
742
+ @list.selected_index = ix
743
+ send_selection(new_tab)
744
+ end
745
+ end
746
+
747
+ def selection_popup
748
+ @popup ||= popup_menu 'Open item in ... ' do
749
+ menu_item 'Open in new tab' do
750
+ on_click {menu_selection true}
751
+ end
752
+ menu_item 'Open in default tab' do
753
+ on_click {menu_selection false}
754
+ end
755
+ end
756
+ end
757
+
691
758
  def result_list
692
759
  @list = list @obj_list do
693
760
  align :LEFT
@@ -696,9 +763,14 @@ class ResultListViewer < Viewer
696
763
  selection_mode :SINGLE_SELECTION
697
764
  on_value_changed do |e|
698
765
  unless e.value_is_adjusting
699
- item_selected
766
+ set_selection
700
767
  end
701
- end
768
+ end
769
+ on_mouse_clicked {|e| list_clicked e}
770
+ on_mouse_pressed {|e| mouse_action e}
771
+ on_mouse_released {|e| mouse_action e}
772
+ on_key_pressed {|e| list_keyed e}
773
+ on_key_released {|e| list_keyed e}
702
774
  end
703
775
  end
704
776
 
@@ -744,7 +816,7 @@ class ResultListViewer < Viewer
744
816
  end
745
817
 
746
818
  def close_view
747
- @main.close_search_view(self)
819
+ @main.close_results_view(self)
748
820
  end
749
821
  end
750
822
 
@@ -818,8 +890,37 @@ class NavObjectViewer
818
890
  end
819
891
  end
820
892
 
821
- def mouse_clicked(val)
822
- @main.new_find(@instance,val)
893
+ def send_selection(id,new_tab=nil)
894
+ @main.show_linked_object(self,id,new_tab)
895
+ end
896
+
897
+ def mouse_clicked(e,id)
898
+ send_selection(id,(e.modifiers_ex & SHIFT) == SHIFT)
899
+ end
900
+
901
+ def mouse_action(e,id)
902
+ if e.popup_trigger?
903
+ @menu_pending = id
904
+ selection_popup.show(e.component,e.x,e.y)
905
+ end
906
+ end
907
+
908
+ def menu_selection(new_tab)
909
+ if id = @menu_pending
910
+ @menu_pending = nil
911
+ send_selection(id,new_tab)
912
+ end
913
+ end
914
+
915
+ def selection_popup
916
+ @popup ||= popup_menu 'Open item in ... ' do
917
+ menu_item 'Open in new tab' do
918
+ on_click {menu_selection true}
919
+ end
920
+ menu_item 'Open in default tab' do
921
+ on_click {menu_selection false}
922
+ end
923
+ end
823
924
  end
824
925
 
825
926
 
@@ -1021,9 +1122,39 @@ class NavModuleViewer
1021
1122
  @children
1022
1123
  end
1023
1124
 
1024
- def mouse_clicked(val)
1025
- @main.new_find(@instance,val)
1125
+ def send_selection(id,new_tab=nil)
1126
+ @main.show_linked_object(self,id,new_tab)
1026
1127
  end
1128
+
1129
+ def mouse_clicked(e,id)
1130
+ send_selection(id,(e.modifiers_ex & SHIFT) == SHIFT)
1131
+ end
1132
+
1133
+ def mouse_action(e,id)
1134
+ if e.popup_trigger?
1135
+ @menu_pending = id
1136
+ selection_popup.show(e.component,e.x,e.y)
1137
+ end
1138
+ end
1139
+
1140
+ def menu_selection(new_tab)
1141
+ if id = @menu_pending
1142
+ @menu_pending = nil
1143
+ send_selection(id,new_tab)
1144
+ end
1145
+ end
1146
+
1147
+ def selection_popup
1148
+ @popup ||= popup_menu 'Open item in ... ' do
1149
+ menu_item 'Open in new tab' do
1150
+ on_click {menu_selection true}
1151
+ end
1152
+ menu_item 'Open in default tab' do
1153
+ on_click {menu_selection false}
1154
+ end
1155
+ end
1156
+ end
1157
+
1027
1158
 
1028
1159
  def view(&block)
1029
1160
  @view ||= scroll_pane do
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: cheri
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.4
7
- date: 2007-06-10 00:00:00 -07:00
6
+ version: 0.0.5
7
+ date: 2007-06-11 00:00:00 -07:00
8
8
  summary: Cheri Builder Platform
9
9
  require_paths:
10
10
  - lib
@@ -75,6 +75,7 @@ files:
75
75
  - lib/cheri/explorer.rb
76
76
  - lib/cheri/html.rb
77
77
  - lib/cheri/image
78
+ - lib/cheri/image/Back24.gif
78
79
  - lib/cheri/image/cheri_icon_16x16.png
79
80
  - lib/cheri/image/cheri_icon_24x24.png
80
81
  - lib/cheri/image/cheri_logo_medium.png
@@ -90,6 +91,7 @@ files:
90
91
  - lib/cheri/image/Delete24.gif
91
92
  - lib/cheri/image/Find24.gif
92
93
  - lib/cheri/image/FindAgain24.gif
94
+ - lib/cheri/image/Forward24.gif
93
95
  - lib/cheri/image/jruby_14x16.png
94
96
  - lib/cheri/image/jruby_logo.png
95
97
  - lib/cheri/image/mod_tree.png