Neurogami-jimpanzee 1.0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ == 1.0.2.1 / 2009-05-16
2
+ * Glommed source from Monkeybars gitorious repo
3
+
Binary file
@@ -0,0 +1,62 @@
1
+ COPYING.txt
2
+ History.txt
3
+ Manifest.txt
4
+ README.md
5
+ Rakefile
6
+ bin/jimpanzee
7
+ lib/foxtrot.jar
8
+ lib/monkeybars.rb
9
+ lib/monkeybars/controller.rb
10
+ lib/monkeybars/debug.rb
11
+ lib/monkeybars/event_handler.rb
12
+ lib/monkeybars/event_handler_registration_and_dispatch_mixin.rb
13
+ lib/monkeybars/exceptions.rb
14
+ lib/monkeybars/global_error_handler.rb
15
+ lib/monkeybars/inflector.rb
16
+ lib/monkeybars/key.rb
17
+ lib/monkeybars/task_processor.rb
18
+ lib/monkeybars/validated_hash.rb
19
+ lib/monkeybars/view.rb
20
+ lib/monkeybars/view_mapping.rb
21
+ lib/monkeybars/view_nesting.rb
22
+ lib/monkeybars/view_positioning.rb
23
+ lib/jimpanzee_version.rb
24
+ skeleton/Rakefile
25
+ skeleton/lib/java/monkeybars-1.0.2.1.jar
26
+ skeleton/lib/ruby/README.txt
27
+ skeleton/src/application_controller.rb
28
+ skeleton/src/application_view.rb
29
+ skeleton/src/main.rb
30
+ skeleton/src/manifest.rb
31
+ skeleton/src/resolver.rb
32
+ skeleton/tasks/monkeybars.rake
33
+ spec/spec_helper.rb
34
+ spec/unit/component_mixin_spec.rb
35
+ spec/unit/controller_spec.rb
36
+ spec/unit/inflector_spec.rb
37
+ spec/unit/monkeybars_window_adapter_spec.rb
38
+ spec/unit/org/monkeybars/TestView.class
39
+ spec/unit/org/monkeybars/TestView.form
40
+ spec/unit/org/monkeybars/TestView.java
41
+ spec/unit/task_processor_spec.rb
42
+ spec/unit/validated_hash_spec.rb
43
+ spec/unit/view_map_methods_spec.rb
44
+ spec/unit/view_mapping_spec.rb
45
+ spec/unit/view_nesting_spec.rb
46
+ spec/unit/view_spec.rb
47
+ tasks/ann.rake
48
+ tasks/bones.rake
49
+ tasks/gem.rake
50
+ tasks/manifest.rake
51
+ tasks/notes.rake
52
+ tasks/post_load.rake
53
+ tasks/rdoc.rake
54
+ tasks/rubyforge.rake
55
+ tasks/setup.rb
56
+ tasks/spec.rake
57
+ tasks/svn.rake
58
+ tasks/test.rake
59
+ lib/util/rspec/application_user.rb
60
+ lib/util/rspec/user.rb
61
+ lib/util/rspec/monkeybars_steps.rb
62
+
@@ -0,0 +1,23 @@
1
+ Jimpanzee 1.0.2.1
2
+
3
+ http://github.com/Neurogami/Jimpanzee/tree/master
4
+
5
+ = DESCRIPTION
6
+ ==============
7
+
8
+ Jimpanzee is the avant-garde fork of Monkeybars, a library that enables you to interface (via JRuby) with and take advantage of Swing without writing any Java directly. Using any editor you like, you create Java GUI elements and Jimpanzee takes care of registering event listeners, receiving and handling events, updating your view from a model, etc. Jimpanzee makes very few assumptions about your model or the structure of the code in your Java forms so it can be integrated into existing projects with a minimum of hassle.
9
+
10
+ = LICENSE
11
+ ==========
12
+
13
+
14
+ Jimpanzee is licensed under the Ruby licence, see COPYING.txt.
15
+
16
+ = ATTRIBUTION:
17
+ ============
18
+
19
+ Monkeybars is the work of many contributors. See the orignal README.txt for somewhat outdated info.
20
+
21
+ Jimpanzee is perpetrated by James Britt
22
+
23
+
@@ -0,0 +1,98 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ load 'tasks/setup.rb'
6
+
7
+ ensure_in_path 'lib'
8
+ require 'jimpanzee_version'
9
+
10
+ PROJ.name = 'jimpanzee'
11
+ PROJ.authors = ' James Britt, David Koontz, Logan Barnett'
12
+ PROJ.email = 'james@neurogami.com'
13
+ PROJ.url = 'http://github.com/Neurogami/Jimpanzee/tree/master'
14
+ PROJ.version = Jimpanzee::VERSION
15
+ PROJ.summary =
16
+ PROJ.rubyforge.name = 'Neurogami'
17
+ PROJ.spec.opts << '--color'
18
+ PROJ.ruby_opts = []
19
+ PROJ.libs << "lib"
20
+ PROJ.rdoc.remote_dir = "api"
21
+ PROJ.rdoc.main = "README.md"
22
+ PROJ.rdoc.title = "Jimpanzee!"
23
+ PROJ.rdoc.include = ['README.md', 'lib']
24
+
25
+
26
+ require 'fileutils'
27
+ require 'spec/rake/spectask'
28
+
29
+ OUTPUT_DIR = "pkg"
30
+ BUILD_DIR = "#{OUTPUT_DIR}/bin"
31
+ SKELETON_DIR = "skeleton"
32
+
33
+ task :default => 'spec'
34
+
35
+ desc "Removes the output directory"
36
+ task :clean do
37
+ FileUtils.remove_dir(OUTPUT_DIR) if File.directory? OUTPUT_DIR
38
+ FileUtils.rm("skeleton/lib/java/monkeybars-#{Jimpanzee::VERSION}.jar", :force => true)
39
+ end
40
+
41
+ task :update_version_readme do
42
+ readme = IO.readlines( 'README.md')
43
+ File.open( 'README.md', 'w' ) { |f|
44
+ f << "Monkeybars #{Jimpanzee::VERSION}\n"
45
+ readme.shift
46
+ f << readme
47
+ }
48
+ end
49
+
50
+ task :prepare do
51
+ Dir.mkdir(OUTPUT_DIR) unless File.directory?(OUTPUT_DIR)
52
+ Dir.mkdir(BUILD_DIR) unless File.directory?(BUILD_DIR)
53
+ Dir.mkdir("skeleton/lib") unless File.directory?("skeleton/lib")
54
+ Dir.mkdir("skeleton/lib/java") unless File.directory?("skeleton/lib/java")
55
+ Dir.mkdir("skeleton/lib/ruby") unless File.directory?("skeleton/lib/ruby")
56
+ File.open( "skeleton/lib/ruby/README.md" , "w") {|f| f << "3rd party Ruby libs and unpacked gems go here." } unless File.exist?( "skeleton/lib/ruby/README.md" )
57
+ end
58
+
59
+ task :gem => [:jar]
60
+
61
+ desc "Creates monkeybars.jar file for distribution"
62
+ task :jar => [:prepare, :update_version_readme] do
63
+ Dir.chdir(BUILD_DIR) do
64
+ $stdout << `jar xvf ../../lib/foxtrot.jar`
65
+ FileUtils.remove_dir('META-INF', true)
66
+ end
67
+ $stdout << `jar cf #{OUTPUT_DIR}/monkeybars-#{Jimpanzee::VERSION}.jar -C lib monkeybars.rb -C lib monkeybars -C lib util -C #{BUILD_DIR} .`
68
+ FileUtils.cp("#{OUTPUT_DIR}/monkeybars-#{Jimpanzee::VERSION}.jar", "skeleton/lib/java/monkeybars-#{Jimpanzee::VERSION}.jar")
69
+ end
70
+
71
+ desc "Creates a zip file version of the project, excluding files from exclude.lst. **ONLY WORKS ON OSX/Linux** Yes this sucks, no I don't want to add another dependency at the moment."
72
+ task :zip do
73
+ `zip -vr pkg/monkeybars-#{Jimpanzee::VERSION}.zip ../monkeybars -x@exclude.lst`
74
+ end
75
+
76
+ desc "Executes a clean followed by a jar"
77
+ task :clean_jar => [:clean, :jar]
78
+
79
+ #desc "Use this instead of the hoe included install_gem"
80
+ #task :mb_install_gem => [:jar, :gem] do
81
+ # $stdout << `gem install -l pkg/monkeybars-#{Jimpanzee::VERSION}.gem`
82
+ #end
83
+
84
+ desc "Only used to make RSpec usable with Java Swing code. Wraps up the target of various view tests into a jar that can be require'd and thus loaded on the classpath by JRuby"
85
+ task :prepare_spec_jar do
86
+ create_test_jar_file
87
+ end
88
+
89
+ task :spec => [:prepare_spec]
90
+
91
+ task :prepare_spec do
92
+ create_test_jar_file unless File.exist?("spec/unit/test_files.jar")
93
+ end
94
+
95
+ def create_test_jar_file
96
+ $stdout << `javac spec/unit/org/monkeybars/TestView.java`
97
+ $stdout << `jar -cf spec/unit/test_files.jar -C spec/unit org`
98
+ end
@@ -0,0 +1,41 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+ require 'rubygems'
5
+ require 'monkeybars_version'
6
+
7
+ def help(message = nil)
8
+ $stdout << message + "\n-----------------------------------\n" unless message.nil?
9
+ $stdout << <<-ENDL
10
+ Usage:
11
+ monkeybars -h/--help
12
+ monkeybars -v/--version
13
+
14
+ monkeybars project_name
15
+ Creates a new directory (project_name) containing a project skeleton with all files needed to run a monkeybars application.
16
+ ENDL
17
+ end
18
+
19
+ if ARGV.empty?
20
+ help
21
+ else
22
+ case ARGV[0]
23
+ when "-h" || "--help" || nil
24
+ help
25
+ when "-v" || "--version"
26
+ $stdout << "Monkeybars version #{Monkeybars::VERSION}\n"
27
+ else
28
+ # create new directory and copy app skeleton over
29
+ project_name = ARGV[0]
30
+
31
+ unless File.directory? project_name
32
+ $stdout << "Creating directory #{project_name}\n"
33
+ FileUtils.mkdir_p(project_name)
34
+ FileUtils.cd(project_name)
35
+ $stdout << "Copying monkeybars project structure\n"
36
+ FileUtils.cp_r(File.dirname(__FILE__) + "/../skeleton/.", "./")
37
+ else
38
+ $stdout << "Directory already exists, aborting\n"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ require 'monkeybars/exceptions'
4
+ require 'monkeybars/controller'
5
+ require 'monkeybars/key'
6
+ require 'monkeybars/global_error_handler'
7
+ require 'monkeybars/debug'
@@ -0,0 +1,608 @@
1
+ require 'thread'
2
+
3
+ require "monkeybars/inflector"
4
+ require "monkeybars/view"
5
+ require "monkeybars/event_handler"
6
+ require "monkeybars/task_processor"
7
+ require "monkeybars/event_handler_registration_and_dispatch_mixin"
8
+
9
+ module Monkeybars
10
+ # Controllers are the traffic cops of your application. They decide how to react to
11
+ # events, they coordinate interaction with other controllers and they define some of
12
+ # the overall characteristics of a view such as which events it will generate and how
13
+ # it should respond to things like the close button being pressed.
14
+ # For a general introduction to the idea of MVC and the role of a controller,
15
+ # please see: http://en.wikipedia.org/wiki/Model-view-controller. Monkeybars is not,
16
+ # strictly speaking, an MVC framework. However the general idea of the seperations
17
+ # of concerns that most people think of when you say 'MVC' is applicable.
18
+ #
19
+ # The controller defines the model and view classes it is associated with. Most
20
+ # controllers will declare a view and a model that will be instantiated along with
21
+ # the controller and whose life cycle is managed by the controller. It is not required
22
+ # to declare a view or model but a controller is of questionable usefulness without it.
23
+ #
24
+ # The controller is where you define any events you are interested in handling
25
+ # (see add_listener) as well as two special events, the pressing of the "close" button
26
+ # of the window (see close_action), and the updating of the MVC tuple (see update_method).
27
+ # Handlers are methods named according to a certain convention that are the recipient of events.
28
+ # Handlers are named <component_event>.
29
+ # No events are actually generated and sent to the controller unless a listener has been added
30
+ # for that component, however, component-specific handlers will automatically add a listener
31
+ # for that component when the class is instantiated. Therefore a method named
32
+ # ok_button_action_performed would be the equivalent of
33
+ #
34
+ # add_listener :type => :action, :components => ["ok_button"]
35
+ #
36
+ # These automatic listener registrations work for any component that can be
37
+ # resolved directly in your view. In the above example, the view could contain
38
+ # a component named ok_button, okButton or even OkButton and the listener
39
+ # would be added correctly. If you have a nested component such as text_field.document
40
+ # then you will need to use an explicit add_listener registration.
41
+ #
42
+ # Handler methods can optionally take one parameter which is the event generated
43
+ # by Swing. This would look like
44
+ #
45
+ # def some_component_event_name(swing_event)
46
+ #
47
+ # While an event handler is running, the Swing Event Dispatch Thread (usually called the EDT)
48
+ # is blocked and as such, no repaint events will occur and no new events will be proccessed.
49
+ # If you have a process that is long running, but you don't want to make asynchronous
50
+ # by spawning a new thread, you can use the repaint_while method which takes a block to execute
51
+ # while still allowing Swing to process graphical events (but not new interaction events like
52
+ # mouse clicking or typing).
53
+ #
54
+ # def button_action_performed
55
+ # repaint_while do
56
+ # sleep(20) # the gui is still responsive while we're in here sleeping
57
+ # end
58
+ # model.text = "done sleeping!"
59
+ # update_view
60
+ # end
61
+ # ==========
62
+ #
63
+ # Example of a controller, this assumes the existance of a Ruby class named MyModel that
64
+ # has an attribute named user_name that is mapped to a field on a subclass of
65
+ # Monkeybars::View named MyView that has a button named "ok_button" and a text field called
66
+ # user_name
67
+ #
68
+ # require 'monkeybars'
69
+ #
70
+ # class MyController < Monkeybars::Controller
71
+ # set_view :MyView
72
+ # set_model :MyModel
73
+ #
74
+ # close_action :exit
75
+ #
76
+ # def ok_button_mouse_released
77
+ # puts "The user's name is: #{view_state.model.user_name}"
78
+ # end
79
+ # end
80
+ #
81
+ # It is important that you do not implement your own initialize and update methods, this
82
+ # will interfere with the operation of the Controller class (or if you do be sure to call
83
+ # super as the first line).
84
+ class Controller
85
+ include TaskProcessor
86
+ include EventHandlerRegistrationAndDispatchMixin
87
+
88
+ @@instance_list ||= Hash.new {|hash, key| hash[key] = []}
89
+ @@instance_lock ||= Hash.new {|hash, key| hash[key] = Mutex.new }
90
+
91
+ # Controllers cannot be instantiated via a call to new, instead use instance
92
+ # to retrieve the instance of the view. Currently only one instance of a
93
+ # controller is created but in the near future a configurable limit will be
94
+ # available so that you can create n instances of a controller.
95
+ def self.instance
96
+ @@instance_lock[self].synchronize do
97
+ controller = @@instance_list[self]
98
+ unless controller.empty?
99
+ controller.last
100
+ else
101
+ __new__
102
+ end
103
+ end
104
+ end
105
+
106
+ # Always returns a new instance of the controller.
107
+ #
108
+ # controller1 = MyController.create_instance
109
+ # controller2 = MyController.create_instance
110
+ # ...
111
+ # controller32 = MyController.create_instance
112
+ #
113
+ def self.create_instance
114
+ new
115
+ end
116
+
117
+ # Declares the view class (as a symbol) to use when instantiating the controller.
118
+ #
119
+ # set_view :MyView
120
+ #
121
+ # The file my_view.rb will be auto-required before attempting to instantiate
122
+ # the MyView class.
123
+ #
124
+ def self.set_view(view)
125
+ self.view_class = view
126
+ end
127
+
128
+ # See set_view. The declared model class is also auto-required prior to the
129
+ # class being instantiated. It is not a requirement that you have a model,
130
+ # Monkeybars will operate without one as long as you do not attempt to use
131
+ # methods that interact with the model.
132
+ #
133
+ # The set_model method may be used in 3 different ways.
134
+ #
135
+ # The first and most common use is to pass the name of the model class as
136
+ # a string. Internally the controller instantiates this class and makes it
137
+ # available to the controller via a private method #model. Note that this
138
+ # form of set_model does not allow passing any parameters to the model
139
+ # class; the model must implement a zero-argument constructor.
140
+ # class FooController < ApplicationController
141
+ # set_model 'FooModel'
142
+ # set_view 'FooView'
143
+ # set_close_action :exit
144
+ # end
145
+ #
146
+ # The second format passes a block to set_model. The block is executed via
147
+ # #instance_eval with the result assigned as the model. The model constructor
148
+ # may take parameters.
149
+ # class FooController < ApplicationController
150
+ # set_model { FooModel.new("arg1", "arg2", "arg3") }
151
+ # set_view 'FooView'
152
+ # set_close_action :exit
153
+ # end
154
+ #
155
+ # The third format takes both a string with the model class name _and_ a block
156
+ # for the purpose of setting initial values. No parameters may be passed
157
+ # to the constructor in this format; it does allow immediate use of any
158
+ # declared accessors.
159
+ # class FooModel
160
+ # attr_accessor :some_value
161
+ # end
162
+ # class FooController < Monkeybars::Controller
163
+ # set_model "FooModel" do
164
+ # model.some_value = 5
165
+ # end
166
+ # end
167
+ #
168
+ def self.set_model(model=nil, &block)
169
+ self.model_class = [model,block]
170
+ end
171
+
172
+ # Declares a method to be called whenever the controller's update method is called.
173
+ def self.set_update_method(method)
174
+ raise "Argument must be a symbol" unless method.kind_of? Symbol
175
+ raise "'Update' is a reserved method name" if :update == method
176
+ self.send(:class_variable_set, :@@update_method_name, method)
177
+ end
178
+
179
+ # Valid close actions are
180
+ # * :nothing
181
+ # * :close (default)
182
+ # * :exit
183
+ # * :dispose
184
+ # * :hide
185
+ #
186
+ # - action :nothing - close action is ignored, this means you cannot
187
+ # close the window unless you provide another way to do this
188
+ # - action :close - calls the controller's close method
189
+ # - action :exit - closes the application when the window's close
190
+ # button is pressed
191
+ # - action :dispose - default action, calls Swing's dispose method which
192
+ # will release the resources for the window and its components, can be
193
+ # brought back with a call to show
194
+ # - action :hide - sets the visibility of the window to false
195
+ def self.set_close_action(action)
196
+ self.send(:class_variable_set, :@@close_action, action)
197
+ end
198
+
199
+ # Returns a frozen hash of ControllerName => [instances] pairs. This is
200
+ # useful if you need to iterate over all active controllers to call update
201
+ # or to check for a status.
202
+ #
203
+ # # NOTE: Controllers that have close called on them will not show up on this
204
+ # list, even if open is subsequently called. If you need a window to remain
205
+ # in the list but not be updated when not visible you can do:
206
+ #
207
+ # Monkeybars::Controller.active_controllers.values.flatten.each{|c| c.update if c.visible? }
208
+ def self.active_controllers
209
+ @@instance_list.clone.freeze
210
+ end
211
+
212
+ # See EventHandlerRegistrationAndDispatchMixin::ClassMethods#add_listener
213
+ class << self
214
+ alias_method :original_add_listener, :add_listener
215
+ end
216
+ def self.add_listener(details)
217
+ original_add_listener(details)
218
+ hide_protected_class_methods #workaround for JRuby bug #1283
219
+ end
220
+
221
+ private
222
+ def self.hide_protected_class_methods #JRuby bug #1283
223
+ private_class_method :new
224
+ end
225
+ hide_protected_class_methods
226
+
227
+ def self.__new__
228
+ object = new
229
+ @@instance_list[self] << object
230
+ @@instance_list[self].uniq!
231
+ object
232
+ end
233
+
234
+ def initialize
235
+ @model_has_block = false
236
+ @__model = create_new_model unless self.class.model_class.nil?
237
+ instance_eval(&self.class.model_class.last) if @model_has_block
238
+ @__view = create_new_view unless self.class.view_class.nil?
239
+ @__transfer = {}
240
+ @__view_state = nil
241
+ setup_implicit_and_explicit_event_handlers
242
+
243
+ action = close_action
244
+ unless [:nothing, :close, :exit, :dispose, :hide].include?(action)
245
+ raise "Unknown close action: #{action}. Only :nothing, :close, :exit, :dispose, and :hide are supported"
246
+ end
247
+
248
+ window_type = if @__view.instance_variable_get(:@main_view_component).kind_of? javax.swing.JInternalFrame
249
+ "internalFrame"
250
+ else
251
+ "window"
252
+ end
253
+
254
+ unless @__view.nil?
255
+ @__view.close_action(Monkeybars::View::CloseActions::METHOD, MonkeybarsWindowAdapter.new(:"#{window_type}Closing" => self.method(:built_in_close_method)))
256
+ end
257
+
258
+ @closed = true
259
+ end
260
+
261
+ def close_action
262
+ if self.class.class_variables.member?("@@close_action")
263
+ action = self.class.send(:class_variable_get, :@@close_action)
264
+ else
265
+ action = :close
266
+ end
267
+ action
268
+ end
269
+
270
+ public
271
+ # Calls the method that was set using Controller.set_update_method. If no method has been set defined, this call is ignored.
272
+ def update
273
+ if self.class.class_variables.member?("@@update_method_name")
274
+ method_name = self.class.send(:class_variable_get, :@@update_method_name)
275
+ send(method_name)
276
+ end
277
+ end
278
+
279
+ # Triggers updating of the view based on the mapping and the current contents
280
+ # of the model and the transfer
281
+ def update_view
282
+ @__view.update(model, transfer)
283
+ end
284
+
285
+ # Sends a signal to the view. The view will process the signal (if it is
286
+ # defined in the view via View.define_signal) and optionally invoke the
287
+ #callback that is passed in as a block.
288
+ #
289
+ # This is useful for communicating one off events such as a state transition
290
+ #
291
+ # def update
292
+ # signal(:red_alert) if model.threshold_exceeded?
293
+ # end
294
+ def signal(signal_name, &callback)
295
+ @__view.process_signal(signal_name, model, transfer, &callback)
296
+ end
297
+
298
+ # Nests a controller under this controller with the given key
299
+ # def add_user_button_action_performed
300
+ # @controllers << UserController.create_instance
301
+ # add_nested_controller(:user_list, @controllers.last)
302
+ # @controllers.last.open
303
+ # end
304
+ # This forces the view to perform its nesting.
305
+ # See also Monkeybars::Controller#remove_nested_controller
306
+ #
307
+ def add_nested_controller(name, sub_controller)
308
+ nested_view = sub_controller.instance_variable_get(:@__view)
309
+ @__view.add_nested_view(name, nested_view, nested_view.instance_variable_get(:@main_view_component), model, transfer)
310
+ end
311
+
312
+ # Removes the nested controller with the given key
313
+ # This does not do any cleanup on the nested controller's instance.
314
+ #
315
+ # def remove_user_button_action_performed
316
+ # remove_nested_controller(:user_list, @controllers.last)
317
+ # UserController.destroy_instance @controllers.last
318
+ # @controllers.delete @controllers.last
319
+ # end
320
+ #
321
+ # This performs the view's nesting.
322
+ # See also Monkeybars::Controller#add_nested_controller
323
+ #
324
+ def remove_nested_controller(name, sub_controller)
325
+ nested_view = sub_controller.instance_variable_get(:@__view)
326
+ @__view.remove_nested_view(name, nested_view, nested_view.instance_variable_get(:@main_view_component), model, transfer)
327
+ end
328
+
329
+ # Returns true if the view is visible, false otherwise
330
+ def visible?
331
+ @__view.visible?
332
+ end
333
+
334
+ # Hides the view
335
+ def hide
336
+ @__view.hide
337
+ end
338
+
339
+ # Disposes the view
340
+ def dispose
341
+ @__view.dispose
342
+ end
343
+
344
+ # Shows the view
345
+ def show
346
+ @__view.show
347
+ end
348
+
349
+ # True if close has been called on the controller
350
+ def closed?
351
+ @closed
352
+ end
353
+
354
+ # Hides the view and unloads its resources
355
+ def close
356
+ @closed = true
357
+ @__view.unload unless @__view.nil?
358
+ unload
359
+ @__view.dispose if @__view.respond_to? :dispose
360
+ @@instance_lock[self.class].synchronize do
361
+ @@instance_list[self.class].delete self
362
+ end
363
+ end
364
+
365
+ # Calls load if the controller has not been opened previously, then calls update_view
366
+ # and shows the view.
367
+ def open(*args, &block)
368
+ @@instance_lock[self.class].synchronize do
369
+ unless @@instance_list[self.class].member? self
370
+ @@instance_list[self.class] << self
371
+ end
372
+ end
373
+
374
+ if closed?
375
+ load(*args, &block)
376
+ @__view.on_first_update(model, transfer)
377
+ clear_view_state
378
+ @closed = false
379
+ end
380
+
381
+ show
382
+
383
+ self #allow var assignment off of open, i.e. screen = SomeScreen.instance.open
384
+ end
385
+
386
+ # Stub to be overriden in sub-class. This is where you put the code you would
387
+ # normally put in initialize, it will be called the first time open is called
388
+ # on the controller.
389
+ def load(*args); end
390
+
391
+ # Stub to be overriden in sub-class. This is called whenever the controller is closed.
392
+ def unload; end
393
+
394
+ alias_method :original_handle_event, :handle_event
395
+ # See EventHandlerRegistrationAndDispatchMixin#handle_event
396
+ def handle_event(component_name, event_name, event) #:nodoc:
397
+ original_handle_event(component_name, event_name, event)
398
+ clear_view_state
399
+ end
400
+
401
+ private
402
+
403
+ # Returns the model object. This is the object that is passed to the view
404
+ # when update_view is called. This model is *not* the same model that you
405
+ # get from #view_state. Values that you want to propogate from the
406
+ # #view_state model to this model can be done using #update_model.
407
+ def model #:doc:
408
+ @__model
409
+ end
410
+
411
+ # Returns the transfer object which is a transient hash passed to the view
412
+ # whenever #update_view is called. The transfer is cleared after each call
413
+ # to #update_view. The transfer is used to pass data to and
414
+ # from the view that is not part of your model. For example, if you had
415
+ # a model that was an ActiveRecord object you would probably not want to
416
+ # put things like the currently selected item into your model. That data
417
+ # could instead be passed as a value in the transfer.
418
+ #
419
+ # transfer[:selected_framework] = "monkeybars"
420
+ #
421
+ # Then in your view, you could use that transfer value to select the correct
422
+ # value out of a list.
423
+ #
424
+ # map :view => "framework_list.selected_item", :transfer => :selected_framework
425
+ #
426
+ # See View#map for more details on the options for your mapping.
427
+ def transfer #:doc:
428
+ @__transfer
429
+ end
430
+
431
+ # Returns a ViewState object which contains a model and a transfer hash of the
432
+ # view's current contents as defined by the view's mappings. This is for use in
433
+ # event handlers. The contents of the model and transfer are *not* the same as
434
+ # the contents of the model and transfer in the controller, they are new objects
435
+ # created when view_state was called. If you wish to propogate the values from the
436
+ # view state's model into the actual model, you must do this yourself. A
437
+ # helper method #update_model is provided to make this easier. In an event
438
+ # handler this method is thread safe as Swing is single threaded and blocks
439
+ # any modification to the GUI while the handler is being proccessed.
440
+ #
441
+ # The view state object has two properties, model and transfer.
442
+ #
443
+ # def ok_button_action_performed
444
+ # if view_state.transfer[:foo] == :bar
445
+ # model.baz = view_model.baz
446
+ # end
447
+ # end
448
+ #
449
+ # Any subsequent call to view_state will return the same object, that is, this
450
+ # method is memoized internally. At the end of each event (after all handlers
451
+ # have been called) the memoized view state is cleared. If you call view_state
452
+ # outside of an event handler it is important that you clear the view state
453
+ # yourself by calling clear_view_state.
454
+ def view_state # :doc:
455
+ return @__view_state unless @__view_state.nil?
456
+ model = self.class.model_class.nil? ? nil : create_new_model
457
+ transfer = {}
458
+ @__view.write_state(model, transfer)
459
+ @__view_state = ViewState.new(model, transfer)
460
+ end
461
+
462
+ # Equivalent to view_state.model
463
+ def view_model # :doc:
464
+ view_state.model
465
+ end
466
+
467
+ # Equivalent to view_state.transfer
468
+ def view_transfer
469
+ view_state.transfer
470
+ end
471
+
472
+ # Resets memoized view_state value. This is called automatically after each
473
+ # event so it would only need to be called if view_state is used outside
474
+ # of an event handler.
475
+ def clear_view_state # :doc:
476
+ @__view_state = nil
477
+ end
478
+
479
+ # This method is almost always used from within an event handler to propogate
480
+ # the view_state to the model. Updates the model from the source provided
481
+ # (typically from view_state). The list of properties defines what is modified
482
+ # on the model.
483
+ #
484
+ # def ok_button_action_perfomed
485
+ # update_model(view_state.model, :user_name, :password)
486
+ # end
487
+ #
488
+ # This would have the same effect as:
489
+ #
490
+ # model.user_name = view_state.model.user_name
491
+ # model.password = view_state.model.password
492
+ def update_model(source, *properties) # :doc:
493
+ update_provided_model(source, @__model, *properties)
494
+ end
495
+
496
+ # This method works just like Controller#update_model except that the target
497
+ # is not implicitly the model. The second parameter is a target object for
498
+ # the properties to be propogated to. This is useful if you have a composite
499
+ # model or need to updated other controllers.
500
+ #
501
+ # def ok_button_action_perfomed
502
+ # update_provided_model(view_state.model, model.user, :user_name, :password)
503
+ # end
504
+ #
505
+ # This would have the same effect as:
506
+ #
507
+ # model.user.user_name = view_state.model.user_name
508
+ # model.user.password = view_state.model.password
509
+ def update_provided_model(source, destination, *properties) # :doc:
510
+ properties.each do |property|
511
+ destination.send("#{property}=", source.send(property))
512
+ end
513
+ end
514
+
515
+ @@model_class_for_child_controller ||= {}
516
+ def self.model_class
517
+ @@model_class_for_child_controller[self]
518
+ end
519
+
520
+ def self.model_class=(model)
521
+ @@model_class_for_child_controller[self] = model
522
+ end
523
+
524
+ @@view_class_for_child_controller ||= {}
525
+ def self.view_class
526
+ @@view_class_for_child_controller[self]
527
+ end
528
+
529
+ def self.view_class=(view)
530
+ @@view_class_for_child_controller[self] = view
531
+ end
532
+
533
+ def sub_controllers
534
+ @__sub_controllers ||= {}
535
+ end
536
+
537
+ def create_new_model
538
+ begin
539
+ unless self.class.model_class.first.nil?
540
+ @model_has_block = true unless self.class.model_class.last.nil?
541
+ instance = self.class.model_class.first.constantize.new
542
+ return instance
543
+ else
544
+ return self.class.model_class.last.call
545
+ end
546
+ rescue NameError
547
+ require self.class.model_class.first.underscore
548
+ self.class.model_class.first.constantize.new
549
+ end
550
+ end
551
+
552
+ def create_new_view
553
+ begin
554
+ self.class.view_class.constantize.new
555
+ rescue NameError
556
+ require self.class.view_class.underscore
557
+ self.class.view_class.constantize.new
558
+ end
559
+ end
560
+
561
+ def built_in_close_method(event)
562
+ if event.getID == java.awt.event.WindowEvent::WINDOW_CLOSING || event.getID == javax.swing.event.InternalFrameEvent::INTERNAL_FRAME_CLOSING
563
+ case (action = close_action)
564
+ when :close
565
+ close
566
+ when :exit
567
+ Monkeybars::Controller.active_controllers.values.flatten.each {|c| c.close }
568
+ java.lang.System.exit(0)
569
+ when :hide
570
+ hide
571
+ when :dispose
572
+ dispose
573
+ else
574
+ raise Monkeybars::InvalidCloseAction.new("Invalid close action: #{action}") unless action == :nothing
575
+ end
576
+ end
577
+ end
578
+ end
579
+
580
+ # A formal object representing the view's model and transfer state. This used to be
581
+ # an array so we emulate the array methods that are in common usage.
582
+ class ViewState
583
+ attr_reader :model, :transfer
584
+
585
+ def initialize(model, transfer)
586
+ @model, @transfer = model, transfer
587
+ end
588
+
589
+ def [](index)
590
+ case index
591
+ when 0
592
+ @model
593
+ when 1
594
+ @transfer
595
+ else
596
+ nil
597
+ end
598
+ end
599
+
600
+ def first
601
+ @model
602
+ end
603
+
604
+ def last
605
+ @transfer
606
+ end
607
+ end
608
+ end