Neurogami-jimpanzee 1.0.2.1
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/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
|