Search and Replace in Xcode

January 27th, 2007 — 5:59pm

This issue came up twice in the past couple weeks thus triggering my rule that if something comes up more than once then others may be interested in it. So, here’s a little tip that may save you some frustration.

When using regular expressions with Xcode’s search, the docs mention that XCode uses the ICU library. Naturally, you’d think it would use ICU’s syntax for specifying backreferences in the replace string, which would be to use variables consisting of a dollar sign ($) followed by the number of the capture group being referenced.

Of course, if that were the case, I probably wouldn’t be writing this tip. Using $ for backreferences turns “Search & Replace” into “Search & Destroy”. The syntax is to use backslash (\) instead of dollar sign. In short, \1 instead of $1.

Type the following in an Xcode editor window: “$1 works in replace strings.”

Now perform the following search & replace:

find-replace-crop.png

Ok, maybe this example makes things more confusing. If in doubt, just remember: $ = bad, \ = good in Xcode Find panel.

As to why Xcode does not use the ICU syntax in the replace strings, beats me. If anyone has a simple explanation, send it my way though I also welcome apocryphal anecdotes and crackpot conspiracy theories (extra points if you can convincingly implicate the Trilateral Commission).

11 comments » | OS X, Programming, Software, Xcode

The Sun Also Sets

January 23rd, 2007 — 1:17pm

Recently, a couple articles caught my attention. Triggered by news of the lack of Java on the iPhone, these articles go on to address Java’s failure on the desktop in general. Check out Duncan Davidson’s post here as well as Jens Alfke’s here. In the spirit of these articles, I’d like to tell a story, one that many have not heard and that, after all these years, should be told. It’s a story not only about what was, but about what might have been.

Years ago, I worked at a company called Lighthouse Design (I refer you here for a brief history and background). Sun bought Lighthouse in 1996. While we were officially Sun, we were somewhat separate. We enjoyed some level of autonomy and even got to stay in our own offices, avoiding a move to Sun’s campus.

It was an odd transitional time for Lighthouse. The codebase for our apps was in Objective-C and Sun was pushing Java. The notion was to possibly port the apps over to Java thus giving Sun an instant desktop suite. Yes, Sun also had OpenStep but Java was shiny, new and all the rage and besides, Sun was slowly killing OpenStep. At the time, the AWT was the only option for doing UI with Java. I don’t think I need to elaborate on why that was a problem. We developed an Openstep-like framework in Java to make porting easier and for the most part we were successful. The Lighthouse Foundation Classes (LFC) were not an exact implementation but they were close enough to make porting more of a mechanical process.

As a result of this, we became another party in the Java GUI Toolkit war. In addition to us, there was Netscape’s IFC also done by NeXT-inspired developers as well as the threat of Microsoft’s AFC. JavaSoft recognized they had to do something about this. The result: they got us and Netscape to discontinue our toolkits in favor of their upcoming project, in order to provide a common front against Microsoft. JavaSoft asked us, along Netscape and Taligent (I forget how they got tangled in this), to join them in the design and implementation what would become Swing.

It quickly became evident that the people involved from the JavaSoft side had little to no experience with OO and/or GUI programming. I realized that if these were the people in charge of implementing the GUI toolkit for a platform, then the project is going to be a mess. Various design discussions ensued with the people who didn’t have any real experience with OO programming or developing and shipping real desktop apps overriding those who did. I soon dropped out. The other LFC team members managed to stick around long enough to do some coding but over time they left as well.

In a nutshell, Sun had at least two toolkits with superior APIs that were ready to go. NIH syndrome was in full effect. Politics and territorialism won.

It’s worth mentioning that Sun is sitting on the Objective-C codebase for all of Lighthouse’s apps; another casualty of war resulting from their hubris. It’s sad how they could ignore those who were experienced (but considered “outsiders”) and how they thought they could figure out this whole desktop experience thing themselves. Sun does make great system-level software but history has shown time and time again that they do not “get it” when it comes to end users and the desktop.

One can only imagine what could have been. A Java GUI framework that was quite similar Cocoa. Or possibly a whole suite of productivity apps for OS X. I can’t say whether it would have been a success for Sun, but at least they would have been taken seriously. Sun “coulda been a contenda.” Sun had the technology and talent to become a force on the desktop and managed to squander both. The code is locked away somewhere in Sun’s vaults and most of the people have moved on to better things (some to places in the Mac sphere). The only thing they seemed to really get out of the Lighthouse deal was a CEO.

