nkeynes@964: /** nkeynes@964: * $Id$ nkeynes@964: * nkeynes@964: * Construct and manage the controller configuration pane nkeynes@964: * nkeynes@964: * Copyright (c) 2008 Nathan Keynes. nkeynes@964: * nkeynes@964: * This program is free software; you can redistribute it and/or modify nkeynes@964: * it under the terms of the GNU General Public License as published by nkeynes@964: * the Free Software Foundation; either version 2 of the License, or nkeynes@964: * (at your option) any later version. nkeynes@964: * nkeynes@964: * This program is distributed in the hope that it will be useful, nkeynes@964: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@964: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@964: * GNU General Public License for more details. nkeynes@964: */ nkeynes@964: nkeynes@964: #include "cocoaui.h" nkeynes@964: #include "config.h" nkeynes@1041: #include "lxpaths.h" nkeynes@964: #include "display.h" nkeynes@964: #include "maple/maple.h" nkeynes@1034: #include "vmu/vmulist.h" nkeynes@964: nkeynes@1297: #include nkeynes@964: nkeynes@1034: #define FIRST_SECONDARY_DEVICE MAPLE_PORTS nkeynes@1034: nkeynes@1034: #define FIRST_VMU_TAG 0x1000 nkeynes@1034: #define LOAD_VMU_TAG -1 nkeynes@1034: #define CREATE_VMU_TAG -2 nkeynes@964: nkeynes@1072: #define LABEL_WIDTH 85 nkeynes@964: nkeynes@1072: nkeynes@1034: static gboolean cocoa_config_vmulist_hook(vmulist_change_type_t type, int idx, void *data); nkeynes@964: /*************************** Top-level controller pane ***********************/ nkeynes@1034: static NSButton *addRadioButton( int port, int sub, int x, int y, id parent ) nkeynes@1034: { nkeynes@1034: char buf[16]; nkeynes@1034: nkeynes@1034: if( sub == 0 ) { nkeynes@1034: snprintf( buf, sizeof(buf), _("Port %c."), 'A'+port ); nkeynes@1034: } else { nkeynes@1034: snprintf( buf, sizeof(buf), _("VMU %d."), sub ); nkeynes@1034: } nkeynes@1034: nkeynes@1034: NSButton *radio = [[NSButton alloc] initWithFrame: NSMakeRect( x, y, 60, TEXT_HEIGHT )]; nkeynes@1034: [radio setTitle: [NSString stringWithUTF8String: buf]]; nkeynes@1034: [radio setTag: MAPLE_DEVID(port,sub) ]; nkeynes@1034: [radio setButtonType: NSRadioButton]; nkeynes@1034: [radio setAlignment: NSRightTextAlignment]; nkeynes@1034: [radio setTarget: parent]; nkeynes@1034: [radio setAction: @selector(radioChanged:)]; nkeynes@1034: [radio setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)]; nkeynes@1034: [parent addSubview: radio]; nkeynes@1034: return radio; nkeynes@1034: } nkeynes@1034: nkeynes@1034: static void setDevicePopupSelection( NSPopUpButton *popup, maple_device_t device ) nkeynes@1034: { nkeynes@1034: if( device == NULL ) { nkeynes@1034: [popup selectItemAtIndex: 0]; nkeynes@1034: } else if( MAPLE_IS_VMU(device) ) { nkeynes@1034: int idx = vmulist_get_index_by_filename( MAPLE_VMU_NAME(device) ); nkeynes@1034: if( idx == -1 ) { nkeynes@1034: [popup selectItemAtIndex: 0]; nkeynes@1034: } else { nkeynes@1034: [popup selectItemWithTag: FIRST_VMU_TAG + idx]; nkeynes@1034: } nkeynes@1034: } else { nkeynes@1034: const struct maple_device_class **devices = maple_get_device_classes(); nkeynes@1034: int i; nkeynes@1034: for( i=0; devices[i] != NULL; i++ ) { nkeynes@1034: if( devices[i] == device->device_class ) { nkeynes@1034: [popup selectItemWithTag: i+1]; nkeynes@1034: return; nkeynes@1034: } nkeynes@1034: } nkeynes@1034: // Should never get here, but if so... nkeynes@1034: [popup selectItemAtIndex: 0]; nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: static void buildDevicePopupMenu( NSPopUpButton *popup, maple_device_t device, BOOL primary ) nkeynes@1034: { nkeynes@1034: int j; nkeynes@1034: const struct maple_device_class **devices = maple_get_device_classes(); nkeynes@1034: nkeynes@1034: [popup removeAllItems]; nkeynes@1034: [popup addItemWithTitle: NS_("")]; nkeynes@1034: [[popup itemAtIndex: 0] setTag: 0]; nkeynes@1034: for( j=0; devices[j] != NULL; j++ ) { nkeynes@1034: int isPrimaryDevice = devices[j]->flags & MAPLE_TYPE_PRIMARY; nkeynes@1034: if( primary ? isPrimaryDevice : (!isPrimaryDevice && !MAPLE_IS_VMU_CLASS(devices[j])) ) { nkeynes@1034: [popup addItemWithTitle: [NSString stringWithUTF8String: devices[j]->name]]; nkeynes@1034: if( device != NULL && device->device_class == devices[j] ) { nkeynes@1034: [popup selectItemAtIndex: ([popup numberOfItems]-1)]; nkeynes@1034: } nkeynes@1034: [[popup itemAtIndex: ([popup numberOfItems]-1)] setTag: (j+1)]; nkeynes@1034: } nkeynes@1034: } nkeynes@1034: nkeynes@1034: if( !primary ) { nkeynes@1034: BOOL vmu_selected = NO; nkeynes@1072: const char *vmu_name = NULL; nkeynes@1034: if( device != NULL && MAPLE_IS_VMU(device) ) { nkeynes@1034: vmu_name = MAPLE_VMU_NAME(device); nkeynes@1072: if( vmu_name == NULL ) { nkeynes@1072: device = NULL; nkeynes@1072: } else { nkeynes@1072: vmu_selected = YES; nkeynes@1072: } nkeynes@1034: } nkeynes@1034: if( [popup numberOfItems] > 0 ) { nkeynes@1034: [[popup menu] addItem: [NSMenuItem separatorItem]]; nkeynes@1034: } nkeynes@1034: nkeynes@1034: unsigned int vmu_count = vmulist_get_size(); nkeynes@1034: for( j=0; jdevice_class); nkeynes@1034: for( j=1; j<=MAPLE_USER_SLOTS; j++ ) { nkeynes@1034: radio[MAPLE_DEVID(i,j)] = addRadioButton(i, j, TEXT_GAP*2, y, self); nkeynes@1034: popup[MAPLE_DEVID(i,j)] = addDevicePopup(i, j, 60 + TEXT_GAP*2, y, maple_get_device(i,j), NO, self); nkeynes@1034: y -= (TEXT_HEIGHT+TEXT_GAP); nkeynes@1034: if( j > max ) { nkeynes@1034: [radio[MAPLE_DEVID(i,j)] setEnabled: NO]; nkeynes@1034: [popup[MAPLE_DEVID(i,j)] setEnabled: NO]; nkeynes@964: } nkeynes@964: } nkeynes@964: } nkeynes@964: nkeynes@964: [radio[0] setState: NSOnState]; nkeynes@1034: nkeynes@1034: register_vmulist_change_hook(cocoa_config_vmulist_hook, self); nkeynes@964: return self; nkeynes@964: } nkeynes@964: } nkeynes@1034: - (void)dealloc nkeynes@1034: { nkeynes@1034: unregister_vmulist_change_hook(cocoa_config_vmulist_hook,self); nkeynes@1034: [super dealloc]; nkeynes@1034: } nkeynes@1034: - (void)vmulistChanged: (id)sender nkeynes@1034: { nkeynes@1034: int i; nkeynes@1034: for( i=FIRST_SECONDARY_DEVICE; i= FIRST_VMU_TAG ) { nkeynes@1034: vmu_filename = vmulist_get_filename( new_device_idx - FIRST_VMU_TAG ); nkeynes@1034: new_device_class = &vmu_class; nkeynes@1034: } else if( new_device_idx > 0) { nkeynes@1034: new_device_class = maple_get_device_classes()[new_device_idx-1]; nkeynes@964: } nkeynes@1034: nkeynes@1034: if( current == NULL ? new_device_class == NULL : nkeynes@1034: (current->device_class == new_device_class && nkeynes@1034: (!MAPLE_IS_VMU(current) || MAPLE_VMU_HAS_NAME(current, vmu_filename))) ) { nkeynes@964: // No change nkeynes@964: [key_bindings setDevice: current]; nkeynes@964: return; nkeynes@964: } nkeynes@964: if( current != NULL && current->device_class == &controller_class ) { nkeynes@1034: save_controller[tag] = current->clone(current); nkeynes@964: } nkeynes@964: if( new_device_class == NULL ) { nkeynes@1034: maple_detach_device(port,slot); nkeynes@1043: if( slot == 0 ) { nkeynes@1043: /* If we detached the top-level dev, any children are automatically detached */ nkeynes@1043: for( i=1; i<=MAPLE_USER_SLOTS; i++ ) { nkeynes@1043: [popup[MAPLE_DEVID(port,i)] selectItemWithTag: 0]; nkeynes@1043: } nkeynes@1043: } nkeynes@964: } else { nkeynes@1034: if( new_device_class == &controller_class && save_controller[tag] != NULL ) { nkeynes@1034: new_device = save_controller[tag]; nkeynes@1034: save_controller[tag] = NULL; nkeynes@964: } else { nkeynes@964: new_device = maple_new_device( new_device_class->name ); nkeynes@964: } nkeynes@1034: if( MAPLE_IS_VMU(new_device) ) { nkeynes@1043: /* Remove the VMU from any other attachment point */ nkeynes@1043: for( i=0; i