Search
lxdream.org :: lxdream/src/cocoaui/cocoa_ctrl.m
lxdream 0.9.1
released Jun 29
Download Now
filename src/cocoaui/cocoa_ctrl.m
changeset 1069:7e2b65496762
prev1058:37f6fdc738e7
next1072:d82e04e6d497
author nkeynes
date Thu Jul 09 08:50:41 2009 +1000 (14 years ago)
permissions -rw-r--r--
last change Fix type-mismatch warning on resignFirstResponder
file annotate diff log raw
nkeynes@964
     1
/**
nkeynes@964
     2
 * $Id$
nkeynes@964
     3
 *
nkeynes@964
     4
 * Construct and manage the controller configuration pane
nkeynes@964
     5
 *
nkeynes@964
     6
 * Copyright (c) 2008 Nathan Keynes.
nkeynes@964
     7
 *
nkeynes@964
     8
 * This program is free software; you can redistribute it and/or modify
nkeynes@964
     9
 * it under the terms of the GNU General Public License as published by
nkeynes@964
    10
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@964
    11
 * (at your option) any later version.
nkeynes@964
    12
 *
nkeynes@964
    13
 * This program is distributed in the hope that it will be useful,
nkeynes@964
    14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@964
    15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@964
    16
 * GNU General Public License for more details.
nkeynes@964
    17
 */
nkeynes@964
    18
nkeynes@964
    19
#include "cocoaui.h"
nkeynes@964
    20
#include "config.h"
nkeynes@1041
    21
#include "lxpaths.h"
nkeynes@964
    22
#include "display.h"
nkeynes@964
    23
#include "maple/maple.h"
nkeynes@1034
    24
#include "vmu/vmulist.h"
nkeynes@964
    25
nkeynes@964
    26
#include <glib/gstrfuncs.h>
nkeynes@964
    27
nkeynes@1034
    28
#define FIRST_SECONDARY_DEVICE MAPLE_PORTS
nkeynes@1034
    29
nkeynes@1034
    30
#define FIRST_VMU_TAG 0x1000
nkeynes@1034
    31
#define LOAD_VMU_TAG -1
nkeynes@1034
    32
#define CREATE_VMU_TAG -2
nkeynes@964
    33
nkeynes@964
    34
#define KEYBINDING_SIZE 110
nkeynes@964
    35
nkeynes@964
    36
static void cocoa_config_keysym_hook(void *data, const gchar *keysym);
nkeynes@1034
    37
static gboolean cocoa_config_vmulist_hook(vmulist_change_type_t type, int idx, void *data);
nkeynes@964
    38
nkeynes@964
    39
@interface KeyBindingEditor (Private)
nkeynes@964
    40
- (void)updateKeysym: (const gchar *)sym;
nkeynes@964
    41
@end
nkeynes@964
    42
nkeynes@964
    43
@implementation KeyBindingEditor
nkeynes@964
    44
- (id)init
nkeynes@964
    45
{
nkeynes@964
    46
    self = [super init];
nkeynes@964
    47
    isPrimed = NO;
nkeynes@964
    48
    lastValue = nil;
nkeynes@964
    49
    [self setFieldEditor: YES];
nkeynes@964
    50
    [self setEditable: FALSE];
nkeynes@964
    51
    return self;
nkeynes@964
    52
}
nkeynes@964
    53
- (void)dealloc
nkeynes@964
    54
{
nkeynes@964
    55
    if( lastValue != nil ) {
nkeynes@964
    56
        [lastValue release];
nkeynes@964
    57
        lastValue = nil;
nkeynes@964
    58
    }
nkeynes@964
    59
    [super dealloc];
nkeynes@964
    60
}
nkeynes@964
    61
- (void)setPrimed: (BOOL)primed
nkeynes@964
    62
{
nkeynes@964
    63
    if( primed != isPrimed ) {
nkeynes@964
    64
        isPrimed = primed;
nkeynes@964
    65
        if( primed ) {
nkeynes@964
    66
            lastValue = [[NSString stringWithString: [self string]] retain];
nkeynes@964
    67
            [self setString: @"<press key>"];
nkeynes@964
    68
            input_set_keysym_hook(cocoa_config_keysym_hook, self);            
nkeynes@964
    69
        } else {
nkeynes@964
    70
            [lastValue release];
nkeynes@964
    71
            lastValue = nil;
nkeynes@964
    72
            input_set_keysym_hook(NULL,NULL);
nkeynes@964
    73
        }
nkeynes@964
    74
    }
nkeynes@964
    75
}
nkeynes@1069
    76
- (BOOL)resignFirstResponder
nkeynes@964
    77
{
nkeynes@964
    78
    if( isPrimed ) {
nkeynes@964
    79
        [self setString: lastValue];
nkeynes@964
    80
        [self setPrimed: NO];
nkeynes@964
    81
    }
nkeynes@1069
    82
    return [super resignFirstResponder];
nkeynes@964
    83
}
nkeynes@964
    84
- (void)fireBindingChanged
nkeynes@964
    85
{
nkeynes@964
    86
    id delegate = [self delegate];
nkeynes@964
    87
    if( delegate != nil && [delegate respondsToSelector:@selector(textDidChange:)] ) {
nkeynes@964
    88
        [delegate textDidChange: [NSNotification notificationWithName: NSTextDidChangeNotification object: self]];
nkeynes@964
    89
    }
nkeynes@964
    90
}
nkeynes@964
    91
        
nkeynes@964
    92
- (void)updateKeysym: (const gchar *)sym
nkeynes@964
    93
{
nkeynes@964
    94
    if( sym != NULL ) {
nkeynes@964
    95
        [self setString: [NSString stringWithCString: sym]];
nkeynes@964
    96
        [self setPrimed: NO];
nkeynes@964
    97
        [self fireBindingChanged];
nkeynes@964
    98
    }
nkeynes@964
    99
}
nkeynes@964
   100