P.S. I apologize if I skimped on details concerning the death of OpenStep. If anyone from that team wants to tell that story, let me know and I’ll link to it. Likewise for the IFC team.

Update: Terrence Talbot of Karelia has put up a great writeup of the history from his perspective of being a member of the OpenStep team at Sun with some background on the IFC thrown in as well. It fills in a lot of holes and goes much more into the specific details concerning the politics at Sun. An essential read.

19 comments » | Java

Silencing Hazel

January 15th, 2007 — 4:07pm

One of the main design precepts for Hazel was that it would do its work without bothering you and I feel I was successful in that goal, unless you are a developer. The one place where Hazel can get chatty is the console logs, much to the annoyance of some developers. I know how it can get in the way when you are trying to sift through your own application’s logs (though I do find that typing the process name into the search field does wonders).

Anyhoo, if you want to redirect the logs, you can use a defaults setting. Just type the following in at the command-line:

defaults write com.noodlesoft.Hazel LogFile file

file, of course, is whatever file you choose, including /dev/null. Removing the default will reverse the change. Note that this only applies to the background program; the UI, being a pref pane, logs wherever System Preferences logs, but it’s not nearly as talkative so hopefully this isn’t an issue. Also, there is no UI for this setting as I consider it to be something only developers would care about.

Random tangential reference to a piece of nostalgia/trivia:
When telling a friend (who was the one who brought up the original gripe) about this setting, I referred to the defaults setting as a dwrite which my friend found amusing (we’re both old NeXTies). In any case, along with dread and dremove, dwrite was the precursor to the defaults command and to this day I still refer to defaults settings that are only accessible via the command-line as “dwrites”. Old habits die hard.

Not-so-random, but nonetheless tangential info:
There’s another camp that would say that Hazel is too quiet. I am working on Growl support so if you have any particular opinions or ideas on the matter, let me know in the forums.

3 comments » | Hazel, Noodlesoft, Software

Post Mortem 1.0 or How I Learned to Stop Worrying and Ship an App

January 2nd, 2007 — 6:57pm

A new year is upon us. As is the cliché, it’s a time to look back before looking forward. While my posts have been mostly technical, I thought I’d start talking about the business and logistical end of things.

Hazel launched some months back and seeing as how you only launch a 1.0 once, I thought I’d post some thoughts on my experiences.

What Went Right

Shipping at all
I had to make some hard choices on what features to cut so I could ship. Otherwise, I would have spent much more time in perpetual beta (the Chinese Democracy Syndrome as Gus Mueller likes to call it). Complicating the release schedule was WWDC. I had originally thought about launching before WWDC but fortunately for me, I waited and ended up revamping a chunk of the UI based on feedback I got there. But in the end, I just had to bite the bullet and ship. I think the feedback I’ve gotten since shipping is far more valuable than any extra time spent toiling on it in isolation. Also, having a clear roadmap for the 1.1 release helped me in terms of my own neuroses about shipping prematurely.

Using 3rd party libraries and services
I used Kagi for my purchasing system. While not without faults (I still do not have in-app purchasing set up with Kagi because of some issues), they have worked well with much less effort than rolling my own. If anything, they freed up a huge chunk of mindspace. Also, using Aquatic Prime has been great for me. Overall, the less I had to worry about, the better.

What Went Wrong

Not shipping with Sparkle
I punted on launching with Sparkle for doing software updates. The main reasons had to do with the fact that it wasn’t just build and go for me (Sparkle needs a bit of tweaking to integrate with preference panes). The result was support issues when upgrading (boiling down to Obj-C not being able to unload classes). This was rectified in version 1.1 but in retrospect I feel that it should have been in there from the start.

A Bad Bug Right At Launch
Soon after launch, someone noticed that the Apple Help buttons weren’t working. While not the end of the world, I felt I had to fix it immediately. It is a new product so having help available was more important now than ever. Oddly, it worked fine for me on my main development machine but I was able to replicate the problem on my Powerbook (even though it had worked in a previous beta version on that same machine). I ended up scrambling and released a 1.0.1 release within hours of launch which is somewhat embarrassing. While the obvious lesson would be more regression testing on different hardware, it’s easier said than done. Being the only person here makes it hard to do all the testing a product needs. Probably the topic for another post some other time but for now this remains an unresolved issue.

What I Learned

