Search
lxdream.org :: lxdream/src/cocoaui/cocoa_ctrl.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/cocoaui/cocoa_ctrl.c
changeset 770:429ff505c450
prev765:4cd066048203
next787:6717c02ff81f
author nkeynes
date Mon Jul 28 03:41:25 2008 +0000 (11 years ago)
permissions -rw-r--r--
last change Implement key-binding configuration pane for Cocoa UI
Minor tweaks for consistency and static-correctness
file annotate diff log raw
nkeynes@765
     1
/**
nkeynes@765
     2
 * $Id$
nkeynes@765
     3
 *
nkeynes@765
     4
 * Construct and manage the controller configuration pane
nkeynes@765
     5
 *
nkeynes@765
     6
 * Copyright (c) 2008 Nathan Keynes.
nkeynes@765
     7
 *
nkeynes@765
     8
 * This program is free software; you can redistribute it and/or modify
nkeynes@765
     9
 * it under the terms of the GNU General Public License as published by
nkeynes@765
    10
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@765
    11
 * (at your option) any later version.
nkeynes@765
    12
 *
nkeynes@765
    13
 * This program is distributed in the hope that it will be useful,
nkeynes@765
    14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@765
    15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@765
    16
 * GNU General Public License for more details.
nkeynes@765
    17
 */
nkeynes@765
    18
nkeynes@765
    19
#include "cocoaui.h"
nkeynes@765
    20
#include "config.h"
nkeynes@770
    21
#include "display.h"
nkeynes@765
    22
#include "maple/maple.h"
nkeynes@765
    23
nkeynes@770
    24
#include <glib/gstrfuncs.h>
nkeynes@770
    25
nkeynes@765
    26
#define MAX_DEVICES 4
nkeynes@765
    27
nkeynes@770
    28
static void cocoa_config_keysym_hook(void *data, const gchar *keysym);
nkeynes@770
    29
nkeynes@770
    30
@interface KeyBindingEditor (Private)
nkeynes@770
    31
- (void)updateKeysym: (const gchar *)sym;
nkeynes@770
    32
@end
nkeynes@770
    33
nkeynes@770
    34
@implementation KeyBindingEditor
nkeynes@770
    35
- (id)init
nkeynes@770
    36
{
nkeynes@770
    37
    self = [super init];
nkeynes@770
    38
    isPrimed = NO;
nkeynes@770
    39
    lastValue = nil;
nkeynes@770
    40
    [self setFieldEditor: YES];
nkeynes@770
    41
    [self setEditable: FALSE];
nkeynes@770
    42
    return self;
nkeynes@770
    43
}
nkeynes@770
    44
- (void)dealloc
nkeynes@770
    45
{
nkeynes@770
    46
    if( lastValue != nil ) {
nkeynes@770
    47
        [lastValue release];
nkeynes@770
    48
        lastValue = nil;
nkeynes@770
    49
    }
nkeynes@770
    50
    [super dealloc];
nkeynes@770
    51
}
nkeynes@770
    52
- (void)setPrimed: (BOOL)primed
nkeynes@770
    53
{
nkeynes@770
    54
    if( primed != isPrimed ) {
nkeynes@770
    55
        isPrimed = primed;
nkeynes@770
    56
        if( primed ) {
nkeynes@770
    57
            lastValue = [[NSString stringWithString: [self string]] retain];
nkeynes@770
    58
            [self setString: @"<press key>"];
nkeynes@770
    59
            input_set_keysym_hook(cocoa_config_keysym_hook, self);            
nkeynes@770
    60
        } else {
nkeynes@770
    61
            [lastValue release];
nkeynes@770
    62
            lastValue = nil;
nkeynes@770
    63
            input_set_keysym_hook(NULL,NULL);
nkeynes@770
    64
        }
nkeynes@770
    65
    }
nkeynes@770
    66
}
nkeynes@770
    67