- (void)updateMousesym: (int)button
nkeynes@964
   101
{
nkeynes@964
   102
    gchar *keysym = input_keycode_to_keysym( &system_mouse_driver, (button+1) );
nkeynes@964
   103
    if( keysym != NULL ) {
nkeynes@964
   104
        [self updateKeysym: keysym ];
nkeynes@964
   105
        g_free(keysym);
nkeynes@964
   106
    }
nkeynes@964
   107
}
nkeynes@964
   108
- (void)keyPressed: (int)keycode
nkeynes@964
   109
{
nkeynes@964
   110
    gchar *keysym = input_keycode_to_keysym(NULL, keycode);
nkeynes@964
   111
    if( keysym != NULL ) {
nkeynes@964
   112
        [self updateKeysym: keysym];
nkeynes@964
   113
        g_free(keysym);
nkeynes@964
   114
    }
nkeynes@964
   115
}
nkeynes@964
   116
- (void)insertText:(id)string
nkeynes@964
   117
{
nkeynes@964
   118
    // Do nothing
nkeynes@964
   119
}
nkeynes@964
   120
- (void)mouseDown: (NSEvent *)event
nkeynes@964
   121
{
nkeynes@964
   122
    if( isPrimed ) {
nkeynes@964
   123
        [self updateMousesym: 0];
nkeynes@964
   124
    } else {
nkeynes@964
   125
        [self setPrimed: YES];
nkeynes@964
   126
        [super mouseDown: event];
nkeynes@964
   127
    }
nkeynes@964
   128
}
nkeynes@964
   129
- (void)rightMouseDown: (NSEvent *)event
nkeynes@964
   130
{
nkeynes@964
   131
    if( isPrimed ) {
nkeynes@964
   132
        [self updateMousesym: 1];
nkeynes@964
   133
    }
nkeynes@964
   134
}
nkeynes@964
   135
- (void)otherMouseDown: (NSEvent *)event
nkeynes@964
   136
{
nkeynes@964
   137
    if( isPrimed ) {
nkeynes@964
   138
        [self updateMousesym: [event buttonNumber]];
nkeynes@964
   139
    }
nkeynes@964
   140
}
nkeynes@964
   141
- (void)keyDown: (NSEvent *) event
nkeynes@964
   142
{
nkeynes@964
   143
    NSString *chars = [event characters];
nkeynes@964
   144
    if( isPrimed ) {
nkeynes@964
   145
        if( chars != NULL && [chars length] == 1 && [chars characterAtIndex: 0] == 27 ) {
nkeynes@964
   146
            // Escape char = abort change
nkeynes@964
   147
            [self setString: lastValue];
nkeynes@964
   148
            [self setPrimed: NO];
nkeynes@964
   149
        } else {
nkeynes@964
   150
            [self keyPressed: ([event keyCode]+1)];
nkeynes@964
   151
        }
nkeynes@964
   152
    } else {
nkeynes@964
   153
        if( chars != NULL && [chars length] == 1 ) {
nkeynes@964
   154
            int ch = [chars characterAtIndex: 0];
nkeynes@964
   155
            switch( ch ) {
nkeynes@964
   156
            case 0x7F:
nkeynes@964
   157
                [self setString: @""]; 
nkeynes@964
   158
                [self fireBindingChanged];
nkeynes@964
   159
                break;
nkeynes@964
   160
            case '\r':
nkeynes@964
   161
                [self setPrimed: YES];
nkeynes@964
   162
                break;
nkeynes@964
   163
            default:
nkeynes@964
   164
                [super keyDown: event];
nkeynes@964
   165
                break;
nkeynes@964
   166
            }
nkeynes@964
   167
        } else {
nkeynes@964
   168
            [super keyDown: event];
nkeynes@964
   169
        }
nkeynes@964
   170
    }
nkeynes@964
   171
}
nkeynes@964
   172
- (void)flagsChanged: (NSEvent *) event
nkeynes@964
   173
{
nkeynes@964
   174
    if( isPrimed ) {
nkeynes@964
   175
        [self keyPressed: ([event keyCode]+1)];
nkeynes@964
   176
    }
nkeynes@964
   177
    [super flagsChanged: event];
nkeynes@964
   178
}
nkeynes@964
   179
@end
nkeynes@964
   180
nkeynes@964
   181
static void cocoa_config_keysym_hook(void *data, const gchar *keysym)
nkeynes@964
   182
{
nkeynes@964
   183
    KeyBindingEditor *editor = (KeyBindingEditor *)data;
nkeynes@964
   184
    [editor updateKeysym: keysym];
nkeynes@964
   185
}
nkeynes@964
   186
nkeynes@964
   187
nkeynes@964
   188
@implementation KeyBindingField
nkeynes@964
   189
@end
nkeynes@964
   190
nkeynes@964
   191
/*************************** Key-binding sub-view ***********************/
nkeynes@964
   192
nkeynes@964
   193
#define MAX_KEY_BINDINGS 32
nkeynes@964
   194
nkeynes@964
   195
@interface ControllerKeyBindingView : NSView
nkeynes@964
   196
{
nkeynes@964
   197
    maple_device_t device;
nkeynes@1034
   198
    NSTextField *field[MAX_KEY_BINDINGS][2];
nkeynes@964
   199
}
nkeynes@964
   200
- (id)initWithFrame: (NSRect)frameRect;
nkeynes@964
   201
- (void)setDevice: (maple_device_t)device;
nkeynes@964
   202
@end
nkeynes@964
   203
nkeynes@964
   204
@implementation ControllerKeyBindingView
nkeynes@964
   205