When running your own business, I have to say that the hardest part of the job is making decisions. Especially being a one-man shop, I do not have the luxury of someone available to bounce ideas off of at will. It’s the decision making that keeps you up at night. The trials of going solo are better served as the subject of another post but I would say that if you are going it alone, use all the resources you have available for getting feedback on your decisions. Personally, I have found the macsb mailing list and IRC channel to be indispensable in this regard. Perspective is a valuable thing.

The other lesson is that bugs will get out there. The important thing is to have the infrastructure in place to deal with them. This ranges from decent feedback channels (email, forums, etc.) to a good QA process to having an easy way for users to keep up to date.

What I Did Not Learn

What I am still not sure about is the marketing end of things. I chose to be somewhat stealth during beta and focus on marketing at launch time. Of course, the cat got out of the bag earlier but the general notion was to not to not overhype too early. Since I was unsure about my exact launch date for a while, I was afraid of any buzz that I generated dying out if I took too long. So, I’m curious as to how other developers out there dealt with promoting their product before and during launch.

So, that’s it, in a nutshell. Not very exciting but what do you expect from a post with “mortem” in the title.

5 comments » | Hazel, Noodlesoft, Software

Icon searching for a home

December 11th, 2006 — 3:45pm

Are you working on some sort of calendaring app? Need an icon?

Icon designer Fernando Lins has got an icon for you. The original client abandoned it but their loss is your gain.

Every year, hundreds of icons go homeless. We hope that in this season of giving, you can find it in your heart to adopt this icon and give it a home for the holidays.

Comment » | Icons

Mmm…Donuts

November 30th, 2006 — 1:33am

This has come up a couple times in the past couple months leading me to believe that there are others out there who might benefit from this. I’m overdue for an post anyways.

The question: How do you draw shapes with holes in them? (i.e. How do I draw a donut?)

Short answer: Winding rules.

So what’s a winding rule? A winding rule is a way to determine whether a point is inside or outside a shape, useful for when you want to fill a shape. Quartz (like Postscript) has two different winding rules: even-odd and non-zero.

Non-zero is the default. How does it work? Take the point in question. Follow a line from that point extending out to infinity. Now, maintain a count. Increment the count every time you cross the path with the path going from left to right. Decrement whenever you cross a path going from right to left. In the end, if the count is zero, you are outside the shape, otherwise you are inside.

nonzerowinding.png

In the diagram above, the final count is zero. Note that this is a compound path (remember, paths do not have to be contiguous). Our imaginary ray crosses the path twice but does so with the paths going in opposite directions. This means that the point in the middle of both circles is considered outside the shape. Voilà , a donut.

With even-odd, it’s a bit simpler. Draw a line out from the point but just increment every time you cross the path (regardless of direction). If the resulting count is even, you are outside, otherwise you are inside.

Except for the labels on the axes, the pic below is actual output from a Cocoa program showing different combinations. The top two shapes have the outer and inner circles specified going in the same direction. The bottom two have the inner cirlce going in the opposite direction as the outer. The shapes on the left are drawn with non-zero winding while the two on the right are drawn with even-odd. Paths are stroked in white.

donuts.png

Notice how the direction of the paths does not matter for even-odd. Also notice how in the case of non-zero with the paths going in the same direction (top-left donut), the inner circle is considered a part of the shape’s interior. This shows that non-zero gives you more control (you can control what parts of the shape are considered inside by adjusting the direction of the paths). It is also more tedious to deal with for the more simple cases. To draw donuts using non-zero, I did the outer circle, created another path for the inner circle, reversed it and added it to the outer circle’s path. Using even-odd, all I had to do is set the winding rule on the path. For more complex cases, the control of non-zero winding may be required but for simple shapes like donuts, use even-odd. Why you’d ever want to draw anything else besides donuts is beyond me; donuts are delicious. While this knowledge can be applied to other less worthy shapes, I cannot be held responsible for the jeers you will undoubtedly receive for even daring to allow to cross one’s mind the slightest notion of contemplating considering drawing anything non-donut-like.

I’m too lazy busy to clean up the code for you to download but play with it yourself. It can all be done via NSBezierPath (key methods are -bezierPathByReversingPath and -setWindingRule:). Also, the winding rules can apply to clipping as indicated by the following pic:

donutclip.png

For extra credit, play with self-intersecting paths.

Enjoy.

Comment » | Cocoa, OS X, Programming, Quartz

Antisocial Software

November 7th, 2006 — 12:10am

