Plugins/Dual Window Interface/AIMessageWindowController.m
author Zachary West <zacw@adium.im>
Tue Jun 02 17:22:19 2009 -0400 (2009-06-02)
changeset 2450 08aa9c3a346c
parent 2130 1b9cd87159b9
child 2634 f8f70fff48f1
permissions -rw-r--r--
Create a new advanced preference called "Confirmations". Move Quit Confirmations there.

Adds a new confirmation type for closing message windows with more than 1 tab open. Fixes #12006.

This new preference has two options: always confirm, only confirm when there's unread content. Displays an alert sheet when trying to close the window.
David@0
     1
/* 
David@0
     2
 * Adium is the legal property of its developers, whose names are listed in the copyright file included
David@0
     3
 * with this source distribution.
David@0
     4
 * 
David@0
     5
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
David@0
     6
 * General Public License as published by the Free Software Foundation; either version 2 of the License,
David@0
     7
 * or (at your option) any later version.
David@0
     8
 * 
David@0
     9
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
David@0
    10
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
David@0
    11
 * Public License for more details.
David@0
    12
 * 
David@0
    13
 * You should have received a copy of the GNU General Public License along with this program; if not,
David@0
    14
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
David@0
    15
 */
David@0
    16
David@0
    17
#import "AIDualWindowInterfacePlugin.h"
David@0
    18
#import <Adium/AIInterfaceControllerProtocol.h>
David@0
    19
#import <Adium/AIMenuControllerProtocol.h>
David@0
    20
#import "AIMessageTabViewItem.h"
David@0
    21
#import "AIMessageViewController.h"
David@0
    22
#import "AIMessageWindowController.h"
David@0
    23
#import "AIDockController.h"
David@0
    24
#import <Adium/AIToolbarControllerProtocol.h>
David@0
    25
#import <Adium/AIAccountControllerProtocol.h>
David@0
    26
#import <AIUtilities/AIApplicationAdditions.h>
David@0
    27
#import <AIUtilities/AIAttributedStringAdditions.h>
David@0
    28
#import <AIUtilities/AIImageAdditions.h>
David@0
    29
#import <AIUtilities/AIStringAdditions.h>
David@0
    30
#import <AIUtilities/AIToolbarUtilities.h>
David@0
    31
#import <AIUtilities/AIArrayAdditions.h>
David@0
    32
#import <AIUtilities/AIWindowAdditions.h>
David@0
    33
#import <Adium/AIChat.h>
David@0
    34
#import <Adium/AIListContact.h>
David@0
    35
#import <Adium/AIListObject.h>
David@0
    36
#import <PSMTabBarControl/PSMTabBarControl.h>
David@0
    37
#import <PSMTabBarControl/PSMOverflowPopUpButton.h>
David@0
    38
#import <PSMTabBarControl/PSMAdiumTabStyle.h>
David@0
    39
#import <PSMTabBarControl/PSMTabStyle.h>
David@0
    40
#import "AIMessageTabSplitView.h"
David@547
    41
#import <Adium/AIStatusIcons.h>
David@0
    42
David@0
    43
#define KEY_MESSAGE_WINDOW_POSITION 			@"Message Window"
David@0
    44
David@0
    45
#define AIMessageTabDragBeganNotification		@"AIMessageTabDragBeganNotification"
David@0
    46
#define AIMessageTabDragEndedNotification    	@"AIMessageTabDragEndedNotification"
David@0
    47
#define	MESSAGE_WINDOW_NIB                      @"MessageWindow"			//Filename of the message window nib
David@0
    48
#define TAB_BAR_FPS                             20.0
David@0
    49
#define TAB_BAR_STEP                            0.6
David@0
    50
#define TOOLBAR_MESSAGE_WINDOW					@"AdiumMessageWindow"			//Toolbar identifier
David@0
    51
David@0
    52
#define HORIZONTAL_TAB_BAR_TO_VIEW_SPACING		7
David@0
    53
David@0
    54
#define KEY_VERTICAL_TABS_WIDTH					@"Vertical Tabs Width"
David@0
    55
#define VERTICAL_DIVIDER_THICKNESS				4
David@0
    56
#define VERTICAL_TAB_BAR_TO_VIEW_SPACING		3
David@0
    57
David@84
    58
@interface AIMessageWindowController ()
David@0
    59
- (id)initWithWindowNibName:(NSString *)windowNibName interface:(AIDualWindowInterfacePlugin *)inInterface containerID:(NSString *)inContainerID containerName:(NSString *)inName;
David@0
    60
- (void)_configureToolbar;
David@0
    61
- (void)_updateWindowTitleAndIcon;
David@0
    62
- (NSString *)_frameSaveKey;
David@0
    63
- (void)_reloadContainedChats;
David@0
    64
@end
David@0
    65
David@0
    66
//Used to squelch compiler warnings on this private call
David@0
    67
@interface NSWindow (AISecretWindowDocumentIconAdditions)
David@0
    68
- (void)addDocumentIconButton;
David@0
    69
@end
David@0
    70
David@0
    71
@implementation AIMessageWindowController
David@0
    72
David@0
    73
//Create a new message window controller
David@0
    74
+ (AIMessageWindowController *)messageWindowControllerForInterface:(AIDualWindowInterfacePlugin *)inInterface
David@0
    75
															withID:(NSString *)inContainerID
David@0
    76
															  name:(NSString *)inName
David@0
    77
{
David@0
    78
    return [[[self alloc] initWithWindowNibName:MESSAGE_WINDOW_NIB
David@0
    79
									  interface:inInterface
David@0
    80
									containerID:inContainerID
David@0
    81
										   containerName:inName] autorelease];
David@0
    82
}
David@0
    83
David@0
    84
//init
David@0
    85
- (id)initWithWindowNibName:(NSString *)windowNibName
David@0
    86
				  interface:(AIDualWindowInterfacePlugin *)inInterface
David@0
    87
				containerID:(NSString *)inContainerID
David@0
    88
					   containerName:(NSString *)inName
David@0
    89
{
David@0
    90
	if ((self = [super initWithWindowNibName:windowNibName])) {
David@0
    91
		NSWindow	*myWindow;
David@0
    92
	
David@0
    93
		interface = [inInterface retain];
David@0
    94
		containerName = [inName retain];
David@0
    95
		containerID = [inContainerID retain];
David@497
    96
		m_containedChats = [[NSMutableArray alloc] init];
David@0
    97
		hasShownDocumentButton = NO;
David@0
    98
		
David@0
    99
		//Load our window
David@0
   100
		myWindow = [self window];
David@0
   101
David@0
   102
		[[NSNotificationCenter defaultCenter] addObserver:self
David@0
   103
												 selector:@selector(tabDraggingNotificationReceived:)
David@0
   104
													 name:PSMTabDragDidBeginNotification
David@0
   105
												   object:nil];
David@0
   106
		
David@0
   107
		[[NSNotificationCenter defaultCenter] addObserver:self
David@0
   108
												 selector:@selector(tabDraggingNotificationReceived:)
David@0
   109
													 name:PSMTabDragDidEndNotification
David@0
   110
												   object:nil];
David@0
   111
		
David@0
   112
		[[NSNotificationCenter defaultCenter] addObserver:self
David@0
   113
												 selector:@selector(tabBarFrameChanged:)
David@0
   114
													 name:NSViewFrameDidChangeNotification
David@0
   115
												   object:tabView_tabBar];
David@0
   116
		
David@0
   117
		[[NSNotificationCenter defaultCenter] addObserver:self 
David@0
   118
												 selector:@selector(windowWillMiniaturize:)
David@0
   119
													 name:NSWindowWillMiniaturizeNotification
David@0
   120
												   object:myWindow];
David@0
   121
		//Prefs
David@95
   122
		[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_DUAL_WINDOW_INTERFACE];
David@0
   123
		
David@0
   124
		//Register as a tab drag observer so we know when tabs are dragged over our window and can show our tab bar
David@0
   125
		[myWindow registerForDraggedTypes:[NSArray arrayWithObject:@"PSMTabBarControlItemPBType"]];
David@0
   126
	}
David@0
   127
David@0
   128
    return self;
David@0
   129
}
David@0
   130
David@0
   131
//dealloc
David@0
   132
- (void)dealloc
David@0
   133
{
David@0
   134
	[[NSNotificationCenter defaultCenter] removeObserver:self];
David@0
   135
David@0
   136
	/* Ensure our window is quite clear we have no desire to ever hear from it again.  sendEvent: with a flags changed
David@0
   137
	 * event is being sent to this AIMessageWindowController instance by the window after dallocing, for some reason.
David@0
   138
	 * It seems likely a double-release is involved.  I can't reproduce this locally, either... but calling
David@0
   139
	 * [self setWindow:nil] appears to fix the problem where it was being experienced..
David@0
   140
	 *
David@0
   141
	 * Something is wrong elsewhere that this could be necessary, but this doesn't hurt I don't believe.
David@0
   142
	 */
David@0
   143
	[[self window] setDelegate:nil];
David@0
   144
	[self setWindow:nil];
David@0
   145
David@0
   146
    [tabView_tabBar setDelegate:nil];
David@0
   147
David@497
   148
	[self.containedChats release];
David@0
   149
	[toolbarItems release];
David@0
   150
	[containerName release];
David@0
   151
	[containerID release];
David@0
   152
David@95
   153
	[adium.preferenceController unregisterPreferenceObserver:self];
David@0
   154
David@0
   155
    [super dealloc];
David@0
   156
}
David@0
   157
David@0
   158
//Human readable container name
David@0
   159
- (NSString *)name
David@0
   160
{
David@0
   161
	return containerName;
David@0
   162
}
David@0
   163
David@0
   164
//Internal container ID
David@0
   165
- (NSString *)containerID
David@0
   166
{
David@0
   167
	return containerID;
David@0
   168
}
David@0
   169
David@0
   170
//PSMTabBarControl accessor
David@0
   171
- (PSMTabBarControl *)tabBar
David@0
   172
{
David@0
   173
	return tabView_tabBar;
David@0
   174
}
David@0
   175
David@0
   176
- (NSString *)adiumFrameAutosaveName
David@0
   177
{
David@0
   178
	return [self _frameSaveKey];
David@0
   179
}
David@0
   180
David@0
   181
//Setup our window before it is displayed
David@0
   182
