rugui 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/LICENSE +165 -0
  2. data/README +67 -0
  3. data/README.rdoc +67 -0
  4. data/Rakefile +56 -0
  5. data/VERSION.yml +4 -0
  6. data/bin/rugui +16 -0
  7. data/lib/rugui/base_controller.rb +194 -0
  8. data/lib/rugui/base_model.rb +22 -0
  9. data/lib/rugui/base_object.rb +73 -0
  10. data/lib/rugui/base_view.rb +302 -0
  11. data/lib/rugui/base_view_helper.rb +23 -0
  12. data/lib/rugui/configuration.rb +136 -0
  13. data/lib/rugui/framework_adapters/GTK.rb +233 -0
  14. data/lib/rugui/framework_adapters/Qt4.rb +171 -0
  15. data/lib/rugui/framework_adapters/base_framework_adapter.rb +90 -0
  16. data/lib/rugui/framework_adapters/framework_adapter_support.rb +35 -0
  17. data/lib/rugui/gem_builder.rb +21 -0
  18. data/lib/rugui/gem_dependency.rb +282 -0
  19. data/lib/rugui/initialize_hooks.rb +36 -0
  20. data/lib/rugui/initializer.rb +162 -0
  21. data/lib/rugui/log_support.rb +118 -0
  22. data/lib/rugui/observable_property_proxy.rb +73 -0
  23. data/lib/rugui/observable_property_support.rb +251 -0
  24. data/lib/rugui/plugin/loader.rb +77 -0
  25. data/lib/rugui/property_observer.rb +58 -0
  26. data/lib/rugui/signal_support.rb +57 -0
  27. data/lib/rugui/tasks/gems_application.rake +71 -0
  28. data/lib/rugui/tasks/rugui.rb +8 -0
  29. data/lib/rugui/tasks/rugui_framework.rb +4 -0
  30. data/lib/rugui/tasks/runner_application.rake +4 -0
  31. data/lib/rugui/tasks/spec_application.rake +64 -0
  32. data/lib/rugui/tasks/spec_framework.rake +27 -0
  33. data/lib/rugui/tasks/test_application.rake +77 -0
  34. data/lib/rugui/vendor_gem_source_index.rb +140 -0
  35. data/lib/rugui/version.rb +9 -0
  36. data/lib/rugui.rb +37 -0
  37. data/spec/framework/base_controller_spec.rb +48 -0
  38. data/spec/framework/base_model_spec.rb +13 -0
  39. data/spec/framework/base_view_helper_spec.rb +13 -0
  40. data/spec/framework/base_view_spec.rb +92 -0
  41. data/spec/framework/log_support_spec.rb +16 -0
  42. data/spec/framework/observable_property_proxy_spec.rb +67 -0
  43. data/spec/framework/observable_property_support_spec.rb +283 -0
  44. data/spec/framework/property_observer_spec.rb +88 -0
  45. data/spec/helpers/controllers.rb +29 -0
  46. data/spec/helpers/initialize_hooks_helper.rb +18 -0
  47. data/spec/helpers/models.rb +9 -0
  48. data/spec/helpers/observables.rb +210 -0
  49. data/spec/helpers/view_helpers.rb +9 -0
  50. data/spec/helpers/views.rb +72 -0
  51. data/spec/rcov.opts +1 -0
  52. data/spec/resource_files/my_other_view.glade +46 -0
  53. data/spec/resource_files/my_view.glade +46 -0
  54. data/spec/spec.opts +4 -0
  55. data/spec/spec_helper.rb +15 -0
  56. metadata +149 -0
