Ejemplo n.º 1
0
void
Timeline::TeardownClock ()
{
	if (clock) {
		DetachCompletedHandler ();
		Clock *c = clock;
		ClockGroup *group = c->GetParentClock();
		if (group)
			group->RemoveChild (c);
		clock = NULL;
		c->unref ();
	}
}
Ejemplo n.º 2
0
bool
Clock::UpdateFromParentTime (TimeSpan parentTime)
{
#define CLAMP_NORMALIZED_TIME do {			\
	if (normalizedTime < 0.0) normalizedTime = 0.0; \
	if (normalizedTime > 1.0) normalizedTime = 1.0; \
} while (0)

	//
	// The idea behind this method is that it is possible (and
	// easier, and clearer) to apply a simple function to our
	// parent clock's time to calculate our own time.
	//
	// We also calculate our progress (MS uses the term
	// "normalized time"), a value in the range [0-1] at the same
	// time.
	//
	// This clock's localTime runs from the range
	// [0-natural_duration] for natural_durations with timespans,
	// and [0-$forever] for Forever durations.  Automatic
	// durations are translated into timespans.

	if (timeline == NULL) {
		// printf ("timeline is null!?  f**k yeah! (clock = %s)\n", name);
		Stop ();
		return false;
	}
		
	if (!GetHasStarted() && !GetWasStopped() && (GetBeginOnTick() || timeline->GetBeginTime () <= parentTime)) {
		if (GetBeginOnTick())
			BeginOnTick (false);
		Begin (parentTime);
	}

	// root_parent_time is the time we were added to our parent clock.
	// timeline->GetBeginTime() is expressed in the time-space of the parent clock.
	//
	// subtracting those two translates our start time to 0
	//
	// we then have to account for our accumulated pause time, and
	// scale the whole thing by our speed ratio.
	//
	// the result is a timespan unaffected by repeatbehavior or
	// autoreverse.  it is simple the timespan our clock has been
	// running.
	TimeSpan localTime = (parentTime - root_parent_time - timeline->GetBeginTime() - accumulated_pause_time) * timeline->GetSpeedRatio();

	bool seek_completed = false;

	if (is_seeking) {
		// if we're seeking, we need to arrange for the above
		// localTime formula to keep time correctly.  we clear
		// accumulated_pause_time, and adjust root_parent_time
		// such that we can re-evaluate localTime and have
		// localTime = seek_time.

		begin_pause_time = 0;
		accumulated_pause_time = 0;

		/* seek_time = localTime

		   seek_time = (parentTime - root_parent_time - timeline->BeginTime() - 0) * timeline->GetSpeedRatio ()

		          seek_time
  		   ------------------------- = parentTime - root_parent_time - timeline->BeginTime();
		   timeline->GetSpeedRatio()
                                                                                  seek_time         
		   root_parent_time = parentTime - timeline->BeginTime() - -------------------------
									   timeline->GetSpeedRatio()
		*/
		root_parent_time = parentTime - (timeline->GetBeginTime () - seek_time) / timeline->GetSpeedRatio ();
		localTime = (seek_time - timeline->GetBeginTime()) * timeline->GetSpeedRatio();
		is_seeking = false;
		seek_completed = true;

		if (!GetHasStarted())
			CalculateFillTime ();
	}
	else if (is_paused) {
		// if we're paused and not seeking, we don't update
		// anything.
		return false;
	}

	// the clock doesn't update and we don't progress if the
	// translated local time is before our begin time.  Keep in
	// mind that this can happen *after* a clock has started,
	// since parentTime isn't strictly increasing.  It can
	// decrease and represent a time before our start time.
	if (localTime < 0)
		return true;

	if (GetClockState () == Clock::Stopped) {
		if (!seek_completed)
			return false;

		// even for stopped clocks we update their position if they're seeked.
	}

	double normalizedTime = 0.0;


	// we only do the bulk of the work if the duration has a
	// timespan.  if we're automatic/forever, our normalizedTime
	// stays pegged at 0.0, and our localTime progresses
	// undisturbed.  i.e. a RepeatBehavior="2x" means nothing if
	// the Duration of the animation is forever.
	if (GetNaturalDuration().HasTimeSpan()) {
		TimeSpan natural_duration_timespan = GetNaturalDuration().GetTimeSpan();
		
		if (natural_duration_timespan <= 0) {
			// for clocks with instantaneous begin times/durations, expressable like so:
			//     <DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" BeginTime="00:00:00" Duration="00:00:00" />
			// we keep our localtime pegged at 0 (FIXME:
			// without filling?) and our normalizedTime at
			// 1.  The latter makes sure that value is applied in full.
 			localTime = 0;
 			normalizedTime = 1.0;
			if (GetClockState () == Clock::Active) {
				FillOnNextTick ();
				Completed ();

				SetCurrentTime (localTime);
				progress = normalizedTime;

				return false;
			}
		}
		else if (natural_duration_timespan > 0) {
			RepeatBehavior *repeat = timeline->GetRepeatBehavior ();

			if (!repeat->IsForever() && localTime >= fillTime) {
				// fillTime represents the local time
				// at which the number of repeats
				// (expressed either as a timespan or
				// e.g. "2x") and autoreverses have
				// completed.  i.e. it's the
				// $natural_duration * $repeat_count
				// for repeat behaviors with counts,
				// and $repeat_duration for repeat
				// behaviors with timespans.

				// if the timeline is auto-reversible,
				// we always end at localTime = 0.
				// Otherwise we know it's fillTime.
				localTime = timeline->GetAutoReverse () ? 0 : fillTime;
				normalizedTime = (double)localTime / natural_duration_timespan;
				CLAMP_NORMALIZED_TIME;
				if (GetClockState () == Clock::Active) {
					FillOnNextTick ();
					Completed ();
				}
				else if ((moonlight_flags & RUNTIME_INIT_USE_IDLE_HINT) && GetClockState () == Clock::Filling) {
					return false;
				}
			}
			else {
				if (GetClockState () != Clock::Active)
					SetClockState (Clock::Active);

				if (localTime > 0) {
					double t = (double)localTime / natural_duration_timespan;
					int ti = (int)t;
					double fract = t - ti;

					// This block of code is the first time where localTime is translated
					// into per-repeat/per-autoreverse segments.  We do it here because it
					// allows us to use a cute hack for determining if we're ascending or
					// descending.
					//
					// for instance:

					// <storyboard duration="00:00:12">
					//   <doubleanimation begintime="00:00:00" repeatbehavior="2x" autoreverse="<below>" duration="00:00:03" />
					// </storyboard>
					//
					//  autoreverse = true                       autoreverse = false
					// 0  / 3 = 0        = 0                  0 / 3 = 0           = 0
					// 1  / 3 = .333     > 0.333              1 / 3 = .333        > 0.333
					// 2  / 3 = .666     > 0.666              2 / 3 = .666        > 0.666
					// 3  / 3 = 1        = 1                  3 / 3 = 1           = 1
					// 4  / 3 = 1.33     < 0.666              4 / 3 = 1.33        > 0.333
					// 5  / 3 = 1.66     < 0.333              5 / 3 = 1.66        > 0.666
					// 6  / 3 = 2        = 0                  6 / 3 = 2           = 1
					// 7  / 3 = 2.33     > 0.333
					// 8  / 3 = 2.66     > 0.666
					// 9  / 3 = 3        = 1
					// 10 / 3 = 3.33     < 0.666
					// 11 / 3 = 3.66     < 0.333
					// 12 / 3 = 4        = 0


					// a little explanation:  the $localtime / $natural_duration = $foo is done
					// to factor out the repeat count.  we know that the time within a given repeated
					// run is just the fractional part of that (if the result has a fraction), or 0 or 1.

					// the >,<,= column above represents whether we're increasing, decreasing, or at an
					// end-point, respectively.

					if (timeline->GetAutoReverse()) {
						// left column above
						if (ti & 1) {
							// e.g:
							// 3  / 3 = 1        = 1    
							// 4  / 3 = 1.33     < 0.666
							// 5  / 3 = 1.66     < 0.333

							// we know we're either at normalized time 1 (at our duration), or we're descending,
							// based on if there's a fractional component.
							if (ti == t) {
								normalizedTime = 1.0;
								localTime = natural_duration_timespan;
							}
							else {
								/* we're descending */
								normalizedTime = 1.0 - fract;
								CLAMP_NORMALIZED_TIME;
								localTime = normalizedTime * natural_duration_timespan;
							}
						}
						else {
							// e.g:
							// 6  / 3 = 2        = 0    
							// 7  / 3 = 2.33     > 0.333
							// 8  / 3 = 2.66     > 0.666

							// we know we're either at normalizd time 0 (at our start time), or we're ascending,
							// based on if there's a fractional component.
							if (ti == t) {
								normalizedTime = 0.0;
								localTime = 0;
							}
							else {
								/* we're ascending */
								normalizedTime = fract;
								CLAMP_NORMALIZED_TIME;
								localTime = normalizedTime * natural_duration_timespan;
							}
						}
					}
					else {
						// e.g.:
						// 0 / 3 = 0           = 0
						// 1 / 3 = .333        > 0.333
						// 2 / 3 = .666        > 0.666
						// 3 / 3 = 1           = 1
						// 4 / 3 = 1.33        > 0.333
						// 5 / 3 = 1.66        > 0.666
						// 6 / 3 = 2           = 1

						// we're always ascending here (since autoreverse is off), and we know we're > 0,
						// so we don't need to concern ourselves with that case.  At the integer points we're
						// at our duration, and otherwise we're at the fractional value.
						if (ti == t) {
							normalizedTime = 1.0;
							localTime = natural_duration_timespan;
						}
						else {
							/* we're ascending */
							normalizedTime = fract;
							CLAMP_NORMALIZED_TIME;
							localTime = normalizedTime * natural_duration_timespan;
						}
					}
				}
			}
		}
	}

	SetCurrentTime (localTime);
	progress = normalizedTime;

	// we check to see if there's a clockgroup in our hierarchy
	// that's Filling.  if there is, we return false here, since
	// we won't be updated beyond our current time anyway.
	if (moonlight_flags & RUNTIME_INIT_USE_IDLE_HINT) {
		Clock *cg = this;
		while ((cg = cg->GetParentClock())) {
			if (cg->GetClockState () == Clock::Active && !((ClockGroup*)cg)->IsTimeManagerClockGroup())
				return true;
		}

		return false;
	}

	return true;
}