#include "cTextElementControl.h" #include #include #include #include // =========================================================================== // cTextElementControl.cpp Version 1.4.2 ©1998-1999 Joakim Braun All rights reserved. // =========================================================================== // // CONTENTS: A framework for constructing LControls of the "date field" kind: // that is, made up of several text elements which may be selected and increased or decreased separately. // Includes code for a clock control, a date control and an SMPTE time code control. // User can click on the text elements to activate them (a focus ring will show) // and use arrow keys or LLittleArrows (automatically created on request) to change control values. // NOTE: Controls may be left or right aligned in relation to its frame in Constructor. // I found this useful since the controls auto-adjust their own size after creation, // based on text element size and text traits. // Date and clock controls set up element order and separator characters according // to information in current 'itl0' resource. // The clock control comes in two flavors: Live (you get a clock tracking the current time, // but can't edit the time shown), or editable (which isn't updated as time passes). // You set the clock type up in Constructor. // Note that these are pure clock controls, not timers. // In upcoming versions there'll be "timer" clocks as well. // HOW TO USE: Add the provided CTYP to your PPOB, register the class you want to use, // then use Constructor to add the control panes. Code should be self-explanatory. // cTextElementControl is free for any and all use. // Do not distribute modified source code under my name. // No support promised, no liability accepted. Provided "as is". // That said, I can be reached at braun@swipnet.se. // Latest version of this and more at http://home4.swipnet.se/~w-41308/ // Change history: // 1.0 December 8, 1998 First release // 1.1 December 9, 1998 Had forgotten to adjust pane height // in cTimeCodeControl::FinishCreateSelf() and cDateControl::FinishCreateSelf() // 1.2 December 10, 1998 Fixed last-minute error in HandleKeyPress() where a ! was misplaced, // resulting in control not reacting to non-cmd key presses. // 1.3 January 10, 1999 Added demo application. // 1.3.1 January 24, 1999 Fixed bug in leap year calculation. // Made InitDateOrder() take a ResIDT parameter for customizability. // 1.4 January 31, 1999 Restructured code by adding a cTimeControl base class for time-related controls. // Added cClockControl. // "SwitchTarget(NULL)" doesn't make the app very responsive. // 1.4.1 February 1, 1999 Rewriting cTextElementLittleArrows wasn't so smart since GA didn't work. // Removed cClockControl::DrawElementSeparator() and fixed base function that didn't erase if it should // 1.4.2 March 20, 1999 Made some slight changes to always use LGALittleArrowsImp, since AM LLittleArrows // for some reason don't work as they should here. This will have to be looked into again. const MessageT msg_ValueIncreased = 'vInc'; const MessageT msg_ValueDecreased = 'vDec'; const ResIDT kCurrentItl0ResNum = 16383; // =========================================================================== // Ä Constructor // =========================================================================== // // Load text traits, set default values cTextElementControl::cTextElementControl(LStream* inStream) : LControl(inStream){ ResIDT textTraitsID; // Read and initialize element text traits *inStream >> textTraitsID; UTextTraits::LoadTextTraits(textTraitsID, mTextTraits); UTextTraits::LoadTextTraits(&mTextTraits); // Size may be zero, but we don't want it to if(mTextTraits.size == 0) mTextTraits.size = 12; // Read custom hilite data *inStream >> mUseCustomHiliting; inStream->ReadData(&mCustomHiliteColor, sizeof(mCustomHiliteColor)); // Read and initialize title text traits *inStream >> textTraitsID; UTextTraits::LoadTextTraits(textTraitsID, mTitleTextTraits); UTextTraits::LoadTextTraits(&mTitleTextTraits); // Size may be zero, but we don't want it to if(mTitleTextTraits.size == 0) mTitleTextTraits.size = 12; // Read title *inStream >> mTitle >> mAlignment; mCurrentElement = eNoCurrentElement; mLittleArrows = NULL; } // =========================================================================== // Ä Destructor // =========================================================================== // // Delete LLittleArrows, if any cTextElementControl::~cTextElementControl(void){ if(mLittleArrows){ mLittleArrows->RemoveListener(this); delete mLittleArrows; } } // =========================================================================== // Ä FinishCreateSelf() // =========================================================================== // // Load font info for calculating text position later on void cTextElementControl::FinishCreateSelf(void){ LControl::FinishCreateSelf(); UTextTraits::SetPortTextTraits(&mTextTraits); ::GetFontInfo(&mFinfo); } // =========================================================================== // Ä CreateLittleArrows() // =========================================================================== // // Call this to create a LLittleArrows to the right of this control. // By default, LLittleArrows are NOT created. void cTextElementControl::CreateLittleArrows(void){ SPaneInfo thePaneInfo; Rect frameRect, arrowsRect; thePaneInfo.paneID = PaneIDT_Unspecified; thePaneInfo.width = 15; // ...say thePaneInfo.height = 26; thePaneInfo.visible = false; thePaneInfo.enabled = false; thePaneInfo.bindings = mFrameBinding; CalcLocalFrameRect(frameRect); thePaneInfo.left = frameRect.right; thePaneInfo.top = frameRect.top; thePaneInfo.userCon = 0; thePaneInfo.superView = mSuperView; // In case LGALittleArrowsImp isn't registered by app RegisterClass_(LGALittleArrowsImp); mLittleArrows = dynamic_cast(new cTextElementLittleArrows(thePaneInfo, msg_ValueIncreased, msg_ValueDecreased)); ThrowIfNil_(mLittleArrows); // Move LLittleArrows to our right flank and center it there mLittleArrows->CalcLocalFrameRect(arrowsRect); mLittleArrows->MoveBy(eElementRectSlopValue, -(((arrowsRect.bottom - arrowsRect.top) - (frameRect.bottom - frameRect.top)) / 2), false); // Start listening to LLittleArrows mLittleArrows->AddListener(this); } // =========================================================================== // Ä AdjustLittleArrows() // =========================================================================== // // If an element is selected, enable LLittleArrows, otherwise disable them. void cTextElementControl::AdjustLittleArrows(void){ if(mLittleArrows){ if(GetCurrentElement() == eNoCurrentElement){ mLittleArrows->Disable(); } else{ mLittleArrows->Enable(); } } } #pragma mark - // =========================================================================== // Ä ObeyCommand() // =========================================================================== // // Allows tab selecting of this control Boolean cTextElementControl::ObeyCommand( CommandT inCommand, void *ioParam){ Boolean result = false; switch(inCommand){ case msg_TabSelect: if (IsEnabled()) result = true; break; default: result = LCommander::ObeyCommand(inCommand, ioParam); } return result; } // =========================================================================== // Ä ListenToMessage() // =========================================================================== // // Listen to messages from LLittleArrows void cTextElementControl::ListenToMessage(MessageT inMessage, void *ioParam){ switch(inMessage){ case msg_ValueIncreased: IncrementCurrentElement(); break; case msg_ValueDecreased: DecrementCurrentElement(); break; case msg_BroadcasterDied: // Don't keep LLittleArrows around if they went away if((LBroadcaster*)ioParam == mLittleArrows) mLittleArrows = NULL; break; } } #pragma mark - // =========================================================================== // Ä DrawSelf() // =========================================================================== // // Draw the control, add or erase a focus ring according to if we're the target or not. void cTextElementControl::DrawSelf(){ FocusDraw(); ApplyForeAndBackColors(); DrawTitle(); UTextTraits::SetPortTextTraits(&mTextTraits); for(Int16 i = 0, max = GetElementCount(); i < max; i++){ DrawElement(i, IsTarget(), true); DrawElementSeparator(i, true); } DrawFocusRing(IsTarget()); } // =========================================================================== // Ä DrawElement() // =========================================================================== // // Draw the element indicated. If inIsTarget is true and inWhichElement is the selected element, // calls DrawElementAsCurrent(), otherwise calls DrawElementAsNonCurrent(). void cTextElementControl::DrawElement(Int16 inWhichElement, Boolean inIsTarget, Boolean inErase){ if(inIsTarget && inWhichElement == GetCurrentElement()) DrawElementAsCurrent(inWhichElement, inErase); else DrawElementAsNonCurrent(inWhichElement, inErase); } // =========================================================================== // Ä RedrawElement() // =========================================================================== // // If an element value changes or the element otherwise needs to be immediately updated, // call this function to avoid redrawing the entire control. // Do not call DrawElement() directly, since it hasn't called FocusDraw() or set the port text traits. // In other words, if control needs to be redrawn, call Refresh(), Draw(nil) or RedrawElement() - nothing else. void cTextElementControl::RedrawElement(Int16 inWhichElement, Boolean inIsTarget){ FocusDraw(); ApplyForeAndBackColors(); UTextTraits::SetPortTextTraits(&mTextTraits); DrawElement(inWhichElement, inIsTarget, true); } // =========================================================================== // Ä DrawElementAsCurrent() // =========================================================================== // // If mUseCustomHiliting is false, calls DrawHilitedElement(). // Otherwise, calls DrawCustomHilitedElement(). void cTextElementControl::DrawElementAsCurrent(Int16 inWhichElement, Boolean inErase){ if(!mUseCustomHiliting) DrawHilitedElement(inWhichElement, inErase); else DrawCustomHilitedElement(inWhichElement, inErase); } // =========================================================================== // Ä DrawElementAsNonCurrent() // =========================================================================== // // Draw an element that's not selected. This assumes port text values and fore and back colors are set correctly. void cTextElementControl::DrawElementAsNonCurrent(Int16 inWhichElement, Boolean inErase){ Rect elementRect; Str32 elementString = "\p"; Int16 stringWidth = 0, vPos = 0, hPos = 0; RGBColor textColor = mTextTraits.color; GetElementRect(inWhichElement, elementRect); GetElementString(inWhichElement, GetElementValue(inWhichElement), elementString); // Erase element rect, if asked to do so if(inErase){ ApplyForeAndBackColors(); ::EraseRect(&elementRect); } // Center element string horizontally and vertically stringWidth = ::StringWidth(elementString); vPos = elementRect.top + eElementRectSlopValue + ((elementRect.bottom - elementRect.top) - (mFinfo.ascent + mFinfo.descent) / 2); hPos = elementRect.left + ((elementRect.right - elementRect.left) - stringWidth) / 2; // Dim if inactive or disabled if(!IsActive() || !IsEnabled()) textColor = UGraphicUtils::Lighten (textColor); ::RGBForeColor(&textColor); ::MoveTo(hPos, vPos); ::DrawString(elementString); } // =========================================================================== // Ä DrawHilitedElement() // =========================================================================== // // Draw an element hilited. Default is to simply call DrawElementAsNonCurrent() // and then invert the element rect using hilite mode. void cTextElementControl::DrawHilitedElement(Int16 inWhichElement, Boolean inErase){ Rect elementRect; DrawElementAsNonCurrent(inWhichElement, inErase); GetElementRect(inWhichElement, elementRect); ::LMSetHiliteMode(true); ::InvertRect(&elementRect); } // =========================================================================== // Ä DrawCustomHilitedElement() // =========================================================================== // // Draw an element custom hilited. Override to provide fancier behavior. // Default version draws element string using mCustomHiliteColor, then underlines it. void cTextElementControl::DrawCustomHilitedElement(Int16 inWhichElement, Boolean inErase){ Rect elementRect; Str32 elementString = "\p"; Int16 stringWidth = 0, vPos = 0, hPos = 0; RGBColor textColor = mCustomHiliteColor; GetElementRect(inWhichElement, elementRect); GetElementString(inWhichElement, GetElementValue(inWhichElement), elementString); // Erase element rect, if asked to do so if(inErase){ ApplyForeAndBackColors(); ::EraseRect(&elementRect); } // Center element string horizontally and vertically stringWidth = ::StringWidth(elementString); vPos = elementRect.top + eElementRectSlopValue + ((elementRect.bottom - elementRect.top) - (mFinfo.ascent + mFinfo.descent) / 2); hPos = elementRect.left + ((elementRect.right - elementRect.left) - stringWidth) / 2; // Dim if inactive or disabled if(!IsActive() || !IsEnabled()) textColor = UGraphicUtils::Lighten (textColor); ::RGBForeColor(&textColor); ::MoveTo(hPos, vPos); ::DrawString(elementString); // Draw underline where descent is. For controls with numerals only, // this looks good. Otherwise, it might not look so good. ::MoveTo(hPos, vPos + mFinfo.descent); ::LineTo(hPos + stringWidth, vPos + mFinfo.descent); } // =========================================================================== // Ä DrawElementSeparator() // =========================================================================== // // Draw the text that separates two elements. // This assumes port text values and fore and back colors are set correctly. void cTextElementControl::DrawElementSeparator(Int16 inAfterWhichElement, Boolean inErase){ Rect firstRect, secondRect; Str32 betweenElementString = "\p:"; Int16 stringWidth = 0; RGBColor textColor = mTextTraits.color; GetElementRect(inAfterWhichElement, firstRect); GetElementSeparatorString(inAfterWhichElement, betweenElementString); stringWidth = ::StringWidth(betweenElementString); if((inAfterWhichElement + 1) < GetElementCount()) GetElementRect(inAfterWhichElement + 1, secondRect); else secondRect.left = firstRect.right + stringWidth; // Erase rect where we'll draw, if asked to if(inErase){ Rect erasedRect = firstRect; erasedRect.left = firstRect.right, erasedRect.right = secondRect.left, ::EraseRect(&erasedRect); } if(!IsActive() || !IsEnabled()) textColor = UGraphicUtils::Lighten (textColor); ::RGBForeColor(&textColor); ::MoveTo(firstRect.right + ((secondRect.left - firstRect.right) - stringWidth) / 2, firstRect.top + eElementRectSlopValue + ((firstRect.bottom - firstRect.top) - (mFinfo.ascent + mFinfo.descent) / 2)); ::DrawString(betweenElementString); } // =========================================================================== // Ä DrawFocusRing() // =========================================================================== // // Draw or erase focus ring. void cTextElementControl::DrawFocusRing(Boolean inHasRing){ Rect frame; CalcLocalFrameRect ( frame ); // Offset focus ring by title width frame.left += GetTitleWidth(); StColorPenState thePenState; thePenState.Normalize(); if (inHasRing) { // Draw ring in accent color RGBColor ringColor; UGAColorRamp::GetFocusAccentColor(ringColor); ::RGBForeColor(&ringColor); ::PenSize(eFocusRingThickness, eFocusRingThickness); ::FrameRoundRect(&frame, 4, 4); } else { // Erase area covered by ring StRegion ringRgn = frame; ::InsetRect(&frame, eFocusRingThickness, eFocusRingThickness); ringRgn -= frame; ApplyForeAndBackColors(); ::EraseRgn(ringRgn); } } // =========================================================================== // Ä DrawTitle() // =========================================================================== // // Draw control title. We use a separate text traits for the title, but be warned // that the calculations are very primitive. If you use a title size within a point or two // of the text size of the rest of the control, it should look OK though. // This is mostly to facilitate having the title in, say, Geneva plain 9 and the control text in Geneva bold 10. void cTextElementControl::DrawTitle(void){ Rect frameRect, elementRect; Int16 vPos = 0; RGBColor textColor = mTitleTextTraits.color; CalcLocalFrameRect(frameRect); GetElementRect(0, elementRect); UTextTraits::SetPortTextTraits(&mTitleTextTraits); // Draw title on same baseline as rest of control text vPos = elementRect.top + eElementRectSlopValue + ((elementRect.bottom - elementRect.top) - (mFinfo.ascent + mFinfo.descent) / 2); // Dim if inactive or disabled if(!IsActive() || !IsEnabled()) textColor = UGraphicUtils::Lighten (textColor); ::RGBForeColor(&textColor); ::MoveTo(frameRect.left, vPos); ::DrawString(mTitle); } // =========================================================================== // Ä GetTitleWidth() // =========================================================================== // // Return width of control title plus a few pixels to look good. Int16 cTextElementControl::GetTitleWidth(void){ StTextState savedTextState; StColorState savedColorState; UTextTraits::SetPortTextTraits(&mTitleTextTraits); return ::StringWidth(mTitle) + eFocusRingThickness; } #pragma mark - // =========================================================================== // Ä GetElementRect() // =========================================================================== // // Calculate the Rect occupied by an element. Sees how wide the widest string is // for each element, adds slop value, adds element separator width. // This is a timeconsuming routine that should only be called once, and its results stored for future use. void cTextElementControl::GetElementRect(Int16 inWhichElement, Rect& outLocalRect){ Rect theRect; Str32 betweenElementString = "\p"; ThrowIf_(inWhichElement < 0 || inWhichElement + 1 > GetElementCount()); CalcLocalFrameRect(theRect); // Compensate for title width if(mAlignment == teFlushLeft) theRect.left += GetTitleWidth(); ::InsetRect(&theRect, eFocusRingThickness + eFocusRingMargin, eFocusRingThickness + eFocusRingMargin); if(mAlignment == teFlushLeft){ theRect.right = theRect.left; // Loop the elements adding up the width until we get to the element we want for(Int16 i = 0; i <= inWhichElement; i++){ theRect.left = theRect.right; theRect.right += (GetMaxElementStringWidth(i) + (2 * eElementRectSlopValue)); // If we'll be adding up another element after this, // take separator string into account if(i < inWhichElement){ GetElementSeparatorString(i, betweenElementString); theRect.left = theRect.right; theRect.right += ::StringWidth(betweenElementString); } } } else{ theRect.left = theRect.right; // Loop the elements adding up the width until we get to the element we want for(Int16 i = GetElementCount() - 1; i >= inWhichElement; i--){ theRect.right = theRect.left; theRect.left -= (GetMaxElementStringWidth(i) + (2 * eElementRectSlopValue)); // If we'll be adding up another element after this, // take separator string into account if(i > inWhichElement){ GetElementSeparatorString(i - 1, betweenElementString); theRect.right = theRect.left; theRect.left -= ::StringWidth(betweenElementString); } } } // Bottom is top plus text height plus slop value theRect.bottom = theRect.top + (2 * eElementRectSlopValue) + (mFinfo.ascent + mFinfo.descent); outLocalRect = theRect; } // =========================================================================== // Ä GetElementString() // =========================================================================== // // Get the text to show for an element given the element's index value. // Default behavior simply stringizes the index and returns it in outString. void cTextElementControl::GetElementString(Int16 inWhichElement, Int16 inIndex, Str32 outString){ ThrowIf_(inIndex > GetElementMaxIndex(inWhichElement)); sprintf((char*)outString, "%02d", inIndex); c2pstr((char*)outString); } // =========================================================================== // Ä GetMaxElementStringWidth() // =========================================================================== // // Get the maximum width of the strings in an element. Int16 cTextElementControl::GetMaxElementStringWidth(Int16 inWhichElement){ Int16 result = 0, thisWidth = 0; Str32 elementString = "\p"; ThrowIf_(inWhichElement >= GetElementCount()); UTextTraits::SetPortTextTraits(&mTextTraits); // Loop the element strings, save width every time we come to a wider string for(int i = 0, max = GetElementMaxIndex(inWhichElement); i < max; i++){ GetElementString(inWhichElement, i, elementString); thisWidth = ::StringWidth(elementString); if(thisWidth > result) result = thisWidth; } return result; } // =========================================================================== // Ä FindHotSpot() // =========================================================================== // // Loop calling PointInHotSpot() until we hit something. Int16 cTextElementControl::FindHotSpot( Point inPoint) const { Int16 result = 0; for(Int16 i = 0, max = (const_cast(this))->GetElementCount(); i < max; i++){ if(PointInHotSpot(inPoint, i + 1)){ result = i + 1; break; } } return result; } // =========================================================================== // Ä PointInHotSpot() // =========================================================================== // // Since a valid hot spot value is at least 1, // simply subtract 1 and call GetElementRect(). Boolean cTextElementControl::PointInHotSpot(Point inPoint, Int16 inHotSpot) const { Boolean result = false; Rect theRect; (const_cast(this))->GetElementRect(inHotSpot - 1, theRect); return ::PtInRect(inPoint, &theRect); } // =========================================================================== // Ä HotSpotAction() // =========================================================================== // // Set current elements when user clicks in control. void cTextElementControl::HotSpotAction(Int16 inHotSpot, Boolean inCurrInside, Boolean inPrevInside){ if(!inPrevInside && inCurrInside) SetCurrentElement(inHotSpot - 1); LControl::HotSpotAction(inHotSpot, inCurrInside, inPrevInside); } // =========================================================================== // Ä SetCurrentElement() // =========================================================================== // // Make an element "active", and the previously active element (if any) inactive. void cTextElementControl::SetCurrentElement(Int16 inWhichElement){ FocusDraw(); // Redraw active element that goes out of "focus" if(mCurrentElement != eNoCurrentElement && mCurrentElement != inWhichElement){ Int16 oldElement = mCurrentElement; mCurrentElement = eNoCurrentElement; RedrawElement(oldElement, true); } ThrowIf_(inWhichElement >= GetElementCount()); if(mCurrentElement != inWhichElement) mCurrentElement = inWhichElement; if(mCurrentElement != eNoCurrentElement) RedrawElement(mCurrentElement, true); AdjustLittleArrows(); } // =========================================================================== // Ä HandleKeyPress() // =========================================================================== // // Dispatch navigation keys and printing characters to subroutines Boolean cTextElementControl::HandleKeyPress(const EventRecord &inKeyEvent) { Boolean keyHandled = true; EKeyStatus theKeyStatus = keyStatus_Input; Int16 theKey = (Int16) (inKeyEvent.message & charCodeMask); LCommander *theTarget = GetTarget(); if (!(inKeyEvent.modifiers & cmdKey) && UKeyFilters::IsNavigationKey(theKey)) keyHandled = HandleNavigationKey(inKeyEvent); else if (!(inKeyEvent.modifiers & cmdKey) && UKeyFilters::IsPrintingChar(theKey)) keyHandled = HandlePrintingKey(inKeyEvent); else keyHandled = LCommander::HandleKeyPress(inKeyEvent); return keyHandled; } // =========================================================================== // Ä HandleNavigationKey() // =========================================================================== // // Default behavior lets user use up/down arrow to increase/decrease current element value, // and left/right arrow to move among the elements. Boolean cTextElementControl::HandleNavigationKey(const EventRecord &inKeyEvent){ Boolean handled = false; Int16 theKey = (Int16) (inKeyEvent.message & charCodeMask), currElement = GetCurrentElement(); switch (theKey) { case char_LeftArrow: // Move to element to the left, or wrap to rightmost element if(currElement == 0 || currElement == eNoCurrentElement) SetCurrentElement(GetElementCount() - 1); else if(currElement > 0) SetCurrentElement(currElement - 1); handled = true; break; case char_RightArrow: // Move to element to the right, or wrap to leftmost element if(currElement == (GetElementCount() - 1) || currElement == eNoCurrentElement) SetCurrentElement(0); else if(currElement < (GetElementCount() - 1)) SetCurrentElement(currElement + 1); handled = true; break; case char_UpArrow: IncrementCurrentElement(); handled = true; break; case char_DownArrow: DecrementCurrentElement(); handled = true; break; } return handled; } // =========================================================================== // Ä ClickSelf() // =========================================================================== // // Make ourselves the target when user clicks in us. void cTextElementControl::ClickSelf( const SMouseDownEvent &inMouseDown){ SwitchTarget(this); LControl::ClickSelf(inMouseDown); } // =========================================================================== // Ä BeTarget() // =========================================================================== // // Show LLittleArrows and focus ring. void cTextElementControl::BeTarget(){ if(mLittleArrows){ AdjustLittleArrows(); mLittleArrows->Show(); mLittleArrows->Draw(nil); mLittleArrows->DontRefresh(); } FocusDraw(); DrawFocusRing(true); // Redraw the element that's the current one if(GetCurrentElement() != eNoCurrentElement) RedrawElement(GetCurrentElement(), true); LCommander::BeTarget(); } // =========================================================================== // Ä DontBeTarget() // =========================================================================== // // Hide LLittleArrows and focus ring. void cTextElementControl::DontBeTarget(){ if(mLittleArrows){ // Erase LLittleArrows rect immediately Rect theRect; mLittleArrows->CalcPortFrameRect(theRect); mLittleArrows->Hide(); ApplyForeAndBackColors(); ::EraseRect(&theRect); ValidPortRect(&theRect); } FocusDraw(); DrawFocusRing(false); // Redraw the element that's the current one if(GetCurrentElement() != eNoCurrentElement) RedrawElement(GetCurrentElement(), false); LCommander::DontBeTarget(); } // =========================================================================== // Ä IncrementCurrentElement() // =========================================================================== // void cTextElementControl::IncrementCurrentElement(void){ if(GetCurrentElement() != eNoCurrentElement){ SetElementValue(GetCurrentElement(), GetElementValue(GetCurrentElement()) + 1); BroadcastValueMessage(); } } // =========================================================================== // Ä DecrementCurrentElement() // =========================================================================== // void cTextElementControl::DecrementCurrentElement(void){ if(GetCurrentElement() != eNoCurrentElement){ SetElementValue(GetCurrentElement(), GetElementValue(GetCurrentElement()) - 1); BroadcastValueMessage(); } } //======================================================================== // Ä ActivateSelf [protected, virtual] //======================================================================== // // Redraw previously dimmed control void cTextElementControl::ActivateSelf(){ LControl::ActivateSelf(); if ( mActive == triState_On ) { Refresh(); } } //======================================================================== // Ä DeactivateSelf [protected, virtual] //======================================================================== // // Don't be target and redraw previously undimmed control void cTextElementControl::DeactivateSelf(){ if (IsOnDuty()) SwitchTarget(GetSuperCommander()); LControl::DeactivateSelf(); Refresh(); } //======================================================================== // Ä DisableSelf [protected, virtual] //======================================================================== // // Don't be target and redraw previously undimmed control void cTextElementControl::DisableSelf(){ if (IsOnDuty()) SwitchTarget(GetSuperCommander()); LControl::DisableSelf(); Refresh(); } #pragma mark - //======================================================================== // Ä Constructor, cTextElementLittleArrows //======================================================================== // // LLittleArrows subclass that broadcasts a message indicating whether value increased or decreased. cTextElementLittleArrows::cTextElementLittleArrows( const SPaneInfo &inPaneInfo, MessageT inValueIncreasedMessage, MessageT inValueDecreasedMessage, Int32 inImpID) : LLittleArrows(inPaneInfo, msg_Nothing, 0, -200000, 200000, inImpID){ mValueIncreasedMessage = inValueIncreasedMessage; mValueDecreasedMessage = inValueDecreasedMessage; mPreviousValue = GetValue(); } //======================================================================== // Ä BroadcastValueMessage() //======================================================================== // // Broadcast a message indicating whether value increased or decreased, void cTextElementLittleArrows::BroadcastValueMessage(void){ Int32 value = GetValue(); if(IsBroadcasting()){ if(value < mPreviousValue){ BroadcastMessage(mValueDecreasedMessage, &value); } else if(value > mPreviousValue) BroadcastMessage(mValueIncreasedMessage, &value); } mPreviousValue = value; } #pragma mark - // =========================================================================== // Ä cStaticRectTextElementControl constructor // =========================================================================== // cStaticRectTextElementControl::cStaticRectTextElementControl(LStream* inStream) : cTextElementControl(inStream){} // =========================================================================== // Ä FinishCreateSelf() // =========================================================================== // // Store element rects in TArray for future use. void cStaticRectTextElementControl::FinishCreateSelf(void){ Rect elementRect, localFrameRect; cTextElementControl::FinishCreateSelf(); FocusDraw(); CalcLocalFrameRect(localFrameRect); for(int i = 0; i < GetElementCount(); i++){ cTextElementControl::GetElementRect(i, elementRect); // Offset rect so that top left is (0, 0) ::OffsetRect(&elementRect, -localFrameRect.left, -localFrameRect.top); mRectArray.AddItem(elementRect); } Int16 oldWidth = mFrameSize.width; mFrameSize.width = GetTitleWidth() + (mRectArray[GetElementCount()].right - mRectArray[LArray::index_First].left) + (2 * eFocusRingThickness) + 2; mFrameSize.height = (mRectArray[GetElementCount()].bottom - mRectArray[LArray::index_First].top) + (2 * eFocusRingThickness) + 2; if(mAlignment != teFlushLeft){ MoveBy(oldWidth - mFrameSize.width, 0, false); TArrayIterator iterator(mRectArray); Rect theRect; while(iterator.Next(theRect)){ ::OffsetRect(&theRect, -(oldWidth - mFrameSize.width), 0); mRectArray[iterator.GetCurrentIndex()] = theRect; } } } // =========================================================================== // Ä GetElementRect() // =========================================================================== // // Get the rects from array, instead of recalculating them void cStaticRectTextElementControl::GetElementRect(Int16 inWhichElement, Rect& outLocalRect){ Rect localFrameRect; ThrowIf_(!mRectArray.ValidIndex(inWhichElement + 1)); outLocalRect = mRectArray[inWhichElement + 1]; // Control might have moved since rects were calculated, // therefore adjust rect from local frame rect values CalcLocalFrameRect(localFrameRect); ::OffsetRect(&outLocalRect, localFrameRect.left, localFrameRect.top); } #pragma mark - // =========================================================================== // Ä Constructor, cTimeCodeControl // =========================================================================== // // Set minimum and maximum of control (to 00:00:00:00 / 23:59:29:29) cTimeCodeControl::cTimeCodeControl(LStream* inStream) : cStaticRectTextElementControl(inStream){ *inStream >> mFramesPerSecond; mTimeCodeValue.hours = 0, mTimeCodeValue.minutes = 0, mTimeCodeValue.seconds = 0, mTimeCodeValue.frames = 0; mTimeCodeMinimum.hours = 0, mTimeCodeMinimum.minutes = 0, mTimeCodeMinimum.seconds = 0, mTimeCodeMinimum.frames = 0; mTimeCodeMaximum.hours = 23, mTimeCodeMaximum.minutes = 59, mTimeCodeMaximum.seconds = 59, mTimeCodeMaximum.frames = mFramesPerSecond - 1; } // =========================================================================== // Ä FinishCreateSelf() // =========================================================================== // // Adjust stored rects and create a LLittleArrows. void cTimeCodeControl::FinishCreateSelf(void){ cStaticRectTextElementControl::FinishCreateSelf(); // We won't have any descents since we're showing integers only TArrayIterator iterator(mRectArray); Rect dummyRect; while(iterator.Next(dummyRect)){ mRectArray[iterator.GetCurrentIndex()].bottom -= mFinfo.descent; } mFrameSize.height -= mFinfo.descent; CreateLittleArrows(); } // =========================================================================== // Ä SetFrameRate() // =========================================================================== // // Does what it says. Perhaps we should convert the values we have to another frame rate? // Right now we just enforce the maximum number of frames for a given rate. void cTimeCodeControl::SetFrameRate(Byte inFramesPerSecond){ mFramesPerSecond = inFramesPerSecond; if(mTimeCodeValue.frames >= mFramesPerSecond) mTimeCodeValue.frames = mFramesPerSecond - 1; if(mTimeCodeMinimum.frames >= mFramesPerSecond) mTimeCodeMinimum.frames = mFramesPerSecond - 1; if(mTimeCodeMaximum.frames >= mFramesPerSecond) mTimeCodeMaximum.frames = mFramesPerSecond - 1; Refresh(); } // =========================================================================== // Ä SetTimeCodeValue() // =========================================================================== // // Set value of control from a TimeCodeTime void cTimeCodeControl::SetTimeCodeValue(TimeCodeTime& inTimeCode){ if(*(long*)&inTimeCode < *(long*)&mTimeCodeMinimum) inTimeCode = mTimeCodeMinimum; else if(*(long*)&inTimeCode > *(long*)&mTimeCodeMaximum) inTimeCode = mTimeCodeMaximum; mTimeCodeValue = inTimeCode; if(mTimeCodeValue.frames >= mFramesPerSecond) mTimeCodeValue.frames = mFramesPerSecond - 1; } // =========================================================================== // Ä GetTimeCodeValue() // =========================================================================== // // Get value of control as a TimeCodeTime void cTimeCodeControl::GetTimeCodeValue(TimeCodeTime& outTimeCode){ outTimeCode = mTimeCodeValue; } // =========================================================================== // Ä SetTimeCodeMinValue() // =========================================================================== // // Set minimum time code value from TimeCodeTime and adjust value if beyond bounds void cTimeCodeControl::SetTimeCodeMinValue(TimeCodeTime& inTimeCode){ mTimeCodeMinimum = inTimeCode; if(mTimeCodeMinimum.frames >= mFramesPerSecond) mTimeCodeMinimum.frames = mFramesPerSecond - 1; if(*(long*)&mTimeCodeValue < *(long*)&mTimeCodeMinimum) SetTimeCodeValue(mTimeCodeMinimum); } // =========================================================================== // Ä GetTimeCodeMinValue() // =========================================================================== // // Get minimum time code value as TimeCodeTime void cTimeCodeControl::GetTimeCodeMinValue(TimeCodeTime& outTimeCode){ outTimeCode = mTimeCodeMinimum; } // =========================================================================== // Ä SetTimeCodeMaxValue() // =========================================================================== // // Set maximum time code value from TimeCodeTime and adjust value if beyond bounds void cTimeCodeControl::SetTimeCodeMaxValue(TimeCodeTime& inTimeCode){ mTimeCodeMaximum = inTimeCode; if(mTimeCodeMaximum.frames >= mFramesPerSecond) mTimeCodeMaximum.frames = mFramesPerSecond - 1; if(*(long*)&mTimeCodeValue > *(long*)&mTimeCodeMaximum) SetTimeCodeValue(mTimeCodeMaximum); } // =========================================================================== // Ä GetTimeCodeMaxValue() // =========================================================================== // // Get maximum time code value as TimeCodeTime void cTimeCodeControl::GetTimeCodeMaxValue(TimeCodeTime& outTimeCode){ outTimeCode = mTimeCodeMaximum; } // =========================================================================== // Ä GetElementValue() // =========================================================================== // // Override from cTextElementControl Int16 cTimeCodeControl::GetElementValue(Int16 inWhichElement){ Int16 result = 0; ThrowIf_(inWhichElement > 3); switch(inWhichElement){ case 0: result = mTimeCodeValue.hours; break; case 1: result = mTimeCodeValue.minutes; break; case 2: result = mTimeCodeValue.seconds; break; case 3: result = mTimeCodeValue.frames; break; } return result; } // =========================================================================== // Ä SetElementValue() // =========================================================================== // // Override from cTextElementControl void cTimeCodeControl::SetElementValue(Int16 inWhichElement, Int16 inValue){ ThrowIf_(inWhichElement > 3); if(inValue < 0 || inValue >= GetElementMaxIndex(inWhichElement)) return; switch(inWhichElement){ case 0: mTimeCodeValue.hours = inValue; break; case 1: mTimeCodeValue.minutes = inValue; break; case 2: mTimeCodeValue.seconds = inValue; break; case 3: mTimeCodeValue.frames = inValue; break; } RedrawElement(inWhichElement, IsTarget()); } // =========================================================================== // Ä GetElementMaxIndex() // =========================================================================== // // Override from cTextElementControl. Int16 cTimeCodeControl::GetElementMaxIndex(Int16 inWhichElement){ Int16 result = 0; ThrowIf_(inWhichElement > 3); switch(inWhichElement){ case 0: result = 24; break; case 1: result = 60; break; case 2: result = 60; break; case 3: result = mFramesPerSecond; break; } return result; } // =========================================================================== // Ä GetElementSeparatorString() // =========================================================================== // // Override from cTextElementControl. Separator is a colon. void cTimeCodeControl::GetElementSeparatorString(Int16 inAfterWhichElement, Str32 outString){ if(inAfterWhichElement < 3){ outString[0] = 1; outString[1] = ':'; } else outString[0] = 0; } // =========================================================================== // Ä IncrementCurrentElement() // =========================================================================== // // Override from cTextElementControl. void cTimeCodeControl::IncrementCurrentElement(void){ if(GetCurrentElement() != eNoCurrentElement){ TimeCodeTime newTimeCode = mTimeCodeValue; switch(GetCurrentElement()){ case 0: AddHours(newTimeCode, 1); break; case 1: AddMinutes(newTimeCode, 1); break; case 2: AddSeconds(newTimeCode, 1); break; case 3: AddFrames(newTimeCode, 1); break; } // Are we within bounds? if(*(long*)&newTimeCode < *(long*)&mTimeCodeMinimum) newTimeCode = mTimeCodeMinimum; else if(*(long*)&newTimeCode > *(long*)&mTimeCodeMaximum) newTimeCode = mTimeCodeMaximum; if(*(long*)&newTimeCode != *(long*)&mTimeCodeValue){ mTimeCodeValue = newTimeCode; BroadcastValueMessage(); Draw(nil); } } } // =========================================================================== // Ä DecrementCurrentElement() // =========================================================================== // // Override from cTextElementControl. void cTimeCodeControl::DecrementCurrentElement(void){ if(GetCurrentElement() != eNoCurrentElement){ TimeCodeTime newTimeCode = mTimeCodeValue; switch(GetCurrentElement()){ case 0: SubtractHours(newTimeCode, 1); break; case 1: SubtractMinutes(newTimeCode, 1); break; case 2: SubtractSeconds(newTimeCode, 1); break; case 3: SubtractFrames(newTimeCode, 1); break; } // Are we within bounds? if(*(long*)&newTimeCode < *(long*)&mTimeCodeMinimum) newTimeCode = mTimeCodeMinimum; else if(*(long*)&newTimeCode > *(long*)&mTimeCodeMaximum) newTimeCode = mTimeCodeMaximum; if(*(long*)&newTimeCode != *(long*)&mTimeCodeValue){ mTimeCodeValue = newTimeCode; BroadcastValueMessage(); Draw(nil); } } } // =========================================================================== // Ä AddHours() // =========================================================================== // // Add hours to time code value of control. Return true if OK, false if not (= if we try to add so that hour value > 23). // In the last case, adjusts value to max 23 hours. Boolean cTimeCodeControl::AddHours(TimeCodeTime& ioTimeCode, Int16 inHours){ Boolean result = true; if(ioTimeCode.hours + inHours > 23){ ioTimeCode.hours = 23; result = false; } else ioTimeCode.hours += inHours; return result; } // =========================================================================== // Ä AddMinutes() // =========================================================================== // // Add minutes to time code value of control. Return true if OK, false if not. // Minutes above 59 are added to hours. Boolean cTimeCodeControl::AddMinutes(TimeCodeTime& ioTimeCode, Int16 inMinutes){ short hoursToAdd = 0; Boolean result = false; while((ioTimeCode.minutes + inMinutes) > 59){ hoursToAdd++; inMinutes -= 60; } result = AddHours(ioTimeCode, hoursToAdd); if(result) ioTimeCode.minutes += inMinutes; else ioTimeCode.minutes = 59; return result; } // =========================================================================== // Ä AddSeconds() // =========================================================================== // // Add seconds to time code value of control. Return true if OK, false if not. // Seconds above 59 are added to minutes. Boolean cTimeCodeControl::AddSeconds(TimeCodeTime& ioTimeCode, Int16 inSeconds){ short minutesToAdd = 0; Boolean result = false; while((ioTimeCode.seconds + inSeconds) > 59){ minutesToAdd++; inSeconds -= 60; } result = AddMinutes(ioTimeCode, minutesToAdd); if(result) ioTimeCode.seconds += inSeconds; else ioTimeCode.seconds = 59; return result; } // =========================================================================== // Ä AddFrames() // =========================================================================== // // Add frames to time code value of control. Return true if OK, false if not. // Frames >= frame rate are added to seconds. Boolean cTimeCodeControl::AddFrames(TimeCodeTime& ioTimeCode, Int16 inFrames){ short secondsToAdd = 0; Boolean result = false; while((ioTimeCode.frames + inFrames) >= mFramesPerSecond){ secondsToAdd++; inFrames -= mFramesPerSecond; } result = AddSeconds(ioTimeCode, secondsToAdd); if(result) ioTimeCode.frames += inFrames; else ioTimeCode.frames = mFramesPerSecond - 1; return result; } // =========================================================================== // Ä SubtractHours() // =========================================================================== // // Subtract hours from time code value of control. Return true if OK, false if not (=result would be sub-zero hours). Boolean cTimeCodeControl::SubtractHours(TimeCodeTime& ioTimeCode, Int16 inHours){ Boolean result = true; if(inHours > ioTimeCode.hours) result = false; else ioTimeCode.hours -= inHours; return result; } // =========================================================================== // Ä SubtractMinutes() // =========================================================================== // // Subtract minutes from time code value of control. Return true if OK, false if not. // Minutes > previous minutes are subtracted from hours. Boolean cTimeCodeControl::SubtractMinutes(TimeCodeTime& ioTimeCode, Int16 inMinutes){ short hoursDropped = 0; Boolean result = false; while(inMinutes > ioTimeCode.minutes){ hoursDropped++; inMinutes -= 60; } result = SubtractHours(ioTimeCode, hoursDropped); if(result) ioTimeCode.minutes -= inMinutes; else ioTimeCode.minutes = 0; return result; } // =========================================================================== // Ä SubtractSeconds() // =========================================================================== // // Subtract seconds from time code value of control. Return true if OK, false if not. // Seconds > previous seconds are subtracted from minutes. Boolean cTimeCodeControl::SubtractSeconds(TimeCodeTime& ioTimeCode, Int16 inSeconds){ short minutesDropped = 0; Boolean result = false; while(inSeconds > ioTimeCode.seconds){ minutesDropped++; inSeconds -= 60; } result = SubtractMinutes(ioTimeCode, minutesDropped); if(result) ioTimeCode.seconds -= inSeconds; else ioTimeCode.seconds = 0; return result; } // =========================================================================== // Ä SubtractFrames() // =========================================================================== // // Subtract frames from time code value of control. Return true if OK, false if not. // Frames > previous frames are subtracted from seconds. Boolean cTimeCodeControl::SubtractFrames(TimeCodeTime& ioTimeCode, Int16 inFrames){ short secondsDropped = 0; Boolean result = false; while(inFrames > ioTimeCode.frames){ secondsDropped++; inFrames -= mFramesPerSecond; } result = SubtractSeconds(ioTimeCode, secondsDropped); if(result) ioTimeCode.frames -= inFrames; else ioTimeCode.frames = 0; return result; } #pragma mark - cTimeControl::cTimeControl(LStream* inStream) : cStaticRectTextElementControl(inStream){ tm theTime; mTimeFormat = NULL; mTimeValue = time(NULL); mMinTimeValue = time(NULL); mMaxTimeValue = time(NULL); // Compute arbitrary default minimum: 1 jan. 1900 theTime = *localtime(&mTimeValue); theTime.tm_year = 0; theTime.tm_mon = 0; theTime.tm_mday = 1; mMinTimeValue = mktime(&theTime); // Compute arbitrary default maximum: 31 dec. 2500 theTime = *localtime(&mTimeValue); theTime.tm_year = 600; theTime.tm_mon = 11; theTime.tm_mday = 31; mMaxTimeValue = mktime(&theTime); InitTimeFormat(kCurrentItl0ResNum); } // =========================================================================== // Ä ~cTimeControl() // =========================================================================== cTimeControl::~cTimeControl(void){ if(mTimeFormat){ ::DisposeHandle((Handle)mTimeFormat); mTimeFormat = NULL; } } // =========================================================================== // Ä SetDateValue() // =========================================================================== // // Set value from time_t, enforcing minimum and maximum values. void cTimeControl::SetTimeValue(time_t& inTime){ if(inTime < mMinTimeValue) inTime = mMinTimeValue; if(inTime > mMaxTimeValue) inTime = mMaxTimeValue; mTimeValue = inTime; } // =========================================================================== // Ä SetTimeValue() // =========================================================================== // // Set value from tm, enforcing minimum and maximum values. void cTimeControl::SetTimeValue(tm& inTime){ mTimeValue = mktime(&inTime); if(mTimeValue < mMinTimeValue) mTimeValue = mMinTimeValue; if(mTimeValue > mMaxTimeValue) mTimeValue = mMaxTimeValue; } // =========================================================================== // Ä SetTimeValueToNow() // =========================================================================== // // Set value of control to current time. void cTimeControl::SetTimeValueToNow(void){ time_t now = time(NULL); SetTimeValue(now); } // =========================================================================== // Ä GetTimeValue() // =========================================================================== // // Get Time value as time_t. void cTimeControl::GetTimeValue(time_t& outTime){ outTime = mTimeValue; } // =========================================================================== // Ä GetTimeValue() // =========================================================================== // // Get Time value as tm. void cTimeControl::GetTimeValue(tm& outTime){ outTime = *localtime(&mTimeValue); } // =========================================================================== // Ä SetTimeMinValue() // =========================================================================== // // Set minimum value from time_t, adjusting value. void cTimeControl::SetTimeMinValue(time_t& inTime){ mMinTimeValue = inTime; if(mTimeValue < mMinTimeValue) SetTimeValue(mMinTimeValue); } // =========================================================================== // Ä SetTimeMinValue() // =========================================================================== // // Set minimum value from tm, adjusting value. void cTimeControl::SetTimeMinValue(tm& inTime){ mMinTimeValue = mktime(&inTime); if(mTimeValue < mMinTimeValue) SetTimeValue(mMinTimeValue); } // =========================================================================== // Ä GetTimeMinValue() // =========================================================================== // // Get minimum value as time_t. void cTimeControl::GetTimeMinValue(time_t& outTime){ outTime = mMinTimeValue; } // =========================================================================== // Ä GetTimeMinValue() // =========================================================================== // // Get minimum value as tm. void cTimeControl::GetTimeMinValue(tm& outTime){ outTime = *localtime(&mMinTimeValue); } // =========================================================================== // Ä SetTimeMaxValue() // =========================================================================== // // Set maximum value from time_t, adjusting value. void cTimeControl::SetTimeMaxValue(time_t& inTime){ mMaxTimeValue = inTime; if(mTimeValue > mMaxTimeValue) SetTimeValue(mMaxTimeValue); } // =========================================================================== // Ä SetTimeMaxValue() // =========================================================================== // // Set maximum value from tm, adjusting value. void cTimeControl::SetTimeMaxValue(tm& inTime){ mMaxTimeValue = mktime(&inTime); if(mTimeValue > mMaxTimeValue) SetTimeValue(mMaxTimeValue); } // =========================================================================== // Ä GetTimeMaxValue() // =========================================================================== // // Get maximum value as time_t. void cTimeControl::GetTimeMaxValue(time_t& outTime){ outTime = mMaxTimeValue; } // =========================================================================== // Ä GetTimeMaxValue() // =========================================================================== // // Get maximum value as tm. void cTimeControl::GetTimeMaxValue(tm& outTime){ outTime = *localtime(&mMaxTimeValue); } // =========================================================================== // Ä InitTimeFormat() // =========================================================================== // // Read 'itl0' resource, which holds date string configuration settings. // Pass in kCurrentItl0ResNum to init from current system 'itl0' resource. void cTimeControl::InitTimeFormat(ResIDT inItl0ResNum){ mTimeFormat = (Intl0Hndl)::GetResource('itl0', inItl0ResNum); // If no handle, fall back on 'itl0' resource for current script region. if(!mTimeFormat) mTimeFormat = (Intl0Hndl)::GetResource('itl0', ::GetScriptManagerVariable(smRegionCode)); // If still no handle, make an empty one. if(!mTimeFormat){ mTimeFormat = (Intl0Hndl)::NewHandleClear(sizeof(Intl0Rec)); } else ::DetachResource((Handle)mTimeFormat); ThrowIfNil_(mTimeFormat); ::HLockHi((Handle)mTimeFormat); } #pragma mark - // =========================================================================== // Ä cDateControl constructor // =========================================================================== cDateControl::cDateControl(LStream* inStream) : cTimeControl(inStream){} // =========================================================================== // Ä FinishCreateSelf() // =========================================================================== // // Create LLittleArrows and adjust stored rects. void cDateControl::FinishCreateSelf(void){ cTimeControl::FinishCreateSelf(); // We won't have any descents since we're showing integers only TArrayIterator iterator(mRectArray); Rect dummyRect; while(iterator.Next(dummyRect)) mRectArray[iterator.GetCurrentIndex()].bottom -= mFinfo.descent; mFrameSize.height -= mFinfo.descent; CreateLittleArrows(); } // =========================================================================== // Ä GetElementValue() // =========================================================================== // Int16 cDateControl::GetElementValue(Int16 inWhichElement){ Int16 result = 0; ThrowIf_(inWhichElement > 2); tm* theTime = localtime(&mTimeValue); switch(GetElementKind(inWhichElement)){ case eYearElement: result = theTime->tm_year; break; case eMonthElement: result = theTime->tm_mon; break; case eDayElement: result = theTime->tm_mday - 1; break; } return result; } // =========================================================================== // Ä SetElementValue() // =========================================================================== // void cDateControl::SetElementValue(Int16 inWhichElement, Int16 inValue){ ThrowIf_(inWhichElement > 3); if(inValue < 0 || inValue > GetElementMaxIndex(inWhichElement)) return; tm currTime = *localtime(&mTimeValue); time_t newTime = 0; switch(GetElementKind(inWhichElement)){ case eYearElement: currTime.tm_year = inValue + 1; newTime = mktime(&currTime); if(newTime >= mMinTimeValue && newTime <= mMaxTimeValue); mTimeValue = newTime; break; case eMonthElement: currTime.tm_mon = inValue; newTime = mktime(&currTime); if(newTime >= mMinTimeValue && newTime <= mMaxTimeValue); mTimeValue = newTime; break; case eDayElement: currTime.tm_mday = inValue + 1; newTime = mktime(&currTime); if(newTime >= mMinTimeValue && newTime <= mMaxTimeValue); mTimeValue = newTime; break; } RedrawElement(inWhichElement, IsTarget()); } // =========================================================================== // Ä GetElementMaxIndex() // =========================================================================== // Int16 cDateControl::GetElementMaxIndex(Int16 inWhichElement){ Int16 result = 0; ThrowIf_(inWhichElement > 2); switch(GetElementKind(inWhichElement)){ case eYearElement: result = 599; break; case eMonthElement: result = 11; break; case eDayElement: // See which month we are in tm* currTime = localtime(&mTimeValue); switch(currTime->tm_mon + 1){ // The thirty day months case 4: case 6: case 9: case 11: result = 29; break; // February case 2: result = IsLeapYear(currTime->tm_year + 1900) ? 28 : 27; break; // All other months default: result = 30; } break; } return result; } // =========================================================================== // Ä GetMaxElementStringWidth() // =========================================================================== // Int16 cDateControl::GetMaxElementStringWidth(Int16 inWhichElement){ Str32 elementString = "\p"; ThrowIf_(inWhichElement >= GetElementCount()); UTextTraits::SetPortTextTraits(&mTextTraits); switch(GetElementKind(inWhichElement)){ case eYearElement: LString::CopyPStr(((*mTimeFormat)->shrtDateFmt & century) ? "\p0000" : "\p00", elementString); break; case eMonthElement: LString::CopyPStr("\p00", elementString); break; case eDayElement: LString::CopyPStr("\p00", elementString); break; } return ::StringWidth(elementString); } // =========================================================================== // Ä GetElementString() // =========================================================================== // // Take into account preferences of script system. void cDateControl::GetElementString(Int16 inWhichElement, Int16 inIndex, Str32 outString){ tm* currTime = localtime(&mTimeValue); ThrowIf_(inIndex > GetElementMaxIndex(inWhichElement)); switch(GetElementKind(inWhichElement)){ case eYearElement: // Subtract 100 from above-100 digits if we don't show centuries if(!((*mTimeFormat)->shrtDateFmt & century) && (currTime->tm_year > 99)) currTime->tm_year -= 100; sprintf((char*)outString, ((*mTimeFormat)->shrtDateFmt & century) ? "%04d" : "%02d", ((*mTimeFormat)->shrtDateFmt & century) ? currTime->tm_year + 1900 : currTime->tm_year); break; case eMonthElement: sprintf((char*)outString, ((*mTimeFormat)->shrtDateFmt & mntLdingZ) ? "%02d" : "%d", currTime->tm_mon + 1); break; case eDayElement: sprintf((char*)outString, ((*mTimeFormat)->shrtDateFmt & dayLdingZ) ? "%02d" : "%d", currTime->tm_mday); break; } c2pstr((char*)outString); } // =========================================================================== // Ä GetElementSeparatorString() // =========================================================================== // // Take into account preferences of script system. void cDateControl::GetElementSeparatorString(Int16 inAfterWhichElement, Str32 outString){ if(inAfterWhichElement < 2){ outString[0] = 1; outString[1] = (*mTimeFormat)->dateSep; } else outString[0] = 0; } // =========================================================================== // Ä IncrementCurrentElement() // =========================================================================== // void cDateControl::IncrementCurrentElement(void){ if(GetCurrentElement() != eNoCurrentElement){ tm currTime = *localtime(&mTimeValue); time_t newTime; switch(GetElementKind(GetCurrentElement())){ case eYearElement: currTime.tm_year++; break; case eMonthElement: currTime.tm_mon++; break; case eDayElement: currTime.tm_mday++; break; } newTime = mktime(&currTime); if(newTime < mMinTimeValue) newTime = mMinTimeValue; else if(newTime > mMaxTimeValue) newTime = mMaxTimeValue; if(newTime != mTimeValue){ mTimeValue = newTime; BroadcastValueMessage(); Draw(nil); } } } // =========================================================================== // Ä DecrementCurrentElement() // =========================================================================== // void cDateControl::DecrementCurrentElement(void){ if(GetCurrentElement() != eNoCurrentElement){ tm currTime = *localtime(&mTimeValue); time_t newTime; switch(GetElementKind(GetCurrentElement())){ case eYearElement: currTime.tm_year--; break; case eMonthElement: currTime.tm_mon--; break; case eDayElement: currTime.tm_mday--; break; } newTime = mktime(&currTime); if(newTime < mMinTimeValue) newTime = mMinTimeValue; else if(newTime > mMaxTimeValue) newTime = mMaxTimeValue; if(newTime != mTimeValue){ mTimeValue = newTime; BroadcastValueMessage(); Draw(nil); } } } // =========================================================================== // Ä GetElementKind() // =========================================================================== // // Based on the order in which date elements should be presented, // calculate which element shows what component of the date. Int16 cDateControl::GetElementKind(Int16 inWhichElement){ Int16 result = 0; ThrowIf_(inWhichElement > 2); switch((*mTimeFormat)->dateOrder){ case mdy: switch(inWhichElement){ case 0: result = eMonthElement; break; case 1: result = eDayElement; break; case 2: result = eYearElement; break; } break; case dmy: switch(inWhichElement){ case 0: result = eDayElement; break; case 1: result = eMonthElement; break; case 2: result = eYearElement; break; } break; case ymd: switch(inWhichElement){ case 0: result = eYearElement; break; case 1: result = eMonthElement; break; case 2: result = eDayElement; break; } break; case myd: switch(inWhichElement){ case 0: result = eMonthElement; break; case 1: result = eYearElement; break; case 2: result = eDayElement; break; } break; case dym: switch(inWhichElement){ case 0: result = eDayElement; break; case 1: result = eYearElement; break; case 2: result = eMonthElement; break; } break; case ydm: switch(inWhichElement){ case 0: result = eYearElement; break; case 1: result = eDayElement; break; case 2: result = eMonthElement; break; } break; default: SignalPStr_("\pBad date order!"); } return result; } // =========================================================================== // Ä IsLeapYear() // =========================================================================== // // All years divisible by four are leap years EXCEPT century years not divisible by 400. Boolean cDateControl::IsLeapYear(Int16 inYear){ double theYear = inYear, integralPart = 0; return (modf(theYear / 4, &integralPart) == 0.0 && (modf(theYear / 100, &integralPart) != 0.0 || modf(theYear / 400, &integralPart) == 0.0)); } #pragma mark - // =========================================================================== // Ä cClockControl() // =========================================================================== // // We set minimum time to 00:00:00, maximum to 23:59:59. // Note that time is stored as time_t, so I have a hunch there may be some issues here to think about. cClockControl::cClockControl(LStream* inStream) : cTimeControl(inStream){ *inStream >> mIsLive >> mShowSeconds; SetTimeMinValue(0, 0, 0); SetTimeMaxValue(23, 59, 59); } // =========================================================================== // Ä FinishCreateSelf() // =========================================================================== // // If we have descents in separator strings, this code may have to be adjusted. // Starts repeating if clock is live. void cClockControl::FinishCreateSelf(void){ cTimeControl::FinishCreateSelf(); // Take a chance on not having descents in separator strings TArrayIterator iterator(mRectArray); Rect dummyRect; while(iterator.Next(dummyRect)) mRectArray[iterator.GetCurrentIndex()].bottom -= mFinfo.descent; mFrameSize.height -= mFinfo.descent; // Adjust frame width for trailing "clock strings" Str32 pStr; UTextTraits::SetPortTextTraits(&mTextTraits); GetElementSeparatorString(GetElementCount() - 1, pStr); mFrameSize.width += ::StringWidth(pStr) + eElementRectSlopValue; if(mIsLive) StartRepeating(); else CreateLittleArrows(); } // =========================================================================== // Ä SetTimeValue() // =========================================================================== // // Set value from shorts for hours, minutes, seconds void cClockControl::SetTimeValue(Int16 inHours, Int16 inMinutes, Int16 inSeconds){ time_t now = time(NULL); tm theTime = *localtime(&now);; theTime.tm_hour = inHours; theTime.tm_min = inMinutes; theTime.tm_sec = inSeconds; cTimeControl::SetTimeValue(theTime); } // =========================================================================== // Ä GetTimeValue() // =========================================================================== // // Get value into shorts for hours, minutes, seconds void cClockControl::GetTimeValue(Int16& outHours, Int16& outMinutes, Int16& outSeconds){ tm theTime; cTimeControl::GetTimeValue(theTime); outHours = theTime.tm_hour; outMinutes = theTime.tm_min; outSeconds = theTime.tm_sec; } // =========================================================================== // Ä SetTimeMinValue() // =========================================================================== // // Set min value from shorts for hours, minutes, seconds void cClockControl::SetTimeMinValue(Int16 inHours, Int16 inMinutes, Int16 inSeconds){ time_t now = time(NULL); tm theTime = *localtime(&now);; theTime.tm_hour = inHours; theTime.tm_min = inMinutes; theTime.tm_sec = inSeconds; cTimeControl::SetTimeMinValue(theTime); } // =========================================================================== // Ä GetTimeMinValue() // =========================================================================== // // Get min value into shorts for hours, minutes, seconds void cClockControl::GetTimeMinValue(Int16& outHours, Int16& outMinutes, Int16& outSeconds){ tm theTime; cTimeControl::GetTimeMinValue(theTime); outHours = theTime.tm_hour; outMinutes = theTime.tm_min; outSeconds = theTime.tm_sec; } // =========================================================================== // Ä SetTimeMaxValue() // =========================================================================== // // Set value from shorts for hours, minutes, seconds void cClockControl::SetTimeMaxValue(Int16 inHours, Int16 inMinutes, Int16 inSeconds){ time_t now = time(NULL); tm theTime = *localtime(&now);; theTime.tm_hour = inHours; theTime.tm_min = inMinutes; theTime.tm_sec = inSeconds; cTimeControl::SetTimeMaxValue(theTime); } // =========================================================================== // Ä GetTimeMaxValue() // =========================================================================== // // Get value into shorts for hours, minutes, seconds void cClockControl::GetTimeMaxValue(Int16& outHours, Int16& outMinutes, Int16& outSeconds){ tm theTime; cTimeControl::GetTimeMaxValue(theTime); outHours = theTime.tm_hour; outMinutes = theTime.tm_min; outSeconds = theTime.tm_sec; } // =========================================================================== // Ä ObeyCommand() // =========================================================================== // // Disallow tab selection if we're a live clock. Boolean cClockControl::ObeyCommand( CommandT inCommand, void *ioParam){ Boolean result = false; switch(inCommand){ case msg_TabSelect: if (!mIsLive) result = cTimeControl::ObeyCommand(inCommand, ioParam); break; default: result = cTimeControl::ObeyCommand(inCommand, ioParam); } return result; } // =========================================================================== // Ä ObeyCommand() // =========================================================================== // // Disallow click selection if we're a live clock. void cClockControl::ClickSelf( const SMouseDownEvent &inMouseDown){ if (!mIsLive) cTimeControl::ClickSelf(inMouseDown); } //======================================================================== // Ä EnableSelf [protected, virtual] //======================================================================== // // Start updating the clock again, if it's live void cClockControl::EnableSelf(){ if (mIsLive) StartRepeating(); cTimeControl::EnableSelf(); } //======================================================================== // Ä DisableSelf [protected, virtual] //======================================================================== // // Stop updating the clock void cClockControl::DisableSelf(){ StopRepeating(); cTimeControl::DisableSelf(); } // =========================================================================== // Ä IncrementCurrentElement() // =========================================================================== // void cClockControl::IncrementCurrentElement(void){ if(GetCurrentElement() != eNoCurrentElement){ tm currTime = *localtime(&mTimeValue); time_t newTime; switch(GetCurrentElement()){ case eHourElement: currTime.tm_hour++; break; case eMinuteElement: currTime.tm_min++; break; case eSecondElement: currTime.tm_sec++; break; } newTime = mktime(&currTime); if(newTime < mMinTimeValue) newTime = mMinTimeValue; else if(newTime > mMaxTimeValue) newTime = mMaxTimeValue; if(newTime != mTimeValue){ mTimeValue = newTime; BroadcastValueMessage(); Draw(nil); } } } // =========================================================================== // Ä DecrementCurrentElement() // =========================================================================== // void cClockControl::DecrementCurrentElement(void){ if(GetCurrentElement() != eNoCurrentElement){ tm currTime = *localtime(&mTimeValue); time_t newTime; switch(GetCurrentElement()){ case eHourElement: currTime.tm_hour--; break; case eMinuteElement: currTime.tm_min--; break; case eSecondElement: currTime.tm_sec--; break; } newTime = mktime(&currTime); if(newTime < mMinTimeValue) newTime = mMinTimeValue; else if(newTime > mMaxTimeValue) newTime = mMaxTimeValue; if(newTime != mTimeValue){ mTimeValue = newTime; BroadcastValueMessage(); Draw(nil); } } } // =========================================================================== // Ä SpendTime() // =========================================================================== // Update clock if time strings changed void cClockControl::SpendTime( const EventRecord &inMacEvent){ if(mIsLive){ time_t oldTime = mTimeValue, newTime = time(NULL); tm oldTm = *localtime(&oldTime), newTm = *localtime(&newTime); // If any of the shown time strings should change, redraw the control if( (mShowSeconds && oldTm.tm_sec != newTm.tm_sec) || oldTm.tm_min != newTm.tm_min || oldTm.tm_hour != newTm.tm_hour){ SetTimeValueToNow(); Draw(NULL); } } } // =========================================================================== // Ä GetElementCount() // =========================================================================== // Return 3 or 2, depending on if we show seconds Int16 cClockControl::GetElementCount(void){ if(mShowSeconds) return 3; else return 2; } // =========================================================================== // Ä GetElementValue() // =========================================================================== Int16 cClockControl::GetElementValue(Int16 inWhichElement){ Int16 result = 0; ThrowIf_(inWhichElement >= GetElementCount()); tm* theTime = localtime(&mTimeValue); switch(inWhichElement){ case eHourElement: result = theTime->tm_hour; break; case eMinuteElement: result = theTime->tm_min; break; case eSecondElement: result = theTime->tm_sec; break; } return result; } // =========================================================================== // Ä GetElementValue() // =========================================================================== void cClockControl::SetElementValue(Int16 inWhichElement, Int16 inValue){ ThrowIf_(inWhichElement >= GetElementCount()); if(inValue < 0 || inValue > GetElementMaxIndex(inWhichElement)) return; tm currTime = *localtime(&mTimeValue); time_t newTime = 0; switch(inWhichElement){ case eHourElement: currTime.tm_hour = inValue; newTime = mktime(&currTime); if(newTime >= mMinTimeValue && newTime <= mMaxTimeValue); mTimeValue = newTime; break; case eMinuteElement: currTime.tm_min = inValue; newTime = mktime(&currTime); if(newTime >= mMinTimeValue && newTime <= mMaxTimeValue); mTimeValue = newTime; break; case eSecondElement: currTime.tm_sec = inValue; newTime = mktime(&currTime); if(newTime >= mMinTimeValue && newTime <= mMaxTimeValue); mTimeValue = newTime; break; } RedrawElement(inWhichElement, IsTarget()); } // =========================================================================== // Ä GetElementMaxIndex() // =========================================================================== Int16 cClockControl::GetElementMaxIndex(Int16 inWhichElement){ Int16 result = 0; ThrowIf_(inWhichElement >= GetElementCount()); switch(inWhichElement){ case eHourElement: result = 23; break; case eMinuteElement: result = 59; break; case eSecondElement: result = 59; break; } return result; } // =========================================================================== // Ä GetElementString() // =========================================================================== // We adjust hour string according to information in 'itl0' resource void cClockControl::GetElementString(Int16 inWhichElement, Int16 inIndex, Str32 outString){ tm* currTime = localtime(&mTimeValue); ThrowIf_(inIndex > GetElementMaxIndex(inWhichElement)); switch(inWhichElement){ case eHourElement: //AM/PM modes if( ((*mTimeFormat)->timeCycle == timeCycleZero || (*mTimeFormat)->timeCycle == timeCycle12) && currTime->tm_hour > 12){ currTime->tm_hour -= 12; } if((*mTimeFormat)->timeCycle == timeCycleZero && currTime->tm_hour == 12){ currTime->tm_hour = 0; } sprintf((char*)outString, ((*mTimeFormat)->timeFmt & hrLeadingZ) ? "%02d" : "%d", currTime->tm_hour); break; case eMinuteElement: sprintf((char*)outString, ((*mTimeFormat)->timeFmt & minLeadingZ) ? "%02d" : "%d", currTime->tm_min); break; case eSecondElement: sprintf((char*)outString, ((*mTimeFormat)->timeFmt & secLeadingZ) ? "%02d" : "%d", currTime->tm_sec); break; } c2pstr((char*)outString); } // =========================================================================== // Ä GetMaxElementStringWidth() // =========================================================================== Int16 cClockControl::GetMaxElementStringWidth(Int16 inWhichElement){ Str32 elementString = "\p00"; ThrowIf_(inWhichElement >= GetElementCount()); UTextTraits::SetPortTextTraits(&mTextTraits); return ::StringWidth(elementString); } // =========================================================================== // Ä GetElementSeparatorString() // =========================================================================== void cClockControl::GetElementSeparatorString(Int16 inAfterWhichElement, Str32 outString){ outString[0] = 0; if(inAfterWhichElement < GetElementCount() - 1){ outString[0] = 1; outString[1] = (*mTimeFormat)->timeSep; } // Last in control: Perhaps append AM/PM strings, etc. else if(inAfterWhichElement == GetElementCount() - 1){ tm* currTime = localtime(&mTimeValue); // If we're AM... if(currTime->tm_hour < 12 || (currTime->tm_hour == 12 && (*mTimeFormat)->timeCycle == timeCycle12)){ //...If we have AM string, load it for(int i = 0; i < 3; i++){ if((*mTimeFormat)->mornStr[i] != 0x00) outString[++outString[0]] = (*mTimeFormat)->mornStr[i]; } //...If we have "post clock"-text (like German "Uhr"), load it if((*mTimeFormat)->time1Suff != 0x00) outString[++outString[0]] = (*mTimeFormat)->time1Suff; if((*mTimeFormat)->time2Suff != 0x00) outString[++outString[0]] = (*mTimeFormat)->time2Suff; if((*mTimeFormat)->time3Suff != 0x00) outString[++outString[0]] = (*mTimeFormat)->time3Suff; if((*mTimeFormat)->time4Suff != 0x00) outString[++outString[0]] = (*mTimeFormat)->time4Suff; } else{ //...If we have AM string, load it for(int i = 0; i < 3; i++){ if((*mTimeFormat)->eveStr[i] != 0x00) outString[++outString[0]] = (*mTimeFormat)->eveStr[i]; } //...If we have "post clock"-text (like German "Uhr"), load it if((*mTimeFormat)->time5Suff != 0x00) outString[++outString[0]] = (*mTimeFormat)->time5Suff; if((*mTimeFormat)->time6Suff != 0x00) outString[++outString[0]] = (*mTimeFormat)->time6Suff; if((*mTimeFormat)->time7Suff != 0x00) outString[++outString[0]] = (*mTimeFormat)->time7Suff; if((*mTimeFormat)->time8Suff != 0x00) outString[++outString[0]] = (*mTimeFormat)->time8Suff; } } }