Wednesday 22 April 2009

NSDateFormatter in OSX 10.4.11

Today I found some strange behaviour in the NSDateFormatter :
If I execute the following code :

NSDateFormatter *timeFormatter = [[[NSDateFormatter alloc] init] autorelease];
[timeFormatter setDateStyle:NSDateFormatterNoStyle];
[timeFormatter setTimeStyle:NSDateFormatterMediumStyle];
NSDate *stringTime = [NSDate date];
NSString *formattedDateStringTime = [timeFormatter stringFromDate:stringTime];
[theTime setStringValue: formattedDateStringTime] ;

Then , instead of displaying the time, it shows me the date.

If I add the following line on top :

[NSDateFormatter setDefaultFormatterBehavior: NSDateFormatterBehavior10_4] ;

It gives me correctly the time.

is this something specific for the 10.4.11 ? Is this behavior also observed in the 10.5.X ?

Saturday 18 April 2009

Application badge in Dock

In Leopard (OSX v10.5 and higher) you have the capability to set an application badge like you can find on iMail for example :



Now for the poor people who still have a pre 10.5 version, or for the people who want more capabilities than what Leopard offers, I wrote an utility class based on the example code found in CocoaDev.

This utility class does not only gives the capability to draw the badge gives also the possibility to change the position of the badge :



Changing the font size of the badge:



Changing the color of the badge:



The code can be downloaded here.

Thursday 16 April 2009

NSNumberformatter: some examples

I was looking for some examples on the usage of the NSNumberformatter class. During this search I stumbled on the weblog of Sam Lam which provides some good examples.

I copy them here , as I use my own blog as a sort of FAQ or HOW-TO for myself.

-(NSString*) formatCurrencyValue:(double)value
{
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setCurrencySymbol:@"$"];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *c = [NSNumber numberWithFloat:value];
return [numberFormatter stringFromNumber:c];
}

-(NSString*) formatPercentValue:(double)value
{
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setPercentSymbol:@"%"];
[numberFormatter setNumberStyle: NSNumberFormatterPercentStyle];
[numberFormatter setDecimalSeparator:@"."];
[numberFormatter setGeneratesDecimalNumbers:TRUE];
[numberFormatter setMinimumFractionDigits:2];
[numberFormatter setRoundingMode: NSNumberFormatterRoundUp];
[numberFormatter setRoundingIncrement:[[NSNumber alloc]initWithDouble:0.05]];
NSNumber *c = [NSNumber numberWithFloat:value];
return [numberFormatter stringFromNumber:c];
}

-(double) formatDoubleFromCurrency:(NSString*)value
{
double ret ;
if(value)
{
ret = [value doubleValue];
if (ret == 0)
{
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setCurrencySymbol:@"$"];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *c = [numberFormatter numberFromString:value];
ret = [c doubleValue];
}
return ret;
}
else
return 0.0;
}

NSButton: setting an image in a button

I always have trouble to know how to set an image in a button. The following code snippet does the job:

NSString* im1 = [[NSBundle mainBundle] pathForResource:@"arrow right" ofType:@"png"];
NSURL* url2 = [NSURL fileURLWithPath:im1];
NSImage *image = [[NSImage alloc] initWithContentsOfURL: url2];


[toggleButton setImage: image] ;

NSString *im2 = [[NSBundle mainBundle] pathForResource:@"arrow left" ofType:@"png"];
url2 = [NSURL fileURLWithPath:im2];
image = [[NSImage alloc] initWithContentsOfURL: url2];
[toggleButton setAlternateImage: image] ;

The code above set's 2 images in a toggle button .

Note that if you want transparant images , you need .png files and not jpeg's (GIF also works)

For telenet users: a telemeter application



Note: the application is updated ! It's now compatible with the new Telemeter service of Telenet (new version since July 2010)

Also available for the iPhone see following blog message .

This little application shows how much data you've downloaded/uploaded from your Telenet account.
(for the impatients : you can find the dmg here ) .


For those with an unlimited download (turbonet, fibernet) the screen will look like follows :



Installation is very easy :

