⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 osxctrls.m

📁 putty
💻 M
📖 第 1 页 / 共 4 页
字号:
/*
 * 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 + -