Archive for Cocoa

On XCode 4…

The only thing worse than managing multiple windows is managing multiple panes… Brent Simmons

I feel like this all the time in Xcode 4. I have been using XCode 4 for quite a while now and while I love the fact that Clang and all the back end smarts are much nicer, the interface has really taken a retro-grade step in my opinion. I recently had to go back to XCode 3 to do some stuff and wow it was soooo much faster to get around and do stuff. There is now so much pane management in XCode 4.

For instance lets consider doing a find across your project. In XCode 4 you hit cmd-shift-F, enter your find text and hit return. Then you have to go drag the find results sub-pane bigger… then look through all your results. Then when you find something interesting you have to double click on a result to look at the find instance (being very careful not to single click it or else the main bit of code you were looking at to provide context to your find disappears). Then when you finally found what you want you have to dismiss the new windows you created, then resize the find column back to something you use for eg a stack trace / build results / whatever (Debug Navigator / Log Navigator) and then go on your way.

In XCode 3 you just hit cmd-shift-F, enter the find and hit return, look through the results, and then close the window. Sooo much quicker and simpler. Its things like this which just made XCode 3 so much smoother…

Apple please, please, please, also allow a more windowed approach like there used to be which was so much more flexible and quicker…

Leave a Comment

MacHg 0.9.13 released!

MacHg 0.9.13 adds QuickLook support, error disclosures, .hgignore event handling, UI improvements, and fixes a number of issues.

In particular one can now use QuickLook to get a snapshot of the historical state of a file in the History View or the Differences View. This facet of QuickLook support is quite nice. In fact the general QuickLook support does all the things it should in the right mac ways, etc.

