>> Another possible improvement (not sure if this is HIG approved, >> but it would offer the user more feedback) would be to install a >> mouse tracking area to change the cursor to a pointing hand (i.e. >> SetThemeCursor( _kThemePointingHandCursor )) when over the URL. > > Yes, this refinement could be added. Personally I find the > (improved) CarbonEvent method sufficiently convincing even without > a cursor change. Steve Van Voorst's modification changes the cursor. > I was going to work on mouse tracking regions (10.2), but note that > they have already been "deprecated" in favor of HIView based > tracking regions in 10.4. Friendly rivalry motivated me to seek out that HIView tracking thingummy (whose crucial part turns out to be HIViewNewTrackingArea) and add it to my previously posted clickable URL demo. Robert P. '--------------------- /* Clickable URL, using the tracking method of Apple's QA1380. Robert P. April 2007 Installing a handler for the appropriate CarbonEvents has advantages over old programming techniques for clickable URLs - a click on the link is tracked, and can therefore be cancelled by moving the mouse away before releasing the button. - any number of clickable URLs can be set up without change to the rest of the program. A call to MakeClickableURL and you're done. - nothing needs disposing when the control or window is closed. This version - allows link text to be different from the URL. - displays the link in red while it is tracked. - shows how to specify the font/size of the link. - changes cursor when over link (in 10.4 or later). */ include "Tlbx HIView.incl" include "Tlbx CoreGraphics.incl" include "Tlbx ControlDefinitions.incl" end globals _kMyURLTextTag = _"URLT" // we get here when any link is clicked local fn DoURLClick( c as ControlRef ) '~'1 dim as Str255 url // retrieve the url string from the control call GetControlProperty( c, 0, _kMyURLTextTag, 256, #0, @url ) // The 'open' command is a handy way to open URLs as well as files open "unix", 222, "open " + url close 222 end fn local fn InstallClickableURLHandler( c as ControlRef ) '~'1 dim as EventTypeSpec myEvents(4) dim as long nEvents begin globals dim as proc sURLEventUPP // 'static' var end globals // The events we intercept are all for the control // itself (not its window or other enclosing HIViews). nEvents = 3 myEvents.eventClass(0) = _kEventClassControl myEvents.eventKind(0) = _kEventControlHitTest myEvents.eventClass(1) = _kEventClassControl myEvents.eventKind(1) = _kEventControlHit myEvents.eventClass(2) = _kEventClassControl myEvents.eventKind(2) = _kEventControlDraw /* Prepare events and TrackingArea for cursor change. This refinement requires 10.4 or later. On older systems the cursor simply won't change. */ long if ( system( _sysVers ) >= 1040 ) nEvents = 5 myEvents.eventClass(3) = _kEventClassControl myEvents.eventKind(3) = _kEventControlTrackingAreaEntered myEvents.eventClass(4) = _kEventClassControl myEvents.eventKind(4) = _kEventControlTrackingAreaExited call HIViewNewTrackingArea( c, 0, 0, 0, #0 ) end if // allocate a 'universal procedure pointer' pointing to our handler enterproc long if ( sURLEventUPP == 0 ) sURLEventUPP = fn NewEventHandlerUPP( [proc "ClickableURLHandler" + _FBprocToProcPtrOffset] ) end if // install the handler with the array of events end fn = fn InstallEventHandler( fn GetControlEventTarget( c ), sURLEventUPP, nEvents, @myEvents(0), #0, #0 ) long if 0 "ClickableURLHandler" enterproc fn ClickableURLHandler( nextHandler as EventHandlerCallRef, theEvent as EventRef, userData as ptr ) '~'1 dim as OSStatus result, ignore dim as ControlRef @ c dim as HIRect bounds dim as HIPoint pt dim as ControlPartCode @ part dim as ControlFontStyleRec cfs result = _eventNotHandledErr call GetEventParameter( theEvent, _kEventParamDirectObject, _typeControlRef, #0, sizeof( c ), #0, @c ) select fn GetEventKind( theEvent ) case _kEventControlHitTest // static text control requires this handler in order to track the mouse call HIViewGetBounds( c, @bounds ) call GetEventParameter( theEvent, _kEventParamMouseLocation, _typeHIPoint, #0, sizeof( pt ), #0, @pt ) part = _kControlNoPart // if mouse over the control, send kControlButtonPart if ( fn CGRectContainsPoint( bounds, pt ) ) then part = _kControlButtonPart call SetEventParameter( theEvent, _kEventParamControlPart, _typeControlPartCode, sizeof( part ), @part ) result = _noErr // we handled the event case _kEventControlDraw // Draw the link text either visibly-clickable (blue) or pressed (red). // Retrieve the existing ControlFontStyleRec ignore = fn GetControlData( c, _kControlEditTextPart, _kControlStaticTextStyleTag, sizeof( cfs ), @cfs, #0 ) // set the foreColor field long if ( fn GetControlHilite( c ) == _kControlButtonPart ) cfs.foreColor.red = 0xffff // pressed link red cfs.foreColor.blue = 0 xelse cfs.foreColor.red = 0 cfs.foreColor.blue = 0xeeee // special "url blue" end if cfs.foreColor.green = 0 ignore = fn SetControlFontStyle( c, cfs ) // let the default handler (i.e. The System) draw the text result = fn CallNextEventHandler( nextHandler, theEvent ) case _kEventControlTrackingAreaEntered // self-explanatory call SetThemeCursor( _kThemePointingHandCursor ) case _kEventControlTrackingAreaExited // self-explanatory call SetThemeCursor( _kThemeArrowCursor ) case _kEventControlHit // act on a completed click-and-release, like a button click fn DoURLClick( c ) result = _noErr // we handled the event end select exitproc = result end if local fn MakeClickableURL( btnNum as long, linkTxt as Str255, url as Str255, r as ^Rect ) '~'1 dim as OSStatus ignore dim as Rect rCopy dim as ControlFontStyleRec cfs dim as ControlRef c rCopy = r // make the text control appearance button btnNum,,,,,, @rCopy, _kControlStaticTextProc cfs.flags = _kControlUseFaceMask _kControlUseForeColorMask cfs.style = _ulineBit% // underlined // uncomment to specify font & size (Courier 12 as a demo) /* cfs.flags = cfs.flags || _kControlUseFontMask || _kControlUseSizeMask cfs.size = 12 cfs.font = _courier */ c = button&( btnNum ) ignore = fn SetControlFontStyle( c, cfs ) def SetButtonTextString( btnNum, linkTxt ) call SetControlProperty( c, 0, _kMyURLTextTag, 256, @url ) // install Carbon event handler on the control fn InstallClickableURLHandler( c ) end fn dim as Str255 url dim as Rect r window 1, "Clickable URLs" SetRect( r, 20, 15, 380, 35 ) url = "http://developer.apple.com/qa/qa2004/qa1380.html" fn MakeClickableURL( 1, url, url, r ) // link text and URL are the same SetRect( r, 20, 55, 130, 75 ) fn MakeClickableURL( 2, "reply to author", "mailto:nobody@...", r ) SetRect( r, 20, 95, 110, 115 ) fn MakeClickableURL( 3, "ftp to Apple", "ftp://ftp.apple.com", r ) do HandleEvents until 0 '---------------------