#include #include #include #include #include "cArrowsManager.h" // =========================================================================== // cArrowsManager.cpp Version 1.2 ©1998 Joakim Braun All rights reserved. // =========================================================================== // // CONTENTS: cArrowsManager // A subclass of LView and LListener that manages an edit field with an LLittleArrows. // When arrow is clicked, edit field is updated; when something is typed in // edit field, arrows value is updated. // cCmdrArrowsManager // Subclass of cArrowsManager, adding capability of trapping char_UpArrow and char_DownArrow // keypresses and simulate a click in upArrow/DownArrow. See NOTE below, though. // cCmdrArrowsManagerAttachment // Attachment used by cCmdrArrowsManager to filter key presses // REQUIRES: This uses my cBetterBroadcasterEditField subclass of LBroadcasterEditField that // calls BroadcastValueMessage() every time text changes. It should be // included in the archive containing this file. // HOW TO USE: Add the included CTYP to your PPOB file and use Constructor to set up the LLittleArrows // and cBetterBroadcasterEditField inside a cArrowsManager, filling in the property fields // as indicated below. If you want to use a cCmdrArrowsManager, set up a cArrowsManager // and change its Class ID field to 'arCM'. // The property fields are: // Text changed msg The message broadcast by the // cBetterBroadcasterEditField // when user has changed the text. // cBetterBroadcastingEditField ID Pane ID of the cBetterBroadcasterEditField. // Arrows changed msg The message broadcast by the // LLittleArrows when user has changed // its value. // LLittleArrows ID Pane ID of the LLittleArrows. // Broadcast changed value Boolean indicating whether the cArrowsManager // should also broadcast when value has changed. // Changed value message The message for cArrowsManager to broadcast // if "Broadcast changed value" is true. // NOTE: There΄s a bug (to my mind) in LGALittleArrowsImp::HotSpotAction() // with the result that one can have either the visual feedback OR the value change // when calling LLittleArrows::SimulateHotSpotClick(). A way to fix it is to add checks // for kControlUpButtonPart/kControlDownButtonPart in LGALittleArrowsImp::HotSpotAction(). // Without the fix, cCmdrArrowsManager won't work (including PP 1.8.1 at least). // cArrowsManager 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. // Change history: // 1.0 May 2, 1998 First release // 1.1 August 14, 1998 Cleaned up code, added parameter constructor. // 1.2 August 30, 1998 Added keyboard upArrow/downArrow support in new cCmdrArrowsManager class // =========================================================================== // € Constructor // =========================================================================== // cArrowsManager::cArrowsManager(void){ mTextChangedMsg = msg_Nothing, mArrowsChangedMsg = msg_Nothing, mChangedValueMsg = msg_Nothing; mEditFieldID = 0, mArrowsID = 0, mEditField = NULL, mLittleArrows = NULL; } // =========================================================================== // € Construct from parameters // =========================================================================== // cArrowsManager::cArrowsManager(PaneIDT inEditFieldID, PaneIDT inArrowsID, MessageT inTextChangedMsg, MessageT inArrowsChangedMsg, MessageT inChangedValueMsg, Boolean inBroadcastChangedValue){ mEditFieldID = inEditFieldID, mArrowsID = inArrowsID, mTextChangedMsg = inTextChangedMsg, mArrowsChangedMsg = inArrowsChangedMsg, mChangedValueMsg = inChangedValueMsg; mIsBroadcasting = inBroadcastChangedValue; mEditField = NULL, mLittleArrows = NULL; } // =========================================================================== // € Stream constructor // =========================================================================== // cArrowsManager::cArrowsManager(LStream* inStream) : LView(inStream){ *inStream >> mTextChangedMsg, *inStream >> mEditFieldID, *inStream >> mArrowsChangedMsg, *inStream >> mArrowsID; *inStream >> mIsBroadcasting; *inStream >> mChangedValueMsg; mEditField = NULL, mLittleArrows = NULL; } // =========================================================================== // € FinishCreateSelf() // Save off pointers to cBetterBroadcasterEditField and LLittleArrows, // and add ourselves as listener to them. // Signal if something goes wrong. Note that we don't check for NULL pointers later on, // so if arrows or edit field are deleted there's crash potential. // =========================================================================== // void cArrowsManager::FinishCreateSelf(void){ LView::FinishCreateSelf(); mEditField = dynamic_cast(FindPaneByID(mEditFieldID)); mLittleArrows = dynamic_cast(FindPaneByID(mArrowsID)); if(!mEditField || !mLittleArrows) SignalPStr_("\pcArrowsManager::FinishCreateSelf(): Bad pane cast!"); else{ mEditField->AddListener(this); mLittleArrows->AddListener(this); ArrowsChanged(mLittleArrows->GetValue()); } } // =========================================================================== // € ListenToMessage() // If we receive an mTextChangedMsg message or an mArrowsChangedMsg message, // act upon it. We might check for msg_BroadcasterDied, and set mEditField or mLittleArrows to NULL accordingly, // but never mind that now. // =========================================================================== // void cArrowsManager:: ListenToMessage(MessageT inMessage, void *ioParam){ if(inMessage == mTextChangedMsg){ Str255 str; mEditField->GetDescriptor(str); TextChanged(str); } else if(inMessage == mArrowsChangedMsg) ArrowsChanged(*(Int32*)ioParam); } // =========================================================================== // € ArrowsChanged() // Arrows have changed. Update edit field and call ManagerChanged(). // =========================================================================== // void cArrowsManager::ArrowsChanged(Int32 newValue){ Str255 str; ::NumToString(newValue, str); mEditField->SetDescriptor(str); mEditField->Draw(nil); mEditField->DontRefresh(); ManagerChanged(); } // =========================================================================== // € TextChanged() // Edit Field has changed. Update arrows and call ManagerChanged(). // =========================================================================== // void cArrowsManager::TextChanged(Str255 newText){ Int32 newNum = 0; Boolean wasListening = IsListening(); StringToNum(newText, &newNum); // Don't listen when arrows broadcast changed value // (Would be infinite loop) StopListening(); // If user has typed a bigger value than max value of arrows, // beep and reset value in edit field to max value of arrows. if(newNum > mLittleArrows->GetMaxValue()){ SysBeep(30); newNum = mLittleArrows->GetMaxValue(); LStr255 newNumStr(newNum); mEditField->SetDescriptor(newNumStr); } mLittleArrows->SetValue(newNum); ManagerChanged(); // Start to listen again, if we did when called if(wasListening) StartListening(); } // =========================================================================== // € ManagerChanged() // We have updated arrows or edit field. Broadcast mChangedValueMsg, if we should. // =========================================================================== // void cArrowsManager::ManagerChanged(void){ if(mChangedValueMsg != msg_Nothing && IsBroadcasting()){ Int32 arrowValue = mLittleArrows->GetValue(); BroadcastMessage(mChangedValueMsg, &arrowValue); } } #pragma mark - // =========================================================================== // € cCmdrArrowsManager() // Same as above, except we enable upArrow/downArrow // =========================================================================== // // =========================================================================== // € Constructors() // Same as cArrowsManager // =========================================================================== // cCmdrArrowsManager::cCmdrArrowsManager(void){} cCmdrArrowsManager::cCmdrArrowsManager( PaneIDT inEditFieldID, PaneIDT inArrowsID, MessageT inTextChangedMsg, MessageT inArrowsChangedMsg, MessageT inChangedValueMsg, Boolean inBroadcastChangedValue) : cArrowsManager(inEditFieldID, inArrowsID, inTextChangedMsg, inArrowsChangedMsg, inChangedValueMsg, inBroadcastChangedValue){} cCmdrArrowsManager::cCmdrArrowsManager(LStream* inStream) : cArrowsManager(inStream){} // =========================================================================== // € FinishCreateSelf() // Add an attachment to the edit field which will redirect upArrow and downArrow keyboard events to us // =========================================================================== // void cCmdrArrowsManager:: FinishCreateSelf(void){ cArrowsManager::FinishCreateSelf(); mEditField->AddAttachment(new cCmdrArrowsManagerAttachment(this)); } // =========================================================================== // € HandleKeyPress() // Trap upArrow/downArrow passed up from edit field. // See NOTE above for limitations of LGALittleArrowsImp::HotSpotAction(), and how to fix it. // =========================================================================== // Boolean cCmdrArrowsManager::HandleKeyPress( const EventRecord &inKeyEvent){ Char16 theChar = (Char16) (inKeyEvent.message & charCodeMask); Boolean result = true; switch(theChar){ case char_UpArrow: // This works only if you fix LGALittleArrowsImp::HotSpotAction() mLittleArrows->SimulateHotSpotClick(kControlUpButtonPart); break; case char_DownArrow: // This works only if you fix LGALittleArrowsImp::HotSpotAction() mLittleArrows->SimulateHotSpotClick(kControlDownButtonPart); break; default: result = LCommander::HandleKeyPress(inKeyEvent); } return result; } #pragma mark - // =========================================================================== // € cCmdrArrowsManagerAttachment() // An attachment that filters char_UpArrow and char_DownArrow typed in edit field // and passes them to cCmdrArrowsManager to let it simulate a click. // =========================================================================== cCmdrArrowsManagerAttachment::cCmdrArrowsManagerAttachment(cCmdrArrowsManager* inManager){ mArrowsManager = inManager; mMessage = msg_KeyPress; } // =========================================================================== // € ExecuteSelf() (Override from LAttachment) // Filter char_UpArrow and char_DownArrow typed in edit field // and pass them to cCmdrArrowsManager to let it simulate a click. // =========================================================================== void cCmdrArrowsManagerAttachment::ExecuteSelf(MessageT inMessage, void* ioParam){ EventRecord *pKeyEvt = (EventRecord*) ioParam; Char16 theChar = (Char16) (pKeyEvt->message & charCodeMask); if(inMessage == msg_KeyPress && (pKeyEvt->what == keyDown || pKeyEvt->what== autoKey) && (theChar == char_UpArrow || theChar == char_DownArrow)){ mArrowsManager->HandleKeyPress(*pKeyEvt); SetExecuteHost(false); } else SetExecuteHost(true); }