Archive for May, 2007

NSImage compositeToPoint

I was working on adding a nice little new mail badge to the Mail Unread Menu icon, when I ran into a small snag with transparency. I’m using NSImage’s compositeToPoint: to place a small 6×6 red circle icon on top of the selected mail icon. I initially used NSCompositeCopy as the operation, which ignores transparency. This was easily fixed by using NSCompositeSourceOver, instead. Take a look…

Widgets for the Masses

So TUAW got a look at the MLB Schedule Widget today with this blog post. And then it made my web stats to do this, courtesy of Mint:

TUAW and Mint Stats
Things have tapered off, but right at 1:30 when it was posted, there were a ton of hits. So that’s pretty cool. I’ve gotten some great feedback about both of the widgets and it seems that, overall, people pretty much enjoy them.

Dashboard Widgets and Python

So over the last week, I’ve had some fun with Dashboard Widgets and using Dashcode to do so. I was less-than-impressed when using Dashcode, just because there are a handful of tiny little quirks that don’t work correctly. Hopefully this stuff is fixed in the Leopard release. Anyways, you can see the result of the work under the Widgets tab in the site navigation.

MLB StandingsI created two Major League Baseball themed widgets. The first, MLB Standings, just displays the division standings for any of the 6 baseball divisions. It’s pretty basic, but it’s great to have quick access to. It’s actually something that I’ve wanted to create for a while now, but more on that later. The second widget, MLB Schedule, displays a five-day range centering around today and details either the score of a finished game or the time and TV station of a yet-to-be-played game. It was a little tougher to create, but turns out to be quite handy. I’ve got a few more features to add, too. I want to be able to choose how many days are shown in MLB Schedule, between 3, 5, 7 and 9. But, resizing the widget to fit requires more work in the way of background images. And I’ve also already added support for multiple instances.

The way these widgets work is actually pretty interesting. I did some searching around a while ago for an RSS feed or some XML page which updates with baseball information, but found absolutely nothing. (I’m still disappointed that this doesn’t exist.) So, in order to get this working, the widgets would have to parse a standard HTML page; screen scraping as it’s called. Although I am slightly familiar with screen scraping (using PHP, it’s how I display the download count for my applications on Macupdate and Version Tracker), I couldn’t find a Javascript based code base to start from. It seemed that the language just wasn’t meant for it.

MLB Schedule

And then, I learned Python and it’s amazing. I’ve already written a handful of scripts that were easy to write and fun to play with (which I’ll probably post later). And since every installation of Tiger comes with Python installed, it’s acceptable to use in the widgets. So there are Python scripts in each of these widgets, which do the actual parsing of ESPN webpages and pass all of the data back to the widget’s Javascript, where it is formatted for outputting. The scripts are simple enough and were fairly easy to write, so it’s a great combination.

Then, last night, I came up with another great way to use Python and Dashboard widgets, but in a slightly different way. Last christmas, my folks bought me a Slingbox that they keep at their house in the San Francisco Bay Area, allowing me to watch their DirecTV box from my house. It’s great because I don’t get San Francisco Giants games on local television, so I can watch them there.

One of the issues we have with the Slingbox is determining if my parents can use the television or if I’m watching it remotely. I believe there is a light on the physical box indicating one way or the other, but ours is hidden away, so that doesn’t help. So, what I did was write a small Python script that runs on my machine, the server in this scenario. It checks to see if my SlingPlayer application is open and posts to a date/time stamp of the last check and a 0 or 1, based on if I’m watching or not. Here’s the script:

#! /usr/bin/python

# upload the status of wether the SlingPlayer application is open to the server

from commands import getoutput
from ftplib import FTP
from datetime import datetime

# see if SlingPlayer is running
if 0 < len( getoutput("ps -ax | grep [S]lingPlayer") ):
    status = 1
    status = 0

