Tool Tip for a Table Column

Hi,

I wonder what’s the correct way to invoke a tool tip for a table column (not a cell).

I tried to assign a tool tip to NSTextFieldCell in Interface Builder, but it didn’t work.

Then I found this method of NSTableView:

  • (NSString *)tableView:(NSTableView *)tv toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc row:(int)row mouseLocation:(NSPoint)mouseLocation

.and after reading various sources and finding some examples, managed to successfully create a class ToolTipController, and set it as the delegate for my table view. (I’m actually proud that it’s the first time I was able to properly create and connect a controller class). Now the table view properly displays a tool tip with the content of the cell if the text in the cell is longer than the column width:

#import “ToolTipController.h”

@implementation ToolTipController

  • (NSString *)tableView:(NSTableView *)tv toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc row:(int)row mouseLocation:(NSPoint)mouseLocation {
    if ([cell isKindOfClass:[NSTextFieldCell class]]) {
    if ([[cell attributedStringValue] size].width > rect->size.width) {
    return [cell stringValue];
    }
    }
    return nil;
    }
    @end

BUT, that’s NOT what I need. I want the tool tip to explain the meaning of the data in the column (i.g. “File name” for a column with identifier “name”, “Image color mode” for a column “mode” etc.)

I tried to insert

if ([cell isKindOfClass:[NSTableHeaderCell class]])

hoping that it’ll make the tool tip to appear when the mouse is over the column header, but it didn’t work.

I also tried this:

  • (NSString *)tableView:(NSTableView *)tv toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc row:(int)row mouseLocation:(NSPoint)mouseLocation {

NSString *idColumn, * testString;
testString=@“File name”;

if ([cell isKindOfClass:[NSTextFieldCell class]]) {

idColumn=[tc identifier];

if (idColumn == @"name"]) {
	    	return testString; 
}
}
return nil;
}

.but it didn’t work either. My understanding is that I’m not coercing the values properly:

The method for getting column’s identifier - [tc identifier] - returns id and not string. So basically I need to know how to coerce the column identifier into string, and my knowledge of Obj-C is still not good enough for this.

Hope it all makes sense.

Thanks for any help,
Leo

Actually, there really isn’t a ‘correct’ way to have a headercell tooltip… or even an incorrect way, for that matter. :frowning:

NSTableColumn is a placeholder class that doesn’t actually handle much of it’s own drawing or behavior. Aside from some layout methods and sorting, it leaves all of it’s ui functions to the cells that it uses to build itself. So, talking about table columns without talking about it’s cells isn’t really possible.

From your post, I gather that it’s the header cell that you want to have show the tooltip. Since nscell, and none of the subclasses of nscell have a “setToolTip” method, you’re out of luck as far as having a built-in way of doing this. As you mentioned, there is a delegate method of nstable view that handles tooltips for the individual rows, which could be used something like this…

BUT, as far as I can tell, this does NOT include the header cells. It’s only intended for returning tooltips for row cells.

I looked around a bit, and don’t see anyone having a way already worked out to provide headerCell tooltips. I do have two ideas of where you could start, but both of them are hacks and may be fairly complicated to implement or not fit with your ui.

  1. Are your columns resizable, reorderable, or sorted? If your headers are static and don’t move or change (or you’re willing to make them that way) you could construct the table headers yourself. This would entail adding an image view with an image of a table header, and a text field that contains the header text. Both the imageview and the textfield descend from nsview and can have tooltips. I use this technique to get around drawing all of my tools that I have placed in my table headers… similar to how xcode has lots of menus in it’s editor header.

  2. If you don’t like option 1, then you might try the nsview method “addToolTipRect:owner:userData:”. This allows you to manually assign a tooltip for a region within an nsview. You could get the rect for the header cells and assign tooltips accordingly. I haven’t used this method before, but from my expereinces with table views, I see two potential problems with this approach. First, table headers do their own thing a lot of the time. Header cells draw with priority… i.e. they don’t let other objects float above them. They capture events within their bounds and don’t really like their actions to be overridden other than by certain methods. You may not be able to get your tooltip to respond because the headercell is capturing all of the events and won’t send them on to your tooltip code. Second, You’ll have to write a bunch more code to handle the reassigning of the tooltip region whenever you resize the columns. The rects you set initially will not automagically follow the size of the header cells as you resize the tableview or individual columns. This might be excessively complicated for the effect you’re going for.