- (void)fireBindingChanged
nkeynes@770
    68
{
nkeynes@770
    69
    id delegate = [self delegate];
nkeynes@770
    70
    if( delegate != nil && [delegate respondsToSelector:@selector(textDidChange:)] ) {
nkeynes@770
    71
        [delegate textDidChange: [NSNotification notificationWithName: NSTextDidChangeNotification object: self]];
nkeynes@770
    72
    }
nkeynes@770
    73
}
nkeynes@770
    74
        
nkeynes@770
    75
- (void)updateKeysym: (const gchar *)sym
nkeynes@770
    76
{
nkeynes@770
    77
    if( sym != NULL ) {
nkeynes@770
    78
        [self setString: [NSString stringWithCString: sym]];
nkeynes@770
    79
        [self setPrimed: NO];
nkeynes@770
    80
        [self fireBindingChanged];
nkeynes@770
    81
    }
nkeynes@770
    82
}
nkeynes@770
    83
- (void)keyPressed: (int)keycode
nkeynes@770
    84
{
nkeynes@770
    85
    gchar *keysym = input_keycode_to_keysym(NULL, keycode);
nkeynes@770
    86
    if( keysym != NULL ) {
nkeynes@770
    87
        [self updateKeysym: keysym];
nkeynes@770
    88
        g_free(keysym);
nkeynes@770
    89
    }
nkeynes@770
    90
}
nkeynes@770
    91
- (void)insertText:(id)string
nkeynes@770
    92
{
nkeynes@770
    93
    // Do nothing
nkeynes@770
    94
}
nkeynes@770
    95
- (void)mouseDown: (NSEvent *)event
nkeynes@770
    96
{
nkeynes@770
    97
    [self setPrimed: YES];
nkeynes@770
    98
    [super mouseDown: event];
nkeynes@770
    99
}
nkeynes@770
   100
- (void)keyDown: (NSEvent *) event
nkeynes@770
   101
{
nkeynes@770
   102
    NSString *chars = [event characters];
nkeynes@770
   103
    if( isPrimed ) {
nkeynes@770
   104
        if( chars != NULL && [chars length] == 1 && [chars characterAtIndex: 0] == 27 ) {
nkeynes@770
   105
            // Escape char = abort change
nkeynes@770
   106
            [self setString: lastValue];
nkeynes@770
   107
            [self setPrimed: NO];
nkeynes@770
   108
        } else {
nkeynes@770
   109
            [self keyPressed: ([event keyCode]+1)];
nkeynes@770
   110
        }
nkeynes@770
   111
    } else {
nkeynes@770
   112
        if( chars != NULL && [chars length] == 1 ) {
nkeynes@770
   113
            int ch = [chars characterAtIndex: 0];
nkeynes@770
   114
            switch( ch ) {
nkeynes@770
   115
            case 0x7F:
nkeynes@770
   116
                [self setString: @""]; 
nkeynes@770
   117
                [self fireBindingChanged];
nkeynes@770
   118
                break;
nkeynes@770
   119
            case '\r':
nkeynes@770
   120
                [self setPrimed: YES];
nkeynes@770
   121
                break;
nkeynes@770
   122
            default:
nkeynes@770
   123
                [super keyDown: event];
nkeynes@770
   124
                break;
nkeynes@770
   125
            }
nkeynes@770
   126
        } else {
nkeynes@770
   127
            [super keyDown: event];
nkeynes@770
   128
        }
nkeynes@770
   129
    }
nkeynes@770
   130
}
nkeynes@770
   131
- (void)flagsChanged: (NSEvent *) event
nkeynes@770
   132
{
nkeynes@770
   133
    if( isPrimed ) {
nkeynes@770
   134
        [self keyPressed: ([event keyCode]+1)];
nkeynes@770
   135
    }
nkeynes@770
   136
    [super flagsChanged: event];
nkeynes@770
   137
}
nkeynes@770
   138
@end
nkeynes@770
   139
nkeynes@770
   140
static void cocoa_config_keysym_hook(void *data, const gchar *keysym)
nkeynes@770
   141
{
nkeynes@770
   142
    KeyBindingEditor *editor = (KeyBindingEditor *)data;
nkeynes@770
   143
    [editor updateKeysym: keysym];
nkeynes@770
   144
}
nkeynes@770
   145
