1*70f497fbSAndrew Rist /**************************************************************
2cdf0e10cSrcweir *
3*70f497fbSAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one
4*70f497fbSAndrew Rist * or more contributor license agreements. See the NOTICE file
5*70f497fbSAndrew Rist * distributed with this work for additional information
6*70f497fbSAndrew Rist * regarding copyright ownership. The ASF licenses this file
7*70f497fbSAndrew Rist * to you under the Apache License, Version 2.0 (the
8*70f497fbSAndrew Rist * "License"); you may not use this file except in compliance
9*70f497fbSAndrew Rist * with the License. You may obtain a copy of the License at
10*70f497fbSAndrew Rist *
11*70f497fbSAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0
12*70f497fbSAndrew Rist *
13*70f497fbSAndrew Rist * Unless required by applicable law or agreed to in writing,
14*70f497fbSAndrew Rist * software distributed under the License is distributed on an
15*70f497fbSAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*70f497fbSAndrew Rist * KIND, either express or implied. See the License for the
17*70f497fbSAndrew Rist * specific language governing permissions and limitations
18*70f497fbSAndrew Rist * under the License.
19*70f497fbSAndrew Rist *
20*70f497fbSAndrew Rist *************************************************************/
21*70f497fbSAndrew Rist
22*70f497fbSAndrew Rist
23cdf0e10cSrcweir
24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
25cdf0e10cSrcweir #include "precompiled_slideshow.hxx"
26cdf0e10cSrcweir
27cdf0e10cSrcweir // must be first
28cdf0e10cSrcweir #include <canvas/debug.hxx>
29cdf0e10cSrcweir #include <canvas/verbosetrace.hxx>
30cdf0e10cSrcweir
31cdf0e10cSrcweir #include <com/sun/star/animations/XAnimate.hpp>
32cdf0e10cSrcweir #include <com/sun/star/presentation/ParagraphTarget.hpp>
33cdf0e10cSrcweir #include <com/sun/star/animations/AnimationFill.hpp>
34cdf0e10cSrcweir #include <com/sun/star/animations/AnimationRestart.hpp>
35cdf0e10cSrcweir #include <com/sun/star/presentation/EffectNodeType.hpp>
36cdf0e10cSrcweir #include <com/sun/star/beans/XPropertySet.hpp>
37cdf0e10cSrcweir
38cdf0e10cSrcweir #include "basenode.hxx"
39cdf0e10cSrcweir #include "eventmultiplexer.hxx"
40cdf0e10cSrcweir #include "basecontainernode.hxx"
41cdf0e10cSrcweir #include "eventqueue.hxx"
42cdf0e10cSrcweir #include "delayevent.hxx"
43cdf0e10cSrcweir #include "tools.hxx"
44cdf0e10cSrcweir #include "nodetools.hxx"
45cdf0e10cSrcweir #include "generateevent.hxx"
46cdf0e10cSrcweir #include "debug.hxx"
47cdf0e10cSrcweir
48cdf0e10cSrcweir #include <boost/bind.hpp>
49cdf0e10cSrcweir #include <vector>
50cdf0e10cSrcweir #include <algorithm>
51cdf0e10cSrcweir #include <iterator>
52cdf0e10cSrcweir
53cdf0e10cSrcweir using namespace ::com::sun::star;
54cdf0e10cSrcweir
55cdf0e10cSrcweir namespace slideshow {
56cdf0e10cSrcweir namespace internal {
57cdf0e10cSrcweir
58cdf0e10cSrcweir namespace {
59cdf0e10cSrcweir
60cdf0e10cSrcweir typedef int StateTransitionTable[17];
61cdf0e10cSrcweir
62cdf0e10cSrcweir // State transition tables
63cdf0e10cSrcweir // =========================================================================
64cdf0e10cSrcweir
getStateTransitionTable(sal_Int16 nRestartMode,sal_Int16 nFillMode)65cdf0e10cSrcweir const int* getStateTransitionTable( sal_Int16 nRestartMode,
66cdf0e10cSrcweir sal_Int16 nFillMode )
67cdf0e10cSrcweir {
68cdf0e10cSrcweir // TODO(F2): restart issues in below tables
69cdf0e10cSrcweir
70cdf0e10cSrcweir // transition table for restart=NEVER, fill=REMOVE
71cdf0e10cSrcweir static const StateTransitionTable stateTransitionTable_Never_Remove = {
72cdf0e10cSrcweir AnimationNode::INVALID,
73cdf0e10cSrcweir AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED
74cdf0e10cSrcweir AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED
75cdf0e10cSrcweir AnimationNode::INVALID,
76cdf0e10cSrcweir AnimationNode::ENDED, // active successors for ACTIVE: no freeze here
77cdf0e10cSrcweir AnimationNode::INVALID,
78cdf0e10cSrcweir AnimationNode::INVALID,
79cdf0e10cSrcweir AnimationNode::INVALID,
80cdf0e10cSrcweir AnimationNode::INVALID, // active successors for FROZEN: this state is unreachable here
81cdf0e10cSrcweir AnimationNode::INVALID,
82cdf0e10cSrcweir AnimationNode::INVALID,
83cdf0e10cSrcweir AnimationNode::INVALID,
84cdf0e10cSrcweir AnimationNode::INVALID,
85cdf0e10cSrcweir AnimationNode::INVALID,
86cdf0e10cSrcweir AnimationNode::INVALID,
87cdf0e10cSrcweir AnimationNode::INVALID,
88cdf0e10cSrcweir AnimationNode::ENDED // active successors for ENDED: this state is a sink here (cannot restart)
89cdf0e10cSrcweir };
90cdf0e10cSrcweir
91cdf0e10cSrcweir // transition table for restart=WHEN_NOT_ACTIVE, fill=REMOVE
92cdf0e10cSrcweir static const StateTransitionTable stateTransitionTable_NotActive_Remove = {
93cdf0e10cSrcweir AnimationNode::INVALID,
94cdf0e10cSrcweir AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED
95cdf0e10cSrcweir AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED
96cdf0e10cSrcweir AnimationNode::INVALID,
97cdf0e10cSrcweir AnimationNode::ENDED, // active successors for ACTIVE: no freeze here
98cdf0e10cSrcweir AnimationNode::INVALID,
99cdf0e10cSrcweir AnimationNode::INVALID,
100cdf0e10cSrcweir AnimationNode::INVALID,
101cdf0e10cSrcweir AnimationNode::INVALID, // active successors for FROZEN:
102cdf0e10cSrcweir // this state is unreachable here
103cdf0e10cSrcweir AnimationNode::INVALID,
104cdf0e10cSrcweir AnimationNode::INVALID,
105cdf0e10cSrcweir AnimationNode::INVALID,
106cdf0e10cSrcweir AnimationNode::INVALID,
107cdf0e10cSrcweir AnimationNode::INVALID,
108cdf0e10cSrcweir AnimationNode::INVALID,
109cdf0e10cSrcweir AnimationNode::INVALID,
110cdf0e10cSrcweir AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE // active successors for ENDED:
111cdf0e10cSrcweir // restart possible when ended
112cdf0e10cSrcweir };
113cdf0e10cSrcweir
114cdf0e10cSrcweir // transition table for restart=ALWAYS, fill=REMOVE
115cdf0e10cSrcweir static const StateTransitionTable stateTransitionTable_Always_Remove = {
116cdf0e10cSrcweir AnimationNode::INVALID,
117cdf0e10cSrcweir AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED
118cdf0e10cSrcweir AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED
119cdf0e10cSrcweir AnimationNode::INVALID,
120cdf0e10cSrcweir AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED, // active successors for ACTIVE: restart
121cdf0e10cSrcweir AnimationNode::INVALID,
122cdf0e10cSrcweir AnimationNode::INVALID,
123cdf0e10cSrcweir AnimationNode::INVALID,
124cdf0e10cSrcweir AnimationNode::INVALID, // active successors for FROZEN:
125cdf0e10cSrcweir // this state is unreachable here
126cdf0e10cSrcweir AnimationNode::INVALID,
127cdf0e10cSrcweir AnimationNode::INVALID,
128cdf0e10cSrcweir AnimationNode::INVALID,
129cdf0e10cSrcweir AnimationNode::INVALID,
130cdf0e10cSrcweir AnimationNode::INVALID,
131cdf0e10cSrcweir AnimationNode::INVALID,
132cdf0e10cSrcweir AnimationNode::INVALID,
133cdf0e10cSrcweir AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED // active successors for ENDED: restart
134cdf0e10cSrcweir };
135cdf0e10cSrcweir
136cdf0e10cSrcweir // transition table for restart=NEVER, fill=FREEZE
137cdf0e10cSrcweir static const StateTransitionTable stateTransitionTable_Never_Freeze = {
138cdf0e10cSrcweir AnimationNode::INVALID,
139cdf0e10cSrcweir AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED
140cdf0e10cSrcweir AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED
141cdf0e10cSrcweir AnimationNode::INVALID,
142cdf0e10cSrcweir AnimationNode::FROZEN|AnimationNode::ENDED, // active successors for ACTIVE: freeze object
143cdf0e10cSrcweir AnimationNode::INVALID,
144cdf0e10cSrcweir AnimationNode::INVALID,
145cdf0e10cSrcweir AnimationNode::INVALID,
146cdf0e10cSrcweir AnimationNode::ENDED, // active successors for FROZEN: end
147cdf0e10cSrcweir AnimationNode::INVALID,
148cdf0e10cSrcweir AnimationNode::INVALID,
149cdf0e10cSrcweir AnimationNode::INVALID,
150cdf0e10cSrcweir AnimationNode::INVALID,
151cdf0e10cSrcweir AnimationNode::INVALID,
152cdf0e10cSrcweir AnimationNode::INVALID,
153cdf0e10cSrcweir AnimationNode::INVALID,
154cdf0e10cSrcweir AnimationNode::ENDED, // active successors for ENDED: this state is a sink here (cannot restart)
155cdf0e10cSrcweir };
156cdf0e10cSrcweir
157cdf0e10cSrcweir // transition table for restart=WHEN_NOT_ACTIVE, fill=FREEZE
158cdf0e10cSrcweir static const StateTransitionTable stateTransitionTable_NotActive_Freeze = {
159cdf0e10cSrcweir AnimationNode::INVALID,
160cdf0e10cSrcweir AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED
161cdf0e10cSrcweir AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED
162cdf0e10cSrcweir AnimationNode::INVALID,
163cdf0e10cSrcweir AnimationNode::FROZEN|AnimationNode::ENDED, // active successors for ACTIVE: freeze object
164cdf0e10cSrcweir AnimationNode::INVALID,
165cdf0e10cSrcweir AnimationNode::INVALID,
166cdf0e10cSrcweir AnimationNode::INVALID,
167cdf0e10cSrcweir AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE, // active successors for FROZEN:
168cdf0e10cSrcweir // restart possible when ended
169cdf0e10cSrcweir AnimationNode::INVALID,
170cdf0e10cSrcweir AnimationNode::INVALID,
171cdf0e10cSrcweir AnimationNode::INVALID,
172cdf0e10cSrcweir AnimationNode::INVALID,
173cdf0e10cSrcweir AnimationNode::INVALID,
174cdf0e10cSrcweir AnimationNode::INVALID,
175cdf0e10cSrcweir AnimationNode::INVALID,
176cdf0e10cSrcweir AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE // active successors for ENDED:
177cdf0e10cSrcweir // restart possible when ended
178cdf0e10cSrcweir };
179cdf0e10cSrcweir
180cdf0e10cSrcweir // transition table for restart=ALWAYS, fill=FREEZE
181cdf0e10cSrcweir static const StateTransitionTable stateTransitionTable_Always_Freeze = {
182cdf0e10cSrcweir AnimationNode::INVALID,
183cdf0e10cSrcweir AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED
184cdf0e10cSrcweir AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED
185cdf0e10cSrcweir AnimationNode::INVALID,
186cdf0e10cSrcweir AnimationNode::FROZEN|AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED, // active successors for ACTIVE:
187cdf0e10cSrcweir // end object, restart
188cdf0e10cSrcweir AnimationNode::INVALID,
189cdf0e10cSrcweir AnimationNode::INVALID,
190cdf0e10cSrcweir AnimationNode::INVALID,
191cdf0e10cSrcweir AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE, // active successors for FROZEN: restart possible
192cdf0e10cSrcweir AnimationNode::INVALID,
193cdf0e10cSrcweir AnimationNode::INVALID,
194cdf0e10cSrcweir AnimationNode::INVALID,
195cdf0e10cSrcweir AnimationNode::INVALID,
196cdf0e10cSrcweir AnimationNode::INVALID,
197cdf0e10cSrcweir AnimationNode::INVALID,
198cdf0e10cSrcweir AnimationNode::INVALID,
199cdf0e10cSrcweir AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED // active successors for ENDED: restart
200cdf0e10cSrcweir };
201cdf0e10cSrcweir
202cdf0e10cSrcweir static const StateTransitionTable* tableGuide[] = {
203cdf0e10cSrcweir &stateTransitionTable_Never_Remove,
204cdf0e10cSrcweir &stateTransitionTable_NotActive_Remove,
205cdf0e10cSrcweir &stateTransitionTable_Always_Remove,
206cdf0e10cSrcweir &stateTransitionTable_Never_Freeze,
207cdf0e10cSrcweir &stateTransitionTable_NotActive_Freeze,
208cdf0e10cSrcweir &stateTransitionTable_Always_Freeze
209cdf0e10cSrcweir };
210cdf0e10cSrcweir
211cdf0e10cSrcweir int nRestartValue;
212cdf0e10cSrcweir switch( nRestartMode ) {
213cdf0e10cSrcweir default:
214cdf0e10cSrcweir case animations::AnimationRestart::DEFAULT:
215cdf0e10cSrcweir // same value: animations::AnimationRestart::INHERIT:
216cdf0e10cSrcweir OSL_ENSURE(
217cdf0e10cSrcweir false, "getStateTransitionTable(): unexpected case for restart" );
218cdf0e10cSrcweir // FALLTHROUGH intended
219cdf0e10cSrcweir case animations::AnimationRestart::NEVER:
220cdf0e10cSrcweir nRestartValue = 0;
221cdf0e10cSrcweir break;
222cdf0e10cSrcweir case animations::AnimationRestart::WHEN_NOT_ACTIVE:
223cdf0e10cSrcweir nRestartValue = 1;
224cdf0e10cSrcweir break;
225cdf0e10cSrcweir case animations::AnimationRestart::ALWAYS:
226cdf0e10cSrcweir nRestartValue = 2;
227cdf0e10cSrcweir break;
228cdf0e10cSrcweir }
229cdf0e10cSrcweir
230cdf0e10cSrcweir int nFillValue;
231cdf0e10cSrcweir switch( nFillMode ) {
232cdf0e10cSrcweir default:
233cdf0e10cSrcweir case animations::AnimationFill::AUTO:
234cdf0e10cSrcweir case animations::AnimationFill::DEFAULT:
235cdf0e10cSrcweir // same value: animations::AnimationFill::INHERIT:
236cdf0e10cSrcweir OSL_ENSURE(
237cdf0e10cSrcweir false, "getStateTransitionTable(): unexpected case for fill" );
238cdf0e10cSrcweir // FALLTHROUGH intended
239cdf0e10cSrcweir case animations::AnimationFill::REMOVE:
240cdf0e10cSrcweir nFillValue = 0;
241cdf0e10cSrcweir break;
242cdf0e10cSrcweir case animations::AnimationFill::FREEZE:
243cdf0e10cSrcweir case animations::AnimationFill::HOLD:
244cdf0e10cSrcweir case animations::AnimationFill::TRANSITION:
245cdf0e10cSrcweir nFillValue = 1;
246cdf0e10cSrcweir break;
247cdf0e10cSrcweir }
248cdf0e10cSrcweir
249cdf0e10cSrcweir return *tableGuide[ 3*nFillValue + nRestartValue ];
250cdf0e10cSrcweir }
251cdf0e10cSrcweir
252cdf0e10cSrcweir /// Little helper predicate, to detect main sequence root node
isMainSequenceRootNode_(const uno::Reference<animations::XAnimationNode> & xNode)253cdf0e10cSrcweir bool isMainSequenceRootNode_(
254cdf0e10cSrcweir const uno::Reference< animations::XAnimationNode >& xNode )
255cdf0e10cSrcweir {
256cdf0e10cSrcweir // detect main sequence root node (need that for
257cdf0e10cSrcweir // end-of-mainsequence signalling below)
258cdf0e10cSrcweir beans::NamedValue const aSearchKey(
259cdf0e10cSrcweir rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) ),
260cdf0e10cSrcweir uno::makeAny( presentation::EffectNodeType::MAIN_SEQUENCE ) );
261cdf0e10cSrcweir
262cdf0e10cSrcweir uno::Sequence<beans::NamedValue> const userData(xNode->getUserData());
263cdf0e10cSrcweir return findNamedValue( userData, aSearchKey );
264cdf0e10cSrcweir }
265cdf0e10cSrcweir
266cdf0e10cSrcweir } // anon namespace
267cdf0e10cSrcweir
268cdf0e10cSrcweir // BaseNode implementation
269cdf0e10cSrcweir //=========================================================================
270cdf0e10cSrcweir
271cdf0e10cSrcweir /** state transition handling
272cdf0e10cSrcweir */
273cdf0e10cSrcweir class BaseNode::StateTransition : private boost::noncopyable
274cdf0e10cSrcweir {
275cdf0e10cSrcweir public:
276cdf0e10cSrcweir enum Options { NONE, FORCE };
277cdf0e10cSrcweir
StateTransition(BaseNode * pNode)278cdf0e10cSrcweir explicit StateTransition( BaseNode * pNode )
279cdf0e10cSrcweir : mpNode(pNode), meToState(INVALID) {}
280cdf0e10cSrcweir
~StateTransition()281cdf0e10cSrcweir ~StateTransition() {
282cdf0e10cSrcweir clear();
283cdf0e10cSrcweir }
284cdf0e10cSrcweir
enter(NodeState eToState,int options=NONE)285cdf0e10cSrcweir bool enter( NodeState eToState, int options = NONE )
286cdf0e10cSrcweir {
287cdf0e10cSrcweir OSL_ENSURE( meToState == INVALID,
288cdf0e10cSrcweir "### commit() before enter()ing again!" );
289cdf0e10cSrcweir if (meToState != INVALID)
290cdf0e10cSrcweir return false;
291cdf0e10cSrcweir bool const bForce = ((options & FORCE) != 0);
292cdf0e10cSrcweir if (!bForce && !mpNode->isTransition( mpNode->meCurrState, eToState ))
293cdf0e10cSrcweir return false;
294cdf0e10cSrcweir // recursion detection:
295cdf0e10cSrcweir if ((mpNode->meCurrentStateTransition & eToState) != 0)
296cdf0e10cSrcweir return false; // already in wanted transition
297cdf0e10cSrcweir // mark transition:
298cdf0e10cSrcweir mpNode->meCurrentStateTransition |= eToState;
299cdf0e10cSrcweir meToState = eToState;
300cdf0e10cSrcweir return true; // in transition
301cdf0e10cSrcweir }
302cdf0e10cSrcweir
commit()303cdf0e10cSrcweir void commit() {
304cdf0e10cSrcweir OSL_ENSURE( meToState != INVALID, "### nothing to commit!" );
305cdf0e10cSrcweir if (meToState != INVALID) {
306cdf0e10cSrcweir mpNode->meCurrState = meToState;
307cdf0e10cSrcweir clear();
308cdf0e10cSrcweir }
309cdf0e10cSrcweir
310cdf0e10cSrcweir // Uncomment the following line to write the node tree to file on
311cdf0e10cSrcweir // every state change of one of its nodes.
312cdf0e10cSrcweir // Debug_ShowNodeTree(mpNode->mpSelf);
313cdf0e10cSrcweir }
314cdf0e10cSrcweir
clear()315cdf0e10cSrcweir void clear() {
316cdf0e10cSrcweir if (meToState != INVALID) {
317cdf0e10cSrcweir OSL_ASSERT( (mpNode->meCurrentStateTransition & meToState) != 0 );
318cdf0e10cSrcweir mpNode->meCurrentStateTransition &= ~meToState;
319cdf0e10cSrcweir meToState = INVALID;
320cdf0e10cSrcweir }
321cdf0e10cSrcweir }
322cdf0e10cSrcweir
323cdf0e10cSrcweir private:
324cdf0e10cSrcweir BaseNode *const mpNode;
325cdf0e10cSrcweir NodeState meToState;
326cdf0e10cSrcweir };
327cdf0e10cSrcweir
BaseNode(const uno::Reference<animations::XAnimationNode> & xNode,const BaseContainerNodeSharedPtr & rParent,const NodeContext & rContext)328cdf0e10cSrcweir BaseNode::BaseNode( const uno::Reference< animations::XAnimationNode >& xNode,
329cdf0e10cSrcweir const BaseContainerNodeSharedPtr& rParent,
330cdf0e10cSrcweir const NodeContext& rContext ) :
331cdf0e10cSrcweir maContext( rContext.maContext ),
332cdf0e10cSrcweir maDeactivatingListeners(),
333cdf0e10cSrcweir mxAnimationNode( xNode ),
334cdf0e10cSrcweir mpParent( rParent ),
335cdf0e10cSrcweir mpSelf(),
336cdf0e10cSrcweir mpStateTransitionTable( NULL ),
337cdf0e10cSrcweir mnStartDelay( rContext.mnStartDelay ),
338cdf0e10cSrcweir meCurrState( UNRESOLVED ),
339cdf0e10cSrcweir meCurrentStateTransition( 0 ),
340cdf0e10cSrcweir mpCurrentEvent(),
341cdf0e10cSrcweir mbIsMainSequenceRootNode( isMainSequenceRootNode_( xNode ) )
342cdf0e10cSrcweir {
343cdf0e10cSrcweir ENSURE_OR_THROW( mxAnimationNode.is(),
344cdf0e10cSrcweir "BaseNode::BaseNode(): Invalid XAnimationNode" );
345cdf0e10cSrcweir
346cdf0e10cSrcweir // setup state transition table
347cdf0e10cSrcweir mpStateTransitionTable = getStateTransitionTable( getRestartMode(),
348cdf0e10cSrcweir getFillMode() );
349cdf0e10cSrcweir }
350cdf0e10cSrcweir
dispose()351cdf0e10cSrcweir void BaseNode::dispose()
352cdf0e10cSrcweir {
353cdf0e10cSrcweir meCurrState = INVALID;
354cdf0e10cSrcweir
355cdf0e10cSrcweir // discharge a loaded event, if any:
356cdf0e10cSrcweir if (mpCurrentEvent) {
357cdf0e10cSrcweir mpCurrentEvent->dispose();
358cdf0e10cSrcweir mpCurrentEvent.reset();
359cdf0e10cSrcweir }
360cdf0e10cSrcweir maDeactivatingListeners.clear();
361cdf0e10cSrcweir mxAnimationNode.clear();
362cdf0e10cSrcweir mpParent.reset();
363cdf0e10cSrcweir mpSelf.reset();
364cdf0e10cSrcweir maContext.dispose();
365cdf0e10cSrcweir }
366cdf0e10cSrcweir
367cdf0e10cSrcweir
getRestartMode()368cdf0e10cSrcweir sal_Int16 BaseNode::getRestartMode()
369cdf0e10cSrcweir {
370cdf0e10cSrcweir const sal_Int16 nTmp( mxAnimationNode->getRestart() );
371cdf0e10cSrcweir return (nTmp != animations::AnimationRestart::DEFAULT &&
372cdf0e10cSrcweir nTmp != animations::AnimationRestart::INHERIT)
373cdf0e10cSrcweir ? nTmp : getRestartDefaultMode();
374cdf0e10cSrcweir }
375cdf0e10cSrcweir
getFillMode()376cdf0e10cSrcweir sal_Int16 BaseNode::getFillMode()
377cdf0e10cSrcweir {
378cdf0e10cSrcweir const sal_Int16 nTmp( mxAnimationNode->getFill() );
379cdf0e10cSrcweir const sal_Int16 nFill((nTmp != animations::AnimationFill::DEFAULT &&
380cdf0e10cSrcweir nTmp != animations::AnimationFill::INHERIT)
381cdf0e10cSrcweir ? nTmp : getFillDefaultMode());
382cdf0e10cSrcweir
383cdf0e10cSrcweir // For AUTO fill mode, SMIL specifies that fill mode is FREEZE,
384cdf0e10cSrcweir // if no explicit active duration is given
385cdf0e10cSrcweir // (no duration, end, repeatCount or repeatDuration given),
386cdf0e10cSrcweir // and REMOVE otherwise
387cdf0e10cSrcweir if( nFill == animations::AnimationFill::AUTO ) {
388cdf0e10cSrcweir return (isIndefiniteTiming( mxAnimationNode->getDuration() ) &&
389cdf0e10cSrcweir isIndefiniteTiming( mxAnimationNode->getEnd() ) &&
390cdf0e10cSrcweir !mxAnimationNode->getRepeatCount().hasValue() &&
391cdf0e10cSrcweir isIndefiniteTiming( mxAnimationNode->getRepeatDuration() ))
392cdf0e10cSrcweir ? animations::AnimationFill::FREEZE
393cdf0e10cSrcweir : animations::AnimationFill::REMOVE;
394cdf0e10cSrcweir }
395cdf0e10cSrcweir else {
396cdf0e10cSrcweir return nFill;
397cdf0e10cSrcweir }
398cdf0e10cSrcweir }
399cdf0e10cSrcweir
getFillDefaultMode() const400cdf0e10cSrcweir sal_Int16 BaseNode::getFillDefaultMode() const
401cdf0e10cSrcweir {
402cdf0e10cSrcweir sal_Int16 nFillDefault = mxAnimationNode->getFillDefault();
403cdf0e10cSrcweir if (nFillDefault == animations::AnimationFill::DEFAULT) {
404cdf0e10cSrcweir nFillDefault = (mpParent != 0
405cdf0e10cSrcweir ? mpParent->getFillDefaultMode()
406cdf0e10cSrcweir : animations::AnimationFill::AUTO);
407cdf0e10cSrcweir }
408cdf0e10cSrcweir return nFillDefault;
409cdf0e10cSrcweir }
410cdf0e10cSrcweir
getRestartDefaultMode() const411cdf0e10cSrcweir sal_Int16 BaseNode::getRestartDefaultMode() const
412cdf0e10cSrcweir {
413cdf0e10cSrcweir sal_Int16 nRestartDefaultMode = mxAnimationNode->getRestartDefault();
414cdf0e10cSrcweir if (nRestartDefaultMode == animations::AnimationRestart::DEFAULT) {
415cdf0e10cSrcweir nRestartDefaultMode = (mpParent != 0
416cdf0e10cSrcweir ? mpParent->getRestartDefaultMode()
417cdf0e10cSrcweir : animations::AnimationRestart::ALWAYS);
418cdf0e10cSrcweir }
419cdf0e10cSrcweir return nRestartDefaultMode;
420cdf0e10cSrcweir }
421cdf0e10cSrcweir
getXAnimationNode() const422cdf0e10cSrcweir uno::Reference<animations::XAnimationNode> BaseNode::getXAnimationNode() const
423cdf0e10cSrcweir {
424cdf0e10cSrcweir return mxAnimationNode;
425cdf0e10cSrcweir }
426cdf0e10cSrcweir
init()427cdf0e10cSrcweir bool BaseNode::init()
428cdf0e10cSrcweir {
429cdf0e10cSrcweir if (! checkValidNode())
430cdf0e10cSrcweir return false;
431cdf0e10cSrcweir meCurrState = UNRESOLVED;
432cdf0e10cSrcweir // discharge a loaded event, if any:
433cdf0e10cSrcweir if (mpCurrentEvent) {
434cdf0e10cSrcweir mpCurrentEvent->dispose();
435cdf0e10cSrcweir mpCurrentEvent.reset();
436cdf0e10cSrcweir }
437cdf0e10cSrcweir return init_st(); // may call derived class
438cdf0e10cSrcweir }
439cdf0e10cSrcweir
init_st()440cdf0e10cSrcweir bool BaseNode::init_st()
441cdf0e10cSrcweir {
442cdf0e10cSrcweir return true;
443cdf0e10cSrcweir }
444cdf0e10cSrcweir
resolve()445cdf0e10cSrcweir bool BaseNode::resolve()
446cdf0e10cSrcweir {
447cdf0e10cSrcweir if (! checkValidNode())
448cdf0e10cSrcweir return false;
449cdf0e10cSrcweir
450cdf0e10cSrcweir OSL_ASSERT( meCurrState != RESOLVED );
451cdf0e10cSrcweir if (inStateOrTransition( RESOLVED ))
452cdf0e10cSrcweir return true;
453cdf0e10cSrcweir
454cdf0e10cSrcweir StateTransition st(this);
455cdf0e10cSrcweir if (st.enter( RESOLVED ) &&
456cdf0e10cSrcweir isTransition( RESOLVED, ACTIVE ) &&
457cdf0e10cSrcweir resolve_st() /* may call derived class */)
458cdf0e10cSrcweir {
459cdf0e10cSrcweir st.commit(); // changing state
460cdf0e10cSrcweir
461cdf0e10cSrcweir // discharge a loaded event, if any:
462cdf0e10cSrcweir if (mpCurrentEvent)
463cdf0e10cSrcweir mpCurrentEvent->dispose();
464cdf0e10cSrcweir
465cdf0e10cSrcweir // schedule activation event:
466cdf0e10cSrcweir
467cdf0e10cSrcweir // This method takes the NodeContext::mnStartDelay value into account,
468cdf0e10cSrcweir // to cater for iterate container time shifts. We cannot put different
469cdf0e10cSrcweir // iterations of the iterate container's children into different
470cdf0e10cSrcweir // subcontainer (such as a 'DelayContainer', which delays resolving its
471cdf0e10cSrcweir // children by a fixed amount), since all iterations' nodes must be
472cdf0e10cSrcweir // resolved at the same time (otherwise, the delayed subset creation
473cdf0e10cSrcweir // will not work, i.e. deactivate the subsets too late in the master
474cdf0e10cSrcweir // shape).
475cdf0e10cSrcweir uno::Any const aBegin( mxAnimationNode->getBegin() );
476cdf0e10cSrcweir if (aBegin.hasValue()) {
477cdf0e10cSrcweir mpCurrentEvent = generateEvent(
478cdf0e10cSrcweir aBegin, boost::bind( &AnimationNode::activate, mpSelf ),
479cdf0e10cSrcweir maContext, mnStartDelay );
480cdf0e10cSrcweir }
481cdf0e10cSrcweir else {
482cdf0e10cSrcweir // For some leaf nodes, PPT import yields empty begin time,
483cdf0e10cSrcweir // although semantically, it should be 0.0
484cdf0e10cSrcweir // TODO(F3): That should really be provided by the PPT import
485cdf0e10cSrcweir
486cdf0e10cSrcweir // schedule delayed activation event. Take iterate node
487cdf0e10cSrcweir // timeout into account
488cdf0e10cSrcweir mpCurrentEvent = makeDelay(
489cdf0e10cSrcweir boost::bind( &AnimationNode::activate, mpSelf ),
490cdf0e10cSrcweir mnStartDelay,
491cdf0e10cSrcweir "AnimationNode::activate with delay");
492cdf0e10cSrcweir maContext.mrEventQueue.addEvent( mpCurrentEvent );
493cdf0e10cSrcweir }
494cdf0e10cSrcweir
495cdf0e10cSrcweir return true;
496cdf0e10cSrcweir }
497cdf0e10cSrcweir return false;
498cdf0e10cSrcweir }
499cdf0e10cSrcweir
resolve_st()500cdf0e10cSrcweir bool BaseNode::resolve_st()
501cdf0e10cSrcweir {
502cdf0e10cSrcweir return true;
503cdf0e10cSrcweir }
504cdf0e10cSrcweir
505cdf0e10cSrcweir
activate()506cdf0e10cSrcweir bool BaseNode::activate()
507cdf0e10cSrcweir {
508cdf0e10cSrcweir if (! checkValidNode())
509cdf0e10cSrcweir return false;
510cdf0e10cSrcweir
511cdf0e10cSrcweir OSL_ASSERT( meCurrState != ACTIVE );
512cdf0e10cSrcweir if (inStateOrTransition( ACTIVE ))
513cdf0e10cSrcweir return true;
514cdf0e10cSrcweir
515cdf0e10cSrcweir StateTransition st(this);
516cdf0e10cSrcweir if (st.enter( ACTIVE )) {
517cdf0e10cSrcweir
518cdf0e10cSrcweir activate_st(); // calling derived class
519cdf0e10cSrcweir
520cdf0e10cSrcweir st.commit(); // changing state
521cdf0e10cSrcweir
522cdf0e10cSrcweir maContext.mrEventMultiplexer.notifyAnimationStart( mpSelf );
523cdf0e10cSrcweir
524cdf0e10cSrcweir return true;
525cdf0e10cSrcweir }
526cdf0e10cSrcweir
527cdf0e10cSrcweir return false;
528cdf0e10cSrcweir }
529cdf0e10cSrcweir
activate_st()530cdf0e10cSrcweir void BaseNode::activate_st()
531cdf0e10cSrcweir {
532cdf0e10cSrcweir scheduleDeactivationEvent();
533cdf0e10cSrcweir }
534cdf0e10cSrcweir
scheduleDeactivationEvent(EventSharedPtr const & pEvent)535cdf0e10cSrcweir void BaseNode::scheduleDeactivationEvent( EventSharedPtr const& pEvent )
536cdf0e10cSrcweir {
537cdf0e10cSrcweir if (mpCurrentEvent) {
538cdf0e10cSrcweir mpCurrentEvent->dispose();
539cdf0e10cSrcweir mpCurrentEvent.reset();
540cdf0e10cSrcweir }
541cdf0e10cSrcweir if (pEvent) {
542cdf0e10cSrcweir if (maContext.mrEventQueue.addEvent( pEvent ))
543cdf0e10cSrcweir mpCurrentEvent = pEvent;
544cdf0e10cSrcweir }
545cdf0e10cSrcweir else {
546cdf0e10cSrcweir // This method need not take the
547cdf0e10cSrcweir // NodeContext::mnStartDelay value into account,
548cdf0e10cSrcweir // because the deactivation event is only scheduled
549cdf0e10cSrcweir // when the effect is started: the timeout is then
550cdf0e10cSrcweir // already respected.
551cdf0e10cSrcweir
552cdf0e10cSrcweir // xxx todo:
553cdf0e10cSrcweir // think about set node, anim base node!
554cdf0e10cSrcweir // if anim base node has no activity, this is called to schedule deactivatiion,
555cdf0e10cSrcweir // but what if it does not schedule anything?
556cdf0e10cSrcweir
557cdf0e10cSrcweir // TODO(F2): Handle end time attribute, too
558cdf0e10cSrcweir mpCurrentEvent = generateEvent(
559cdf0e10cSrcweir mxAnimationNode->getDuration(),
560cdf0e10cSrcweir boost::bind( &AnimationNode::deactivate, mpSelf ),
561cdf0e10cSrcweir maContext, 0.0 );
562cdf0e10cSrcweir }
563cdf0e10cSrcweir }
564cdf0e10cSrcweir
deactivate()565cdf0e10cSrcweir void BaseNode::deactivate()
566cdf0e10cSrcweir {
567cdf0e10cSrcweir if (inStateOrTransition( ENDED | FROZEN ) || !checkValidNode())
568cdf0e10cSrcweir return;
569cdf0e10cSrcweir
570cdf0e10cSrcweir if (isTransition( meCurrState, FROZEN, false /* no OSL_ASSERT */ )) {
571cdf0e10cSrcweir // do transition to FROZEN:
572cdf0e10cSrcweir StateTransition st(this);
573cdf0e10cSrcweir if (st.enter( FROZEN, StateTransition::FORCE )) {
574cdf0e10cSrcweir
575cdf0e10cSrcweir deactivate_st( FROZEN );
576cdf0e10cSrcweir st.commit();
577cdf0e10cSrcweir
578cdf0e10cSrcweir notifyEndListeners();
579cdf0e10cSrcweir
580cdf0e10cSrcweir // discharge a loaded event, before going on:
581cdf0e10cSrcweir if (mpCurrentEvent) {
582cdf0e10cSrcweir mpCurrentEvent->dispose();
583cdf0e10cSrcweir mpCurrentEvent.reset();
584cdf0e10cSrcweir }
585cdf0e10cSrcweir }
586cdf0e10cSrcweir }
587cdf0e10cSrcweir else {
588cdf0e10cSrcweir // use end instead:
589cdf0e10cSrcweir end();
590cdf0e10cSrcweir }
591cdf0e10cSrcweir // state has changed either to FROZEN or ENDED
592cdf0e10cSrcweir }
593cdf0e10cSrcweir
deactivate_st(NodeState)594cdf0e10cSrcweir void BaseNode::deactivate_st( NodeState )
595cdf0e10cSrcweir {
596cdf0e10cSrcweir }
597cdf0e10cSrcweir
end()598cdf0e10cSrcweir void BaseNode::end()
599cdf0e10cSrcweir {
600cdf0e10cSrcweir bool const bIsFrozenOrInTransitionToFrozen = inStateOrTransition( FROZEN );
601cdf0e10cSrcweir if (inStateOrTransition( ENDED ) || !checkValidNode())
602cdf0e10cSrcweir return;
603cdf0e10cSrcweir
604cdf0e10cSrcweir // END must always be reachable. If not, that's an error in the
605cdf0e10cSrcweir // transition tables
606cdf0e10cSrcweir OSL_ENSURE( isTransition( meCurrState, ENDED ),
607cdf0e10cSrcweir "end state not reachable in transition table" );
608cdf0e10cSrcweir
609cdf0e10cSrcweir StateTransition st(this);
610cdf0e10cSrcweir if (st.enter( ENDED, StateTransition::FORCE )) {
611cdf0e10cSrcweir
612cdf0e10cSrcweir deactivate_st( ENDED );
613cdf0e10cSrcweir st.commit(); // changing state
614cdf0e10cSrcweir
615cdf0e10cSrcweir // if is FROZEN or is to be FROZEN, then
616cdf0e10cSrcweir // will/already notified deactivating listeners
617cdf0e10cSrcweir if (!bIsFrozenOrInTransitionToFrozen)
618cdf0e10cSrcweir notifyEndListeners();
619cdf0e10cSrcweir
620cdf0e10cSrcweir // discharge a loaded event, before going on:
621cdf0e10cSrcweir if (mpCurrentEvent) {
622cdf0e10cSrcweir mpCurrentEvent->dispose();
623cdf0e10cSrcweir mpCurrentEvent.reset();
624cdf0e10cSrcweir }
625cdf0e10cSrcweir }
626cdf0e10cSrcweir }
627cdf0e10cSrcweir
notifyDeactivating(const AnimationNodeSharedPtr & rNotifier)628cdf0e10cSrcweir void BaseNode::notifyDeactivating( const AnimationNodeSharedPtr& rNotifier )
629cdf0e10cSrcweir {
630cdf0e10cSrcweir (void) rNotifier; // avoid warning
631cdf0e10cSrcweir OSL_ASSERT( rNotifier->getState() == FROZEN ||
632cdf0e10cSrcweir rNotifier->getState() == ENDED );
633cdf0e10cSrcweir // TODO(F1): for end sync functionality, this might indeed be used some day
634cdf0e10cSrcweir }
635cdf0e10cSrcweir
notifyEndListeners() const636cdf0e10cSrcweir void BaseNode::notifyEndListeners() const
637cdf0e10cSrcweir {
638cdf0e10cSrcweir // notify all listeners
639cdf0e10cSrcweir std::for_each( maDeactivatingListeners.begin(),
640cdf0e10cSrcweir maDeactivatingListeners.end(),
641cdf0e10cSrcweir boost::bind( &AnimationNode::notifyDeactivating, _1,
642cdf0e10cSrcweir boost::cref(mpSelf) ) );
643cdf0e10cSrcweir
644cdf0e10cSrcweir // notify state change
645cdf0e10cSrcweir maContext.mrEventMultiplexer.notifyAnimationEnd( mpSelf );
646cdf0e10cSrcweir
647cdf0e10cSrcweir // notify main sequence end (iff we're the main
648cdf0e10cSrcweir // sequence root node). This is because the main
649cdf0e10cSrcweir // sequence determines the active duration of the
650cdf0e10cSrcweir // slide. All other sequences are secondary, in that
651cdf0e10cSrcweir // they don't prevent a slide change from happening,
652cdf0e10cSrcweir // even if they have not been completed. In other
653cdf0e10cSrcweir // words, all sequences except the main sequence are
654cdf0e10cSrcweir // optional for the slide lifetime.
655cdf0e10cSrcweir if (isMainSequenceRootNode())
656cdf0e10cSrcweir maContext.mrEventMultiplexer.notifySlideAnimationsEnd();
657cdf0e10cSrcweir }
658cdf0e10cSrcweir
getState() const659cdf0e10cSrcweir AnimationNode::NodeState BaseNode::getState() const
660cdf0e10cSrcweir {
661cdf0e10cSrcweir return meCurrState;
662cdf0e10cSrcweir }
663cdf0e10cSrcweir
registerDeactivatingListener(const AnimationNodeSharedPtr & rNotifee)664cdf0e10cSrcweir bool BaseNode::registerDeactivatingListener(
665cdf0e10cSrcweir const AnimationNodeSharedPtr& rNotifee )
666cdf0e10cSrcweir {
667cdf0e10cSrcweir if (! checkValidNode())
668cdf0e10cSrcweir return false;
669cdf0e10cSrcweir
670cdf0e10cSrcweir ENSURE_OR_RETURN_FALSE(
671cdf0e10cSrcweir rNotifee,
672cdf0e10cSrcweir "BaseNode::registerDeactivatingListener(): invalid notifee" );
673cdf0e10cSrcweir maDeactivatingListeners.push_back( rNotifee );
674cdf0e10cSrcweir
675cdf0e10cSrcweir return true;
676cdf0e10cSrcweir }
677cdf0e10cSrcweir
setSelf(const BaseNodeSharedPtr & rSelf)678cdf0e10cSrcweir void BaseNode::setSelf( const BaseNodeSharedPtr& rSelf )
679cdf0e10cSrcweir {
680cdf0e10cSrcweir ENSURE_OR_THROW( rSelf.get() == this,
681cdf0e10cSrcweir "BaseNode::setSelf(): got ptr to different object" );
682cdf0e10cSrcweir ENSURE_OR_THROW( !mpSelf,
683cdf0e10cSrcweir "BaseNode::setSelf(): called multiple times" );
684cdf0e10cSrcweir
685cdf0e10cSrcweir mpSelf = rSelf;
686cdf0e10cSrcweir }
687cdf0e10cSrcweir
688cdf0e10cSrcweir // Debug
689cdf0e10cSrcweir //=========================================================================
690cdf0e10cSrcweir
691cdf0e10cSrcweir #if defined(VERBOSE) && defined(DBG_UTIL)
showState() const692cdf0e10cSrcweir void BaseNode::showState() const
693cdf0e10cSrcweir {
694cdf0e10cSrcweir const AnimationNode::NodeState eNodeState( getState() );
695cdf0e10cSrcweir
696cdf0e10cSrcweir if( eNodeState == AnimationNode::INVALID )
697cdf0e10cSrcweir VERBOSE_TRACE( "Node state: n0x%X [label=\"%s\",style=filled,"
698cdf0e10cSrcweir "fillcolor=\"0.5,0.2,0.5\"]",
699cdf0e10cSrcweir (const char*)this+debugGetCurrentOffset(),
700cdf0e10cSrcweir getDescription() );
701cdf0e10cSrcweir else
702cdf0e10cSrcweir VERBOSE_TRACE( "Node state: n0x%X [label=\"%s\",style=filled,"
703cdf0e10cSrcweir "fillcolor=\"%f,1.0,1.0\"]",
704cdf0e10cSrcweir (const char*)this+debugGetCurrentOffset(),
705cdf0e10cSrcweir getDescription(),
706cdf0e10cSrcweir log(double(getState()))/4.0 );
707cdf0e10cSrcweir
708cdf0e10cSrcweir // determine additional node information
709cdf0e10cSrcweir uno::Reference<animations::XAnimate> const xAnimate( mxAnimationNode,
710cdf0e10cSrcweir uno::UNO_QUERY );
711cdf0e10cSrcweir if( xAnimate.is() )
712cdf0e10cSrcweir {
713cdf0e10cSrcweir uno::Reference< drawing::XShape > xTargetShape( xAnimate->getTarget(),
714cdf0e10cSrcweir uno::UNO_QUERY );
715cdf0e10cSrcweir
716cdf0e10cSrcweir if( !xTargetShape.is() )
717cdf0e10cSrcweir {
718cdf0e10cSrcweir ::com::sun::star::presentation::ParagraphTarget aTarget;
719cdf0e10cSrcweir
720cdf0e10cSrcweir // no shape provided. Maybe a ParagraphTarget?
721cdf0e10cSrcweir if( (xAnimate->getTarget() >>= aTarget) )
722cdf0e10cSrcweir xTargetShape = aTarget.Shape;
723cdf0e10cSrcweir }
724cdf0e10cSrcweir
725cdf0e10cSrcweir if( xTargetShape.is() )
726cdf0e10cSrcweir {
727cdf0e10cSrcweir uno::Reference< beans::XPropertySet > xPropSet( xTargetShape,
728cdf0e10cSrcweir uno::UNO_QUERY );
729cdf0e10cSrcweir
730cdf0e10cSrcweir // read shape name
731cdf0e10cSrcweir ::rtl::OUString aName;
732cdf0e10cSrcweir if( (xPropSet->getPropertyValue(
733cdf0e10cSrcweir ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Name") ) )
734cdf0e10cSrcweir >>= aName) )
735cdf0e10cSrcweir {
736cdf0e10cSrcweir const ::rtl::OString& rAsciiName(
737cdf0e10cSrcweir ::rtl::OUStringToOString( aName,
738cdf0e10cSrcweir RTL_TEXTENCODING_ASCII_US ) );
739cdf0e10cSrcweir
740cdf0e10cSrcweir VERBOSE_TRACE( "Node info: n0x%X, name \"%s\"",
741cdf0e10cSrcweir (const char*)this+debugGetCurrentOffset(),
742cdf0e10cSrcweir rAsciiName.getStr() );
743cdf0e10cSrcweir }
744cdf0e10cSrcweir }
745cdf0e10cSrcweir }
746cdf0e10cSrcweir }
747cdf0e10cSrcweir
getDescription() const748cdf0e10cSrcweir const char* BaseNode::getDescription() const
749cdf0e10cSrcweir {
750cdf0e10cSrcweir return "BaseNode";
751cdf0e10cSrcweir }
752cdf0e10cSrcweir
showTreeFromWithin() const753cdf0e10cSrcweir void BaseNode::showTreeFromWithin() const
754cdf0e10cSrcweir {
755cdf0e10cSrcweir // find root node
756cdf0e10cSrcweir BaseNodeSharedPtr pCurrNode( mpSelf );
757cdf0e10cSrcweir while( pCurrNode->mpParent ) pCurrNode = pCurrNode->mpParent;
758cdf0e10cSrcweir
759cdf0e10cSrcweir pCurrNode->showState();
760cdf0e10cSrcweir }
761cdf0e10cSrcweir #endif
762cdf0e10cSrcweir
763cdf0e10cSrcweir } // namespace internal
764cdf0e10cSrcweir } // namespace slideshow
765cdf0e10cSrcweir
766