Neurogami-jimpanzee 1.0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/Jimpanzee.gemspec +0 -0
- data/Manifest.txt +62 -0
- data/README.md +23 -0
- data/Rakefile +98 -0
- data/bin/jimpanzee +41 -0
- data/lib/monkeybars.rb +7 -0
- data/lib/monkeybars/controller.rb +608 -0
- data/lib/monkeybars/debug.rb +69 -0
- data/lib/monkeybars/event_handler.rb +89 -0
- data/lib/monkeybars/event_handler_registration_and_dispatch_mixin.rb +251 -0
- data/lib/monkeybars/exceptions.rb +10 -0
- data/lib/monkeybars/global_error_handler.rb +34 -0
- data/lib/monkeybars/inflector.rb +68 -0
- data/lib/monkeybars/key.rb +206 -0
- data/lib/monkeybars/task_processor.rb +47 -0
- data/lib/monkeybars/validated_hash.rb +22 -0
- data/lib/monkeybars/view.rb +609 -0
- data/lib/monkeybars/view_mapping.rb +379 -0
- data/lib/monkeybars/view_nesting.rb +114 -0
- data/lib/monkeybars/view_positioning.rb +66 -0
- data/skeleton/Rakefile +5 -0
- data/skeleton/lib/java/README.txt +1 -0
- data/skeleton/lib/java/monkeybars-1.0.2.1.jar +0 -0
- data/skeleton/lib/ruby/README.md +1 -0
- data/skeleton/lib/ruby/README.txt +1 -0
- data/skeleton/src/application_controller.rb +4 -0
- data/skeleton/src/application_view.rb +3 -0
- data/skeleton/src/main.rb +52 -0
- data/skeleton/src/manifest.rb +58 -0
- data/skeleton/src/resolver.rb +33 -0
- data/skeleton/tasks/monkeybars.rake +89 -0
- metadata +99 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
# This code is a modified version of the Inflector class
|
2
|
+
# from the Ruby on Rails project (http://www.rubyonrails.com)
|
3
|
+
|
4
|
+
module Monkeybars
|
5
|
+
module Inflector
|
6
|
+
# The reverse of +camelize+. Makes an underscored form from the expression in the string.
|
7
|
+
#
|
8
|
+
# Changes '::' to '/' to convert namespaces to paths.
|
9
|
+
#
|
10
|
+
# Examples
|
11
|
+
# "ActiveRecord".underscore #=> "active_record"
|
12
|
+
# "ActiveRecord::Errors".underscore #=> active_record/errors
|
13
|
+
def underscore()
|
14
|
+
self.to_s.gsub(/::/, '/').
|
15
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
16
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
17
|
+
tr("-", "_").
|
18
|
+
downcase
|
19
|
+
end
|
20
|
+
|
21
|
+
# Constantize tries to find a declared constant with the name specified
|
22
|
+
# in the string. It raises a NameError when the name is not in CamelCase
|
23
|
+
# or is not initialized.
|
24
|
+
#
|
25
|
+
# Examples
|
26
|
+
# "Module".constantize #=> Module
|
27
|
+
# "Class".constantize #=> Class
|
28
|
+
def constantize()
|
29
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self.to_s
|
30
|
+
raise NameError, "#{self.inspect} is not a valid constant name!"
|
31
|
+
end
|
32
|
+
|
33
|
+
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
34
|
+
end
|
35
|
+
|
36
|
+
# By default, camelize converts strings to UpperCamelCase. If the argument to camelize
|
37
|
+
# is set to ":lower" then camelize produces lowerCamelCase.
|
38
|
+
#
|
39
|
+
# camelize will also convert '/' to '::' which is useful for converting paths to namespaces
|
40
|
+
#
|
41
|
+
# Examples
|
42
|
+
# "active_record".camelize #=> "ActiveRecord"
|
43
|
+
# "active_record".camelize(:lower) #=> "activeRecord"
|
44
|
+
# "active_record/errors".camelize #=> "ActiveRecord::Errors"
|
45
|
+
# "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
|
46
|
+
def camelize(first_letter_in_uppercase = true)
|
47
|
+
if first_letter_in_uppercase
|
48
|
+
self.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
49
|
+
else
|
50
|
+
self.to_s[0..0] + camelize(self.to_s)[1..-1]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class String
|
57
|
+
include Monkeybars::Inflector
|
58
|
+
end
|
59
|
+
|
60
|
+
class Symbol
|
61
|
+
include Monkeybars::Inflector
|
62
|
+
end
|
63
|
+
|
64
|
+
class Class
|
65
|
+
def constantize
|
66
|
+
self
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module Monkeybars
|
2
|
+
class Key
|
3
|
+
@@codes = {}
|
4
|
+
@@symbols = {}
|
5
|
+
[[:VK_0,48],
|
6
|
+
[:VK_1,49],
|
7
|
+
[:VK_2,50],
|
8
|
+
[:VK_3,51],
|
9
|
+
[:VK_4,52],
|
10
|
+
[:VK_5,53],
|
11
|
+
[:VK_6,54],
|
12
|
+
[:VK_7,55],
|
13
|
+
[:VK_8,56],
|
14
|
+
[:VK_9,57],
|
15
|
+
[:VK_A,65],
|
16
|
+
[:VK_ACCEPT,30],
|
17
|
+
[:VK_ADD,107],
|
18
|
+
[:VK_AGAIN,65481],
|
19
|
+
[:VK_ALL_CANDIDATES,256],
|
20
|
+
[:VK_ALPHANUMERIC,240],
|
21
|
+
[:VK_ALT,18],
|
22
|
+
[:VK_ALT_GRAPH,65406],
|
23
|
+
[:VK_AMPERSAND,150],
|
24
|
+
[:VK_ASTERISK,151],
|
25
|
+
[:VK_AT,512],
|
26
|
+
[:VK_B,66],
|
27
|
+
[:VK_BACK_QUOTE,192],
|
28
|
+
[:VK_BACK_SLASH,92],
|
29
|
+
[:VK_BACK_SPACE,8],
|
30
|
+
[:VK_BEGIN,65368],
|
31
|
+
[:VK_BRACELEFT,161],
|
32
|
+
[:VK_BRACERIGHT,162],
|
33
|
+
[:VK_C,67],
|
34
|
+
[:VK_CANCEL,3],
|
35
|
+
[:VK_CAPS_LOCK,20],
|
36
|
+
[:VK_CIRCUMFLEX,514],
|
37
|
+
[:VK_CLEAR,12],
|
38
|
+
[:VK_CLOSE_BRACKET,93],
|
39
|
+
[:VK_CODE_INPUT,258],
|
40
|
+
[:VK_COLON,513],
|
41
|
+
[:VK_COMMA,44],
|
42
|
+
[:VK_COMPOSE,65312],
|
43
|
+
[:VK_CONTEXT_MENU,525],
|
44
|
+
[:VK_CONTROL,17],
|
45
|
+
[:VK_CONVERT,28],
|
46
|
+
[:VK_COPY,65485],
|
47
|
+
[:VK_CUT,65489],
|
48
|
+
[:VK_D,68],
|
49
|
+
[:VK_DEAD_ABOVEDOT,134],
|
50
|
+
[:VK_DEAD_ABOVERING,136],
|
51
|
+
[:VK_DEAD_ACUTE,129],
|
52
|
+
[:VK_DEAD_BREVE,133],
|
53
|
+
[:VK_DEAD_CARON,138],
|
54
|
+
[:VK_DEAD_CEDILLA,139],
|
55
|
+
[:VK_DEAD_CIRCUMFLEX,130],
|
56
|
+
[:VK_DEAD_DIAERESIS,135],
|
57
|
+
[:VK_DEAD_DOUBLEACUTE,137],
|
58
|
+
[:VK_DEAD_GRAVE,128],
|
59
|
+
[:VK_DEAD_IOTA,141],
|
60
|
+
[:VK_DEAD_MACRON,132],
|
61
|
+
[:VK_DEAD_OGONEK,140],
|
62
|
+
[:VK_DEAD_SEMIVOICED_SOUND,143],
|
63
|
+
[:VK_DEAD_TILDE,131],
|
64
|
+
[:VK_DEAD_VOICED_SOUND,142],
|
65
|
+
[:VK_DECIMAL,110],
|
66
|
+
[:VK_DELETE,127],
|
67
|
+
[:VK_DIVIDE,111],
|
68
|
+
[:VK_DOLLAR,515],
|
69
|
+
[:VK_DOWN,40],
|
70
|
+
[:VK_E,69],
|
71
|
+
[:VK_END,35],
|
72
|
+
[:VK_ENTER,10],
|
73
|
+
[:VK_EQUALS,61],
|
74
|
+
[:VK_ESCAPE,27],
|
75
|
+
[:VK_EURO_SIGN,516],
|
76
|
+
[:VK_EXCLAMATION_MARK,517],
|
77
|
+
[:VK_F,70],
|
78
|
+
[:VK_F1,112],
|
79
|
+
[:VK_F10,121],
|
80
|
+
[:VK_F11,122],
|
81
|
+
[:VK_F12,123],
|
82
|
+
[:VK_F13,61440],
|
83
|
+
[:VK_F14,61441],
|
84
|
+
[:VK_F15,61442],
|
85
|
+
[:VK_F16,61443],
|
86
|
+
[:VK_F17,61444],
|
87
|
+
[:VK_F18,61445],
|
88
|
+
[:VK_F19,61446],
|
89
|
+
[:VK_F2,113],
|
90
|
+
[:VK_F20,61447],
|
91
|
+
[:VK_F21,61448],
|
92
|
+
[:VK_F22,61449],
|
93
|
+
[:VK_F23,61450],
|
94
|
+
[:VK_F24,61451],
|
95
|
+
[:VK_F3,114],
|
96
|
+
[:VK_F4,115],
|
97
|
+
[:VK_F5,116],
|
98
|
+
[:VK_F6,117],
|
99
|
+
[:VK_F7,118],
|
100
|
+
[:VK_F8,119],
|
101
|
+
[:VK_F9,120],
|
102
|
+
[:VK_FINAL,24],
|
103
|
+
[:VK_FIND,65488],
|
104
|
+
[:VK_FULL_WIDTH,243],
|
105
|
+
[:VK_G,71],
|
106
|
+
[:VK_GREATER,160],
|
107
|
+
[:VK_H,72],
|
108
|
+
[:VK_HALF_WIDTH,244],
|
109
|
+
[:VK_HELP,156],
|
110
|
+
[:VK_HIRAGANA,242],
|
111
|
+
[:VK_HOME,36],
|
112
|
+
[:VK_I,73],
|
113
|
+
[:VK_INPUT_METHOD_ON_OFF,263],
|
114
|
+
[:VK_INSERT,155],
|
115
|
+
[:VK_INVERTED_EXCLAMATION_MARK,518],
|
116
|
+
[:VK_J,74],
|
117
|
+
[:VK_JAPANESE_HIRAGANA,260],
|
118
|
+
[:VK_JAPANESE_KATAKANA,259],
|
119
|
+
[:VK_JAPANESE_ROMAN,261],
|
120
|
+
[:VK_K,75],
|
121
|
+
[:VK_KANA,21],
|
122
|
+
[:VK_KANA_LOCK,262],
|
123
|
+
[:VK_KANJI,25],
|
124
|
+
[:VK_KATAKANA,241],
|
125
|
+
[:VK_KP_DOWN,225],
|
126
|
+
[:VK_KP_LEFT,226],
|
127
|
+
[:VK_KP_RIGHT,227],
|
128
|
+
[:VK_KP_UP,224],
|
129
|
+
[:VK_L,76],
|
130
|
+
[:VK_LEFT,37],
|
131
|
+
[:VK_LEFT_PARENTHESIS,519],
|
132
|
+
[:VK_LESS,153],
|
133
|
+
[:VK_M,77],
|
134
|
+
[:VK_META,157],
|
135
|
+
[:VK_MINUS,45],
|
136
|
+
[:VK_MODECHANGE,31],
|
137
|
+
[:VK_MULTIPLY,106],
|
138
|
+
[:VK_N,78],
|
139
|
+
[:VK_NONCONVERT,29],
|
140
|
+
[:VK_NUM_LOCK,144],
|
141
|
+
[:VK_NUMBER_SIGN,520],
|
142
|
+
[:VK_NUMPAD0,96],
|
143
|
+
[:VK_NUMPAD1,97],
|
144
|
+
[:VK_NUMPAD2,98],
|
145
|
+
[:VK_NUMPAD3,99],
|
146
|
+
[:VK_NUMPAD4,100],
|
147
|
+
[:VK_NUMPAD5,101],
|
148
|
+
[:VK_NUMPAD6,102],
|
149
|
+
[:VK_NUMPAD7,103],
|
150
|
+
[:VK_NUMPAD8,104],
|
151
|
+
[:VK_NUMPAD9,105],
|
152
|
+
[:VK_O,79],
|
153
|
+
[:VK_OPEN_BRACKET,91],
|
154
|
+
[:VK_P,80],
|
155
|
+
[:VK_PAGE_DOWN,34],
|
156
|
+
[:VK_PAGE_UP,33],
|
157
|
+
[:VK_PASTE,65487],
|
158
|
+
[:VK_PAUSE,19],
|
159
|
+
[:VK_PERIOD,46],
|
160
|
+
[:VK_PLUS,521],
|
161
|
+
[:VK_PREVIOUS_CANDIDATE,257],
|
162
|
+
[:VK_PRINTSCREEN,154],
|
163
|
+
[:VK_PROPS,65482],
|
164
|
+
[:VK_Q,81],
|
165
|
+
[:VK_QUOTE,222],
|
166
|
+
[:VK_QUOTEDBL,152],
|
167
|
+
[:VK_R,82],
|
168
|
+
[:VK_RIGHT,39],
|
169
|
+
[:VK_RIGHT_PARENTHESIS,522],
|
170
|
+
[:VK_ROMAN_CHARACTERS,245],
|
171
|
+
[:VK_S,83],
|
172
|
+
[:VK_SCROLL_LOCK,145],
|
173
|
+
[:VK_SEMICOLON,59],
|
174
|
+
[:VK_SEPARATER,108],
|
175
|
+
[:VK_SEPARATOR,108],
|
176
|
+
[:VK_SHIFT,16],
|
177
|
+
[:VK_SLASH,47],
|
178
|
+
[:VK_SPACE,32],
|
179
|
+
[:VK_STOP,65480],
|
180
|
+
[:VK_SUBTRACT,109],
|
181
|
+
[:VK_T,84],
|
182
|
+
[:VK_TAB,9],
|
183
|
+
[:VK_U,85],
|
184
|
+
[:VK_UNDEFINED,0],
|
185
|
+
[:VK_UNDERSCORE,523],
|
186
|
+
[:VK_UNDO,65483],
|
187
|
+
[:VK_UP,38],
|
188
|
+
[:VK_V,86],
|
189
|
+
[:VK_W,87],
|
190
|
+
[:VK_WINDOWS,524],
|
191
|
+
[:VK_X,88],
|
192
|
+
[:VK_Y,89],
|
193
|
+
[:VK_Z,90]].each do |pair|
|
194
|
+
@@codes[pair[1]] = pair[0]
|
195
|
+
@@symbols[pair[0]] = pair[1]
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.code_to_symbol(code)
|
199
|
+
@@codes[code]
|
200
|
+
end
|
201
|
+
|
202
|
+
def self.symbol_to_code(symbol)
|
203
|
+
@@symbols[symbol]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
include_class "foxtrot.Worker"
|
2
|
+
include_class "foxtrot.Job"
|
3
|
+
|
4
|
+
module Monkeybars
|
5
|
+
# Module that contains methods and classes used to take care of background
|
6
|
+
# task processing. Primarily this is the repaint_while method.
|
7
|
+
module TaskProcessor
|
8
|
+
# Passes the supplied block to a separate thread and returns the result
|
9
|
+
# of the executed block back to the caller. This should be utilized for long-
|
10
|
+
# running tasks that ought not tie up the Swing Event Dispatch Thread.
|
11
|
+
# Passing a block to this method will allow the GUI to remain responsive
|
12
|
+
# (and repaint), while the long-running task is executing.
|
13
|
+
def repaint_while(&task)
|
14
|
+
runner = Runner.new(&task)
|
15
|
+
Worker.post(runner)
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_edt(&task)
|
19
|
+
if javax.swing.SwingUtilities.event_dispatch_thread?
|
20
|
+
javax.swing.SwingUtilities.invoke_later Runnable.new(task)
|
21
|
+
else
|
22
|
+
javax.swing.SwingUtilities.invoke_and_wait Runnable.new(task)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Runner < Job
|
27
|
+
def initialize(&proc)
|
28
|
+
@proc = proc
|
29
|
+
end
|
30
|
+
|
31
|
+
def run
|
32
|
+
@proc.call
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Runnable
|
37
|
+
include Java::java::lang::Runnable
|
38
|
+
def initialize(explicit_block=nil, &block)
|
39
|
+
@block = explicit_block || block
|
40
|
+
end
|
41
|
+
|
42
|
+
def run
|
43
|
+
@block.call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class InvalidHashKeyError < Exception; end
|
2
|
+
|
3
|
+
module ValidatedHash
|
4
|
+
# Raises an exception if a key in the hash does not exist in the list of valid keys
|
5
|
+
def validate_only(*keys)
|
6
|
+
self.keys.each {|key| raise InvalidHashKeyError.new("#{key} is not a valid key for this hash") unless keys.member?(key)}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Raises an exception if any of the keys provided are not found in the hash
|
10
|
+
def validate_all(*keys)
|
11
|
+
keys.each {|key| raise InvalidHashKeyError.new("#{key} is required for this hash") unless self.keys.member?(key)}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Raises an exception if any of the keys provided are found in the hash
|
15
|
+
def validate_none(*keys)
|
16
|
+
keys.each {|key| raise InvalidHashKeyError.new("#{key} is not allowed for this hash") if self.keys.member?(key)}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Hash
|
21
|
+
include ValidatedHash
|
22
|
+
end
|
@@ -0,0 +1,609 @@
|
|
1
|
+
include_class javax.swing.JComponent
|
2
|
+
include_class javax.swing.KeyStroke
|
3
|
+
|
4
|
+
require 'monkeybars/exceptions'
|
5
|
+
require 'monkeybars/inflector'
|
6
|
+
require 'monkeybars/validated_hash'
|
7
|
+
require 'monkeybars/view_mapping'
|
8
|
+
require 'monkeybars/task_processor'
|
9
|
+
require 'monkeybars/view_nesting'
|
10
|
+
require 'monkeybars/view_positioning'
|
11
|
+
require "monkeybars/event_handler_registration_and_dispatch_mixin"
|
12
|
+
|
13
|
+
module Monkeybars
|
14
|
+
# The view is the gatekeeper to the actual Java (or sometimes non-Java) view class.
|
15
|
+
# The view defines how data moves into and out of the view via the model.
|
16
|
+
#
|
17
|
+
# Any property of the underlying "real" view can be accessed as if it were a
|
18
|
+
# property of the view class.
|
19
|
+
#
|
20
|
+
# Thus if you have a JFrame that has a member variable okButton, you could do
|
21
|
+
# the following:
|
22
|
+
#
|
23
|
+
# okButton.text = "Confirm"
|
24
|
+
#
|
25
|
+
# You must use the exact name that is used in the underlying view, no normal
|
26
|
+
# JRuby javaName to java_name conversion is performed. ok_button.text = "Confirm"
|
27
|
+
# would fail.
|
28
|
+
#
|
29
|
+
# Example, (assume a JFrame with a label, text area and button):
|
30
|
+
#
|
31
|
+
# require 'monkeybars'
|
32
|
+
#
|
33
|
+
# class MyView < Monkeybars::View
|
34
|
+
# set_java_class "com.project.MyCoolJFrame"
|
35
|
+
# map :view => "titleLabel.text", :model => :title_text
|
36
|
+
# map :view => "okButton.text", :model => :button_text
|
37
|
+
# map :view => "mainTextArea.text", :model => :text, :using => [:convert_to_string, :convert_to_array]
|
38
|
+
# map :view => "titleLabel.selected_text_color", :transfer => :text_color
|
39
|
+
# COLOR_TRANSLATION = {:red => Color.new(1.0, 0.0, 0.0), :green => Color.new(0.0, 1.0, 0.0), :blue => Color.new(0.0, 0.0, 1.0)}
|
40
|
+
# map :view => "titleLabel.selected_text_color", :model => :text, :translate_using => COLOR_TRANSLATION
|
41
|
+
#
|
42
|
+
# def convert_to_string(model)
|
43
|
+
# model.text.join("\n")
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# def convert_to_array(model)
|
47
|
+
# mainTextArea.text.split("\n")
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# It is important that you do not implement your own initialize method, doing so will
|
52
|
+
# interfere with the operation of the View class (or if you must, remember to
|
53
|
+
# call super on the first line of your initialize).
|
54
|
+
#
|
55
|
+
# It is possible to have a view that is not related to a Java class, in which
|
56
|
+
# case no set_java_class delcaration is used. If using a pure Ruby view (or
|
57
|
+
# manually wrapping a Java class) you must set the @main_view_component
|
58
|
+
# member variable yourself. The object you assign to @main_view_component
|
59
|
+
# should respond to any methods that normally interact with the Java object such
|
60
|
+
# as visisble?, hide, and dispose
|
61
|
+
class View
|
62
|
+
include TaskProcessor
|
63
|
+
include EventHandlerRegistrationAndDispatchMixin
|
64
|
+
include Positioning
|
65
|
+
|
66
|
+
module CloseActions #:nodoc:
|
67
|
+
DO_NOTHING = javax::swing::WindowConstants::DO_NOTHING_ON_CLOSE
|
68
|
+
DISPOSE = javax::swing::WindowConstants::DISPOSE_ON_CLOSE
|
69
|
+
EXIT = javax::swing::WindowConstants::EXIT_ON_CLOSE
|
70
|
+
HIDE = javax::swing::WindowConstants::HIDE_ON_CLOSE
|
71
|
+
METHOD = :method
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
@@view_nestings_for_child_view ||= {}
|
76
|
+
def self.view_nestings
|
77
|
+
@@view_nestings_for_child_view[self] ||= {}
|
78
|
+
end
|
79
|
+
|
80
|
+
@@view_mappings_for_child_view ||= {}
|
81
|
+
def self.view_mappings
|
82
|
+
@@view_mappings_for_child_view[self] ||= []
|
83
|
+
end
|
84
|
+
|
85
|
+
@@java_class_for_child_view ||= {}
|
86
|
+
def self.instance_java_class
|
87
|
+
@@java_class_for_child_view[self]
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.instance_java_class=(java_class)
|
91
|
+
@@java_class_for_child_view[self] = java_class
|
92
|
+
end
|
93
|
+
|
94
|
+
@@signal_mappings ||= {}
|
95
|
+
def self.signal_mappings
|
96
|
+
@@signal_mappings[self] ||= {}
|
97
|
+
end
|
98
|
+
|
99
|
+
public
|
100
|
+
# Declares what class to instantiate when creating the view. Any listeners
|
101
|
+
# set on the component are added to this class as well as the setting of the
|
102
|
+
# close action that is defined in the controller.
|
103
|
+
# Accepts a string that is the Java package, or a class for the Java or JRuby UI object
|
104
|
+
def self.set_java_class(java_class)
|
105
|
+
# We're allowing two options: The existing "Give me a string", and
|
106
|
+
# passing a constant (which is new behavior).
|
107
|
+
# In a view class, the develoepr can simply give the name of a defined class
|
108
|
+
# to use, in which case this code does not need to try to load anything.
|
109
|
+
if java_class.is_a?(String)
|
110
|
+
include_class java_class
|
111
|
+
class_name = /.*?\.?(\w+)$/.match(java_class)[1]
|
112
|
+
self.instance_java_class = const_get(class_name)
|
113
|
+
elsif java_class.is_a?(Class)
|
114
|
+
self.instance_java_class = java_class
|
115
|
+
else
|
116
|
+
raise "Setting the view class requires either a string naming the class to load, or an actual class constant. set_java_class was given #{java_class.inspect}."
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Declares a mapping between the properties of the view and either the model's
|
121
|
+
# or the transfer's properties. This mapping is used when creating the model.
|
122
|
+
# If you wish to trigger subsequent updates of the view, you may call
|
123
|
+
# update_view manually from the controller.
|
124
|
+
#
|
125
|
+
# There are several ways to declare a mapping based on what level of control
|
126
|
+
# you need over the process. The simplest form is:
|
127
|
+
#
|
128
|
+
# map :view => :foo, :model => :bar
|
129
|
+
#
|
130
|
+
# or
|
131
|
+
#
|
132
|
+
# map :view => :foo, :transfer => :bar
|
133
|
+
#
|
134
|
+
# Which means, when update is called, self.foo = model.bar and
|
135
|
+
# self.foo = transfer[:bar] respectively.
|
136
|
+
#
|
137
|
+
# Strings may be used interchangably with symbols for model mappings. If you
|
138
|
+
# have nested view or properties you may specify them as a string:
|
139
|
+
#
|
140
|
+
# map :view => "foo.sub_property", :model => "bar.other_sub_property"
|
141
|
+
#
|
142
|
+
# which means, self.foo.sub_property = model.bar.other_sub_property
|
143
|
+
#
|
144
|
+
# It should be noted that these mappings are bi-directional. They are
|
145
|
+
# referenced for both update and write_state. When used
|
146
|
+
# for write_state the assignment direction is reversed, so a view with
|
147
|
+
#
|
148
|
+
# map :view => :foo, :model => :bar
|
149
|
+
#
|
150
|
+
# would mean model.bar = self.foo
|
151
|
+
#
|
152
|
+
# When a direct assignment is not sufficient you may provide a method to
|
153
|
+
# filter or adapt the contents of the model's value before assignment to
|
154
|
+
# the view. This is accomplished by adding a :using key to the hash.
|
155
|
+
# The key's value is an array of the method names to be used when converting from
|
156
|
+
# the model to the view, and when converting from the view back to the model.
|
157
|
+
# If you only want to use a custom method for either the conversion from a model
|
158
|
+
# to a view or vice versa, you can specify :default, for the other parameter
|
159
|
+
# and the normal mapping will take place. If you want to disable the copying
|
160
|
+
# of data in one direction you can pass nil as the method parameter.
|
161
|
+
#
|
162
|
+
# map :view => :foo, :model => :bar, :using => [:from_model, :to_model]
|
163
|
+
#
|
164
|
+
# would mean self.foo = from_model() when called by update and
|
165
|
+
# model.bar = to_model() when called by write_state.
|
166
|
+
#
|
167
|
+
# map :view => :foo, :model => :bar, :using => [:from_model, :default]
|
168
|
+
#
|
169
|
+
# would mean self.foo = from_model() when called by update and
|
170
|
+
# model.bar = self.foo when called by write_state.
|
171
|
+
#
|
172
|
+
# map :view => :foo, :model => :bar, :using => [:from_model, nil]
|
173
|
+
#
|
174
|
+
# would mean self.foo = from_model() when called by update and
|
175
|
+
# would do nothing when called by write_state.
|
176
|
+
#
|
177
|
+
# For constant value translation, :translate_using provides a one-line approach.
|
178
|
+
# :translate_using takes a hash with the model values as the key, and the view values as the value.
|
179
|
+
# This only works when the view state and the model state has a one-to-one translation.
|
180
|
+
#
|
181
|
+
# COLOR_TRANSLATION = {:red => Color.new(1.0, 0.0, 0.0), :green => Color.new(0.0, 1.0, 0.0), :blue => Color.new(0.0, 0.0, 1.0)}
|
182
|
+
# map :view => "titleLabel.selected_text_color", :model => :text, :translate_using => COLOR_TRANSLATION
|
183
|
+
#
|
184
|
+
# If you want to invoke disable_handlers during the call to update
|
185
|
+
# you can add the :ignoring key. The key's value is either a single type or
|
186
|
+
# an array of types to be ignored during the update process.
|
187
|
+
#
|
188
|
+
# map :view => :foo, :model => :bar, :ignoring => :item
|
189
|
+
#
|
190
|
+
# This will wrap up the update in a call to disable_handlers on the view
|
191
|
+
# component, which is assumed to be the first part of the mapping UP TO THE
|
192
|
+
# FIRST PERIOD. This means that
|
193
|
+
#
|
194
|
+
# map :view => "foo.bar", :model => :model_property, :ignoring => :item
|
195
|
+
#
|
196
|
+
# would translate to
|
197
|
+
#
|
198
|
+
# foo.disable_handlers(:item) do
|
199
|
+
# foo.bar = model.model_property
|
200
|
+
# end
|
201
|
+
#
|
202
|
+
# during a call to update. During write_to_model, the ignoring
|
203
|
+
# definition has no meaning as there are no event handlers on models.
|
204
|
+
#
|
205
|
+
# The final option for mapping properties is a simply your own method. As with
|
206
|
+
# a method provided via the using method you may provide a method for conversion
|
207
|
+
# into the view, out of the view or both.
|
208
|
+
#
|
209
|
+
# raw_mapping :from_model, :to_model
|
210
|
+
#
|
211
|
+
# would simply invoke the associated method when update or
|
212
|
+
# write_state was called. Thus any assignment to view properties
|
213
|
+
# must be done within the method (hence the 'raw').
|
214
|
+
#
|
215
|
+
# To disable handlers in a raw mapping, call Component#disable_handlers
|
216
|
+
# inside your mapping method
|
217
|
+
#
|
218
|
+
def self.map(properties)
|
219
|
+
mapping = Mapping.new(properties)
|
220
|
+
view_mappings << mapping
|
221
|
+
end
|
222
|
+
|
223
|
+
# See View.map
|
224
|
+
def self.raw_mapping(to_view_method, from_view_method, handlers_to_ignore = [])
|
225
|
+
view_mappings << Mapping.new(:using => [to_view_method, from_view_method], :ignoring => handlers_to_ignore)
|
226
|
+
end
|
227
|
+
|
228
|
+
# Declares a mapping between a signal and a method to process the signal. When
|
229
|
+
# the signal is received, the method is called with the model and the transfer as parameters.
|
230
|
+
# If a signal is sent that is not defined, an UnknownSignalError exception is raised.
|
231
|
+
#
|
232
|
+
# define_signal :name => :error_state, :handler => :disable_go_button
|
233
|
+
#
|
234
|
+
# def disable_go_button(model, transfer)
|
235
|
+
# go_button.enabled = false
|
236
|
+
# end
|
237
|
+
#
|
238
|
+
def self.define_signal(options, method_name = nil)
|
239
|
+
if options.kind_of? Hash
|
240
|
+
begin
|
241
|
+
options.validate_only :name, :handler
|
242
|
+
options.validate_all :name, :handler
|
243
|
+
rescue InvalidHashKeyError
|
244
|
+
raise InvalidSignalError, ":signal and :handler must be provided for define_signal. Options provided: #{options.inspect}"
|
245
|
+
end
|
246
|
+
signal_mappings[options[:name]] = options[:handler]
|
247
|
+
else
|
248
|
+
#support two styles for now, deprecating the old (signal, method_name) style
|
249
|
+
warn "The usage of define_signal(signal, method_name) has been deprecated, please use define_signal :name => :signal, :handler => :method_name"
|
250
|
+
signal_mappings[options] = method_name
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
# Declares how nested views from their respective nested controllers are to be
|
256
|
+
# added and removed from the view. Multiple nestings for the different nested
|
257
|
+
# controllers are possible through the :sub_view value, which is basically a grouping
|
258
|
+
# name. Two kinds of mapping are possible: A property nesting, and a method nesting.
|
259
|
+
# Property nesting:
|
260
|
+
#
|
261
|
+
# nest :sub_view => :user_list, :view => :user_panel
|
262
|
+
#
|
263
|
+
# This essentially calls user_panel.add nested_view_component on Monkeybars::Controller#add_nested_controller
|
264
|
+
# and user_panel.remove nested_view_componenent on Monkeybars::Controller#remove_nested_controller.
|
265
|
+
# A layout on the container being used is preferred, as no orientational information will be conveyed
|
266
|
+
# to either component.
|
267
|
+
#
|
268
|
+
# Method nesting:
|
269
|
+
#
|
270
|
+
# nest :sub_view => :user_list, :using => [:add_user, :remove_user]
|
271
|
+
#
|
272
|
+
# def add_user(nested_view, nested_component, model, transfer)
|
273
|
+
# user_panel.add nested_component
|
274
|
+
# nested_component.set_location(nested_component.x, nested_component.height * transfer[:user_list_size])
|
275
|
+
# end
|
276
|
+
#
|
277
|
+
# def remove_user(nested_view, nested_component, model, transfer)
|
278
|
+
# # nested_view is the Ruby view object
|
279
|
+
# # nested_component is the Java form, aka @main_view_component
|
280
|
+
#
|
281
|
+
# user_panel.remove nested_component
|
282
|
+
# # lots of code to re-order previous components
|
283
|
+
# end
|
284
|
+
#
|
285
|
+
# Method nesting calls the methods in :using (in the same way that :using works for mapping) during
|
286
|
+
# add and remove (Monkeybars::Controller#add_nested_controller and Monkeybars::Controller#remove_nested_controller).
|
287
|
+
# Both methods are passed in the view, the view's main view component, the mode, and the transfer, respectively.
|
288
|
+
#
|
289
|
+
# New users using Netbeans will need to know that the GroupLayout (aka FreeDesign) is a picky layout that demands
|
290
|
+
# constraints while adding components. By default, all containers use GroupLayout in Netbeans's GUI builder. If you're
|
291
|
+
# not sure what all this means, just start off with a BoxLayout (which is not picky). If your layout needs constraints,
|
292
|
+
# you will need to pass them in with your Method Nesting. Some layouts even need @main_view_component#revalidate to be
|
293
|
+
# called. In short, be aware of Swing's quirks.
|
294
|
+
def self.nest(properties)
|
295
|
+
view_nestings[properties[:sub_view]] ||= []
|
296
|
+
view_nestings[properties[:sub_view]] << Nesting.new(properties)
|
297
|
+
end
|
298
|
+
|
299
|
+
def initialize
|
300
|
+
@__field_references = {}
|
301
|
+
# We have at three possibilities:
|
302
|
+
# - The UI form is a Java class all the way; that it, it came from Java code compiled into a .class file.
|
303
|
+
# - The UI form was written in Ruby, but inherits from a Java class (e.g. JFrame). It is quite servicable
|
304
|
+
# for the UI, but will behave differently in regards to Java reflection -- We'll refer to this as a JRuby class
|
305
|
+
# - The UI form is not Java at all.
|
306
|
+
@main_view_component = create_main_view_component
|
307
|
+
raise MissingMainViewComponentError, "Missing @main_view_component. Use View.set_java_class or override create_main_view_component." if @main_view_component.nil?
|
308
|
+
@is_java_class = !@main_view_component.class.respond_to?(:java_proxy_class)
|
309
|
+
setup_implicit_and_explicit_event_handlers
|
310
|
+
load
|
311
|
+
end
|
312
|
+
|
313
|
+
# This is set via the constructor, do not call directly unless you know what
|
314
|
+
# you are doing.
|
315
|
+
#
|
316
|
+
# Defines the close action for a visible window. This method is only applicable
|
317
|
+
# for JFrame or decendants, it is ignored for all other classes.
|
318
|
+
#
|
319
|
+
# close_action expects an action from Monkeybars::View::CloseActions
|
320
|
+
#
|
321
|
+
# The CloseActions::METHOD action expects a second parameter which should be a
|
322
|
+
# MonkeybarsWindowAdapter.
|
323
|
+
def close_action(action, handler = nil)
|
324
|
+
if @main_view_component.kind_of?(javax.swing.JFrame) || @main_view_component.kind_of?(javax.swing.JInternalFrame) || @main_view_component.kind_of?(javax.swing.JDialog)
|
325
|
+
if CloseActions::METHOD == action
|
326
|
+
@main_view_component.default_close_operation = CloseActions::DO_NOTHING
|
327
|
+
unless @main_view_component.kind_of?(javax.swing.JInternalFrame)
|
328
|
+
@main_view_component.add_window_listener(handler)
|
329
|
+
else
|
330
|
+
@main_view_component.add_internal_frame_listener(handler)
|
331
|
+
end
|
332
|
+
else
|
333
|
+
@main_view_component.set_default_close_operation(action)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# This method is called when Controller#load has completed (usually during Controller#open)
|
339
|
+
# but before the view is shown. This method is meant to be overriden in views that
|
340
|
+
# need control over how their mappings are initially run. By overriding this method
|
341
|
+
# you could use disable_handlers to disable certain handlers during the
|
342
|
+
# initial mapping process or perform some actions after the mappings complete.
|
343
|
+
#
|
344
|
+
# When overriding on_first_update, you must at some point make a call to
|
345
|
+
# super or the View#update method in order for your view's mappings to be invoked.
|
346
|
+
def on_first_update(model, transfer)
|
347
|
+
update(model, transfer)
|
348
|
+
end
|
349
|
+
|
350
|
+
def visible?
|
351
|
+
return @main_view_component.visible
|
352
|
+
end
|
353
|
+
|
354
|
+
def visible=(visibility)
|
355
|
+
@main_view_component.visible = visibility
|
356
|
+
end
|
357
|
+
|
358
|
+
def show
|
359
|
+
@main_view_component.visible = true
|
360
|
+
end
|
361
|
+
|
362
|
+
def hide
|
363
|
+
@main_view_component.visible = false
|
364
|
+
end
|
365
|
+
|
366
|
+
# This is set via the controller, do not call directly unless you know what
|
367
|
+
# you are doing.
|
368
|
+
#
|
369
|
+
# Looks up the appropriate component and calls addXXXListener on the
|
370
|
+
# component. Components can be nested, so textField.document would be a valid
|
371
|
+
# component and the listner would be added to the document object of the text
|
372
|
+
# field.
|
373
|
+
#
|
374
|
+
# add_handler returns a hash of objects as keys and their normalized (underscored
|
375
|
+
# and . replaced with _ ) names as values
|
376
|
+
def add_handler(handler, component)
|
377
|
+
component = component.to_s
|
378
|
+
if "global" == component
|
379
|
+
raise "Global handler declarations are not yet supported"
|
380
|
+
elsif "java_window" == component
|
381
|
+
begin
|
382
|
+
@main_view_component.send("add#{handler.type.camelize}Listener", handler)
|
383
|
+
rescue NameError
|
384
|
+
raise InvalidHandlerError, "There is no listener of type #{handler.type} on #{@main_view_component}"
|
385
|
+
end
|
386
|
+
else
|
387
|
+
begin
|
388
|
+
object = instance_eval(component, __FILE__, __LINE__)
|
389
|
+
rescue NameError
|
390
|
+
raise UndefinedComponentError, "Cannot add #{handler.type} handler to #{component} on #{self}, the component could not be found"
|
391
|
+
end
|
392
|
+
|
393
|
+
begin
|
394
|
+
object.send("add#{handler.type.camelize}Listener", handler)
|
395
|
+
rescue NameError
|
396
|
+
raise InvalidHandlerError, "There is no listener of type #{handler.type} on #{component}"
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
# Attempts to find a member variable in the underlying @main_view_component
|
402
|
+
# object if one is set, otherwise falls back to default method_missing implementation.
|
403
|
+
#
|
404
|
+
# Also, detect if the user is trying to set a new value for a given instance
|
405
|
+
# variable in the form. If so, the field will be updated to refer to the provided
|
406
|
+
# value. The passed in argument MUST BE A JAVA OBJECT or this call will fail.
|
407
|
+
def method_missing(method, *args, &block)
|
408
|
+
if match = /(.*)=$/.match(method.to_s)
|
409
|
+
if @is_java_class
|
410
|
+
field = get_field(match[1])
|
411
|
+
field.set_value(Java.ruby_to_java(@main_view_component), Java.ruby_to_java(args[0]))
|
412
|
+
else
|
413
|
+
set_jruby_field(match[1], args[0])
|
414
|
+
end
|
415
|
+
else
|
416
|
+
begin
|
417
|
+
return get_field_value(method)
|
418
|
+
rescue NameError
|
419
|
+
super
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def set_jruby_field(getter, value)
|
425
|
+
@main_view_component.send("#{getter}=", value)
|
426
|
+
end
|
427
|
+
|
428
|
+
def add_nested_view(nested_name, nested_view, nested_component, model, transfer) #:nodoc:
|
429
|
+
self.class.view_nestings[nested_name].select{|nesting| nesting.nests_with_add?}.each {|nesting| nesting.add(self, nested_view, nested_component, model, transfer)}
|
430
|
+
end
|
431
|
+
|
432
|
+
def remove_nested_view(nested_name, nested_view, nested_component, model, transfer) #:nodoc:
|
433
|
+
self.class.view_nestings[nested_name].select{|nesting|
|
434
|
+
nesting.nests_with_remove?
|
435
|
+
}.each {|nesting|
|
436
|
+
nesting.remove(self, nested_view, nested_component, model, transfer)
|
437
|
+
}
|
438
|
+
end
|
439
|
+
|
440
|
+
def update(model, transfer)
|
441
|
+
self.class.view_mappings.select{|mapping| mapping.maps_to_view?}.each {|mapping| mapping.to_view(self, model, transfer)}
|
442
|
+
transfer.clear
|
443
|
+
end
|
444
|
+
|
445
|
+
# The inverse of update. Called when view_state is called in the controller.
|
446
|
+
def write_state(model, transfer)
|
447
|
+
transfer.clear
|
448
|
+
self.class.view_mappings.select{|mapping| mapping.maps_from_view?}.each {|mapping| mapping.from_view(self, model, transfer)}
|
449
|
+
end
|
450
|
+
|
451
|
+
def process_signal(signal, model, transfer, &block)
|
452
|
+
handler = self.class.signal_mappings[signal]
|
453
|
+
if handler.nil?
|
454
|
+
raise UndefinedSignalError, "There is no signal '#{signal}' defined"
|
455
|
+
else
|
456
|
+
raise InvalidSignalHandlerError, "There is no handler method '#{handler}' on view #{self.class}" unless respond_to?(handler)
|
457
|
+
self.send(handler, model, transfer, &block) unless handler.nil?
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
# Stub to be overriden in sub-class. This is where you put the code you would
|
462
|
+
# normally put in initialize. Load will be called whenever a new class is instantiated
|
463
|
+
# which happens when the Controller's instance method is called on a non-instantiated
|
464
|
+
# controller. Thus this method will always be called before the Controller's
|
465
|
+
# load method (which is called during Controlller#open).
|
466
|
+
def load; end
|
467
|
+
|
468
|
+
# Stub to be overriden in sub-class. This is called whenever the view is closed.
|
469
|
+
def unload; end
|
470
|
+
|
471
|
+
# Uses get_field to retrieve the value of a particular field, this is typically
|
472
|
+
# a component on a Java form. Used internally by method missing to enable:
|
473
|
+
#
|
474
|
+
# some_component.method
|
475
|
+
def get_field_value(field_name)
|
476
|
+
if "java_window" == field_name.to_s
|
477
|
+
@main_view_component
|
478
|
+
else
|
479
|
+
field_name = field_name.to_sym
|
480
|
+
if @is_java_class
|
481
|
+
field_object = get_field(field_name)
|
482
|
+
Java.java_to_ruby(field_object.value(Java.ruby_to_java(@main_view_component)))
|
483
|
+
else
|
484
|
+
get_field(field_name).call
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# Uses reflection to pull a private field out of the Java objects. In cases where
|
490
|
+
# no Java object is being used, the view object itself is referenced. A field
|
491
|
+
# is not the same as the object it refers to, you only need this method if you
|
492
|
+
# want to change what a view field references using the set_value method.
|
493
|
+
#
|
494
|
+
# field = get_field("my_button")
|
495
|
+
# field.set_value(Java.ruby_to_java(@main_view_component), Java.ruby_to_java(my_new_button))
|
496
|
+
def get_field(field_name)
|
497
|
+
field_name = field_name.to_sym
|
498
|
+
field = @__field_references[field_name]
|
499
|
+
|
500
|
+
if field.nil?
|
501
|
+
if @is_java_class
|
502
|
+
[field_name.to_s, field_name.camelize, field_name.camelize(false), field_name.underscore].uniq.each do |name|
|
503
|
+
begin
|
504
|
+
field = self.class.instance_java_class.java_class.declared_field(name)
|
505
|
+
rescue NameError, NoMethodError
|
506
|
+
end
|
507
|
+
break unless field.nil?
|
508
|
+
end
|
509
|
+
raise UndefinedComponentError, "There is no component named #{field_name} on view #{@main_view_component.class}" if field.nil?
|
510
|
+
|
511
|
+
field.accessible = true
|
512
|
+
else
|
513
|
+
begin
|
514
|
+
field = @main_view_component.method(field_name)
|
515
|
+
rescue NameError, NoMethodError
|
516
|
+
raise UndefinedComponentError, "There is no component named #{field_name} on view #{@main_view_component.class}"
|
517
|
+
end
|
518
|
+
end
|
519
|
+
@__field_references[field_name] = field
|
520
|
+
end
|
521
|
+
field
|
522
|
+
end
|
523
|
+
|
524
|
+
def dispose
|
525
|
+
@main_view_component.dispose if @main_view_component.respond_to? :dispose
|
526
|
+
end
|
527
|
+
|
528
|
+
def get_field_names
|
529
|
+
fields = []
|
530
|
+
if @is_java_class
|
531
|
+
klass = self.class.instance_java_class.java_class
|
532
|
+
while(klass.name !~ /^java[x]?\./)
|
533
|
+
fields << klass.declared_fields
|
534
|
+
klass = klass.superclass
|
535
|
+
end
|
536
|
+
fields.flatten.map! {|field| field.name }
|
537
|
+
else
|
538
|
+
@main_view_component.send(:instance_variables).map! {|name| name.sub('@', '')}
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
private
|
543
|
+
# Creates and returns the main view component to be assigned to @main_view_component.
|
544
|
+
# Override this when a non-default constructor is needed.
|
545
|
+
def create_main_view_component
|
546
|
+
self.class.instance_java_class.new if self.class.instance_java_class.respond_to?(:new)
|
547
|
+
end
|
548
|
+
|
549
|
+
# Retrieves all the components on the main view. This will work even if
|
550
|
+
# @main_view_component is not a Java object as long as it implements
|
551
|
+
# a components method.
|
552
|
+
def get_all_components(list = [], components = @main_view_component.components)
|
553
|
+
components.each do |component|
|
554
|
+
list << component
|
555
|
+
get_all_components(list, component.components) if component.respond_to? :components
|
556
|
+
end
|
557
|
+
list
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
end
|
562
|
+
|
563
|
+
class InvalidSignalError < Exception; end
|
564
|
+
class MissingMainViewComponentError< Exception; end
|
565
|
+
|
566
|
+
module HandlerContainer
|
567
|
+
# Removes the handlers associated with a component for the duration of the block.
|
568
|
+
# All handlers are re-added to the component afterwards.
|
569
|
+
#
|
570
|
+
# some_text_field.disable_handlers(:action, :key) do
|
571
|
+
# # some code that we don't want to trigger action listener methods or key listener methods for
|
572
|
+
# end
|
573
|
+
#
|
574
|
+
def disable_handlers(*types)
|
575
|
+
types.map! { |t| t.camelize }
|
576
|
+
listeners = {}
|
577
|
+
types.each do |type|
|
578
|
+
listener_class = if Monkeybars::Handlers::AWT_TYPES.member?(type)
|
579
|
+
instance_eval("java.awt.event.#{type}Listener", __FILE__, __LINE__)
|
580
|
+
elsif Monkeybars::Handlers::SWING_TYPES.member?(type)
|
581
|
+
instance_eval("javax.swing.event.#{type}Listener", __FILE__, __LINE__)
|
582
|
+
elsif Monkeybars::Handlers::BEAN_TYPES.member?(type)
|
583
|
+
instance_eval("java.beans.#{type}Listener", __FILE__, __LINE__)
|
584
|
+
end
|
585
|
+
listeners[type] = self.get_listeners(listener_class.java_class)
|
586
|
+
listeners[type].each do |listener|
|
587
|
+
self.send("remove#{type}Listener", listener)
|
588
|
+
end
|
589
|
+
end
|
590
|
+
yield
|
591
|
+
types.each do |type|
|
592
|
+
listeners[type].each do |listener|
|
593
|
+
self.send("add#{type}Listener", listener)
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
PlainDocument = javax.swing.text.PlainDocument
|
600
|
+
class PlainDocument
|
601
|
+
include HandlerContainer
|
602
|
+
end
|
603
|
+
|
604
|
+
Component = java.awt.Component
|
605
|
+
# The java.awt.Component class is opened and a new method is added to allow
|
606
|
+
# you to ignore certain events during a call to update_view.
|
607
|
+
class Component
|
608
|
+
include HandlerContainer
|
609
|
+
end
|