Files
cappuccino/Tools/XcodeCapp/XcodeCapp/XCCMainController.m
T

512 lines
17 KiB
Objective-C

//
// MainWindowController.m
// XcodeCapp
//
// Created by Alexandre Wilhelm on 5/20/15.
// Copyright (c) 2015 cappuccino-project. All rights reserved.
//
#import "NSMutableArray+moveIndexes.h"
#import "XCCMainController.h"
#import "XCCCappuccinoProject.h"
#import "XCCCappuccinoProjectController.h"
#import "XCCCappuccinoProjectControllerDataView.h"
#import "XCCUserDefaults.h"
#import "XCCOperationsViewController.h"
#import "XCCErrorsViewController.h"
#import "XCCSettingsViewController.h"
#import "XCCWelcomeView.h"
@implementation XCCMainController
#pragma mark - Initialization
- (void)windowDidLoad
{
[self showWindow:self];
self->welcomeViewMask.mainXcodeCappController = self;
[self->welcomeViewMask showLoading:YES];
[self _showWelcomeView:YES];
[self _showMaskingView:YES];
NSTabViewItem *itemConfiguration = [[NSTabViewItem alloc] initWithIdentifier:@"configuration"];
[itemConfiguration setView:self.settingsViewController.view];
[self->tabViewProject addTabViewItem:itemConfiguration];
NSTabViewItem *itemErrors = [[NSTabViewItem alloc] initWithIdentifier:@"errors"];
[itemErrors setView:self.errorsViewController.view];
[self->tabViewProject addTabViewItem:itemErrors];
NSTabViewItem *itemOperations = [[NSTabViewItem alloc] initWithIdentifier:@"operations"];
[itemOperations setView:self.operationsViewController.view];
[self->tabViewProject addTabViewItem:itemOperations];
[self _setTextColor:[NSColor controlTextColor] forButton:self->buttonSelectConfigurationTab];
[self _setTextColor:[NSColor controlTextColor] forButton:self->buttonSelectErrorsTab];
[self _setTextColor:[NSColor controlTextColor] forButton:self->buttonSelectOperationsTab];
[self updateSelectedTab:self->buttonSelectConfigurationTab];
[self->projectTableView registerForDraggedTypes:@[@"projects", NSFilenamesPboardType]];
[self->projectTableView setAllowsEmptySelection:YES];
[self->welcomeViewMask showLoading:NO];
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
[self _restoreManagedProjects];
[self _restoreLastSelectedProject];
}
- (void)_setTextColor:(NSColor *)color forButton:(NSButton *)button
{
NSMutableParagraphStyle *paragraphStyle= [NSMutableParagraphStyle new];
[paragraphStyle setAlignment:NSCenterTextAlignment];
NSDictionary *attrs = @{NSFontAttributeName: [NSFont systemFontOfSize:11],
NSForegroundColorAttributeName: color,
NSParagraphStyleAttributeName: paragraphStyle};
button.attributedTitle = [[NSMutableAttributedString alloc] initWithString:button.title attributes:attrs];
}
#pragma mark - Private Utilities
- (void)_showMaskingView:(BOOL)shouldShow
{
if (shouldShow)
{
if (self->maskingView.superview)
return;
self->projectViewContainer.hidden = YES;
self->maskingView.frame = [[self->splitView subviews][1] bounds];
[[self->splitView subviews][1] addSubview:self->maskingView positioned:NSWindowAbove relativeTo:nil];
}
else
{
if (!self->maskingView.superview)
return;
self->projectViewContainer.hidden = NO;
[self->maskingView removeFromSuperview];
}
}
- (void)_showWelcomeView:(BOOL)shouldShow
{
if (shouldShow)
{
if (self->welcomeViewMask.superview)
return;
self->splitView.hidden = YES;
self->welcomeViewMask.frame = [self->splitView.superview bounds];
[self->splitView.superview addSubview:self->welcomeViewMask positioned:NSWindowAbove relativeTo:nil];
}
else
{
if (!self->welcomeViewMask.superview)
return;
self->splitView.hidden = NO;
[self->welcomeViewMask removeFromSuperview];
}
}
- (void)_restoreLastSelectedProject
{
NSLog(@"Start : selecting last selected project");
NSString *lastSelectedProjectPath = [[NSUserDefaults standardUserDefaults] valueForKey:XCCUserDefaultsSelectedProjectPath];
[self _selectCappuccinoProjectControllerWithPath:lastSelectedProjectPath];
NSLog(@"Stop : selecting last selected project");
}
- (void)_selectCappuccinoProjectControllerWithPath:(NSString*)aCappuccinoProjectPath
{
NSInteger indexToSelect = 0;
if ([aCappuccinoProjectPath length])
{
for (XCCCappuccinoProjectController *controller in self.cappuccinoProjectControllers)
{
if ([controller.cappuccinoProject.projectPath isEqualToString:aCappuccinoProjectPath])
{
indexToSelect = [self.cappuccinoProjectControllers indexOfObject:controller];
break;
}
}
[self->projectTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:indexToSelect] byExtendingSelection:NO];
}
}
- (void)_restoreManagedProjects
{
NSLog(@"restore managed projects");
self.cappuccinoProjectControllers = [@[] mutableCopy];
NSArray *projectHistory = [[NSUserDefaults standardUserDefaults] arrayForKey:XCCUserDefaultsManagedProjects];
NSFileManager *fm = [NSFileManager defaultManager];
NSMutableArray *missingProjects = [@[] mutableCopy];
for (NSString *path in projectHistory)
{
NSLog(@"Checking previously managed project at path: %@", path);
if (![fm fileExistsAtPath:path isDirectory:nil])
{
[missingProjects addObject:path];
NSLog(@"Not found: project at path: %@", path);
continue;
}
XCCCappuccinoProjectController *cappuccinoProjectController = [[XCCCappuccinoProjectController alloc] initWithPath:path controller:self];
[self.cappuccinoProjectControllers addObject:cappuccinoProjectController];
}
[self _reloadProjectsList];
if (missingProjects.count)
{
NSRunAlertPanel(@"Missing Projects",
@"Some managed projects could not be found and have been removed:\n\n"
@"%@\n\n",
@"OK",
nil,
nil,
[missingProjects componentsJoinedByString:@", "]);
}
[self _saveManagedProjectsToUserDefaults];
for (XCCCappuccinoProjectController *controller in self.cappuccinoProjectControllers)
{
if (controller.cappuccinoProject.previousSavedStatus)
[controller switchProjectListeningStatus:self];
}
NSLog(@"managed projects restored");
}
- (void)_saveSelectedProject
{
NSString *path = self.currentCappuccinoProjectController.cappuccinoProject.projectPath;
if (!path)
path = @"";
[[NSUserDefaults standardUserDefaults] setObject:path forKey:XCCUserDefaultsSelectedProjectPath];
}
- (void)_saveManagedProjectsToUserDefaults
{
NSMutableArray *historyProjectPaths = [@[] mutableCopy];
for (XCCCappuccinoProjectController *controller in self.cappuccinoProjectControllers)
[historyProjectPaths addObject:controller.cappuccinoProject.projectPath];
[[NSUserDefaults standardUserDefaults] setObject:historyProjectPaths forKey:XCCUserDefaultsManagedProjects];
}
- (void)_reloadProjectsList
{
[self->projectTableView reloadData];
if (self.cappuccinoProjectControllers.count == 0)
[self _showWelcomeView:YES];
else
[self _showWelcomeView:NO];
}
#pragma mark - Public Utilities
- (void)manageCappuccinoProjectControllerForPath:(NSString *)path
{
if ([[self.cappuccinoProjectControllers filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"cappuccinoProject.projectPath == %@", path]] count])
{
NSRunAlertPanel(@"This project is already managed.", @"Please remove the other project or use the reset button.", @"OK", nil, nil, nil);
return;
}
XCCCappuccinoProjectController *controller = [[XCCCappuccinoProjectController alloc] initWithPath:path controller:self];
[self.cappuccinoProjectControllers addObject:controller];
NSInteger index = [self.cappuccinoProjectControllers indexOfObject:controller];
[self _reloadProjectsList];
[self->projectTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
[self->projectTableView scrollRowToVisible:index];
[self _saveManagedProjectsToUserDefaults];
[controller switchProjectListeningStatus:self];
}
- (void)unmanageCappuccinoProjectController:(XCCCappuccinoProjectController*)aController
{
NSInteger selectedCappuccinoProject = [self.cappuccinoProjectControllers indexOfObject:aController];
if (selectedCappuccinoProject == -1)
return;
[self->projectTableView deselectRow:selectedCappuccinoProject];
[aController cleanUpBeforeDeletion];
[self.cappuccinoProjectControllers removeObjectAtIndex:selectedCappuccinoProject];
[self _reloadProjectsList];
[self _saveManagedProjectsToUserDefaults];
}
- (void)notifyCappuccinoControllersApplicationIsClosing
{
[self.cappuccinoProjectControllers makeObjectsPerformSelector:@selector(applicationIsClosing)];
}
- (void)reloadTotalNumberOfErrors
{
int totalErrors = 0;
for (XCCCappuccinoProjectController *controller in self.cappuccinoProjectControllers)
totalErrors += controller.errors.count;
[self willChangeValueForKey:@"totalNumberOfErrors"];
self.totalNumberOfErrors = totalErrors;
[self didChangeValueForKey:@"totalNumberOfErrors"];
if (self.totalNumberOfErrors)
[[[NSApplication sharedApplication] dockTile] setBadgeLabel:@(self.totalNumberOfErrors).description];
else
[[[NSApplication sharedApplication] dockTile] setBadgeLabel:nil];
}
#pragma mark - Actions
- (IBAction)cleanAllErrors:(id)aSender
{
[self.cappuccinoProjectControllers makeObjectsPerformSelector:@selector(cleanProjectErrors:) withObject:self];
}
- (IBAction)addProject:(id)aSender
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.title = @"Add a new Cappuccino Project to XcodeCapp";
openPanel.canCreateDirectories = YES;
openPanel.canChooseDirectories = YES;
openPanel.canChooseFiles = NO;
if ([openPanel runModal] != NSFileHandlingPanelOKButton)
return;
NSString *projectPath = [[openPanel.URLs[0] path] stringByStandardizingPath];
[self manageCappuccinoProjectControllerForPath:projectPath];
}
- (IBAction)removeProject:(id)aSender
{
NSInteger selectedCappuccinoProject = [self->projectTableView selectedRow];
if (selectedCappuccinoProject == -1)
return;
[self unmanageCappuccinoProjectController:(self.cappuccinoProjectControllers)[selectedCappuccinoProject]];
}
- (IBAction)updateSelectedTab:(NSButton *)sender
{
self->buttonSelectConfigurationTab.state = NSOffState;
self->buttonSelectErrorsTab.state = NSOffState;
self->buttonSelectOperationsTab.state = NSOffState;
[self _setTextColor:[NSColor controlTextColor] forButton:self->buttonSelectConfigurationTab];
[self _setTextColor:[NSColor controlTextColor] forButton:self->buttonSelectErrorsTab];
[self _setTextColor:[NSColor controlTextColor] forButton:self->buttonSelectOperationsTab];
sender.state = NSOnState;
[self _setTextColor:[NSColor colorWithCalibratedRed:107.0/255.0 green:148.0/255.0 blue:236.0/255.0 alpha:1.0] forButton:sender];
if (sender == self->buttonSelectConfigurationTab)
{
[_operationsViewController setSelected:NO];
[_errorsViewController setSelected:NO];
[self->tabViewProject selectTabViewItemAtIndex:0];
}
else if (sender == self->buttonSelectErrorsTab)
{
[_operationsViewController setSelected:NO];
[_errorsViewController setSelected:YES];
[self->tabViewProject selectTabViewItemAtIndex:1];
[_errorsViewController reload];
}
else if (sender == self->buttonSelectOperationsTab)
{
[_operationsViewController setSelected:YES];
[_errorsViewController setSelected:NO];
[self->tabViewProject selectTabViewItemAtIndex:2];
[_operationsViewController reload];
}
}
#pragma mark - NSUserNotificationCenter delegate
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
{
NSDictionary *userInfo = [notification userInfo];
[center removeDeliveredNotification:notification];
[self _selectCappuccinoProjectControllerWithPath:userInfo[@"cappuccinoProjectPath"]];
[self updateSelectedTab:self->buttonSelectErrorsTab];
[self.errorsViewController selectItem:userInfo[@"sourcePath"]];
}
#pragma mark - SplitView delegate
- (CGFloat)splitView:(NSSplitView *)aSplitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex
{
return 300.0;
}
- (CGFloat)splitView:(NSSplitView *)aSplitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex
{
return 350.0;
}
- (void)splitView:(NSSplitView *)aSplitView resizeSubviewsWithOldSize:(NSSize)oldSize
{
[aSplitView adjustSubviews];
[aSplitView setPosition:((NSView *)splitView.subviews.firstObject).frame.size.width ofDividerAtIndex:0];
}
#pragma mark - TableView DataSource
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [self.cappuccinoProjectControllers count];
}
#pragma mark - TableView Delegates
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
XCCCappuccinoProjectControllerDataView *dataView = [tableView makeViewWithIdentifier:@"MainCell" owner:nil];
XCCCappuccinoProjectController *cappuccinoProjectController = (self.cappuccinoProjectControllers)[row];
dataView.controller = cappuccinoProjectController;
return dataView;
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
NSInteger selectedIndex = [self->projectTableView selectedRow];
self.currentCappuccinoProjectController = (selectedIndex == -1) ? nil : (self.cappuccinoProjectControllers)[selectedIndex];
self.operationsViewController.cappuccinoProjectController = self.currentCappuccinoProjectController;
self.errorsViewController.cappuccinoProjectController = self.currentCappuccinoProjectController;
self.settingsViewController.cappuccinoProjectController = self.currentCappuccinoProjectController;
[self.operationsViewController reload];
[self.errorsViewController reload];
[self.settingsViewController reload];
[self _showMaskingView:(selectedIndex == -1)];
[self _saveSelectedProject];
}
- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowsIndexes toPasteboard:(NSPasteboard*)pasteboard
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowsIndexes];
[pasteboard declareTypes:@[@"projects"] owner:self];
[pasteboard setData:data forType:@"projects"];
return YES;
}
- (NSDragOperation)tableView:(NSTableView*)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation
{
NSPasteboard *pboard = [info draggingPasteboard];
if ([pboard.types containsObject:(NSString *)NSFilenamesPboardType])
{
NSArray *draggedFiles = [pboard propertyListForType:(NSString *)NSFilenamesPboardType];
for (NSString *file in draggedFiles)
{
BOOL isDir;
[[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDir];
if (!isDir)
return NSDragOperationNone;
}
return NSDragOperationCopy;
}
else if ([pboard.types containsObject:@"projects"])
{
if (operation == NSTableViewDropOn)
return NSDragOperationNone;
return NSDragOperationMove;
}
else
{
return NSDragOperationNone;
}
}
- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation
{
NSPasteboard *pboard = [info draggingPasteboard];
if ([pboard.types containsObject:(NSString *)NSFilenamesPboardType])
{
NSArray *draggedFolders = [pboard propertyListForType:(NSString *)NSFilenamesPboardType];
for (NSString *folder in draggedFolders)
[self manageCappuccinoProjectControllerForPath:folder];
return YES;
}
else if ([pboard.types containsObject:@"projects"])
{
NSData *rowData = [pboard dataForType:@"projects"];
NSIndexSet *rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData];
[self.cappuccinoProjectControllers moveIndexes:rowIndexes toIndex:row];
[self _reloadProjectsList];
[self _saveManagedProjectsToUserDefaults];
return YES;
}
else
{
return NO;
}
}
@end