emacs inside of screen is made difficult because both want to use C-A for something important. C-O (AFAIK so far) isn't too useful for my use of emacs, so I use that for screen now:
screen -e^Oo emacs
screen -r
$HOME directory, plus the /Applications directory. There's probably other stuff that should be snarfed (like /Library), but I didn't on my last backup, and doing a restore from scratch seemed to work OK. Here's a quick outline of backing up to the external firewire drive called "Wikkit":
% sudo mkdir /Volumes/Wikkit/backup
% cd /Users
% sudo hfstar cf - bork | (cd /Volumes/Wikkit/backup; sudo hfstar xf -)
To Restore:
% cd /Users
% sudo mv bork bork-orig
% cd /Volumes/Wikkit/backup
% sudo hfstar cf - bork | (cd /Users; sudo hfstar xf -)
Some folks have had success with psync, but we haven't used it yet.
~/.inputrc file:
set completion-ignore-case On
Then when you type ls /appli in the shell you'll get ls /Applications/ like you ought to!
% ~/bin/rename.pl 's/.oop$/.ack/' *.oop
To tack on an extension to all files, do something like
% ~/bin/rename.pl 's/(.*)/$1.jpg/' *
% perl -pi -e 's/\r/\n/g' mealcsv.csv
tr '\r' '\n' < macfile.txt > unixfile.txt
% hdiutil create -ov -imagekey zlib-level=9 -fs HFS+ -format UDZO -scrub -srcfolder /Path/To/The/Goodies fooby.dmg
# svc -d /service/whatever - Bring the server down
# svc -u /service/whatever - Start the server up and leave it in keepalive mode.
# svc -o /service/whatever - Start the server up once. Do not restart it if it stops.
# svc -t /service/whatever - Stop and immediately restart the server.
# svc -k /service/whatever - Sends the server a KILL signal. This is like KILL -9. If svc -t fails to fully kill the process, use this option.
(Thanks to the OpenACS documentation)
% cd /Users/bork/Library/Screen\ Savers
or you can surround it with quotes:
% cd "/Users/bork/Library/Screen Savers"
Note that tilde expansion (~/Library) is suppressed if you use the quotes.
% screen -r
There is a screen on:
8395.pts-0.vikki (Multi, attached)
You can boot off that other loser by using:
% screen -d 8395.pts-0.vikkiand then doing
screen-r again
% du -sk dir_name
To see how much each of a directory's contents consume, use
% du -sk dir_name/* | sort -n
to get a list ordered by size.
% cd directory
% find . -name "*.h" -exec grep NSDoWhatIMean {} ; -print{} construct. Also make sure there's a space after the {} and ;
-h flag:
% grep -h chicken ~/Documents/ircLogs/FreeNode/2007*/#macsb*
% sw_vers
ProductName: Mac OS X ProductVersion: 10.3.7 BuildVersion: 7S215
% ps -eo "%p %P %c"
% find . -type f -perm +06000 -print
(or use find / to start looking from the root directory.)
% grep -c "fix me" *.m
% curl -O http://borkware.com/quickies/files/fullscreensaver.tar.gz
-l (dash ell) flag to grep. If a file contains the expression, the name of the file (rather than the matching line) is displayed.
e.g. to open all the files that contain 'rzolf'
% grep -l rzolf *.ham | xargs open
-e option to the rescue:
% grep -e -H filename
% hdiutil mount diskimage.dmg
LANG environment variable to be en_US to make it happier.
% rm -- -i-am-a-bad-file
% perl -pi.bak -e 's/OLDSTRING/NEWSTRING/g' *.html
% find . -type f -name "*.html" -exec grep Spoon {} ; -print
% sudo sh -c "tar cf - * | (cd /mirror4/web; tar xf -)"
% xmllint -noout kipple.xml
Handy for a quick check after hand-editing a file.
% gcc -v --help
ls -l:
% ls -l ~/junk/SnapshotRepository.sparseimage -rw-r--r--@ 1 markd markd 135270400 Jul 24 20:38 /Users/markd/junk/SnapshotRepository.sparseimageThat means there's some extended attributes. use
ls -l@ to see them:
% ls -l@ ~/junk/SnapshotRepository.sparseimage -rw-r--r--@ 1 markd markd 135270400 Jul 24 20:38 /Users/markd/junk/SnapshotRepository.sparseimage com.apple.diskimages.fsck 20
% tail -f /var/tmp/console.log
% unzip -l snorgle.zip
ppid option with the -o flag:
% ps -axo user,pid,ppid,vsz,tt,state,start,time,command
^t is the hotkey, doing ^t ^g will toggle the visible bell on and off.
% find . \( -name "*.jpg" -or -name "*.gif" \) -exec do_something_with {} ;
% find . -name "*.jpg" -exec convert -verbose -geometry 150x150 {} {} ;
% identify filename.jpg
If you want to be studly, you can use -format to tailor
the output
% identify -format "insert into bw_graphics values (generate_primary_key(), '/images/llamas/%f', %w, %h);" *.jpg
generates a sql insert statement for each image.
nm outputs a three-column format. I needed to get the set of defined symbols (in particular those that start with __, in tracking down a C++ linking problem), which is the third column. awk is good for that. Use $N to get the nth column (zero-index). This pipeline did the trick:
nm ./oopack.o | awk '{print $2}' | sort | grep __
(Thanks to DougEDoug for the pointer)
% awk '{ for (f=1; f <= NF; f++) { if (f != 1 && f != 9) printf("%s ", $f);} printf("
");}' < ook.txt
Or you can put this at the end of a pipeline instead of directing a text file into the command. (courtesy of DougEDoug)
% tar cf - ./stuff | ssh theOtherMachine "tar xf -"
% cd /Users/bork/development/cool\ stuff/neat\ things
The other is to use shell completion to put in the backslashes for you. In
the above path, if you type
% cd /Users/bork/development/cool[tab], it'll expand
"cool stuff" for you.
cp -R is generally unsafe to copy directories with since it will copy through symbolic links rather than just copying the link (newer cp's have an -H flag that will work around this). The classic unix way of doing things like this is a push-pull tar:
% tar cf - dir-name | (cd /destination-dir; tar xf -)
In English: tar out through a pipe into a subshell that's changed directories, feeding that pipe into the tar extraction. If you run both sides of the pipeline as root (via sudo), the file owners and permissions will be preserved (which cp won't do)
rf command to append to an archive rather than creating one:
% find . -name "*.yuck" -print0 | xargs -0 tar rvf oop.tar
-print0 (zero) tells find to output the filenames with trailing NUL characters (rather than newlines), and -0 tells xargs to use a NUL as its input separator, rather than newlines/spaces. The upshot is that you can handle filenames with spaces in them.
Thanks to Ben Cox for the -print0 / -0 suggestion.
% ssh-keygen -t rsa
% scp ~/.ssh/id_rsa.pub gitdown.com:
% cat ~/id_rsa.pub >> ~/.ssh/authorized_keys
mv: rename build/ to oopack: Is a directorywhen using
mv or ln on the command line, that's a Darwin error. To correct the problem, remove the trailing slash from the directory name.
% sudo tcpdump -Atq -s 0 -i en1
-i en1 will display traffic on your airport card. Use en0 (or nothing, for most systems) for built-in ethernettage.
You can add a host line to limit output to a particular host
% sudo tcpdump -Atq -s 0 -i en1 host zombo.com(thanks to Dan Jalkut for this one)
touch. You can specify a specific date or time using the format [[CC]YY]MMDDhhmm[.SS]]. Here's an example with alternating bolds to make it easier to read:
% touch -t 200703141536 snork.waffle % ls -l snork.waffle -rw-r--r-- 1 markd markd 298 Mar 14 15:36 snork.waffle
% hdid ram://size
where size is the size in bytes. the system will round that number up or down as it sees fit.
% sw_vers -productVersion 10.5.1
/usr/bin/drutil info to get a listing of the kinds of media your drive supports. For example, on the 17" iLamp:
% drutil info
Vendor Product Rev
PIONEER DVD-RW DVR-104 A227
Interconnect: ATAPI
SupportLevel: Apple Shipping
Cache: 2000k
CD-Write: -R, -RW, BUFE, Test, IndexPts, ISRC
DVD-Write: -R, -RW, BUFE, Test
Strategies: CD-TAO, CD-SAO, DVD-DAO
chflags in Mac OS X works like chattr over in Linux-land. So to make a file immutable (can't change it, even if you're the superuser), you can sudo chflags uchg file-name(s). To undo it, do sudo chflags nouchg file-name(s)
# niutil -create . /machines/tower # niutil -createprop . /machines/tower ip_address 10.0.1.155 # niutil niutil -createprop . /machines/tower serves ./local
/System/Library/StartupItems/SystemTuning/SystemTuningtweak some values, like
sysctl -w kern.sysv.shmmax=167772160 # bytes: 160 megs sysctl -w kern.sysv.shmmin=1 sysctl -w kern.sysv.shmmni=32 sysctl -w kern.sysv.shmseg=8 sysctl -w kern.sysv.shmall=65536 # 4k pages: 256 megsSave, and restart your system.
On some Panther seeds, you may need to add these things to /etc/rc
/etc/rc, but you want to be able to back out your changes. You also want to preserve the original timestamp of the file. That way if you back out your change, someone else coming along doesn't have to figure out "why did /etc/rc change yesterday, it doesn't look any different?" Here's how:
% sudo mv rc rc-orig
% sudo cp rc-orig rc
% sudo vi rc
% sudo mv rc-orig rcThereby undoing your changes, and not messing up the timestamp of the original file.
(mucho thankos to Louis Bertrand for this one)
cc1obj: error: type '<built-in>' does not have a known size(void), a typo that should have been (void *).
@executable_path/../blah. Sometimes you get a library from someone else (or something generated by a gigantic configure script) and need to change the install name. The install_name_tool will do that for you. Say I had a library, libbork.dylib, that will end up being placed side-by-side with the executable of a cocoa app, this is how I would fix it:
% install_name_tool -id @executable_path/libbork.dylib ./libbork.dylib
In Interface Builder 2: If you hold down the control key while resizing, though, the contents obey their spring configurations and will resize accordingly. Makes the process a whole lot easier.
In Interface Builder 3: If you hold down command while resizing, the contents obey their spring configurations and will resize accordingly. (This avoids the annoying process of resizing each widget within the windows afterwards.) Holding option while resizing will display the pixel values of the padding. Holding command-option while dragging displays both.
(muchos thankos to Quinn Taylor at the BYU CocoaHeads for the IB3 update)
% gcc -E -dM -x c /dev/null
(thanks to xmath on #macdev for this one)
% otool -L /path/to/application.app/Contents/MacOS/application
The problem is, if I include the usefulStuff.c in my plugin and my test harness, there are two copies of the static variables, and the test harness can't control the plugin. If I don't include usefulStuff.c in the plugin I get link errors. If I don't declare as weak linked the functions I use, I'll get errors when the final program loads the plugin. Sucks to be me.
Here's one way of doing it (which is kinda hacky, but after spending a day inside of the ld man page, and other pain, the fact it works is good enough for me for right now).
In the source file for the plugin that uses stuff from usefulStuff:
Declare the function to be weak:
extern void BWDebugLog (int blah, char *blah) __attribute__((weak_import));(you can also do this in your header files. In this case, I didn't want to touch the header)
Before you use the function, make sure it's not NULL. Note there's no trailing () after the function name.
if (BWDebugLog != NULL) {
BWDebugLog (23, "stuff");
}
In the Xcode project for the plugin
Add the flags -flat_namespace and -undefined dynamic_lookup to the "Other Linker Flags" (a.k.a. OTHER_LDFLAGS). The flat namespace lets the symbols be found in the test harness when the plugin is loaded. The undefined dynamic_lookup means to suppress warnings about undefined symbols. This could conceivably mask errors, but it's no worse than ZeroLink.
In the Xcode project for the test harness
Add usefulStuff.c, and turn on the "Preserve Private External Symbols" checkbox (a.k.a -keep_private_externs). That turns the symbol BWDebugLog (and others) into exported symbols that'll be visible to the plugin. Otherwise the plugin will never get past that != NULL check earlier.
Once all that's done, my plugin loads into the test harness and can get controlled. It can be loaded into the Real App without undefined symbol errors.
2009-06-01 12:49:29.447 otest[70099:203] *** NSTask: Task create for path '/blah/blah/blah' failed: 8, "Exec format error". Terminating temporary process.
You can force otest to run a particular architecture with the arch command:
% arch -arch i386 /Developer/Tools/otest build/Debug/Snoogle.octest
BWNagType.h:23: `BWConcreteType' defined as wrong kind of tag
In this particular case, I had a type I had declared with:
@class BWConcreteType;
and later on I had decided to turn it into an enum:
typedef enum BWConcreteType { ... } BWConcreteType;
without removing the previous @class declaration. So be on the look out for conflicting types for the same symbol.
xcodebuild. We've got a rant about cocoa development in emacs, which includes a discussion of pbxbuild, the predecessor to xcodebuild. Some of the arguments have changed over the years. I usually run it like: % xcodebuild -configuration "Debug" -target "My Groovy App"If you have one target, or have a default target, you can leave out the
-target argument.
@interface FSContinousTextField : NSTextField
@end
@implementation FSContinousTextField
- (void) textDidChange: (NSNotification *) aNotification {
[[self target] performSelector:[self action] withObject:self];
}
@end
#define CONVERT_SYMBOL_TO_NSSTRING_2(x) @#x #define CONVERT_SYMBOL_TO_NSSTRING(x) CONVERT_SYMBOL_TO_NSSTRING_2(x) NSString *version = CONVERT_SYMBOL_TO_NSSTRING (BUILD_NUMBER);(Thanks to TVL for helping me figure this one out)
% defaults write com.borkware.snongflozzle ookook -array thing1 thing2 thing3
[self performSelector: @selector(whatever) withObject: nil afterDelay: 0];
The selector gets posted after a sub event loop finishes. You can use this for finding out when a live slider is done being manipulated, for instance
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
gridType = [defaults objectForKey: @"gridType"];
Will have the value of "flarn" whether you do something like
% defaults write com.borkware.BorkStitch gridType flarnor
% ./build/Debug/BorkStitch.app/Contents/MacOS/BorkStitch -gridType flarn
void giveSomeLove ()
{
// give the app some love so it'll update the window
[[NSRunLoop currentRunLoop] runUntilDate: [NSDate distantPast]];
} // giveSomeLove
unsigned index;
for (index = [indexSet firstIndex];
index != NSNotFound; index = [indexSet indexGreaterThanIndex: index]) {
...
}
(courtesy of mikeash)
if ([NSBundle loadNibNamed: @"theNibName.nib" owner: self]) {
... life is happy
} else {
... couldn't load the nib
}
NSRect selectionRect = ...;
NSValue *value;
value = [NSValue valueWithRect: selectionRect];
% find . -name "*.h" -print -exec cat {} \; > ~/moby-file.h
void *tempCopyOf(void *data, NSUInteger size)
{
return data ? [[NSData dataWithBytes:data length:size] mutableBytes] : NULL;
}
OBTW, these techniques will fail under GC. Here's how you'd write a working one under GC:
void *tempCopyOf(void *data, NSUInteger size)
{
void *buffer = NULL;
if( data ) {
buffer = NSAllocateCollectable(size, 0);
memcpy(buffer, data, size);
}
return buffer;
}
Unfortunately I'm not aware of a nice way that works in dual-mode code.
Quicktime X's player doesn't have an A/V controls window, but you can option-click the fast-forward button to increase the speed by 0.1x. Keep clicking to go faster. Unfortunately you can't pause when sped up, so if you pause, you have to re-click to your desired speedup.
NSString *filename = @"/this/is/my/file/path";
NSData *data;
data = [NSData dataWithContentsOfFile: filename];
In this case, the selector being called takes two arguments, one of which is an object, the other is an NSTimeInterval. The atIndex: jazz starts with 2 so that the self parameter and the selector can be passed to the method.
NSMethodSignature *signature = [target_ methodSignatureForSelector:selector_];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector_];
[invocation setTarget:target_];
[invocation setArgument:&self
atIndex:2];
[invocation setArgument:&lastWait_
atIndex:3];
[invocation invoke];
- (IBAction) openEarthinizerDoc: (id) sender {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel beginSheetModalForWindow: self.window
completionHandler: ^(NSInteger result) {
if (result == NSFileHandlingPanelOKButton) {
NSURL *url = [[panel URLs] objectAtIndex: 0];
[self openURL: url];
}
}];
} // openEarthinizerDoc
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setPrompt: @"Stuff for the Choose Button"];
[panel beginSheetForDirectory: nil
file: nil
types: [NSArray arrayWithObject: @"buf"] // or other file types
modalForWindow: window
modalDelegate: self
didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
contextInfo: nil];
and the didEndSelector implementation looks kinda like this:
- (void) openPanelDidEnd: (NSOpenPanel *) sheet
returnCode: (int) returnCode
contextInfo: (void *) context
{
if (returnCode == NSOKButton) {
NSArray *fileNames = [sheet filenames];
NSLog (@"wooOOooot! %@", [fileNames objectAtIndex: 0]);
}
} // openPanelDidEnd
Localizable.string, your NSLocalizedString() call will not look up the strings in the file. To verify the file, do something like this:
% pl < Localizable.strings
% gcc -E -dM - </dev/null
// NSLog() writes out entirely too much stuff. Most of the time I'm
// not interested in the program name, process ID, and current time
// down to the subsecond level.
// This takes an NSString with printf-style format, and outputs it.
// regular old printf can't be used instead because it doesn't
// support the '%@' format option.
void QuietLog (NSString *format, ...) {
va_list argList;
va_start (argList, format);
NSString *message = [[[NSString alloc] initWithFormat: format
arguments: argList] autorelease];
printf ("%s
", [message UTF8String]);
va_end (argList);
} // QuietLog
# pmset lidwake 0
NSString *pathString = ... whatever ...;
FSRef ref;
status = FSPathMakeRef ((const UInt8 *)[pathString fileSystemRepresentation],
&ref, NULL);
if (status != noErr) {
NSLog (@"bummer. couldn't make FSREf from path '%@'",
pathString);
}
% /Developer/Tools/DeRez -useDF ./blarg.rsrc /Developer/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon/MacTypes.r > oopack2And the reverse process is like:
% /Developer/Tools/Rez -o blah.rsrc -useDF /Developer/SDKs/MacOSX10.4u.sdk/Developer/Headers/FlatCarbon/MacTypes.r oopack2
% cat /System/Library/Frameworks/Foundation.framework/Headers/*.h > ~/moby % cat /System/Library/Frameworks/AppKit.framework/Headers/*.h >> ~/moby % chmod 444 ~/moby
- (void) snorgWaffle: (NSString *) start, ... {
va_list argList;
va_start (argList, start);
NSString *string = start;
while (string != nil) {
NSLog (@"Done did saw string '%@'", string);
string = va_arg (argList, NSString *);
}
va_end (argList);
} // snorgWaffle
and can be called like
[self snorgWaffle:@"red", @"planet", @"zeitgeist", nil];
printf ("hello %.*s\n", 5, "there is no good going on");
NSLog (@"hello %.*s\n", 5, "there is no good going on");
results in
hello there 2007-05-04 09:14:15.575 printf[4914] hello there
Similarly, something like printf ("hello %*.*s\n", 10, 5, "florgsnorgle"); will right-justify 'florg' in a field of 10 characters.
(Thanks to TVL for this goodie)
int AmIBeingDebugged(void)
{
int mib[4];
struct kinfo_proc info;
size_t size;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
size = sizeof(info);
info.kp_proc.p_flag = 0;
sysctl(mib,4,&info,&size,NULL,0);
return ((info.kp_proc.p_flag & P_TRACED) == P_TRACED);
}
#define StopIfInDebugger() __asm__ volatile ("twnei %0,0" : : "r" (AmIBeingDebugged()))
(courtesy of arwyn on #macdev. Useful uses: controlling voluminous console output when running in gdb, or if running non-debugged, an assert routine that generates stack trace and logs it when not-being debugged, but just traps into the debugger at the point of failure when being debugged)
gdb There are two ways to enable core files:
% limit coredumpsize unlimited.cshrc. I don't know what to do for bash)
struct rlimit corelimit = { RLIM_INFINITY, RLIM_INFINITY };
setrlimit (RLIMIT_CORE, &corelimit);
You may need to also add#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
Core files seem to be getting dumped into /cores, rather than the directory the program was run from.
/System/Library/Frameworks/Foundation.framework/Headers/NSDebug.h. Lots of low-level unsupported APIs for mucking around with gory technical details.
% defaults write com.apple.CrashReporter DialogType none
Turn turn it back on, replace none with prompt to get the original behavior. See for details.
fprintf
% gdb build/Debug/Snorkfizzle.app/Contents/MacOS/Snorkfizzle (gdb) set env NSZombieEnabled=YES (gdb) fb -[_NSZombie methodSignatureForSelector:] (gdb) runThen exercise your app and trip the error.
(gdb) thread apply all where
(gdb) fb -[NSTextView drawRect:]
b objc_msgSend comm silent printf "%c[%s %s]\n", $r3&&((id)$r3)->isa->info&2?'+':'-', $r3?((id)$r3)->isa->name:"nil", $r4 cont end b objc_msgSend_rtp comm silent printf "%c[%s %s]\n", $r3&&((id)$r3)->isa->info&2?'+':'-', $r3?((id)$r3)->isa->name:"nil", $r4 cont endAnd you'll get some output like this:
-[NSTableColumn _bindingAdaptor] +[NSBinder binderClassesForObject:] +[NSBinder _allBinderClasses] +[NSDisplayPatternTitleBinder isUsableWithObject:] +[NSBox self](courtesy of Rob Mayoff)
Borkdoku(11062,0xcec0600) malloc: *** error for object 0xd109010: incorrect checksum for freed object - object was probably modified after being freed, break at szone_error to debugWhich is fine and dandy, but it lies. I've never gotten
szone_error to actually do anything. Try breaking on malloc_printf instead.
2007-05-05 17:18:00.702 QueenStitcher[2804:117] *** Assertion failure in -[NSColorWell setColor:], NSColorWell.m:497, u suk l0s3r, and then the runloop happily runs again, giving you no clue where the problem is. I tell gdb to always break on Cocoa exceptions:
fb -[NSException raise] fb objc_exception_throw()For maximal enjoyment, add these two lines to your
~/.gdbinit file, so they'll get set no matter how you invoke gdb (no need to add these to every single project, for instance).
I've been told VoiceOver uses exceptions heavily, so if you're doing VoiceOver development, these breaks may cause you pain.
'bork'. You can have gdb print them out if you need to look at one or two of them:
(gdb) print/T 1936746868 $4 = 'spit'(thanks to Daniel Jalkut for the print/T trick)
(gdb) po *(int*)($ebp+8)
(gdb) x /80xb attrBufferwill give you something like
0x7fff5fbfeeb0: 0x14 0x00 0x00 0x00 0xdb 0x0b 0x19 0x4c 0x7fff5fbfeeb8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x7fff5fbfeec0: 0x00 0x00 0x00 0x00 0xec 0xc8 0x62 0xe5 0x7fff5fbfeec8: 0xc7 0x48 0x18 0xd2 0xe7 0x9e 0x88 0xd8 0x7fff5fbfeed0: 0xd9 0x60 0x9f 0x61 0xea 0x37 0x3b 0x0c 0x7fff5fbfeed8: 0x87 0x1b 0x4e 0x7e 0x6a 0x26 0x98 0x62 0x7fff5fbfeee0: 0x6d 0x01 0xed 0xed 0x17 0x6d 0xae 0x05 0x7fff5fbfeee8: 0xe7 0x22 0xd0 0x96 0x33 0x34 0x16 0x1a 0x7fff5fbfeef0: 0x17 0xa7 0x87 0xb2 0x76 0x7b 0x29 0x0b 0x7fff5fbfeef8: 0xea 0x51 0x4c 0x45 0x4f 0x45 0x65 0x88
(gdb) handle SIGTRAP nostop
The signal still goes to your program. Another handy option is 'ignore' to prevent it coming to the program. Also there is 'print' to print a message go on.
The attach command's -waitfor option to make gdb lie in wait for a launching process:
% gdb /some/app/blah (gdb) attach -waitfor blahNow when a
blah process launches, gdb will stop it and attach to it.
$r3 and go up from there. For Objective-C method sends, $r3 has 'self', and $r4 has the name of the method. Subsequent arguments are in $5 and so on.
(gdb) print (char*) $r4 $5 = 0x90874160 "drawRect:" (gdb) po $r5 <BWStitchView: 0x1a6670>
(gdb) print (int)[theObject retainCount]
If you're expecting to have an excessively high number of retains, you can use (unsigned int) in the cast. I find (int) a skootch faster to type.
.gdbinit and then you can use wchar_print:
define wchar_print
echo "
set $i = 0
while (1 == 1)
set $c = (char)(($arg0)[$i++])
if ($c == '\0')
loop_break
end
printf "%c", $c
end
echo "
end
document wchar_print
wchar_print <wstr>
Print ASCII part of <wstr>, which is a wide character string of type wchar_t*.
end
info selectors will show you all of the selectors in the application's symbol table. info functions will show you all of the functions. You can supply regular expressions to limit the output.
libgmalloc puts guard pages at the end of malloc'd blocks of memory, letting you catch buffer overruns. (This will hugely inflate your program's working set, and may lead to swapping) To turn on libgmalloc in gdb, do this:
(gdb) set env DYLD_INSERT_LIBRARIES /usr/lib/libgmalloc.dylib
(gdb) call (void)[textField setStringValue: @"Bork"]
% gdb /Developer/Tools/otest
2008-10-31 19:29:50.834 otest[711:813] Error loading
/blah/blah/blah/build/Debug/Tests.octest/Contents/MacOS/Tests:
dlopen(/blah/blah/blah/build/Debug/Tests.octest/Contents/MacOS/Tests,
265): no suitable image found. Did find:
/blah/blah/blah/build/Debug/Tests.octest/Contents/MacOS/Tests:
mach-o, but wrong architecture
2008-10-31 19:29:50.887 otest[711:813] The test bundle at
build/Debug/Tests.octest could not be loaded because it is built for a
different architecture than the currently-running test rig (which is
running as unknown).
2008-10-31 19:29:50.904 otest[714:203] *** NSTask: Task create for
path '/blah/blah/blah/build/Debug/Tests.octest/Contents/MacOS/Tests'
failed: 8, "Exec format error". Terminating temporary process.
You can supply a -arch flag to pick what you want:
% gdb -arch i386 /Developer/Tools/otestAnd then debug your 32-bit unit test.
(gdb) call (id) objc_getClass("NSBundle")
$1 = (struct objc_object *) 0xa0a051d8
gdb) call (id)[$1 bundleWithPath:@"/blah/blah/PoseAsClassBundle.bundle"]
$2 = (struct objc_object *) 0x51467e0
(gdb) call (BOOL)[$2 load]
Reading symbols for shared libraries . done
.emacs file:(global-set-key "C-Z" nil)
#define DONT_REOPEN_PTY
(setq c-default-style "bsd"
c-basic-offset 4)
deletechar into backward-delete-char-untabify causes backspace in incremental search to cancel the search, which is annoying.
One option is to set the TERM env var to rxvt:
% setenv TERM rxvtBefore cranking up screen.
C-x ( : start recording keyboard macroC-x ) : stop recording keyboard macroC-x e : replay current keyboard macro
.emacs file. This will make spaces the indent character, and use 4 spaces per indent level, for C, C++, and Objective C:
(setq c-mode-hook
(function (lambda ()
(setq indent-tabs-mode nil)
(setq c-indent-level 4))))
(setq objc-mode-hook
(function (lambda ()
(setq indent-tabs-mode nil)
(setq c-indent-level 4))))
(setq c++-mode-hook
(function (lambda ()
(setq indent-tabs-mode nil)
(setq c-indent-level 4))))
!$). M-x dirs will tell the shell buffer to figure out what the current working directory is.
M-x narrow-to-region
Hides everything not in the current region.
$Id: $ tags for CVS are nice, but it can be a pain when you're doing lots of checkins and have to re-load the file each time. You can either execute M-x revert-bufer or bind that to a key, or else use a trick by doing C-x C-v which invokes find-alternate-file, but just so happens to have the current buffer name, so you just have to do C-x C-v RET
uuidgen, for instance:
C-U M-! ret uuidgen ret
C-U 0 C-L(you can put in another number besides zero to scroll the line with the cursor to that particular line in the buffer)
/* For the emacs weenies in the crowd. Local Variables: c-basic-offset: 2 End: */
M-x column-number-mode
C-X C-Q
(setq comint-highlight-input nil)
(global-font-lock-mode -1)
(setq search-highlight nil)
You may also need to
(setq isearch-lazy-highlight nil)
To turn off underlining of matching results. Only some OS X installs need this setting.
(setq comint-scroll-show-maximum-output nil)
C-U C-_
M-x widen
C-Q C-J (control-Q control-J) each time you want to include a carriage return. e.g. to double-space everything
M-x replace-string RET C-Q C-J RET C-Q C-J C-Q C-J RET
Or to put "bloogie " at the beginning of every line
M-x replace-string RET C-Q C-J RET C-Q C-J b l o o g i e SPACE RET
.el files take a long time to load.
You can compile them into .elc files by using:% emacs -batch -f batch-byte-compile filename.el
(select stuff) C-x r x 1where "1" is the register identifier.
Getting stuff out of a register:
C-x r g 1
C-/
[NSApp beginSheet: saveSheet
modalForWindow: window
modalDelegate: self
didEndSelector: @selector(saveSheetDidEnd:returnCode:contextInfo:)
contextInfo: NULL];
In the controls in the sheet, use something like
[NSApp endSheet: saveSheet returnCode: NSOKButton];To invoke the
didEndSelector. Inside of that method, you can check the return code and decide what to do:
- (void) saveSheetDidEnd: (NSWindow *) sheet
returnCode: (int) returnCode
contextInfo: (void *) contextInfo
{
if (returnCode == NSOKButton) {
// ...
} else if (returnCode == NSCancelButton) {
// ...
} else {
// ...
}
[sheet close];
} // saveSheetDidEnd
setFrame: for a window, you have to account for the height of the title bar. In the Classic days it was 16 pixels. In Aqua-land, it's currently 22 pixels. But that's not safe to use, so try this instead:
- (float) titleBarHeight
{
NSRect frame = NSMakeRect (0, 0, 100, 100);
NSRect contentRect;
contentRect = [NSWindow contentRectForFrameRect: frame
styleMask: NSTitledWindowMask];
return (frame.size.height - contentRect.size.height);
} // titleBarHeight
Rainer Brockerhoff points out that this might miss any system-suppled window decorations at the bottom of the window. There aren't any now, but Mac OS X 10.37 Sabertooth might, so you may want to take into account the y positions of the original rectangle and the newly calculated content rect.
window method, that will force the nib file to be loaded. Something like this:
+ (BWInspector *) sharedInspector
{
static BWInspector *s_inspector;
if (s_inspector == nil) {
s_inspector = [[BWInspector alloc]
initWithWindowNibName: @"BWInspector"];
assert (s_inspector != nil);
// force loading of nib
(void) [s_inspector window];
}
return (s_inspector);
} // sharedInspector
[sheet close] in addition to your [NSApp endSheet: sheet returnCode:23]
[panel setHidesOnDeactivate: NO];
setFrame:display:animate: to resize a window, and have the window animate between the two sizes. Remember that Cocoa uses a bottom-left origin, which is a pain when dealing with windows. You want the window's top left to be the same between the old and new sizes, so you have to dink with the origin as well as the size:
float delta = ... how much to make the window bigger or smaller ...;
NSRect frame = [window frame];
frame.origin.y -= delta;
frame.size.height += delta;
[window setFrame: frame
display: YES
animate: YES];
NSViewAnimation lets you resize a window and cross-fade some views in one operation. (note I've had problems with the window size getting a couple of pixels off using this. Hopefully either I'm doing something dumb, or Apple fixes a bug). This code resizes the window, and changes the view that lives inside of a box. This is like how the Pages inspector works (except Pages doesn't do the gratuitous animation effect)
NSRect newWindowFrame = ... the new window size;
NSDictionary *windowResize;
windowResize = [NSDictionary dictionaryWithObjectsAndKeys:
window, NSViewAnimationTargetKey,
[NSValue valueWithRect: newWindowFrame],
NSViewAnimationEndFrameKey,
nil];
NSDictionary *oldFadeOut = nil;
if (oldView != nil) {
oldFadeOut = [NSDictionary dictionaryWithObjectsAndKeys:
oldView, NSViewAnimationTargetKey,
NSViewAnimationFadeOutEffect,
NSViewAnimationEffectKey, nil];
}
NSDictionary *newFadeIn;
newFadeIn = [NSDictionary dictionaryWithObjectsAndKeys:
newView, NSViewAnimationTargetKey,
NSViewAnimationFadeInEffect,
NSViewAnimationEffectKey, nil];
NSArray *animations;
animations = [NSArray arrayWithObjects:
windowResize, newFadeIn, oldFadeOut, nil];
NSViewAnimation *animation;
animation = [[NSViewAnimation alloc]
initWithViewAnimations: animations];
[animation setAnimationBlockingMode: NSAnimationBlocking];
[animation setDuration: 0.5]; // or however long you want it for
[animation startAnimation]; // because it's blocking, once it returns, we're done
[animation release];
// I am the very modal of a modern major general. int blah = [NSApp runModalForWindow:[self window]];and then elsewhere, like in your OK or cancel button
[NSApp stopModalWithCode:NSOKButton];
NSWindowController:
@interface BWInspector : NSWindowController
{
// ivars, IBOutlets, etc
}
// IBActions, etc
+ (BWInspector *) sharedInspector;
@end // BWInspector
Then make a nib file with the window, the controls, and other fun stuff you want. This one lives in English.lproj/BWInspector.nib
Then in that class method load the window:
+ (BWInspector *) sharedInspector
{
static BWInspector *g_inspector;
if (g_inspector == nil) {
g_inspector = [[BWInspector alloc]
initWithWindowNibName: @"BWInspector"];
assert (g_inspector != nil); // or other error handling
[g_inspector showWindow: self];
}
return (g_inspector);
} // sharedInspector
That will load the nib file, set up the connections and then open the window for display
@implementation NSView (BWYellowView)
- (void) drawRect: (NSRect) rect
{
NSColor *transparentYellow;
rect = [self bounds];
transparentYellow = [NSColor colorWithCalibratedRed: 1.0
green: 1.0
blue: 0.0
alpha: 0.333];
[transparentYellow set];
[NSBezierPath fillRect: rect];
} // rect
@end // BWYellowView
I have no idea how you colorize the titlebar.
NSWindow and override sendEvent:
- (void) sendEvent: (NSEvent *) anEvent
{
if ([anEvent type] == NSKeyDown) {
[someOtherWindow sendEvent: anEvent];
} else {
[super sendEvent: anEvent];
}
} // sendEvent
It's up to you to figure out what "someOtherWindow" is, whether it's a delegate or an instance variable that holds the other window.
static void *tempCopyOf(void *data,UInt32 size) {
void *buffer = NULL;
if (data) {
buffer = malloc(size);
if (buffer) {
bcopy(data,buffer,size);
[NSData dataWithBytesNoCopy: buffer length: size freeWhenDone: YES];
}
}
return (buffer);
}
You can get really fancy and assign buffer to calloc(1, size) if you want to give it "pass NULL to get a zero-filled buffer back" semantics.
Also note this doesn't play well with garbage collection - caveat nerdor. (Thanks to Ken Ferry for that note)
poseAsClass: to hook yourself into the matrix during the +load method:
@interface BorkSpellChecker : NSSpellChecker
{
}
@end // BorkSpellChecker
@implementation BorkSpellChecker
+ (void) load
{
NSLog (@"posing");
[self poseAsClass: [NSSpellChecker class]];
} // load
- (void) ignoreWord: (NSString *) word inSpellDocumentWithTag: (int) tag
{
NSLog (@"ignore word: %@ intag: %d",
word, tag);
[super ignoreWord: word
inSpellDocumentWithTag: tag];
} // ignoreWord
The UIApplication itemTimerDisabled controls whether the screen should dim. Disable it by enabling it:
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];or equivalently
[UIApplication sharedApplication].idleTimerDisabled = YES;
Unfortunately, this is not a reliable operation. Sometimes twiddling a UI control or playing music will let the screen dim and lock up. What I ended up doing is toggling the value in a timer:
[[UIApplication sharedApplication] setIdleTimerDisabled: NO]; [[UIApplication sharedApplication] setIdleTimerDisabled: YES];
Open up Internet Connect, select your VPN icon in the toolbar, and choose options from the Connect menu. Uncheck "Disconnect when switching user accounts".
if ([event modifierFlags] & NSShiftKeyMask) {
constrain = YES;
}
If you need to check them outside of your mouseDown, dip into carbon:
NSPoint point = [self convertPoint: [event locationInWindow] fromView: nil];
- (void) keyDown: (NSEvent *) event
{
NSString *characters;
characters = [event characters];
unichar character;
character = [characters characterAtIndex: 0];
if (character == NSRightArrowFunctionKey) {
[self moveSelectedBlockBy: 1];
} else if (character == NSLeftArrowFunctionKey) {
[self moveSelectedBlockBy: -1];
} ... and look at whatever other keys you want
}
NSView's viewWithTag: to find it.
[self lockFocus];
NSBitmapImageRep *bits;
bits = [[NSBitmapImageRep alloc]
initWithFocusedViewRect: [self bounds]];
[self unlockFocus];
Then you can add it to an NSImage or save it to a file, or whatever.
If you want to retain the vectoritude of the drawing, you can use [self dataWithPDFInsideRect: [self bounds]], and then make an NSPDFImageRep. Avoid the NSEPSImageRep since it makes a trip through NSPDFImageRep along with a slow PS to PDF conversion. (Thanks to Peter Hosey for the PDF hint)
NSEventTrackingRunLoopMode, such as you wanting to use an NSNotificationQueue to coalesce updates for your own mouse tracking code. I've been sticking a call to this in my mouseDown: handler for the cases when I want the secondary run loop
- (void) runEventTrackingRunLoop
{
NSEventType eventType = NSLeftMouseDown;
unsigned int eventMask = NSLeftMouseDownMask | NSLeftMouseUpMask
| NSLeftMouseDraggedMask | NSMouseMovedMask;
while (eventType != NSLeftMouseUp) {
NSEvent *event;
event = [NSApp nextEventMatchingMask: eventMask
untilDate: [NSDate distantFuture]
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
eventType = [event type];
if (eventType == NSLeftMouseDragged) {
[self mouseDragged: event];
}
}
} // runEventTrackingRunLoop
(and thanks to Rainer Brockerhof for pointing out a no-op line of code from the original version of this)
@interface Blah : NSView
{
NSPoint grabOrigin;
NSPoint scrollOrigin;
}
@end // Blah
...
@implementation Blah
...
- (void) mouseDown: (NSEvent *) event
{
// deal in window coordinates. there is a scrolling problem
// if using view coordinates because view coordinates
// can get transformed
grabOrigin = [event locationInWindow];
NSClipView *contentView;
contentView = (NSClipView*)[layerView superview];
scrollOrigin = [contentView bounds].origin;
} // mouseDown
- (void) mouseDragged: (NSEvent *) event
{
NSPoint mousePoint;
mousePoint = [event locationInWindow];
float deltaX, deltaY;
deltaX = grabOrigin.x - mousePoint.x;
deltaY = mousePoint.y - grabOrigin.y;
NSPoint newOrigin;
newOrigin = NSMakePoint (scrollOrigin.x + deltaX,
scrollOrigin.y + deltaY);
[layerView scrollPoint: newOrigin];
} // mouseDragged
...
@end // Blah
You can be fancy and check for the option key by look at [event modifierFlags] and looking for NSAlternateKeyMask, and also use the NSCursor open/closedHandCursor.
- (void) keyDown: (NSEvent *) event
{
NSString *chars = [event characters];
unichar character = [chars characterAtIndex: 0];
if (character == 27) {
NSLog (@"ESCAPE!");
}
} // keyDown
NSDeleteCharacter in the event's character string:
- (void) keyDown: (NSEvent *) event
{
NSString *chars = [event characters];
unichar character = [chars characterAtIndex: 0];
if (character == NSDeleteCharacter) {
NSLog (@"Delete!");
}
} // keyDown
flagsChanged: (NSEvent *) event and look at the event there. (flagsChanged: comes from NSResponder, so any responder in the responder chain can react to it)
-[NSView setCursor:] call (drat!) You can get something similar by adding this to your NSView subclass:
- (void) resetCursorRects
{
[super resetCursorRects];
[self addCursorRect: [self bounds]
cursor: [NSCursor crosshairCursor]];
} // resetCursorRects
Set it "permanently" via
% defaults write com.apple.TextEdit NSShowAllViews YES(or whatever your application identifier is), or use the one-shot version:
/Developer/Applications/Xcode.app/Contents/MacOS/Xcode -NSShowAllViews YES
- (BOOL) _hasEditableCell
{
return (YES);
} // _hasEditableCell
This workaround is necessary at least for Mac OS X 10.1.*.
environment method
Growl.framework into your application bundle.
Pick a class to be the contact point with Growl. Your AppController class is a good place. Import <Growl/Growl.h>
Set the delegate to the GrowlApplicationBridge:
[GrowlApplicationBridge setGrowlDelegate: self];
Doing this will eventually have the registrationDictionaryForGrowl delegate message called. Return a dictionary with two arrays (Which can be the same). These are the names of the alerts you will be posting. These are human-readable, so you'll want to use a localized string (which I've already set in the global variable here:
- (NSDictionary *) registrationDictionaryForGrowl
{
NSArray *notifications;
notifications = [NSArray arrayWithObject: g_timesUpString];
NSDictionary *dict;
dict = [NSDictionary dictionaryWithObjectsAndKeys:
notifications, GROWL_NOTIFICATIONS_ALL,
notifications, GROWL_NOTIFICATIONS_DEFAULT, nil];
return (dict);
} // registrationDictionaryForGrowl
And use this to post a notification:
[GrowlApplicationBridge notifyWithTitle: @"Woop! Time has expired!"
description: @"You have been waiting for 37 minutes"
notificationName: g_timesUpString
iconData: nil
priority: 0
isSticky: NO
clickContext: nil];
Consult the SDK documentation for more explanations of the features, but they are pretty self-explanitory.
float blah = FixedToFloat(someFixedvalue);other goodies in
FixMath.h
~/Library/Application Support/Chimera/Profiles/default/xyz.slt/prefs.js file:
user_pref("browser.urlbar.autocomplete.enabled", false);
NSString has a couple of convenience functions for print out basic structures like NSRect and NSSize:
NSStringFromRect (someNSRect);
NSStringFromPoint (someNSPoint);
NSStringFromSize (someNSSize);
NSSearchPathForDirectoriesInDomains is how you find the location of things like Library directories, or User directories, and the like (this is the NSSearchPathDirectory). The NSSearchPathDomainMask is what domains to find things in. For instance for a NSLibraryDirectory, a NSUserDomainMask will give you the path to ~/Library, NSSystemDomainMask will give you the path to /System/Library, and so on.
The directories inside of Library, like "Preferences" and "Application Support" are in English in the file system, and the Finder presents localized versions to the user. If you need ~/Library/Application Support/Borkware, you can construct it like
NSMutableString *path;
path = [[NSMutableString alloc] init];
// find /User/user-name/Library
NSArray *directories;
directories = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
NSUserDomainMask, YES);
// if you had more than one user domain, you would walk directories and
// work with each path
[path appendString: [directories objectAtIndex: 0]];
[path appendString: @"/Application Support"];
[path appendString: @"/Borkware"];
NSUserName() or NSFullUserName().
Thanks to Peter Hosey for letting us know about a better API for getting these.
logo.jpg)
logo.jpg that's on the thingie drive
logo.jpg file/Developer/Tools/SetFile -a V /Volumes/thingie/logo.jpg
Image->Convert Image will let you compress the image. As a side-effect of the compressed format, this makes the image read-only.
~/.MacOSX/environment.plist. Make the root a Dictionary, and add the key/value pairs (all strings) to it. Don't forget to logout and back in.
% sudo mdutil -i off /
#importint main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; [pool release]; } // main
And you can compile it on the command line with:
cc -o test -framework Foundation file-name.m
% /usr/bin/mdimport -d2 ../Book.pdf >& oopack.txtAnd edit out the little bit of extra metadata output.
ld: multiple definitions of symbol OpenSP::Text::~Text [in-charge]() Entity.lo definition of OpenSP::Text::~Text [in-charge]() in section (__TEXT,__text) Group.lo definition of OpenSP::Text::~Text [in-charge]() in section (__TEXT,__text) ld: multiple definitions of symbol OpenSP::Text::~Text [not-in-charge]() Entity.lo definition of OpenSP::Text::~Text [not-in-charge]() in section (__TEXT,__text) Group.lo definition of OpenSP::Text::~Text [not-in-charge]() in section (__TEXT,__text) Param.lo definition of OpenSP::Text::~Text [in-charge]() in section (__TEXT,__text) Param.lo definition of OpenSP::Text::~Text [not-in-charge]() in section (__TEXT,__text)These are caused by the class in question (Text) not having an explicit destructor declared, so the compiler is creating one for us in each of the files. In Text.cxx, add
Text::~Text() { }
and in Text.h, add~Text();to the Text class. (I got it to compile and link, so I didn't go back to check to see if just sticking
~Text() {} in Text.h would work.)
gcc -dynamiclib -o .libs/libogrove.0.0.1.dylib Node.lo LocNode.lo -lm -lc -install_name /usr/local/lib/libogrove.0.dylib ld: Undefined symbols: vtable for __cxxabiv1::__class_type_info vtable for __cxxabiv1::__si_class_type_info operator delete(void*) operator new(unsigned long) ___cxa_pure_virtual ___gxx_personality_v0 /usr/bin/libtool: internal link edit command failedThe link line needs to have "-lstdc++" added to the end. For OpenJade, edit the libtool script and add
-lstdc++ to the end archive_cmds line. (but leave it inside the last closing double-quote)
lsregister tool that lives in /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support
% defaults write com.apple.mail PreferPlainText -bool TRUE
/Library/WebServer/Documents
~/Library/Screen Savers folder? If so, you can make a symbolic link that points to your screen saver bundle, and that will automatically get used whenever you engage the screen saver engine. e.g.
% cd ~/Library/Screen Savers % ln -s ~/Projects/MonkeySaver/build/Debug/MonkeySaver.saver .
NSBundle *bundle;
NSString *path;
bundle = [NSBundle bundleForClass: [self class]];
path = [bundle pathForResource: @"atomsymbol" ofType: @"jpg"];
image = [[NSImage alloc] initWithContentsOfFile: path];
Targets pane in Project Builder
Build Settings tab
Product Name under General Settings
~/Library/Preferences/ByHost directory. The name of the file is the name that you pass to defaultsForModuleWithName:, followed by
.someBigNumber.plist.
So, on my machine,
userPrefs = [ScreenSaverDefaults defaultsForModuleWithName: @"BWDrip"];creates a file
/Users/bork/Library/Preferences/ByHost/BWDrip.0003931024a6.plist
% /System/Library/Frameworks/ScreenSaver.framework/Resources/ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine -background &
(all one command)
- (void) handleTimer: (NSTimer *) timer
{
do some work here...
} // handleTimer
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
target: self
selector: @selector(handleTimer:)
userInfo: nil
repeats: YES];
applicationShouldTerminate: message, you're likely to want to returnNSTerminateLater so that you can first gracefully shut down the connection before quitting.
There is a gotcha: returning NSTerminateLater stops the main runloop, so your NSTimers will stop working from that point on. Thus, if you were depending on a timer firing to finish up your shut-down process, you'll never see it, and you'll hang. The solution is to return NSTerminateCancel, then do whatever you need to do and then terminate yourself manually. (Thanks to Larry Gerndt for this one!)
defaults = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 25.0], @"density",
[NSNumber numberWithFloat: 40.0], @"rate",
nil];
% cvs admin -kb filename
cvswrappers file provided in the devtools. The easiest thing to do is to copy it to your homedir and prepend a dot:
% cp /Developer/Tools/cvswrappers ~/.cvswrappers
You can also add the contents of that fike to your repository.
.pbproj files aren't handled by the cvswrappers by default. You'll need to add a line like this:
*.pbproj -k 'b' -f '/Developer/Tools/cvs-unwrap %s' -t '/Developer/Tools/cvs-wrap %s %s' -m 'COPY'
% cvs checkout -D "3 days ago" blork
Or via a date (month/day/year)
% cvs checkout -D "10/17/2002" fnord
or recently in time
% cvs diff -D "1 hour ago" ook.blah
% cvs update -d [whatever]
-d option. Otherwise CVS will just ignore any directories added in the branch.
% cvs update -Ad #switch to the trunk (if you haven't already) % cvs update -d -j branch_name [resolve conflicts] % cvs commit -m "whatever"
(Courtesy of Jeremy Stein)
% find . -type d -print | grep -v CVS | xargs cvs add % find . -type f -print | grep -v CVS | xargs cvs addThe first adds the directories, the second adds the files. Filenames with spaces in them won't be added, and it won't do the Right Thing with
-kb for binary files.
% cvs log -r1.2:1.5 multiprocessing.txt
NSURL *url;
NSData *data;
NSString *blork;
url = [NSURL URLWithString: @"http://borkware.com/hacks/random"];
data = [url resourceDataUsingCache: NO];
blork = [[NSString alloc] initWithData: data encoding: NSASCIIStringEncoding];
NSURL *url;
NSData *data;
NSImage *blork;
url = [NSURL URLWithString: @"http://borkware.com/hacks/random-pic"];
data = [url resourceDataUsingCache: NO];
blork = [[NSImage alloc] initWithData: data];
float domain[2] = { 0.0, 1.0 }; // 1-in function
float range[8] = { 0.0, 1.0, // N-out, RGBA
0.0, 1.0,
0.0, 1.0,
0.0, 1.0 };
CGFunctionCallbacks callbacks = { 0, evaluate, NULL };
CGFunctionRef shaderFunction;
shaderFunction = CGFunctionCreate (self, // info / rock / context
1, // # of inputs for domain
domain, // domain
4, // # of inputs for range
range, // range
&callbacks);
CGColorSpaceRef deviceRGB;
deviceRGB = CGColorSpaceCreateDeviceRGB ();
CGShadingRef shader;
shader = CGShadingCreateAxial (deviceRGB, // colorspace
cgpoint(start), // start of axis
cgpoint(end), // end of axis
shaderFunction, // shader, 1-n, n-out
NO, // extend start
NO); // extend end
CGContextSaveGState (context); {
NSRect bounds = [self bounds];
CGContextClipToRect (context, cgrect(bounds));
CGContextDrawShading (context, shader);
} CGContextRestoreGState (context);
[self drawSpotAt: start size: 4];
[self drawSpotAt: end size: 4];
CGFunctionRelease (shaderFunction);
CGColorSpaceRelease (deviceRGB);
CGShadingRelease (shader);
And the evaluator function is given an array of inputs and outputs. Use the in value(s) (which run from your domain's start-to-finish values) to generate the out values (which should be in the range's start-to-finish values):
static void evaluate (void *info, const float *in, float *out)
{
float thing;
thing = in[0];
out[0] = thing;
out[1] = thing;
out[2] = thing;
out[3] = 1.0;
} // evaluate
NSString *blah = @"Placeholder Pane";
NSSize size = [blah sizeWithAttributes: nil];
NSPoint startPoint;
startPoint.x = bounds.origin.x + bounds.size.width / 2 - size.width / 2;
startPoint.y = bounds.origin.y + bounds.size.height / 2 - size.height / 2;
[blah drawAtPoint: startPoint
withAttributes: nil];
#define cgrect(nsrect) (*(CGRect *)&(nsrect))
- (void) drawRect: (NSRect) rect
{
NSRect bounds = [self bounds];
NSGraphicsContext *cocoaContext = [NSGraphicsContext currentContext];
CGContextRef context = (CGContextRef)[cocoaContext graphicsPort];
CGContextSetLineWidth (context, 5.0);
CGContextBeginPath(context); {
CGContextAddRect (context, cgrect(bounds));
CGContextSetRGBFillColor (context, 1.0, 0.9, 0.8, 1.0);
} CGContextFillPath(context);
} // drawRect
- (NSImage *) captureScreenImageWithFrame: (NSRect) frame
{
// Fetch a graphics port of the screen
CGrafPtr screenPort = CreateNewPort ();
Rect screenRect;
GetPortBounds (screenPort, &screenRect);
// Make a temporary window as a receptacle
NSWindow *grabWindow = [[NSWindow alloc] initWithContentRect: frame
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreRetained
defer: NO
screen: nil];
CGrafPtr windowPort = GetWindowPort ([grabWindow windowRef]);
Rect windowRect;
GetPortBounds (windowPort, &windowRect);
SetPort (windowPort);
// Copy the screen to the temporary window
CopyBits (GetPortBitMapForCopyBits(screenPort),
GetPortBitMapForCopyBits(windowPort),
&screenRect,
&windowRect,
srcCopy,
NULL);
// Get the contents of the temporary window into an NSImage
NSView *grabContentView = [grabWindow contentView];
[grabContentView lockFocus];
NSBitmapImageRep *screenRep;
screenRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: frame];
[grabContentView unlockFocus];
NSImage *screenImage = [[NSImage alloc] initWithSize: frame.size];
[screenImage addRepresentation: screenRep];
// Clean up
[grabWindow close];
DisposePort(screenPort);
return (screenImage);
} // captureScreenImageWithFrame
- (void) makeLayerInContext: (CGContextRef) enclosingContext
{
layer = CGLayerCreateWithContext (enclosingContext, CGSizeMake(100, 100),
NULL); // options - unused in Tiger
CGContextRef context;
context = CGLayerGetContext (layer);
// .. and do your drawing
} // makeLayer
And then to draw the layer at a particular point (like replicating it a bunch of different points as done here):
for (i = 0; i < pointCount; i++) {
CGContextDrawLayerAtPoint (context, locations[i], layer);
}
- (NSBezierPath *) makePathFromString: (NSString *) string
forFont: (NSFont *) font
{
NSTextView *textview;
textview = [[NSTextView alloc] init];
[textview setString: string];
[textview setFont: font];
NSLayoutManager *layoutManager;
layoutManager = [textview layoutManager];
NSRange range;
range = [layoutManager glyphRangeForCharacterRange:
NSMakeRange (0, [string length])
actualCharacterRange: NULL];
NSGlyph *glyphs;
glyphs = (NSGlyph *) malloc (sizeof(NSGlyph)
* (range.length * 2));
[layoutManager getGlyphs: glyphs range: range];
NSBezierPath *path;
path = [NSBezierPath bezierPath];
[path moveToPoint: NSMakePoint (20.0, 20.0)];
[path appendBezierPathWithGlyphs: glyphs
count: range.length inFont: font];
free (glyphs);
[textview release];
return (path);
} // makePathFromString
To make editing end, you need to subclass NSTableView and add code to catch the textDidEndEditing delegate notification, massage the text movement value to be something other than the return and tab text movement, and then let NSTableView handle things.
// make return and tab only end editing, and not cause other cells to edit
- (void) textDidEndEditing: (NSNotification *) notification
{
NSDictionary *userInfo = [notification userInfo];
int textMovement = [[userInfo valueForKey:@"NSTextMovement"] intValue];
if (textMovement == NSReturnTextMovement
|| textMovement == NSTabTextMovement
|| textMovement == NSBacktabTextMovement) {
NSMutableDictionary *newInfo;
newInfo = [NSMutableDictionary dictionaryWithDictionary: userInfo];
[newInfo setObject: [NSNumber numberWithInt: NSIllegalTextMovement]
forKey: @"NSTextMovement"];
notification =
[NSNotification notificationWithName: [notification name]
object: [notification object]
userInfo: newInfo];
}
[super textDidEndEditing: notification];
[[self window] makeFirstResponder:self];
} // textDidEndEditing
(Thanks to Steven Jacowski for a tweak that ends editing on clicks on different cells)
NSTableViewDropAbove- (NSDragOperation) tableView: (NSTableView*) tableView
validateDrop: (id ) info
proposedRow: (int) row
proposedDropOperation: (NSTableViewDropOperation) op
{
int result = NSDragOperationNone;
if (op == NSTableViewDropAbove) {
result = NSDragOperationMove;
}
return (result);
} // validateDrop
NSTableView's -setDoubleAction: method, and supply it a standard IBAction-style method selector.
You may need to also do -setTarget:. double-clicks get sent if the column isn't editable, so you may need to grab the column and do a -setEditable: NO on it.
- (BOOL) tableView: (NSTableView *) view
writeRows: (NSArray *) rows
toPasteboard: (NSPasteboard *) pboard
in your table view data source (and don't forget to hook up the datasource if you use bindings to populate the tableview)
- (void) textDidEndEditing: (NSNotification *) notification
{
NSDictionary *userInfo;
userInfo = [notification userInfo];
NSNumber *textMovement;
textMovement = [userInfo objectForKey: @"NSTextMovement"];
int movementCode;
movementCode = [textMovement intValue];
// see if this a 'pressed-return' instance
if (movementCode == NSReturnTextMovement) {
// hijack the notification and pass a different textMovement
// value
textMovement = [NSNumber numberWithInt: NSIllegalTextMovement];
NSDictionary *newUserInfo;
newUserInfo = [NSDictionary dictionaryWithObject: textMovement
forKey: @"NSTextMovement"];
notification = [NSNotification notificationWithName:
[notification name]
object: [notification object]
userInfo: newUserInfo];
}
[super textDidEndEditing: notification];
} // textDidEndEditing
mouseDownInHeaderOfTableColumn or didClickTableColumn:
NSImage *indicatorImage;
if (sortAscending) {
sort your data ascending
indicatorImage = [NSImage imageNamed: @"NSAscendingSortIndicator"];
} else {
sort your data descending
indicatorImage = [NSImage imageNamed: @"NSDescendingSortIndicator"];
}
sortAscending = !sortAscending;
[tableView setIndicatorImage: indicatorImage
inTableColumn: tableColumn];
[tableView reloadData];
- (void) tableViewSelectionDidChange: (NSNotification *) notification
{
int row;
row = [tableView selectedRow];
if (row == -1) {
do stuff for the no-rows-selected case
} else {
do stuff for the selected row
}
} // tableViewSelectionDidChange
If you have more than one tableview, the notification's object is the tableview that had the selection change.
-setDropRow:dropOperation:, like so:
- (NSDragOperation) tableView: (NSTableView *) view
validateDrop: (id ) info
proposedRow: (int) row
proposedDropOperation: (NSTableViewDropOperation) op
{
// have the table highlight on-row / between-row correctly
[view setDropRow: row
dropOperation: op];
// or use whatever drag operation is appropriate
NSDragOperation dragOp = NSDragOperationCopy;
return (dragOp);
} // validateDrop
and in the acceptDrop method, look at the operation:
- (BOOL) tableView: (NSTableView *) view
acceptDrop: (id ) info
row: (int) row
dropOperation: (NSTableViewDropOperation) op
{
if (op == NSTableViewDropOn) {
// replace existing
} else if (op == NSTableViewDropAbove) {
// add new
} else {
NSLog (@"unexpected operation (%d) in %s",
op, __FUNCTION__);
}
// documentation doesn't actually say what this signifies
return (YES);
} // acceptDrop
- (int) numberOfRowsInTableView: (NSTableView *) tableView
- (id) tableView: (NSTableView *) tableView
objectValueForTableColumn: (NSTableColumn *) tableColumn
row: (int) row
% createlang plpgsql template1
alter table pfo_survey_response_2006 add column section text
% pg_dump databasename > db.out
pg_hba.conf, you can connect to the database, logged in as an ordinary person, by doing
% psql -U nsadmin opeancs
% createdb database-name
psql=# create user bork with password 'bork';
You can also do this for a passwordless user:
% crateuser bork
psql=# select to_timestamp('10:45am', 'HH12:MIam')::timetz::time;
to_timestamp
--------------
10:45:00
(1 row)
limit" statement:
select stuff from some_table where stuff.blah > 12 limit 5
% psql database-name user-name
% psql -h tower borkdb nettest
% pg_ctl -D /usr/local/pgsql/data -l /tmp/pgsql.log start
insert into blah (id, stuff)
values (nextval('sequence_name'), 'stuff');
% psql -d databasename -f db.out
% psql -l
random() function.
select stuff.src, stuff.width, stuff.height from
(select src, width, height,
random()
from bw_eyes
order by 4) stuff
limit 1
This selects a random row from bw_eyes and returns the interesting data from it. This query is used on the quickies pages to randomly select an image of eyes staring back.
select current_timestamp()::date - entry_date::date as days_old from kb_nuggets;
wplug-oacs=# \pset pager
Pager usage is off.
select blah from whatever where date-column > (current_timestamp - 21) to get all the blahs from whatever in the last 21 days. This doesn't work in newer versions of Postgresql. Instead, you need to use a time interval:select to_char (whenx, 'fmDD fmMonth YYYY') as pretty_when, text from blog where whenx > (current_timestamp - interval '21 days') order by whenx desc
shared_buffers in your postgresql.conf, and get an error similar to this:
IpcMemoryCreate: shmget(key=5432001, size=266371072, 03600) failed:
Invalid argument
This error usually means that PostgreSQL's request for a shared memory
segment exceeded your kernel's SHMMAX parameter.
You should increase your the shmmax parameters using tips in the Unix administration quickies.
- Darwin Website (Darwin->General)
[permalink]
Since I can never remember this, it's at developer.apple.com/darwin.
- Darwin's CVSROOT string (Darwin->CVS)
[permalink]
for read-only checkouts:
setenv CVSROOT :pserver:username@anoncvs.opensource.apple.com:/cvs/Darwin
- Appending to the end of a textview (NSTextView->General)
[permalink]
NSRange range;
range = NSMakeRange ([[textView string] length], 0);
[textView replaceCharactersInRange: range withString: string];
And Peter Hosey provided a one-liner that may suit your needs better:
[[[textView textStorage] mutableString] appendString: string];
- Deleting the selected text (NSTextView->General)
[permalink]
[textView delete: nil]
- Determining the paragraph style at a particular point (NSTextView->General)
[permalink]
- (NSParagraphStyle *) paragraphStyleInTextView: (NSTextView *) textView
atIndex: (int) index
{
NSTextStorage *storage = [textView textStorage];
NSDictionary *attributes;
NSRange effectiveRange;
attributes = [storage attributesAtIndex: index
effectiveRange: &effectiveRange];
NSParagraphStyle *style;
style = [attributes valueForKey: NSParagraphStyleAttributeName];
return (style);
} // paragraphStyleInTextView
- Finding the span of a paragraph (NSTextView->General)
[permalink]
The NSString paragraphRangeForRange: method gives you the span of a paragraph. If you're using the text architecture, you need to get the string from the text storage. This chunklet shows the paragraph span for the current selection:
NSRange selectedRange = [textview selectedRange];
NSTextStorage *storage = [textview textStorage];
effectiveRange = [[storage string] paragraphRangeForRange: selectedRange];
- Forcing validation of pending text field entries (NSTextView->General)
[permalink]
[window makeFirstResponder: window]
- Moving insertion point to the end (NSTextView->General)
[permalink]
NSRange range = { [[textView string] length], 0 };
[textView setSelectedRange: range];
- Moving insertion point to the start (NSTextView->General)
[permalink]
NSRange zeroRange = { 0, 0 };
[textView setSelectedRange: zeroRange];
- Replacing NSTextView's contents with an attributed string (NSTextView->General)
[permalink]
[[textView textStorage] setAttributedString: theNewString];
- Restricting typed in text to just digits (NSTextView->General)
[permalink]
Create a subclass of NSNumberFormatter, add this method, and hook up the formatter to your text fields.
- (BOOL) isPartialStringValid: (NSString **) partialStringPtr
proposedSelectedRange: (NSRangePointer) proposedSelRangePtr
originalString: (NSString *) origString
originalSelectedRange: (NSRange) origSelRange
errorDescription: (NSString **) error
{
NSCharacterSet *nonDigits;
NSRange newStuff;
NSString *newStuffString;
nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
newStuff = NSMakeRange(origSelRange.location,
proposedSelRangePtr->location
- origSelRange.location);
newStuffString = [*partialStringPtr substringWithRange: newStuff];
if ([newStuffString rangeOfCharacterFromSet: nonDigits
options: NSLiteralSearch].location != NSNotFound) {
*error = @"Input is not an integer";
return (NO);
} else {
*error = nil;
return (YES);
}
} // isPartialStringValid
- Scrolling to the end of a textview (NSTextView->General)
[permalink]
NSRange range;
range = NSMakeRange ([[textView string] length], 0);
[textView scrollRangeToVisible: range];
I've heard that scrollRangeToVisible is O(N) for the length of the text. So be on the lookout if you have lots of text involved.
- Show NSTextField text in gray if the field is disabled (NSTextView->General)
[permalink]
NSTextField doesn't change its text color if the field is enabled or disabled (?) Even more bizarre, using NSColor's disabledControlTextColor won't draw the text in the disabled color. You need to use the secondarySelectedControlColor, which supposedly is for active controls that don't have focus. Go figure.
To do it yourself, subclass NSTextField and override setEnabled: to change the color:
- (void) setEnabled: (BOOL) flag
{
[super setEnabled: flag];
if (flag == NO) {
[self setTextColor: [NSColor secondarySelectedControlColor]];
} else {
[self setTextColor: [NSColor controlTextColor]];
}
} // setEnabled
This actually kind of a gross workaround for a Cocoa bug - the disabled color is getting made darker rather than lighter. The secondarySelectedControlColor ends up looking disabled by a happy coincidence that it starts out lighter before being darkened. (or something like this. UberDude Dave MacLachlan has done the legwork to figure out the underlying problem.)
- Truncating text in the middle of a string (NSTextView->General)
[permalink]
Sometimes you see text that is of the form "Some Gro...eral Fish", with the middle truncated but the ends still readable. You can get this effect yourself with:
[[textfield cell] setLineBreakMode: NSLineBreakByTruncatingMiddle]
(Thanks to Daniel Jalkut for this one)
- scrolling to the beginning of a textview (NSTextView->General)
[permalink]
NSRange zeroRange = { 0, 0 };
[textView scrollRangeToVisible: zeroRange];
- Getting the contents of a TextView as an NSString (NSTextView->NSString)
[permalink]
NSString *commitMessage;
commitMessage = [[commitTextView textStorage] string];
- Changing checkbox toggle state (NSControl->General)
[permalink]
[myCheckbox setState: NSOffState]
NSOnState also works.
- Respond to every keystroke in a textfield (NSControl->General)
[permalink]
Make your object a delegate of the textfield, and add this NSControl delegate method:
- (void) controlTextDidChange: (NSNotification *) notification
{
// do work here, like count the characters or something
} // controlTextDidBeginEditing
- 'join' an array of strings into a single string (NSString->General)
[permalink]
NSArray *chunks = ... get an array, say by splitting it;
string = [chunks componentsJoinedByString: @" :-) "];
would produce something like
oop :-) ack :-) bork :-) greeble :-) ponies
- 'split' a string into an array (NSString->General)
[permalink]
NSString *string = @"oop:ack:bork:greeble:ponies";
NSArray *chunks = [string componentsSeparatedByString: @":"];
- Converting string to an integer (NSString->General)
[permalink]
NSString *string = ...;
Similarly, there is a floatValue and doubleValue NSString methods.
- (void) iterateAttributesForString: (NSAttributedString *) string
{
NSDictionary *attributeDict;
NSRange effectiveRange = { 0, 0 };
do {
NSRange range;
range = NSMakeRange (NSMaxRange(effectiveRange),
[string length] - NSMaxRange(effectiveRange));
attributeDict = [string attributesAtIndex: range.location
longestEffectiveRange: &effectiveRange
inRange: range];
NSLog (@"Range: %@ Attributes: %@",
NSStringFromRange(effectiveRange), attributeDict);
} while (NSMaxRange(effectiveRange) < [string length]);
} // iterateAttributesForString
Localizable.strings that lives in your English.lproj directory (or whatever localization directory is appropriate). It has this syntax:
"BorkDown" = "BorkDown"; "Start Timer" = "Start Timer"; "Stop Timer" = "Stop Timer";That is, a key followed by a localized value.
In your code, you can then use NSLocalizedString() or one of its variants:
[statusItem setTitle: NSLocalizedString(@"BorkDown", nil)];The second argument is ignored by the function. Obstensively it is a
/* comment */ in the strings file so that you can match the key back to what it is supposed to actually be.
NSlog puts too much crud in front of the logging line. For a foundation tool that output stuff, it gets in the way. I'd still like for a replacement to expand %@, which the printf() family won't do. Here's a some code that'll do that.
#include <stdarg.h>
void LogIt (NSString *format, ...)
{
va_list args;
va_start (args, format);
NSString *string;
string = [[NSString alloc] initWithFormat: format arguments: args];
va_end (args);
printf ("%s\n", [string cString]);
[string release];
} // LogIt
- (NSAttributedString *) prettyName
{
NSTextAttachment *attachment;
attachment = [[[NSTextAttachment alloc] init] autorelease];
NSCell *cell = [attachment attachmentCell];
NSImage *icon = [self icon]; // or wherever you are getting your image
[cell setImage: icon];
NSString *name = [self name];
NSAttributedString *attrname;
attrname = [[NSAttributedString alloc] initWithString: name];
NSMutableAttributedString *prettyName;
prettyName = (id)[NSMutableAttributedString attributedStringWithAttachment:
attachment]; // cast to quiet compiler warning
[prettyName appendAttributedString: attrname];
return (prettyName);
} // prettyName
This puts the image at the front of the string. To put the image in the middle of the string, you'll need to create an attributedstring with attachment, and then append that to your final attributed string.
NSMutableString *mstring = [NSMutableString stringWithString:string];
NSRange wholeShebang = NSMakeRange(0, [mstring length]);
[mstring replaceOccurrencesOfString: @"
"
withString: @""
options: 0
range: wholeShebang];
return [NSString stringWithString: mstring];
(this can also be used for generic string manipulations, not just stripping out newlines).
This technique takes half the time (at least) of split/join. But probably not enough to make an impact. In a simple test, split/join took 0.124 seconds to strip 36909 newlines in a 1.5 meg textfile, and 0.071 seconds to do the same.
NSRange range = [[string name] rangeOfString: otherString options: NSCaseInsensitiveSearch];
[[NSDate date] descriptionWithCalendarFormat: @"%B %e, %Y" timeZone: nil locale: nil](Thanks to Mike Morton for this one)
NSString *ook = @"\n \t\t hello there \t\n \n\n";
NSString *trimmed =
[ook stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(@"trimmed: '%@'", trimmed);
produces
2009-12-24 18:24:42.431 trim[6799:903] trimmed: 'hello there'
@implementation NSString (PasteboardGoodies)
- (void) sendToPasteboard
{
[[NSPasteboard generalPasteboard]
declareTypes: [NSArray arrayWithObject: NSStringPboardType]
owner:nil];
[[NSPasteboard generalPasteboard]
setString: self
forType: NSStringPboardType];
} // sendToPasteboard
@end // PasteboardGoodies
Thanks to Dan Jalkut for this tip.
- (void) drawLabel: (NSString *) label
atPoint: (NSPoint) point
bold: (BOOL) bold {
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
NSFont *currentFont = [NSFont userFontOfSize: 14.0];
if (bold) {
NSFontManager *fm = [NSFontManager sharedFontManager];
NSFont *boldFont = [fm convertFont: currentFont
toHaveTrait: NSBoldFontMask];
[attributes setObject: boldFont
forKey: NSFontAttributeName];
} else {
[attributes setObject: currentFont
forKey: NSFontAttributeName];
}
[label drawAtPoint: point withAttributes: attributes];;
} // drawLabel
/usr/share/dict/words or from a script, and you want to process them. In this case turning a bunch of strings into floats:
NSMutableArray *altitudes = [NSMutableArray array];
NSString *altitudeString = [self altitudeStringFromGoogle: coords];
[altitudeString enumerateLinesUsingBlock: ^(NSString *line, BOOL *stop) {
float value = [line floatValue];
[altitudes addObject: [NSNumber numberWithFloat: value]];
}];
-windowForSheet (which doesn't tell you the window associated with a particular sheet, but instead returns the window to be used for showing sheets, which is usually what you want when talking about the document's window - Thanks to Peter Hosey for this one).
For folks stuck in 10.2 land, or who want to do it themselves, there's always this:
- (NSWindow *) window
{
NSArray *windowControllers;
windowControllers = [self windowControllers];
NSWindowController *controller;
controller = [windowControllers objectAtIndex: 0];
NSWindow *window;
window = [controller window];
return (window);
} // window
defaults write com.flyingmeat.acorn NSRecentDocumentsLimit 137(thanks to Gus Mueller for this one)
keepBackupFile:
- (BOOL) keepBackupFile
{
return (YES);
} // keepBackupFile
This will do the tilde thing to the older version of the file.
NSDocumentController *documentController;
documentController = [NSDocumentController sharedDocumentController];
BWBorkStitchDocument *noteDocument;
noteDocument = [documentController documentForWindow: noteWindow];
[window frame] of the window in question. (That's left as an exercise to the reader, based on how you're storing your document contents.) When restoring the window location, you have to turn of NSWindowController's cascading, otherwise it'll honor your width and height, but not your origin. In your document's windowControllerDidLoadNib method, you'll need:
[[self windowController] setShouldCascadeWindows: NO];
NSWindow *window;
window = [self window];
[window setFrame: loadedWindowBounds display: YES];
- (NSString *) windowTitleForDocumentDisplayName: (NSString *) displayName
{
NSString *string;
string = [NSString stringWithFormat: @"Overview of %@", displayName];
return (string);
} // windowTitleForDocumentDisplayName
- (void) windowDidLoad
{
[super windowDidLoad];
[self setShouldCascadeWindows: NO];
[self setWindowFrameAutosaveName: @"pannerWindow"];
// and any other windowDidLoad work to be done
} // windowDidLoad
validateMenuItem: and do something like this:
- (BOOL) validateMenuItem: (id <NSMenuItem>) menuItem
{
BOOL result = YES;
if ([menuItem action] == @selector(deleteNote:)) {
if ([notes count] == 1) {
result = NO; // can't delete the last note
}
} else if ([menuItem action] == @selector(gotoNote:)) {
if ([notes count] == 1) {
result = NO; // can't go to a different not if only one
}
}
return (result);
} // validateMenuItem
NSMenuWillSendActionNotification is posted to the notification center before the action is invoked.
gettimeofday() to get sub-second granularity for timing:#include <sys/time.h> struct timeval start, end; ... gettimeofday (&start, NULL); ... the code you're timing gettimeofday (&end, NULL); double fstart, fend; fstart = (start.tv_sec * 1000000.0 + start.tv_usec) / 1000000.0; fend = (end.tv_sec * 1000000.0 + end.tv_usec) / 1000000.0; NSLog (@"it took %f seconds", fend - fstart);
time_t now;
now = time(NULL);
struct tm *tm;
tm = localtime (&now);
char timestamp[200];
strftime (timestamp, 200, "%m/%d/%Y-%H:%M", tm);
- (void) awakeFromNib
{
[browser setTarget: self];
[browser setDoubleAction: @selector(browserDoubleClick:)];
} // awakeFromNib
...
- (IBAction) browserDoubleClick: (id) sender
{
int column = [browser selectedColumn];
int row = [browser selectedRowInColumn: column];
// then dig into your data structure with the row and column
} // browserDoubleClick
NSString *filename = @"/my/original/file/name";
NSString *tildeFilename;
tildeFilename = [NSString stringWithFormat: @"%@~", filename];
// remove it first, otherwise the move will fail
[defaultManager removeFileAtPath: tildeFilename
handler: nil];
// now rename the file
[defaultManager movePath: filename
toPath: tildeFilename
handler: nil];
If you want to stick the tidle before the file extension (so the finder could open ook~.tiff), try this:
NSString *pathExtension = [filename pathExtension];
if (!pathExtension) {
tildeFilename = [filename stringByAppendingString: @"~"];
} else {
tildeFilename = [NSString stringWithFormat: @"%@~.%@", [filename stringByDeletingPathExtension], pathExtension];
}
(Thanks to Peter Hosey for this one)
NSFileManager *defaultManager;
defaultManager = [NSFileManager defaultManager];
[defaultManager removeFileAtPath: tildeFilename
handler: nil];
The handler is an object that will be sent message, like fileManager:shouldProceedAfterError: if something goes wrong during the removal.
NSString *filename = @"/this/is/my/file/name";
NSData *data = // get NSData from somewhere, like NSPropertyListSerialization
[data writeToFile: filename atomically: NO];
NSData *data = // get NSData from somewhere, like NSFileManager
if (data) {
myRootObject = [NSPropertyListSerialization
propertyListFromData: data
mutabilityOption: NSPropertyListMutableContainers
format: nil
errorDescription: nil];
}
For the mutability options of the resulting object, you can also use NSPropertyListImmutable and NSPropertyListMutableContainersAndLeaves
NSDictionary, NSArray,NSNumber, NSString, NSData) as XML like this:
NSData *data;
data = [NSPropertyListSerialization
dataFromPropertyList: notes
format: NSPropertyListXMLFormat_v1_0
errorDescription: nil];
Then write out the data using NSFileManager, or whatever other mechanism you wish.
- (NSData *) dataOfType: (NSString *) typeName
error: (NSError **) error
{
*error = nil;
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver;
archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData: data];
[archiver setOutputFormat: NSPropertyListXMLFormat_v1_0];
[archiver encodeObject: stitches forKey: @"stitches"];
// and archive other stuff you want
[archiver finishEncoding];
return ([data autorelease]);
} // dataOfType
- (BOOL) readFromData: (NSData *) data
ofType: (NSString *) typeName
error: (NSError **) error
{
*error = nil;
NSKeyedUnarchiver *archiver;
archiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData: data];
[stitches release];
stitches = [archiver decodeObjectForKey: @"stitches"];
// decode other stuff of interest
[stitches retain];
return (YES);
} // readFromData
StitchEdit[3522] *** class error for 'BWRawPath': class not loaded
In a convenient place (like in the +load method of your class), use NSUnarchiver's decodeClassName:asClassName:
@implementation BWSymbol
+ (void) load
{
[NSUnarchiver decodeClassName: @"BWRawPath"
asClassName: @"BWSymbol"];
// No need to [super load] - the superclass +load has already
// been invoked automatically by the runtime.
} // load
Note that this won't help you if you're wanting to rename something that was added via encodeValueOfObjCType:. You'll have to write some code to unarchive your data using the old @encode(oldName) and then re-archive it as @encode(newName)
(Thanks to Greg Miller for spotting an error in this quickie)
- (NSData *) dataRepresentationOfType: (NSString *) aType
{
NSData *data;
data = [NSArchiver archivedDataWithRootObject: group];
return (data);
} // dataRepresentationOfType
- (BOOL) loadDataRepresentation: (NSData *) data
ofType: (NSString *) aType
{
// replace the moedl
[group release];
group = [NSUnarchiver unarchiveObjectWithData: data];
[group retain];
// make any associations to hook the new data into your document
return (YES);
} // loadDataRepresentation
NSCoding protocol.
- (void) encodeWithCoder: (NSCoder *) coder
{
[coder encodeInt: x forKey: @"x"];
[coder encodeInt: y forKey: @"y"];
[coder encodeObject: color1 forKey: @"color1"];
[coder encodeObject: color2 forKey: @"color2"];
[coder encodeInt: direction forKey: @"direction"];
} // encodeWithCoder
- (id) initWithCoder: (NSCoder *) coder
{
if (self = [super init]) {
x = [coder decodeIntForKey: @"x"];
y = [coder decodeIntForKey: @"y"];
color1 = [coder decodeObjectForKey: @"color1"];
color2 = [coder decodeObjectForKey: @"color2"];
direction = [coder decodeIntForKey: @"direction"];
}
return (self);
} // initWithCoder
- (id) initWithCoder: (NSCoder *) aDecoder
{
if (self = [super init]) {
// or else [super initWithCoder: aDecoder] if your superclass
// supports it. NSObject does not.
// decoding a C type
[aDecoder decodeValueOfObjCType: @encode(int)
at: &itemCount];
// decoding an array of C structs
if (items != NULL) {
free (items);
}
items = malloc (sizeof(BWRawPathItem) * itemCount);
int i;
for (i = 0; i < itemCount; i++) {
[aDecoder decodeValueOfObjCType: @encode(BWRawPathItem)
at: &items[i]];
}
// decoding an object
name = [aDecoder decodeObject];
[name retain];
// any other random initializations that don't come from
// the encoder
fooby = -1;
}
return (self);
} // initWithCoder
- (void) encodeWithCoder: (NSCoder *) aCoder
{
// [super encodeWithCoder:] if you inherit from a class
// that supports encoding. NSObject does not.
// encoding a C type
[aCoder encodeValueOfObjCType: @encode(int)
at: &itemCount];
// encoding an array of C structs
int i;
for (i = 0; i < itemCount; i++) {
[aCoder encodeValueOfObjCType: @encode(BWRawPathItem)
at: &items[i]];
}
// encoding an object
[aCoder encodeObject: name];
} // encodeWithCoder
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: @"/bin/ls"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: @"-l", @"-a", @"-t", nil];
[task setArguments: arguments];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
NSData *data;
data = [file readDataToEndOfFile];
NSString *string;
string = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
NSLog (@"woop! got\n%@", string);
Of course, you can use different NSFileHandle methods for different styles of reading, and you can make a pipe for standard input so you can feed data to the task.
NSTasks and a bunch of NSPipes and hook them together, or you can use the "sh -c" trick to feed a shell a command, and let it parse it and set up all the IPC. This pipeline cats /usr/share/dict/words, finds all the words with 'ham' in them, reverses them, and shows you the last 5.
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: @"/bin/sh"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: @"-c",
@"cat /usr/share/dict/words | grep -i ham | rev | tail -5", nil];
[task setArguments: arguments];
// and then do all the other jazz for running an NSTask.
One important note:, don't feed in arbitrary user data using this trick, since they could sneak in extra commands you probably don't want to execute.
stripper.pl which removes everything that looks like an HTML / SGML / XML tag:
#!/usr/bin/perl
while (<>) {
$_ =~ s/<[^>]*>//gs;
print $_;
}
Be sure to chmod +x the script. Add it to your project, add a new "Copy Files" phase, and have it stick this script into the Executables directory.
This method will take a string and feed it through the perl script:
- (NSString *) stringStrippedOfTags: (NSString *) string
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *stripperPath;
stripperPath = [bundle pathForAuxiliaryExecutable: @"stripper.pl"];
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: stripperPath];
NSPipe *readPipe = [NSPipe pipe];
NSFileHandle *readHandle = [readPipe fileHandleForReading];
NSPipe *writePipe = [NSPipe pipe];
NSFileHandle *writeHandle = [writePipe fileHandleForWriting];
[task setStandardInput: writePipe];
[task setStandardOutput: readPipe];
[task launch];
[writeHandle writeData: [string dataUsingEncoding: NSASCIIStringEncoding]];
[writeHandle closeFile];
NSMutableData *data = [[NSMutableData alloc] init];
NSData *readData;
while ((readData = [readHandle availableData])
&& [readData length]) {
[data appendData: readData];
}
NSString *strippedString;
strippedString = [[NSString alloc]
initWithData: data
encoding: NSASCIIStringEncoding];
[task release];
[data release];
[strippedString autorelease];
return (strippedString);
} // stringStrippedOfTags
First step was to make the tools inherit from NSResponder:
@interface BWTool : NSResponder
{
// blah
}
// ... more blah
@end // BWTool
Then, in the view class where the tool gets set, put the tool in the responder chain by setting its next responder to be the view's current next responder. If a different tool gets set, take the current tool's next responder and give it to the new tool. Otherwise the view gets its original next responder back:
- (void) setTool: (BWTool *) newTool
{
NSResponder *nextResponder;
// this is the next element in the chain
if (currentTool != nil) {
nextResponder = [currentTool nextResponder];
} else {
nextResponder = [self nextResponder];
}
// decide who gets to point to the next responder
if (newTool != nil) {
// stick the tool into the chain
[self setNextResponder: newTool];
[newTool setNextResponder: nextResponder];
} else {
// cut the tool out of the chain (if there was one)
[self setNextResponder: nextResponder];
}
[newTool retain];
[currentTool release];
currentTool = newTool;
} // setDrawTool
And now tools can have action methods, and menu items that enable and disable appropriately.
- (NSString*) title
{
return (title);
} // title
- (void) setTitle: (NSString *) newTitle
{
[title autorelease];
title = [newTitle copy];
} // setTitle
For maximum safety in the face of threads, use this:
- (NSString *) title
{
return [[title retain] autorelease];
} // title
This puts title into the current thread's autorelease pool, so title is protected from being destroyed by someone else in another thread.
- (void) setTitle: (NSString *) newTitle
{
// use a mutex or NSLock to protect this
if (title != newtitle) {
[title release];
title = [newTitle copy];
}
} // setTitle
By making a copy of the object, you're protected from another thread changing the value underneath you.
id instance variable in your class, and make methods to set and get it:
id delegate;
...
-(void) setDelegate: (id) del
{
delegate = del; // you don't need to retain it
} // setDelegate
- (id) delegate
{
return (delegate);
} // delegate
NSObject so the compiler won't generate warnings, and also to document the delegate methods you support:@interface NSObject(BWStitchViewDelegate) - (BOOL) shouldHandleStuff: (NSData *) data; @end
...
if ([delegate respondsToSelector: @selector(shouldHandleStuff:)]) {
BOOL blah;
blah = [delegate shouldHandleStuff: somedata];
if (blah) ...
}
- (BOOL) respondsToSelector: (SEL) aSelector
{
NSLog (@"%s", (char *) aSelector);
return ([super respondsToSelector: aSelector]);
} // respondsToSelector
This takes advantage of an implementation detail where the SEL is actually a pointer to a C string
[themStitches sortUsingSelector: @selector(compareByRowLocation:)];
And this selector on the BWCrossStitch class:
- (NSComparisonResult) compareByRowLocation: (BWCrossStitch *) thing2;Figure out which is lesser or greater, and return one of these. Note that
self is the object's logical value, not the actual value of the self pointer.
NSOrderedAsending if self < thing2
NSOrderedDescending if self > thing2
NSOrderedSame if self == thing2
NSColor you used was created in Device space (e.g. colorWithDeviceRed:green:blue:alpha:) If you use a Calibrated color (colorWithCalibratedRed:green:blue:alpha:), the triangle goes away.
extern NSString *BWStitchGroup_VisualAttributeChangeNotification;
In the .m file:
NSString *BWStitchGroup_VisualAttributeChangeNotification
= @"BWStitchGroup Visual Attribute Change Notification";
...
NSNotificationCenter *center;
center = [NSNotificationCenter defaultCenter];
[center postNotificationName:
BWStitchGroup_VisualAttributeChangeNotification
object: self];
If you want to pass a userinfo dictionary, you can use a variant method:
NSDictionary *userInfo = ...;
[center postNotificationName: BWStitchGroup_VisualAttributeChangeNotification
object: self
userInfo: userInfo];
NSNotificationCenter *center;
center = [NSNotificationCenter defaultCenter];
[center addObserver: self
selector: @selector(groupVisualChange:)
name: BWStitchGroup_VisualAttributeChangeNotification
object: group];
Where the selector looks like this:
- (void) groupVisualChange: (NSNotification *) notification
{
// do stuff with the notification
} // groupVisualChange
-dealloc:
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver: self];
#import <Cocoa/Cocoa.h>
#import <stdio.h> // for printf()
@interface NotificationSpy
{
}
+ (void) startSpying;
+ (void) stopSpying;
@end // NotificationSpy
// prevent us from adding ourselves multiple times
static BOOL g_spying;
@implementation NotificationSpy
// turn on listening for all notifications.
+ (void) startSpying
{
if (!g_spying) {
NSNotificationCenter *center;
// first the default notification center, which is all
// notifications that just happen inside of our program
center = [NSNotificationCenter defaultCenter];
[center addObserver: self
selector: @selector(observeDefaultCenterStuff:)
name: nil
object: nil];
// then the NSWorkspace notification center, which tells us things
// like other applications launching, and the machine sleeping
// and waking
center = [[NSWorkspace sharedWorkspace]
notificationCenter];
[center addObserver: self
selector: @selector(observeWorkspaceStuff:)
name: nil
object: nil];
// and lastly the distributed notification center. This is a
// global (to the computer) notification center. You can find
// out when a different program gets focus, or the sound or
// screen brightness changes.
center = [NSDistributedNotificationCenter
notificationCenterForType: NSLocalNotificationCenterType];
[center addObserver: self
selector: @selector(observeDistributedStuff:)
name: nil
object: nil];
g_spying = YES;
}
} // startSpying
// remove us as observers
+ (void) stopSpying
{
if (!g_spying) {
NSNotificationCenter *center;
// take us off the default center for our app
center = [NSNotificationCenter defaultCenter];
[center removeObserver: self];
// and for the workspace
center = [[NSWorkspace sharedWorkspace]
notificationCenter];
[center removeObserver: self];
// and finally off of the machine-wide center
center = [NSDistributedNotificationCenter
notificationCenterForType: NSLocalNotificationCenterType];
[center removeObserver: self];
g_spying = NO;
}
} // stopSpying
+ (void) observeDefaultCenterStuff: (NSNotification *) notification
{
// QuietLog can also be found in the quickies
QuietLog (@"default: %@", [notification name]);
} // observeDefaultCenterStuff
+ (void) observeDistributedStuff: (NSNotification *) notification
{
QuietLog (@"distributed: %@", [notification name]);
} // observeDistributedStuff
+ (void) observeWorkspaceStuff: (NSNotification *) notification
{
QuietLog (@"workspace: %@", [notification name]);
} // observeWorkspaceStuff
@end // NotificationSpy
File->Convert, set these attributes:
NSSound
Addendum: NSSound is based on Core Audio and QuickTime as of 10.3, so it may be able to play .snd resource files as-is. So give that a try if you don't need to support 10.2 or earlier. (Thanks to Peter Hosey for the clarification.)
xsd:date has a format that's a subset of the full ISO8601 format. Here's a quick way to convert an xsd:date to an NSDate. Based on a forum posting by Jens Alfke. For a full ISO 8601 parser, check out the one by Peter Hosey.
Note that thiscode
NSDate *xsdDateTimeToNSDate (NSString *dateTime) {
static NSDateFormatter *xsdDateTimeFormatter;
if (!xsdDateTimeFormatter) {
xsdDateTimeFormatter = [[NSDateFormatter alloc] init]; // Keep around forever
xsdDateTimeFormatter.timeStyle = NSDateFormatterFullStyle;
xsdDateTimeFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:sszzz";
}
// Date formatters don't grok a single trailing Z, so make it "GMT".
if ([dateTime hasSuffix: @"Z"]) {
dateTime = [[dateTime substringToIndex: dateTime.length - 1]
stringByAppendingString: @"GMT"];
}
NSDate *date = [xsdDateTimeFormatter dateFromString: dateTime];
if (!date) NSLog(@"could not parse date '%@'", dateTime);
return (date);
} // xsdDateTimeToNSDate
NSDate *today; today = [NSDate date];
English.lproj.
index.html Put in these headers:<head> <meta http-equiv="content-type" content="text/html;charset=iso-8859-1"> <title>AppName Help</title> <meta name="AppleTitle" content="AppName Help"> <meta name="AppleIcon" content="AppName%20Help/images/icon-name.png"> <meta name="AppleFont" content="Lucida Grande,Helvetica,Arial"> <meta name="AppleSearchResultsFont" content="Lucida Grande,Geneva,Arial"> <meta name="robots" content="noindex"> </head>Along with your actual help content.
AppName Help directory into your XCode project. When Adding, make sure "Copy items into destination group's folder" is unchecked, and select "Create Folder References for any add folders". You wnat XCode to preserve your AppName Help directory.
<key>CFBundleHelpBookFolder</key> <string>AppName Help</string> <key>CFBundleHelpBookName</key> <string>AppName Help</string>
Set your Versioning System (VERSIONING_SYSTEM) to "apple-generic".
Set your Current Project Version (CURRENT_PROJECT_VERSION) to some floating point value that you'll increment every build. You could just start at 1, unless you already have builds out there with other numbers.
This will generate and build a source file automatically that defines two globals whenever you build your project. One is a double corresponding to CURRENT_PROJECT_VERSION, the other is a string. The file is a derived source file; it won't be added to your project, but it will be built with it.
If you're building a framework, there are other build settings you'll probably want to set as well, such as Current Version (DYLIB_CURRENT_VERSION), Compatibility Version (DYLIB_COMPATIBILITY_VERSION), and VERSION_INFO_PREFIX.
To update the version number, you can use agvtool next-version or agvtool new-version.
/Library/Application Support/Apple/Developer Tools/Project Templates/ and edit the projects there.
(For Project Builder, go to /Developer/ProjectBuilder Extras/Project Templates/ and edit the projects there. The changes will take effect next time you create a new project.)
xcodebuild is the pbxbuild equivalent. To get a development build, you'll need to specify the style:% xcodebuild -buildstyle Development
.octest file of interest:
% find . -name "*.octest" -print ./build/Debug/Tests.octestThen gdb
Tools/otest:
% gdb /Developer/Tools/otestThen run it with the name of the bundle:
(gdb) run ./build/Debug/Tests.octest
ld: Undefined symbols: .objc_class_name_BWTrackerPluginYou can fix this by adding
-undefined dynamic_lookup to the "Other Linker Flags" of the Build Tab of the Target Settings for your plugin (whew!). You also need to make sure that the "Mac OS X Deployment Target" is 10.2 or later before using this flag.
Here's how Jens cast out that demon. It's two different edits:
To make the TOC section (left side bar) of the developer documentation default to be hidden, do as follows:
Before you begin, make the two files and their parent-folders writable.
In the file....
/Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.CoreReference.docset/Contents/Resources/Documents/documentation/Resources/CSS/frameset_styles.css
...change the line...
#bodyText { margin-left: 210px; }
...to read...
#bodyText { /* margin-left: 210px; */ margin-left:10px; /* TOC-FIX */ }
...And in the file...
/Developer/Documentation/DocSets/com.apple.ADC_Reference_Library.CoreReference.docset/Contents/Resources/Documents/documentation/Resources/JavaScript/page.js
...add the following somewhere in initialize_page() function, for instance at the bottom, right before the closing brace...
showHideTOC('hide'); // TOC-FIX
...now you have a much better view!!
Note that you'll need to apply this patch when the docs get upgraded.
Project->New Group. Give it a name. Note that the hierarchy of files in Xcode is a fantasy of the project, and doesn't necessarily reflect what's actually in the file system.
ld: multiple definitions of symbol __ZN13CaptureWidgetD2Ev
/Users/blah/blah/build/./capture.build/Debug/capture.build/Objects-normal/ppc/capturewidget-F17F43C0.o definition of __ZN13CaptureWidgetD2Ev in section (__TEXT,__text)
/Users/blah/blah/build/./capture.build/Debug/capture.build/Objects-normal/ppc/capturewidget-F17F43C0.o definition of __ZN13CaptureWidgetD2Ev in section (__TEXT,__text)
This is especially annoying, because the same object file is listed as having a duplicated symbol. If you take out the file from being compiled, you get undefined symbols for that particular source file, and if you put it back in, you get duplicated symbols.
Turn out the problem is that Xcode (somehow) added the C++ file to the "Build Sources" phase of the target. It happily generated two identical object files, and proceded to link them (hence the error), but doesn't get the reporting information correct (hence saying that both symbols come from the same file)
perl -pi -e 's/MyDocument/BWGraphDocument/g' *.[hm] *.xcodeproj/* *.plist
mv MyDocument.h BWGraphDocument.h
mv MyDocument.m BWGraphDocument.m
mv MyDocument.nib BWGraphDocument.nib
- open the nib file, drag in your document header and change the class of the File's Owner to the new class. Delete the MyDocument type.
translate to run the powerPC side of the world:
% xcodebuild -configuration Release -target "Test All" -project Tests.xcodeproj NATIVE_ARCH="i386 ppc" % /usr/libexec/oah/translate /Developer/Tools/otest build/Release/Tests.octest
% /Developer/Tools/otest -SenTest ClassName ./build/Debug/TheUnitTest.octest
% /Developer/Tools/otest path/to/build/Unittest/TheUnit Test.octest
compile sources build phase. If you get info on the source file in other places in Xcode, there's no build tab, and you're left with that "Am I crazy? I know I've seen that setting somewhere before, but where is it? I better go back to listening to Finnish polkas 24/7 and give up this software thing" kind of feeling.
(In Project Builder, open the Targets tab, double-click the target, go to the "Build Settings" tab, scroll down to Build Settings, look for WARNING_CFLAGS. Edit that, and add -Werror to what's already there.)
% defaults write com.apple.Xcode XCShowUndoPastSaveWarning NO
% cd '/Library/Application Support/Apple/Developer Tools/Project Templates'
% find . -name "*.pbxproj" -exec perl -pi -e "s/ZERO_LINK = YES/ZERO_LINK = NO/g" {} \; -print
blah.M (upper case M), or name your source file blah.mm (two lower case Ms). It's best to use .mm because of the case-insensitive (yet case-preserving) nature of HFS+
/Library/Application Support/Apple/Developer Tools/Project Templates/ for the templates for its projects. You can put your own project templates here, and tweak the existing templates (for instance, Xcode 1.1's Cocoa projects have a compiler warning in main.m, which is really annoying for those of use who treat warnings as errors. You can go into /Library/Application Support/Apple/Developer Tools/Project Templates/Application/Cocoa Application/ and fix that warning if you wish.
This
% cd '/Library/Application Support/Apple/Developer Tools/Project Templates'
% find . -name "*.pbxproj" -exec perl -pi -e "s/ZERO_LINK = YES/ZERO_LINK = NO/g" {} \; -print
// // TapDance.h // Groovilicous // // Created by markd on 7/25/08. // Copyright 2008 __MyCompanyName__. All rights reserved. //With the __MyCompanyName__ placeholder. There is no UI to change this, for obvious reasons. (Why would anyone want to easily and conveniently change something they'll otherwise need to edit in each and every source file they create. That's unpossible). The obvious solution is to drop to the terminal and run the straightforward command:
% defaults write com.apple.Xcode PBXCustomTemplateMacroDefinitions '{"ORGANIZATIONNAME" = "Borkware";}'
Seemple, no? Zee trick, she is doone.
This advice brought to you by a DTS incident.
One way to hack around it is to tell the image to use the pixel size of the underlying image representation:
NSImage *image;
image = [[NSImage alloc] initWithContentsOfFile: path];
NSBitmapImageRep *rep = [[image representations] objectAtIndex: 0];
// If you think you might get something other than a bitmap image representation,
// check for it here.
NSSize size = NSMakeSize ([rep pixelsWide], [rep pixelsHigh]);
[image setSize: size];
NSBitmapImageRep *bits = ...; // get a rep from your image, or grab from a view
NSData *data;
data = [bits representationUsingType: NSPNGFileType
properties: nil];
[data writeToFile: @"/path/to/wherever/test.png"
atomically: NO];
There are also TIFF, BMP, GIF, JPEG file types in addition to PNG.
awakeFromNib method, do [objController setContent: self];
awakeFromNib helps prevent a retain cycle which can lead to memory leaks.
Put this in a convenient place:
+ (void) initialize
{
[NSTextFieldCell setDefaultPlaceholder: @""
forMarker: NSNotApplicableMarker
withBinding: NSValueBinding];
} // initialize
- (void) observeValueForKeyPath: (NSString *) keyPath
ofObject: (id) object
change: (NSDictionary *) change
context: (void *) context
The NSKeyValueChangeKindKey key in the dictionary tells you if the change was an insertion
(NSKeyValueMinusSetMutation) or a deletion (NSKeyValueIntersectSetMutation)
If it is an insertion, the NSKeyValueChangeIndexesKey is an index set that contains the index of the inserted object. You can query the collection for the object at that index to get the new object.
if it a deletion, the NSKeyValueChangeIndexesKey tells you the index where the object was deleted from, and the NSKeyValueChangeOldKey contains an NSArray of objects which were removed, in case you want to hang on to it, or use it to clean out some of your data structures.
valueForUndefinedKey::
- (id) valueForUndefinedKey: (NSString *) key
{
id value;
value = [self lookUpValueUsingSomeOtherMechanism: key];
if (value == nil) {
value = [super valueForUndefinedKey: key];
}
return (value);
} // valueForUndefinedKey
Some handy uses for this is using the user defaults for storing values (you can use the key directly to [NSUserDefaults stringForKey:], or use it to query the contents of an NSDictionary
The counterpart for this is
- (void) setValue: (id) value forUndefinedKey: (NSString *) key, which you can use to stash stuff into user prefs or a dictionary.
In the header file
#import <Cocoa/Cocoa.h>
@interface BWSearchArrayController : NSArrayController
{
NSString *searchString;
}
- (IBAction) search: (id) sender;
@end // BWSearchArrayController
and then in the implementation:// returns an array containing the content of the objects arranged
// with the user's critera entered into the search box thingie
- (NSArray *) arrangeObjects: (NSArray *) objects
{
// result of the filtering
NSArray *returnObjects = objects;
// if there is a search string, use it to compare with the
// search field string
if (searchString != nil) {
// where to store the filtered
NSMutableArray *filteredObjects;
filteredObjects = [NSMutableArray arrayWithCapacity: [objects count]];
// walk the enumerator
NSEnumerator *enumerator = [objects objectEnumerator];
id item; // actully BWFileEntries
while (item = [enumerator nextObject]) {
// get the filename from the entry
NSString *filename;
filename = [item valueForKeyPath: @"fileName"];
// see if the file name matches the search string
NSRange range;
range = [filename rangeOfString: searchString
options: NSCaseInsensitiveSearch];
// found the search string in the file name, add it to
// the result set
if (range.location != NSNotFound) {
[filteredObjects addObject: item];
}
}
returnObjects = filteredObjects;
}
// have the superclass arrange them too, to pick up NSTableView sorting
return ([super arrangeObjects:returnObjects]);
} // arrangeObjects
and then to set the search string: - (void) setSearchString: (NSString *) string
{
[searchString release];
if ([string length] == 0) {
searchString = nil;
} else {
searchString = [string copy];
}
} // setSearchString
- (void) search: (id) sender
{
[self setSearchString: [sender stringValue]];
[self rearrangeObjects];
} // search
observeValueForKeyPath:ofObject:change:context:, (or you do override it but don't handle your callbacks) you'll generally get an exception.
This trains you to do it right and never call super for the callbacks you're handling, or you'll get an exception.
Except
if you've added KVO to a subclass of NSArrayController (and possibly all controller types), and don't call super's observeValueForKeyPath:ofObject:change:context:, bindings won't work at all, with no warning/notice/nothing. (Courtesy of Duncan Wilcox)
[imageView bind: @"valuePath"
toObject: imagesController
withKeyPath: @"selection.fullPath"
options: nil];
In Interface Builder, "Bind To" corresponds to imagesController, "Controller Key" would be selection, and "Model Key Path would be fullPath.
Use
[imageView unbind: @"valuePath"];to remove a binding.
- (NSArray *) layers
{
return (layers);
} // layers
- (void) insertObject: (id) obj
inLayersAtIndex: (unsigned) index
{
[layers insertObject: obj atIndex: index];
} // insertObjectInLayers
- (void) removeObjectFromLayersAtIndex: (unsigned) index
{
[layers removeObjectAtIndex: index];
} // removeObjectFromLayersAtIndex
In Interface Builder, drag over the NSSearchField, and bind the predicate like this:
what contains[c] $value (assuming the attribute you're filtering is called what)
[searchArrayController addObserver: self
forKeyPath: @"selectionIndexes"
options: NSKeyValueObservingOptionNew
context: NULL];
This makes self an observer of the searchArrayController. We'll get notified when the selectionIndexes value changes, and we'll be notified with the new value.
When the notification happens, this method (invoked against the self used earlier) is invoked:
- (void) observeValueForKeyPath: (NSString *) keyPath
ofObject: (id) object
change: (NSDictionary *) change
context: (void *) context
{
} // observeValueForKeyPath
and you can poke around the arguments to see wha'happened.
- (void) setDefaultPrefs
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject: NSLocalizedString(@"BorkDown", nil)
forKey: @"menuTitle"];
[dictionary setObject: NSLocalizedString(@"Time has run out.", nil)
forKey: @"alertWindowText"];
// and any other preferences you might have
[[NSUserDefaults standardUserDefaults]
registerDefaults: dictionary];
} // setDefaultPrefs
First, in the +initialize for the class that's going to be observed:
+ (void) initialize
{
NSArray *keys;
keys = [NSArray arrayWithObjects: @"showMajorLines", @"minorWeight",
@"minorColor", @"minorWeightIndex", @"minorColorIndex",
@"majorWeightIndex", @"majorColorIndex", nil];
[BWGridAttributes
setKeys: keys
triggerChangeNotificationsForDependentKey: @"gridAttributeChange"];
} // initialize
So now when "showMajorLines" changes, "gridAttributeChange" will also be observed. KVO requires there actually must exist a gridAttributeChange method (or ivar I presume) before it'll do the notification to observing objects, so there needs to be a do-nothing method:
- (BOOL) gridAttributeChange
{
return (YES);
} // gridAttributeChange
So now the view can do this:
- (void) setGridAttributes: (BWGridAttributes *) a
{
[attributes removeObserver: self
forKeyPath: @"gridAttributeChange"];
[a retain];
[attributes release];
attributes = a;
[a addObserver: self
forKeyPath: @"gridAttributeChange"
options: NSKeyValueObservingOptionNew
context: NULL];
} // setGridAttributes
And will get updated whenever an individual attribute changes.
setValue:forKeyPath: to stick the value back into the
bound object.
The bindings and path are ivars:
id selectionRectBinding;
NSString *selectionRectKeyPath;
In bind:toObject:withKeyPath:options: hang on to the binding
object and the key path and set up observing:
// hold on to the binding info
selectionRectBinding = observableObject;
selectionRectKeyPath = [observableKeyPath copy];
// connect KVO
[valuePathBinding addObserver: self
forKeyPath: selectionRectKeyPath
options: nil
context: NULL];
// new binding, needs to redraw
[self setNeedsDisplay: YES];
And in the mouseUp: handler, set the value back into the bound object:
// figure out the selection rectangle
NSRect selectionRect = [self normalizedSelectionRect];
// wrap in a value and tell the bound object the new value
NSValue *value;
value = [NSValue valueWithRect: selectionRect];
[selectionRectBinding setValue: value
forKeyPath: selectionRectKeyPath];
- (oneway void) release;
{
// special case when count is 3, we are being retained twice by the object controller...
if ( [self retainCount] == 3 )
{
[super release];
[filesOwnerProxy setContent:nil];
return;
}
[super release];
}
selection, using @count.
observeValueForKeyPath: method. Register your observer like this:
[searchArrayController addObserver: self
forKeyPath: @"selectionIndexes"
options: NSKeyValueObservingOptionNew
context: NULL];
So now self's observeValue method will get invoked when selectionIndexes changes in the array controller.
[[scrollView contentView] setPostsBoundsChangedNotifications: YES];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter] ;
[center addObserver: self
selector: @selector(boundsDidChangeNotification:)
name: NSViewBoundsDidChangeNotification
object: [scrollView contentView]];
...
- (void) boundsDidChangeNotification: (NSNotification *) notification
{
[self setNeedsDisplay: YES];
// or whatever work you need to do
} // boundsDidChangeNotification
[[scrollView contentView] scrollToPoint: pointToScrollTo];
[scrollView reflectScrolledClipView: [scrollView contentView]];
% svn mkdir -m "initial revision" file:///usr/local/svnroot/MilleBorks
% mkdir DungeonBorkventure % cd DungeonBorkventure % mkdir branches tags trunk # put source files into trunk % svn import . file:///usr/local/svnroot -m "initial checkin" % cd .. % rm -rf DungeonBorkventure # or move it aside % svn checkout file:///usr/local/svnroot/trunk DungeonBorkventure % cd DungeonBorkventure # and get to work
First, make the directories in the repository:
% svn mkdir -m "initial revision" file:///usr/local/svnroot/DungeonBorkventure % svn mkdir -m "initial revision" file:///usr/local/svnroot/MilleBorksThen import the different code bases:
% cd /path/to/DungeonBorkventure % svn import -m "initial revision" . file:///usr/local/svnroot/DungeonBorkventure % cd /path/to/MilleBorks % svn import -m "initial revision" . file:///usr/local/svnroot/MilleBorksThen checkout working copies of the projects:
% cd /work/and/play/area % svn checkout file:///usr/local/svnroot/MilleBorks/trunk MilleBorks % svn checkout file:///usr/local/svnroot/DungeonBorkventure/trunk DungeonBorkventure
svn status. Add this to your ~/.subversion/config file.
[miscellany] global-ignores = build *.mode* *.pbxuser *~.nib .DS_Store *~Now it won't bug me about the build directory the -per-user Xcode files, nib backup files, the #$&!! .DS_Store file that the Finder litters everywhere, and also don't bug me about emacs backup files.
You can search for global-ignores in that file to see some info about the setting. You might want to check out Spring Cleaning with Subversion too.
% svnadmin create --fs-type fsfs /usr/local/svnroot/
% svn copy file:///usr/local/svnroot/HackDie/trunk
file:///usr/local/svnroot/HackDie/tags/stable-1
-m "tag for HackDie internal stable release #1"
% svn status --show-updates
You can specify files and directories of interest.
% svn log -v -r 373 ------------------------------------------------------------------------ r373 | markd | 2005-06-22 16:05:38 -0400 (Wed, 22 Jun 2005) | 1 line Changed paths: A /xyz/trunk/docs/bugreports/4158233-nsbrowser A /xyz/trunk/docs/bugreports/4158233-nsbrowser/browser-rows.png A /xyz/trunk/docs/bugreports/4158233-nsbrowser/browserdraw.tar.gz A /xyz/trunk/docs/bugreports/4158233-nsbrowser/bug.txt initial revision ------------------------------------------------------------------------and you can look repository-wide too:
% svn log -v -r 373 file:///usr/local/svnroot/
% svn mkdir -m "initial revision" svn+ssh://borkware.com/path/to/svnroot/thingie
% svn add bunnies@2x.png svn: warning: 'bunnies' not found % svn add bunnies@2x.png@ A bunnies@2x.png(thanks to Gus Mueller, Mike Ash, and Guy English)
% svn switch --relocate http://from-address.flongswozzle.net http://to-address.borkware.comMuchos thankos to Lucas Eckels for the tip.
radius
- (float) radius
{
[self willAccessValueForKey: @"radius"];
float f = radius;
[self didAccessvalueForKey: @"radius"];
return (f);
} // radius
- (void) setRadius: (float) newRadius
{
[self willChangeValueForKey: @"radius"];
radius = newRadius;
[self didChangeValueForKey: @"radius"];
} // setRadius
- (NSString *) name
{
[self willAccessValueForKey: @"name"];
NSString *string = [self primitiveValueForKey: @"name"];
[self didAccessValueForKey: @"name"];
} // name
- (void) setName: (NSString *) x
{
[self willChangeValueForKey: @"name"];
[self setPrimitiveValue: x forKey: @"name"];
[self didChangeValueForKey: @"name"];
} // setName
self has methods to get the managed object context and managed object model (Apple's CoreData Application template does)
Thanks to Evan Moseman who pointed me at the new easy way to do it:
NSManagedObjectContext *moc = [self managedObjectContext];
NSManagedObject *obj = [NSEntityDescription
insertNewObjectForEntityForName :@"Condition"
inManagedObjectContext: context];
And the older, more manual way if you need some more control over the process:
NSManagedObjectContext *moc = [self managedObjectContext];
NSManagedObjectModel *mom = [self managedObjectModel];
NSEntityDescription *entity;
entity = [[mom entitiesByName] objectForKey: @"Condition"];
NSString *className;
className = [entity managedObjectClassName];
NSManagedObject *obj;
obj = [[NSClassFromString(className) alloc]
initWithEntity: entity
insertIntoManagedObjectContext: moc];
[obj autorelease];
[obj setValue: @"nook" forKey: @"name"];
[obj setValue: [NSNumber numberWithInt: 23] forKey: @"ranking"];
mutableSetValueForKey: This returns a proxy that mutates the relationship and does KVO notifications. Think of the name as "[NS]MutableSet" "valueForKey" rather than "mutableSetValue" "forKey", because it returns a mutable set that you manipulate
NSMutableSet *employees; employees = [department mutableSetValueForKey: @"employees"]; [employees addObject: newEmployee]; [employees removeObject: sackedEmployee];
NSSortDescriptor *sorter;
sorter = [[NSSortDescriptor alloc]
initWithKey: @"when"
ascending: YES];
[fetchRequest setSortDescriptors:
[NSArray arrayWithObject: sortDescriptor]];
awakeFromInsert:
- (void) awakeFromInsert
{
[super awakeFromInsert];
NSDate *date = [NSDate date];
[self setValue: date forKey: @"when"];
} // awakeFromInsert
- (void) setWhenSortDescriptors: (NSArray *) descriptors
{
} // setWhenSortDescriptors
- (NSArray *) whenSortDescriptors
{
NSSortDescriptor *sorter;
sorter = [[[NSSortDescriptor alloc]
initWithKey: @"when"
ascending: NO] autorelease];
return ([NSArray arrayWithObject: sorter]);
} // whenSortDescriptors
This is for a 'permanent' sorting, not allowing the user to change the sorting. Also, new/changed objects added to the collection don't appear to be placed in sorted order.
Don't use the XML store, but instead use the SQLite store
If an entity has a large data blob as an attribute, you should make a separate entity for that attribute.
e.g. a Person has a 'photo' attribute. Create a Photo entity with a single attribute (the data) and a relationship back to the person (if it makes sense, usually it does), and a relationship from the Person to the Photo entity.
This means that the photo data will only be loaded from the persistent store if you actually use it.
(markd: this also has the nice side effect of letting the db put all the blob data elsewhere, rather than in with the other data, giving you better locality of reference to your Person data without slogging through all the Photo jazz if you don't need it)
In my NSSegmentedControl category, I have a method to solve this problem:
@interface NSSegmentedControl (BorkwareAdditions)
- (void) unselectAllSegments;
// ... other goodies
@end // NSSegmentedControl
- (void) unselectAllSegments
{
NSSegmentSwitchTracking current;
current = [self trackingMode];
[self setTrackingMode: NSSegmentSwitchTrackingMomentary];
int i;
for (i = 0; i < [self segmentCount]; i++) {
[self setSelected: NO forSegment: i];
}
[self setTrackingMode: current];
} // unselectAllSegments
defaults write com.apple.Safari IncludeDebugMenu 1
if (window.console) {
window.console.log ("ook1");
}
(window.console exists in Safari, but not in Dashboard)
WebArchive *archive = ... get from somewhere...; WebFrame *frame = [webView mainFrame]; [frame loadArchive: webarchive];
...
[someWebView setPolicyDelegate: self];
...
- (void) webView:(WebView *)webView
decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
frame:(WebFrame *)frame
decisionListener:(id)listener {
NSURL *url = [request URL];
if (url != nil) {
[[NSWorkspace sharedWorkspace] openURL:url];
}
} // decidePolicyForNavigationAction
% defaults write com.apple.Safari WebKitOmitPDFSupport -bool YESif you have the misfortune of having the Acrobat Reader plug-in, you can nuke
/Library/Internet Plug-Ins/AdobePDFViewer.plugin
So, combine the two for maximal pleasure:
function cavemanLog(message) {
if (window.widget) {
// Won't display an error box, but will show in the console log.
window.alert(message);
} else {
// Send output to the javascript console.
window.console.log(message);
}
} // cavemanLog
open it from the terminal, you get asked if you want to install it (which will move your widget to ~/Library/Widgets, which is really annoying when it drags it out of your development area. When faced with the dialog, hold down command and option to get a "Run" button. That'll run the widget in-pace. If you tun on the devmode:
% defaults write com.apple.dashboard devmode YESYou can drag widgets out of the dashboard area and have them on-screen. Cmd-R will reload the widget.
% sdef /Applications/iTunes.app | sdp -fh --basename iTunesMakes iTunes.h
#define runScriptName @"checknewnow"
#define runScriptType @"scpt"
- (IBAction)runScript:(id)sender
{
/* Locate that darn thing*/
NSString *scriptPath = [[NSBundle mainBundle]
pathForResource: runScriptName
ofType: runScriptType];
NSURL *scriptURL = [NSURL fileURLWithPath: scriptPath];
NSAppleScript *as = [[NSAppleScript alloc]
initWithContentsOfURL: scriptURL
error: nil];
[as executeAndReturnError: NULL];
[as release];
}
(Thanks to of Chris "The_Tick" Forsythe)
$title$ for the page term.
tell application "VoodooPad" taunt end tell
NSData *blah = [xmldocument XMLDataWithOptions: NSXMLNodePrettyPrint];
NSString *snork = [[NSString alloc] initWithData: blah
encoding: NSUTF8StringEncoding];
NSAttributedString *arrrgh;
arrrgh = [[NSAttributedString alloc] initWithString: snork];
[[xmlTextView textStorage]
setAttributedString: [arrrgh autorelease]];
% p4 revert -c default ...
p4 revert -awill revert all files that haven't actually be edited, leaving the edited opened.
(Thanks to TVL for this one)
p4 edit -t text [file_list] p4 submit(Thanks to TVL for this one)
shift-click
{
$any_click
if @click.simple_name == ""
if @my.right_item == "caduceus"
"\use /off\r"
end if
else if @click.simple_name == @my.simple_name
if @my.right_item != "moonstone"
"\equip \"moonstone\"\r"
end if
"\use 3\r"
else
if @my.right_item != "caduceus"
"\equip \"caduceus\"\r"
end if
"\use " @click.simple_name "\r"
message "* Now healing" @click.name
end if
}
NSMutableArray *sortedArray = [NSMutableArray arrayWithObjects: @"Alice", @"Beth", @"Carol",@"Ellen",nil];
//Where is "Beth"?
unsigned index = (unsigned)CFArrayBSearchValues((CFArrayRef)sortedArray,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)sortedArray)),
(CFStringRef)@"Beth",
(CFComparatorFunction)CFStringCompare,
NULL);
if (index < [sortedArray count])
{
NSLog(@"Beth was found at index %u", index);
} else {
NSLog(@"Beth was not found (index is beyond the bounds of sortedArray)");
}
//Where should we insert "Debra"?
unsigned insertIndex = (unsigned)CFArrayBSearchValues((CFArrayRef)sortedArray,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)sortedArray)),
(CFStringRef)@"Debra",
(CFComparatorFunction)CFStringCompare,
NULL);
[sortedArray insertObject:@"Debra" atIndex:insertIndex];
NSLog([sortedArray description]);
//note: NSArray indices and counts were typed as unsigned. With the move to 64-bit, they are NSUInteger.
//CFArray indices and counts are CFIndex, which was SInt32 but also will move to 64-bit?
//Why was it ever signed and will it remain so?
Muchos Thankos to James Hober for this one.
Gus Mueller chimed in saying that if you use CFArrayBSearchValues, be sure to sort with CFArraySortValues rather than using the Cocoa sorting routines (or at least the comparison) - they treat diacritic marks differently, leading to strange errors. From Gus, a quick objc addition to NSMutableArray:
- (void) cfStringSort {
CFArraySortValues((CFMutableArrayRef)self, CFRangeMake(0, [self count]), (CFComparatorFunction)CFStringCompare, NULL);
}
// We must take care here, since Intel leaves junk in high bytes of return register // for predicates that return BOOL. // For details see: // unibin chapter and verse (link currently broken courtesy of Apple) // and //comment at red-sweater. - (BOOL)filterObject:(id)obj returning:(id *)resultp { *resultp = obj; return ((BOOL (*)(id, SEL, id))objc_msgSend)(obj, operation_, object_); }The explicit cast to the expected return value gets rid of the junk.
hg status? Make a .hgignore at the top level of your repository. It supports glob and regex, and plain names. man hgignore for more details.
~/.hgrc file and make sure the permissions on the file are such that nobody else can read it.
[auth] bitbucket.prefix = bitbucket.org/markd2 bitbucket.username = markd2 bitbucket.password = B4dg3rzRuL3 bitbucket.schemes = http httpsSo now when you do a
% hg push https://bitbucket.org/markd2/borkfitno need to authorize.
@implementation BlahViewController
- (id) init {
if ((self = [super initWithNibName: @"BlahViewController"
bundle: nil])) {
// other initialization
}
return self;
} // init
- (id) initWithNibName: (NSString *) nibNameOrNil
bundle: (NSBundle *) nibBundleOrNil {
return [self init];
} // initWithNibName
UIAlertView *alert =
[[UIAlertView alloc] initWithTitle: @"Some Title"
message: @"You look very nice today."
delegate: self
cancelButtonTitle: @"OK"
otherButtonTitles: nil];
[alert show];
[alert release];
If you want to know what got tapped, use a delegate method. The cancel button is index zero.
- (void) alertView: (UIAlertView *) alertView
clickedButtonAtIndex: (NSInteger) buttonIndex {
NSLog(@"foobage! %d", buttonIndex);
} // clickedButtonAtIndex
-viewDidLoad :
UIBarButtonItem *cancelButton =
[[UIBarButtonItem alloc] initWithTitle: @"Cancel"
style: UIBarButtonItemStylePlain
target: self
action: @selector(cancel:)];
self.navigationItem.rightBarButtonItem = cancelButton;
[cancelButton release];
self.navigationItem.hidesBackButton = YES;
- (void) navigationController: (UINavigationController *) navigationController
willShowViewController: (UIViewController *) viewController
animated: (BOOL) animated {
if (viewController == _menuController) {
[_navigationController setNavigationBarHidden: YES
animated: YES];
}
} // willShowViewController
- (void)tableView: (UITableView *) tableView
didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
// might want someone to know what the user picked.
[_delegate kindChooser: self
choseWorkoutType: mapRowToWorkoutType(indexPath.row)];
// pop ourselves off the stack.
[self.navigationController popViewControllerAnimated: YES];
} // didSelectRowAtIndexPath
GRChooseRideKindViewController *chooseKind =
[[GRChooseRideKindViewController alloc] init];
[self.navigationController pushViewController: chooseKind
animated: YES];
[chooseKind release];
UITableViewCell *cell = ...; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;The delegate method (accessoryTypeForRowWithIndexPath) has been deprecated, so don't use that.
Use UITableViewCellAccessoryCheckmark for a checkmark.
And then override these. In this case, I split up an array into an array of arrays, each sub-array being the contents of a section that corresponds to a string to display in the index. _tableIndex is an array of strings for display of the index.
- (NSArray *) sectionIndexTitlesForTableView: (UITableView *) tableView {
return _tableIndex;
} // sectionIndexTitles
- (NSInteger) tableView: (UITableView *) tableView
sectionForSectionIndexTitle: (NSString *) title
atIndex: (NSInteger) index {
return index;
} // sectionForSectionIndexTitle
if (someProperty) cell.textLabel.textColor = [UIColor grayColor];
else cell.textLabel.textColor = [UIColor blackColor];
- (BOOL) tableView: (UITableView *) tableView
canMoveRowAtIndexPath: (NSIndexPath *) indexPath {
if (indexPath.row == 0) return NO;
else if (indexPath.row == _cues.count - 1) return NO;
else return YES;
} // canMoveRowAtIndexPath
And then prevent rows from being dragged to the first and last position:
- (NSIndexPath *) tableView: (UITableView *) tableView
targetIndexPathForMoveFromRowAtIndexPath: (NSIndexPath *) source
toProposedIndexPath: (NSIndexPath *) destination {
if (destination.row > 0 && destination.row < _cues.count - 1) {
// No violence necessary.
return destination;
}
NSIndexPath *indexPath = nil;
// If your table can have <= 2 items, you might want to robusticize the index math.
if (destination.row == 0) {
indexPath = [NSIndexPath indexPathForRow: 1 inSection: 0];
} else {
indexPath = [NSIndexPath indexPathForRow: _cues.count - 2
inSection: 0];
}
return indexPath;
} // targetIndexPathForMoveFromRowAtIndexPathPleaseOHaiThanksForMovingKthxBai
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row
inSection: 0];
- (IBAction) elaborateType: (id) sender {
if (![sender isKindOfClass: [UIButton class]]) return; // be paranoid
UITableViewCell *cell = (UITableViewCell *)[sender superview];
if (![cell isKindOfClass: [UITableViewCell class]]) return; // be paranoid
NSIndexPath *indexPath = [self.kindPickerTableView indexPathForCell: cell];
// do something with indexPath.row and/or indexPath.section.
} // elaborateType
- (void) tableView: (UITableView *) tableView
commitEditingStyle: (UITableViewCellEditingStyle) editingStyle
forRowAtIndexPath: (NSIndexPath *) indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[_cues removeObjectAtIndex: indexPath.row]; // manipulate your data structure.
[tableView deleteRowsAtIndexPaths: [NSArray arrayWithObject: indexPath]
withRowAnimation: UITableViewRowAnimationFade];
[self updateUI];
}
} // commitEditingStyle
-reloadData, but don't. That's an awfully big hammer. Instead just reload a row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row
inSection: 0];
NSArray *array = [NSArray arrayWithObject: indexPath];
[_playlistTableView reloadRowsAtIndexPaths: array
withRowAnimation: UITableViewRowAnimationNone];
[self.cuesTableView setEditing: YES animated: YES];
- (void) tableView: (UITableView *) tableView
moveRowAtIndexPath: (NSIndexPath *) from
toIndexPath: (NSIndexPath *) to {
// How to rearrange stuff if you're backed by an NSMutableArray:
GRCue *cue = [_cues objectAtIndex: from.row];
[cue retain]; // Let it survive being removed from the array.
[_cues removeObjectAtIndex: from.row];
[_cues insertObject: cue atIndex: to.row];
[cue release];
} // moveRowAtIndexPath
- (void)tableView: (UITableView *) tableView
didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
// Do something logical with indexPath.row, etc.
} // didSelectRowAtIndexPath
NSIndexPath *someRow = [NSIndexPath indexPathForRow: random() % blah.count
inSection: 0];
[self.cuesTableView scrollToRowAtIndexPath: someRow
atScrollPosition: UITableViewScrollPositionBottom
animated: YES];
You can also scroll to PositionTop and PositionMiddle. If you crash, make sure you've done a -reloadData on the table view prior to trying to scroll.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: index
inSection: 0];
[self.tableView reloadData]; // necessary if selecting row in -viewDidLoad
[self.tableView selectRowAtIndexPath: indexPath
animated: YES
scrollPosition: UITableViewScrollPositionNone];
// Optional
- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView {
return 1;
} // numberOfSectionsInTableView
- (NSInteger) tableView: (UITableView *) tableView
numberOfRowsInSection: (NSInteger) section {
return dataArray.count;
} // numberOfRowsInSection
- (UITableViewCell *) tableView: (UITableView *) tableView
cellForRowAtIndexPath: (NSIndexPath *) indexPath {
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier: @"BlahTableViewCell"];
if (!cell) {
cell = [[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: @"BlahTableViewCell"];
[cell autorelease];
}
cell.textLabel.text = [dataArray objectAtIndex: indexPath.row];
return cell;
} // cellForRowAtIndexPath
Make the cell with UITableViewCellStyleSubtitle:
cell = [[[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleSubtitle
reuseIdentifier: @"UITableViewCell"] autorelease];
and set the label values:
cell.textLabel.text = @"somewhere";
cell.detailTextLabel.text = @"over the rainbow";
_tableView.scrollEnabled = NO;
[playButton addTarget: self
action: @selector(play)
forControlEvents: UIControlEventTouchUpInside];
hooverLayer.transform = CATransform3DRotate(CATransform3DIdentity,
90.0 * M_PI / 180.0,
0.0f, 0.0f, 1.0f);
(lldb) set env-vars OOP=ack BORK=greeble
#import "CJSONDeserializer.h"
NSData *jsonData = [NSData dataWithContentsOfFile: path]; // or from network, or whatever
NSError *error;
NSArray *playlists =
[[CJSONDeserializer deserializer] deserializeAsArray: jsonData
error: &error];
#import "CJSONSerializer.h"
NSArray *allSongs = [self allSongs];
NSString *jsonString = [[CJSONSerializer serializer] serializeObject: allSongs];
NSData *data = [jsonString dataUsingEncoding: NSUTF8StringEncoding];
// write |data| to a file, send over the network, etc
[_durationPicker selectRow: row
inComponent: 0
animated: NO];
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
- (NSInteger) pickerView: (UIPickerView *) picker
numberOfRowsInComponent: (NSInteger) component
- (NSString *) pickerView: (UIPickerView *) picker
titleForRow: (NSInteger) row
forComponent: (NSInteger) component
- (void) pickerView: (UIPickerView *) picker
didSelectRow: (NSInteger) row
inComponent: (NSInteger) component
// Are we compiling for the device, simulator, or desktop? #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR #define GR_TARGET_SIMULATOR 1 #elif TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR #define GR_TARGET_DEVICE 1 #else #define GR_TARGET_DESKTOP 1 #endifThen I use the GR_TARGET symbols for #if'ing in platform-specific chunklets of code.
-isEqual: and -hash
- (BOOL) isEqual: (id) object; - (NSUInteger) hash;
UITapGestureRecognizer *tap =
[[UITapGestureRecognizer alloc] initWithTarget: self
action: @selector(twoByThreeTap:)];
tap.numberOfTapsRequired = 2;
tap.numberOfTouchesRequired = 3;
[self addGestureRecognizer: tap];
[tap release];
If you're not getting taps, perhaps you're using a default UIImageView, you'll probably need to turn on interaction and multitouch, either in IB or in code:
// So we can get the taps.
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = YES;
typedef CGRect (^GroovyBlock)(NSInteger spoon, NSString *waffle);Creates a typedef called GroovyBlock that returns a CGRect and takes an integer and a string.