1. Open the dmg file (download here) ;
2. drag and drop the folder 'Telemeter' wherever you want ;
3. Start the Telemeter v2 application.

The very first thing when you start the application a popup will come up to warn you that you need to set your telenet username/password . This can be done clicking the green user icon (on the first line on the right). And if all is oke you should see your usage.

In the screen you see also a button with an arrow (>) , if you click that you'll get the daily usage details.



The application will update the data from Telenet every 30 minutes.



Next enhancements :

1. Make a dashboard widget of it
2. Change the icon colour depending on the used bandwidth


Note: this is a first version , so please report any bugs to me (via blog or email). Comments are also welcome of course.

UPDATE :

I made the following enhancements :

1. you see in the tray bar (system menu bar) the percentage used :



2. In the dock , the icon will also show the percentage used



3. several small bugfixes

UPDATE 2: just found out that I have some memory leaks, will solve them ASAP.

UPDATE 3 : memory leaks solved + some minor improvements

UPDATE 4 : major enhancements

UPDATE 5 : Currently (5 May) telemeter service is down

UPDATE 6: We are 7th of May and the telemeter service is still down. And this in Internet age....

UPDATE 7: And now they've changed the protocol.... pff they really annoy me. I changed the XML processing , so it works again

UPDATE 8: Application rewritten because Telenet has changed completely the webservice

Tuesday 14 April 2009

Drawing pie charts

Recently I was looking for a way to draw pie charts (you know, the one with an 'exploded' slice) on my Macbook.
Unfortunealy I didn't find much on the Internet, so I've started to experiment a bit myselfs.
The following code draws a pie chart with one slice exploded from the other :

The trick is that you draw an imaginary circle around the center, on this circle is the center of the 'exploded' slice.

#define PI 3.14159265358979323846

NSBezierPath *greenPath = [NSBezierPath bezierPath] ;

// set some line width

[greenPath setLineWidth: 2 ] ;

// move to the center so that we have a closed slice
// size_x and size_y are the height and width of the view

[greenPath moveToPoint: NSMakePoint( size_x/2, size_y/2 ) ] ;

// draw an arc (perc is a certain percentage ; something between 0 and 1
[greenPath appendBezierPathWithArcWithCenter:NSMakePoint( size_x/2, size_y/2) radius:50 startAngle:0 endAngle: 360 * perc ] ;

// close the slice , by drawing a line to the center
[greenPath lineToPoint: NSMakePoint(size_x/2, size_y/2) ] ;
[greenPath stroke] ;

[[NSColor greenColor] set] ;
// and fill it
[greenPath fill] ;

greenPath = [NSBezierPath bezierPath] ;

[[NSColor blackColor] set] ;
[greenPath setLineWidth: 2 ] ;

// draw the second slice, now exploded from the original center

// so to get it exploded I move (10,7) points from the original center
// but on the imaginary circle (thats why the cos and the sin)
// note mide_angle is the angle halve way from the arc, you can experiment with multiple
// angles, note also that the angle is in degrees
[greenPath moveToPoint: NSMakePoint(size_x/2 - 10 * cos ( PI * mid_angle /
180 ) , size_y/2 - 7 * sin ( PI * mid_angle / 180 )) ] ;

// and now draw the other slice
[greenPath appendBezierPathWithArcWithCenter:NSMakePoint( size_x/2 - 10 * cos ( PI * mid_angle / 180 ) , size_y/2 - 7 * sin ( PI * mid_angle / 180 )) radius:50 startAngle:360 * perc endAngle:360 ] ;

// close the slice
[greenPath lineToPoint: NSMakePoint( size_x/2 - 10 * cos ( PI * mid_angle / 180 ) , size_y/2 - 7 * sin ( PI * mid_angle / 180 ) ) ] ;
[greenPath stroke] ;
[[NSColor blueColor] set] ;

[greenPath fill] ;


The result of above code is here :



Next time I'll add some code to add text to the slices

Tuesday 7 April 2009

SOAP: webservices in Objective-C/Cocoa

