/**************************************************************************** * libwiigui * * Tantric 2009 * * gui_element.cpp * * GUI class definitions ***************************************************************************/ #include "gui.h" /** * Constructor for the Object class. */ //mutex_t GuiElement::mutex = LWP_MUTEX_NULL; mutex_t GuiElement::_lock_mutex = LWP_MUTEX_NULL; GuiElement::GuiElement() { xoffset = 0; yoffset = 0; zoffset = 0; xmin = 0; xmax = 0; ymin = 0; ymax = 0; width = 0; height = 0; alpha = 255; scale = 1; state = STATE_DEFAULT; stateChan = -1; trigger[0] = NULL; trigger[1] = NULL; trigger[2] = NULL; trigger[3] = NULL; trigger[4] = NULL; trigger[5] = NULL; parentElement = NULL; rumble = true; selectable = false; clickable = false; holdable = false; visible = true; focus = -1; // cannot be focused updateCB = NULL; yoffsetDyn = 0; xoffsetDyn = 0; yoffsetDynFloat = 0; alphaDyn = -1; scaleDyn = 1; effects = 0; effectAmount = 0; effectTarget = 0; effectsOver = 0; effectAmountOver = 0; effectTargetOver = 0; frequency = 0.0f; changervar = 0; degree = -90.0f; circleamount = 360.0f; Radius = 150; angleDyn = 0.0f; anglespeed = 0.0f; // default alignment - align to top left alignmentVert = ALIGN_TOP; alignmentHor = ALIGN_LEFT; // if(mutex == LWP_MUTEX_NULL) LWP_MutexInit(&mutex, true); if ( _lock_mutex == LWP_MUTEX_NULL ) LWP_MutexInit( &_lock_mutex, true ); _lock_thread = LWP_THREAD_NULL; _lock_count = 0; _lock_queue = LWP_TQUEUE_NULL; } /** * Destructor for the GuiElement class. */ GuiElement::~GuiElement() { // LWP_MutexDestroy(mutex); } void GuiElement::SetParent( GuiElement * e ) { LOCK( this ); parentElement = e; } GuiElement * GuiElement::GetParent() { return parentElement; } /** * Get the left position of the GuiElement. * @see SetLeft() * @return Left position in pixel. */ int GuiElement::GetLeft() { int x = 0; int pWidth = 0; int pLeft = 0; if ( parentElement ) { pWidth = parentElement->GetWidth(); pLeft = parentElement->GetLeft(); } if ( effects & ( EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT | EFFECT_GOROUND | EFFECT_ROCK_VERTICLE ) ) pLeft += xoffsetDyn; switch ( alignmentHor ) { case ALIGN_LEFT: x = pLeft; break; case ALIGN_CENTRE: x = pLeft + ( pWidth / 2 ) - ( width / 2 ); break; case ALIGN_RIGHT: x = pLeft + pWidth - width; break; } return x + xoffset; } /** * Get the top position of the GuiElement. * @see SetTop() * @return Top position in pixel. */ int GuiElement::GetTop() { int y = 0; int pHeight = 0; int pTop = 0; if ( parentElement ) { pHeight = parentElement->GetHeight(); pTop = parentElement->GetTop(); } if ( effects & ( EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT | EFFECT_GOROUND | EFFECT_ROCK_VERTICLE ) ) pTop += yoffsetDyn; switch ( alignmentVert ) { case ALIGN_TOP: y = pTop; break; case ALIGN_MIDDLE: y = pTop + ( pHeight / 2 ) - ( height / 2 ); break; case ALIGN_BOTTOM: y = pTop + pHeight - height; break; } return y + yoffset; } void GuiElement::SetMinX( int x ) { LOCK( this ); xmin = x; } int GuiElement::GetMinX() { return xmin; } void GuiElement::SetMaxX( int x ) { LOCK( this ); xmax = x; } int GuiElement::GetMaxX() { return xmax; } void GuiElement::SetMinY( int y ) { LOCK( this ); ymin = y; } int GuiElement::GetMinY() { return ymin; } void GuiElement::SetMaxY( int y ) { LOCK( this ); ymax = y; } int GuiElement::GetMaxY() { return ymax; } /** * Get the width of the GuiElement. * @see SetWidth() * @return Width of the GuiElement. */ int GuiElement::GetWidth() { return width; } /** * Get the height of the GuiElement. * @see SetHeight() * @return Height of the GuiElement. */ int GuiElement::GetHeight() { return height; } /** * Set the width and height of the GuiElement. * @param[in] Width Width in pixel. * @param[in] Height Height in pixel. * @see SetWidth() * @see SetHeight() */ void GuiElement::SetSize( int w, int h ) { LOCK( this ); width = w; height = h; } /** * Get visible. * @see SetVisible() * @return true if visible, false otherwise. */ bool GuiElement::IsVisible() { return visible; } /** * Set visible. * @param[in] Visible Set to true to show GuiElement. * @see IsVisible() */ void GuiElement::SetVisible( bool v ) { LOCK( this ); visible = v; } void GuiElement::SetAlpha( int a ) { LOCK( this ); alpha = a; } int GuiElement::GetAlpha() { int a; if ( alphaDyn >= 0 ) a = alphaDyn; else a = alpha; if ( parentElement ) a *= parentElement->GetAlpha() / 255.0; return a; } float GuiElement::GetAngleDyn() { float a = 0.0; if ( angleDyn ) a = angleDyn; if ( parentElement && !angleDyn ) a = parentElement->GetAngleDyn(); return a; } void GuiElement::SetScale( float s ) { LOCK( this ); scale = s; } float GuiElement::GetScale() { float s = scale * scaleDyn; if ( parentElement ) s *= parentElement->GetScale(); return s; } int GuiElement::GetState() { return state; } int GuiElement::GetStateChan() { return stateChan; } void GuiElement::SetState( int s, int c ) { LOCK( this ); state = s; stateChan = c; } void GuiElement::ResetState() { LOCK( this ); if ( state != STATE_DISABLED ) { state = STATE_DEFAULT; stateChan = -1; } } void GuiElement::SetClickable( bool c ) { LOCK( this ); clickable = c; } void GuiElement::SetSelectable( bool s ) { LOCK( this ); selectable = s; } void GuiElement::SetHoldable( bool d ) { LOCK( this ); holdable = d; } bool GuiElement::IsSelectable() { if ( state == STATE_DISABLED || state == STATE_CLICKED ) return false; else return selectable; } bool GuiElement::IsClickable() { if ( state == STATE_DISABLED || state == STATE_CLICKED || state == STATE_HELD ) return false; else return clickable; } bool GuiElement::IsHoldable() { if ( state == STATE_DISABLED ) return false; else return holdable; } void GuiElement::SetFocus( int f ) { LOCK( this ); focus = f; } int GuiElement::IsFocused() { return focus; } void GuiElement::SetTrigger( GuiTrigger * t ) { LOCK( this ); if ( !trigger[0] ) trigger[0] = t; else if ( !trigger[1] ) trigger[1] = t; else if ( !trigger[2] ) trigger[2] = t; else if ( !trigger[3] ) trigger[3] = t; else if ( !trigger[4] ) trigger[4] = t; else if ( !trigger[5] ) trigger[5] = t; else // both were assigned, so we'll just overwrite the first one trigger[0] = t; } void GuiElement::SetTrigger( u8 i, GuiTrigger * t ) { LOCK( this ); trigger[i] = t; } void GuiElement::RemoveTrigger( u8 i ) { LOCK( this ); trigger[i] = NULL; } bool GuiElement::Rumble() { return rumble; } void GuiElement::SetRumble( bool r ) { LOCK( this ); rumble = r; } int GuiElement::GetEffect() { LOCK( this ); return effects; } int GuiElement::GetEffectOnOver() { LOCK( this ); return effectsOver; } float GuiElement::GetFrequency() { LOCK( this ); return frequency; } void GuiElement::SetEffect( int eff, int speed, f32 circles, int r, f32 startdegree, f32 anglespeedset, int center_x, int center_y ) { if ( eff & EFFECT_GOROUND ) { xoffsetDyn = 0; //!position of circle in x yoffsetDyn = 0; //!position of circle in y Radius = r; //!radius of the circle degree = startdegree; //!for example -90 (°) to start at top of circle circleamount = circles; //!circleamoutn in degrees for example 360 for 1 circle angleDyn = 0.0f; //!this is used by the code to calc the angle anglespeed = anglespeedset; //!This is anglespeed depending on circle speed 1 is same speed and 0.5 half speed temp_xoffset = center_x; //!position of center in x temp_yoffset = center_y; //!position of center in y } effects |= eff; effectAmount = speed; //!Circlespeed } void GuiElement::SetEffect( int eff, int amount, int target ) { LOCK( this ); if ( eff & EFFECT_SLIDE_IN ) { // these calculations overcompensate a little if ( eff & EFFECT_SLIDE_TOP ) yoffsetDyn = -screenheight; else if ( eff & EFFECT_SLIDE_LEFT ) xoffsetDyn = -screenwidth; else if ( eff & EFFECT_SLIDE_BOTTOM ) yoffsetDyn = screenheight; else if ( eff & EFFECT_SLIDE_RIGHT ) xoffsetDyn = screenwidth; } if ( eff & EFFECT_FADE && amount > 0 ) { alphaDyn = 0; } else if ( eff & EFFECT_FADE && amount < 0 ) { alphaDyn = alpha; } else if ( eff & EFFECT_ROCK_VERTICLE ) { changervar = 0; yoffsetDyn = 0; yoffsetDynFloat = 0.0; } effects |= eff; effectAmount = amount; effectTarget = target; } void GuiElement::SetEffectOnOver( int eff, int amount, int target ) { LOCK( this ); effectsOver |= eff; effectAmountOver = amount; effectTargetOver = target; } void GuiElement::SetEffectGrow() { SetEffectOnOver( EFFECT_SCALE, 4, 110 ); } void GuiElement::StopEffect() { xoffsetDyn = 0; yoffsetDyn = 0; effects = 0; effectsOver = 0; effectAmount = 0; effectAmountOver = 0; effectTarget = 0; effectTargetOver = 0; scaleDyn = 1; frequency = 0.0f; changervar = 0; //angleDyn = 0.0f; anglespeed = 0.0f; } void GuiElement::UpdateEffects() { LOCK( this ); if ( effects & ( EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT | EFFECT_GOROUND ) ) { if ( effects & EFFECT_SLIDE_IN ) { if ( effects & EFFECT_SLIDE_LEFT ) { xoffsetDyn += effectAmount; if ( xoffsetDyn >= 0 ) { xoffsetDyn = 0; effects = 0; } } else if ( effects & EFFECT_SLIDE_RIGHT ) { xoffsetDyn -= effectAmount; if ( xoffsetDyn <= 0 ) { xoffsetDyn = 0; effects = 0; } } else if ( effects & EFFECT_SLIDE_TOP ) { yoffsetDyn += effectAmount; if ( yoffsetDyn >= 0 ) { yoffsetDyn = 0; effects = 0; } } else if ( effects & EFFECT_SLIDE_BOTTOM ) { yoffsetDyn -= effectAmount; if ( yoffsetDyn <= 0 ) { yoffsetDyn = 0; effects = 0; } } } else { if ( effects & EFFECT_SLIDE_LEFT ) { xoffsetDyn -= effectAmount; if ( xoffsetDyn <= -screenwidth ) effects = 0; // shut off effect } else if ( effects & EFFECT_SLIDE_RIGHT ) { xoffsetDyn += effectAmount; if ( xoffsetDyn >= screenwidth ) effects = 0; // shut off effect } else if ( effects & EFFECT_SLIDE_TOP ) { yoffsetDyn -= effectAmount; if ( yoffsetDyn <= -screenheight ) effects = 0; // shut off effect } else if ( effects & EFFECT_SLIDE_BOTTOM ) { yoffsetDyn += effectAmount; if ( yoffsetDyn >= screenheight ) effects = 0; // shut off effect } } } if ( effects & EFFECT_GOROUND ) { //!< check out gui.h for info xoffset = temp_xoffset; yoffset = temp_yoffset; if ( fabs( frequency ) < circleamount ) { angleDyn = ( frequency + degree + 90.0f ) * anglespeed; xoffsetDyn = ( int ) lround( ( ( f32 ) Radius ) * cos( ( frequency + degree ) * PI / 180.0f ) ); yoffsetDyn = ( int ) lround( ( ( f32 ) Radius ) * sin( ( frequency + degree ) * PI / 180.0f ) ); frequency += ( ( f32 ) effectAmount ) * 0.01f; } else { f32 temp_frequency = ( ( effectAmount < 0 ) ? -1.0f : 1.0f ) * circleamount; angleDyn = ( temp_frequency + degree + 90.0f ) * anglespeed; xoffsetDyn = ( int ) lround( ( ( f32 ) Radius ) * cos( ( temp_frequency + degree ) * PI / 180.0f ) ); yoffsetDyn = ( int ) lround( ( ( f32 ) Radius ) * sin( ( temp_frequency + degree ) * PI / 180.0f ) ); xoffset += xoffsetDyn; yoffset += yoffsetDyn; effects ^= EFFECT_GOROUND; frequency = 0.0f; } } if ( effects & EFFECT_ROCK_VERTICLE ) { //move up to 10pixel above 0 if ( changervar == 0 && yoffsetDynFloat < 11.0 ) { yoffsetDynFloat += ( effectAmount * 0.01 ); } else if ( yoffsetDynFloat > 10.0 ) { changervar = 1; } //move down till 10pixel under 0 if ( changervar == 1 && yoffsetDynFloat > -11.0 ) { yoffsetDynFloat -= ( effectAmount * 0.01 ); } else if ( yoffsetDynFloat < -10.0 ) { changervar = 0; } yoffsetDyn = ( int )( yoffsetDynFloat ); } if ( effects & EFFECT_FADE ) { alphaDyn += effectAmount; if ( effectAmount < 0 && alphaDyn <= 0 ) { alphaDyn = 0; effects = 0; // shut off effect } else if ( effectAmount > 0 && alphaDyn >= alpha ) { alphaDyn = alpha; effects = 0; // shut off effect } } if ( effects & EFFECT_SCALE ) { scaleDyn += effectAmount / 100.0; if ( ( effectAmount < 0 && scaleDyn <= effectTarget / 100.0 ) || ( effectAmount > 0 && scaleDyn >= effectTarget / 100.0 ) ) { scaleDyn = effectTarget / 100.0; effects = 0; // shut off effect } } if ( effects & EFFECT_PULSE ) { int percent = 10; //go down from target by this if ( ( scaleDyn <= ( effectTarget*0.01 ) ) && ( !changervar ) ) { scaleDyn += ( effectAmount * 0.001 ); } else if ( scaleDyn > ( effectTarget*0.01 ) ) { changervar = 1; } if ( ( scaleDyn >= ( ( effectTarget - percent )*0.01 ) ) && ( changervar ) ) { scaleDyn -= ( effectAmount * 0.001 ); } else if ( scaleDyn < ( ( effectTarget - percent )*0.01 ) ) { changervar = 0; } } } void GuiElement::Update( GuiTrigger * t ) { LOCK( this ); if ( updateCB ) updateCB( this ); } void GuiElement::SetUpdateCallback( UpdateCallback u ) { LOCK( this ); updateCB = u; } void GuiElement::SetPosition( int xoff, int yoff, int zoff ) { LOCK( this ); xoffset = xoff; yoffset = yoff; zoffset = zoff; } void GuiElement::SetAlignment( int hor, int vert ) { LOCK( this ); alignmentHor = hor; alignmentVert = vert; } int GuiElement::GetSelected() { return -1; } /** * Draw an element on screen. */ void GuiElement::Draw() { } /** * Draw Tooltips on screen. */ void GuiElement::DrawTooltip() { } /** * Check if a position is inside the GuiElement. * @param[in] x X position in pixel. * @param[in] y Y position in pixel. */ bool GuiElement::IsInside( int x, int y ) { if ( x > this->GetLeft() && x < ( this->GetLeft() + width ) && y > this->GetTop() && y < ( this->GetTop() + height ) ) return true; return false; } void GuiElement::Lock() { // LWP_MutexLock(mutex); for ( ;; ) // loop while element is locked by self { LWP_MutexLock( _lock_mutex ); if ( _lock_thread == LWP_THREAD_NULL ) // element is not locked { _lock_thread = LWP_GetSelf(); // mark as locked _lock_count = 1; // set count of lock to 1 LWP_MutexUnlock( _lock_mutex ); return; } else if ( _lock_thread == LWP_GetSelf() ) // thread is locked by my self { _lock_count++; // inc count of locks; LWP_MutexUnlock( _lock_mutex ); return; } else // otherwise the element is locked by an other thread { if ( _lock_queue == LWP_TQUEUE_NULL ) // no queue - meens it is the first access to the locked element LWP_InitQueue( &_lock_queue ); // init queue LWP_MutexUnlock( _lock_mutex ); LWP_ThreadSleep( _lock_queue ); // and sleep // try lock again; } } } void GuiElement::Unlock() { // LWP_MutexUnlock(mutex); LWP_MutexLock( _lock_mutex ); // only the thread was locked this element, can call unlock if ( _lock_thread == LWP_GetSelf() ) // but we check it here – safe is safe { if ( --_lock_count == 0 ) // dec count of locks and check if it last lock; { _lock_thread = LWP_THREAD_NULL; // mark as unlocked if ( _lock_queue != LWP_TQUEUE_NULL ) // has a queue { LWP_CloseQueue( _lock_queue ); // close the queue and wake all waited threads _lock_queue = LWP_TQUEUE_NULL; } } } LWP_MutexUnlock( _lock_mutex ); } SimpleLock::SimpleLock( GuiElement *e ) : element( e ) { element->Lock(); } SimpleLock::~SimpleLock() { element->Unlock(); }