- (void)windowDidLoad
David@0
   183
{
David@0
   184
	[super windowDidLoad];
David@0
   185
	
David@0
   186
	NSWindow	*theWindow = [self window];
David@0
   187
David@0
   188
    //Exclude this window from the window menu (since we add it manually)
David@0
   189
    [theWindow setExcludedFromWindowsMenu:YES];
David@0
   190
	[theWindow useOptimizedDrawing:YES];
David@0
   191
David@0
   192
	[self _configureToolbar];
David@0
   193
David@0
   194
    //Remove any tabs from our tab view, it needs to start out empty
David@0
   195
    while ([tabView_messages numberOfTabViewItems] > 0) {
David@0
   196
        [tabView_messages removeTabViewItem:[tabView_messages tabViewItemAtIndex:0]];
David@0
   197
    }
David@0
   198
	
David@0
   199
	//Setup the tab bar
David@0
   200
	tabView_tabStyle = [[[PSMAdiumTabStyle alloc] init] autorelease];
David@0
   201
	[tabView_tabBar setStyle:tabView_tabStyle];
David@0
   202
	[tabView_tabBar setCanCloseOnlyTab:YES];
David@0
   203
	[tabView_tabBar setUseOverflowMenu:NO];
David@0
   204
	[tabView_tabBar setAllowsResizing:NO];
David@0
   205
	[tabView_tabBar setSizeCellsToFit:YES];
David@0
   206
	[tabView_tabBar setHideForSingleTab:!alwaysShowTabs];
David@0
   207
	[tabView_tabBar setSelectsTabsOnMouseDown:YES];
David@0
   208
	[tabView_tabBar setAutomaticallyAnimates:NO];
David@0
   209
	[tabView_tabBar setAllowsScrubbing:YES];
Evan@181
   210
	[tabView_tabBar setAllowsBackgroundTabClosing:[[NSUserDefaults standardUserDefaults] boolForKey:@"AIAllowBackgroundTabClosing"]];
David@0
   211
	[tabView_tabBar setTearOffStyle:PSMTabBarTearOffAlphaWindow];
David@0
   212
}
David@0
   213
David@0
   214
//Frames
David@0
   215
- (NSString *)_frameSaveKey
David@0
   216
{
David@95
   217
	if ([[adium.preferenceController preferenceForKey:KEY_TABBED_CHATTING
David@0
   218
												  group:PREF_GROUP_INTERFACE] boolValue] &&
David@95
   219
		![[adium.preferenceController preferenceForKey:KEY_GROUP_CHATS_BY_GROUP
David@0
   220
												   group:PREF_GROUP_INTERFACE] boolValue]) {
David@0
   221
		return KEY_MESSAGE_WINDOW_POSITION;
David@0
   222
David@0
   223
	} else {
David@0
   224
		//Not using tabbed chatting, or we're tabbing by groups: Save the window position on a per-container basis
David@0
   225
		return [NSString stringWithFormat:@"%@ %@",KEY_MESSAGE_WINDOW_POSITION, containerID];	
David@0
   226
	}
David@0
   227
}
David@0
   228
- (BOOL)shouldCascadeWindows
David@0
   229
{
David@95
   230
	if ([[adium.preferenceController preferenceForKey:KEY_TABBED_CHATTING  group:PREF_GROUP_INTERFACE] boolValue])
David@0
   231
		return NO;
David@0
   232
	else //Not using tabbed chatting: Cascade if we have no frame
David@0
   233
		return ([self savedFrameString] == nil);
David@0
   234
}
David@0
   235
David@0
   236
//Close the message window
David@0
   237
- (IBAction)closeWindow:(id)sender
David@0
   238
{
David@0
   239
	windowIsClosing = YES;
zacw@2450
   240
	
zacw@2450
   241
	[[self window] performClose:nil];
zacw@2450
   242
}
David@0
   243
zacw@2450
   244
/*!
zacw@2450
   245
 * @brief Confirm if we should close the window.
zacw@2450
   246
 */
zacw@2450
   247
- (BOOL)windowShouldClose:(id)window
zacw@2450
   248
{
zacw@2450
   249
	if (!windowIsClosing
zacw@2450
   250
		&& self.containedChats.count > 1
zacw@2450
   251
		&& [[adium.preferenceController preferenceForKey:KEY_CONFIRM_MSG_CLOSE group:PREF_GROUP_CONFIRMATIONS] boolValue]) {
zacw@2450
   252
		NSString *suppressionText = nil;
zacw@2450
   253
		
zacw@2450
   254
		NSInteger unreadCount = 0;
zacw@2450
   255
		
zacw@2450
   256
		for (AIChat *chat in self.containedChats) {
zacw@2450
   257
			if (chat.unviewedContentCount) {
zacw@2450
   258
				unreadCount++;
zacw@2450
   259
			}
zacw@2450
   260
		}
zacw@2450
   261
		
zacw@2450
   262
		switch ([[adium.preferenceController preferenceForKey:KEY_CONFIRM_MSG_CLOSE_TYPE group:PREF_GROUP_CONFIRMATIONS] integerValue]) {
zacw@2450
   263
			case AIMessageCloseAlways:
zacw@2450
   264
				suppressionText = AILocalizedString(@"Do not warn when closing multiple chats", nil);
zacw@2450
   265
				break;
zacw@2450
   266
				
zacw@2450
   267
			case AIMessageCloseUnread:
zacw@2450
   268
				if (unreadCount) {
zacw@2450
   269
					suppressionText = AILocalizedString(@"Do not warn when closing unread chats", nil);
zacw@2450
   270
				}
zacw@2450
   271
				break;
zacw@2450
   272
		}
zacw@2450
   273
		
zacw@2450
   274
		NSString *question = nil;
zacw@2450
   275
		if (unreadCount) {
zacw@2450
   276
			if (unreadCount == 1) {
zacw@2450
   277
				question = [NSString stringWithFormat:AILocalizedString(@"%u chats are open in this window, 1 of which has unviewed messages. Do you want to close this window anyway?",nil),
zacw@2450
   278
							self.containedChats.count];
zacw@2450
   279
			} else {
zacw@2450
   280
				question = [NSString stringWithFormat:AILocalizedString(@"%u chats are open in this window, %u of which have unviewed messages. Do you want to close this window anyway?",nil),
zacw@2450
   281
							self.containedChats.count,
zacw@2450
   282
							unreadCount];	
zacw@2450
   283
			}
zacw@2450
   284
		} else {
zacw@2450
   285
			question = [NSString stringWithFormat:AILocalizedString(@"%u chats are open in this window. Do you want to close this window anyway?",nil),
zacw@2450
   286
						self.containedChats.count];
zacw@2450
   287
		}
zacw@2450
   288
		
zacw@2450
   289
		if (suppressionText) {
zacw@2450
   290
			NSAlert *alert = [NSAlert alertWithMessageText:AILocalizedString(@"Are you sure you want to close this window?", nil)
zacw@2450
   291
											 defaultButton:AILocalizedString(@"Close", nil)
zacw@2450
   292
										   alternateButton:AILocalizedStringFromTable(@"Cancel", @"Buttons", nil)
zacw@2450
   293
											   otherButton:nil
zacw@2450
   294
								 informativeTextWithFormat:question];
zacw@2450
   295
			
zacw@2450
   296
			[alert setShowsSuppressionButton:YES];
zacw@2450
   297
			[[alert suppressionButton] setTitle:suppressionText];
zacw@2450
   298
			
zacw@2450
   299
			[alert beginSheetModalForWindow:self.window 
zacw@2450
   300
							  modalDelegate:self 
zacw@2450
   301
							 didEndSelector:@selector(closeAlertDidEnd:returnCode:contextInfo:) 
zacw@2450
   302
								contextInfo:nil];
zacw@2450
   303
			
zacw@2450
   304
			return NO;
zacw@2450
   305
		}
zacw@2450
   306
	}
zacw@2450
   307
	
zacw@2450
   308
	return YES;
zacw@2450
   309
}
zacw@2450
   310
zacw@2450
   311
- (void)closeAlertDidEnd:(NSAlert *)alert returnCode:(int)result contextInfo:(void *)contextInfo;
zacw@2450
   312
{
zacw@2450
   313
	
zacw@2450
   314
	if ([alert suppressionButton].state == NSOnState) {
zacw@2450
   315
		[adium.preferenceController setPreference:nil
zacw@2450
   316
										   forKey:KEY_CONFIRM_MSG_CLOSE
zacw@2450
   317
											group:PREF_GROUP_CONFIRMATIONS];
zacw@2450
   318
	}
zacw@2450
   319
zacw@2450
   320
	if (result == NSAlertDefaultReturn) {
zacw@2450
   321
		// Dismiss the alert sheet.
zacw@2450
   322
		[self.window orderOut:nil];
zacw@2450
   323
		// Don't prompt again.
zacw@2450
   324
		windowIsClosing = YES;
zacw@2450
   325
		// Close the window.
zacw@2450
   326
		[self closeWindow:nil];
zacw@2450
   327
	}
David@0
   328
}
David@0
   329
David@0
   330
/*!
David@0
   331
 * @brief Called as the window closes
David@0
   332
 */
David@0
   333
- (void)windowWillClose:(id)sender
David@0
   334
{
David@0
   335
    NSEnumerator			*enumerator;
David@0
   336
    AIMessageTabViewItem	*tabViewItem;
David@0
   337
	
David@0
   338
	if ([tabView_tabBar orientation] == PSMTabBarVerticalOrientation) {
David@3
   339
		CGFloat widthToStore;
David@0
   340
		if ([tabView_tabBar isTabBarHidden]) {
David@0
   341
			widthToStore = lastTabBarWidth;
David@0
   342
		} else {
David@0
   343
			widthToStore = NSWidth([tabView_tabBar frame]);
David@0
   344
		}
David@0
   345
David@95
   346
		[adium.preferenceController setPreference:[NSNumber numberWithDouble:widthToStore]
David@0
   347
											 forKey:KEY_VERTICAL_TABS_WIDTH
David@0
   348
											  group:PREF_GROUP_DUAL_WINDOW_INTERFACE];
David@0
   349
	}
David@0
   350
	
David@0
   351
	windowIsClosing = YES;
David@0
   352
	[super windowWillClose:sender];
David@0
   353
David@95
   354
	[adium.preferenceController unregisterPreferenceObserver:self];
David@0
   355
David@0
   356
    //Close all our tabs (The array will change as we remove tabs, so we must work with a copy)
David@0
   357
	enumerator = [[tabView_messages tabViewItems] reverseObjectEnumerator];
David@0
   358
    while ((tabViewItem = [enumerator nextObject])) {
Evan@166
   359
		[adium.interfaceController closeChat:tabViewItem.chat];
David@0
   360
	}
David@0
   361
David@0
   362
	//Chats have all closed, set active to nil, let the interface know we closed.  We should skip this step if our
David@0
   363
	//window is no longer visible, since in that case another window will have already became active.
David@0
   364
	if ([[self window] isVisible] && [[self window] isKeyWindow]) {
David@100
   365
		[adium.interfaceController chatDidBecomeActive:nil];
David@0
   366
	}
David@0
   367
	[interface containerDidClose:self];
David@0
   368
David@0
   369
    return;
David@0
   370
}
David@0
   371
