Interesting take on Linux and UI design

I stumbled over an interesting article the other day when trying to figure out how to turn BBEdit TextFactories into droplets.

Recently I have struggled a with several characters in the Mercurial development scene. Interestingly this article about UI design and Linux mindset thinking although old, was so on the money its eerily scary. (at least with some of the characters I have encountered…) Things haven’t changed. Tellingly from the article was the quote:

The list of fantastic open source developer software is long. The list of fantastic open source GUI software is short. This is not a function of chance.

Comments (1)

My Mercurial Prompt

This has come up a few times and so I thought I would post this. I use Steve Losh’s hg-prompt extension and I have it configured so that it displays the revision and the patch number if I am using Mercurial queues: Like so:

My Mercurial Prompt

To get this paste the following into your .bashrc file:

############################################################################# # Set up Prompt ############################################################################# noColor='\[\033[0m\]' blackColor='\[\033[0;30m\]' redColor='\[\033[0;31m\]' greenColor='\[\033[0;32m\]' yellowColor='\[\033[0;33m\]' blueColor='\[\033[0;34m\]' purpleColor='\[\033[0;35m\]' cyanColor='\[\033[0;36m\]' greyColor='\[\033[0;37m\]' boldGrayColor='\[\033[1;30m\]' boldRedColor='\[\033[1;31m\]' boldGreenColor='\[\033[1;32m\]' boldYellowColor='\[\033[1;33m\]' boldBlueColor='\[\033[1;34m\]' boldPurpleColor='\[\033[1;35m\]' boldCyanColor='\[\033[1;36m\]' boldWhiteColor='\[\033[1;37m\]' # Change the prompt to be user@host:path$ with some colors # simple version would be say # export PS1="${noColor}[${yelloColor}\h${noColor}:${greenColor}\w${noColor}] \$ " # Function that returns the last n path components of the specified path after # replacing $HOME with ~. Call like: # pathComponents path n nameColor seperatorColor function pathComponents { echo `python -c\ "import string,re; pth=re.sub('^$HOME','~',r\"$1\",1); n = string.atoi(r\"$2\"); nc=r\"$3\"; sc=r\"$4\";\ print nc + string.join(pth.split('/')[-n:], sc + '/' + nc);"` } # this is my mercurial prompt. It relies on the hg-prompt extension : http://stevelosh.com/projects/hg-prompt/ # its basically: basename rev(tip) on branch at bookmark patchnum#/patchcount# hg_ps1() { # hg prompt "{ on ${greenColor}{branch}${noColor}}{ at {bookmark}}" 2> /dev/null hg prompt "${cyanColor}{root|basename}${noColor}\ { ${blueColor}{rev}${greyColor}}\ {(${cyanColor}{tip}${greyColor})${noColor}}\ { ${greyColor}on:${cyanColor}{branch|quiet}${noColor}}\ { ${greyColor}at:${noColor}{bookmark}${noColor}}\ { ${greyColor}mq:${purpleColor}{patch|applied|quiet}${noColor}}{/${purpleColor}{patch|count|quiet}${noColor}} " 2> /dev/null } #export PS1='\u at \h in \w$(hg_ps1)\n$ ' function myPromptCommand { local newPWD=`pathComponents "\${PWD}" 4 \${blueColor} \${redColor}` # the trimed colored path echo -ne "\033]0;${HOSTNAME%%.*}:${PWD/$HOME/~}\007" # this sets the title export PS1="${noColor}[${greenColor}\h${noColor}:${newPWD}${noColor}] $(hg_ps1)⌘ " } PROMPT_COMMAND=myPromptCommand # Change the title of each window, and PS1 to reflect where we are

Leave a Comment

Versioning your application with the Mercurial changeset hash

A user of MacHg (Marko Käning) wanted to be able to see the changeset hash in the About MacHg box (see here). I had been meaning to do this at some stage and, well, given the request, now seemed as good a time as any… It turns out it was quite easy to configure XCode to create the appropriate information while building MacHg.

(In my workflow I release versions of MacHg every so often, and I want users who get the source code off of bitbucket and build it for themselves to be able to easily tell me something like “In such and such a version of MacHg I am seeing XYZ happen.”)

So now MacHg shows the following sort of thing when you look in the About MacHg box (note the grayed c0d2f1df774d hash code):

ok so how did I go about making this work? It happens in two steps:

  1. As part of the build process, get the hash key automatically and insert it into the applications info.plist.
  2. Inside your application extract the stored value from the hash key and display it in the about box

Steps to Automatically set the BuildHashKey when building your Application

  1. Open your XCode project.
  2. Under targets select your application.
  3. Right click on the target and choose Add -> New Build Phase -> New Run Script Build Phase, as in:
  4. Change the shell to be /usr/bin/python instead of /bin/sh.
  5. Then in the script, copy and paste the following — I actually did this in bash first but decided to do it properly again in python so that I will be able to read it again in six months time if need be, python being that much more readable :

