Using a string as Hyperlink in NSTextview

Hi,

after years of AppleScripting I started with ASOC a few weeks ago. Please excuse, if my questions are a little bit “beginner-like” regarding to ASOC…

Inside my actual project I calculate a URL (e.g.: http://myowndomain.com/1234567/) and want to output it as a clickable hyperlink (from an NSTextField). I found some older solutions here in the forum:

http://74.218.91.186/viewtopic.php?id=32623
http://macscripter.net/viewtopic.php?id=29247

and a reference inside the developer documentation:

http://developer.apple.com/library/mac/#qa/qa1487/_index.html.

But I can’t figure it out. Using the “DSClickableURLTextField Class” (adding .h and .m to my project) I can choose the new “DSClickableURLTextField” in the Identity Inspector. But if I compile the project I get 6 “Semantic Issues” inside the DSClickableURLTextField.m. Nevertheless the project compiles, my program works and assigns the URL to the field. But it isn’t clickable…

Seems that I have to add some additional code to the AppDelegate.applescript … or this solutions are incompatible with Xcode 4?

I hope for your help! Thanks in advance.

Rebewslin

Model: MacBook 2.1 3/500
AppleScript: XCode 4.2.1
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

You could use a NSTextView (usually embedded inside an NSScrollView) and activate the Smart Links option. It would detect links and make them editable automatically, at least I think.

NSTextView has a method named setAutomaticLinkDetectionEnabled: to enable it on and off, and even a toggle method called toggleAutomaticLinkDetection:.

I wouldn’t worry too much about the errors in semantics, unless they cause problems by using deprecated methods. Can you share part of your code (ASOC part) so we help a bit more?

EDIT: I just noticed: you say you changed the class of the text field for the DSClickableURLTextField’s class in your XIB file? You still need to use the provided methods to set the URL, like setStringValue: or setAttributedStringValue:. And also, the error in semantics is actually a missing method called textField:openURL: that should do exactly what you want, but it is not present in the .m file, even though it is declared in the .h. Strange…

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

And I just found this, could be helpful. It explains how to add a clickable URL category to NSAttributedString class so it can be added to any text field or text view.

http://developer.apple.com/library/mac/#qa/qa1487/_index.html

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

Ok, found the solution. In Xcode, go to File > New File, then select the Cocao item in Mac OS X, then select Objective-C category on the right. Click next, don’t worry about the subclass thing and just add the files to your project. Now in the .h file put this in, replacing all the code:

#import <Foundation/Foundation.h>

@interface NSAttributedString (Hyperlink)

+(id)hyperlinkFromString:(NSString*)inString withURL:(NSURL*)aURL;

@end

and in the .m file put this in, again replacing all the code:

#import "NSAttributedString+Hyperlink.h"

@implementation NSAttributedString (Hyperlink)

+(id)hyperlinkFromString:(NSString*)inString withURL:(NSURL*)aURL
{
    NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString: inString];
    NSRange range = NSMakeRange(0, [attrString length]);
    
    [attrString beginEditing];
    [attrString addAttribute:NSLinkAttributeName value:[aURL absoluteString] range:range];
    
    // make the text appear in blue
    [attrString addAttribute:NSForegroundColorAttributeName value:[NSColor grayColor] range:range];
    // make the text appear the default system font at a point size of your liking. Or comment the next line with // and uncomment the next one if you'd like to set the font and size. It's one or the other.
    [attrString addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:28] range:range];
    // make the text appear in the font and size of your liking. Uncomment the next line if you want to set the font and size and comment the other line.
    //[attrString addAttribute:NSFontAttributeName value:[NSFont fontWithName:@"Courier" size:12] range:range];
    
    // next make the text appear with an underline
    [attrString addAttribute:
     NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:range];
    
    [attrString endEditing];
    
    return [attrString autorelease];
}

@end