I also like the new error disclosures. They are much snazzier than the previous versions. (I even find myself every so often repeatedly typing in purposely wrong paths and names just so I can see them smoothly animate out in MacHg and think to myself “ohh, that really looks nice…” Then again I guess it’s only ever GUI designers who think such things, and normal people will just think “Oh… yes, I guess you are right, that does look nice.” But never really notice it otherwise… Still it’s details like this which make Mac applications, well Mac like…

Also a really nice change in MacHg 0.9.13 is that I can now get from Mercurial the regular expression representing all the files Mercurial will ignore. Before, whenever I auto-detected that something changed inside some directory (via FSEvents), I had to send through to Mercurial a request to see what had changed. However, if that directory was ignored by Mercurial, I would still have to go through the whole rigmarole of checking via Mercurial that indeed nothing had changed. Now I can just check against the regular expression, which is much faster. Eg a typical example of this is having an ignored build directory in your programming project. Typically though, when you are compiling the files inside in your project, often the files within the build directory are changing like mad.  Thus previously a flood of checks would have to go out from MacHg to Mercurial to see if anything changed, and this could in fact cause your build to go some 2 times slower than without MacHg running… Anyway happily this is now fixed.

Also I have gone with the new status icons provided by David Keegan. I think they are better than the ones I had. Also David sent along some new toolbar items and revamped the icons for the preferences items. (Thanks David!)

There are also a number of other UI fixes. See the full [release notes]:

In more general terms of the overall MacHg timeline, the change to stable graph drawing in 0.9.12 has gone very well. There has been only one reported issue against this massive refactor, and moreover that issue is now fixed in 0.9.13.  So all in all the huge refactor and speed up in 0.9.12 went very smoothly.

MacHg 0.9.13 is definitely a candidate for MacHg 1.0.0. (In fact MacHg 0.9.12 was also a candidate, but I just wanted to see that everything was ok with the stable graph drawing change.)

Beyond the 1.0.0 release I plan to add a super Annotation View, an improved history editing interface, handling for git and svn through their respective extensions, improved discovery and other issues.

Cheers, Jason

Leave a Comment

Where is NSBrowserSelectionDidChangeNotification when you need it?

While I was updating MacHg to include QuickLook support I came across the case where it would be really nice if NSBrowser had the notification NSBrowserSelectionDidChangeNotification (which doesn’t exist) whereas for comparison NSTableView has NSTableViewSelectionDidChangeNotification. There are a couple of hacks and ways to get around this, and MacHg currently updates more often than it should. Right now I just target the action to a method which always updates, but sometimes the selection updates when it doesn’t need to… Googling around for this, you can see various people searching for a solution… Eg here and here. Anyway, if anyone knows of a nice way to do this, please let me know!

Thanks,

Jason

Comments (1)

MacHg 0.9.9 released!

I am really happy to announce MacHg 0.9.9.

Its getting really close to MacHg 1.0.0! Here is the release notes for MacHg 0.9.9

  • Major revamp of the Commit Sheet.
  • Files can now be excluded / re-included from the commit sheet, and visually disabled / enabled.
  • An expandable “Advanced Options” section is now accessible on the commit sheet. One can override the user or the date of a commit.
  • Introduce the “Amend Option”. Although Mercurial does not have a native command for this, MacHg uses Mercurial queues to do the amending of files. (this is the same as the git command.)
  • Fix a problem where if you click too quickly on a repository you get kicked back to the welcome screen instead of loading the repository.
  • Big internal enhancement to DisclosureBoxController so it shuffles views around better.
  • Add tooltip stating that the password will be stored securely in the system keychain.
  • Improve performance and avoid threading issues, by internally putting many events inside a delayEventsUntilFinishBlock.
  • Fix issue #110, where the times of commits would appear incorrectly offset by the local time zone.
  • Fix issues to do with date parsing. Ensures one gets the correct date when importing patches.
  • Make the rollback menu item disabled when there is no rollback information available.
  • Make sure the push / pull / incoming / outgoing counts are laid out correctly when resizing the corresponding sheet.
  • Fix issue #86. Previously, a collapse of multiple changesets would result in the historyView’s selected indices ‘sticking’ despite the collapse. Now, after a collapse, the ‘lower’ revision is selected.
  • Make the delete key in the ImportPatches sheet delete a patch from the list of patches we are about to import.
  • Fix for issue #126. Save the overall window size for non-independent window sizes.
  • Fix issue #96 : “Built-in” typo in the Preferences pane.
  • Remove unused “count”-string in PullSheet.
  • Fix issue #121. Corrected the documentation to use “Add Server” instead of “Bookmark Server”.
  • Fix issue #106. Generate a better name in the rename sheet and fix button layout.
  • Include nice addition from Eugene Golushkov to make parent revisions appear as buttons.
  • Also from Eugene Golushkov make the NSTextAttachments copy to the clipboard correctly.
  • Fix issue #114. Add a preference item to control if the views have independent window sizes. (Several people had previously asked for this option.)
  • Fix issue #113. Added a rebase option to pull sheet.
  • Fix issue #116 and #108. We can now ignore files with a ‘#’,'+’, or ‘*’ in them.
  • Fix pinning of the “Browse…” button in the local repository sheet.
  • Added some underlying support for undo /redo. The backup works perfectly, unfortunately it’s just too slow for large repositories like OpenOffice, so I will have to go with some plan B (but leave the functionality in for now…)
  • Add some documentation on the Help Generation process.
  • Add help topic about empty repositories. (Relates to issue #134).
  • Make building instructions for MacHg a little more prominent.
  • Document the simple instructions necessary to change the Mercurial binary used by MacHg in MacHg/CodeOverview.txt
  • Use only MacHg’s local Mercurial version and remove the preference item UseWhichMercurialBinary. It really makes no sense to allow the specification of a different Mercurial binary and it can only lead to problems. If the user wants to change the Mercurial version inside the MacHg bundle they can easily follow the documented instructions.
  • Simplify the include paths presented to the users in the advanced options. Previously it was a bit more general but users were getting confused. Thus Remove preference IncludeMacHgHgrcInHGRCPATH and always have the ~/Application Support/MacHg/hgrc file included in the HGRC path.
  • Make sure that the application support hgrc file contains a valid user name, so if the preferences are switched mid session then things still function correctly.
  • By default now include the users ~/.hgrc file in the HGRCPATH.
  • Make switching on the editing extensions in checkConfigFileForEditingExtensions check only in the ~/Application Support/MacHg/hgrc configuration file.
  • Fix issue #95. Check for the existence of a ~/.hgignore file on startup. If this file doesn’t exist then create it with the contents of DefaultFiles/hgignore.
  • Prevent #124 from happening.
  • Release version 0.9.9
  • Patch contributors for this release (Thank you all!): Eugene Golushkov, Sven Weidauer, Tojek Anselm, Marko Kaning

General status comment on MacHg: I am still working on the graph jiggling issue before I can finally go 1.0.0. (http://bitbucket.org/jfh/machg/issue/31) It is my top priority but its quite hard to do since getting incremental child information is an n^2 thing. I have stuff working in my own private branch, and I will hopefully finish the polish on it and release it soon.

Comments (2)

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

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