Unless you installed your operating system from floppies, chances are that you have a bunch of programs running on your computer at any one time. There are enough things that can go wrong in any one particular program and when you add them into the same soup, the interactions between them create more scenarios where things can go awry. The operating system has ways of preventing programs from mucking with each other via protected memory and resource limits but it’s not that surprising when some program will interfere with another.

It’s something I have to deal with a lot with Hazel. Since Hazel runs in the background and manipulates files it does not have full control over, there’s a lot to be watch out for. I have to coordinate with browsers to make sure not to move the file while it’s still being downloaded. I try and detect when the user is copying something with the Finder (which for some reason uses ‘brok’ as the file type instead of the ‘bzy ‘ or ‘bzy?’ types which I believe was the convention from the older MacOS days). I run my background processes with lower priority and even use low priority IO. Try this test. Take a large file and copy it. Turn off Hazel’s trash management options, move one of the files to the trash and use Finder’s “Secure Empty Trash”. Observe how usable your machine is while this is happening (for me, the answer is “barely”). Now, turn on Hazel’s trash management, setting the trash size threshold to something lower than the file size. Set Hazel to throw away oversized files immediately and to do so securely. Now, throw away the other file. You can tell something is happening but at least from all my tests, my machine is more responsive.

So, it’s a bit frustrating when you go through the trouble of making your software a good citizen when someone else’s software goes out of their way to interfere with yours.

A while back, I got a report from a customer that Hazel was not working. Looking at the logs, the customer noticed that it would stop working whenever he ran Logic Pro. When he quit Logic, Hazel would magically work again. At first, I didn’t believe it. It seemed too weird but I downloaded a trial of Logic Express and sure enough, I experienced the exact same behavior. After a bit of poking around I discovered what was happening. Logic Pro/Express stops all launchd jobs. Hazel uses launchd to start its background processes so it was a bit disconcerting to see another program, especially one from Apple, disabling yours on purpose, albeit indirectly. At least Logic is nice enough to start the jobs again when it quits.

Now, I’m sure the Logic team is probably doing this to ensure a level of performance but scouring the Apple lists and the web at large turned up nothing about this behavior. Maybe a tech note somewhere would have been nice. It’s unclear if any of Apple’s other pro apps exhibit this behavior but at least now I know what to look for.

Should this be a preference in Logic? Maybe a warning when it detects that the user has their own launchd jobs? I know it’s not common for software out there to depend on launchd but it is pretty annoying for those of us it does affect. I could write my own daemon using kqueue (which launchd uses to monitor file changes). Logic will not interfere with it then, but part of me feels that this would make Hazel impolite. Logic is doing what it’s doing for a reason and to work around its intentions goes against Hazel’s good manners. So for the time being, I’ll just have to accept that the housekeeper must go on break whenever the cops come in to commandeer the house.

Comment » | Hazel, OS X, Programming

Hazel 1.1

October 26th, 2006 — 8:03pm

It’s here. A bunch of improvements and fixes, including the ability for rules to execute Automator workflows, AppleScript and shell scripts. Since this blog is a bit more developer oriented, maybe one of you out there can come up with some interesting scripts.

This release also incorporates Sparkle for software updates. In actuality, I’m using the Sparkle+ variant, though I’m not using the system profiling aspect of it. If any of you are developing a pref pane and want to incorporate Sparkle, feel free to drop me a line as you have to change a few things. Tom Harrington, who is the maintainer of Sparkle+ also has experience doing this but I’m not going to volunteer him for questions though if you join the Sparkle+ mailing list I’m sure he’ll answer them.

In short: Sparkle good. In retrospect, I should have included it from the beginning since you get problems when installing a pref pane over itself. I know that Objective-C can’t unload classes but you’d think that System Preferences would restart when this happens. It does detect it since it asks the user if they want to install over the old one.

At the risk of raising the ire of those Geico cavemen: Download. Buy. *grunt*

Comment » | Hazel, Noodlesoft, Software

Previewing NSColors

October 16th, 2006 — 12:40pm

Many of you probably know about this already but for those who don’t, here’s a nifty little tidbit for you developers.

NSColor has all those methods to retrieve system colors. Wouldn’t it be nice to see what they actually are? Bring up a color panel (doesn’t matter which app), select the color list icon (it should be the 3rd icon) up top. In the “List” pop-up, select “Developer”.

color-panel.png

What you get is a list of colors with NSColor’s method names. In addition, it seems to update if you change your color scheme in System Preferences.

Not that this clears up all questions. Still not sure where those purple “knob” colors appear.

