forked from Eneroth3/persistent-notifier
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpersistent_notifier.rb
154 lines (136 loc) · 4.41 KB
/
persistent_notifier.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# Wrapper for adding observers that persists between models.
#
# Used to avoid boilerplate code for re-adding observer to new models.
module PersistentNotifier
# The observer classes supported by this wrapper.
VALID_OBSERVERS = [
Sketchup::ModelObserver,
Sketchup::SelectionObserver,
Sketchup::PagesObserver
].freeze
# Registered observers and the subjects they are observing.
# A single observer may observe multiple objects, e.g. multiple open models
# on Mac.
@observers ||= {}
# Add observer and have it persists between models.
#
# @param observer [Object]
# See VALID_OBSERVERS for supported observer classes.
# What object observer gets attached to is determined by its class.
#
# AppObservers and FrameChangeObservers are already persistent in SketchUp
# and not supported by this wrapper.
#
# Observers listening to objects that can re-occur in a model, e.g.
# Entities or Entity, are not supported, as this wrapper can't guess what
# object you want to listen to.
#
# @raise [ArgumentError] for unsupported observers.
#
# @return [void]
def self.add_observer(observer)
raise ArgumentError unless VALID_OBSERVERS.any? { |c| observer.is_a?(c) }
@observers[observer] ||= Set.new
register_observers(Sketchup.active_model)
nil
end
# Remove persistent observer.
#
# @param observer [Object]
# See VALID_OBSERVERS for supported observer classes.
#
# @raise [ArgumentError] if observer has not first been added.
# @raise [ArgumentError] for unsupported observers.
#
# @return [void]
def self.remove_observer(observer)
raise ArgumentError unless VALID_OBSERVERS.any? { |c| observer.is_a?(c) }
raise ArgumentError, "Observer not attached." unless @observers[observer]
@observers[observer].each { |s| s.remove_observer(observer) if valid?(s) }
@observers.delete(observer)
nil
end
#-----------------------------------------------------------------------------
# Internal method for actually registering the observers to the SketchUp Ruby
# API.
#
# @param model [Sketchup::Model]
#
# @return [void]
def self.register_observers(model)
@observers.each do |observer, subjects|
subject = guess_subject(model, observer)
# Ass observer regardless of whether subject is in the existing subjects
# set, as SketchUp re-uses the same Model object when switching model on
# PC.
# At least SketchUp seems to be smart enough to not fire callbacks
# multiple times if the observer is added multiple times.
### next if subjects.include?(subject)
subject.add_observer(observer)
subjects.add(subject)
end
end
private_class_method :register_observers
# Purge deleted subjects from observer subject set.
#
# @return [void]
def self.purge_invalid_subjects
@observers.each_value { |ss| ss.select! { |s| valid?(s) } }
end
private_class_method :purge_invalid_subjects
# Get object to attach observer to based on observer's class.
#
# @param model [Sketchup::Model]
# @param observer [Object]
#
# @return [#add_observer]
def self.guess_subject(model, observer)
case observer
when Sketchup::ModelObserver
model
when Sketchup::SelectionObserver
model.selection
when Sketchup::PagesObserver
model.pages
else
raise ArgumentError
end
end
private_class_method :guess_subject
# Check if object still exists.
#
# @param object [Sketchup::Entity, #model]
def self.valid?(object)
# There is no #valid? method for Selection. Instead check if its model
# is valid.
(object.is_a?(Sketchup::Entity) && object.valid?) \
|| (object.respond_to?(:model) && object.model.valid?)
end
private_class_method :valid?
# @private
# Expected to be called whenever a model is created, opened, or activated
# (switched to in multi document Mac version of SketchUp).
def self.on_model_init(model)
purge_invalid_subjects
register_observers(model)
end
# @private
class AppObserver < Sketchup::AppObserver
def expectsStartupModelNotifications
true
end
def onNewModel(model)
PersistentNotifier.on_model_init(model)
end
def onOpenModel(model)
PersistentNotifier.on_model_init(model)
end
def onActivateModel(model)
PersistentNotifier.on_model_init(model)
end
end
unless @loaded
@loaded = true
Sketchup.add_observer(AppObserver.new)
end
end