@@ -0,0 +1,302 @@
1
+ module RuGUI
2
+ # A base class for views.
3
+ #
4
+ # To use this class create a subclass and reimplement #setup_widgets, if you
5
+ # want to create your interface by hand. If you want to use a builder file
6
+ # just call #use_builder and, optionally, call #builder_file passing the
7
+ # filename to use.
8
+ #
9
+ # Each adapter may implement additional features, extending this class, look
10
+ # for it in adapter classes.
11
+ #
12
+ # The view may have ViewHelpers, which works as 'models' for views, i.e., they
13
+ # have observable properties that can be observed by the view. A default
14
+ # helper, named as <code>{view_name}Helper</code> is registered if it exists.
15
+ # For example, for a view named *MyView*, the default view helper should be
16
+ # named *MyViewHelper*. This helper can be accessed as a <code>helper</code>
17
+ # attribute. Other helpers may be registered if needed.
18
+ #
19
+ # Example (using GTK framework adapter):
20
+ # class MyGladeView < RuGUI::BaseView
21
+ # builder_file 'my_file.glade' # this is optional, if the glade file was called my_glade_view.glade this wouldn't be needed.
22
+ # root :top_window
23
+ # use_builder
24
+ # end
25
+ #
26
+ # class MyHandView < RuGUI::BaseView
27
+ # def setup_widgets
28
+ # # do your hand-made code here...
29
+ # end
30
+ # end
31
+ class BaseView < BaseObject
32
+ include RuGUI::LogSupport
33
+ include RuGUI::PropertyObserver
34
+ include RuGUI::SignalSupport
35
+
36
+ attr_accessor :controllers
37
+ attr_reader :widgets
38
+ attr_reader :unnamed_widgets
39
+
40
+ class_inheritable_accessor :configured_builder_file
41
+ class_inheritable_accessor :configured_builder_file_usage
42
+ class_inheritable_accessor :configured_builder_file_extension
43
+ class_inheritable_accessor :configured_root
44
+ class_inheritable_accessor :configured_display_root
45
+
46
+ def initialize
47
+ @controllers = {}
48
+ @helpers = {}
49
+ @unnamed_widgets = []
50
+ @widgets = {}
51
+
52
+ register_default_helper
53
+ setup_view_helpers
54
+ build_from_builder_file if use_builder?
55
+ setup_widgets
56
+ autoconnect_signals(self)
57
+ end
58
+
59
+ # This is included here so that the initialize method is properly updated.
60
+ include RuGUI::InitializeHooks
61
+
62
+ # Returns the framework_adapter for this class.
63
+ def framework_adapter
64
+ framework_adapter_for('BaseView')
65
+ end
66
+
67
+ # Reimplement this method to create widgets by hand.
68
+ def setup_widgets
69
+ end
70
+
71
+ # Reimplement this method to setup view helpers.
72
+ def setup_view_helpers
73
+ end
74
+
75
+ # Adds the given widget to a container widget.
76
+ def add_widget_to_container(widget, container_widget_or_name)
77
+ self.framework_adapter.add_widget_to_container(widget, from_widget_or_name(container_widget_or_name))
78
+ end
79
+
80
+ # Adds the given widget to a container widget.
81
+ def remove_widget_from_container(widget, container_widget_or_name)
82
+ self.framework_adapter.remove_widget_from_container(widget, from_widget_or_name(container_widget_or_name))
83
+ end
84
+
85
+ # Includes a view root widget inside the given container widget.
86
+ def include_view(container_widget_name, view)
87
+ raise RootWidgetNotSetForIncludedView, "You must set a root for views to be included." if view.root_widget.nil?
88
+ add_widget_to_container(view.root_widget, container_widget_name)
89
+ end
90
+
91
+ # Removes a view root widget from the given container widget.
92
+ def remove_view(container_widget_name, view)
93
+ raise RootWidgetNotSetForIncludedView, "You must set a root for views to be removed." if view.root_widget.nil?
94
+ remove_widget_from_container(view.root_widget, container_widget_name)
95
+ end
96
+
97
+ # Removes all children from the given container widget
98
+ def remove_all_children(container_widget)
99
+ self.framework_adapter.remove_all_children(container_widget)
100
+ end
101
+
102
+ # Registers a controller as receiver of signals from the view widgets.
103
+ def register_controller(controller, name = nil)
104
+ name ||= controller.class.to_s.underscore
105
+ autoconnect_signals(controller)
106
+ @controllers[name.to_sym] = controller
107
+ end
108
+
109
+ # Registers a view helper for the view.
110
+ def register_helper(helper, name = nil)
111
+ helper = create_instance_if_possible(helper) if helper.is_a?(String) or helper.is_a?(Symbol)
112
+ unless helper.nil?()
113
+ name ||= helper.class.to_s.underscore
114
+ helper.register_observer(self, name)
115
+ @helpers[name.to_sym] = helper
116
+ create_attribute_reader(:helpers, name)
117
+ helper.post_registration(self)
118
+ end
119
+ end
120
+
121
+ # Called after the view is registered in a controller.
122
+ def post_registration(controller)
123
+ end
124
+
125
+ # Returns the root widget if one is set.
126
+ def root_widget
127
+ send(root.to_sym) if not root.nil?
128
+ end
129
+
130
+ # Returns the builder file.
131
+ def builder_file
132
+ self.configured_builder_file
133
+ end
134
+
135
+ # Returns the builder file extension.
136
+ def builder_file_extension
137
+ self.configured_builder_file_extension
138
+ end
139
+
140
+ # Returns true if builder file is being used for this view.
141
+ def use_builder?
142
+ self.configured_builder_file_usage
143
+ end
144
+
145
+ # Framework adapters should implement this if they support builder files.
146
+ def build_from_builder_file
147
+ filename = get_builder_file
148
+ raise BuilderFileNotFoundError, "Could not find builder file for view #{self.class.name}. UI file paths: #{RuGUI.configuration.builder_files_paths.join(', ')}." if filename.nil?
149
+
150
+ self.framework_adapter.build_widgets_from(filename)
151
+ self.framework_adapter.register_widgets
152
+ end
153
+
154
+ # Returns the name of the root widget for this view.
155
+ def root
156
+ self.configured_root.to_s unless self.configured_root.nil?
157
+ end
158
+
159
+ def display_root?
160
+ !!self.configured_display_root
161
+ end
162
+
163
+ class << self
164
+ # Sets the name of the root widget for this view.
165
+ #
166
+ # This is specially useful when more than one view uses the same glade
167
+ # file, but each one uses a diferent widget tree inside that glade file.
168
+ #
169
+ # Other use for this is when building a reusable widget, composed of the
170
+ # contents of a glade file. One could create a window, place a vertical
171
+ # box, and then place elements inside this vertical box. Later, this glade
172
+ # file is used to insert the contents of the vertical box inside another
173
+ # vertical box in other glade file.
174
+ def root(root_widget_name)
175
+ self.configured_root = root_widget_name
176
+ end
177
+
178
+ # Sets the builder file to use when creating this view.
179
+ def builder_file(file)
180
+ self.configured_builder_file = file
181
+ end
182
+
183
+ # Tells whether we should use a builder file when creating this view.
184
+ #
185
+ # By default the root widget will be displayed, but you can pass false to
186
+ # this method to prevent it for being displayed.
187
+ def use_builder(display_root = true)
188
+ self.configured_builder_file_usage = true
189
+ self.configured_display_root = display_root
190
+
191
+ self.configured_builder_file_extension = self.framework_adapter_class.builder_file_extension
192
+ default_builder_file_path = RuGUI.root.join('app', 'resources', "#{self.configured_builder_file_extension}")
193
+ RuGUI.configuration.builder_files_paths << default_builder_file_path unless RuGUI.configuration.builder_files_paths.include?(default_builder_file_path)
194
+ end
195
+
196
+ def framework_adapter_class
197
+ class_adapter_for('BaseView')
198
+ end
199
+ end
200
+
201
+ protected
202
+ # Builds a widget of the given type, possibly adding it to a parent
203
+ # widget, and display it.
204
+ #
205
+ # The *args are passed to the widget constructor.
206
+ def build_widget(widget_type, widget_name = nil, parent = nil, *args)
207
+ widget = widget_type.new(*args)
208
+ self.framework_adapter.set_widget_name(widget, widget_name)
209
+ add_widget(widget, widget_name)
210
+ add_widget_to_container(widget, parent) unless parent.nil?
211
+ widget.show
212
+ end
213
+
214
+ # Adds the widget to the view.
215
+ #
216
+ # If +widget_name+ is not given one is assumed the widget`s name will be
217
+ # used instead. If the widget doesn't have a name it will be added as an
218
+ # unnamed widget (accessible through #unnamed_widgets property.
219
+ def add_widget(widget, widget_name = nil)
220
+ widget_name ||= widget.name
221
+ widget_name = widget_name.to_s
222
+
223
+ unless widget_name.nil? || widget_name.empty?
224
+ create_attribute_for_widget(widget_name)
225
+ send("#{widget_name}=", widget)
226
+ @widgets[widget_name] = widget
227
+ else
228
+ @unnamed_widgets << widget
229
+ end
230
+ end
231
+
232
+ def from_widget_or_name(widget_or_name)
233
+ if widget_or_name.is_a?(String) || widget_or_name.is_a?(Symbol)
234
+ send(widget_or_name)
235
+ else
236
+ widget_or_name
237
+ end
238
+ end
239
+
240
+ def autoconnect_signals(receiver)
241
+ receiver.autoconnect_declared_signals(self)
242
+ self.framework_adapter.autoconnect_signals(receiver)
243
+ end
244
+
245
+ private
246
+ def get_builder_file
247
+ filename = (not self.builder_file.nil?) ? self.builder_file : "#{self.class.to_s.underscore}.#{builder_file_extension}"
248
+
249
+ # The builder file given may already contain a full path to a glade file.
250
+ return filename if File.file?(filename)
251
+
252
+ filename = "#{filename}.#{builder_file_extension}" unless File.extname(filename) == ".#{builder_file_extension}"
253
+
254
+ paths = RuGUI.configuration.builder_files_paths.select do |path|
255
+ File.file?(File.join(path, filename))
256
+ end
257
+ File.join(paths.first, filename) unless paths.empty?
258
+ end
259
+
260
+ # Attempts to register the default helper for the view
261
+ def register_default_helper
262
+ register_helper("#{self.class.name}Helper", :helper)
263
+ end
264
+
265
+ def create_attribute_for_widget(widget_name)
266
+ self.instance_eval <<-instance_eval
267
+ def #{widget_name}
268
+ @#{widget_name}
269
+ end
270
+
271
+ def #{widget_name}=(widget)
272
+ @#{widget_name} = widget
273
+ end
274
+ instance_eval
275
+ end
276
+
277
+ # Creates an attribute reader for the some entity.
278
+ def create_attribute_reader(entity, name)
279
+ self.class.class_eval <<-class_eval
280
+ def #{name}
281
+ @#{entity}[:#{name}]
282
+ end
283
+ class_eval
284
+ end
285
+
286
+ # Creates an instance of the given class.
287
+ def create_instance_if_possible(klass_name, *args)
288
+ klass_name.to_s.camelize.constantize.new(*args)
289
+ rescue NameError
290
+ # Couldn't create instance.
291
+ end
292
+ end
293
+
294
+ # Exception thrown when the builder file for this view could not be found.
295
+ class BuilderFileNotFoundError < Exception
296
+ end
297
+
298
+ # Exception thrown when attempting to include a view which don't have a root
299
+ # set.
300
+ class RootWidgetNotSetForIncludedView < Exception
301
+ end
302
+ end
@@ -0,0 +1,23 @@
1
+ module RuGUI
2
+ # A base class for view helpers.
3
+ class BaseViewHelper < BaseObject
4
+ include RuGUI::ObservablePropertySupport
5
+ include RuGUI::LogSupport
6
+
7
+ def initialize(observable_properties_values = {})
8
+ initialize_observable_property_support(observable_properties_values)
9
+ end
10
+
11
+ # This is included here so that the initialize method is properly updated.
12
+ include RuGUI::InitializeHooks
13
+
14
+ # Returns the framework_adapter for this class.
15
+ def framework_adapter
16
+ framework_adapter_for('BaseViewHelper')
17
+ end
18
+
19
+ # Called after the view helper is registered in a view.
20
+ def post_registration(view)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,136 @@
1
+ # Defines the environment to be used by the application.
2
+ RUGUI_ENV = (ENV['RUGUI_ENV'] || 'development').dup unless defined?(RUGUI_ENV)
3
+
4
+ module RuGUI
5
+ # Defines configurations for a RuGUI application.
6
+ class Configuration
7
+ # The application root path, defined by ::APPLICATION_ROOT
8
+ attr_reader :root_path
9
+
10
+ # The environment for this application.
11
+ attr_accessor :environment
12
+
13
+ # The framework adapter to be used for this application. Defaults to GTK.
14
+ # For now it can only be GTK.
15
+ attr_accessor :framework_adapter
16
+
17
+ # The specific logger to use. By default, a logger will be created and
18
+ # initialized using #log_path and #log_level, but a programmer may
19
+ # specifically set the logger to use via this accessor and it will be
20
+ # used directly.
21
+ attr_accessor :logger
22
+
23
+ # An array of paths for builder files.
24
+ attr_accessor :builder_files_paths
25
+
26
+ # An array of paths which should be automaticaly loaded.
27
+ attr_accessor :load_paths
28
+
29
+ # An array of paths which should be searched for gtkrc styles files.
30
+ #
31
+ # It searchs for files which have the '.rc' extension or files which have
32
+ # the string 'gtkrc' in its filename.
33
+ #
34
+ # The order in which the files are loaded is random, so do not rely on it.
35
+ #
36
+ # If you need to use absolute paths in a gtkrc file, such as set the pixmap
37
+ # path, you can use "{ROOT_PATH}", which will be substituted by the
38
+ # application root path when the file is read.
39
+ attr_accessor :styles_paths
40
+
41
+ # The timeout for queued calls. Useful when performing long tasks.
42
+ attr_accessor :queue_timeout
43
+
44
+ # A hash of application specific configurations.
45
+ attr_accessor :application
46
+
47
+ # An array of gems that this RuGUI application depends on. RuGUI will automatically load
48
+ # these gems during installation, and allow you to install any missing gems with:
49
+ #
50
+ # rake gems:install
51
+ #
52
+ # You can add gems with the #gem method.
53
+ attr_accessor :gems
54
+
55
+ def initialize
56
+ set_root_path!
57
+
58
+ self.environment = default_environment
59
+ self.framework_adapter = default_framework_adapter
60
+ self.load_paths = default_load_paths
61
+ self.builder_files_paths = default_builder_files_paths
62
+ self.styles_paths = default_styles_paths
63
+ self.queue_timeout = default_queue_timeout
64
+ self.gems = default_gems
65
+ self.logger = {}
66
+ self.application = {}
67
+ end
68
+
69
+ # The path to the current environment's file (<tt>development.rb</tt>, etc.). By
70
+ # default the file is at <tt>config/environments/#{environment}.rb</tt>.
71
+ def environment_path
72
+ root_path.join('config', 'environments', "#{environment}.rb")
73
+ end
74
+
75
+ def set_root_path!
76
+ raise 'APPLICATION_ROOT is not set' unless defined?(::APPLICATION_ROOT)
77
+ raise 'APPLICATION_ROOT is not a directory' unless File.directory?(::APPLICATION_ROOT)
78
+
79
+ @root_path = Pathname.new(File.expand_path(::APPLICATION_ROOT))
80
+ end
81
+
82
+ # Adds a single Gem dependency to the RuGUI application. By default, it will require
83
+ # the library with the same name as the gem. Use :lib to specify a different name.
84
+ #
85
+ # # gem 'aws-s3', '>= 0.4.0'
86
+ # # require 'aws/s3'
87
+ # config.gem 'aws-s3', :lib => 'aws/s3', :version => '>= 0.4.0', \
88
+ # :source => "http://code.whytheluckystiff.net"
89
+ #
90
+ # To require a library be installed, but not attempt to load it, pass :lib => false
91
+ #
92
+ # config.gem 'qrp', :version => '0.4.1', :lib => false
93
+ def gem(name, options = {})
94
+ @gems << RuGUI::GemDependency.new(name, options)
95
+ end
96
+
97
+ private
98
+ def default_environment
99
+ ::RUGUI_ENV
100
+ end
101
+
102
+ def default_framework_adapter
103
+ 'GTK'
104
+ end
105
+
106
+ def default_load_paths
107
+ paths = []
108
+
109
+ paths.concat %w(
110
+ app
111
+ app/models
112
+ app/controllers
113
+ app/views
114
+ app/views/helpers
115
+ config
116
+ lib
117
+ ).map { |dir| root_path.join(dir) }.select { |dir| File.directory?(dir) }
118
+ end
119
+
120
+ def default_builder_files_paths
121
+ []
122
+ end
123
+
124
+ def default_styles_paths
125
+ [root_path.join('app', 'resources', 'styles')]
126
+ end
127
+
128
+ def default_queue_timeout
129
+ 50
130
+ end
131
+
132
+ def default_gems
133
+ []
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,233 @@
1
+ require 'gtk2'
2
+ require 'libglade2'
3
+
4
+ # See the discussion here: http://eigenclass.org/hiki.rb?instance_exec
5
+ class Object
6
+ module InstanceExecHelper; end
7
+ include InstanceExecHelper
8
+ def instance_exec(*args, &block)
9
+ begin
10
+ old_critical, Thread.critical = Thread.critical, true
11
+ n = 0
12
+ n += 1 while respond_to?(mname="__instance_exec#{n}")
13
+ InstanceExecHelper.module_eval{ define_method(mname, &block) }
14
+ ensure
15
+ Thread.critical = old_critical
16
+ end
17
+ begin
18
+ ret = send(mname, *args)
19
+ ensure
20
+ InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
21
+ end
22
+ ret
23
+ end
24
+ end
25
+
26
+ module Gtk
27
+ GTK_PENDING_BLOCKS = []
28
+ GTK_PENDING_BLOCKS_LOCK = Monitor.new
29
+
30
+ def Gtk.queue(&block)
31
+ if Thread.current == Thread.main
32
+ block.call
33
+ else
34
+ GTK_PENDING_BLOCKS_LOCK.synchronize do
35
+ GTK_PENDING_BLOCKS << block
36
+ end
37
+ end
38
+ end
39
+
40
+ # Adds a timeout to execute pending blocks in a queue.
41
+ def Gtk.queue_timeout(timeout)
42
+ Gtk.timeout_add timeout do
43
+ GTK_PENDING_BLOCKS_LOCK.synchronize do
44
+ GTK_PENDING_BLOCKS.each do |block|
45
+ block.call
46
+ end
47
+ GTK_PENDING_BLOCKS.clear
48
+ end
49
+ true
50
+ end
51
+ end
52
+
53
+ def Gtk.load_style_paths
54
+ styles_paths = RuGUI.configuration.styles_paths.select { |path| File.directory?(path) }
55
+ styles_paths.each do |path|
56
+ Dir.new(path).each do |entry|
57
+ Gtk::RC.parse_string(get_style_file_contents(path, entry)) if is_style_file?(path, entry)
58
+ end
59
+ end
60
+ end
61
+
62
+ def Gtk.is_style_file?(path, filename)
63
+ File.extname(filename) == '.rc' or /gtkrc/.match(filename) if File.file?(File.join(path, filename))
64
+ end
65
+
66
+ def Gtk.get_style_file_contents(path, filename)
67
+ IO.read(File.join(path, filename)).sub('{ROOT_PATH}', RuGUI.configuration.root_path)
68
+ end
69
+ end
70
+
71
+ Gtk.load_style_paths
72
+
73
+ module RuGUI
74
+ module FrameworkAdapters
75
+ module GTK
76
+ class BaseController < RuGUI::FrameworkAdapters::BaseFrameworkAdapter::BaseController
77
+ def queue(&block)
78
+ Gtk.queue(&block)
79
+ end
80
+ end
81
+
82
+ class BaseMainController < RuGUI::FrameworkAdapters::GTK::BaseController
83
+ def run
84
+ Gtk.queue_timeout(RuGUI.configuration.queue_timeout)
85
+ Gtk.main
86
+ end
87
+
88
+ def quit
89
+ Gtk.main_quit
90
+ end
91
+ end
92
+
93
+ class BaseView < RuGUI::FrameworkAdapters::BaseFrameworkAdapter::BaseView
94
+ # Queues the block call, so that it is only gets executed in the main thread.
95
+ def queue(&block)
96
+ Gtk.queue(&block)
97
+ end
98
+
99
+ # Adds a widget to the given container widget.
100
+ def add_widget_to_container(widget, container_widget)
101
+ container_widget.add(widget)
102
+ end
103
+
104
+ # Removes a widget from the given container widget.
105
+ def remove_widget_from_container(widget, container_widget)
106
+ container_widget.remove(widget)
107
+ end
108
+
109
+ # Removes all children from the given container widget.
110
+ def remove_all_children(container_widget)
111
+ container_widget.children.each do |child|
112
+ container_widget.remove(child)
113
+ end
114
+ end
115
+
116
+ # Sets the widget name for the given widget if given.
117
+ def set_widget_name(widget, widget_name)
118
+ widget.name = widget_name.to_s unless widget_name.nil?
119
+ end
120
+
121
+ # Autoconnects signals handlers for the view. If +other_target+ is given
122
+ # it is used instead of the view itself.
123
+ def autoconnect_signals(other_target = nil)
124
+ if self.adapted_object.use_builder?
125
+ self.adapted_object.glade.signal_autoconnect_full do |source, target, signal_name, handler_name, signal_data, after|
126
+ target ||= other_target
127
+ self.adapted_object.glade.connect(source, target, signal_name, handler_name, signal_data) if target.respond_to?(handler_name)
128
+ end
129
+ end
130
+ end
131
+
132
+ # Connects the signal from the widget to the given receiver block.
133
+ # The block is executed in the context of the receiver.
134
+ def connect_declared_signal_block(widget, signal, receiver, block)
135
+ widget.signal_connect(signal) do |*args|
136
+ receiver.instance_exec(*args, &block)
137
+ end
138
+ end
139
+
140
+ # Connects the signal from the widget to the given receiver method.
141
+ def connect_declared_signal(widget, signal, receiver, method)
142
+ widget.signal_connect(signal) do |*args|
143
+ receiver.send(method, *args)
144
+ end
145
+ end
146
+
147
+ # Builds widgets from the given filename, using the proper builder.
148
+ def build_widgets_from(filename)
149
+ self.adapted_object.glade = GladeXML.new(filename, self.adapted_object.root, nil, nil, GladeXML::FILE)
150
+
151
+ self.adapted_object.glade.widget_names.each do |widget_name|
152
+ self.adapted_object.send(:create_attribute_for_widget, widget_name) unless self.adapted_object.glade[widget_name].nil?
153
+ end
154
+ self.adapted_object.root_widget.show if self.adapted_object.display_root? and not self.adapted_object.root_widget.nil?
155
+ end
156
+
157
+ # Registers widgets as attributes of the view class.
158
+ def register_widgets
159
+ self.adapted_object.glade.widget_names.each do |widget_name|
160
+ unless self.adapted_object.glade[widget_name].nil?
161
+ self.adapted_object.send("#{widget_name}=".to_sym, self.adapted_object.glade[widget_name])
162
+ self.adapted_object.widgets[widget_name] = self.adapted_object.glade[widget_name]
163
+ end
164
+ end
165
+ end
166
+
167
+ class << self
168
+ # Returns the builder file extension to be used for this view class.
169
+ def builder_file_extension
170
+ 'glade'
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ module RuGUI
179
+ class BaseView < BaseObject
180
+ class_inheritable_accessor :configured_glade_usage
181
+
182
+ attr_accessor :glade
183
+
184
+ # Adds a signal handler for all widgets of the given type.
185
+ def add_signal_handler_for_widget_type(widget_type, signal, &block)
186
+ widgets = []
187
+ widgets.concat(@widgets.values.select { |widget| widget.kind_of?(widget_type) }) unless @widgets.empty?
188
+ widgets.concat(@unnamed_widgets.select { |widget| widget.kind_of?(widget_type) }) unless @unnamed_widgets.empty?
189
+
190
+ widgets.each do |widget|
191
+ widget.signal_connect(signal, &block)
192
+ end
193
+ end
194
+
195
+ # Changes the widget style to use the given widget style.
196
+ #
197
+ # This widget style should be declared in a gtkrc file, by specifying a
198
+ # style using a widget path, such as:
199
+ #
200
+ # widget "main_window" style "main_window_style"
201
+ # widget "main_window_other" style "main_window_other_style"
202
+ #
203
+ # In this example, if you called this method like this:
204
+ #
205
+ # change_widget_style(:main_window, 'main_window_other')
206
+ # change_widget_style(self.main_window, 'main_window_other') # or, passing the widget instance directly
207
+ #
208
+ # The widget style would be set to "main_window_other_style".
209
+ #
210
+ # NOTE: Unfortunately, gtk doesn't offer an API to get declared styles, so
211
+ # you must set a style to a widget. Since the widget name set in the style
212
+ # definition doesn't need to point to an existing widget we can use this
213
+ # to simplify the widget styling here.
214
+ def change_widget_style(widget_or_name, widget_path_style)
215
+ if widget_or_name.is_a?(Gtk::Widget)
216
+ widget = widget_or_name
217
+ else
218
+ widget = @glade[widget_or_name.to_s]
219
+ end
220
+ style = Gtk::RC.get_style_by_paths(Gtk::Settings.default, widget_path_style.to_s, nil, nil)
221
+ widget.style = style
222
+ end
223
+
224
+ class << self
225
+ # Call this method at class level if the view should be built from a glade
226
+ # file.
227
+ def use_glade
228
+ self.logger.warn('DEPRECATED - Call use_builder class method instead in your view.')
229
+ use_builder
230
+ end
231
+ end
232
+ end
233
+ end