# create status file
file = open("/tmp/slingboxstatus", 'w')
file.write( str( + "n" + str(status) )
file = open("/tmp/slingboxstatus")

# ftp the status to the server
ftp = FTP('')
ftp.login('##loginname##', '##password##')
ftp.storlines("STOR index.html", file)

Using unix’s crontab functionality, I’ve set it up to run every ten minutes, so I don’t even have to think about it. It can easily be done by opening a Terminal window, entering crontab -e, entering in the line */10 * * * * /Users/logan/Scripts/ in the text editor, saving, and then running crontab -l to verify the changes. (The only thing I’m concerned about is wether FTPing every 10 minutes to my server is really a good idea.)

Slingbox WidgetThen, I created a very small Dashboard widget, which simply checks that status. Every time the Dashboard is open, the widget gets the information from online, formats it and prints it out. So, my parents can run this widget on their Mac and quickly check if I’m watching the Slingbox or not. You can download the widget in this zip file. So, yeah. Pretty cool stuff.


Today, I was without my Wii controllers after lending them to a friend, so I spent a lot of time upgrading some of the little things with my little online portal here.

  • A Mail Unread Menu user Nicolas sent over a French localization yesterday, but the program wasn’t completely ready for it. I spent some time placing NSLocalizedString macros in the code, so that anything outside of the .nib file could be localized. This was easy for the small Mail Unread Menu project, but Assignment Planner is going to be a much larger ordeal.
  • Changed the name of the website from Logan Design to Logan Rockmore Design, something that I’ve wanted to do for a while now. I feel like it’s more personal and unique to myself, and not that big of a change to make.
  • My twitter username also made the change to burgundylogan.
  • With the name change, I created a small and simple LRD logo that I plan to use in a few places. Right now it’s in use on Twitter and on the Forums, with a favicon soon to follow.
  • Posted on the blog about the validation badge, something that I’ve wanted to do for a while.
  • Fixed the “Last Modified” timestamps at the bottom of each page to be my local Pacific timezone.
  • Renamed the CommandLine code to LRCommandLine, something that should’ve been done from the beginning.
  • Added a similar project titled LRApplescript, which is basically a Cocoa – Applescript interface that I use in a few of my projects.
  • Spent a little bit more time working on another utility that’s in the works. I pretty much just need a good looking icon for it.
  • I even was able to watch Tim Lincecum’s impressive major league debut on national television.
So that’s about it. I feel very accomplished.

Valid Web Badge

One feature that I added to this website awhile back was the small badge at the bottom of each page that indicates weather the XHTML and the CSS was valid or not. I enjoy keeping my code as valid as possible and this really helps to quickly indicate how well I’m doing. Basically, it just uses PHP to open up the validation page and looks for the success phrase. It’s not very flexible and if these validation websites decide to change their layout or wording, this will break, but it works.

When I use this on my pages, I use AJAX to asynchronously display the output. Otherwise, the loading speed of my page actually depends on the loading speed of the server and if their having some problems, I want my page to be able to finish loading.

Here’s my validate.php file:

if( !$page ) {
    echo 'could not validate current page';

} else {

    // Validate XHTML
    $xhtmlURL = '' . $page;
    $h = new http();
    if ( $h->fetch("$xhtmlURL") ) {
        if( strpos($h->body, "This Page Is Valid") ) {
            $xhtmlLink = '<a href="' . $xhtmlURL . '">xhtml</a>';
        } else {
            $xhtmlLink = '<a href="' . $xhtmlURL . '" style="border:none; text-decoration:line-through;">xhtml</a>';

    // Validate CSS
    $cssURL = '' . $page;
    $h = new http();
    if ( $h->fetch("$cssURL") ) {
        if( strpos($h->body, "Congratulations! No Error Found.") ) {
            $cssLink = '<a href="' . $cssURL . '">css</a>';
        } else {
            $cssLink = '<a href="' . $cssURL . '" style="border:none; text-decoration:line-through;">css</a>';

    // Output
    if( $xhtmlLink && $cssLink )
        echo 'valid ' . $xhtmlLink . ' and ' . $cssLink;
    else if( $xhtmlLink )
        echo 'valid ' . $xhtmlLink;
    else if( $cssLink )
        echo 'valid ' . $cssLink;

And here is how it’s called from the pages on my server. It might be different for yours. You have to pass in the page name, because calling $PHP_SELF from validate.php will return “validate.php”, rather than the calling page, which is what you really would like. (If anyone knows a way to bypass this, so you don’t have to pass the variable, that would be great.)

$page = "" . substr($_SERVER['SCRIPT_FILENAME'],26);
include("" . $page );

Assignment Planner 3.1.1

I forgot to note that I released Assignment Planner version 3.1.1 yesterday. It’s just a small update, but it’s still great! Get it stat.

  • Changes
    • Address Book contacts with only a first name can now be accessed.
    • The main window can now be sized much smaller.

Mail Unread Menu 2.2

Hurray for Sparkle updating and customizable icons! Get it at the usual place.

  • New Features
    • Added a choice of menu bar icons.
    • Added the Sparkle framework, which provides automated version upgrading.
    • The status item will correctly remove itself from the menu bar when quitting.

:: RSS Feed :: Archive :: Categories :: Admin