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 787:6717c02ff81f
prev770:429ff505c450
next849:bbe26d798fc2
author nkeynes
date Tue Jul 29 10:55:51 2008 +0000 (14 years ago)
permissions -rw-r--r--
last change Auto-cancel if the binding field loses focus
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)resignFirstResponder
    68 {
    69     if( isPrimed ) {
    70         [self setString: lastValue];
    71         [self setPrimed: NO];
    72     }
    73     [super resignFirstResponder];
    74 }
    75 - (void)fireBindingChanged
    76 {
    77     id delegate = [self delegate];
    78     if( delegate != nil && [delegate respondsToSelector:@selector(textDidChange:)] ) {
    79         [delegate textDidChange: [NSNotification notificationWithName: NSTextDidChangeNotification object: self]];
    80     }
    81 }
    83 - (void)updateKeysym: (const gchar *)sym
    84 {
    85     if( sym != NULL ) {
    86         [self setString: [NSString stringWithCString: sym]];
    87         [self setPrimed: NO];
    88         [self fireBindingChanged];
    89     }
    90 }
    91 - (void)keyPressed: (int)keycode
    92 {
    93     gchar *keysym = input_keycode_to_keysym(NULL, keycode);
    94     if( keysym != NULL ) {
    95         [self updateKeysym: keysym];
    96         g_free(keysym);
    97     }
    98 }
    99 - (void)insertText:(id)string
   100 {
   101     // Do nothing
   102 }
   103 - (void)mouseDown: (NSEvent *)event
   104 {
   105     [self setPrimed: YES];
   106     [super mouseDown: event];
   107 }
   108 - (void)keyDown: (NSEvent *) event
   109 {
   110     NSString *chars = [event characters];
   111     if( isPrimed ) {
   112         if( chars != NULL && [chars length] == 1 && [chars characterAtIndex: 0] == 27 ) {
   113             // Escape char = abort change
   114             [self setString: lastValue];
   115             [self setPrimed: NO];
   116         } else {
   117             [self keyPressed: ([event keyCode]+1)];
   118         }
   119     } else {
   120         if( chars != NULL && [chars length] == 1 ) {
   121             int ch = [chars characterAtIndex: 0];
   122             switch( ch ) {
   123             case 0x7F:
   124                 [self setString: @""]; 
   125                 [self fireBindingChanged];
   126                 break;
   127             case '\r':
   128                 [self setPrimed: YES];
   129                 break;
   130             default:
   131                 [super keyDown: event];
   132                 break;
   133             }
   134         } else {
   135             [super keyDown: event];
   136         }
   137     }
   138 }
   139 - (void)flagsChanged: (NSEvent *) event
   140 {
   141     if( isPrimed ) {
   142         [self keyPressed: ([event keyCode]+1)];
   143     }
   144     [super flagsChanged: event];
   145 }
   146 @end
   148 static void cocoa_config_keysym_hook(void *data, const gchar *keysym)
   149 {
   150     KeyBindingEditor *editor = (KeyBindingEditor *)data;
   151     [editor updateKeysym: keysym];
   152 }
   155 @implementation KeyBindingField
   156 @end
   158 /*************************** Key-binding sub-view ***********************/
   160 #define MAX_KEY_BINDINGS 32
   162 @interface ControllerKeyBindingView : NSView
   163 {
   164     maple_device_t device;
   165     KeyBindingField *field[MAX_KEY_BINDINGS][2];
   166 }
   167 - (id)initWithFrame: (NSRect)frameRect;
   168 - (void)setDevice: (maple_device_t)device;
   169 @end
   171 @implementation ControllerKeyBindingView
   172 - (id)initWithFrame: (NSRect)frameRect
   173 {
   174     if( [super initWithFrame: frameRect] == nil ) {
   175         return nil;
   176     } else {
   177         device = NULL;
   178         return self;
   179     }
   180 }
   181 - (BOOL)isFlipped
   182 {
   183     return YES;
   184 }
   185 - (void)removeSubviews
   186 {
   187     [[self subviews] makeObjectsPerformSelector: @selector(removeFromSuperview)];
   188 }
   189 - (void)controlTextDidChange: (NSNotification *)notify
   190 {
   191     int binding = [[notify object] tag];
   192     NSString *val1 = [field[binding][0] stringValue];
   193     NSString *val2 = [field[binding][1] stringValue];
   194     char buf[ [val1 length] + [val2 length] + 2 ];
   195     const gchar *p = NULL;
   197     if( [val1 length] == 0 ) {
   198         if( [val2 length] != 0 ) {
   199             p = [val2 UTF8String];
   200         }
   201     } else if( [val2 length] == 0 ) {
   202         p = [val1 UTF8String];
   203     } else {
   204         sprintf( buf, "%s,%s", [val1 UTF8String], [val2 UTF8String] );
   205         p = buf;
   206     }
   207     maple_set_device_config_value( device, binding, p ); 
   208     lxdream_save_config();
   209 }
   210 - (void)setDevice: (maple_device_t)newDevice
   211 {
   212     device = newDevice;
   213     [self removeSubviews];
   214     if( device != NULL ) {
   215         lxdream_config_entry_t config = maple_get_device_config(device);
   216         if( config != NULL ) {
   217             int count, i, y, x;
   219             for( count=0; config[count].key != NULL; count++ );
   220             x = TEXT_GAP;
   221             NSSize size = NSMakeSize(85*3+TEXT_GAP*4, count*(TEXT_HEIGHT+TEXT_GAP)+TEXT_GAP);
   222             [self setFrameSize: size];
   223             [self scrollRectToVisible: NSMakeRect(0,0,1,1)]; 
   224             y = TEXT_GAP;
   225             for( i=0; config[i].key != NULL; i++ ) {
   226                 NSRect frame = NSMakeRect(x, y + 2, 85, LABEL_HEIGHT);
   227                 NSTextField *label = cocoa_gui_add_label(self, NS_(config[i].label), frame);
   228                 [label setAlignment: NSRightTextAlignment];
   230                 frame = NSMakeRect( x + 85 + TEXT_GAP, y, 85, TEXT_HEIGHT);
   231                 field[i][0] = [[KeyBindingField alloc] initWithFrame: frame];
   232                 [field[i][0] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
   233                 [field[i][0] setTag: i];
   234                 [field[i][0] setDelegate: self];
   235                 [self addSubview: field[i][0]];
   237                 frame = NSMakeRect( x + (85*2) + (TEXT_GAP*2), y, 85, TEXT_HEIGHT);
   238                 field[i][1] = [[KeyBindingField alloc] initWithFrame: frame];
   239                 [field[i][1] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
   240                 [field[i][1] setTag: i];
   241                 [field[i][1] setDelegate: self];
   242                 [self addSubview: field[i][1]];
   244                 if( config[i].value != NULL ) {
   245                     gchar **parts = g_strsplit(config[i].value,",",3);
   246                     if( parts[0] != NULL ) {
   247                         [field[i][0] setStringValue: [NSString stringWithCString: parts[0]]];
   248                         if( parts[1] != NULL ) {
   249                             [field[i][1] setStringValue: [NSString stringWithCString: parts[1]]];
   250                         }
   251                     }
   252                     g_strfreev(parts);
   253                 }
   255                 y += (TEXT_HEIGHT + TEXT_GAP);
   256             }
   257         } else {
   258             [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
   259         }
   260     } else {
   261         [self setFrameSize: NSMakeSize(100,TEXT_HEIGHT+TEXT_GAP) ];
   262     }
   263 }
   264 @end
   266 /*************************** Top-level controller pane ***********************/
   268 @interface LxdreamPrefsControllerPane: LxdreamPrefsPane
   269 {
   270     struct maple_device *save_controller[4];
   271     NSButton *radio[4];
   272     ControllerKeyBindingView *key_bindings;
   273 }
   274 + (LxdreamPrefsControllerPane *)new;
   275 @end
   277 @implementation LxdreamPrefsControllerPane
   278 + (LxdreamPrefsControllerPane *)new
   279 {
   280     return [[LxdreamPrefsControllerPane alloc] initWithFrame: NSMakeRect(0,0,600,400)];
   281 }
   282 - (id)initWithFrame: (NSRect)frameRect
   283 {
   284     if( [super initWithFrame: frameRect title: NS_("Controllers")] == nil ) {
   285         return nil;
   286     } else {
   287         const struct maple_device_class **devices = maple_get_device_classes();
   288         char buf[16];
   289         int i,j;
   290         int y = [self contentHeight] - TEXT_HEIGHT - TEXT_GAP;
   292         NSBox *rule = [[NSBox alloc] initWithFrame: 
   293                 NSMakeRect(210+(TEXT_GAP*3), 1, 1, [self contentHeight] + TEXT_GAP - 2)];
   294         [rule setAutoresizingMask: (NSViewMaxXMargin|NSViewHeightSizable)];
   295         [rule setBoxType: NSBoxSeparator];
   296         [self addSubview: rule];
   298         NSRect bindingFrame = NSMakeRect(210+(TEXT_GAP*4), 0,
   299                    frameRect.size.width - (210+(TEXT_GAP*4)), [self contentHeight] + TEXT_GAP );
   300         NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame: bindingFrame];
   301         key_bindings = [[ControllerKeyBindingView alloc] initWithFrame: bindingFrame ];
   302         [scrollView setAutoresizingMask: (NSViewWidthSizable|NSViewHeightSizable)];
   303         [scrollView setDocumentView: key_bindings];
   304         [scrollView setDrawsBackground: NO];
   305         [scrollView setHasVerticalScroller: YES];
   306         [scrollView setAutohidesScrollers: YES];
   308         [self addSubview: scrollView];
   309         [key_bindings setDevice: maple_get_device(0,0)];
   311         for( i=0; i<MAX_DEVICES; i++ ) {
   312             int x = TEXT_GAP;
   313             save_controller[i] = NULL;
   314             maple_device_t device = maple_get_device(i,0);
   316             snprintf( buf, sizeof(buf), _("Slot %d."), i );
   317             radio[i] = [[NSButton alloc] initWithFrame: NSMakeRect( x, y, 60, TEXT_HEIGHT )];
   318             [radio[i] setTitle: [NSString stringWithUTF8String: buf]];
   319             [radio[i] setTag: i];
   320             [radio[i] setButtonType: NSRadioButton];
   321             [radio[i] setAlignment: NSRightTextAlignment];
   322             [radio[i] setTarget: self];
   323             [radio[i] setAction: @selector(radioChanged:)];
   324             [radio[i] setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
   325             [self addSubview: radio[i]];
   326             x += 60 + TEXT_GAP;
   328             NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame: NSMakeRect(x,y,150,TEXT_HEIGHT) 
   329                                                           pullsDown: NO];
   330             [popup addItemWithTitle: NS_("<empty>")];
   331             [popup setAutoresizingMask: (NSViewMinYMargin|NSViewMaxXMargin)];
   332             [[popup itemAtIndex: 0] setTag: 0];
   333             for( j=0; devices[j] != NULL; j++ ) {
   334                 [popup addItemWithTitle: [NSString stringWithUTF8String: devices[j]->name]];
   335                 if( device != NULL && device->device_class == devices[j] ) {
   336                     [popup selectItemAtIndex: (j+1)];
   337                 }
   338                 [[popup itemAtIndex: (j+1)] setTag: (j+1)];
   339             }
   340             [popup setTarget: self];
   341             [popup setAction: @selector(deviceChanged:)];
   342             [popup setTag: i];
   343             [self addSubview: popup];
   344             y -= (TEXT_HEIGHT+TEXT_GAP);
   345         }
   347         [radio[0] setState: NSOnState];
   348         return self;
   349     }
   350 }
   351 - (void)radioChanged: (id)sender
   352 {
   353     int slot = [sender tag];
   354     int i;
   355     for( i=0; i<MAX_DEVICES; i++ ) {
   356         if( i != slot ) {
   357             [radio[i] setState: NSOffState];
   358         }
   359     }
   360     [key_bindings setDevice: maple_get_device(slot,0)];
   361 }
   362 - (void)deviceChanged: (id)sender
   363 {
   364     int slot = [sender tag];
   365     int new_device_idx = [sender indexOfSelectedItem] - 1, i; 
   366     maple_device_class_t new_device_class = NULL;
   368     for( i=0; i<MAX_DEVICES; i++ ) {
   369         if( i == slot ) {
   370             [radio[i] setState: NSOnState];
   371         } else {
   372             [radio[i] setState: NSOffState];
   373         }
   374     }
   376     maple_device_t current = maple_get_device(slot,0);
   377     maple_device_t new_device = NULL;
   378     if( new_device_idx != -1 ) {
   379         new_device_class = maple_get_device_classes()[new_device_idx];
   380     }
   381     if( current == NULL ? new_device_class == NULL : current->device_class == new_device_class ) {
   382         // No change
   383         [key_bindings setDevice: current];
   384         return;
   385     }
   386     if( current != NULL && current->device_class == &controller_class ) {
   387         save_controller[slot] = current->clone(current);
   388     }
   389     if( new_device_class == NULL ) {
   390         maple_detach_device(slot,0);
   391     } else {
   392         if( new_device_class == &controller_class && save_controller[slot] != NULL ) {
   393             new_device = save_controller[slot];
   394             save_controller[slot] = NULL;
   395         } else {
   396             new_device = maple_new_device( new_device_class->name );
   397         }
   398         maple_attach_device(new_device,slot,0);
   399     }
   400     [key_bindings setDevice: maple_get_device(slot,0)];
   401     lxdream_save_config();
   402 }
   403 @end
   405 NSView *cocoa_gui_create_prefs_controller_pane()
   406 {
   407     return [LxdreamPrefsControllerPane new];
   408 }
.