Lately I was experimenting a bit with webservices, and this for a good reason : my ISP was trying to bill me extra gigabytes while I was sure I didn't used my full quota.
My ISP (telenet in Belgium) is using a nice web page where you can check your quota but they also offer a webservice which gives full statistics on the bandwidth usage.

So I decided to build a Dashboard widget (in Objective-C, not in Javascript) to see the statistics from my desktop.

Now I didn't do any SOAP/Webservices since a long time , and I never did in Objective-C (my experience goes back to VisualWave, Smalltalk and Java).
So I did some experimentation with the samples from Apple itself :


// SOAP request settings

NSURL *url = [NSURL URLWithString:@"https://telemeter4tools.services.telenet.be/TelemeterService"];

// the name of the webservice
NSString *method = @"getUsage";

// the namespace
NSString *namespace = @"https://telemeter4tools.services.telenet.be/";


// SOAP request params

NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:@"**username**",

@"string",
@"**password**",
@"string0",
nil];

// the parameters itself are in a dictionary, a dictionary is an unordered collection
// so to define the order in which the parameters must be sent
// you need to fill an array with the NAMES of the parameters

NSArray *paramOrder = [NSArray arrayWithObjects:@"string", @"string0"];



// set SOAP request http headers -- some SOAP server impls require even empty SOAPAction headers



NSDictionary *reqHeaders = [NSDictionary dictionaryWithObject:@"" forKey:@"SOAPAction"];



// create SOAP request


WSMethodInvocationRef soapReq = createsoapReq(url, method, namespace, params, paramOrder, reqHeaders);



// invoke SOAP request


NSDictionary *result = (NSDictionary *)WSMethodInvocationInvoke(soapReq);



// get HTTP response from SOAP request so we can see response HTTP status code


CFHTTPMessageRef res = (CFHTTPMessageRef)[result objectForKey:(id)kWSHTTPResponseMessage];


The response of a SOAP requests is a dictionary, and it looks like this :

result: {
"/Result" = { ... <<>> };

"/WSDebugInBody" = "...etc;

"/WSDebugInHeaders" = {
"Accept-Ranges" = none;
Connection = close;
"Content-Length" = 4840;
"Content-Type" = "text/xml; charset=utf-8";
Date = "Wed, 08 Apr 2009 06:44:46 GMT";
"Set-Cookie" = "JSESSIONID=JcHpkYZkhhn2pzp1LY7fvGQ1WXkb24WKDY2LBnf0JpLYyhsk23Q1!-344393110; path=/, st8id=c42908deb564326d86d900fd90459934.01.af0318b08a904d84c5824b76a132ab93; domain=.services.telenet.be; path=/, st8id_wat_%2Eservices%2Etelenet%2Ebe_%2F=SlNFU1NJT05JRA__?1607678332ef30e3c1b9fc43cda54cb6; domain=.services.telenet.be; path=/";
};
"/WSDebugOutBody" = <<>>";
"/WSDebugOutHeaders" = {
"Content-Type" = "text/xml";
Host = "telemeter4tools.services.telenet.be";
Soapaction = "";
"User-Agent" = "Mac OS X; WebServicesCore.framework (1.1.0)";
};
"/kWSHTTPResponseMessage" = <<>>
}

Note that I stripped a bit the contents of the dictionary.

Now to get the result of the webservice you must get the proper data from the dictionary (in this case this is data = [result objectForKey: @"result"].

This will return a string value, so a next step is to create an XMLDocument of it :

NSXMLDocument *document ;
NSError *error ;
NSXMLNode *node ;
document = (NSXMLDocument *) [NSXMLDocument document] ;
[document initWithXMLString: data options: NSXMLDocumentTidyXML error: &error ] ;

And once it is in a XMLDocument , you can use XPath expressions to query the document :

myArray = [document objectsForXQuery: @"/ns1:telemeter[1]/ns1:usage-info[1]/ns1:data[1]/@timestamp" error: &error ] ;

Now this looks to me a very complex process to get something from a webservice.
I'm now looking at WSDL and the utility WSMakeStubs to see if I can simplify this.
The nicest thing would be that we have something like JAXB to 'objectify' webservice responses