Then in your ASOC code, you can create a new URL object, a new attributed string object and then set the contents of the text field to this new attributed string. Works like a charm. Below is just a simple demonstration:

	on applicationWillFinishLaunching_(aNotification)
        --Assumes you have a textField property that is connected to an NSTextField in your main window.
        --these attributes are needed for the URL to be clickable.
        textField's setAllowsEditingTextAttributes_(true)
        textField's setSelectable_(true)
        
        
        set theUrl to current application's NSURL'S URLWithString_("http://www.apple.com") --create an URL object that's gonna be used in the next line after the next
        set stringWithURL to current application's NSMutableAttributedString's alloc()'s init() --create a mutable attributed string, because on the next line we're gonna add the URL to it.
        stringWithURL's appendAttributedString_(current application's NSAttributedString's hyperlinkFromString_withURL_("Apple Computer", theUrl)) --create a new attributed string with a title and a url object then append to the NSMutableAttributedString object defined in the previous line
        
        textField's setAttributedStringValue_(stringWithURL) --set the text field's contents to the attributed string, which is a clickable URL
        
	end applicationWillFinishLaunching_

That should do the trick. Enjoy!

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

Hi leonsimard,

thanks for your answer!

I’ve tried this before. It works only in parts… Only if I edit the textfield after the app is started it recognizes an URL in the string and shows it as a hyperlink. Before an edit it is only an unclickable string :frowning:

Of course, here’s the code from my AppDelegate.applescript:


script AppDelegate
    property parent : class "NSObject"
    property myownURL : missing value
    
    on doAction_(sender)--connected to a button

        tell mainWindow to displayIfNeeded()
        
        
        --for testing, fill the textfield with an string
        
        set my myownURL to "http://www.macscripter.net/"
        
         tell mainWindow to displayIfNeeded()
        
    end doAction_

Yes, that is what I assume: I have to use the provided methods… but how I have to do this? These are the errors:

http://dl.dropbox.com/u/154942/error.jpg (can’t copy the text in Xcode)

Rebewslin

Edit: Have seen that you have posted again in the mean time, will try it and post here! Thank you!

Just look at my previous to last post, I found you an elegant solution with NSAttributedString.

You can even set the font and size optionally. Just read the instructions in the comments in the ObjC files…

:slight_smile:

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

Just a little help with your ASOC code, see the comments/questions inside the code:

script AppDelegate
property parent : class "NSObject"
property myownURL : missing value --To what is this connected? Your text field? If so, then you can't use the "set my myownURL to" to set the URL

on doAction_(sender)--connected to a button

tell mainWindow to displayIfNeeded() --this extra refresh of the window is useless unless there is something to refresh. The second one helps, but not absolutely necessary.


--for testing, fill the textfield with an string

set my myownURL to "http://www.macscripter.net/" --If you try to tell the text field to change it's contents to this URL, then this is a different result. What you need is this:
myownURL's setStringValue_("http://www.macscripter.net/")

mainWindow's setNeedsDisplay_(true) --helps to use that sometimes with the next method.
mainWindow's displayIfNeeded() --like I said, helps, but not absolutely necessary. Changed the spelling, makes for less typing this way ;-)

end doAction_

Also, I do not see where your mainWindow property is declared in all this. Did you forget to include it in your post perhaps? Because it is required and also has to be linked in your XIB to the window for any method to work.

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

And just because I like flexibility, here’s an updated version that will let you set the font, point size and link color from your ASOC code, optionally. You can still use the default one too.

NSAttributedString+Hyperlink.h

#import <Foundation/Foundation.h>

@interface NSAttributedString (Hyperlink)

+(id)hyperlinkFromString:(NSString*)inString withURL:(NSURL*)aURL;
+(id)hyperlinkFromString:(NSString*)inString withURL:(NSURL*)aURL linkColor:(NSColor*)aColor usingFontNamed:(NSString*)aFont andPointSize:(CGFloat)pointSize;

@end

NSAttributedString+Hyperlink.m

#import "NSAttributedString+Hyperlink.h"

@implementation NSAttributedString (Hyperlink)

+(id)hyperlinkFromString:(NSString*)inString withURL:(NSURL*)aURL
{
    NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString: inString];
    NSRange range = NSMakeRange(0, [attrString length]);
    
    [attrString beginEditing];
    [attrString addAttribute:NSLinkAttributeName value:[aURL absoluteString] range:range];
    
    // make the text appear in blue
    [attrString addAttribute:NSForegroundColorAttributeName value:[NSColor darkGrayColor] range:range];
    // make the text appear the default system font at a point size of your liking
    //[attrString addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:28] range:range];
    // make the text appear in the font and size of your liking
    [attrString addAttribute:NSFontAttributeName value:[NSFont fontWithName:@"Arial" size:10] range:range];
    
    // next make the text appear with an underline
    [attrString addAttribute:
     NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:range];
    
    [attrString endEditing];
    
    return [attrString autorelease];
}

