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