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
data/History.txt
ADDED
data/Jimpanzee.gemspec
ADDED
Binary file
|
data/Manifest.txt
ADDED
@@ -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
|
+
|
data/README.md
ADDED
@@ -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
|
+
|
data/Rakefile
ADDED
@@ -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
|
data/bin/jimpanzee
ADDED
@@ -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
|
data/lib/monkeybars.rb
ADDED
@@ -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
|