nkeynes@770
   146
nkeynes@770
   147
@implementation KeyBindingField
nkeynes@770
   148
@end
nkeynes@770
   149
nkeynes@770
   150
/*************************** Key-binding sub-view ***********************/
nkeynes@770
   151
nkeynes@770
   152
#define MAX_KEY_BINDINGS 32
nkeynes@770
   153
nkeynes@770
   154
@interface ControllerKeyBindingView : NSView
nkeynes@770
   155
{
nkeynes@770
   156
    maple_device_t device;
nkeynes@770
   157
    KeyBindingField *field[MAX_KEY_BINDINGS][2];
nkeynes@770
   158
}
nkeynes@770
   159
- (id)initWithFrame: (NSRect)frameRect;
nkeynes@770
   160
- (void)setDevice: (maple_device_t)device;
nkeynes@770
   161
@end
nkeynes@770
   162
nkeynes@770
   163
@implementation ControllerKeyBindingView
nkeynes@770
   164
- (id)initWithFrame: (NSRect)frameRect
nkeynes@770
   165
{
nkeynes@770
   166
    if( [super initWithFrame: frameRect] == nil ) {
nkeynes@770
   167
        return nil;
nkeynes@770
   168
    } else {
nkeynes@770
   169
        device = NULL;
nkeynes@770
   170
        return self;
nkeynes@770
   171
    }
nkeynes@770
   172
}
nkeynes@770
   173
- (BOOL)isFlipped
nkeynes@770
   174
{
nkeynes@770
   175
    return YES;
nkeynes@770
   176
}
nkeynes@770
   177