+(id)hyperlinkFromString:(NSString*)inString withURL:(NSURL*)aURL linkColor:(NSColor*)aColor usingFontNamed:(NSString*)aFont andPointSize:(CGFloat)pointSize
{
    NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString: inString];
    NSRange range = NSMakeRange(0, [attrString length]);
    
    [attrString beginEditing];
    [attrString addAttribute:NSLinkAttributeName value:[aURL absoluteString] range:range];
    
    // make the text appear in blue
    [attrString addAttribute:NSForegroundColorAttributeName value:aColor range:range];
    // make the text appear in the font and size of your liking
    [attrString addAttribute:NSFontAttributeName value:[NSFont fontWithName:aFont size:pointSize] range:range];
    
    // next make the text appear with an underline
    [attrString addAttribute:
     NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:range];
    
    [attrString endEditing];
    
    return [attrString autorelease];
}

@end

and the ASOC code:

on applicationWillFinishLaunching_(aNotification)
        
        textField's setAllowsEditingTextAttributes_(true)
        textField's setSelectable_(true)
        
        
        set theUrl to current application's NSURL'S URLWithString_("http://www.apple.com")
        set stringWithURL to current application's NSMutableAttributedString's alloc()'s init()
        set linkColor to current application'S NSColor's redColor() --define a color to use. Could be anything. See the docs for NSColor under "Creating an NSColor with Preset Components" for the basic ones
        set hyperlinkString to current application's NSAttributedString's hyperlinkFromString_withURL_linkColor_usingFontNamed_andPointSize_("Apple Computer", theUrl, linkColor, "Arial-Black", 14) --sending the font name as a string for simplification. Needs to be typed like shown in the Font Book app, under info, PostScript Name. In this case "Arial-Black" and not "Arial Black".
        stringWithURL's appendAttributedString_(hyperlinkString)

        --the previous way, if you still want to use it:
        --set hyperlinkString to current application's NSAttributedString's hyperlinkFromString_withURL_("Apple Computer", theUrl)
        --stringWithURL's appendAttributedString_(hyperlinkString)
        
        textField's setAttributedStringValue_(stringWithURL)
        mainWindow's makeKeyAndOrderFront_(me)
end applicationWillFinishLaunching_

Hello leonsimard,

thanks again. Tried it, but the script does not fill the textfield. Seems, that I have a problem with the binding (I’ve connected the textField to the “Value”-field in the Bindings Inspector)?

I’ve created a new project similar to my project, inserted your complete code. I’ve uploaded it here:

http://dl.dropbox.com/u/154942/HyperLink_in_TextField.zip

Perhaps you can have a look on it and give me an advice what I have to do to solve the problem. Thank you!

Rebewslin

Ok, two things: there is two kinds of linking. The one you were using is for a different use. In this case you need to make a link between user interface elements in your XIB and your script. To accomplish this you control-drag from the App Delegate’s blue cube to the element you wish to link, and then you select a property … : missing value from the list. Then in your script you can send methods to the UI elements in your XIB, like setting their contents, or make a window show up.

Second thing: I’ve downloaded your project and did the changes, added comments for you. As it is presently, it works as expected. I’ve taken the liberty of adding my last update which lets you control the link color, font and size. And i’ve adjusted the text field’s appearance to remove the background and the frame.

Here:

http://dl.dropbox.com/u/2244321/HyperLink_in_TextField_Modified.zip

Let me know if you have any more questions.

BTW, I strongly suggest obtaining Shane Stanley’s book on AppleScriptObjC programming. It’s the only one that exists and helped a lot of people getting started with their projects, including me. Here’s the link:

http://www.macosxautomation.com/applescript/apps/index.html

:slight_smile:

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

Hello leonsimard,

thank you very much for your work!

Yep, it works and it will give me lots of suggestions to improve my skills in ASOC!

Rebewslin

Glad to know I could help. Have fun!

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)