David@0
   372
- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
David@0
   373
							object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
David@0
   374
{
David@0
   375
    if ([group isEqualToString:PREF_GROUP_DUAL_WINDOW_INTERFACE]) {
David@0
   376
		NSWindow	*window = [self window];
David@0
   377
		alwaysShowTabs = ![[prefDict objectForKey:KEY_AUTOHIDE_TABBAR] boolValue];
David@0
   378
		[tabView_tabBar setHideForSingleTab:!alwaysShowTabs];
David@0
   379
		NSNumber *useOverflow = [prefDict objectForKey:KEY_TABBAR_OVERFLOW];
David@0
   380
		[tabView_tabBar setUseOverflowMenu:(useOverflow ? [useOverflow boolValue] : YES)];
David@0
   381
		
David@0
   382
		[[tabView_tabBar overflowPopUpButton] setAlternateImage:[AIStatusIcons statusIconForStatusName:@"content" statusType:AIAvailableStatusType iconType:AIStatusIconTab direction:AIIconNormal]];
David@0
   383
		//NSImage *overflowImage = [[[NSImage alloc] initByReferencingFile:[[NSBundle mainBundle] pathForImageResource:@"overflow_overlay"]] autorelease];
David@0
   384
		//[[tabView_tabBar overflowPopUpButton] setAlternateImage:overflowImage];
David@0
   385
		
David@0
   386
		//change the frame of the tab bar according to the orientation
David@0
   387
		if (firstTime || [key isEqualToString:KEY_TABBAR_POSITION]) {
David@0
   388
			PSMTabBarOrientation orientation;
David@0
   389
			
David@3
   390
			tabPosition = [[prefDict objectForKey:KEY_TABBAR_POSITION] integerValue];
David@0
   391
			orientation = ((tabPosition == AdiumTabPositionBottom || tabPosition == AdiumTabPositionTop) ?
David@0
   392
						   PSMTabBarHorizontalOrientation :
David@0
   393
						   PSMTabBarVerticalOrientation);
David@0
   394
David@0
   395
			NSRect tabBarFrame = [tabView_tabBar frame], tabViewMessagesFrame = [tabView_messages frame];
David@0
   396
			NSRect contentRect = [[[self window] contentView] frame];
David@0
   397
			
David@0
   398
			//remove the split view if the last orientation was vertical
David@0
   399
			if ([tabView_tabBar orientation] == PSMTabBarVerticalOrientation) {
David@0
   400
				[tabView_messages retain];
David@0
   401
				[tabView_messages removeFromSuperview];
David@0
   402
				[tabView_tabBar retain];
David@0
   403
				[tabView_tabBar removeFromSuperview];
David@0
   404
				[tabView_splitView removeFromSuperview];
David@0
   405
				
David@0
   406
				[[[self window] contentView] addSubview:tabView_messages];
David@0
   407
				[[[self window] contentView] addSubview:tabView_tabBar];
David@0
   408
				[tabView_messages release];
David@0
   409
				[tabView_tabBar release];
David@0
   410
			}
David@0
   411
			
David@0
   412
			[tabView_tabBar setOrientation:orientation];
David@0
   413
			
David@0
   414
			switch (orientation) {
David@0
   415
				case PSMTabBarHorizontalOrientation:
David@0
   416
				{
zacw@1979
   417
					tabBarFrame.size.height = [tabView_tabBar isTabBarHidden] ? 1 : kPSMTabBarControlHeight;
David@0
   418
					tabBarFrame.size.width = contentRect.size.width + 1;
David@0
   419
					
David@0
   420
					//set the position of the tab bar (top/bottom)
David@0
   421
					if (tabPosition == AdiumTabPositionBottom) {
David@0
   422
						tabBarFrame.origin.y = NSMinY(contentRect);
David@0
   423
						tabViewMessagesFrame.origin.y = NSHeight(tabBarFrame) + ([tabView_tabBar isTabBarHidden] ? 0 : (HORIZONTAL_TAB_BAR_TO_VIEW_SPACING - 1));
David@0
   424
						tabViewMessagesFrame.size.height = (NSHeight(contentRect) - NSHeight(tabBarFrame) -
David@0
   425
															([tabView_tabBar isTabBarHidden] ? 0 : (HORIZONTAL_TAB_BAR_TO_VIEW_SPACING - 2)));
David@0
   426
						[tabView_tabBar setAutoresizingMask:(NSViewMaxYMargin | NSViewWidthSizable)];
David@0
   427
						
David@0
   428
					} else {
zacw@1980
   429
						// This arbitrary sizedown is so that top tabs look visually connected to their content below.
zacw@1980
   430
						tabBarFrame.size.height -= 3;
zacw@1980
   431
						
David@0
   432
						tabBarFrame.origin.y = NSMaxY(contentRect) - NSHeight(tabBarFrame);
David@0
   433
						tabViewMessagesFrame.origin.y = NSMinY(contentRect);
zacw@1979
   434
						tabViewMessagesFrame.size.height = NSHeight(contentRect) - NSHeight(tabBarFrame);
David@0
   435
						[tabView_tabBar setAutoresizingMask:(NSViewMinYMargin | NSViewWidthSizable)];
David@0
   436
					}
David@0
   437
					/* If the cell is less than 60, icon + title + unread message count may overlap */
David@0
   438
					[tabView_tabBar setCellMinWidth:60];
David@0
   439
					[tabView_tabBar setCellMaxWidth:250];
David@0
   440
					
David@0
   441
					tabBarFrame.origin.x = 0;
David@0
   442
					
David@0
   443
					//Items within the tabview draw frames, so be sure to clip the left and right edges.
David@0
   444
					tabViewMessagesFrame.origin.x = -1;
David@0
   445
					tabViewMessagesFrame.size.width = NSWidth(contentRect) + 2;
David@0
   446
					break;
David@0
   447
				}
David@0
   448
				case PSMTabBarVerticalOrientation:
David@0
   449
				{
David@3
   450
					CGFloat width = ([prefDict objectForKey:KEY_VERTICAL_TABS_WIDTH] ?
David@3
   451
								   [[prefDict objectForKey:KEY_VERTICAL_TABS_WIDTH] doubleValue] :
David@0
   452
								   100);
David@0
   453
					lastTabBarWidth = width;
David@0
   454
					
David@0
   455
					tabBarFrame.size.height = [[[self window] contentView] frame].size.height;
David@0
   456
					tabBarFrame.size.width = [tabView_tabBar isTabBarHidden] ? 1 : width;
David@0
   457
					tabBarFrame.origin.y = NSMinY(contentRect);
David@0
   458
					tabViewMessagesFrame.origin.y = NSMinY(contentRect);
David@0
   459
					tabViewMessagesFrame.size.height = NSHeight(contentRect) + 1;
David@0
   460
					tabViewMessagesFrame.size.width = NSWidth(contentRect) - NSWidth(tabBarFrame) - 5;
David@0
   461
					
David@0
   462
					//set the position of the tab bar (left/right)
David@0
   463
					if (tabPosition == AdiumTabPositionLeft) {
David@0
   464
						tabBarFrame.origin.x = NSMinX(contentRect);
David@0
   465
						tabViewMessagesFrame.origin.x = NSMaxX(tabBarFrame);
David@0
   466
						[tabView_tabBar setAutoresizingMask:NSViewHeightSizable];
David@0
   467
					} else {
David@0
   468
						tabViewMessagesFrame.origin.x = NSMinX(contentRect);
David@0
   469
						tabBarFrame.origin.x = NSWidth(contentRect) - NSWidth(tabBarFrame) + 1;
David@0
   470
						[tabView_tabBar setAutoresizingMask:NSViewHeightSizable | NSViewMinXMargin];
David@0
   471
					}
David@0
   472
					[tabView_tabBar setCellMinWidth:50];
David@0
   473
					[tabView_tabBar setCellMaxWidth:200];
David@0
   474
					
David@0
   475
					//put the subviews into a split view
David@0
   476
					NSRect splitViewRect = [[[self window] contentView] frame];
David@0
   477
					splitViewRect.size.width++;
David@0
   478
					splitViewRect.size.height++;
David@0
   479
					tabView_splitView = [[[AIMessageTabSplitView alloc] initWithFrame:splitViewRect] autorelease];
David@0
   480
					[tabView_splitView setDividerThickness:([tabView_tabBar isTabBarHidden] ? 0 : VERTICAL_DIVIDER_THICKNESS)];
David@0
   481
					[tabView_splitView setVertical:YES];
David@0
   482
					[tabView_splitView setDelegate:self];
David@0
   483
					if (tabPosition == AdiumTabPositionLeft) {
David@0
   484
						[tabView_splitView addSubview:tabView_tabBar];
David@0
   485
						[tabView_splitView addSubview:tabView_messages];
David@0
   486
						[tabView_splitView setLeftColor:[NSColor colorWithCalibratedWhite:0.92 alpha:1.0]
David@0
   487
											 rightColor:[NSColor colorWithCalibratedWhite:0.91 alpha:1.0]];
David@0
   488
					} else {
David@0
   489
						[tabView_splitView addSubview:tabView_messages];
David@0
   490
						[tabView_splitView addSubview:tabView_tabBar];
David@0
   491
						[tabView_splitView setLeftColor:[NSColor colorWithCalibratedWhite:0.91 alpha:1.0]
David@0
   492
											 rightColor:[NSColor colorWithCalibratedWhite:0.92 alpha:1.0]];
David@0
   493
					}
David@0
   494
					[tabView_splitView adjustSubviews];
David@0
   495
					[tabView_splitView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
David@0
   496
					[[[self window] contentView] addSubview:tabView_splitView];
David@0
   497
					break;
David@0
   498
				}
David@0
   499
			}
David@0
   500
			
David@0
   501
			[tabView_messages setFrame:tabViewMessagesFrame];
David@0
   502
			[tabView_tabBar setFrame:tabBarFrame];
David@0
   503
			
David@0
   504
			//update the tab bar and tab view frame
David@0
   505
			[[self window] display];
David@0
   506
		}
David@0
   507
		
David@0
   508
		//set tab style drawing attributes
David@0
   509
		[tabView_tabStyle setDrawsRight:(tabPosition == AdiumTabPositionRight)];
David@0
   510
		[tabView_tabStyle setDrawsUnified:(tabPosition == AdiumTabPositionTop)];
David@0
   511
		//[[[self window] toolbar] setShowsBaselineSeparator:(tabPosition != AdiumTabPositionTop)];
David@0
   512
		
David@0
   513
		[self _updateWindowTitleAndIcon];
David@0
   514
David@3
   515
		AIWindowLevel	windowLevel = [[prefDict objectForKey:KEY_WINDOW_LEVEL] integerValue];
David@3
   516
		NSInteger				level = NSNormalWindowLevel;
David@0
   517
		
David@0
   518
		switch (windowLevel) {
David@0
   519
			case AINormalWindowLevel: level = NSNormalWindowLevel; break;
David@0
   520
			case AIFloatingWindowLevel: level = NSFloatingWindowLevel; break;
David@0
   521
			case AIDesktopWindowLevel: level = kCGDesktopWindowLevel; break;
David@0
   522
		}
David@0
   523
		[window setLevel:level];
David@0
   524
		[window setHidesOnDeactivate:[[prefDict objectForKey:KEY_WINDOW_HIDE] boolValue]];
David@0
   525
    }
David@0
   526
}
David@0
   527
David@0
   528
- (void)updateOverflowMenuUnviewedContentIcon
David@0
   529
{
David@0
   530
	BOOL someUnviewedContent = NO;
David@0
   531
	
David@3
   532
	NSInteger count = [[tabView_tabBar representedTabViewItems] count];
David@3
   533
	for (NSInteger i = [tabView_tabBar numberOfVisibleTabs]; i < count; i++) {
David@0
   534
		if ([[[[tabView_tabBar representedTabViewItems] objectAtIndex:i] chat] unviewedContentCount] > 0) {
David@0
   535
			someUnviewedContent = YES;
David@0
   536
			break;
David@0
   537
		}
David@0
   538
	}
David@0
   539
	
David@0
   540
	[[tabView_tabBar overflowPopUpButton] setAnimatingAlternateImage:someUnviewedContent];	
David@0
   541
}
David@0
   542
David@0
   543
David@0
   544
- (void)updateIconForTabViewItem:(AIMessageTabViewItem *)tabViewItem
David@0
   545
{
David@0
   546
	if (tabViewItem == [tabView_messages selectedTabViewItem]) {
David@0
   547
		[self _updateWindowTitleAndIcon];
David@0
   548
	}
David@0
   549
	
David@0
   550
	if ([[tabView_tabBar representedTabViewItems] indexOfObject:tabViewItem] >= [tabView_tabBar numberOfVisibleTabs]) {
David@0
   551
		//The chat is in the overflow menu. If any chat has unviewed content, it should be animating to demonstrate that.
David@0
   552
		[self updateOverflowMenuUnviewedContentIcon];
David@0
   553
	}
David@0
   554
}
David@0
   555
David@0
   556
- (AdiumTabPosition)tabPosition
David@0
   557
{
David@0
   558
	return tabPosition;
David@0
   559
}
David@0
   560
David@0
   561
//Contained Chats ------------------------------------------------------------------------------------------------------
David@0
   562
#pragma mark Contained Chats
David@0
   563
//Add a tab view item container at the end of the tabs (without changing the current selection)
David@0
   564
- (void)addTabViewItem:(AIMessageTabViewItem *)inTabViewItem
David@0
   565
{    
David@0
   566
    [self addTabViewItem:inTabViewItem atIndex:-1 silent:NO];
David@0
   567
}
David@0
   568
David@0
   569
//Add a tab view item container (without changing the current selection)
David@0
   570
//If silent is NO, the interface controller will be informed of the add
David@3
   571
- (void)addTabViewItem:(AIMessageTabViewItem *)inTabViewItem atIndex:(NSInteger)index silent:(BOOL)silent
David@0
   572
{
David@0
   573
	/* XXX This mirrors the hack in -[AIMessageTabViewItem initWithMessageView]. It may have been undone
David@0
   574
	 * in removeTabViewItem:silent: below if the tab was moving between windows.
David@0
   575
	 */
David@0
   576
	[inTabViewItem setIdentifier:inTabViewItem];
David@0
   577
David@0
   578
	if (index == -1) {
David@0
   579
		[tabView_messages addTabViewItem:inTabViewItem];
David@0
   580
	} else {
David@0
   581
		[tabView_messages insertTabViewItem:inTabViewItem atIndex:index];
David@0
   582
	}
David@0
   583
David@0
   584
	//Refresh our list and order of chats
David@0
   585
	[self _reloadContainedChats];
David@0
   586
	
David@0
   587
	if (![tabView_messages selectedTabViewItem]) [tabView_messages selectNextTabViewItem:nil];
David@0
   588
	
David@813
   589
	if (!silent) [adium.interfaceController chatDidOpen:inTabViewItem.chat];
David@0
   590
}
David@0
   591
David@0
   592
//Remove a tab view item container
David@0
   593
//If silent is NO, the interface controller will be informed of the remove
David@0
   594
- (void)removeTabViewItem:(AIMessageTabViewItem *)inTabViewItem silent:(BOOL)silent
David@0
   595
{
David@0
   596
	/* When a tab isn't selected, its views are not within any window. We want the tab to be able to remove tracking rects
David@0
   597
	 * from the window before closing, so if it isn't selected we need to select it briefly to let this happen. Since this is
David@0
   598
	 * all within the same run loop, as long as code in the tab view's delegate is well-behaved and uses setNeedsDisplay: rather
David@0
   599
	 * than display if it does drawing, the UI shouldn't change at all.
David@0
   600
	 */
David@0
   601
	if ([tabView_messages selectedTabViewItem] != inTabViewItem) {
David@0
   602
		NSTabViewItem	*oldTabViewItem = [tabView_messages selectedTabViewItem];
David@0
   603
		[tabView_messages selectTabViewItem:inTabViewItem];
David@0
   604
		
David@0
   605
		//The tab view item needs to know that this window controller no longer contains it
David@0
   606
		[inTabViewItem setWindowController:nil];	
David@0
   607
David@0
   608
		[tabView_messages selectTabViewItem:oldTabViewItem];
David@0
   609
David@0
   610
	} else {
David@0
   611
		//The tab view item needs to know that this window controller no longer contains it
David@0
   612
		[inTabViewItem setWindowController:nil];
David@0
   613
	}
David@0
   614
	
David@0
   615
David@0
   616
    //If the tab is selected, select the next tab before closing it (To mirror the behavior of safari)
David@0
   617
    if (!windowIsClosing && inTabViewItem == [tabView_messages selectedTabViewItem]) {
David@0
   618
		[tabView_messages selectNextTabViewItem:nil];
David@0
   619
    }
David@0
   620
	
David@0
   621
    //Remove the tab and let the interface know a container closed
David@813
   622
	[m_containedChats removeObject:inTabViewItem.chat];
David@813
   623
	if (!silent) [adium.interfaceController chatDidClose:inTabViewItem.chat];
David@0
   624
David@0
   625
	//Now remove the tab view item from our NSTabView
David@0
   626
    [tabView_messages removeTabViewItem:inTabViewItem];
David@0
   627
David@0
   628
	/* AIMessageTabViewItem sets itself as its own identifer.  We have to break the recursive retain from the outside.
David@0
   629
	 * This must be done last so that the NSTabView and its delegate (PSMTabBarControl) can still make use of the idenfitier.
David@0
   630
	 */
David@0
   631
	[inTabViewItem setIdentifier:nil];
David@0
   632
David@0
   633
	//close if we're empty
David@497
   634
	if (!windowIsClosing && [self.containedChats count] == 0) {
David@0
   635
		[self closeWindow:nil];
David@0
   636
	}
David@0
   637
}
David@0
   638
David@3
   639
- (void)moveTabViewItem:(AIMessageTabViewItem *)inTabViewItem toIndex:(NSInteger)index
David@0
   640
{
David@813
   641
	AIChat	*chat = inTabViewItem.chat;
David@0
   642
David@497
   643
	if ([self.containedChats indexOfObject:chat] != index) {
David@0
   644
		NSMutableArray *cells = [tabView_tabBar cells];
David@0
   645
		
David@0
   646
		[cells moveObject:[cells objectAtIndex:[[tabView_tabBar representedTabViewItems] indexOfObject:inTabViewItem]] toIndex:index];
David@0
   647
		[tabView_tabBar setNeedsDisplay:YES];
David@497
   648
		[m_containedChats moveObject:chat toIndex:index];
David@0
   649
		
David@100
   650
		[adium.interfaceController chatOrderDidChange];
David@0
   651
	}
David@0
   652
}
David@0
   653
David@0
   654
//Returns YES if we are empty (currently contain no chats)
David@0
   655
- (BOOL)containerIsEmpty
David@0
   656
{
David@497
   657
	return [self.containedChats count] == 0;
David@0
   658
}
David@0
   659
David@0
   660
//Returns an array of the chats we contain
David@497
   661
@synthesize containedChats = m_containedChats;
David@0
   662
David@0
   663
- (void)_reloadContainedChats
David@0
   664
{
David@0
   665
	NSEnumerator			*enumerator;
David@0
   666
	AIMessageTabViewItem	*tabViewItem;
David@0
   667
David@0
   668
	//Update our contained chats array to mirror the order of the tabs
David@497
   669
	[m_containedChats release]; m_containedChats = [[NSMutableArray alloc] init];
David@0
   670
	enumerator = [[tabView_messages tabViewItems] objectEnumerator];
David@0
   671
	
David@0
   672
	while ((tabViewItem = [enumerator nextObject])) {
David@0
   673
		[tabViewItem setWindowController:self];
David@497
   674
		[m_containedChats addObject:[tabViewItem chat]];
David@0
   675
	}
David@0
   676
}
David@0
   677
David@0
   678
//Active Chat Tracking -------------------------------------------------------------------------------------------------
David@0
   679
#pragma mark Active Chat Tracking
David@0
   680
//Our selected tab is now the active chat
David@0
   681
- (void)windowDidBecomeKey:(NSNotification *)notification
David@0
   682
{
David@100
   683
	[adium.interfaceController chatDidBecomeActive:[(AIMessageTabViewItem *)[tabView_messages selectedTabViewItem] chat]];
David@0
   684
}
David@0
   685
David@0
   686
//Our selected tab is no longer the active chat
David@0
   687
- (void)windowDidResignKey:(NSNotification *)notification
David@0
   688
{
zacw@1537
   689
	[((AIMessageTabViewItem *)tabView_messages.selectedTabViewItem).messageViewController.messageDisplayController markForFocusChange];
David@100
   690
	[adium.interfaceController chatDidBecomeActive:nil];
David@0
   691
}
David@0
   692
David@0
   693
//Update our window title
David@0
   694
- (void)_updateWindowTitleAndIcon
David@0
   695
{
David@0
   696
	NSString	*label = [(AIMessageTabViewItem *)[tabView_messages selectedTabViewItem] label];
David@0
   697
	NSString	*title;
David@0
   698
	NSButton	*button;
David@0
   699
	NSWindow	*window = [self window];
David@0
   700
	
David@0
   701
	//Window Title
David@0
   702
    if (([tabView_messages numberOfTabViewItems] == 1) || !containerName) {
David@0
   703
        title = (label ? [NSString stringWithFormat:@"%@", label] : nil);
David@0
   704
    } else {
David@0
   705
		if (containerName && label) {
David@0
   706
			title = [NSString stringWithFormat:@"%@ - %@", containerName, label];
David@0
   707
		} else {
David@0
   708
			if (containerName)
David@0
   709
				title = containerName;
David@0
   710
			else if (label)
David@0
   711
				title = label;
David@0
   712
			else
David@0
   713
				title = nil;
David@0
   714
		}
David@0
   715
    }
David@0
   716
	
David@0
   717
	if (title) [window setTitle:title];
David@0
   718
	
David@0
   719
	//Window Icon (We display state in the window title if tabs are not visible)
David@0
   720
	if (!hasShownDocumentButton) {
David@0
   721
		if ([window respondsToSelector:@selector(addDocumentIconButton)]) {
David@0
   722
			[window addDocumentIconButton];
David@0
   723
		}
David@0
   724
		hasShownDocumentButton = YES;
David@0
   725
	}
David@0
   726
David@0
   727
	button = [window standardWindowButton:NSWindowDocumentIconButton];
David@0
   728
		  
David@0
   729
	if ([tabView_tabBar isTabBarHidden]) {
David@0
   730
		NSImage *image = [(AIMessageTabViewItem *)[tabView_messages selectedTabViewItem] stateIcon];
David@0
   731
		if (image != [button image]) {
David@0
   732
			[button setImage:image];
David@0
   733
		}
David@0
   734
David@0
   735
	} else {
David@0
   736
		if ([button image]) {
David@0
   737
			[button setImage:nil];
David@0
   738
		}
David@0
   739
	}
David@0
   740
}
David@0
   741
David@0
   742
- (AIChat *)activeChat
David@0
   743
{
David@0
   744
	AIMessageTabViewItem *selectedTabViewItem = (AIMessageTabViewItem *)[tabView_messages selectedTabViewItem];
David@0
   745
	
David@0
   746
	if (![selectedTabViewItem isKindOfClass:[AIMessageTabViewItem class]]) {
David@0
   747
		return nil;
David@0
   748
	}
David@0
   749
	
David@0
   750
	return [selectedTabViewItem chat];
David@0
   751
}
David@0
   752
David@0
   753
//AISplitView Delegate -------------------------------------------------------------------------------------------------
David@0
   754
#pragma mark AISplitView Delegate
David@0
   755
David@0
   756
#define MINIMUM_WIDTH_FOR_VERTICAL_TABS 50
David@0
   757
#define MAXIMUM_WIDTH_FOR_VERTICAL_TABS 250
David@0
   758
David@0
   759
//handles the minimum size of vertical tabs
David@3
   760
- (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)offset
David@0
   761
{
David@3
   762
	CGFloat min = proposedMin;
David@0
   763
David@0
   764
	if (sender == tabView_splitView) {
David@0
   765
		switch (tabPosition) {
David@0
   766
			case AdiumTabPositionBottom:
David@0
   767
			case AdiumTabPositionTop:
David@0
   768
				/* should never be passed these */
David@0
   769
				break;
David@0
   770
			case AdiumTabPositionLeft:
David@0
   771
				min = ([tabView_tabBar isTabBarHidden] ? 0 : MINIMUM_WIDTH_FOR_VERTICAL_TABS);
David@0
   772
				break;
David@0
   773
			case AdiumTabPositionRight:
David@0
   774
				min = ([tabView_tabBar isTabBarHidden] ?
David@0
   775
					   NSWidth([tabView_splitView frame]) :
David@0
   776
					   (NSWidth([tabView_splitView frame]) - MAXIMUM_WIDTH_FOR_VERTICAL_TABS - [sender dividerThickness]));
David@0
   777
				break;				
David@0
   778
		}
David@0
   779
	} else {
David@0
   780
		NSLog(@"Unknown split view");
David@0
   781
	}
David@0
   782
	
David@0
   783
	return min;
David@0
   784
}
David@0
   785
David@0
   786
//handles the maximum size of vertical tabs
David@3
   787
- (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset
David@0
   788
{
David@3
   789
	CGFloat max = proposedMax;
David@0
   790
	
David@0
   791
	if (sender == tabView_splitView) {
David@0
   792
		switch (tabPosition) {
David@0
   793
			case AdiumTabPositionBottom:
David@0
   794
			case AdiumTabPositionTop:
David@0
   795
				/* should never be passed these */
David@0
   796
				break;
David@0
   797
			case AdiumTabPositionLeft:
David@0
   798
				max = MAXIMUM_WIDTH_FOR_VERTICAL_TABS;
David@0
   799
				break;
David@0
   800
			case AdiumTabPositionRight:
David@0
   801
				max = proposedMax - MINIMUM_WIDTH_FOR_VERTICAL_TABS;
David@0
   802
				break;
David@0
   803
		}
David@0
   804
	} else {
David@0
   805
		NSLog(@"Unknown split view");
David@0
   806
	}
David@0
   807
	
David@0
   808
	return max;
David@0
   809
}
David@0
   810
David@0
   811
- (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize
David@0
   812
{
David@0
   813
	NSRect messageFrame = [tabView_messages frame], tabBarFrame = [tabView_tabBar frame];
David@0
   814
	messageFrame.size = NSMakeSize([sender frame].size.width - tabBarFrame.size.width - [sender dividerThickness], [sender frame].size.height);
David@0
   815
	tabBarFrame.size = NSMakeSize(tabBarFrame.size.width, [sender frame].size.height);
David@0
   816
	
David@0
   817
	if (sender == tabView_splitView) {
David@0
   818
		switch (tabPosition) {
David@0
   819
			case AdiumTabPositionBottom:
David@0
   820
			case AdiumTabPositionTop:
David@0
   821
				/* should never be passed these */
David@0
   822
				break;
David@0
   823
			case AdiumTabPositionLeft:
David@0
   824
				messageFrame.origin.x = NSWidth(tabBarFrame) + [sender dividerThickness];
David@0
   825
				break;
David@0
   826
			case AdiumTabPositionRight:
David@0
   827
				messageFrame.origin.x = 0;
David@0
   828
				tabBarFrame.origin.x = NSWidth(messageFrame) + [sender dividerThickness];
David@0
   829
				break;
David@0
   830
		}
David@0
   831
	}
David@0
   832
	
David@0
   833
	[tabView_messages setFrame:messageFrame];
David@0
   834
	[tabView_tabBar setFrame:tabBarFrame];
David@0
   835
}
David@0
   836
David@0
   837
David@0
   838
//PSMTabBarControl Delegate -------------------------------------------------------------------------------------------------
David@0
   839
#pragma mark PSMTabBarControl Delegate
David@0
   840
David@0
   841
//Handle closing a tab
David@0
   842
- (BOOL)tabView:(NSTabView *)tabView shouldCloseTabViewItem:(NSTabViewItem *)tabViewItem
David@0
   843
{
David@0
   844
	//The window controller handles removing the tab as we need to dispose of tracking rects properly
David@0
   845
	if ([tabViewItem respondsToSelector:@selector(chat)]) {
David@0
   846
		AIChat	*chat = [(AIMessageTabViewItem *)tabViewItem chat];
David@0
   847
		
David@100
   848
		[adium.interfaceController closeChat:chat];
David@0
   849
	}
David@0
   850
	
David@0
   851
	return NO;
David@0
   852
}
David@0
   853
Evan@1430
   854
- (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem
Evan@1430
   855
{
Evan@1430
   856
	AIMessageTabViewItem *selectedTabViewItem = (AIMessageTabViewItem *)[tabView_messages selectedTabViewItem];
Evan@1430
   857
	
Evan@1430
   858
	if ([selectedTabViewItem isKindOfClass:[AIMessageTabViewItem class]]) {
Evan@1430
   859
        [selectedTabViewItem tabViewItemWillDeselect];
Evan@1430
   860
	}
Evan@1430
   861
}
Evan@1430
   862
David@0
   863
//Our selected tab has changed, update the active chat
David@0
   864
- (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
David@0
   865
{
David@0
   866
	if (tabViewItem != nil) {
David@0
   867
		AIChat	*chat = [(AIMessageTabViewItem *)tabViewItem chat];
David@0
   868
        [(AIMessageTabViewItem *)tabViewItem tabViewItemWasSelected]; //Let the tab know it was selected
David@0
   869
		
David@0
   870
        if ([[self window] isMainWindow]) { //If our window is main, set the newly selected container as active
David@100
   871
			[adium.interfaceController chatDidBecomeActive:chat];
David@0
   872
        }
David@0
   873
		
David@0
   874
        [self _updateWindowTitleAndIcon]; //Reflect change in window title
David@100
   875
		[adium.interfaceController chatDidBecomeVisible:chat inWindow:[self window]];
David@0
   876
    }
David@0
   877
}
David@0
   878
David@0
   879
- (BOOL)tabView:(NSTabView*)tabView shouldDragTabViewItem:(NSTabViewItem *)tabViewItem fromTabBar:(PSMTabBarControl *)tabBarControl
David@0
   880
{
David@0
   881
	return YES;
David@0
   882
}
David@0
   883
David@0
   884
- (BOOL)tabView:(NSTabView*)tabView shouldDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl
David@0
   885
{
David@0
   886
	return YES;
David@0
   887
}
David@0
   888
David@0
   889
- (void)tabView:(NSTabView *)tabView closeWindowForLastTabViewItem:(NSTabViewItem *)tabViewItem
David@0
   890
{
David@0
   891
	[self closeWindow:self];
David@0
   892
}
David@0
   893
David@0
   894
//Contextual menu for tabs
David@0
   895
- (NSMenu *)tabView:(NSTabView *)tabView menuForTabViewItem:(NSTabViewItem *)tabViewItem
David@0
   896
{
David@0
   897
	AIChat			*chat = [(AIMessageTabViewItem *)tabViewItem chat];
zacw@2130
   898
    AIListContact	*selectedObject = chat.listObject.parentContact;
David@0
   899
    NSMenu			*tmp = nil;
David@0
   900
David@0
   901
    if (selectedObject) {
David@0
   902
		NSMutableArray *locations;
David@0
   903
		if ([selectedObject isIntentionallyNotAStranger]) {
David@0
   904
			locations = [NSMutableArray arrayWithObjects:
David@3
   905
				[NSNumber numberWithInteger:Context_Contact_Manage],
David@3
   906
				[NSNumber numberWithInteger:Context_Contact_Action],
David@3
   907
				[NSNumber numberWithInteger:Context_Contact_NegativeAction],
David@3
   908
				[NSNumber numberWithInteger:Context_Contact_ChatAction],
David@3
   909
				[NSNumber numberWithInteger:Context_Contact_Additions], nil];
David@0
   910
		} else {
David@0
   911
			locations = [NSMutableArray arrayWithObjects:
David@3
   912
				[NSNumber numberWithInteger:Context_Contact_Manage],
David@3
   913
				[NSNumber numberWithInteger:Context_Contact_Action],
David@3
   914
				[NSNumber numberWithInteger:Context_Contact_NegativeAction],
David@3
   915
				[NSNumber numberWithInteger:Context_Contact_ChatAction],
David@3
   916
				[NSNumber numberWithInteger:Context_Contact_Stranger_ChatAction],
David@3
   917
				[NSNumber numberWithInteger:Context_Contact_Additions], nil];
David@0
   918
		}
David@0
   919
		
David@3
   920
		[locations addObject:[NSNumber numberWithInteger:Context_Tab_Action]];
David@0
   921
David@100
   922
		tmp = [adium.menuController contextualMenuWithLocations:locations
David@0
   923
													 forListObject:selectedObject
David@0
   924
														   inChat:chat];
David@0
   925
        
zacw@911
   926
    } else if (chat.isGroupChat) {
zacw@1243
   927
		NSArray *locations = [NSArray arrayWithObjects:
zacw@1243
   928
							  [NSNumber numberWithInteger:Context_GroupChat_Manage],
zacw@1243
   929
							  [NSNumber numberWithInteger:Context_GroupChat_Action],
zacw@1243
   930
							  [NSNumber numberWithInteger:Context_Tab_Action], nil];
zacw@911
   931
		
zacw@911
   932
		tmp = [adium.menuController contextualMenuWithLocations:locations
zacw@911
   933
														forChat:chat];
zacw@911
   934
	}
David@0
   935
	
David@0
   936
	return tmp;
David@0
   937
}
David@0
   938
David@0
   939
//Tab count changed
David@0
   940
- (void)tabViewDidChangeNumberOfTabViewItems:(NSTabView *)tabView
David@0
   941
{
David@0
   942
    [self _updateWindowTitleAndIcon];
David@0
   943
	[self _reloadContainedChats];
David@100
   944
	[adium.interfaceController chatOrderDidChange];
David@0
   945
	
David@0
   946
	//Remaining disabled until the last crasher I know of is removed
David@0
   947
	/*if ([tabView numberOfTabViewItems] > 0) {
David@0
   948
		[tabView_tabBar setAutomaticallyAnimates:YES];
David@0
   949
	}*/
David@0
   950
}
David@0
   951
David@0
   952
//Tabs reordered
David@0
   953
- (void)tabView:(NSTabView*)aTabView didDropTabViewItem:(NSTabViewItem *)tabViewItem inTabBar:(PSMTabBarControl *)tabBarControl;
David@0
   954
{
David@0
   955
	[self _reloadContainedChats];
David@100
   956
	[adium.interfaceController chatOrderDidChange];
David@0
   957
}
David@0
   958
David@0
   959
//Allow dragging of text
David@0
   960
- (NSArray *)allowedDraggedTypesForTabView:(NSTabView *)aTabView
David@0
   961
{
David@0
   962
	return [NSArray arrayWithObjects:NSRTFPboardType, NSStringPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType, NSPICTPboardType, nil];
David@0
   963
}
David@0
   964
David@0
   965
//Accept dragged text
David@0
   966
- (void)tabView:(NSTabView *)aTabView acceptedDraggingInfo:(id <NSDraggingInfo>)draggingInfo onTabViewItem:(NSTabViewItem *)tabViewItem
David@0
   967
{
David@0
   968
	[[(AIMessageTabViewItem *)tabViewItem messageViewController] addDraggedDataToTextEntryView:draggingInfo];
David@0
   969
}
David@0
   970
David@0
   971
//Get an image representation of the chat
David@754
   972
- (NSImage *)tabView:(NSTabView *)tabView imageForTabViewItem:(NSTabViewItem *)tabViewItem offset:(NSSize *)offset styleMask:(unsigned *)styleMask
David@0
   973
{
David@0
   974
	// grabs whole window image
David@0
   975
	NSImage *viewImage = [[[NSImage alloc] init] autorelease];
David@0
   976
	NSRect contentFrame = [[[self window] contentView] frame];
David@0
   977
	[[[self window] contentView] lockFocus];
David@0
   978
	NSBitmapImageRep *viewRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:contentFrame] autorelease];
David@0
   979
	[viewImage addRepresentation:viewRep];
David@0
   980
	[[[self window] contentView] unlockFocus];
David@0
   981
	
David@0
   982
    // grabs snapshot of dragged tabViewItem's view (represents content being dragged)
David@0
   983
	NSView *viewForImage = [tabViewItem view];
David@0
   984
	NSRect viewRect = [viewForImage frame];
David@0
   985
	NSImage *tabViewImage = [[[NSImage alloc] initWithSize:viewRect.size] autorelease];
David@0
   986
	[tabViewImage lockFocus];
David@0
   987
	[viewForImage drawRect:[viewForImage bounds]];
David@0
   988
	[tabViewImage unlockFocus];
David@0
   989
	
David@0
   990
	[viewImage lockFocus];
David@0
   991
	NSPoint tabOrigin = [tabView frame].origin;
David@0
   992
	tabOrigin.x += 10;
David@0
   993
	tabOrigin.y += 13;
David@0
   994
	[tabViewImage compositeToPoint:tabOrigin operation:NSCompositeSourceOver];
David@0
   995
	[viewImage unlockFocus];
David@0
   996
	
David@0
   997
	//draw over where the tab bar would usually be
David@0
   998
	NSRect tabFrame = [tabView_tabBar frame];
David@0
   999
	[viewImage lockFocus];
David@0
  1000
	[[NSColor windowBackgroundColor] set];
David@0
  1001
	NSRectFill(tabFrame);
David@0
  1002
	//draw the background flipped, which is actually the right way up
David@0
  1003
	NSAffineTransform *transform = [NSAffineTransform transform];
David@0
  1004
	[transform scaleXBy:1.0 yBy:-1.0];
David@0
  1005
	[transform concat];
David@0
  1006
	tabFrame.origin.y = -tabFrame.origin.y - tabFrame.size.height;
David@0
  1007
	[(id <PSMTabStyle>)[[tabView delegate] style] drawBackgroundInRect:tabFrame];
David@0
  1008
	[transform invert];
David@0
  1009
	[transform concat];
David@0
  1010
	
David@0
  1011
	[viewImage unlockFocus];
David@0
  1012
	
David@0
  1013
	id <PSMTabStyle> style = (id <PSMTabStyle>)[[tabView delegate] style];
David@0
  1014
	
David@0
  1015
	switch (tabPosition) {
David@0
  1016
		case AdiumTabPositionBottom:
David@0
  1017
			offset->width = [style leftMarginForTabBarControl];
David@0
  1018
			offset->height = contentFrame.size.height;
David@0
  1019
			break;
David@0
  1020
		case AdiumTabPositionTop:
David@0
  1021
			offset->width = [style leftMarginForTabBarControl];
David@0
  1022
			offset->height = 21;
David@0
  1023
			break;
David@0
  1024
		case AdiumTabPositionLeft:
David@0
  1025
			offset->width = 0;
David@0
  1026
			offset->height = 21 + [style topMarginForTabBarControl];
David@0
  1027
			break;
David@0
  1028
		case AdiumTabPositionRight:
David@0
  1029
			offset->width = [tabView_tabBar frame].origin.x;
David@0
  1030
			offset->height = 21 + [style topMarginForTabBarControl];
David@0
  1031
			break;
David@0
  1032
	}
David@0
  1033
	
David@0
  1034
	*styleMask = NSTitledWindowMask;
David@0
  1035
	
David@0
  1036
	return viewImage;
David@0
  1037
}
David@0
  1038
David@0
  1039
//Create a new tab window
David@0
  1040
- (PSMTabBarControl *)tabView:(NSTabView *)tabView newTabBarForDraggedTabViewItem:(NSTabViewItem *)tabViewItem atPoint:(NSPoint)point
David@0
  1041
{
David@0
  1042
	id newController = [interface openNewContainer];
David@0
  1043
	NSRect frame;
David@0
  1044
	id <PSMTabStyle> style = (id <PSMTabStyle>)[[tabView delegate] style];
David@0
  1045
	
David@0
  1046
	//set the size of the new window
David@0
  1047
	//set the size and origin separately so that toolbar visibility and size doesn't mess things up
David@0
  1048
	frame.size = [[self window] frame].size;
David@0
  1049
	frame.origin = NSZeroPoint;
David@0
  1050
	[[newController window] setFrame:frame display:NO];
David@0
  1051
	
David@0
  1052
	switch (tabPosition) {
David@0
  1053
		case AdiumTabPositionBottom:
David@0
  1054
			point.x -= [style leftMarginForTabBarControl];
David@0
  1055
			point.y -= 22;
David@0
  1056
			break;
David@0
  1057
		case AdiumTabPositionTop:
David@0
  1058
			point.x -= [style leftMarginForTabBarControl];
David@0
  1059
			point.y -= NSHeight([[[newController window] contentView] frame]) + 1;
David@0
  1060
			break;
David@0
  1061
		case AdiumTabPositionLeft:
David@0
  1062
			point.y -= NSHeight([[[newController window] contentView] frame]) - [style topMarginForTabBarControl] + 1;
David@0
  1063
			break;
David@0
  1064
		case AdiumTabPositionRight:
David@0
  1065
			point.x -= NSMinX([tabView_tabBar frame]);
David@0
  1066
			point.y -= NSHeight([[[newController window] contentView] frame]) - [style topMarginForTabBarControl] + 1;
David@0
  1067
	}
David@0
  1068
	
David@0
  1069
	//set the origin point of the new window
David@0
  1070
	frame.origin = point;
David@0
  1071
	[[newController window] setFrame:frame display:NO];
David@0
  1072
	
David@0
  1073
	return [newController tabBar];
David@0
  1074
}
David@0
  1075
David@0
  1076
- (void)tabView:(NSTabView *)tabView tabBarDidHide:(PSMTabBarControl *)tabBarControl
David@0
  1077
{
David@0
  1078
    //hide the space between the tab bar and the tab view
David@0
  1079
    NSRect frame = [tabView frame];
David@0
  1080
	switch ([tabBarControl orientation]) {
David@0
  1081
		case PSMTabBarHorizontalOrientation:
David@0
  1082
			/* We put a space between a bottom horizontal tab bar and the message tab view.
David@0
  1083
			 * This space is not needed when the tab bar is at the top.
David@0
  1084
			 */			
David@0
  1085
			if (tabPosition == AdiumTabPositionBottom) {
David@0
  1086
				frame.origin.y -= HORIZONTAL_TAB_BAR_TO_VIEW_SPACING;
David@0
  1087
				frame.size.height += HORIZONTAL_TAB_BAR_TO_VIEW_SPACING;				
David@0
  1088
			}
David@0
  1089
			break;
David@0
  1090
David@0
  1091
		case PSMTabBarVerticalOrientation:
David@0
  1092
			frame.origin.x -= VERTICAL_TAB_BAR_TO_VIEW_SPACING;
David@0
  1093
			frame.size.width += VERTICAL_TAB_BAR_TO_VIEW_SPACING;
David@0
  1094
			
David@0
  1095
			[tabView_splitView setDividerThickness:0];
David@0
  1096
			break;
David@0
  1097
	}
David@0
  1098
David@0
  1099
	[tabView setFrame:frame];
David@0
  1100
	[tabBarControl setHidden:YES];
David@0
  1101
	[tabView setNeedsDisplay:YES];
David@0
  1102
David@0
  1103
	[[tabView_messages tabViewItems] makeObjectsPerformSelector:@selector(tabViewDidChangeVisibility)];
David@0
  1104
}
David@0
  1105
David@0
  1106
- (void)tabView:(NSTabView *)tabView tabBarDidUnhide:(PSMTabBarControl *)tabBarControl
David@0
  1107
{
David@0
  1108
	//show the space between the tab bar and the tab view
David@0
  1109
    NSRect frame = [tabView frame];
David@0
  1110
	
David@0
  1111
	switch ([tabBarControl orientation]) {
David@0
  1112
		case PSMTabBarHorizontalOrientation:
David@0
  1113
			/* We put a space between a bottom horizontal tab bar and the message tab view.
David@0
  1114
			 * This space is not needed when the tab bar is at the top.
David@0
  1115
			 */
David@0
  1116
			if (tabPosition == AdiumTabPositionBottom) {
David@0
  1117
				frame.origin.y += HORIZONTAL_TAB_BAR_TO_VIEW_SPACING;
David@0
  1118
				frame.size.height -= HORIZONTAL_TAB_BAR_TO_VIEW_SPACING;				
David@0
  1119
			}
David@0
  1120
			break;
David@0
  1121
David@0
  1122
		case PSMTabBarVerticalOrientation:
David@0
  1123
			frame.origin.x += VERTICAL_TAB_BAR_TO_VIEW_SPACING;
David@0
  1124
			frame.size.width -= VERTICAL_TAB_BAR_TO_VIEW_SPACING;
David@0
  1125
			
David@0
  1126
			[tabView_splitView setDividerThickness:VERTICAL_DIVIDER_THICKNESS];
David@0
  1127
			break;
David@0
  1128
    }
David@0
  1129
    
David@0
  1130
    [tabView setFrame:frame];
David@0
  1131
	[tabBarControl setHidden:NO];
David@0
  1132
    [tabView setNeedsDisplay:YES];
David@0
  1133
	
David@0
  1134
	[[tabView_messages tabViewItems] makeObjectsPerformSelector:@selector(tabViewDidChangeVisibility)];
David@0
  1135
}
David@0
  1136
David@754
  1137
- (float)desiredWidthForVerticalTabBar:(PSMTabBarControl *)tabBarControl
David@0
  1138
{
David@0
  1139
	return (lastTabBarWidth ? lastTabBarWidth : 120);
David@0
  1140
}
David@0
  1141
David@0
  1142
- (NSString *)tabView:(NSTabView *)tabView toolTipForTabViewItem:(NSTabViewItem *)tabViewItem
David@0
  1143
{
David@0
  1144
	AIChat		*chat = [(AIMessageTabViewItem *)tabViewItem chat];
David@0
  1145
	NSString	*tooltip = nil;
David@0
  1146
David@428
  1147
	if (chat.isGroupChat) {
David@428
  1148
		tooltip = [NSString stringWithFormat:AILocalizedString(@"%@ in %@","AccountName on ChatRoomName"), chat.account.formattedUID, chat.name];
David@0
  1149
	} else {
David@426
  1150
		AIListObject	*destination = chat.listObject;
David@428
  1151
		NSString		*destinationDisplayName = destination.displayName;
David@428
  1152
		NSString		*destinationFormattedUID = destination.formattedUID;
David@0
  1153
		BOOL			includeDestination = NO;
David@0
  1154
		BOOL			includeSource = NO;
David@0
  1155
		
David@0
  1156
		if (destinationFormattedUID && destinationDisplayName &&
David@0
  1157
			![[destinationDisplayName compactedString] isEqualToString:[destinationFormattedUID compactedString]]) {
David@0
  1158
			includeDestination = YES;
David@0
  1159
		}
David@0
  1160
zacwest@1825
  1161
		NSUInteger onlineAccounts = 0;
catfish@1824
  1162
		for (AIAccount *account in adium.accountController.accounts) {
catfish@1824
  1163
			if (onlineAccounts >= 2) break;
David@715
  1164
			if (account.online) onlineAccounts++;
David@0
  1165
		}
David@0
  1166
catfish@1824
  1167
		if (onlineAccounts >= 2)
David@0
  1168
			includeSource = YES;
catfish@1824
  1169
David@428
  1170
		AILog(@"Displaying tooltip for %@ --> %@ (%@) --> %@ (%@)", chat, chat.account, chat.account.formattedUID, destination, destinationFormattedUID);
David@0
  1171
		if (includeDestination && includeSource) {
David@428
  1172
			tooltip = [NSString stringWithFormat:AILocalizedString(@"%@ talking to %@","AccountName talking to Username"), chat.account.formattedUID, destinationFormattedUID];
David@0
  1173
David@0
  1174
		} else if (includeDestination) {
David@0
  1175
			tooltip = destinationFormattedUID;
David@0
  1176
			
David@0
  1177
		} else if (includeSource) {
David@428
  1178
			tooltip = chat.account.formattedUID;
David@0
  1179
		}
David@0
  1180
	}
David@0
  1181
	
David@0
  1182
	return tooltip;
David@0
  1183
}
David@0
  1184
David@0
  1185
- (void)tabView:(NSTabView *)aTabView tabViewItem:(NSTabViewItem *)tabViewItem isInOverflowMenu:(BOOL)inOverflowMenu
David@0
  1186
{
David@0
  1187
	//Wait until the next run loop, then update the icons so the overflow menu will be updated appropriately
David@0
  1188
	[self performSelector:@selector(updateOverflowMenuUnviewedContentIcon)
David@0
  1189
			   withObject:nil
David@0
  1190
			   afterDelay:0];
David@0
  1191
}
David@0
  1192
David@0
  1193
//Tab Bar Visibility --------------------------------------------------------------------------------------------------
David@0
  1194
#pragma mark Tab Bar Visibility/Drag And Drop
David@0
  1195
David@0
  1196
//Replaced by PSMTabBarControl
David@0
  1197
David@0
  1198
//Make sure auto-hide suppression is off after a drag completes
David@0
  1199
- (void)tabDraggingNotificationReceived:(NSNotification *)notification
David@0
  1200
{
David@0
  1201
	if ([[notification name] isEqualToString:PSMTabDragDidBeginNotification]) {
David@0
  1202
		[tabView_tabBar setHideForSingleTab:NO];
David@0
  1203
	} else {
David@0
  1204
		[tabView_tabBar setHideForSingleTab:!alwaysShowTabs];
David@0
  1205
	}
David@0
  1206
}
David@0
  1207
David@0
  1208
//Save width of the vertical tabs when changed
David@0
  1209
- (void)tabBarFrameChanged:(NSNotification *)notification {
David@0
  1210
	if ([tabView_tabBar orientation] == PSMTabBarVerticalOrientation) {
David@0
  1211
		if (![tabView_tabBar isTabBarHidden]) {
David@3
  1212
			CGFloat newWidth = NSWidth([tabView_tabBar frame]);
David@0
  1213
			if (newWidth >= MINIMUM_WIDTH_FOR_VERTICAL_TABS)
David@0
  1214
				lastTabBarWidth = newWidth;
David@0
  1215
			
David@0
  1216
		}
David@0
  1217
	}
David@0
  1218
}
David@0
  1219
David@0
  1220
/*//Custom Tabs Delegate -------------------------------------------------------------------------------------------------
David@0
  1221
#pragma mark Custom Tabs Delegate
David@0
  1222
//Bring our window to the front
David@0
  1223
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
David@0
  1224
{
David@0
  1225
	NSDragOperation tmp = NSDragOperationNone;
David@0
  1226
    NSString 		*type = [[sender draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:TAB_CELL_IDENTIFIER,nil]];
David@0
  1227
David@0
  1228
    if (sender == nil || type) {
David@0
  1229
        if (![[self window] isKeyWindow]) [[self window] makeKeyAndOrderFront:nil];
David@0
  1230
		[self _suppressTabHiding:YES];
David@0
  1231
        tmp = NSDragOperationPrivate;
David@0
  1232
    }
David@0
  1233
	return tmp;
David@0
  1234
}
David@0
  1235
David@0
  1236
- (void)draggingExited:(id <NSDraggingInfo>)sender
David@0
  1237
{
David@0
  1238
	NSString 		*type = [[sender draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:TAB_CELL_IDENTIFIER,nil]];
David@0
  1239
	
David@0
  1240
    if (sender == nil || type) [self _suppressTabHiding:NO];
David@0
  1241
}
David@0
  1242
David@0
  1243
- (void)_suppressTabHiding:(BOOL)suppress
David@0
  1244
{
David@0
  1245
	supressHiding = suppress;
David@0
  1246
	[self updateTabBarVisibilityAndAnimate:YES];
David@0
  1247
}
David@0
  1248
David@0
  1249
//Send the print message to our view
David@0
  1250
- (void)adiumPrint:(id)sender
David@0
  1251
{
David@0
  1252
	id	controller = [(AIMessageTabViewItem *)[tabView_messages selectedTabViewItem] messageViewController];
David@0
  1253
	
David@0
  1254
	if ([controller respondsToSelector:@selector(adiumPrint:)]) {
David@0
  1255
		[controller adiumPrint:sender];
David@0
  1256
	}
David@0
  1257
}*/
David@0
  1258
David@0
  1259
//Toolbar --------------------------------------------------------------------------------------------------------------
David@0
  1260
#pragma mark Toolbar
David@0
  1261
//Install our toolbar
David@0
  1262
- (void)_configureToolbar
David@0
  1263
{
David@0
  1264
//	NSToolbar *toolbar; change this if need be
David@0
  1265
    toolbar = [[[NSToolbar alloc] initWithIdentifier:TOOLBAR_MESSAGE_WINDOW] autorelease];
David@0
  1266
	
David@0
  1267
    [toolbar setDelegate:self];
David@0
  1268
    [toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
David@0
  1269
    [toolbar setSizeMode:NSToolbarSizeModeSmall];
David@0
  1270
    [toolbar setVisible:YES];
David@0
  1271
    [toolbar setAllowsUserCustomization:YES];
David@0
  1272
    [toolbar setAutosavesConfiguration:YES];
David@0
  1273
	
David@0
  1274
    //
David@100
  1275
	toolbarItems = [[adium.toolbarController toolbarItemsForToolbarTypes:[NSArray arrayWithObjects:@"General", @"ListObject", @"TextEntry", @"MessageWindow", nil]] retain];
David@0
  1276
David@0
  1277
	/* Seemingly randomly, setToolbar: may throw:
David@0
  1278
	 * Exception:	NSInternalInconsistencyException
David@0
  1279
	 * Reason:		Uninitialized rectangle passed to [View initWithFrame:].
David@0
  1280
	 *
David@0
  1281
	 * With the same window positioning information as a user for whom this happens consistently, I can't reproduce. Let's
David@0
  1282
	 * fail to set the toolbar gracefully.
David@0
  1283
	 */
David@0
  1284
	@try
David@0
  1285
	{
David@0
  1286
		[[self window] setToolbar:toolbar];
David@0
  1287
	}
David@0
  1288
	@catch(id exc)
David@0
  1289
	{
David@0
  1290
		NSLog(@"Warning: While setting the message window's toolbar, exception %@ was thrown.", exc);
David@0
  1291
	}
David@0
  1292
}
David@0
  1293
David@0
  1294
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
David@0
  1295
{
David@0
  1296
	return [AIToolbarUtilities toolbarItemFromDictionary:toolbarItems withIdentifier:itemIdentifier];
David@0
  1297
}
David@0
  1298
David@0
  1299
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
David@0
  1300
{
David@0
  1301
    return [NSArray arrayWithObjects:@"UserIcon",@"Encryption",  NSToolbarSeparatorItemIdentifier, 
David@0
  1302
		@"SourceDestination", @"InsertEmoticon", @"BlockParticipants", @"LinkEditor", @"SafariLink", @"AddBookmark", NSToolbarShowColorsItemIdentifier,
David@0
  1303
		NSToolbarShowFontsItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, @"SendFile",
David@0
  1304
		@"ShowInfo", @"LogViewer", nil];
David@0
  1305
}
David@0
  1306
David@0
  1307
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
David@0
  1308
{
David@0
  1309
    return [[toolbarItems allKeys] arrayByAddingObjectsFromArray:
David@0
  1310
		[NSArray arrayWithObjects:NSToolbarSeparatorItemIdentifier,
David@0
  1311
			NSToolbarSpaceItemIdentifier,
David@0
  1312
			NSToolbarFlexibleSpaceItemIdentifier,
David@0
  1313
			NSToolbarShowColorsItemIdentifier,
David@0
  1314
			NSToolbarShowFontsItemIdentifier,
David@0
  1315
			NSToolbarCustomizeToolbarItemIdentifier, nil]];
David@0
  1316
}
David@0
  1317
David@0
  1318
- (void)toolbarWillAddItem:(NSNotification *)notification
David@0
  1319
{
David@0
  1320
David@0
  1321
	NSToolbarItem *item = [[notification userInfo] objectForKey:@"item"];
David@0
  1322
David@0
  1323
	if ([[item itemIdentifier] isEqualToString:NSToolbarShowFontsItemIdentifier]) {
David@100
  1324
		[item setTarget:adium.interfaceController];
David@0
  1325
		[item setAction:@selector(toggleFontPanel:)];
David@0
  1326
	}
David@0
  1327
}
David@0
  1328
David@0
  1329
- (void)removeToolbarItemWithIdentifier:(NSString*)identifier
David@0
  1330
{
David@0
  1331
	NSArray			*itemArray = [toolbar items];
David@0
  1332
	NSEnumerator	*enumerator = [itemArray objectEnumerator];
David@0
  1333
	NSToolbarItem	*item;
Evan@166
  1334
	NSInteger		index = NSNotFound;
David@0
  1335
David@0
  1336
	while ((item = [enumerator nextObject])) {
David@0
  1337
		if ([[item itemIdentifier] isEqualToString:identifier]) {
David@0
  1338
			index = [itemArray indexOfObject:item];
David@0
  1339
			break;
David@0
  1340
		}
David@0
  1341
	}
David@0
  1342
David@0
  1343
	if (index != NSNotFound) {
David@0
  1344
		[toolbar removeItemAtIndex:index];
David@0
  1345
	}
David@0
  1346
}
David@0
  1347
David@0
  1348
#pragma mark Miniaturization
David@0
  1349
/*!
David@0
  1350
 * @brief Our window is about to minimize
David@0
  1351
 *
David@0
  1352
 * Set our miniwindow image, which will display in the dock, appropriately.
David@0
  1353
 */
David@0
  1354
- (void)windowWillMiniaturize:(NSNotification *)notification
David@0
  1355
{
David@0
  1356
	NSImage *miniwindowImage;
David@0
  1357
	NSImage	*chatImage = [[(AIMessageTabViewItem *)[tabView_messages selectedTabViewItem] chat] chatImage];
David@95
  1358
	NSImage	*appImage = [adium.dockController baseApplicationIconImage];
David@0
  1359
	NSSize	chatImageSize = [chatImage size];
David@0
  1360
	NSSize	appImageSize = [appImage size];
David@0
  1361
	NSSize	newChatImageSize;
David@0
  1362
	NSSize	badgeSize;
David@0
  1363
	
David@0
  1364
	miniwindowImage = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
David@0
  1365
	
David@0
  1366
	//Determine the properly scaled chat image size
David@0
  1367
	newChatImageSize = NSMakeSize(96,96);
David@0
  1368
	if (chatImageSize.width != chatImageSize.height) {
David@0
  1369
		if (chatImageSize.width > chatImageSize.height) {
David@0
  1370
			//Give width priority: Make the height change by the same proportion as the width will change
David@0
  1371
			newChatImageSize.height = chatImageSize.height * (newChatImageSize.width / chatImageSize.width);
David@0
  1372
		} else {
David@0
  1373
			//Give height priority: Make the width change by the same proportion as the height will change
David@0
  1374
			newChatImageSize.width = chatImageSize.width * (newChatImageSize.height / chatImageSize.height);
David@0
  1375
		}		
David@0
  1376
	}
David@0
  1377
	
David@0
  1378
	//OS X 10.4 always returns a square application icon of 128x128, but better safe than sorry
David@0
  1379
	badgeSize = NSMakeSize(48, 48);
David@0
  1380
	if (appImageSize.width != appImageSize.height) {
David@0
  1381
		if (appImageSize.width > appImageSize.height) {
David@0
  1382
			//Give width priority: Make the height change by the same proportion as the width will change
David@0
  1383
			badgeSize.height = appImageSize.height * (badgeSize.width / appImageSize.width);
David@0
  1384
		} else {
David@0
  1385
			//Give height priority: Make the width change by the same proportion as the height will change
David@0
  1386
			badgeSize.width = appImageSize.width * (badgeSize.height / appImageSize.height);
David@0
  1387
		}		
David@0
  1388
	}
David@0
  1389
	
David@0
  1390
	[miniwindowImage lockFocus];
David@0
  1391
	{
David@0
  1392
		//Draw the chat image with space around it (the dock will do ugly scaling if we don't make a transparent border)
David@0
  1393
		[chatImage drawInRect:NSMakeRect((128 - newChatImageSize.width)/2, (128 - newChatImageSize.height)/2,
David@0
  1394
										 newChatImageSize.width, newChatImageSize.height)
David@0
  1395
					 fromRect:NSMakeRect(0, 0, chatImageSize.width, chatImageSize.height)
David@0
  1396
					operation:NSCompositeSourceOver
David@0
  1397
					 fraction:1.0];
David@0
  1398
		
David@0
  1399
		//Draw the Adium icon as a badge in the bottom right
David@0
  1400
		[appImage drawInRect:NSMakeRect(128 - badgeSize.width,
David@0
  1401
										0,
David@0
  1402
										badgeSize.width,
David@0
  1403
										badgeSize.height)
David@0
  1404
					fromRect:NSMakeRect(0, 0, appImageSize.width, appImageSize.height)
David@0
  1405
				   operation:NSCompositeSourceOver
David@0
  1406
					fraction:1.0];
David@0
  1407
	}
David@0
  1408
	[miniwindowImage unlockFocus];
David@0
  1409
	
David@0
  1410
	//Set the image
David@0
  1411
	[[self window] setMiniwindowImage:miniwindowImage];
David@0
  1412
	
David@0
  1413
	//Cleanup
David@0
  1414
	[miniwindowImage release];
David@0
  1415
}
David@0
  1416
David@0
  1417
- (BOOL)window:(NSWindow *)sender shouldDragDocumentWithEvent:(NSEvent *)mouseEvent from:(NSPoint)startPoint withPasteboard:(NSPasteboard *)pasteboard
David@0
  1418
{
David@0
  1419
	return NO;
David@0
  1420
}
David@0
  1421
David@0
  1422
#pragma mark Printing
David@0
  1423
/*!
David@0
  1424
 * @brief Support for printing.  Forward the print command to the active tab view's message view controller
David@0
  1425
 */
David@0
  1426
- (void)adiumPrint:(id)sender
David@0
  1427
{
David@0
  1428
	[[(AIMessageTabViewItem *)[tabView_messages selectedTabViewItem] messageViewController] adiumPrint:sender];
David@0
  1429
}
David@0
  1430
David@0
  1431
#pragma mark Gestures
David@0
  1432
David@0
  1433
/*!
David@0
  1434
 * @brief Responds to a swipe gesture
David@0
  1435
 *
David@0
  1436
 * This is a private method added in AppKit 949.18.0.
David@0
  1437
 */
David@0
  1438
- (void)swipeWithEvent:(NSEvent *)inEvent
David@0
  1439
{
David@0
  1440
	// We don't do anything for vertical swipes.
David@0
  1441
	if ([inEvent deltaY] != 0)
David@0
  1442
		return;
David@0
  1443
	
David@0
  1444
	// Horizontal swipe; +1f is left, -1f is right.
David@0
  1445
	if ([inEvent deltaX] == -1) {
David@100
  1446
		[adium.interfaceController nextChat:nil];
David@0
  1447
	} else {
David@100
  1448
		[adium.interfaceController previousChat:nil];
David@0
  1449
	}
David@0
  1450
}
David@0
  1451
David@497
  1452
//inherit this
David@497
  1453
@dynamic window;
David@497
  1454
David@0
  1455
@end