- (id)initWithFrame: (NSRect)frameRect
nkeynes@964
   206
{
nkeynes@964
   207
    if( [super initWithFrame: frameRect] == nil ) {
nkeynes@964
   208
        return nil;
nkeynes@964
   209
    } else {
nkeynes@964
   210
        device = NULL;
nkeynes@964
   211
        return self;
nkeynes@964
   212
    }
nkeynes@964
   213
}
nkeynes@964
   214
- (BOOL)isFlipped
nkeynes@964
   215
{
nkeynes@964
   216
    return YES;
nkeynes@964
   217
}
nkeynes@964
   218
- (void)removeSubviews
nkeynes@964
   219
{
nkeynes@964
   220
    [[self subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
nkeynes@964
   221
}
nkeynes@964
   222
- (void)controlTextDidChange: (NSNotification *)notify
nkeynes@964
   223
{
nkeynes@1034
   224
    const gchar *p = NULL;
nkeynes@964
   225
    int binding = [[notify object] tag];
nkeynes@964
   226
    NSString *val1 = [field[binding][0] stringValue];
nkeynes@1034
   227
    if( field[binding][1] == NULL ) {
nkeynes@964
   228
        p = [val1 UTF8String];
nkeynes@964
   229
    } else {
nkeynes@1034
   230
        NSString *val2 = [field[binding][1] stringValue];
nkeynes@1034
   231
        char buf[ [val1 length] + [val2 length] + 2 ];
nkeynes@1034
   232
nkeynes@1034
   233
        if( [val1 length] == 0 ) {
nkeynes@1034
   234
            if( [val2 length] != 0 ) {
nkeynes@1034
   235
                p = [val2 UTF8String];
nkeynes@1034
   236
            }
nkeynes@1034
   237
        } else if( [val2 length] == 0 ) {
nkeynes@1034
   238
            p = [val1 UTF8String];
nkeynes@1034
   239
        } else {
nkeynes@1034
   240
            sprintf( buf, "%s,%s", [val1 UTF8String], [val2 UTF8String] );
nkeynes@1034
   241
            p = buf;
nkeynes@1034
   242
        }
nkeynes@964
   243
    }
nkeynes@964
   244
    maple_set_device_config_value( device, binding, p ); 
nkeynes@964
   245
    lxdream_save_config();
nkeynes@964
   246
}
nkeynes@964
   247
- (void)setDevice: (maple_device_t)newDevice
nkeynes@964
   248
{
nkeynes@964
   249
    device = newDevice;
nkeynes@964
   250
    [self removeSubviews];
nkeynes@1034
   251
    if( device != NULL && !MAPLE_IS_VMU(device) ) {
nkeynes@964
   252
        lxdream_config_entry_t config = maple_get_device_config(device);
nkeynes@964
   253
        if( config != NULL ) {
nkeynes@964
   254
            int count, i, y, x;
nkeynes@964
   255
nkeynes@964
   256
            for( count=0; config[count].key != NULL; count++ );
nkeynes@964
   257
            x = TEXT_GAP;
nkeynes@964
   258
            NSSize size = NSMakeSize(85+KEYBINDING_SIZE*2+TEXT_GAP*4, count*(TEXT_HEIGHT+TEXT_GAP)+TEXT_GAP);
nkeynes@964
   259
            [self setFrameSize: size];
nkeynes@964
   260
            [self scrollRectToVisible: NSMakeRect(0,0,1,1)]; 
nkeynes@964
   261
            y = TEXT_GAP;
nkeynes@964
   262
            for( i=0; config[i].key != NULL; i++ ) {
nkeynes@1034
   263
                /* Add label */
nkeynes@964
   264
                NSRect frame = NSMakeRect(x, y + 2, 85, LABEL_HEIGHT);
nkeynes@964
   265
                NSTextField *label = cocoa_gui_add_label(self, NS_(config[i].label), frame);
nkeynes@964
   266
                [label setAlignment: NSRightTextAlignment];
nkeynes@1034
   267
                
nkeynes@1034
   268
                switch(config[i].type) {
nkeynes@1034
   269
                case CONFIG_TYPE_KEY:
nkeynes@1034
   270
                    frame = NSMakeRect( x + 85 + TEXT_GAP, y, KEYBINDING_SIZE, TEXT_HEIGHT);
nkeynes@1034
   271
                    field[i][0] = [[KeyBindingField alloc] initWithFrame: frame];
nkeynes@1034
   272
                    [field[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
nkeynes@1034
   273
                    [field[i][0] setTag: i];
nkeynes@1034
   274
                    [field[i][0] setDelegate: self];
nkeynes@1034
   275
                    [self addSubview: field[i][0]];
nkeynes@964
   276
nkeynes@1034
   277
                    frame = NSMakeRect( x + 85 + KEYBINDING_SIZE + (TEXT_GAP*2), y, KEYBINDING_SIZE, TEXT_HEIGHT);
nkeynes@1034
   278
                    field[i][1] = [[KeyBindingField alloc] initWithFrame: frame];
nkeynes@1034
   279
                    [field[i][1] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
nkeynes@1034
   280
                    [field[i][1] setTag: i];
nkeynes@1034
   281
                    [field[i][1] setDelegate: self];
nkeynes@1034
   282
                    [self addSubview: field[i][1]];
nkeynes@964
   283
nkeynes@1034
   284
                    if( config[i].value != NULL ) {
nkeynes@1034
   285
                        gchar **parts = g_strsplit(config[i].value,",",3);
nkeynes@1034
   286
                        if( parts[0] != NULL ) {
nkeynes@1034
   287
                            [field[i][0] setStringValue: [NSString stringWithCString: parts[0]]];
nkeynes@1034
   288
                            if( parts[1] != NULL ) {
nkeynes@1034
   289
                                [field[i][1] setStringValue: [NSString stringWithCString: parts[1]]];
nkeynes@1034
   290
                            }
nkeynes@964
   291
                        }
nkeynes@1034
   292
                        g_strfreev(parts);
nkeynes@964
   293
                    }
nkeynes@1034
   294
                    break;
nkeynes@1034
   295
                case CONFIG_TYPE_FILE:
nkeynes@1034
   296
                case CONFIG_TYPE_PATH:
nkeynes@1034
   297
                    frame = NSMakeRect( x + 85 + TEXT_GAP, y, KEYBINDING_SIZE*2+TEXT_GAP, TEXT_HEIGHT);
nkeynes@1034
   298
                    field[i][0] = [[NSTextField alloc] initWithFrame: frame];
nkeynes@1034
   299
                    [field[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
nkeynes@1034
   300
                    [field[i][0] setTag: i];
nkeynes@1034
   301
                    [field[i][0] setDelegate: self];
nkeynes@1034
   302
                    [self addSubview: field[i][0]];
nkeynes@1034
   303
                    if( config[i].value != NULL ) {
nkeynes@1034
   304
                        [field[i][0] setStringValue: [NSString stringWithCString: config[i].value]];
nkeynes@1034
   305
                    }
nkeynes@1034
   306
                    field[i][1] = NULL;
nkeynes@1034
   307
                } 
nkeynes@964
   308
                y += (TEXT_HEIGHT + TEXT_GAP);
nkeynes@964
   309
            }
nkeynes@964
   310
        } else {
nkeynes@964
   311
            [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
nkeynes@964
   312
        }
nkeynes@964
   313
    } else {
nkeynes@964
   314
        [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
nkeynes@964
   315
    }
nkeynes@964
   316
}
nkeynes@964
   317
@end
nkeynes@964
   318
nkeynes@964
   319
/*************************** Top-level controller pane ***********************/
nkeynes@1034
   320
static NSButton *addRadioButton( int port, int sub, int x, int y, id parent )
nkeynes@1034
   321
{
nkeynes@1034
   322
    char buf[16];
nkeynes@1034
   323
    
nkeynes@1034
   324
    if( sub == 0 ) {
nkeynes@1034
   325
        snprintf( buf, sizeof(buf), _("Port %c."), 'A'+port );
nkeynes@1034
   326
    } else {
nkeynes@1034
   327
        snprintf( buf, sizeof(buf), _("VMU %d."), sub );
nkeynes@1034
   328
    }
nkeynes@1034
   329
nkeynes@1034
   330
    NSButton *radio = [[NSButton alloc] initWithFrame: NSMakeRect( x, y, 60, TEXT_HEIGHT )];
nkeynes@1034
   331
    [radio setTitle: [NSString stringWithUTF8String: buf]];
nkeynes@1034
   332
    [radio setTag: MAPLE_DEVID(port,sub) ];
nkeynes@1034
   333
    [radio setButtonType: NSRadioButton];
nkeynes@1034
   334
    [radio setAlignment: NSRightTextAlignment];
nkeynes@1034
   335
    [radio setTarget: parent];
nkeynes@1034
   336
    [radio setAction: @selector(radioChanged:)];
nkeynes@1034
   337
    [radio setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
nkeynes@1034
   338
    [parent addSubview: radio];
nkeynes@1034
   339
    return radio;
nkeynes@1034
   340
}
nkeynes@1034
   341
nkeynes@1034
   342
static void setDevicePopupSelection( NSPopUpButton *popup, maple_device_t device )
nkeynes@1034
   343
{
nkeynes@1034
   344
    if( device == NULL ) {
nkeynes@1034
   345
        [popup selectItemAtIndex: 0];
nkeynes@1034
   346
    } else if( MAPLE_IS_VMU(device) ) {
nkeynes@1034
   347
        int idx = vmulist_get_index_by_filename( MAPLE_VMU_NAME(device) );
nkeynes@1034
   348
        if( idx == -1 ) {
nkeynes@1034
   349
            [popup selectItemAtIndex: 0];
nkeynes@1034
   350
        } else {
nkeynes@1034
   351
            [popup selectItemWithTag: FIRST_VMU_TAG + idx];
nkeynes@1034
   352
        }
nkeynes@1034
   353
    } else {
nkeynes@1034
   354
        const struct maple_device_class **devices = maple_get_device_classes();
nkeynes@1034
   355
        int i;
nkeynes@1034
   356
        for( i=0; devices[i] != NULL; i++ ) {
nkeynes@1034
   357
            if( devices[i] == device->device_class ) {
nkeynes@1034
   358
                [popup selectItemWithTag: i+1];
nkeynes@1034
   359
                return;
nkeynes@1034
   360
            }
nkeynes@1034
   361
        }
nkeynes@1034
   362
        // Should never get here, but if so...
nkeynes@1034
   363
        [popup selectItemAtIndex: 0];
nkeynes@1034
   364
    }
nkeynes@1034
   365
}
nkeynes@1034
   366
nkeynes@1034
   367
static void buildDevicePopupMenu( NSPopUpButton *popup, maple_device_t device, BOOL primary )
nkeynes@1034
   368
{
nkeynes@1034
   369
    int j;
nkeynes@1034
   370
    const struct maple_device_class **devices = maple_get_device_classes();
nkeynes@1034
   371
nkeynes@1034
   372
    [popup removeAllItems];
nkeynes@1034
   373
    [popup addItemWithTitle: NS_("<empty>")];
nkeynes@1034
   374
    [[popup itemAtIndex: 0] setTag: 0];
nkeynes@1034
   375
    for( j=0; devices[j] != NULL; j++ ) {
nkeynes@1034
   376
        int isPrimaryDevice = devices[j]->flags & MAPLE_TYPE_PRIMARY;
nkeynes@1034
   377
        if( primary ? isPrimaryDevice : (!isPrimaryDevice && !MAPLE_IS_VMU_CLASS(devices[j])) ) {
nkeynes@1034
   378
            [popup addItemWithTitle: [NSString stringWithUTF8String: devices[j]->name]];
nkeynes@1034
   379
            if( device != NULL && device->device_class == devices[j] ) {
nkeynes@1034
   380
                [popup selectItemAtIndex: ([popup numberOfItems]-1)];
nkeynes@1034
   381
            }
nkeynes@1034
   382
            [[popup itemAtIndex: ([popup numberOfItems]-1)] setTag: (j+1)];
nkeynes@1034
   383
        }
nkeynes@1034
   384
    }
nkeynes@1034
   385
    
nkeynes@1034
   386
    if( !primary ) {
nkeynes@1034
   387
        BOOL vmu_selected = NO;
nkeynes@1034
   388
        const char *vmu_name;
nkeynes@1034
   389
        if( device != NULL && MAPLE_IS_VMU(device) ) {
nkeynes@1034
   390
            vmu_selected = YES;
nkeynes@1034
   391
            vmu_name = MAPLE_VMU_NAME(device);
nkeynes@1034
   392
        }
nkeynes@1034
   393
        if( [popup numberOfItems] > 0 ) {
nkeynes@1034
   394
            [[popup menu] addItem: [NSMenuItem separatorItem]];
nkeynes@1034
   395
        }
nkeynes@1034
   396
        
nkeynes@1034
   397
        unsigned int vmu_count = vmulist_get_size();
nkeynes@1034
   398
        for( j=0; j<vmu_count; j++ ) {
nkeynes@1034
   399
            const char *name = vmulist_get_name(j);
nkeynes@1034
   400
            [popup addItemWithTitle: [NSString stringWithUTF8String: name]];
nkeynes@1034
   401
            if( vmu_selected && strcmp(vmu_name, vmulist_get_filename(j)) == 0 ) {
nkeynes@1034
   402
                [popup selectItemAtIndex: ([popup numberOfItems]-1)];
nkeynes@1034
   403
            }
nkeynes@1034
   404
            [[popup itemAtIndex: ([popup numberOfItems]-1)] setTag: FIRST_VMU_TAG + j];
nkeynes@1034
   405
        }
nkeynes@1034
   406
        
nkeynes@1034
   407
        [popup addItemWithTitle: NS_("Load VMU...")];
nkeynes@1034
   408
        [[popup itemAtIndex: ([popup numberOfItems]-1)] setTag: LOAD_VMU_TAG];
nkeynes@1034
   409
        [popup addItemWithTitle: NS_("Create VMU...")];
nkeynes@1034
   410
        [[popup itemAtIndex: ([popup numberOfItems]-1)] setTag: CREATE_VMU_TAG];
nkeynes@1034
   411
    }
nkeynes@1034
   412
    
nkeynes@1034
   413
}
nkeynes@1034
   414
nkeynes@1034
   415
static NSPopUpButton *addDevicePopup( int port, int sub, int x, int y, maple_device_t device, BOOL primary, id parent )
nkeynes@1034
   416
{
nkeynes@1034
   417
    NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame: NSMakeRect(x,y,150,TEXT_HEIGHT) 
nkeynes@1034
   418
                                                  pullsDown: NO];
nkeynes@1034
   419
    [popup setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
nkeynes@1034
   420
    buildDevicePopupMenu(popup,device,primary);
nkeynes@1034
   421
nkeynes@1034
   422
    [popup setTarget: parent];
nkeynes@1034
   423
    [popup setAction: @selector(deviceChanged:)];
nkeynes@1034
   424
    [popup setTag: MAPLE_DEVID(port,sub) ];
nkeynes@1034
   425
    [parent addSubview: popup];    
nkeynes@1034
   426
    return popup;
nkeynes@1034
   427
}
nkeynes@1034
   428
nkeynes@1034
   429
@interface VMULoadValidator : NSObject
nkeynes@1034
   430
{
nkeynes@1034
   431
}
nkeynes@1034
   432
- (BOOL)panel:(id) sender isValidFilename: (NSString *)filename;
nkeynes@1034
   433
@end
nkeynes@1034
   434
nkeynes@1034
   435
@implementation VMULoadValidator 
nkeynes@1034
   436
- (BOOL)panel:(id) sender isValidFilename: (NSString *)filename
nkeynes@1034
   437
{
nkeynes@1034
   438
    const char *c_fn = [filename UTF8String];
nkeynes@1034
   439
    vmu_volume_t vol = vmu_volume_load( c_fn );
nkeynes@1034
   440
    if( vol != NULL ) {
nkeynes@1034
   441
        vmulist_add_vmu(c_fn, vol);
nkeynes@1034
   442
        return YES;
nkeynes@1034
   443
    } else {
nkeynes@1034
   444
        ERROR( "Unable to load VMU file (not a valid VMU)" );
nkeynes@1034
   445
        return NO;
nkeynes@1034
   446
    }
nkeynes@1034
   447
}
nkeynes@1034
   448
nkeynes@1034
   449
@end
nkeynes@1034
   450
nkeynes@1034
   451
@interface VMUCreateValidator : NSObject
nkeynes@1034
   452
{
nkeynes@1034
   453
}
nkeynes@1034
   454
- (BOOL)panel:(id) sender isValidFilename: (NSString *)filename;
nkeynes@1034
   455
@end
nkeynes@1034
   456
nkeynes@1034
   457
@implementation VMUCreateValidator 
nkeynes@1034
   458
- (BOOL)panel:(id) sender isValidFilename: (NSString *)filename
nkeynes@1034
   459
{
nkeynes@1034
   460
    const char *vmu_filename = [filename UTF8String];
nkeynes@1034
   461
    int idx = vmulist_create_vmu(vmu_filename, FALSE);
nkeynes@1034
   462
    if( idx == -1 ) {
nkeynes@1034
   463
        ERROR( "Unable to create file: %s\n", strerror(errno) );
nkeynes@1034
   464
        return NO;
nkeynes@1034
   465
    } else {
nkeynes@1034
   466
        return YES;
nkeynes@1034
   467
    }
nkeynes@1034
   468
}
nkeynes@1034
   469
@end
nkeynes@1034
   470
nkeynes@964
   471
nkeynes@964
   472
@interface LxdreamPrefsControllerPane: LxdreamPrefsPane
nkeynes@964
   473
{
nkeynes@1034
   474
    struct maple_device *save_controller[MAPLE_MAX_DEVICES];
nkeynes@1034
   475
    NSButton *radio[MAPLE_MAX_DEVICES];
nkeynes@1034
   476
    NSPopUpButton *popup[MAPLE_MAX_DEVICES];
nkeynes@964
   477
    ControllerKeyBindingView *key_bindings;
nkeynes@964
   478
}
nkeynes@964
   479
+ (LxdreamPrefsControllerPane *)new;
nkeynes@1034
   480
- (void)vmulistChanged: (id)sender;
nkeynes@964
   481
@end
nkeynes@964
   482
nkeynes@1034
   483
static gboolean cocoa_config_vmulist_hook(vmulist_change_type_t type, int idx, void *data)
nkeynes@1034
   484
{
nkeynes@1034
   485
    LxdreamPrefsControllerPane *pane = (LxdreamPrefsControllerPane *)data;
nkeynes@1034
   486
    [pane vmulistChanged: nil];
nkeynes@1034
   487
    return TRUE;
nkeynes@1034
   488
}
nkeynes@1034
   489
nkeynes@964
   490
@implementation LxdreamPrefsControllerPane
nkeynes@964
   491
+ (LxdreamPrefsControllerPane *)new
nkeynes@964
   492
{
nkeynes@964
   493
    return [[LxdreamPrefsControllerPane alloc] initWithFrame: NSMakeRect(0,0,600,400)];
nkeynes@964
   494
}
nkeynes@964
   495
- (id)initWithFrame: (NSRect)frameRect
nkeynes@964
   496
{
nkeynes@964
   497
    if( [super initWithFrame: frameRect title: NS_("Controllers")] == nil ) {
nkeynes@964
   498
        return nil;
nkeynes@964
   499
    } else {
nkeynes@964
   500
        int i,j;
nkeynes@964
   501
        int y = [self contentHeight] - TEXT_HEIGHT - TEXT_GAP;
nkeynes@964
   502
nkeynes@1034
   503
        memset( radio, 0, sizeof(radio) );
nkeynes@1034
   504
        memset( save_controller, 0, sizeof(save_controller) );
nkeynes@964
   505
        NSBox *rule = [[NSBox alloc] initWithFrame: 
nkeynes@964
   506
                NSMakeRect(210+(TEXT_GAP*3), 1, 1, [self contentHeight] + TEXT_GAP - 2)];
nkeynes@964
   507
        [rule setAutoresizingMask: (NSViewMaxXMargin|NSViewHeightSizable)];
nkeynes@964
   508
        [rule setBoxType: NSBoxSeparator];
nkeynes@964
   509
        [self addSubview: rule];
nkeynes@964
   510
        
nkeynes@964
   511
        NSRect bindingFrame = NSMakeRect(210+(TEXT_GAP*4), 0,
nkeynes@964
   512
                   frameRect.size.width - (210+(TEXT_GAP*4)), [self contentHeight] + TEXT_GAP );
nkeynes@964
   513
        NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: bindingFrame];
nkeynes@964
   514
        key_bindings = [[ControllerKeyBindingView alloc] initWithFrame: bindingFrame ];
nkeynes@964
   515
        [scrollView setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)];
nkeynes@964
   516
        [scrollView setDocumentView: key_bindings];
nkeynes@964
   517
        [scrollView setDrawsBackground: NO];
nkeynes@964
   518
        [scrollView setHasVerticalScroller: YES];
nkeynes@964
   519
        [scrollView setAutohidesScrollers: YES];
nkeynes@964
   520
 
nkeynes@964
   521
        [self addSubview: scrollView];
nkeynes@964
   522
        [key_bindings setDevice: maple_get_device(0,0)];
nkeynes@964
   523
        
nkeynes@1034
   524
        for( i=0; i<MAPLE_PORTS; i++ ) {
nkeynes@964
   525
            maple_device_t device = maple_get_device(i,0);
nkeynes@964
   526
nkeynes@1034
   527
            radio[i] = addRadioButton(i,0,TEXT_GAP,y,self);
nkeynes@1034
   528
            popup[i] = addDevicePopup(i,0,60 + (TEXT_GAP*2),y,device, YES,self);
nkeynes@1034
   529
            y -= (TEXT_HEIGHT+TEXT_GAP);
nkeynes@1034
   530
            
nkeynes@1034
   531
            int j,max = device == NULL ? 0 : MAPLE_SLOTS(device->device_class);
nkeynes@1034
   532
            for( j=1; j<=MAPLE_USER_SLOTS; j++ ) {
nkeynes@1034
   533
                radio[MAPLE_DEVID(i,j)] = addRadioButton(i, j, TEXT_GAP*2, y, self);
nkeynes@1034
   534
                popup[MAPLE_DEVID(i,j)] = addDevicePopup(i, j, 60 + TEXT_GAP*2, y, maple_get_device(i,j), NO, self);
nkeynes@1034
   535
                y -= (TEXT_HEIGHT+TEXT_GAP);
nkeynes@1034
   536
                if( j > max ) {
nkeynes@1034
   537
                    [radio[MAPLE_DEVID(i,j)] setEnabled: NO];
nkeynes@1034
   538
                    [popup[MAPLE_DEVID(i,j)] setEnabled: NO];
nkeynes@964
   539
                }
nkeynes@964
   540
            }
nkeynes@964
   541
        }
nkeynes@964
   542
        
nkeynes@964
   543
        [radio[0] setState: NSOnState];
nkeynes@1034
   544
        
nkeynes@1034
   545
        register_vmulist_change_hook(cocoa_config_vmulist_hook, self);
nkeynes@964
   546
        return self;
nkeynes@964
   547
    }
nkeynes@964
   548
}
nkeynes@1034
   549
- (void)dealloc
nkeynes@1034
   550
{
nkeynes@1034
   551
    unregister_vmulist_change_hook(cocoa_config_vmulist_hook,self);
nkeynes@1034
   552
    [super dealloc];
nkeynes@1034
   553
}
nkeynes@1034
   554
- (void)vmulistChanged: (id)sender
nkeynes@1034
   555
{
nkeynes@1034
   556
    int i;
nkeynes@1034
   557
    for( i=FIRST_SECONDARY_DEVICE; i<MAPLE_MAX_DEVICES; i++ ) {
nkeynes@1034
   558
        if( popup[i] != NULL ) {
nkeynes@1034
   559
            buildDevicePopupMenu(popup[i], maple_get_device(MAPLE_DEVID_PORT(i), MAPLE_DEVID_SLOT(i)), NO );
nkeynes@1034
   560
        }
nkeynes@1034
   561
    }
nkeynes@1034
   562
}
nkeynes@964
   563
- (void)radioChanged: (id)sender
nkeynes@964
   564
{
nkeynes@1034
   565
    int tag = [sender tag];
nkeynes@964
   566
    int i;
nkeynes@1034
   567
    for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
nkeynes@1034
   568
        if( i != tag && radio[i] != NULL ) {
nkeynes@964
   569
            [radio[i] setState: NSOffState];
nkeynes@964
   570
        }
nkeynes@964
   571
    }
nkeynes@1034
   572
    [key_bindings setDevice: maple_get_device(MAPLE_DEVID_PORT(tag),MAPLE_DEVID_SLOT(tag))];
nkeynes@964
   573
}
nkeynes@964
   574
- (void)deviceChanged: (id)sender
nkeynes@964
   575
{
nkeynes@1034
   576
    int tag = [sender tag];
nkeynes@1034
   577
    int port = MAPLE_DEVID_PORT(tag);
nkeynes@1034
   578
    int slot = MAPLE_DEVID_SLOT(tag);
nkeynes@1034
   579
    int new_device_idx = [[sender selectedItem] tag], i; 
nkeynes@964
   580
    maple_device_class_t new_device_class = NULL;
nkeynes@1034
   581
    const gchar *vmu_filename = NULL;
nkeynes@964
   582
    
nkeynes@1034
   583
    for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
nkeynes@1034
   584
        if( radio[i] != NULL ) {
nkeynes@1034
   585
            if( i == tag ) {
nkeynes@1034
   586
                [radio[i] setState: NSOnState];
nkeynes@1034
   587
            } else {
nkeynes@1034
   588
                [radio[i] setState: NSOffState];
nkeynes@1034
   589
            }
nkeynes@964
   590
        }
nkeynes@964
   591
    }
nkeynes@964
   592
    
nkeynes@1034
   593
    maple_device_t current = maple_get_device(port,slot);
nkeynes@964
   594
    maple_device_t new_device = NULL;
nkeynes@1034
   595
    if( new_device_idx == LOAD_VMU_TAG ) {
nkeynes@1034
   596
        NSArray *array = [NSArray arrayWithObjects: @"vmu", nil];
nkeynes@1034
   597
        NSOpenPanel *panel = [NSOpenPanel openPanel];
nkeynes@1034
   598
        VMULoadValidator *valid = [[VMULoadValidator alloc] autorelease];
nkeynes@1034
   599
        [panel setDelegate: valid];
nkeynes@1058
   600
        int result = [panel runModalForDirectory: [NSString stringWithUTF8String: get_gui_path(CONFIG_VMU_PATH)]
nkeynes@1034
   601
               file: nil types: array];
nkeynes@1034
   602
        if( result == NSOKButton ) {
nkeynes@1034
   603
            vmu_filename = [[panel filename] UTF8String];
nkeynes@1034
   604
            int idx = vmulist_get_index_by_filename(vmu_filename);
nkeynes@1034
   605
            [sender selectItemWithTag: (FIRST_VMU_TAG+idx)];
nkeynes@1034
   606
            new_device_class = &vmu_class;
nkeynes@1041
   607
            set_gui_path(CONFIG_VMU_PATH, [[panel directory] UTF8String]);
nkeynes@1034
   608
        } else {
nkeynes@1034
   609
            /* Cancelled - restore previous value */
nkeynes@1034
   610
            setDevicePopupSelection( sender, current );
nkeynes@1034
   611
            return;
nkeynes@1034
   612
        }
nkeynes@1034
   613
    } else if( new_device_idx == CREATE_VMU_TAG ) {
nkeynes@1034
   614
        NSSavePanel *panel = [NSSavePanel savePanel];
nkeynes@1034
   615
        [panel setTitle: NS_("Create VMU")];
nkeynes@1034
   616
        [panel setCanCreateDirectories: YES];
nkeynes@1034
   617
        [panel setRequiredFileType: @"vmu"];
nkeynes@1034
   618
        VMUCreateValidator *valid = [[VMUCreateValidator alloc] autorelease];
nkeynes@1034
   619
        [panel setDelegate: valid];
nkeynes@1058
   620
        int result = [panel runModalForDirectory: [NSString stringWithUTF8String: get_gui_path(CONFIG_VMU_PATH)]
nkeynes@1034
   621
               file: nil];
nkeynes@1034
   622
        if( result == NSFileHandlingPanelOKButton ) {
nkeynes@1034
   623
            /* Validator has already created the file by now */
nkeynes@1034
   624
            vmu_filename = [[panel filename] UTF8String];
nkeynes@1034
   625
            int idx = vmulist_get_index_by_filename(vmu_filename);
nkeynes@1034
   626
            [sender selectItemWithTag: (FIRST_VMU_TAG+idx)];
nkeynes@1034
   627
            new_device_class = &vmu_class;
nkeynes@1041
   628
            set_gui_path(CONFIG_VMU_PATH, [[panel directory] UTF8String]);
nkeynes@1034
   629
        } else {
nkeynes@1034
   630
            setDevicePopupSelection( sender, current );
nkeynes@1034
   631
            return;
nkeynes@1034
   632
        }
nkeynes@1034
   633
    } else if( new_device_idx >= FIRST_VMU_TAG ) {
nkeynes@1034
   634
        vmu_filename = vmulist_get_filename( new_device_idx - FIRST_VMU_TAG );
nkeynes@1034
   635
        new_device_class = &vmu_class;
nkeynes@1034
   636
    } else if( new_device_idx > 0) {
nkeynes@1034
   637
        new_device_class = maple_get_device_classes()[new_device_idx-1];
nkeynes@964
   638
    }
nkeynes@1034
   639
    
nkeynes@1034
   640
    if( current == NULL ? new_device_class == NULL : 
nkeynes@1034
   641
        (current->device_class == new_device_class && 
nkeynes@1034
   642
                (!MAPLE_IS_VMU(current) || MAPLE_VMU_HAS_NAME(current, vmu_filename))) ) {
nkeynes@964
   643
        // No change
nkeynes@964
   644
        [key_bindings setDevice: current];
nkeynes@964
   645
        return;
nkeynes@964
   646
    }
nkeynes@964
   647
    if( current != NULL && current->device_class == &controller_class ) {
nkeynes@1034
   648
        save_controller[tag] = current->clone(current);
nkeynes@964
   649
    }
nkeynes@964
   650
    if( new_device_class == NULL ) {
nkeynes@1034
   651
        maple_detach_device(port,slot);
nkeynes@1043
   652
        if( slot == 0 ) {
nkeynes@1043
   653
            /* If we detached the top-level dev, any children are automatically detached */
nkeynes@1043
   654
            for( i=1; i<=MAPLE_USER_SLOTS; i++ ) {
nkeynes@1043
   655
                [popup[MAPLE_DEVID(port,i)] selectItemWithTag: 0];
nkeynes@1043
   656
            }
nkeynes@1043
   657
        }
nkeynes@964
   658
    } else {
nkeynes@1034
   659
        if( new_device_class == &controller_class && save_controller[tag] != NULL ) {
nkeynes@1034
   660
            new_device = save_controller[tag];
nkeynes@1034
   661
            save_controller[tag] = NULL;
nkeynes@964
   662
        } else {
nkeynes@964
   663
            new_device = maple_new_device( new_device_class->name );
nkeynes@964
   664
        }
nkeynes@1034
   665
        if( MAPLE_IS_VMU(new_device) ) {
nkeynes@1043
   666
            /* Remove the VMU from any other attachment point */
nkeynes@1043
   667
            for( i=0; i<MAPLE_MAX_DEVICES; i++ ) {
nkeynes@1043
   668
                maple_device_t dev = maple_get_device(MAPLE_DEVID_PORT(i),MAPLE_DEVID_SLOT(i));
nkeynes@1043
   669
                if( dev != NULL && MAPLE_IS_VMU(dev) && MAPLE_VMU_HAS_NAME(dev,vmu_filename) ) {
nkeynes@1043
   670
                    maple_detach_device(MAPLE_DEVID_PORT(i),MAPLE_DEVID_SLOT(i));
nkeynes@1043
   671
                    [popup[i] selectItemWithTag: 0];
nkeynes@1043
   672
                }
nkeynes@1043
   673
            }
nkeynes@1034
   674
            MAPLE_SET_VMU_NAME(new_device,vmu_filename);
nkeynes@1034
   675
        }
nkeynes@1034
   676
        maple_attach_device(new_device,port,slot);
nkeynes@964
   677
    }
nkeynes@1034
   678
    [key_bindings setDevice: maple_get_device(port,slot)];
nkeynes@1034
   679
    
nkeynes@1034
   680
    if( slot == 0 ) { /* Change primary */
nkeynes@1034
   681
        int max = new_device_class == NULL ? 0 : MAPLE_SLOTS(new_device_class);
nkeynes@1034
   682
        for( i=1; i<=MAPLE_USER_SLOTS; i++ ) {
nkeynes@1034
   683
            if( i <= max ) {
nkeynes@1034
   684
                [radio[MAPLE_DEVID(port,i)] setEnabled: YES];
nkeynes@1034
   685
                [popup[MAPLE_DEVID(port,i)] setEnabled: YES];
nkeynes@1034
   686
            } else {
nkeynes@1034
   687
                [radio[MAPLE_DEVID(port,i)] setEnabled: NO];
nkeynes@1034
   688
                [popup[MAPLE_DEVID(port,i)] setEnabled: NO];
nkeynes@1034
   689
            }                
nkeynes@1034
   690
        }
nkeynes@1034
   691
    }
nkeynes@964
   692
    lxdream_save_config();
nkeynes@964
   693
}
nkeynes@964
   694
@end
nkeynes@964
   695
nkeynes@964
   696
NSView *cocoa_gui_create_prefs_controller_pane()
nkeynes@964
   697
{
nkeynes@964
   698
    return [LxdreamPrefsControllerPane new];
nkeynes@964
   699
}
.