import os, subprocess, re, sys

targetBuildDir = os.getenv("TARGET_BUILD_DIR") getChangeset = subprocess.Popen('hg parent --template "{node|short}" --cwd ' + targetBuildDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

if (getChangeset.stderr.read() != ""): print "Error in obtaining current changeset of the Mercurial repository" sys.exit(0) # if you want the build to fail here since the changeset is malformed change this to sys.exit(1)

changeset = getChangeset.stdout.read() if (not re.search("^[0-9a-f]{12}$", changeset)): print "Current changeset of the Mercurial repository is malformed" sys.exit(0) # if you want the build to fail here since the changeset is malformed change this to sys.exit(1)

infoPlist = os.path.join(targetBuildDir, "MacHg.app/Contents/Info.plist") if not os.path.exists(infoPlist): print "Cannot locate MacHg.app/Contents/Info.plist" sys.exit(1) # if you want the build to not fail here change this to sys.exit(0)

result = subprocess.Popen('/usr/libexec/PlistBuddy -c "Delete BuildHashKey" ' + infoPlist, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) result = subprocess.Popen('/usr/libexec/PlistBuddy -c "Add BuildHashKey string '+ changeset + '" ' + infoPlist, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

print "MacHg BuildHashKey set to " + changeset

Of course you need to change MacHg.app to the name of your application. Also you might need to specify the full path to hg (ie /usr/local/bin/hg) in the line:
getChangeset = subprocess.Popen('hg parent --template "{node|short}" --cwd ' + targetBuildDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
Also you might want to have the build fail if the changeset is not available, since currently the script above just keeps going.  (I want users who download and compile MacHg to not get lots of critical errors if things are not set up just right.)

Using the BuildHasKey in your Application

Then back in your application you can have a method which will get the hash key from the info.plist:

- (NSString) macHgBuildHashKeyString
{
    NSString key = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"BuildHashKey"];
    return key ? key : @"";
}
Then it’s just a matter of styling it and viola, you have the ingredients for versioning your application with the Mercurial changeset hash key. Actually, after doing this I found out that over at Cocoa is My Girlfriend he had done something similar but in bash.

Leave a Comment

Launched!

Whew! I just launched MacHg 0.9.0 to the public. I opened up the website and bitbucket sources to public viewing, added a google group, updated the sparkle app cast, fixed a few last bugs, synchronized the information now between the website, the bit bucket wiki, and the announcement email, and well launched… Time to go celebrate… Here is the full text of the launch:

Hi All,

I am pleased to announce the initial public release of MacHg. MacHg is a gui client for Mercurial. It runs under OSX 10.6 (Snow leopard). It is free to use and donation ware.

Links

You can get an executable application here: http://jasonfharris.com/machg/downloads/downloads.html

You can view (and clone) the MacHg sources at: http://bitbucket.org/jfh/machg

You can view a screen cast ‘MacHg Introduction’ at: http://jasonfharris.com/machg/screencasts/introduction/screencast_introduction.html

Discussion group about MacHg at: http://groups.google.com/group/machg_mercurial

MacHg Features

  • Document based structure with multiple repositories per document
  • View incoming and outgoing at a glance between compatible repositories
  • Auto refreshes the status of tracked files when they change
  • Incremental loading of history (for efficiency)
  • Works with large repositories (ie for example can load and work with the Mozilla repository which is 3.35Gb)
  • Fully compatible with command line Mercurial (You can make changes on the command line and they are reflected in MacHg.)
  • Fully multi-threaded using Grand Central Dispatch and threading goodness
  • File browser view to work with files
  • History view to explore the changeset history
  • Differences view to explore the historical changes between different revisions
  • Interacting with other repositories: pull, push, incoming, outgoing, clone, etc
  • Contextual menus and shortcuts in many places
  • Diff multiple files at once
  • Renaming, merging, updating, reverting, adding files, removing files, etc
  • Add and remove local tags, global tags, bookmarks, and branches
  • Interface to history editing: collapse, histedit, rebase, strip
  • Importing and exporting of patches
  • Uses the Sparkle framework for “in application” updates

Contributing

If you would like to contribute I would be happy to receive and acknowledge help in a large number of areas, including: – Testing – Documentation (MacHg has some documentation but it would be nice to have a much more complete set of documentation.) – Cocoa coding (Lots of potential enhancements here) – Mercurial extension writing (There are a few extensions I could use help with: for example, around permissibility of dragging and dropping in history rewriting, etc.) – Translating (Especially those who have experience using Apple’s polyglot tool, etc.) – Web designers (There are a number of enhancements to be made to web pages, wikis, etc.) – Graphic design (Icon design, etc.)

Thanks, Jason

Leave a Comment

Make FileMerge diff .plist files

In doing macintosh program development I had often come across the case where I wanted to view the difference between two .plist files. Specifically when you are doing diffs of say your UserDefaults.plist in your application you are developing in XCode say…

Previously .plist files were all stored as ASCII files but in OSX 10.4 Apple started compressing them. If you attempt to diff one of these compressed .plist files in FileMerge it complains that the files are binary and should it proceed anyway. If you do proceed you get a comparison between garbage and garbage and you can’t see any of your changes to verify that you made the right changes in Xcode.

Anyway I had googled for a solution but didn’t find one easily. But recently this annoyed me enough that I overcame the thresh-hold of annoyance and looked for a solution. (likely you are reading this post for the same reason!) So it turns out that its pretty easy to get FileMerge to nicely display diffs of .plist files.

You need to add a simple filter to FileMerge. You can do this by hand.

  1. Open File Merge
  2. Go to Preferences -> Filters
  3. After the last item in the list of filters double click to enter new values and enter without the quote characters
  4. ‘plist’ in the extension column.
  5. ‘/usr/bin/plutil -convert xml1 -o –  $(FILE)’ in the Filter column.
  6. ‘Filtered’ in the Display column.
  7. ‘No’ in the Apply* column

Thats it. It should look like something like (I have a few other filters in there as well):

Picture of final FileMerge preferences

FileMerge Preferences

Or if you are more macho about such things, execute the following in the terminal:

defaults write com.apple.FileMerge Filters -array-add '{ Apply = 0; Display = 0; Extension = plist; Filter = "/usr/bin/plutil -convert xml1 -o -  \$(FILE)"; }'

Thats it. Go do some diffing!

Comments (4)

A tweak to a better NSLog()

Recently I wanted to make sure my NSLog()’s were not present in my release build. And so I did the requisute google knowing I could copy and paste some snippet of #if…#endif.

There was just one such approach in the Cocoa Is My Girlfriend site of DroppingNSLog in release builds but even a bit nicer was the actual debug output produced in Karl’s article: A drop-in replacement for NSLog(). Better debug logging was one of those things which is a really low level priority so you never get around to it, but it would be nicer to have better logging.

So I went with Karl’s in the end but I’ll just note that to get it going there was a slight problem with Karl’s version, that only showed up if you have code of the form:

if (sometest)
   DebugLog(....);
else
   DebugLog(...);

To make things work one has to be careful about the terminating semi-colons, and the null case. This is a standard trick in C++ and I have seen it in many other places the do {} while(0) to put in an empty statment in a hash-define. So in the end the final version should be to replace all NSLog(…) calls with DebugLog(…) and add the following to an appropriately common header in your personal project:

#ifdef DEBUG
#  define DebugLog(args...) _DebugLog(__FILE__,__LINE__,__PRETTY_FUNCTION__,args)
#else
#  define DebugLog(x...) do {} while(0)
#endif
void _DebugLog(const char *file, int lineNumber, const char *funcName, NSString *format,...);

This together with his original code in the corresponding .m file of:

void _DebugLog(const char *file, int lineNumber, const char *funcName, NSString *format,...)
{
    va_list ap;

    va_start (ap, format);
    if (![format hasSuffix: @"\n"])
        format = [format stringByAppendingString: @"\n"];

    NSString *body =  [[NSString alloc] initWithFormat: format arguments: ap];
    va_end (ap);
    const char *threadName = [[[NSThread currentThread] name] UTF8String];
    NSString *fileName=[[NSString stringWithUTF8String:file] lastPathComponent];
    if (threadName)
        fprintf(stderr,"%s/%s (%s:%d) %s",threadName,funcName,[fileName UTF8String],lineNumber,[body UTF8String]);
    else
        fprintf(stderr,"%p/%s (%s:%d) %s",[NSThread currentThread],funcName,[fileName UTF8String],lineNumber,[body UTF8String]);
    [body release];
}

Will do the trick nicely. Don’t forget to go into the targets and define -DDEBUG on your debug target like so:

Build Settings

Leave a Comment

Getting closer

Actually the thing that is holding me up right now is getting the web site up. I have found Dreamweaver to be a bit too general. Its harder to get things done quickly, I always seem to get extra tags when I didn’t want them and its not so quick to just get general pages up and make sure everything looks uniform. I want some sort of mix between using markdown or maybe one of the XML editors and some powerful XSLT to transform things fast enough. In the end though I think I am going to just go with RapidWeaver to get things going in a fast way. I have been frustrated with a few of the things I can’t change easily. Like I want to be able to add a table in the stacks plugin to rapid weaver but I can’t seem to nicely set a fixed pixel width on my table elements…

Leave a Comment

Almost there

Right my first blog post!

In my free time I have been working on developing a cocoa program MacHG. Its almost ready to launch to beta testing…

As is evident word press is mostly set up since you are reading this. My bitbucket setup is almost there… and well dreamhost was activated today and is hosting this as is evident. Google email is up. There are still some services which I will get set up like the forum thingy which is served by php. Also hosting of the screen casts. But soon…

Leave a Comment