1/* =============================================================================
2	FILE:		UKPrefsPanel.h
3
4	AUTHORS:	M. Uli Kusterer (UK), (c) Copyright 2003, all rights reserved.
5
6	DIRECTIONS:
7		UKPrefsPanel is ridiculously easy to use: Create a tabless NSTabView,
8		where the name of each tab is the name for the toolbar item, and the
9		identifier of each tab is the identifier to be used for the toolbar
10		item to represent it. Then create image files with the identifier as
11		their names to be used as icons in the toolbar.
12
13		Finally, drag UKPrefsPanel.h into the NIB with the NSTabView,
14		instantiate a UKPrefsPanel and connect its tabView outlet to your
15		NSTabView. When you open the window, the UKPrefsPanel will
16		automatically add a toolbar to the window with all tabs represented by
17		a toolbar item, and clicking an item will switch between the tab view's
18		items.
19
20
21	REVISIONS:
22		2003-08-13	UK	Added auto-save, fixed bug with empty window titles.
23		2003-07-22  UK  Added Panther stuff, documented.
24		2003-06-30  UK  Created.
25   ========================================================================== */
26
27/* -----------------------------------------------------------------------------
28	Headers:
29   -------------------------------------------------------------------------- */
30
31#import "UKPrefsPanel.h"
32
33
34@implementation UKPrefsPanel
35
36/* -----------------------------------------------------------------------------
37	Constructor:
38   -------------------------------------------------------------------------- */
39
40-(id) init
41{
42  if ( ! ( self = [super init] ) )
43    return nil;
44
45
46  tabView = nil;
47  itemsList = [[NSMutableDictionary alloc] init];
48  baseWindowName = [@"" retain];
49  autosaveName = [@"com.ulikusterer" retain];
50
51  return self;
52}
53
54
55/* -----------------------------------------------------------------------------
56	Destructor:
57   -------------------------------------------------------------------------- */
58
59-(void)	dealloc
60{
61	[itemsList release];
62	[baseWindowName release];
63	[autosaveName release];
64	[super dealloc];
65}
66
67
68/* -----------------------------------------------------------------------------
69	awakeFromNib:
70		This object and all others in the NIB have been created and hooked up.
71		Fetch the window name so we can modify it to indicate the current
72		page, and add our toolbar to the window.
73
74		This method is the great obstacle to making UKPrefsPanel an NSTabView
75		subclass. When the tab view's awakeFromNib method is called, the
76		individual tabs aren't set up yet, meaning mapTabsToToolbar gives us an
77		empty toolbar. ... bummer.
78
79		If anybody knows how to fix this, you're welcome to tell me.
80   -------------------------------------------------------------------------- */
81
82-(void)	awakeFromNib
83{
84	NSString*		key;
85	int				index = 0;
86	NSString*		wndTitle = nil;
87
88	// Generate a string containing the window's title so we can display the original window title plus the selected pane:
89	wndTitle = [[tabView window] title];
90	if( [wndTitle length] > 0 )
91	{
92		[baseWindowName release];
93		baseWindowName = [[NSString stringWithFormat: @"%@ : ", wndTitle] retain];
94	}
95
96	// Make sure our autosave-name is based on the one of our prefs window:
97	[self setAutosaveName: [[tabView window] frameAutosaveName]];
98
99	// Select the preferences page the user last had selected when this window was opened:
100	key = [NSString stringWithFormat: @"%@.prefspanel.recentpage", autosaveName];
101	index = [[NSUserDefaults standardUserDefaults] integerForKey: key];
102	[tabView selectTabViewItemAtIndex: 0];
103
104	// Actually hook up our toolbar and the tabs:
105	[self mapTabsToToolbar];
106
107	id box = [[[[tabView tabViewItemAtIndex:[tabView indexOfTabViewItem:[tabView selectedTabViewItem]]] view] subviews] objectAtIndex:0];
108	if ([box isKindOfClass:[NSBox class]])
109	{
110		/*float sizeDifference = [box frame].size.height - [[[[[tabView tabViewItemAtIndex:[tabView indexOfTabViewItem:[tabView selectedTabViewItem]]] view] subviews] objectAtIndex:0] frame].size.height;
111		[[windowController window] setFrame:NSMakeRect([[windowController window] frame].origin.x,
112													   [[windowController window] frame].origin.y,
113													   [[windowController window] frame].size.width,
114													   [[windowController window] frame].size.height + sizeDifference)
115									display:YES
116									animate:YES];*/
117		[[windowController window] setContentSize:NSMakeSize([[[windowController window] contentView] frame].size.width, [box frame].size.height)];
118		[box setFrameOrigin:NSMakePoint([box frame].origin.x, 0)];
119	}
120}
121
122
123/* -----------------------------------------------------------------------------
124	mapTabsToToolbar:
125		Create a toolbar based on our tab control.
126
127		Tab title		-   Name for toolbar item.
128		Tab identifier  -	Image file name and toolbar item identifier.
129   -------------------------------------------------------------------------- */
130
131-(void) mapTabsToToolbar
132{
133    // Create a new toolbar instance, and attach it to our document window
134    NSToolbar		*toolbar =[[tabView window] toolbar];
135	int				itemCount = 0,
136					x = 0;
137	NSTabViewItem	*currPage = nil;
138
139	if( toolbar == nil )   // No toolbar yet? Create one!
140		toolbar = [[[NSToolbar alloc] initWithIdentifier: [NSString stringWithFormat: @"%@.prefspanel.toolbar", autosaveName]] autorelease];
141
142    // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
143    [toolbar setAllowsUserCustomization: YES];
144    [toolbar setAutosavesConfiguration: YES];
145    //[toolbar setDisplayMode: NSToolbarDisplayModeIconOnly];
146
147	// Set up item list based on Tab View:
148	itemCount = [tabView numberOfTabViewItems];
149
150	[itemsList removeAllObjects];	// In case we already had a toolbar.
151
152	for( x = 0; x < itemCount; x++ )
153	{
154		NSTabViewItem*		theItem = [tabView tabViewItemAtIndex:x];
155		NSString*			theIdentifier = [theItem identifier];
156		NSString*			theLabel = [theItem label];
157
158		[itemsList setObject:theLabel forKey:theIdentifier];
159	}
160
161    // We are the delegate
162    [toolbar setDelegate: self];
163
164    // Attach the toolbar to the document window
165    [[tabView window] setToolbar: toolbar];
166
167	// Set up window title:
168	currPage = [tabView selectedTabViewItem];
169	if( currPage == nil )
170		currPage = [tabView tabViewItemAtIndex:0];
171	[[tabView window] setTitle: [baseWindowName stringByAppendingString: [currPage label]]];
172
173	#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
174	if( [toolbar respondsToSelector: @selector(setSelectedItemIdentifier:)] )
175		[toolbar setSelectedItemIdentifier: [currPage identifier]];
176	#endif
177}
178
179
180/* -----------------------------------------------------------------------------
181	orderFrontPrefsPanel:
182		IBAction to assign to "Preferences..." menu item.
183   -------------------------------------------------------------------------- */
184
185-(IBAction)		orderFrontPrefsPanel: (id)sender
186{
187	[[tabView window] makeKeyAndOrderFront:sender];
188}
189
190
191/* -----------------------------------------------------------------------------
192	setTabView:
193		Accessor for specifying the tab view to query.
194   -------------------------------------------------------------------------- */
195
196-(void)			setTabView: (NSTabView*)tv
197{
198	tabView = tv;
199}
200
201
202-(NSTabView*)   tabView
203{
204	return tabView;
205}
206
207
208/* -----------------------------------------------------------------------------
209	setAutosaveName:
210		Name used for saving state of prefs window.
211   -------------------------------------------------------------------------- */
212
213-(void)			setAutosaveName: (NSString*)name
214{
215	[name retain];
216	[autosaveName release];
217	autosaveName = name;
218}
219
220
221-(NSString*)	autosaveName
222{
223	return autosaveName;
224}
225
226
227/* -----------------------------------------------------------------------------
228	toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:
229		Create an item with the proper image and name based on our list
230		of tabs for the specified identifier.
231   -------------------------------------------------------------------------- */
232
233-(NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted
234{
235    // Required delegate method:  Given an item identifier, this method returns an item
236    // The toolbar will use this method to obtain toolbar items that can be displayed in the customization sheet, or in the toolbar itself
237    NSToolbarItem   *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
238    NSString*		itemLabel;
239
240    if( (itemLabel = [itemsList objectForKey:itemIdent]) != nil )
241	{
242		// Set the text label to be displayed in the toolbar and customization palette
243		[toolbarItem setLabel: itemLabel];
244		[toolbarItem setPaletteLabel: itemLabel];
245		[toolbarItem setTag:[tabView indexOfTabViewItemWithIdentifier:itemIdent]];
246
247		// Set up a reasonable tooltip, and image   Note, these aren't localized, but you will likely want to localize many of the item's properties
248		[toolbarItem setToolTip: itemLabel];
249		[toolbarItem setImage: [NSImage imageNamed:itemIdent]];
250
251		// Tell the item what message to send when it is clicked
252		[toolbarItem setTarget: self];
253		[toolbarItem setAction: @selector(changePanes:)];
254    }
255	else
256	{
257		// itemIdent refered to a toolbar item that is not provide or supported by us or cocoa
258		// Returning nil will inform the toolbar this kind of item is not supported
259		toolbarItem = nil;
260    }
261
262    return toolbarItem;
263}
264
265
266/* -----------------------------------------------------------------------------
267	toolbarSelectableItemIdentifiers:
268		Make sure all our custom items can be selected. NSToolbar will
269		automagically select the appropriate item when it is clicked.
270   -------------------------------------------------------------------------- */
271
272#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
273-(NSArray*) toolbarSelectableItemIdentifiers: (NSToolbar*)toolbar
274{
275	return [itemsList allKeys];
276}
277#endif
278
279
280/* -----------------------------------------------------------------------------
281	changePanes:
282		Action for our custom toolbar items that causes the window title to
283		reflect the current pane and the proper pane to be shown in response to
284		a click.
285   -------------------------------------------------------------------------- */
286
287-(IBAction)	changePanes: (id)sender
288{
289	NSString*		key;
290
291	[[tabView window] setTitle: [baseWindowName stringByAppendingString: [sender label]]];
292
293	key = [NSString stringWithFormat: @"%@.prefspanel.recentpage", autosaveName];
294	[[NSUserDefaults standardUserDefaults] setInteger:[sender tag] forKey:key];
295
296	id box = [[[[tabView tabViewItemAtIndex:[sender tag]] view] subviews] objectAtIndex:0];
297	if ([box isKindOfClass:[NSBox class]])
298	{
299		float sizeDifference = [box frame].size.height - [[[[[tabView tabViewItemAtIndex:[tabView indexOfTabViewItem:[tabView selectedTabViewItem]]] view] subviews] objectAtIndex:0] frame].size.height;
300		[[windowController window] setFrame:NSMakeRect([[windowController window] frame].origin.x,
301													   [[windowController window] frame].origin.y - sizeDifference,
302													   [[windowController window] frame].size.width,
303													   [[windowController window] frame].size.height + sizeDifference)
304									display:YES
305									animate:YES];
306		[box setFrameOrigin:NSMakePoint([box frame].origin.x, 0)];
307	}
308	[tabView selectTabViewItemAtIndex: [sender tag]];
309}
310
311
312/* -----------------------------------------------------------------------------
313	toolbarDefaultItemIdentifiers:
314		Return the identifiers for all toolbar items that will be shown by
315		default.
316		This is simply a list of all tab view items in order.
317   -------------------------------------------------------------------------- */
318
319-(NSArray*) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
320{
321	int					itemCount = [tabView numberOfTabViewItems],
322						x;
323	NSTabViewItem*		theItem = [tabView tabViewItemAtIndex:0];
324	//NSMutableArray*	defaultItems = [NSMutableArray arrayWithObjects: [theItem identifier], NSToolbarSeparatorItemIdentifier, nil];
325	NSMutableArray*	defaultItems = [NSMutableArray array];
326
327	for( x = 0; x < itemCount; x++ )
328	{
329		theItem = [tabView tabViewItemAtIndex:x];
330
331		[defaultItems addObject: [theItem identifier]];
332	}
333
334	return defaultItems;
335}
336
337
338/* -----------------------------------------------------------------------------
339	toolbarAllowedItemIdentifiers:
340		Return the identifiers for all toolbar items that *can* be put in this
341		toolbar. We allow a couple more items (flexible space, separator lines
342		etc.) in addition to our custom items.
343   -------------------------------------------------------------------------- */
344
345-(NSArray*) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
346{
347    NSMutableArray*		allowedItems = [[itemsList allKeys] mutableCopy];
348
349	[allowedItems addObjectsFromArray: [NSArray arrayWithObjects: NSToolbarSeparatorItemIdentifier,
350				NSToolbarSpaceItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
351				NSToolbarCustomizeToolbarItemIdentifier, nil] ];
352
353	return allowedItems;
354}
355
356
357@end
358