Perhaps it might be best to provide some other form of help… something that isn’t so significantly outside the mainstream? Consider that since this is not a standard behavior, your users may not ever even figure out that there are tooltips. If your users can’t figure out what to do from your ui, your doc’s, and the header label than you may want to put some more thought into how you’ve designed the interface. If it’s not intended for the mainstream, then you probably don’t need to worry about the tooltips.

One alternative, if you’re not using sorting, is the “tableView:mouseDownInHeaderOfTableColumn:” delegate method. WHile this, too, would be astray from the normal behavior, you could override this method to pop up a help window or some other help when the table header was clicked.

Good luck,
j

Hi irevzin,

here a quick& dirty tableview subclass that provides tooltips for it’s column headers. It ‘abuses’ the table column’s identifiers (as set in Interface Builder) as tool tip strings. Columns stay resizable/movable):

/* WITableView.h */

#import <Cocoa/Cocoa.h>

@interface WITableView.h : NSTableView{
}
- (void)activateIdentifierToolTips;
- (void)deactivateIdentifierToolTips;

- (void)setHeaderCellToolTips;
@end

/* WITableView.m: */

#import "WITableView.h"

@implementation WITableView

- (void)activateIdentifierToolTips{
	[self setHeaderCellToolTips];
	[[NSNotificationCenter defaultCenter] addObserver:self 
											 selector:@selector(columnsChanged:) 
												 name:NSTableViewColumnDidMoveNotification
											   object:self];
	[[NSNotificationCenter defaultCenter] addObserver:self 
											 selector:@selector(columnsChanged:) 
												 name:NSTableViewColumnDidResizeNotification
											   object:self];
}

- (void)deactivateIdentifierToolTips{
	[[self headerView] removeAllToolTips];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:NSTableViewColumnDidMoveNotification   object:self];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:NSTableViewColumnDidResizeNotification object:self];
}



- (void)setHeaderCellToolTips{
	[[self headerView] removeAllToolTips];
	NSEnumerator *cols = [[self tableColumns] objectEnumerator];
	int n = 0; 
	NSTableColumn *col;
	while (col = [cols nextObject]) {
		NSRect ttRect = [[self headerView] headerRectOfColumn:n++];
		if ([col identifier]) {
			NSString *tt = [NSString stringWithString:[[col identifier] description]];
			[tt retain];
			[[self headerView] addToolTipRect:ttRect owner:tt userData:nil];
		}
	}
}

- (void)columnsChanged:(NSNotification *)aNotification{
	[self setHeaderCellToolTips];
}

@end

from AppleScript you simply call:

call method "activateIdentifierToolTips" of (table view ...)
call method "deactivateIdentifierToolTips" of (table view ...)

to activate/deactivate

hope that helps …

D.

jobu and Dominic:

Thanks a lot for your help! I learned quite a few things from jobu’s post and will work on implementing Dominik’s amazing solution as soon as possible.

From Dominik’s method I also found out what was missing from my attempts to manipulate the column identifier: the description method to return the identifier as string.

Later on I also found out why my condition if idColumn == @“name” didn’t work: I had to use the isEqualToString: method:

if [idColumn isEqualToString:@“name”]

Getting to know some basics.

As it works now, I’ll probably implement it into Dominik’s solution to create the tool tips depending on the column identifiers without changing the identifiers themselves.

Thanks again,
Leo