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