1/****************************************************************************** 2 * $Id: GroupsPrefsController.m 13251 2012-03-13 02:52:11Z livings124 $ 3 * 4 * Copyright (c) 2007-2012 Transmission authors and contributors 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 *****************************************************************************/ 24 25#import "GroupsPrefsController.h" 26#import "GroupsController.h" 27#import "ExpandedPathToPathTransformer.h" 28#import "ExpandedPathToIconTransformer.h" 29#import "NSApplicationAdditions.h" 30 31#define GROUP_TABLE_VIEW_DATA_TYPE @"GroupTableViewDataType" 32 33#define ADD_TAG 0 34#define REMOVE_TAG 1 35 36@interface GroupsPrefsController (Private) 37 38- (void) updateSelectedGroup; 39- (void) refreshCustomLocationWithSingleGroup; 40 41@end 42 43@implementation GroupsPrefsController 44 45- (void) awakeFromNib 46{ 47 [fTableView registerForDraggedTypes: [NSArray arrayWithObject: GROUP_TABLE_VIEW_DATA_TYPE]]; 48 49 [fSelectedColorView addObserver: self forKeyPath: @"color" options: 0 context: NULL]; 50 51 [self updateSelectedGroup]; 52} 53 54- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableview 55{ 56 return [[GroupsController groups] numberOfGroups]; 57} 58 59- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row 60{ 61 GroupsController * groupsController = [GroupsController groups]; 62 NSInteger groupsIndex = [groupsController indexForRow: row]; 63 64 NSString * identifier = [tableColumn identifier]; 65 if ([identifier isEqualToString: @"Color"]) 66 return [groupsController imageForIndex: groupsIndex]; 67 else 68 return [groupsController nameForIndex: groupsIndex]; 69} 70 71- (void) tableViewSelectionDidChange: (NSNotification *) notification 72{ 73 [self updateSelectedGroup]; 74} 75 76- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object change: (NSDictionary *) change context: (void *) context 77{ 78 if (object == fSelectedColorView && [fTableView numberOfSelectedRows] == 1) 79 { 80 NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 81 [[GroupsController groups] setColor: [fSelectedColorView color] forIndex: index]; 82 [fTableView setNeedsDisplay: YES]; 83 } 84} 85 86- (void) controlTextDidEndEditing: (NSNotification *) notification 87{ 88 if ([notification object] == fSelectedColorNameField) 89 { 90 NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 91 [[GroupsController groups] setName: [fSelectedColorNameField stringValue] forIndex: index]; 92 [fTableView setNeedsDisplay: YES]; 93 } 94} 95 96- (BOOL) tableView: (NSTableView *) tableView writeRowsWithIndexes: (NSIndexSet *) rowIndexes toPasteboard: (NSPasteboard *) pboard 97{ 98 [pboard declareTypes: [NSArray arrayWithObject: GROUP_TABLE_VIEW_DATA_TYPE] owner: self]; 99 [pboard setData: [NSKeyedArchiver archivedDataWithRootObject: rowIndexes] forType: GROUP_TABLE_VIEW_DATA_TYPE]; 100 return YES; 101} 102 103- (NSDragOperation) tableView: (NSTableView *) tableView validateDrop: (id <NSDraggingInfo>) info 104 proposedRow: (NSInteger) row proposedDropOperation: (NSTableViewDropOperation) operation 105{ 106 NSPasteboard * pasteboard = [info draggingPasteboard]; 107 if ([[pasteboard types] containsObject: GROUP_TABLE_VIEW_DATA_TYPE]) 108 { 109 [fTableView setDropRow: row dropOperation: NSTableViewDropAbove]; 110 return NSDragOperationGeneric; 111 } 112 113 return NSDragOperationNone; 114} 115 116- (BOOL) tableView: (NSTableView *) tableView acceptDrop: (id <NSDraggingInfo>) info row: (NSInteger) newRow 117 dropOperation: (NSTableViewDropOperation) operation 118{ 119 NSPasteboard * pasteboard = [info draggingPasteboard]; 120 if ([[pasteboard types] containsObject: GROUP_TABLE_VIEW_DATA_TYPE]) 121 { 122 NSIndexSet * indexes = [NSKeyedUnarchiver unarchiveObjectWithData: [pasteboard dataForType: GROUP_TABLE_VIEW_DATA_TYPE]]; 123 NSInteger oldRow = [indexes firstIndex], selectedRow = [fTableView selectedRow]; 124 125 if (oldRow < newRow) 126 newRow--; 127 128 if ([NSApp isOnLionOrBetter]) 129 [fTableView beginUpdates]; 130 131 [[GroupsController groups] moveGroupAtRow: oldRow toRow: newRow]; 132 133 if ([NSApp isOnLionOrBetter]) 134 { 135 [fTableView moveRowAtIndex: oldRow toIndex: newRow]; 136 [fTableView endUpdates]; 137 } 138 else 139 { 140 if (selectedRow == oldRow) 141 selectedRow = newRow; 142 else if (selectedRow > oldRow && selectedRow <= newRow) 143 selectedRow--; 144 else if (selectedRow < oldRow && selectedRow >= newRow) 145 selectedRow++; 146 else; 147 148 [fTableView selectRowIndexes: [NSIndexSet indexSetWithIndex: selectedRow] byExtendingSelection: NO]; 149 [fTableView reloadData]; 150 } 151 } 152 153 return YES; 154} 155 156- (void) addRemoveGroup: (id) sender 157{ 158 if ([NSColorPanel sharedColorPanelExists]) 159 [[NSColorPanel sharedColorPanel] close]; 160 161 NSInteger row; 162 163 switch ([[sender cell] tagForSegment: [sender selectedSegment]]) 164 { 165 case ADD_TAG: 166 if ([NSApp isOnLionOrBetter]) 167 [fTableView beginUpdates]; 168 169 [[GroupsController groups] addNewGroup]; 170 171 row = [fTableView numberOfRows]; 172 173 if ([NSApp isOnLionOrBetter]) 174 { 175 [fTableView insertRowsAtIndexes: [NSIndexSet indexSetWithIndex: row] withAnimation: NSTableViewAnimationSlideUp]; 176 [fTableView endUpdates]; 177 } 178 else 179 [fTableView reloadData]; 180 181 [fTableView selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO]; 182 [fTableView scrollRowToVisible: row]; 183 184 [[fSelectedColorNameField window] makeFirstResponder: fSelectedColorNameField]; 185 186 break; 187 188 case REMOVE_TAG: 189 row = [fTableView selectedRow]; 190 191 192 if ([NSApp isOnLionOrBetter]) 193 [fTableView beginUpdates]; 194 195 [[GroupsController groups] removeGroupWithRowIndex: row]; 196 197 if ([NSApp isOnLionOrBetter]) 198 { 199 [fTableView removeRowsAtIndexes: [NSIndexSet indexSetWithIndex: row] withAnimation: NSTableViewAnimationSlideUp]; 200 [fTableView endUpdates]; 201 } 202 else 203 [fTableView reloadData]; 204 205 if ([fTableView numberOfRows] > 0) 206 { 207 if (row == [fTableView numberOfRows]) 208 --row; 209 [fTableView selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO]; 210 [fTableView scrollRowToVisible: row]; 211 } 212 213 break; 214 } 215 216 [self updateSelectedGroup]; 217} 218 219- (void) customDownloadLocationSheetShow: (id) sender 220{ 221 NSOpenPanel * panel = [NSOpenPanel openPanel]; 222 223 [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")]; 224 [panel setAllowsMultipleSelection: NO]; 225 [panel setCanChooseFiles: NO]; 226 [panel setCanChooseDirectories: YES]; 227 [panel setCanCreateDirectories: YES]; 228 229 [panel beginSheetModalForWindow: [fCustomLocationPopUp window] completionHandler: ^(NSInteger result) { 230 const NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 231 if (result == NSFileHandlingPanelOKButton) 232 { 233 NSString * path = [[[panel URLs] objectAtIndex: 0] path]; 234 [[GroupsController groups] setCustomDownloadLocation: path forIndex: index]; 235 [[GroupsController groups] setUsesCustomDownloadLocation: YES forIndex: index]; 236 } 237 else 238 { 239 if (![[GroupsController groups] customDownloadLocationForIndex: index]) 240 [[GroupsController groups] setUsesCustomDownloadLocation: NO forIndex: index]; 241 } 242 243 [self refreshCustomLocationWithSingleGroup]; 244 245 [fCustomLocationPopUp selectItemAtIndex: 0]; 246 }]; 247} 248 249- (IBAction) toggleUseCustomDownloadLocation: (id) sender 250{ 251 NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 252 if ([fCustomLocationEnableCheck state] == NSOnState) 253 { 254 if ([[GroupsController groups] customDownloadLocationForIndex: index]) 255 [[GroupsController groups] setUsesCustomDownloadLocation: YES forIndex: index]; 256 else 257 [self customDownloadLocationSheetShow: nil]; 258 } 259 else 260 [[GroupsController groups] setUsesCustomDownloadLocation: NO forIndex: index]; 261 262 [fCustomLocationPopUp setEnabled: ([fCustomLocationEnableCheck state] == NSOnState)]; 263} 264 265#pragma mark - 266#pragma mark Rule editor 267 268- (IBAction) toggleUseAutoAssignRules: (id) sender; 269{ 270 NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 271 if ([fAutoAssignRulesEnableCheck state] == NSOnState) 272 { 273 if ([[GroupsController groups] autoAssignRulesForIndex: index]) 274 [[GroupsController groups] setUsesAutoAssignRules: YES forIndex: index]; 275 else 276 [self orderFrontRulesSheet: nil]; 277 } 278 else 279 [[GroupsController groups] setUsesAutoAssignRules: NO forIndex: index]; 280 281 [fAutoAssignRulesEditButton setEnabled: [fAutoAssignRulesEnableCheck state] == NSOnState]; 282} 283 284- (IBAction) orderFrontRulesSheet: (id) sender; 285{ 286 if (!fGroupRulesSheetWindow) 287 [NSBundle loadNibNamed: @"GroupRules" owner: self]; 288 289 NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 290 NSPredicate *predicate = [[GroupsController groups] autoAssignRulesForIndex: index]; 291 [fRuleEditor setObjectValue: predicate]; 292 293 if ([fRuleEditor numberOfRows] == 0) 294 [fRuleEditor addRow: nil]; 295 296 [NSApp beginSheet: fGroupRulesSheetWindow modalForWindow: [fTableView window] modalDelegate: nil didEndSelector: NULL 297 contextInfo: NULL]; 298} 299 300- (IBAction) cancelRules: (id) sender; 301{ 302 [fGroupRulesSheetWindow orderOut: nil]; 303 [NSApp endSheet: fGroupRulesSheetWindow]; 304 305 NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 306 if (![[GroupsController groups] autoAssignRulesForIndex: index]) 307 { 308 [[GroupsController groups] setUsesAutoAssignRules: NO forIndex: index]; 309 [fAutoAssignRulesEnableCheck setState: NO]; 310 [fAutoAssignRulesEditButton setEnabled: NO]; 311 } 312} 313 314- (IBAction) saveRules: (id) sender; 315{ 316 [fGroupRulesSheetWindow orderOut: nil]; 317 [NSApp endSheet: fGroupRulesSheetWindow]; 318 319 NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 320 [[GroupsController groups] setUsesAutoAssignRules: YES forIndex: index]; 321 322 NSPredicate * predicate = [fRuleEditor objectValue]; 323 [[GroupsController groups] setAutoAssignRules: predicate forIndex: index]; 324 325 [fAutoAssignRulesEnableCheck setState: [[GroupsController groups] usesAutoAssignRulesForIndex: index]]; 326 [fAutoAssignRulesEditButton setEnabled: [fAutoAssignRulesEnableCheck state] == NSOnState]; 327} 328 329- (void) ruleEditorRowsDidChange: (NSNotification *) notification 330{ 331 const CGFloat heightDifference = [fRuleEditor numberOfRows] * [fRuleEditor rowHeight] - [fRuleEditor frame].size.height; 332 NSRect windowFrame = [fRuleEditor window].frame; 333 windowFrame.size.height += heightDifference; 334 windowFrame.origin.y -= heightDifference; 335 336 [fRuleEditor.window setFrame: windowFrame display: YES animate: YES]; 337} 338 339@end 340 341@implementation GroupsPrefsController (Private) 342 343- (void) updateSelectedGroup 344{ 345 [fAddRemoveControl setEnabled: [fTableView numberOfSelectedRows] > 0 forSegment: REMOVE_TAG]; 346 if ([fTableView numberOfSelectedRows] == 1) 347 { 348 const NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 349 [fSelectedColorView setColor: [[GroupsController groups] colorForIndex: index]]; 350 [fSelectedColorView setEnabled: YES]; 351 [fSelectedColorNameField setStringValue: [[GroupsController groups] nameForIndex: index]]; 352 [fSelectedColorNameField setEnabled: YES]; 353 354 [self refreshCustomLocationWithSingleGroup]; 355 356 [fAutoAssignRulesEnableCheck setState: [[GroupsController groups] usesAutoAssignRulesForIndex: index]]; 357 [fAutoAssignRulesEnableCheck setEnabled: YES]; 358 [fAutoAssignRulesEditButton setEnabled: ([fAutoAssignRulesEnableCheck state] == NSOnState)]; 359 } 360 else 361 { 362 [fSelectedColorView setColor: [NSColor whiteColor]]; 363 [fSelectedColorView setEnabled: NO]; 364 [fSelectedColorNameField setStringValue: @""]; 365 [fSelectedColorNameField setEnabled: NO]; 366 [fCustomLocationEnableCheck setEnabled: NO]; 367 [fCustomLocationPopUp setEnabled: NO]; 368 [fAutoAssignRulesEnableCheck setEnabled: NO]; 369 [fAutoAssignRulesEditButton setEnabled: NO]; 370 } 371} 372 373- (void) refreshCustomLocationWithSingleGroup 374{ 375 const NSInteger index = [[GroupsController groups] indexForRow: [fTableView selectedRow]]; 376 377 const BOOL hasCustomLocation = [[GroupsController groups] usesCustomDownloadLocationForIndex: index]; 378 [fCustomLocationEnableCheck setState: hasCustomLocation]; 379 [fCustomLocationEnableCheck setEnabled: YES]; 380 [fCustomLocationPopUp setEnabled: hasCustomLocation]; 381 382 NSString * location = [[GroupsController groups] customDownloadLocationForIndex: index]; 383 if (location) 384 { 385 ExpandedPathToPathTransformer * pathTransformer = [[[ExpandedPathToPathTransformer alloc] init] autorelease]; 386 [[fCustomLocationPopUp itemAtIndex: 0] setTitle: [pathTransformer transformedValue: location]]; 387 ExpandedPathToIconTransformer * iconTransformer = [[[ExpandedPathToIconTransformer alloc] init] autorelease]; 388 [[fCustomLocationPopUp itemAtIndex: 0] setImage: [iconTransformer transformedValue: location]]; 389 } 390 else 391 { 392 [[fCustomLocationPopUp itemAtIndex: 0] setTitle: @""]; 393 [[fCustomLocationPopUp itemAtIndex: 0] setImage: nil]; 394 } 395} 396 397@end 398