- (void)removeSubviews
nkeynes@770
   178
{
nkeynes@770
   179
    [[self subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
nkeynes@770
   180
}
nkeynes@770
   181
- (void)controlTextDidChange: (NSNotification *)notify
nkeynes@770
   182
{
nkeynes@770
   183
    int binding = [[notify object] tag];
nkeynes@770
   184
    NSString *val1 = [field[binding][0] stringValue];
nkeynes@770
   185
    NSString *val2 = [field[binding][1] stringValue];
nkeynes@770
   186
    char buf[ [val1 length] + [val2 length] + 2 ];
nkeynes@770
   187
    const gchar *p = NULL;
nkeynes@770
   188
    
nkeynes@770
   189
    if( [val1 length] == 0 ) {
nkeynes@770
   190
        if( [val2 length] != 0 ) {
nkeynes@770
   191
            p = [val2 UTF8String];
nkeynes@770
   192
        }
nkeynes@770
   193
    } else if( [val2 length] == 0 ) {
nkeynes@770
   194
        p = [val1 UTF8String];
nkeynes@770
   195
    } else {
nkeynes@770
   196
        sprintf( buf, "%s,%s", [val1 UTF8String], [val2 UTF8String] );
nkeynes@770
   197
        p = buf;
nkeynes@770
   198
    }
nkeynes@770
   199
    maple_set_device_config_value( device, binding, p ); 
nkeynes@770
   200
    lxdream_save_config();
nkeynes@770
   201
}
nkeynes@770
   202
- (void)setDevice: (maple_device_t)newDevice
nkeynes@770
   203
{
nkeynes@770
   204
    device = newDevice;
nkeynes@770
   205
    [self removeSubviews];
nkeynes@770
   206
    if( device != NULL ) {
nkeynes@770
   207
        lxdream_config_entry_t config = maple_get_device_config(device);
nkeynes@770
   208
        if( config != NULL ) {
nkeynes@770
   209
            int count, i, y, x;
nkeynes@770
   210
nkeynes@770
   211
            for( count=0; config[count].key != NULL; count++ );
nkeynes@770
   212
            x = TEXT_GAP;
nkeynes@770
   213
            NSSize size = NSMakeSize(85*3+TEXT_GAP*4, count*(TEXT_HEIGHT+TEXT_GAP)+TEXT_GAP);
nkeynes@770
   214
            [self setFrameSize: size];
nkeynes@770
   215
            [self scrollRectToVisible: NSMakeRect(0,0,1,1)]; 
nkeynes@770
   216
            y = TEXT_GAP;
nkeynes@770
   217
            for( i=0; config[i].key != NULL; i++ ) {
nkeynes@770
   218
                NSRect frame = NSMakeRect(x, y + 2, 85, LABEL_HEIGHT);
nkeynes@770
   219
                NSTextField *label = cocoa_gui_add_label(self, NS_(config[i].label), frame);
nkeynes@770
   220
                [label setAlignment: NSRightTextAlignment];
nkeynes@770
   221
nkeynes@770
   222
                frame = NSMakeRect( x + 85 + TEXT_GAP, y, 85, TEXT_HEIGHT);
nkeynes@770
   223
                field[i][0] = [[KeyBindingField alloc] initWithFrame: frame];
nkeynes@770
   224
                [field[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
nkeynes@770
   225
                [field[i][0] setTag: i];
nkeynes@770
   226
                [field[i][0] setDelegate: self];
nkeynes@770
   227
                [self addSubview: field[i][0]];
nkeynes@770
   228
                
nkeynes@770
   229
                frame = NSMakeRect( x + (85*2) + (TEXT_GAP*2), y, 85, TEXT_HEIGHT);
nkeynes@770
   230
                field[i][1] = [[KeyBindingField alloc] initWithFrame: frame];
nkeynes@770
   231
                [field[i][1] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
nkeynes@770
   232
                [field[i][1] setTag: i];
nkeynes@770
   233
                [field[i][1] setDelegate: self];
nkeynes@770
   234
                [self addSubview: field[i][1]];
nkeynes@770
   235
nkeynes@770
   236
                if( config[i].value != NULL ) {
nkeynes@770
   237
                    gchar **parts = g_strsplit(config[i].value,",",3);
nkeynes@770
   238
                    if( parts[0] != NULL ) {
nkeynes@770
   239
                        [field[i][0] setStringValue: [NSString stringWithCString: parts[0]]];
nkeynes@770
   240
                        if( parts[1] != NULL ) {
nkeynes@770
   241
                            [field[i][1] setStringValue: [NSString stringWithCString: parts[1]]];
nkeynes@770
   242
                        }
nkeynes@770
   243
                    }
nkeynes@770
   244
                    g_strfreev(parts);
nkeynes@770
   245
                }
nkeynes@770
   246
                
nkeynes@770
   247
                y += (TEXT_HEIGHT + TEXT_GAP);
nkeynes@770
   248
            }
nkeynes@770
   249
        } else {
nkeynes@770
   250
            [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
nkeynes@770
   251
        }
nkeynes@770
   252
    } else {
nkeynes@770
   253
        [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
nkeynes@770
   254
    }
nkeynes@770
   255
}
nkeynes@770
   256
@end
nkeynes@770
   257
nkeynes@770
   258
/*************************** Top-level controller pane ***********************/
nkeynes@770
   259
nkeynes@770
   260
@interface LxdreamPrefsControllerPane: LxdreamPrefsPane
nkeynes@770
   261
{
nkeynes@770
   262
    struct maple_device *save_controller[4];
nkeynes@770
   263
    NSButton *radio[4];
nkeynes@770
   264
    ControllerKeyBindingView *key_bindings;
nkeynes@770
   265
}
nkeynes@770
   266
+ (LxdreamPrefsControllerPane *)new;
nkeynes@770
   267
@end
nkeynes@770
   268
nkeynes@765
   269
@implementation LxdreamPrefsControllerPane
nkeynes@765
   270
+ (LxdreamPrefsControllerPane *)new
nkeynes@765
   271
{
nkeynes@765
   272
    return [[LxdreamPrefsControllerPane alloc] initWithFrame: NSMakeRect(0,0,600,400)];
nkeynes@765
   273
}
nkeynes@765
   274
- (id)initWithFrame: (NSRect)frameRect
nkeynes@765
   275
{
nkeynes@765
   276
    if( [super initWithFrame: frameRect title: NS_("Controllers")] == nil ) {
nkeynes@765
   277
        return nil;
nkeynes@765
   278
    } else {
nkeynes@765
   279
        const struct maple_device_class **devices = maple_get_device_classes();
nkeynes@765
   280
        char buf[16];
nkeynes@765
   281
        int i,j;
nkeynes@770
   282
        int y = [self contentHeight] - TEXT_HEIGHT - TEXT_GAP;
nkeynes@770
   283
nkeynes@770
   284
        NSBox *rule = [[NSBox alloc] initWithFrame: 
nkeynes@770
   285
                NSMakeRect(210+(TEXT_GAP*3), 1, 1, [self contentHeight] + TEXT_GAP - 2)];
nkeynes@770
   286
        [rule setAutoresizingMask: (NSViewMaxXMargin|NSViewHeightSizable)];
nkeynes@770
   287
        [rule setBoxType: NSBoxSeparator];
nkeynes@770
   288
        [self addSubview: rule];
nkeynes@770
   289
        
nkeynes@770
   290
        NSRect bindingFrame = NSMakeRect(210+(TEXT_GAP*4), 0,
nkeynes@770
   291
                   frameRect.size.width - (210+(TEXT_GAP*4)), [self contentHeight] + TEXT_GAP );
nkeynes@770
   292
        NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: bindingFrame];
nkeynes@770
   293
        key_bindings = [[ControllerKeyBindingView alloc] initWithFrame: bindingFrame ];
nkeynes@770
   294
        [scrollView setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)];
nkeynes@770
   295
        [scrollView setDocumentView: key_bindings];
nkeynes@770
   296
        [scrollView setDrawsBackground: NO];
nkeynes@770
   297
        [scrollView setHasVerticalScroller: YES];
nkeynes@770
   298
        [scrollView setAutohidesScrollers: YES];
nkeynes@770
   299
 
nkeynes@770
   300
        [self addSubview: scrollView];
nkeynes@770
   301
        [key_bindings setDevice: maple_get_device(0,0)];
nkeynes@770
   302
        
nkeynes@765
   303
        for( i=0; i<MAX_DEVICES; i++ ) {
nkeynes@770
   304
            int x = TEXT_GAP;
nkeynes@765
   305
            save_controller[i] = NULL;
nkeynes@765
   306
            maple_device_t device = maple_get_device(i,0);
nkeynes@770
   307
nkeynes@765
   308
            snprintf( buf, sizeof(buf), _("Slot %d."), i );
nkeynes@770
   309
            radio[i] = [[NSButton alloc] initWithFrame: NSMakeRect( x, y, 60, TEXT_HEIGHT )];
nkeynes@770
   310
            [radio[i] setTitle: [NSString stringWithUTF8String: buf]];
nkeynes@770
   311
            [radio[i] setTag: i];
nkeynes@770
   312
            [radio[i] setButtonType: NSRadioButton];
nkeynes@770
   313
            [radio[i] setAlignment: NSRightTextAlignment];
nkeynes@770
   314
            [radio[i] setTarget: self];
nkeynes@770
   315
            [radio[i] setAction: @selector(radioChanged:)];
nkeynes@770
   316
            [radio[i] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
nkeynes@770
   317
            [self addSubview: radio[i]];
nkeynes@770
   318
            x += 60 + TEXT_GAP;
nkeynes@770
   319
nkeynes@770
   320
            NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame: NSMakeRect(x,y,150,TEXT_HEIGHT) 
nkeynes@770
   321
                                                          pullsDown: NO];
nkeynes@765
   322
            [popup addItemWithTitle: NS_("<empty>")];
nkeynes@765
   323
            [popup setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
nkeynes@765
   324
            [[popup itemAtIndex: 0] setTag: 0];
nkeynes@765
   325
            for( j=0; devices[j] != NULL; j++ ) {
nkeynes@765
   326
                [popup addItemWithTitle: [NSString stringWithUTF8String: devices[j]->name]];
nkeynes@765
   327
                if( device != NULL && device->device_class == devices[j] ) {
nkeynes@765
   328
                    [popup selectItemAtIndex: (j+1)];
nkeynes@765
   329
                }
nkeynes@765
   330
                [[popup itemAtIndex: (j+1)] setTag: (j+1)];
nkeynes@765
   331
            }
nkeynes@765
   332
            [popup setTarget: self];
nkeynes@765
   333
            [popup setAction: @selector(deviceChanged:)];
nkeynes@765
   334
            [popup setTag: i];
nkeynes@765
   335
            [self addSubview: popup];
nkeynes@770
   336
            y -= (TEXT_HEIGHT+TEXT_GAP);
nkeynes@765
   337
        }
nkeynes@770
   338
        
nkeynes@770
   339
        [radio[0] setState: NSOnState];
nkeynes@765
   340
        return self;
nkeynes@765
   341
    }
nkeynes@765
   342
}
nkeynes@770
   343
- (void)radioChanged: (id)sender
nkeynes@770
   344
{
nkeynes@770
   345
    int slot = [sender tag];
nkeynes@770
   346
    int i;
nkeynes@770
   347
    for( i=0; i<MAX_DEVICES; i++ ) {
nkeynes@770
   348
        if( i != slot ) {
nkeynes@770
   349
            [radio[i] setState: NSOffState];
nkeynes@770
   350
        }
nkeynes@770
   351
    }
nkeynes@770
   352
    [key_bindings setDevice: maple_get_device(slot,0)];
nkeynes@770
   353
}
nkeynes@765
   354
- (void)deviceChanged: (id)sender
nkeynes@765
   355
{
nkeynes@765
   356
    int slot = [sender tag];
nkeynes@770
   357
    int new_device_idx = [sender indexOfSelectedItem] - 1, i; 
nkeynes@765
   358
    maple_device_class_t new_device_class = NULL;
nkeynes@765
   359
    
nkeynes@770
   360
    for( i=0; i<MAX_DEVICES; i++ ) {
nkeynes@770
   361
        if( i == slot ) {
nkeynes@770
   362
            [radio[i] setState: NSOnState];
nkeynes@770
   363
        } else {
nkeynes@770
   364
            [radio[i] setState: NSOffState];
nkeynes@770
   365
        }
nkeynes@770
   366
    }
nkeynes@770
   367
    
nkeynes@765
   368
    maple_device_t current = maple_get_device(slot,0);
nkeynes@765
   369
    maple_device_t new_device = NULL;
nkeynes@765
   370
    if( new_device_idx != -1 ) {
nkeynes@765
   371
        new_device_class = maple_get_device_classes()[new_device_idx];
nkeynes@765
   372
    }
nkeynes@765
   373
    if( current == NULL ? new_device_class == NULL : current->device_class == new_device_class ) {
nkeynes@765
   374
        // No change
nkeynes@770
   375
        [key_bindings setDevice: current];
nkeynes@765
   376
        return;
nkeynes@765
   377
    }
nkeynes@765
   378
    if( current != NULL && current->device_class == &controller_class ) {
nkeynes@765
   379
        save_controller[slot] = current->clone(current);
nkeynes@765
   380
    }
nkeynes@765
   381
    if( new_device_class == NULL ) {
nkeynes@765
   382
        maple_detach_device(slot,0);
nkeynes@765
   383
    } else {
nkeynes@765
   384
        if( new_device_class == &controller_class && save_controller[slot] != NULL ) {
nkeynes@765
   385
            new_device = save_controller[slot];
nkeynes@765
   386
            save_controller[slot] = NULL;
nkeynes@765
   387
        } else {
nkeynes@765
   388
            new_device = maple_new_device( new_device_class->name );
nkeynes@765
   389
        }
nkeynes@765
   390
        maple_attach_device(new_device,slot,0);
nkeynes@765
   391
    }
nkeynes@770
   392
    [key_bindings setDevice: maple_get_device(slot,0)];
nkeynes@765
   393
    lxdream_save_config();
nkeynes@765
   394
}
nkeynes@765
   395
@end
nkeynes@770
   396
nkeynes@770
   397
NSView *cocoa_gui_create_prefs_controller_pane()
nkeynes@770
   398
{
nkeynes@770
   399
    return [LxdreamPrefsControllerPane new];
nkeynes@770
   400
}
.