Prevent dragging groups into groups. Fixes #8706
[adiumx.git] / Source / AIEditAccountWindowController.m
blobb41dc81a645ab34996c8face96d9c1deafef8b7d
1 /*
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  *
5  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6  * General Public License as published by the Free Software Foundation; either version 2 of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
11  * Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along with this program; if not,
14  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15  */
17 #import <Adium/AIAccountControllerProtocol.h>
18 #import "AIAccountProxySettings.h"
19 #import <Adium/AIContactControllerProtocol.h>
20 #import <Adium/AIPreferenceControllerProtocol.h>
21 #import "AIEditAccountWindowController.h"
22 #import <AIUtilities/AIStringAdditions.h>
23 #import <AIUtilities/AITabViewAdditions.h>
24 #import <AIUtilities/AIViewAdditions.h>
25 #import <AIUtilities/AIImageAdditions.h>
26 #import <AIUtilities/AIImageViewWithImagePicker.h>
27 #import <Adium/AIAccount.h>
28 #import <Adium/AIAccountViewController.h>
29 #import <Adium/AIService.h>
30 #import <Adium/AIService.h>
31 #import <Adium/AIServiceIcons.h>
33 @interface AIEditAccountWindowController (PRIVATE)
34 - (id)initWithWindowNibName:(NSString *)windowNibName account:(AIAccount *)inAccount notifyingTarget:(id)inTarget;
35 - (void)_addCustomViewAndTabsForAccount:(AIAccount *)inAccount;
36 - (void)_addCustomView:(NSView *)customView toView:(NSView *)setupView tabViewItemIdentifier:(NSString *)identifier
37                 runningHeight:(int *)height width:(int *)width;
38 - (void)_configureResponderChain:(NSTimer *)inTimer;
39 - (void)_removeCustomViewAndTabs;
40 - (void)_localizeTabViewItemLabels;
41 - (void)saveConfiguration;
42 - (void)configureControlDimming;
43 @end
45 /*!
46  * @class AIEditAccountWindowController
47  * @brief Window controller for configuring an <tt>AIAccount</tt>
48  */
49 @implementation AIEditAccountWindowController
51 /*!
52  * @brief Begin editing
53  *
54  * @param inAccount The account to edit
55  * @param parentWindow A window on which to show the edit account window as a sheet.  If nil, account editing takes place in an independent window.
56  * @param notifyingTarget Target to notify when editing is complete.
57  */
58 + (void)editAccount:(AIAccount *)inAccount onWindow:(id)parentWindow notifyingTarget:(id)inTarget
60         AIEditAccountWindowController   *controller;
62         controller = [[self alloc] initWithWindowNibName:@"EditAccountSheet"
63                                                                                          account:inAccount
64                                                                          notifyingTarget:inTarget];
66         if (parentWindow) {
67                 [NSApp beginSheet:[controller window]
68                    modalForWindow:parentWindow
69                         modalDelegate:controller
70                    didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
71                           contextInfo:nil];
72         } else {
73                 [controller showWindow:nil];
74         }
77 /*!
78  * @brief Init the window controller
79  */
80 - (id)initWithWindowNibName:(NSString *)windowNibName account:(AIAccount *)inAccount notifyingTarget:(id)inTarget
82         if ((self = [super initWithWindowNibName:windowNibName])) {
83                 account = [inAccount retain];
84                 notifyTarget = inTarget;
85                 userIconData = nil;
86                 didDeleteUserIcon = NO;
87         }
88         return self;
91 /*!
92  * @brief Deallocate
93  */
94 - (void)dealloc
96         [account release];
97         [userIconData release]; userIconData = nil;
99         [super dealloc];
103  * @brief Setup the window before it is displayed
104  */
105 - (void)windowDidLoad
107         //Center our window if we're not a sheet (or opening a sheet failed)
108         [[self window] center];
109         
110         [[self window] setTitle:AILocalizedString(@"Edit Account", nil)];
112         //Account Overview
113         [textField_serviceName setStringValue:[[account service] longDescription]];
114         [textField_accountDescription setStringValue:[account UID]];
116         [button_chooseIcon setLocalizedString:[AILocalizedString(@"Choose Icon",nil) stringByAppendingEllipsis]];
117         [button_OK setLocalizedString:AILocalizedString(@"OK",nil)];
118         [button_cancel setLocalizedString:AILocalizedString(@"Cancel",nil)];
119         [checkbox_autoconnect setLocalizedString:AILocalizedString(@"Connect when Adium opens", "Account preferences checkbox for automatically conencting the account when Adium opens")];
121         [[matrix_userIcon cellWithTag:0] setTitle:AILocalizedString(@"Use global icon", "Radio button in the Personal tab of Account preferences.  This -must- be a short string of 20 characters or less.")];
122         [[matrix_userIcon cellWithTag:1] setTitle:AILocalizedString(@"Use this icon:", "Radio button in the Personal tab of Account preferences; an image is shown beneath it to select the account's icon.  This -must- be a short string of 20 characters or less.")];
123         
124         //User icon
125         if ([account preferenceForKey:KEY_USER_ICON group:GROUP_ACCOUNT_STATUS ignoreInheritedValues:YES]) {
126                 //If this account has a icon set directly on it, then it has its own icon
127                 [matrix_userIcon selectCellWithTag:1];
129         } else {
130                 //Otherwise it is using the global icon
131                 [matrix_userIcon selectCellWithTag:0];
132         }
134         [imageView_userIcon setMaxSize:NSMakeSize(256, 256)];
135         [imageView_userIcon setImage:[account userIcon]];
137         [checkbox_autoconnect setState:[[account preferenceForKey:KEY_AUTOCONNECT
138                                                                                                                 group:GROUP_ACCOUNT_STATUS] boolValue]];
140         //Insert the custom controls for this account
141         [self _removeCustomViewAndTabs];
142         [self _addCustomViewAndTabsForAccount:account];
143         [self _localizeTabViewItemLabels];
144         
145         [self configureControlDimming];
148 - (IBAction)showWindow:(id)sender {
149         [super showWindow:sender];
150         if([notifyTarget respondsToSelector:@selector(editAccountWindow:didOpenForAccount:)])
151                 [notifyTarget editAccountWindow:[self window] didOpenForAccount:account];
155  * @brief Window is closing
156  */
157 - (void)windowWillClose:(id)sender
159         [super windowWillClose:sender];
160         [self autorelease];
164  * @brief Called as the user list edit sheet closes, dismisses the sheet
165  */
166 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
168     [sheet orderOut:nil];
171 - (void)configureControlDimming
173         BOOL enableUserIcon = ([[matrix_userIcon selectedCell] tag] == 1);
174         
175         [imageView_userIcon setEnabled:enableUserIcon];
176         [button_chooseIcon setEnabled:enableUserIcon];
180  * @brief The user changed the selection in the icon setting matrix which determines availability of the icon controls
181  */
182 - (IBAction)changedIconSetting:(id)sender
184         [self configureControlDimming];
188  * @brief Cancel
190  * Close without saving changes.
191  */
192 - (IBAction)cancel:(id)sender
194         if (notifyTarget) [notifyTarget editAccountSheetDidEndForAccount:account withSuccess:NO];
195         [self closeWindow:nil];
199  * @brief Okay.
201  * Save changes and close.
202  */
203 - (IBAction)okay:(id)sender
205         [self saveConfiguration];
206         [accountViewController saveConfiguration];
207         [accountProxyController saveConfiguration];
209         if (notifyTarget) [notifyTarget editAccountSheetDidEndForAccount:account withSuccess:YES];
210         [self closeWindow:nil];
214  * @brief Save any configuration managed by the window controller
216  * Most configuration is handled by the custom view controllers.  Save any other configuration, such as the user icon.
217  */
218 - (void)saveConfiguration
220         BOOL enableUserIcon = ([[matrix_userIcon selectedCell] tag] == 1);
222         if (!enableUserIcon) {
223                 [userIconData release]; userIconData = nil;
224                 didDeleteUserIcon = YES;
225         }
227         /* User icon - save if we have data or we deleted
228          * (so if we don't have data that's the desired thing to set as the pref) */
229         if (userIconData || didDeleteUserIcon) {
230                 [account setPreference:userIconData
231                                                 forKey:KEY_USER_ICON
232                                                  group:GROUP_ACCOUNT_STATUS];
233         }
235         [account setPreference:[NSNumber numberWithBool:[checkbox_autoconnect state]]
236                                         forKey:KEY_AUTOCONNECT
237                                          group:GROUP_ACCOUNT_STATUS];
241  * @brief Add the custom views for an account
242  */
243 - (void)_addCustomViewAndTabsForAccount:(AIAccount *)inAccount
245         NSRect  windowFrame = [[self window] frame];
246         int             baseHeight = NSHeight([view_accountSetup frame]);
247         int             baseWidth = NSWidth([view_accountSetup frame]);
248         int             newHeight = baseHeight, newWidth = baseWidth;
250         //Configure our account and proxy view controllers
251         accountViewController = [[[inAccount service] accountViewController] retain];
252         [accountViewController configureForAccount:inAccount];
254         accountProxyController = ([[inAccount service] supportsProxySettings] ?
255                                                           [[AIAccountProxySettings alloc] init] :
256                                                           nil);
257         [accountProxyController configureForAccount:inAccount];
259         //Account setup view
260         [self _addCustomView:[accountViewController setupView]
261                                   toView:view_accountSetup
262    tabViewItemIdentifier:@"account"
263                  runningHeight:&newHeight
264                                    width:&newWidth];
265         
266         //Account Profile View
267         [self _addCustomView:[accountViewController profileView]
268                                   toView:view_accountProfile
269    tabViewItemIdentifier:@"profile"
270                  runningHeight:&newHeight
271                                    width:NULL];
272         
273         //Account Options view
274         [self _addCustomView:[accountViewController optionsView]
275                                   toView:view_accountOptions
276    tabViewItemIdentifier:@"options"
277                  runningHeight:&newHeight
278                                    width:&newWidth];
279         
280         //Account Privacy view
281         [self _addCustomView:[accountViewController privacyView]
282                                   toView:view_accountPrivacy
283    tabViewItemIdentifier:@"privacy"
284                  runningHeight:&newHeight
285                                    width:&newWidth];
286         
287         //Add proxy view
288         [self _addCustomView:[accountProxyController view]
289                                   toView:view_accountProxy
290    tabViewItemIdentifier:@"proxy"
291                  runningHeight:&newHeight
292                                    width:&newWidth];
293         
294         //Resize our window as necessary to make room for the custom views
295         windowFrame.size.height += newHeight - baseHeight;
296         windowFrame.size.width += newWidth - baseWidth;
298         [[self window] setFrame:windowFrame display:YES];
300         //Responder chains are a pain in 10.3.  The tab view will set them up correctly when we switch tabs, but doesn't
301         //get a chance to setup the responder chain for our default tab.  A quick hack to get the tab view to set things
302         //up correctly is to switch tabs away and then back to our default.  This causes little harm, since our window
303         //isn't visible at this point anyway.
304         //XXX - I believe we're getting a method that will avoid the need for this hack in 10.4 -ai
305         [tabView_auxiliary selectLastTabViewItem:nil];
306         [tabView_auxiliary selectFirstTabViewItem:nil];
310  * @brief Used when configuring to add custom views and remove tabs as necessary
312  * Add customView to setupView and return the height difference between the two if customView is taller than setupView.
313  * Remove the tabViewItem with the passed identifier if no customView exists, avoiding empty tabs.
315  * @param customView The view to add
316  * @param setupView The view within our nib which will be filled by customView
317  * @param identifier Identifier of the <tt>NSTabViewItem</tt> which will be removed from tabView_auxiliary if customView == nil
318  * @param requiredHeight The current required view height to display all our views
319  * @result The new required window height to display our existing views and the newly added view
320  */
321 - (void)_addCustomView:(NSView *)customView toView:(NSView *)setupView tabViewItemIdentifier:(NSString *)identifier
322           runningHeight:(int *)height width:(int *)width
324         if (customView) {
325                 //Adjust height as necessary if our view needs more room
326                 if (NSHeight([customView frame]) > *height) {
327                         *height = NSHeight([customView frame]);
328                 }
330                 //Adjust height as necessary if our view needs more room
331                 if (width && (NSWidth([customView frame]) > *width)) {
332                         *width = NSWidth([customView frame]);
333                 }
334                 
335                 //Align our view to the top and insert it into the window
336                 if (width && (NSWidth([setupView frame]) > NSWidth([customView frame])))
337                         [customView setFrameOrigin:NSMakePoint(floor((NSWidth([setupView frame]) - NSWidth([customView frame])) / 2),
338                                                                                                    NSHeight([setupView frame]) - NSHeight([customView frame]))];
339                 else
340                         [customView setFrameOrigin:NSMakePoint(0,
341                                                                                                    NSHeight([setupView frame]) - NSHeight([customView frame]))];
343                 [customView setAutoresizingMask:(NSViewMinYMargin | NSViewMinXMargin | NSViewMaxXMargin)];
344                 [setupView addSubview:customView];
346         } else {
347                 //If no view is available, remove the corresponding tab
348                 [tabView_auxiliary removeTabViewItem:[tabView_auxiliary tabViewItemWithIdentifier:identifier]];
349         }
353  * @brief Remove any existing custom views
354  */
355 - (void)_removeCustomViewAndTabs
357     //Close any currently open controllers
358     [view_accountSetup removeAllSubviews];
359     [accountViewController release]; accountViewController = nil;
363  * @brief Localization
364  */
365 - (void)_localizeTabViewItemLabels
367         [[tabView_auxiliary tabViewItemWithIdentifier:@"account"] setLabel:AILocalizedString(@"Account",nil)];
368         [[tabView_auxiliary tabViewItemWithIdentifier:@"profile"] setLabel:AILocalizedString(@"Personal",nil)];
369         [[tabView_auxiliary tabViewItemWithIdentifier:@"options"] setLabel:AILocalizedString(@"Options",nil)];
370         [[tabView_auxiliary tabViewItemWithIdentifier:@"privacy"] setLabel:AILocalizedString(@"Privacy",nil)];
371         [[tabView_auxiliary tabViewItemWithIdentifier:@"proxy"] setLabel:AILocalizedString(@"Proxy",nil)];
375 // AIImageViewWithImagePicker Delegate ---------------------------------------------------------------------
376 #pragma mark AIImageViewWithImagePicker Delegate
377 - (void)deleteInImageViewWithImagePicker:(AIImageViewWithImagePicker *)sender
379         [userIconData release]; userIconData = nil;
380         didDeleteUserIcon = YES;
382         //User icon - restore to the default icon
383         [imageView_userIcon setImage:[account userIcon]];
384         
385         //We're now using the global icon
386         [matrix_userIcon selectCellWithTag:0];
389 - (void)imageViewWithImagePicker:(AIImageViewWithImagePicker *)sender didChangeToImageData:(NSData *)imageData
391         [userIconData release];
392         userIconData = [imageData retain];
393         
394         if (!userIconData) {
395                 //If we got a nil user icon, that means the icon was deleted
396                 [self deleteInImageViewWithImagePicker:sender];
397         }
400 - (NSString *)fileNameForImageInImagePicker:(AIImageViewWithImagePicker *)picker
402         return [[account displayName] safeFilenameString];
405 - (NSImage *)emptyPictureImageForImageViewWithImagePicker:(AIImageViewWithImagePicker *)picker
407         return [AIServiceIcons serviceIconForObject:account type:AIServiceIconLarge direction:AIIconNormal];
410 @end