4 comments » | Cocoa, OS X, Programming

More Fun With Gradients

October 11th, 2006 — 4:11pm

For the past couple weeks on and off (mostly off), I’ve been working on this gradient code. In my previous post, I mentioned my attempts at vectorizing it which resulted in no performance gain in the end. Here, I recount a tale of writing a generic gradient method in a NSBezierPath category. Yes, a tedious story, but this is a blog after all, plus you stand to get some actually-useful, free source code in the end so bear with me. Or, like a kid pouring all the cereal out of the box to get the glow-in-the-dark prize inside, you could, through the power of the scrollbar, skip all this and just grab the code.

So yes, I wanted to write a category to provide a method to fill the path with a gradient. Of course, you need to provide a start and end color but I also thought, why not also provide an angle? I didn’t need to do gradients at arbitrary angles (the only gradients I need right now are vertical) but when writing this stuff, I like to make it generic. When Apple starts using -23.728° angle gradients all over the place, I’ll be ready.

Now, the issue is figuring out the starting and ending points for the gradient given an angle. You want the gradient to fully fill the path. Let’s take filling a rectangle at a specific angle:

gradients-figure1.png

In the figure, with an angle α and a starting point A, you’d want the gradient to end at B to maintain the angle and also fill in the opposite corner of the rectangle. Recalling the trig and geometry from the deep recesses of my mind, I came up with the following equation (using known quantities w, h and α):

x = sqrt(h2 + w2) * cos(α - atan(h/w)) * cos(α)

I was a little disappointed that this didn’t reduce down to something a bit more tidy (makes you appreciate E = mc2) but it did seem to work. The problem: not every path is a rectangle. While using the above does cover whatever path may be bound by that box, it is not optimal. Another diagram is in order.

gradients-figure2.pngsadsun.png

In this example, there’s slop at the start and end. Imagine pulling a squeegee from point A to point B. There’s a gap before you hit the shape starting from A and then a gap after the shape before you hit B. How does this affect the gradient? Why is Mr. Sun sad? See below:

gradients-figure3.png

All these diagrams were done in OmniGraffle (except for Mr. Sun which obviously required more sophisticated software). You can see that the “L” shape on the left has a truncated gradient (the black is somewhere where the upper right corner should be). As a user, I’d expect that if I had specified a gradient from white to black, that I would get that range within the shape, otherwise, I would have specified gray as the darker color. It appears OmniGraffle is shading according to the bounding box. Here’s another picture:

gradients-figure4.png

The first shape I freehanded with the correct orientation. The second is the “L” shape from the previous diagram rotated. Notice the difference in gradients. It seems to me the user should not need to care how the shape was originally created. The gradient should be white to black in both cases. By the way, I’m not picking on OmniGraffle; I was using it for these diagrams and decided to test it’s behavior in this regard since I had it running. My guess is that this is the common behavior for software implementing gradients with rotation.

If someone specifies an angle and start and end colors, then the final result should be at that angle and have those start and end colors visible in the gradient and not cut off, dag gummit! I feel in the ideal case, you should really be filling the shape with a gradient, not just providing a window to what ever portion of the gradient happens to be lined up under the shape.

The problem here is that we are using the bounding box in the original coordinate system. What would be ideal is to calculate the bounding box in a rotated coordinate system so we can get the minimum span along the axis of the gradient (the line going through A to B in the first two diagrams).

gradients-figure5.png

In the case above, we want the bounding box on the right.

My initial approach was to apply the path to the current state, transform the coordinate space to the rotated space and get the bounding box there. The problem with this is that I was getting the bounding box of the bounding box in the original coordinate system. In a sense, I was getting what I originally calculated above. The solution was to transform the path and not the coordinate system.

After some futzing and some stupid mistakes which sent me down some dead-ends, I got it working. Rotate the path, calculate the bounding box and set the start and end points to the edge of the bounding box that corresponds to the gradient axis, then do an inverse transform back on those two points to get them back into the original coordinate system.

What does this get me? Well, for me personally, not much as I mentioned before I only needed to do a vertical gradient. But you, gentle reader, get to enjoy the fruits of my labor. This code is provided under MIT license. If you use it then just mention me and the Noodlesoft site wherever you put your attributions. And of course, if you have any bug reports, fixes, suggestions or whatever, send them my way so I can address/incorporate them.

Noodle Gradient Test + NSBezierPath-NoodleGradient category

5 comments » | Cocoa, Downloads, OS X, Programming, Quartz

Back to top