void IGraphicsCarbon::CreateTextEntry(IControl* pControl, IText* pText, IRECT* pTextRect, const char* pString, IParam* pParam) { if (!pControl || mTextEntryView || !mIsComposited) return; // Only composited carbon supports text entry WindowRef window = mWindow; TXNFrameOptions txnFrameOptions = kTXNMonostyledTextMask | kTXNDisableDragAndDropMask | kTXNSingleLineOnlyMask; TXNObject txnObject = 0; TXNFrameID frameID = 0; TXNObjectRefcon txnObjectRefCon = 0; HIRect rct; HIViewGetFrame(this->mView, &rct); HIViewRef contentView; HIViewFindByID (HIViewGetRoot(this->mWindow), kHIViewWindowContentID, &contentView); HIViewConvertRect(&rct, HIViewGetSuperview((HIViewRef)this->mView), contentView); Rect rect = { rct.origin.y + pTextRect->T, rct.origin.x + pTextRect->L, rct.origin.y + pTextRect->B + 1, rct.origin.x + pTextRect->R + 1 }; if (TXNNewObject(NULL, window, &rect, txnFrameOptions, kTXNTextEditStyleFrameType, kTXNSingleStylePerTextDocumentResType, kTXNMacOSEncoding, &txnObject, &frameID, txnObjectRefCon) == noErr) { TXNSetFrameBounds(txnObject, rect.top, rect.left, rect.bottom, rect.right, frameID); mTextEntryView = txnObject; // Set the text to display by defualt TXNSetData(mTextEntryView, kTXNTextData, pString, strlen(pString)/*+1*/, kTXNStartOffset, kTXNEndOffset); // center aligned text has problems with uneven string lengths RGBColor tc; tc.red = pText->mTextEntryFGColor.R * 257; tc.green = pText->mTextEntryFGColor.G * 257; tc.blue = pText->mTextEntryFGColor.B * 257; TXNBackground bg; bg.bgType = kTXNBackgroundTypeRGB; bg.bg.color.red = pText->mTextEntryBGColor.R * 257; bg.bg.color.green = pText->mTextEntryBGColor.G * 257; bg.bg.color.blue = pText->mTextEntryBGColor.B * 257; TXNSetBackground(mTextEntryView, &bg); // Set justification SInt16 justification; Fract flushness; switch ( pText->mAlign ) { case IText::kAlignCenter: justification = kTXNCenter; // seems to be buggy wrt dragging and alignement with uneven string lengths flushness = kATSUCenterAlignment; break; case IText::kAlignFar: justification = kTXNFlushRight; flushness = kATSUEndAlignment; break; case IText::kAlignNear: default: justification = kTXNFlushLeft; flushness = kATSUStartAlignment; break; } TXNControlTag controlTag[1]; TXNControlData controlData[1]; controlTag[0] = kTXNJustificationTag; controlData[0].sValue = justification; TXNSetTXNObjectControls(mTextEntryView, false, 1, controlTag, controlData); ATSUFontID fontid = kATSUInvalidFontID; if (pText->mFont && pText->mFont[0]) { ATSUFindFontFromName(pText->mFont, strlen(pText->mFont), kFontFullName /* kFontFamilyName? */ , (FontPlatformCode)kFontNoPlatform, kFontNoScriptCode, kFontNoLanguageCode, &fontid); } // font (NOT working) TXNTypeAttributes attributes[3]; attributes[0].tag = kATSUFontTag; attributes[0].size = sizeof(ATSUFontID); attributes[0].data.dataPtr = &fontid; // size attributes[1].tag = kTXNQDFontSizeAttribute; attributes[1].size = kTXNFontSizeAttributeSize; attributes[1].data.dataValue = pText->mSize << 16; // color attributes[2].tag = kTXNQDFontColorAttribute; attributes[2].size = kTXNQDFontColorAttributeSize; attributes[2].data.dataPtr = &tc; // Finally set the attributes TXNSetTypeAttributes(mTextEntryView, 3, attributes, kTXNStartOffset, kTXNEndOffset); // Ensure focus remains consistent SetUserFocusWindow(window); AdvanceKeyboardFocus(window); // Set the focus to the edit window TXNFocus(txnObject, true); TXNSelectAll(mTextEntryView); TXNShowSelection(mTextEntryView, true); // The event types const static EventTypeSpec eventTypes[] = { { kEventClassMouse, kEventMouseMoved }, { kEventClassMouse, kEventMouseDown }, { kEventClassMouse, kEventMouseUp }, { kEventClassMouse, kEventMouseWheelMoved }, { kEventClassWindow, kEventWindowClosed }, { kEventClassWindow, kEventWindowDeactivated }, { kEventClassWindow, kEventWindowFocusRelinquish }, { kEventClassKeyboard, kEventRawKeyDown }, { kEventClassKeyboard, kEventRawKeyRepeat } }; // Install the event handler InstallWindowEventHandler(window, TextEntryHandler, GetEventTypeCount(eventTypes), eventTypes, this, &mTextEntryHandler); mEdControl = pControl; mEdParam = pParam; mTextEntryRect = *pTextRect; } }
/* YASTControlCarbonEventHandler defines the main entry point for all of the carbon event handlers installed for the YASTControl. */ static pascal OSStatus YASTControlCarbonEventHandler( EventHandlerCallRef myHandler, EventRef event, void* userData) { #pragma unused ( myHandler ) OSStatus err, returnedResult; YASTControlVarsPtr varsp; UInt32 eclass, ekind; /* set up locals */ eclass = GetEventClass(event); ekind = GetEventKind(event); varsp = (YASTControlVarsPtr) userData; returnedResult = eventNotHandledErr; /* dispatch the event by class*/ switch (eclass) { case kEventClassWindow: if ( ekind == kEventWindowCursorChange ) { Point where; UInt32 modifiers; Boolean cursorWasSet; /* get the mouse position */ err = GetEventParameter( event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(where), NULL, &where); if (err == noErr) { err = GetEventParameter( event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers); if (err == noErr) { SetPort(varsp->fGrafPtr); GlobalToLocal(&where); if (PtInRect(where, &varsp->fRBounds)) { err = HandleControlSetCursor( varsp->fControl, where, modifiers, &cursorWasSet); if (err != noErr) cursorWasSet = false; if ( ! cursorWasSet ) InitCursor(); returnedResult = noErr; } } } } break; case kEventClassMouse: /* handle mouse downs in the control, but only if the control is in focus. */ if ( ekind == kEventMouseDown ) { EventRecord outEvent; if ( varsp->fInFocus ) { if (ConvertEventRefToEventRecord( event, &outEvent)) { TXNClick( varsp->fTXNObject, &outEvent); } returnedResult = noErr; } } break; case kEventClassTextInput: if ( ekind == kEventUnicodeForKeyEvent && varsp->fTabMovesFocus) { UniChar mUnicodeText[8]; UInt32 bytecount, nchars; /* get the character */ err = GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText, NULL, sizeof(mUnicodeText), &bytecount, (char*) mUnicodeText); if ((err == noErr) && (bytecount >= sizeof(UniChar))) { nchars = ( bytecount / sizeof(UniChar) ); /* if it's not the tab key, forget it... */ if ( mUnicodeText[0] == '\t' ) { EventRef rawKeyEvent; Boolean shiftDown; /* is the shift key held down? */ shiftDown = false; err = GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(rawKeyEvent), NULL, &rawKeyEvent); if (err == noErr) { UInt32 modifiers; err = GetEventParameter(rawKeyEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers); if (err == noErr) { shiftDown = ( (modifiers & shiftKey) != 0 ); } } /* advance the keyboard focus, backwards if shift is down */ if (shiftDown) ReverseKeyboardFocus( varsp->fWindow ); else AdvanceKeyboardFocus( varsp->fWindow ); /* noErr lets the CEM know we handled the event */ returnedResult = noErr; } } } break; case kEventClassControl: switch (ekind) { case kEventControlSetFocusPart: { ControlPartCode thePart; err = GetEventParameter(event, kEventParamControlPart, typeControlPartCode, NULL, sizeof(thePart), NULL, &thePart); if (err == noErr) { switch (thePart) { default: case kControlFocusNoPart: /* turn off focus */ if ( varsp->fInFocus ) { TXNFocus( varsp->fTXNObject, false); varsp->fInFocus = false; } thePart = kControlFocusNoPart; break; case kYASTControlOnlyPart: /* turn on focus */ if ( ! varsp->fInFocus ) { TXNFocus( varsp->fTXNObject, true); varsp->fInFocus = true; } thePart = kYASTControlOnlyPart; break; case kControlFocusPrevPart: /* toggle focus on/off */ case kControlFocusNextPart: varsp->fInFocus = ! varsp->fInFocus; TXNFocus( varsp->fTXNObject, varsp->fInFocus); thePart = (varsp->fInFocus ? kYASTControlOnlyPart : kControlFocusNoPart); break; } SetPort(varsp->fGrafPtr); /* calculate the next highlight state */ SetTextActivation(varsp, varsp->fIsActive && varsp->fInFocus); /* redraw the text fram and focus rectangle to indicate the new focus state */ DrawThemeEditTextFrame(&varsp->fRTextOutline, varsp->fIsActive ? kThemeStateActive: kThemeStateInactive); RedrawFocusOutline(varsp); } /* pass back the foocus part code */ err = SetEventParameter( event, kEventParamControlPart, typeControlPartCode, sizeof(thePart), &thePart); returnedResult = err; } break; case kEventControlHitTest: /* this event does not necessairly mean that a mouse click has occured. Here we are simply testing to see if a particular point is located inside of the control. More complicated controls would return different part codes for different parts of themselves; but, since YASTControls only advertise one part, the hit test here is more or less a boolean test. */ { ControlPartCode thePart; Point where; err = GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(where), NULL, &where); if (err == noErr) { if (PtInRect(where, &varsp->fRTextArea)) { thePart = kYASTControlOnlyPart; } else thePart = 0; err = SetEventParameter( event, kEventParamControlPart, typeControlPartCode, sizeof(thePart), &thePart); } returnedResult = err; } break; case kEventControlClick: /* here we handle focus switching on the control. Actual tracking of mouse down events in the control is performed in the kEventClassMouse mouse down handler above. */ if ( ! varsp->fInFocus ) { SetKeyboardFocus(varsp->fWindow, varsp->fControl, kYASTControlOnlyPart); returnedResult = noErr; } break; case kEventControlBoundsChanged: /* we moved, or switched size - recalculate our rectangles */ { Rect bounds; err = GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(bounds), NULL, &bounds); if (err == noErr) { YASTControlCalculateBounds(varsp, &bounds); TXNSetFrameBounds( varsp->fTXNObject, varsp->fRTextArea.top, varsp->fRTextArea.left, varsp->fRTextArea.bottom, varsp->fRTextArea.right, varsp->fTXNFrameID); } } break; case kEventControlActivate: case kEventControlDeactivate: { SetPort(varsp->fGrafPtr); varsp->fIsActive = (ekind == kEventControlActivate); SetTextActivation(varsp, varsp->fIsActive && varsp->fInFocus); /* redraw the frame */ DrawThemeEditTextFrame(&varsp->fRTextOutline, varsp->fIsActive ? kThemeStateActive: kThemeStateInactive); RedrawFocusOutline(varsp); returnedResult = noErr; } break; case kEventControlDraw: /* redraw the control */ SetPort(varsp->fGrafPtr); /* update the text region */ TXNDraw(varsp->fTXNObject, NULL); /* restore the drawing environment */ /* draw the text frame and focus frame (if necessary) */ DrawThemeEditTextFrame(&varsp->fRTextOutline, varsp->fIsActive ? kThemeStateActive: kThemeStateInactive); RedrawFocusOutline(varsp); returnedResult = noErr; break; case kEventControlSetCursor: /* cursor adjustment */ { SetPortWindowPort(varsp->fWindow); TXNAdjustCursor( varsp->fTXNObject, varsp->fRTextOutlineRegion); returnedResult = noErr; } break; case kEventControlDispose: /* RemoveEventHandler(varsp->fControlEvents); -- this call has been left out on purpose because it will be called automatically when the control is disposed. */ RemoveEventHandler(varsp->fWindowEvents); TXNDeleteObject(varsp->fTXNObject); DisposeRgn(varsp->fRTextOutlineRegion); free(varsp); /* returnedResult = noErr; -- this has been left out on purpose because we want the dispatching to continue and dispose of the control */ break; case kEventControlSetData: { ResType inTagName; Size inBufferSize; void * inBuffer; err = GetEventParameter( event, kEventParamControlDataTag, typeEnumeration, NULL, sizeof(inTagName), NULL, &inTagName); if (err == noErr) { err = GetEventParameter( event, kEventParamControlDataBuffer, typePtr, NULL, sizeof(inBuffer), NULL, &inBuffer); if (err == noErr) { err = GetEventParameter( event, kEventParamControlDataBufferSize, typeLongInteger, NULL, sizeof(inBufferSize), NULL, &inBufferSize); if (err == noErr) { err = YASTControlSetData(varsp, inTagName, inBuffer, inBufferSize); } } } returnedResult = err; } break; case kEventControlGetData: { ResType inTagName; Size inBufferSize, outBufferSize; void * inBuffer; err = GetEventParameter( event, kEventParamControlDataTag, typeEnumeration, NULL, sizeof(inTagName), NULL, &inTagName); if (err == noErr) { err = GetEventParameter( event, kEventParamControlDataBuffer, typePtr, NULL, sizeof(inBuffer), NULL, &inBuffer); if (err == noErr) { err = GetEventParameter( event, kEventParamControlDataBufferSize, typeLongInteger, NULL, sizeof(inBufferSize), NULL, &inBufferSize); if (err == noErr) { err = YASTControlGetData(varsp, inTagName, inBuffer, inBufferSize, &outBufferSize); if (err == noErr) { err = SetEventParameter( event, kEventParamControlDataBufferSize, typeLongInteger, sizeof(outBufferSize), &outBufferSize); } } } } returnedResult = err; } break; } break; case kEventClassCommand: if ( ekind == kEventProcessCommand ) { HICommand command; err = GetEventParameter( event, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command); if (err == noErr) { switch (command.commandID) { case kHICommandUndo: TXNUndo(varsp->fTXNObject); returnedResult = noErr; break; case kHICommandRedo: TXNRedo(varsp->fTXNObject); returnedResult = noErr; break; case kHICommandCut: ClearCurrentScrap(); err = TXNCut(varsp->fTXNObject); if (err == noErr) err = TXNConvertToPublicScrap(); returnedResult = err; break; case kHICommandCopy: ClearCurrentScrap(); err = TXNCopy(varsp->fTXNObject); if (err == noErr) err = TXNConvertToPublicScrap(); returnedResult = err; break; case kHICommandPaste: err = TXNConvertFromPublicScrap(); if (err == noErr) err = TXNPaste(varsp->fTXNObject); returnedResult = err; break; case kHICommandClear: err = TXNClear(varsp->fTXNObject); returnedResult = err; break; case kHICommandSelectAll: err = TXNSetSelection(varsp->fTXNObject, kTXNStartOffset, kTXNEndOffset); returnedResult = err; break; } } } else if ( ekind == kEventCommandUpdateStatus ) { HICommand command; TXNOffset oStartOffset, oEndOffset; TXNActionKey oActionKey; err = GetEventParameter( event, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command); if ((err == noErr) && ((command.attributes & kHICommandFromMenu) != 0)) { switch (command.commandID) { case kHICommandUndo: if (TXNCanUndo(varsp->fTXNObject, &oActionKey)) { EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); } else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); returnedResult = noErr; break; case kHICommandRedo: if (TXNCanRedo(varsp->fTXNObject, &oActionKey)) { EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); } else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); returnedResult = noErr; break; case kHICommandCut: case kHICommandCopy: case kHICommandClear: TXNGetSelection(varsp->fTXNObject, &oStartOffset, &oEndOffset); if (oStartOffset != oEndOffset) { EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); } else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); returnedResult = noErr; break; case kHICommandPaste: if (TXNIsScrapPastable()) { EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); } else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); returnedResult = noErr; break; case kHICommandSelectAll: if(TXNDataSize(varsp->fTXNObject) > 0) { EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); } else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); returnedResult = noErr; break; } } } break; } return returnedResult; }