📄 osxctrls.m
字号:
/*
* osxctrls.m: OS X implementation of the dialog.h interface.
*/
#import <Cocoa/Cocoa.h>
#include "putty.h"
#include "dialog.h"
#include "osxclass.h"
#include "tree234.h"
/*
* Still to be implemented:
*
* - file selectors (NSOpenPanel / NSSavePanel)
*
* - font selectors
* - colour selectors
* * both of these have a conceptual oddity in Cocoa that
* you're only supposed to have one per application. But I
* currently expect to be able to have multiple PuTTY config
* boxes on screen at once; what happens if you trigger the
* font selector in each one at the same time?
* * if it comes to that, the _font_ selector can probably be
* managed by other means: nobody is forcing me to implement
* a font selector using a `Change...' button. The portable
* dialog interface gives me the flexibility to do this how I
* want.
* * The colour selector interface, in its present form, is
* more interesting and _if_ a radical change of plan is
* required then it may stretch across the interface into the
* portable side.
* * Before I do anything rash I should start by looking at the
* Mac Classic port and see how it's done there, on the basis
* that Apple seem reasonably unlikely to have invented this
* crazy restriction specifically for OS X.
*
* - focus management
* * I tried using makeFirstResponder to give keyboard focus,
* but it appeared not to work. Try again, and work out how
* it should be done.
* * also look into tab order. Currently pressing Tab suggests
* that only edit boxes and list boxes can get the keyboard
* focus, and that buttons (in all their forms) are unable to
* be driven by the keyboard. Find out for sure.
*
* - dlg_error_msg
* * this may run into the usual aggro with modal dialog boxes.
*/
/*
* For Cocoa control layout, I need a two-stage process. In stage
* one, I allocate all the controls and measure their natural
* sizes, which allows me to compute the _minimum_ width and height
* of a given section of dialog. Then, in stage two, I lay out the
* dialog box as a whole, decide how much each section of the box
* needs to receive, and assign it its final size.
*/
/*
* As yet unsolved issues [FIXME]:
*
* - Sometimes the height returned from create_ctrls and the
* height returned from place_ctrls differ. Find out why. It may
* be harmless (e.g. results of NSTextView being odd), but I
* want to know.
*
* - NSTextViews are indented a bit. It'd be nice to put their
* left margin at the same place as everything else's.
*
* - I don't yet know whether we even _can_ support tab order or
* keyboard shortcuts. If we can't, then fair enough, we can't.
* But if we can, we should.
*
* - I would _really_ like to know of a better way to correct
* NSButton's stupid size estimates than by subclassing it and
* overriding sizeToFit with hard-wired sensible values!
*
* - Speaking of stupid size estimates, the amount by which I'm
* adjusting a titled NSBox (currently equal to the point size
* of its title font) looks as if it isn't _quite_ enough.
* Figure out what the real amount should be and use it.
*
* - I don't understand why there's always a scrollbar displayed
* in each list box. I thought I told it to autohide scrollers?
*
* - Why do I have to fudge list box heights by adding one? (Might
* it be to do with the missing header view?)
*/
/*
* Subclass of NSButton which corrects the fact that the normal
* one's sizeToFit method persistently returns 32 as its height,
* which is simply a lie. I have yet to work out a better
* alternative than hard-coding the real heights.
*/
@interface MyButton : NSButton
{
int minht;
}
@end
@implementation MyButton
- (id)initWithFrame:(NSRect)r
{
self = [super initWithFrame:r];
minht = 25;
return self;
}
- (void)setButtonType:(NSButtonType)t
{
if (t == NSRadioButton || t == NSSwitchButton)
minht = 18;
else
minht = 25;
[super setButtonType:t];
}
- (void)sizeToFit
{
NSRect r;
[super sizeToFit];
r = [self frame];
r.size.height = minht;
[self setFrame:r];
}
@end
/*
* Class used as the data source for NSTableViews.
*/
@interface MyTableSource : NSObject
{
tree234 *tree;
}
- (id)init;
- (void)add:(const char *)str withId:(int)id;
- (int)getid:(int)index;
- (void)swap:(int)index1 with:(int)index2;
- (void)removestr:(int)index;
- (void)clear;
@end
@implementation MyTableSource
- (id)init
{
self = [super init];
tree = newtree234(NULL);
return self;
}
- (void)dealloc
{
char *p;
while ((p = delpos234(tree, 0)) != NULL)
sfree(p);
freetree234(tree);
[super dealloc];
}
- (void)add:(const char *)str withId:(int)id
{
addpos234(tree, dupprintf("%d\t%s", id, str), count234(tree));
}
- (int)getid:(int)index
{
char *p = index234(tree, index);
return atoi(p);
}
- (void)removestr:(int)index
{
char *p = delpos234(tree, index);
sfree(p);
}
- (void)swap:(int)index1 with:(int)index2
{
char *p1, *p2;
if (index1 > index2) {
int t = index1; index1 = index2; index2 = t;
}
/* delete later one first so it doesn't affect index of earlier one */
p2 = delpos234(tree, index2);
p1 = delpos234(tree, index1);
/* now insert earlier one before later one for the inverse reason */
addpos234(tree, p2, index1);
addpos234(tree, p1, index2);
}
- (void)clear
{
char *p;
while ((p = delpos234(tree, 0)) != NULL)
sfree(p);
}
- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
return count234(tree);
}
- (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(int)rowIndex
{
int j = [[aTableColumn identifier] intValue];
char *p = index234(tree, rowIndex);
while (j >= 0) {
p += strcspn(p, "\t");
if (*p) p++;
j--;
}
return [NSString stringWithCString:p length:strcspn(p, "\t")];
}
@end
/*
* Object to receive messages from various control classes.
*/
@class Receiver;
struct fe_dlg {
NSWindow *window;
NSObject *target;
SEL action;
tree234 *byctrl;
tree234 *bywidget;
tree234 *boxes;
void *data; /* passed to portable side */
Receiver *rec;
};
@interface Receiver : NSObject
{
struct fe_dlg *d;
}
- (id)initWithStruct:(struct fe_dlg *)aStruct;
@end
struct fe_ctrl {
union control *ctrl;
NSButton *button, *button2;
NSTextField *label, *editbox;
NSComboBox *combobox;
NSButton **radiobuttons;
NSTextView *textview;
NSPopUpButton *popupbutton;
NSTableView *tableview;
NSScrollView *scrollview;
int nradiobuttons;
void *privdata;
int privdata_needs_free;
};
static int fe_ctrl_cmp_by_ctrl(void *av, void *bv)
{
struct fe_ctrl *a = (struct fe_ctrl *)av;
struct fe_ctrl *b = (struct fe_ctrl *)bv;
if (a->ctrl < b->ctrl)
return -1;
if (a->ctrl > b->ctrl)
return +1;
return 0;
}
static int fe_ctrl_find_by_ctrl(void *av, void *bv)
{
union control *a = (union control *)av;
struct fe_ctrl *b = (struct fe_ctrl *)bv;
if (a < b->ctrl)
return -1;
if (a > b->ctrl)
return +1;
return 0;
}
struct fe_box {
struct controlset *s;
id box;
};
static int fe_boxcmp(void *av, void *bv)
{
struct fe_box *a = (struct fe_box *)av;
struct fe_box *b = (struct fe_box *)bv;
if (a->s < b->s)
return -1;
if (a->s > b->s)
return +1;
return 0;
}
static int fe_boxfind(void *av, void *bv)
{
struct controlset *a = (struct controlset *)av;
struct fe_box *b = (struct fe_box *)bv;
if (a < b->s)
return -1;
if (a > b->s)
return +1;
return 0;
}
struct fe_backwards { /* map Cocoa widgets back to fe_ctrls */
id widget;
struct fe_ctrl *c;
};
static int fe_backwards_cmp_by_widget(void *av, void *bv)
{
struct fe_backwards *a = (struct fe_backwards *)av;
struct fe_backwards *b = (struct fe_backwards *)bv;
if (a->widget < b->widget)
return -1;
if (a->widget > b->widget)
return +1;
return 0;
}
static int fe_backwards_find_by_widget(void *av, void *bv)
{
id a = (id)av;
struct fe_backwards *b = (struct fe_backwards *)bv;
if (a < b->widget)
return -1;
if (a > b->widget)
return +1;
return 0;
}
static struct fe_ctrl *fe_ctrl_new(union control *ctrl)
{
struct fe_ctrl *c;
c = snew(struct fe_ctrl);
c->ctrl = ctrl;
c->button = c->button2 = nil;
c->label = nil;
c->editbox = nil;
c->combobox = nil;
c->textview = nil;
c->popupbutton = nil;
c->tableview = nil;
c->scrollview = nil;
c->radiobuttons = NULL;
c->nradiobuttons = 0;
c->privdata = NULL;
c->privdata_needs_free = FALSE;
return c;
}
static void fe_ctrl_free(struct fe_ctrl *c)
{
if (c->privdata_needs_free)
sfree(c->privdata);
sfree(c->radiobuttons);
sfree(c);
}
static struct fe_ctrl *fe_ctrl_byctrl(struct fe_dlg *d, union control *ctrl)
{
return find234(d->byctrl, ctrl, fe_ctrl_find_by_ctrl);
}
static void add_box(struct fe_dlg *d, struct controlset *s, id box)
{
struct fe_box *b = snew(struct fe_box);
b->box = box;
b->s = s;
add234(d->boxes, b);
}
static id find_box(struct fe_dlg *d, struct controlset *s)
{
struct fe_box *b = find234(d->boxes, s, fe_boxfind);
return b ? b->box : NULL;
}
static void add_widget(struct fe_dlg *d, struct fe_ctrl *c, id widget)
{
struct fe_backwards *b = snew(struct fe_backwards);
b->widget = widget;
b->c = c;
add234(d->bywidget, b);
}
static struct fe_ctrl *find_widget(struct fe_dlg *d, id widget)
{
struct fe_backwards *b = find234(d->bywidget, widget,
fe_backwards_find_by_widget);
return b ? b->c : NULL;
}
void *fe_dlg_init(void *data, NSWindow *window, NSObject *target, SEL action)
{
struct fe_dlg *d;
d = snew(struct fe_dlg);
d->window = window;
d->target = target;
d->action = action;
d->byctrl = newtree234(fe_ctrl_cmp_by_ctrl);
d->bywidget = newtree234(fe_backwards_cmp_by_widget);
d->boxes = newtree234(fe_boxcmp);
d->data = data;
d->rec = [[Receiver alloc] initWithStruct:d];
return d;
}
void fe_dlg_free(void *dv)
{
struct fe_dlg *d = (struct fe_dlg *)dv;
struct fe_ctrl *c;
struct fe_box *b;
while ( (c = delpos234(d->byctrl, 0)) != NULL )
fe_ctrl_free(c);
freetree234(d->byctrl);
while ( (c = delpos234(d->bywidget, 0)) != NULL )
sfree(c);
freetree234(d->bywidget);
while ( (b = delpos234(d->boxes, 0)) != NULL )
sfree(b);
freetree234(d->boxes);
[d->rec release];
sfree(d);
}
@implementation Receiver
- (id)initWithStruct:(struct fe_dlg *)aStruct
{
self = [super init];
d = aStruct;
return self;
}
- (void)buttonPushed:(id)sender
{
struct fe_ctrl *c = find_widget(d, sender);
assert(c && c->ctrl->generic.type == CTRL_BUTTON);
c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION);
}
- (void)checkboxChanged:(id)sender
{
struct fe_ctrl *c = find_widget(d, sender);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -