зеркало из https://github.com/github/putty.git
1816 строки
44 KiB
Objective-C
1816 строки
44 KiB
Objective-C
/*
|
|
* 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);
|
|
|
|
assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);
|
|
c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
|
|
}
|
|
- (void)radioChanged:(id)sender
|
|
{
|
|
struct fe_ctrl *c = find_widget(d, sender);
|
|
int j;
|
|
|
|
assert(c && c->radiobuttons);
|
|
for (j = 0; j < c->nradiobuttons; j++)
|
|
if (sender != c->radiobuttons[j])
|
|
[c->radiobuttons[j] setState:NSOffState];
|
|
c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
|
|
}
|
|
- (void)popupMenuSelected:(id)sender
|
|
{
|
|
struct fe_ctrl *c = find_widget(d, sender);
|
|
c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
|
|
}
|
|
- (void)controlTextDidChange:(NSNotification *)notification
|
|
{
|
|
id widget = [notification object];
|
|
struct fe_ctrl *c = find_widget(d, widget);
|
|
assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
|
|
c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
|
|
}
|
|
- (void)controlTextDidEndEditing:(NSNotification *)notification
|
|
{
|
|
id widget = [notification object];
|
|
struct fe_ctrl *c = find_widget(d, widget);
|
|
assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
|
|
c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_REFRESH);
|
|
}
|
|
- (void)tableViewSelectionDidChange:(NSNotification *)notification
|
|
{
|
|
id widget = [notification object];
|
|
struct fe_ctrl *c = find_widget(d, widget);
|
|
assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
|
|
c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_SELCHANGE);
|
|
}
|
|
- (BOOL)tableView:(NSTableView *)aTableView
|
|
shouldEditTableColumn:(NSTableColumn *)aTableColumn
|
|
row:(int)rowIndex
|
|
{
|
|
return NO; /* no editing permitted */
|
|
}
|
|
- (void)listDoubleClicked:(id)sender
|
|
{
|
|
struct fe_ctrl *c = find_widget(d, sender);
|
|
assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
|
|
c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION);
|
|
}
|
|
- (void)dragListButton:(id)sender
|
|
{
|
|
struct fe_ctrl *c = find_widget(d, sender);
|
|
int direction, row, nrows;
|
|
assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
|
|
c->ctrl->listbox.draglist);
|
|
|
|
if (sender == c->button)
|
|
direction = -1; /* up */
|
|
else
|
|
direction = +1; /* down */
|
|
|
|
row = [c->tableview selectedRow];
|
|
nrows = [c->tableview numberOfRows];
|
|
|
|
if (row + direction < 0 || row + direction >= nrows) {
|
|
NSBeep();
|
|
return;
|
|
}
|
|
|
|
[[c->tableview dataSource] swap:row with:row+direction];
|
|
[c->tableview reloadData];
|
|
[c->tableview selectRow:row+direction byExtendingSelection:NO];
|
|
|
|
c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
|
|
}
|
|
@end
|
|
|
|
void create_ctrls(void *dv, NSView *parent, struct controlset *s,
|
|
int *minw, int *minh)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
int ccw[100]; /* cumulative column widths */
|
|
int cypos[100];
|
|
int ncols;
|
|
int wmin = 0, hmin = 0;
|
|
int i, j, cw, ch;
|
|
NSRect rect;
|
|
NSFont *textviewfont = nil;
|
|
int boxh = 0, boxw = 0;
|
|
|
|
if (!s->boxname && s->boxtitle) {
|
|
/* This controlset is a panel title. */
|
|
|
|
NSTextField *tf;
|
|
|
|
tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];
|
|
[tf setEditable:NO];
|
|
[tf setSelectable:NO];
|
|
[tf setBordered:NO];
|
|
[tf setDrawsBackground:NO];
|
|
[tf setStringValue:[NSString stringWithCString:s->boxtitle]];
|
|
[tf sizeToFit];
|
|
rect = [tf frame];
|
|
[parent addSubview:tf];
|
|
|
|
/*
|
|
* I'm going to store this NSTextField in the boxes tree,
|
|
* because I really can't face having a special tree234
|
|
* mapping controlsets to panel titles.
|
|
*/
|
|
add_box(d, s, tf);
|
|
|
|
*minw = rect.size.width;
|
|
*minh = rect.size.height;
|
|
|
|
return;
|
|
}
|
|
|
|
if (*s->boxname) {
|
|
/*
|
|
* Create an NSBox to contain this subset of controls.
|
|
*/
|
|
NSBox *box;
|
|
NSRect tmprect;
|
|
|
|
box = [[NSBox alloc] initWithFrame:NSMakeRect(0,0,1,1)];
|
|
if (s->boxtitle)
|
|
[box setTitle:[NSString stringWithCString:s->boxtitle]];
|
|
else
|
|
[box setTitlePosition:NSNoTitle];
|
|
add_box(d, s, box);
|
|
tmprect = [box frame];
|
|
[box setContentViewMargins:NSMakeSize(20,20)];
|
|
[box setFrameFromContentFrame:NSMakeRect(100,100,100,100)];
|
|
rect = [box frame];
|
|
[box setFrame:tmprect];
|
|
boxh = (int)(rect.size.height - 100);
|
|
boxw = (int)(rect.size.width - 100);
|
|
[parent addSubview:box];
|
|
|
|
if (s->boxtitle)
|
|
boxh += [[box titleFont] pointSize];
|
|
|
|
/*
|
|
* All subsequent controls will be placed within this box.
|
|
*/
|
|
parent = box;
|
|
}
|
|
|
|
ncols = 1;
|
|
ccw[0] = 0;
|
|
ccw[1] = 100;
|
|
cypos[0] = 0;
|
|
|
|
/*
|
|
* Now iterate through the controls themselves, create them,
|
|
* and add their width and height to the overall width/height
|
|
* calculation.
|
|
*/
|
|
for (i = 0; i < s->ncontrols; i++) {
|
|
union control *ctrl = s->ctrls[i];
|
|
struct fe_ctrl *c;
|
|
int colstart = COLUMN_START(ctrl->generic.column);
|
|
int colspan = COLUMN_SPAN(ctrl->generic.column);
|
|
int colend = colstart + colspan;
|
|
int ytop, wthis;
|
|
|
|
switch (ctrl->generic.type) {
|
|
case CTRL_COLUMNS:
|
|
for (j = 1; j < ncols; j++)
|
|
if (cypos[0] < cypos[j])
|
|
cypos[0] = cypos[j];
|
|
|
|
assert(ctrl->columns.ncols < lenof(ccw));
|
|
|
|
ccw[0] = 0;
|
|
for (j = 0; j < ctrl->columns.ncols; j++) {
|
|
ccw[j+1] = ccw[j] + (ctrl->columns.percentages ?
|
|
ctrl->columns.percentages[j] : 100);
|
|
cypos[j] = cypos[0];
|
|
}
|
|
|
|
ncols = ctrl->columns.ncols;
|
|
|
|
continue; /* no actual control created */
|
|
case CTRL_TABDELAY:
|
|
/*
|
|
* I'm currently uncertain that we can implement tab
|
|
* order in OS X.
|
|
*/
|
|
continue; /* no actual control created */
|
|
}
|
|
|
|
c = fe_ctrl_new(ctrl);
|
|
add234(d->byctrl, c);
|
|
|
|
cw = ch = 0;
|
|
|
|
switch (ctrl->generic.type) {
|
|
case CTRL_BUTTON:
|
|
case CTRL_CHECKBOX:
|
|
{
|
|
NSButton *b;
|
|
|
|
b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];
|
|
[b setBezelStyle:NSRoundedBezelStyle];
|
|
if (ctrl->generic.type == CTRL_CHECKBOX)
|
|
[b setButtonType:NSSwitchButton];
|
|
[b setTitle:[NSString stringWithCString:ctrl->generic.label]];
|
|
if (ctrl->button.isdefault)
|
|
[b setKeyEquivalent:@"\r"];
|
|
else if (ctrl->button.iscancel)
|
|
[b setKeyEquivalent:@"\033"];
|
|
[b sizeToFit];
|
|
rect = [b frame];
|
|
|
|
[parent addSubview:b];
|
|
|
|
[b setTarget:d->rec];
|
|
if (ctrl->generic.type == CTRL_CHECKBOX)
|
|
[b setAction:@selector(checkboxChanged:)];
|
|
else
|
|
[b setAction:@selector(buttonPushed:)];
|
|
add_widget(d, c, b);
|
|
|
|
c->button = b;
|
|
|
|
cw = rect.size.width;
|
|
ch = rect.size.height;
|
|
}
|
|
break;
|
|
case CTRL_EDITBOX:
|
|
{
|
|
int editp = ctrl->editbox.percentwidth;
|
|
int labelp = editp == 100 ? 100 : 100 - editp;
|
|
NSTextField *tf;
|
|
NSComboBox *cb;
|
|
|
|
tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];
|
|
[tf setEditable:NO];
|
|
[tf setSelectable:NO];
|
|
[tf setBordered:NO];
|
|
[tf setDrawsBackground:NO];
|
|
[tf setStringValue:[NSString
|
|
stringWithCString:ctrl->generic.label]];
|
|
[tf sizeToFit];
|
|
rect = [tf frame];
|
|
[parent addSubview:tf];
|
|
c->label = tf;
|
|
|
|
cw = rect.size.width * 100 / labelp;
|
|
ch = rect.size.height;
|
|
|
|
if (ctrl->editbox.has_list) {
|
|
cb = [[NSComboBox alloc]
|
|
initWithFrame:NSMakeRect(0,0,1,1)];
|
|
[cb setStringValue:@"x"];
|
|
[cb sizeToFit];
|
|
rect = [cb frame];
|
|
[parent addSubview:cb];
|
|
c->combobox = cb;
|
|
} else {
|
|
if (ctrl->editbox.password)
|
|
tf = [NSSecureTextField alloc];
|
|
else
|
|
tf = [NSTextField alloc];
|
|
|
|
tf = [tf initWithFrame:NSMakeRect(0,0,1,1)];
|
|
[tf setEditable:YES];
|
|
[tf setSelectable:YES];
|
|
[tf setBordered:YES];
|
|
[tf setStringValue:@"x"];
|
|
[tf sizeToFit];
|
|
rect = [tf frame];
|
|
[parent addSubview:tf];
|
|
c->editbox = tf;
|
|
|
|
[tf setDelegate:d->rec];
|
|
add_widget(d, c, tf);
|
|
}
|
|
|
|
if (editp == 100) {
|
|
/* the edit box and its label are vertically separated */
|
|
ch += VSPACING + rect.size.height;
|
|
} else {
|
|
/* the edit box and its label are horizontally separated */
|
|
if (ch < rect.size.height)
|
|
ch = rect.size.height;
|
|
}
|
|
|
|
if (cw < rect.size.width * 100 / editp)
|
|
cw = rect.size.width * 100 / editp;
|
|
}
|
|
break;
|
|
case CTRL_TEXT:
|
|
{
|
|
NSTextView *tv;
|
|
int testwid;
|
|
|
|
if (!textviewfont) {
|
|
NSTextField *tf;
|
|
tf = [[NSTextField alloc] init];
|
|
textviewfont = [tf font];
|
|
[tf release];
|
|
}
|
|
|
|
testwid = (ccw[colend] - ccw[colstart]) * 3;
|
|
|
|
tv = [[NSTextView alloc]
|
|
initWithFrame:NSMakeRect(0,0,testwid,1)];
|
|
[tv setEditable:NO];
|
|
[tv setSelectable:NO];
|
|
//[tv setBordered:NO];
|
|
[tv setDrawsBackground:NO];
|
|
[tv setFont:textviewfont];
|
|
[tv setString:
|
|
[NSString stringWithCString:ctrl->generic.label]];
|
|
rect = [tv frame];
|
|
[tv sizeToFit];
|
|
[parent addSubview:tv];
|
|
c->textview = tv;
|
|
|
|
cw = rect.size.width;
|
|
ch = rect.size.height;
|
|
}
|
|
break;
|
|
case CTRL_RADIO:
|
|
{
|
|
NSTextField *tf;
|
|
int j;
|
|
|
|
if (ctrl->generic.label) {
|
|
tf = [[NSTextField alloc]
|
|
initWithFrame:NSMakeRect(0,0,1,1)];
|
|
[tf setEditable:NO];
|
|
[tf setSelectable:NO];
|
|
[tf setBordered:NO];
|
|
[tf setDrawsBackground:NO];
|
|
[tf setStringValue:
|
|
[NSString stringWithCString:ctrl->generic.label]];
|
|
[tf sizeToFit];
|
|
rect = [tf frame];
|
|
[parent addSubview:tf];
|
|
c->label = tf;
|
|
|
|
cw = rect.size.width;
|
|
ch = rect.size.height;
|
|
} else {
|
|
cw = 0;
|
|
ch = -VSPACING; /* compensate for next advance */
|
|
}
|
|
|
|
c->nradiobuttons = ctrl->radio.nbuttons;
|
|
c->radiobuttons = snewn(ctrl->radio.nbuttons, NSButton *);
|
|
|
|
for (j = 0; j < ctrl->radio.nbuttons; j++) {
|
|
NSButton *b;
|
|
int ncols;
|
|
|
|
b = [[MyButton alloc] initWithFrame:NSMakeRect(0,0,1,1)];
|
|
[b setBezelStyle:NSRoundedBezelStyle];
|
|
[b setButtonType:NSRadioButton];
|
|
[b setTitle:[NSString
|
|
stringWithCString:ctrl->radio.buttons[j]]];
|
|
[b sizeToFit];
|
|
rect = [b frame];
|
|
[parent addSubview:b];
|
|
|
|
c->radiobuttons[j] = b;
|
|
|
|
[b setTarget:d->rec];
|
|
[b setAction:@selector(radioChanged:)];
|
|
add_widget(d, c, b);
|
|
|
|
/*
|
|
* Add to the height every time we place a
|
|
* button in column 0.
|
|
*/
|
|
if (j % ctrl->radio.ncolumns == 0) {
|
|
ch += rect.size.height + VSPACING;
|
|
}
|
|
|
|
/*
|
|
* Add to the width by working out how many
|
|
* columns this button spans.
|
|
*/
|
|
if (j == ctrl->radio.nbuttons - 1)
|
|
ncols = (ctrl->radio.ncolumns -
|
|
(j % ctrl->radio.ncolumns));
|
|
else
|
|
ncols = 1;
|
|
|
|
if (cw < rect.size.width * ctrl->radio.ncolumns / ncols)
|
|
cw = rect.size.width * ctrl->radio.ncolumns / ncols;
|
|
}
|
|
}
|
|
break;
|
|
case CTRL_FILESELECT:
|
|
case CTRL_FONTSELECT:
|
|
{
|
|
NSTextField *tf;
|
|
NSButton *b;
|
|
int kh;
|
|
|
|
tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];
|
|
[tf setEditable:NO];
|
|
[tf setSelectable:NO];
|
|
[tf setBordered:NO];
|
|
[tf setDrawsBackground:NO];
|
|
[tf setStringValue:[NSString
|
|
stringWithCString:ctrl->generic.label]];
|
|
[tf sizeToFit];
|
|
rect = [tf frame];
|
|
[parent addSubview:tf];
|
|
c->label = tf;
|
|
|
|
cw = rect.size.width;
|
|
ch = rect.size.height;
|
|
|
|
tf = [NSTextField alloc];
|
|
tf = [tf initWithFrame:NSMakeRect(0,0,1,1)];
|
|
if (ctrl->generic.type == CTRL_FILESELECT) {
|
|
[tf setEditable:YES];
|
|
[tf setSelectable:YES];
|
|
[tf setBordered:YES];
|
|
} else {
|
|
[tf setEditable:NO];
|
|
[tf setSelectable:NO];
|
|
[tf setBordered:NO];
|
|
[tf setDrawsBackground:NO];
|
|
}
|
|
[tf setStringValue:@"x"];
|
|
[tf sizeToFit];
|
|
rect = [tf frame];
|
|
[parent addSubview:tf];
|
|
c->editbox = tf;
|
|
|
|
kh = rect.size.height;
|
|
if (cw < rect.size.width * 4 / 3)
|
|
cw = rect.size.width * 4 / 3;
|
|
|
|
b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];
|
|
[b setBezelStyle:NSRoundedBezelStyle];
|
|
if (ctrl->generic.type == CTRL_FILESELECT)
|
|
[b setTitle:@"Browse..."];
|
|
else
|
|
[b setTitle:@"Change..."];
|
|
// [b setKeyEquivalent:somethingorother];
|
|
// [b setTarget:somethingorother];
|
|
// [b setAction:somethingorother];
|
|
[b sizeToFit];
|
|
rect = [b frame];
|
|
[parent addSubview:b];
|
|
|
|
c->button = b;
|
|
|
|
if (kh < rect.size.height)
|
|
kh = rect.size.height;
|
|
ch += VSPACING + kh;
|
|
if (cw < rect.size.width * 4)
|
|
cw = rect.size.width * 4;
|
|
}
|
|
break;
|
|
case CTRL_LISTBOX:
|
|
{
|
|
int listp = ctrl->listbox.percentwidth;
|
|
int labelp = listp == 100 ? 100 : 100 - listp;
|
|
NSTextField *tf;
|
|
NSPopUpButton *pb;
|
|
NSTableView *tv;
|
|
NSScrollView *sv;
|
|
|
|
if (ctrl->generic.label) {
|
|
tf = [[NSTextField alloc]
|
|
initWithFrame:NSMakeRect(0,0,1,1)];
|
|
[tf setEditable:NO];
|
|
[tf setSelectable:NO];
|
|
[tf setBordered:NO];
|
|
[tf setDrawsBackground:NO];
|
|
[tf setStringValue:
|
|
[NSString stringWithCString:ctrl->generic.label]];
|
|
[tf sizeToFit];
|
|
rect = [tf frame];
|
|
[parent addSubview:tf];
|
|
c->label = tf;
|
|
|
|
cw = rect.size.width;
|
|
ch = rect.size.height;
|
|
} else {
|
|
cw = 0;
|
|
ch = -VSPACING; /* compensate for next advance */
|
|
}
|
|
|
|
if (ctrl->listbox.height == 0) {
|
|
pb = [[NSPopUpButton alloc]
|
|
initWithFrame:NSMakeRect(0,0,1,1)];
|
|
[pb sizeToFit];
|
|
rect = [pb frame];
|
|
[parent addSubview:pb];
|
|
c->popupbutton = pb;
|
|
|
|
[pb setTarget:d->rec];
|
|
[pb setAction:@selector(popupMenuSelected:)];
|
|
add_widget(d, c, pb);
|
|
} else {
|
|
assert(listp == 100);
|
|
if (ctrl->listbox.draglist) {
|
|
int bi;
|
|
|
|
listp = 75;
|
|
|
|
for (bi = 0; bi < 2; bi++) {
|
|
NSButton *b;
|
|
b = [[MyButton alloc]
|
|
initWithFrame:NSMakeRect(0, 0, 1, 1)];
|
|
[b setBezelStyle:NSRoundedBezelStyle];
|
|
if (bi == 0)
|
|
[b setTitle:@"Up"];
|
|
else
|
|
[b setTitle:@"Down"];
|
|
[b sizeToFit];
|
|
rect = [b frame];
|
|
[parent addSubview:b];
|
|
|
|
if (bi == 0)
|
|
c->button = b;
|
|
else
|
|
c->button2 = b;
|
|
|
|
[b setTarget:d->rec];
|
|
[b setAction:@selector(dragListButton:)];
|
|
add_widget(d, c, b);
|
|
|
|
if (cw < rect.size.width * 4)
|
|
cw = rect.size.width * 4;
|
|
}
|
|
}
|
|
|
|
sv = [[NSScrollView alloc] initWithFrame:
|
|
NSMakeRect(20,20,10,10)];
|
|
[sv setBorderType:NSLineBorder];
|
|
tv = [[NSTableView alloc] initWithFrame:[sv frame]];
|
|
[[tv headerView] setFrame:NSMakeRect(0,0,0,0)];
|
|
[sv setDocumentView:tv];
|
|
[parent addSubview:sv];
|
|
[sv setHasVerticalScroller:YES];
|
|
[sv setAutohidesScrollers:YES];
|
|
[tv setAllowsColumnReordering:NO];
|
|
[tv setAllowsColumnResizing:NO];
|
|
[tv setAllowsMultipleSelection:ctrl->listbox.multisel];
|
|
[tv setAllowsEmptySelection:YES];
|
|
[tv setAllowsColumnSelection:YES];
|
|
[tv setDataSource:[[MyTableSource alloc] init]];
|
|
rect = [tv frame];
|
|
/*
|
|
* For some reason this consistently comes out
|
|
* one short. Add one.
|
|
*/
|
|
rect.size.height = (ctrl->listbox.height+1)*[tv rowHeight];
|
|
[sv setFrame:rect];
|
|
c->tableview = tv;
|
|
c->scrollview = sv;
|
|
|
|
[tv setDelegate:d->rec];
|
|
[tv setTarget:d->rec];
|
|
[tv setDoubleAction:@selector(listDoubleClicked:)];
|
|
add_widget(d, c, tv);
|
|
}
|
|
|
|
if (c->tableview) {
|
|
int ncols, *percentages;
|
|
int hundred = 100;
|
|
|
|
if (ctrl->listbox.ncols) {
|
|
ncols = ctrl->listbox.ncols;
|
|
percentages = ctrl->listbox.percentages;
|
|
} else {
|
|
ncols = 1;
|
|
percentages = &hundred;
|
|
}
|
|
|
|
for (j = 0; j < ncols; j++) {
|
|
NSTableColumn *col;
|
|
|
|
col = [[NSTableColumn alloc] initWithIdentifier:
|
|
[NSNumber numberWithInt:j]];
|
|
[c->tableview addTableColumn:col];
|
|
}
|
|
}
|
|
|
|
if (labelp == 100) {
|
|
/* the list and its label are vertically separated */
|
|
ch += VSPACING + rect.size.height;
|
|
} else {
|
|
/* the list and its label are horizontally separated */
|
|
if (ch < rect.size.height)
|
|
ch = rect.size.height;
|
|
}
|
|
|
|
if (cw < rect.size.width * 100 / listp)
|
|
cw = rect.size.width * 100 / listp;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Update the width and height data for the control we've
|
|
* just created.
|
|
*/
|
|
ytop = 0;
|
|
|
|
for (j = colstart; j < colend; j++) {
|
|
if (ytop < cypos[j])
|
|
ytop = cypos[j];
|
|
}
|
|
|
|
for (j = colstart; j < colend; j++)
|
|
cypos[j] = ytop + ch + VSPACING;
|
|
|
|
if (hmin < ytop + ch)
|
|
hmin = ytop + ch;
|
|
|
|
wthis = (cw + HSPACING) * 100 / (ccw[colend] - ccw[colstart]);
|
|
wthis -= HSPACING;
|
|
|
|
if (wmin < wthis)
|
|
wmin = wthis;
|
|
}
|
|
|
|
if (*s->boxname) {
|
|
/*
|
|
* Add a bit to the width and height for the box.
|
|
*/
|
|
wmin += boxw;
|
|
hmin += boxh;
|
|
}
|
|
|
|
//printf("For controlset %s/%s, returning w=%d h=%d\n",
|
|
// s->pathname, s->boxname, wmin, hmin);
|
|
*minw = wmin;
|
|
*minh = hmin;
|
|
}
|
|
|
|
int place_ctrls(void *dv, struct controlset *s, int leftx, int topy,
|
|
int width)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
int ccw[100]; /* cumulative column widths */
|
|
int cypos[100];
|
|
int ncols;
|
|
int i, j, ret;
|
|
int boxh = 0, boxw = 0;
|
|
|
|
if (!s->boxname && s->boxtitle) {
|
|
/* Size and place the panel title. */
|
|
|
|
NSTextField *tf = find_box(d, s);
|
|
NSRect rect;
|
|
|
|
rect = [tf frame];
|
|
[tf setFrame:NSMakeRect(leftx, topy-rect.size.height,
|
|
width, rect.size.height)];
|
|
return rect.size.height;
|
|
}
|
|
|
|
if (*s->boxname) {
|
|
NSRect rect, tmprect;
|
|
NSBox *box = find_box(d, s);
|
|
|
|
assert(box != NULL);
|
|
tmprect = [box frame];
|
|
[box setFrameFromContentFrame:NSMakeRect(100,100,100,100)];
|
|
rect = [box frame];
|
|
[box setFrame:tmprect];
|
|
boxw = rect.size.width - 100;
|
|
boxh = rect.size.height - 100;
|
|
if (s->boxtitle)
|
|
boxh += [[box titleFont] pointSize];
|
|
topy -= boxh;
|
|
width -= boxw;
|
|
}
|
|
|
|
ncols = 1;
|
|
ccw[0] = 0;
|
|
ccw[1] = 100;
|
|
cypos[0] = topy;
|
|
ret = 0;
|
|
|
|
/*
|
|
* Now iterate through the controls themselves, placing them
|
|
* appropriately.
|
|
*/
|
|
for (i = 0; i < s->ncontrols; i++) {
|
|
union control *ctrl = s->ctrls[i];
|
|
struct fe_ctrl *c;
|
|
int colstart = COLUMN_START(ctrl->generic.column);
|
|
int colspan = COLUMN_SPAN(ctrl->generic.column);
|
|
int colend = colstart + colspan;
|
|
int xthis, ythis, wthis, ch;
|
|
NSRect rect;
|
|
|
|
switch (ctrl->generic.type) {
|
|
case CTRL_COLUMNS:
|
|
for (j = 1; j < ncols; j++)
|
|
if (cypos[0] > cypos[j])
|
|
cypos[0] = cypos[j];
|
|
|
|
assert(ctrl->columns.ncols < lenof(ccw));
|
|
|
|
ccw[0] = 0;
|
|
for (j = 0; j < ctrl->columns.ncols; j++) {
|
|
ccw[j+1] = ccw[j] + (ctrl->columns.percentages ?
|
|
ctrl->columns.percentages[j] : 100);
|
|
cypos[j] = cypos[0];
|
|
}
|
|
|
|
ncols = ctrl->columns.ncols;
|
|
|
|
continue; /* no actual control created */
|
|
case CTRL_TABDELAY:
|
|
continue; /* nothing to do here, move along */
|
|
}
|
|
|
|
c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
ch = 0;
|
|
ythis = topy;
|
|
|
|
for (j = colstart; j < colend; j++) {
|
|
if (ythis > cypos[j])
|
|
ythis = cypos[j];
|
|
}
|
|
|
|
xthis = (width + HSPACING) * ccw[colstart] / 100;
|
|
wthis = (width + HSPACING) * ccw[colend] / 100 - HSPACING - xthis;
|
|
xthis += leftx;
|
|
|
|
switch (ctrl->generic.type) {
|
|
case CTRL_BUTTON:
|
|
case CTRL_CHECKBOX:
|
|
rect = [c->button frame];
|
|
[c->button setFrame:NSMakeRect(xthis,ythis-rect.size.height,wthis,
|
|
rect.size.height)];
|
|
ch = rect.size.height;
|
|
break;
|
|
case CTRL_EDITBOX:
|
|
{
|
|
int editp = ctrl->editbox.percentwidth;
|
|
int labelp = editp == 100 ? 100 : 100 - editp;
|
|
int lheight, theight, rheight, ynext, editw;
|
|
NSControl *edit = (c->editbox ? c->editbox : c->combobox);
|
|
|
|
rect = [c->label frame];
|
|
lheight = rect.size.height;
|
|
rect = [edit frame];
|
|
theight = rect.size.height;
|
|
|
|
if (editp == 100)
|
|
rheight = lheight;
|
|
else
|
|
rheight = (lheight < theight ? theight : lheight);
|
|
|
|
[c->label setFrame:
|
|
NSMakeRect(xthis, ythis-(rheight+lheight)/2,
|
|
(wthis + HSPACING) * labelp / 100 - HSPACING,
|
|
lheight)];
|
|
if (editp == 100) {
|
|
ynext = ythis - rheight - VSPACING;
|
|
rheight = theight;
|
|
} else {
|
|
ynext = ythis;
|
|
}
|
|
|
|
editw = (wthis + HSPACING) * editp / 100 - HSPACING;
|
|
|
|
[edit setFrame:
|
|
NSMakeRect(xthis+wthis-editw, ynext-(rheight+theight)/2,
|
|
editw, theight)];
|
|
|
|
ch = (ythis - ynext) + theight;
|
|
}
|
|
break;
|
|
case CTRL_TEXT:
|
|
[c->textview setFrame:NSMakeRect(xthis, 0, wthis, 1)];
|
|
[c->textview sizeToFit];
|
|
rect = [c->textview frame];
|
|
[c->textview setFrame:NSMakeRect(xthis, ythis-rect.size.height,
|
|
wthis, rect.size.height)];
|
|
ch = rect.size.height;
|
|
break;
|
|
case CTRL_RADIO:
|
|
{
|
|
int j, ynext;
|
|
|
|
if (c->label) {
|
|
rect = [c->label frame];
|
|
[c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height,
|
|
wthis,rect.size.height)];
|
|
ynext = ythis - rect.size.height - VSPACING;
|
|
} else
|
|
ynext = ythis;
|
|
|
|
for (j = 0; j < ctrl->radio.nbuttons; j++) {
|
|
int col = j % ctrl->radio.ncolumns;
|
|
int ncols;
|
|
int lx,rx;
|
|
|
|
if (j == ctrl->radio.nbuttons - 1)
|
|
ncols = ctrl->radio.ncolumns - col;
|
|
else
|
|
ncols = 1;
|
|
|
|
lx = (wthis + HSPACING) * col / ctrl->radio.ncolumns;
|
|
rx = ((wthis + HSPACING) *
|
|
(col+ncols) / ctrl->radio.ncolumns) - HSPACING;
|
|
|
|
/*
|
|
* Set the frame size.
|
|
*/
|
|
rect = [c->radiobuttons[j] frame];
|
|
[c->radiobuttons[j] setFrame:
|
|
NSMakeRect(lx+xthis, ynext-rect.size.height,
|
|
rx-lx, rect.size.height)];
|
|
|
|
/*
|
|
* Advance to next line if we're in the last
|
|
* column.
|
|
*/
|
|
if (col + ncols == ctrl->radio.ncolumns)
|
|
ynext -= rect.size.height + VSPACING;
|
|
}
|
|
ch = (ythis - ynext) - VSPACING;
|
|
}
|
|
break;
|
|
case CTRL_FILESELECT:
|
|
case CTRL_FONTSELECT:
|
|
{
|
|
int ynext, eh, bh, th, mx;
|
|
|
|
rect = [c->label frame];
|
|
[c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height,
|
|
wthis,rect.size.height)];
|
|
ynext = ythis - rect.size.height - VSPACING;
|
|
|
|
rect = [c->editbox frame];
|
|
eh = rect.size.height;
|
|
rect = [c->button frame];
|
|
bh = rect.size.height;
|
|
th = (eh > bh ? eh : bh);
|
|
|
|
mx = (wthis + HSPACING) * 3 / 4 - HSPACING;
|
|
|
|
[c->editbox setFrame:
|
|
NSMakeRect(xthis, ynext-(th+eh)/2, mx, eh)];
|
|
[c->button setFrame:
|
|
NSMakeRect(xthis+mx+HSPACING, ynext-(th+bh)/2,
|
|
wthis-mx-HSPACING, bh)];
|
|
|
|
ch = (ythis - ynext) + th + VSPACING;
|
|
}
|
|
break;
|
|
case CTRL_LISTBOX:
|
|
{
|
|
int listp = ctrl->listbox.percentwidth;
|
|
int labelp = listp == 100 ? 100 : 100 - listp;
|
|
int lheight, theight, rheight, ynext, listw, xlist;
|
|
NSControl *list = (c->scrollview ? (id)c->scrollview :
|
|
(id)c->popupbutton);
|
|
|
|
if (ctrl->listbox.draglist) {
|
|
assert(listp == 100);
|
|
listp = 75;
|
|
}
|
|
|
|
rect = [list frame];
|
|
theight = rect.size.height;
|
|
|
|
if (c->label) {
|
|
rect = [c->label frame];
|
|
lheight = rect.size.height;
|
|
|
|
if (labelp == 100)
|
|
rheight = lheight;
|
|
else
|
|
rheight = (lheight < theight ? theight : lheight);
|
|
|
|
[c->label setFrame:
|
|
NSMakeRect(xthis, ythis-(rheight+lheight)/2,
|
|
(wthis + HSPACING) * labelp / 100 - HSPACING,
|
|
lheight)];
|
|
if (labelp == 100) {
|
|
ynext = ythis - rheight - VSPACING;
|
|
rheight = theight;
|
|
} else {
|
|
ynext = ythis;
|
|
}
|
|
} else {
|
|
ynext = ythis;
|
|
rheight = theight;
|
|
}
|
|
|
|
listw = (wthis + HSPACING) * listp / 100 - HSPACING;
|
|
|
|
if (labelp == 100)
|
|
xlist = xthis;
|
|
else
|
|
xlist = xthis+wthis-listw;
|
|
|
|
[list setFrame: NSMakeRect(xlist, ynext-(rheight+theight)/2,
|
|
listw, theight)];
|
|
|
|
/*
|
|
* Size the columns for the table view.
|
|
*/
|
|
if (c->tableview) {
|
|
int ncols, *percentages;
|
|
int hundred = 100;
|
|
int cpercent = 0, cpixels = 0;
|
|
NSArray *cols;
|
|
|
|
if (ctrl->listbox.ncols) {
|
|
ncols = ctrl->listbox.ncols;
|
|
percentages = ctrl->listbox.percentages;
|
|
} else {
|
|
ncols = 1;
|
|
percentages = &hundred;
|
|
}
|
|
|
|
cols = [c->tableview tableColumns];
|
|
|
|
for (j = 0; j < ncols; j++) {
|
|
NSTableColumn *col = [cols objectAtIndex:j];
|
|
int newcpixels;
|
|
|
|
cpercent += percentages[j];
|
|
newcpixels = listw * cpercent / 100;
|
|
[col setWidth:newcpixels-cpixels];
|
|
cpixels = newcpixels;
|
|
}
|
|
}
|
|
|
|
ch = (ythis - ynext) + theight;
|
|
|
|
if (c->button) {
|
|
int b2height, centre;
|
|
int bx, bw;
|
|
|
|
/*
|
|
* Place the Up and Down buttons for a drag list.
|
|
*/
|
|
assert(c->button2);
|
|
|
|
rect = [c->button frame];
|
|
b2height = VSPACING + 2 * rect.size.height;
|
|
|
|
centre = ynext - rheight/2;
|
|
|
|
bx = (wthis + HSPACING) * 3 / 4;
|
|
bw = wthis - bx;
|
|
bx += leftx;
|
|
|
|
[c->button setFrame:
|
|
NSMakeRect(bx, centre+b2height/2-rect.size.height,
|
|
bw, rect.size.height)];
|
|
[c->button2 setFrame:
|
|
NSMakeRect(bx, centre-b2height/2,
|
|
bw, rect.size.height)];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
for (j = colstart; j < colend; j++)
|
|
cypos[j] = ythis - ch - VSPACING;
|
|
if (ret < topy - (ythis - ch))
|
|
ret = topy - (ythis - ch);
|
|
}
|
|
|
|
if (*s->boxname) {
|
|
NSBox *box = find_box(d, s);
|
|
assert(box != NULL);
|
|
[box sizeToFit];
|
|
|
|
if (s->boxtitle) {
|
|
NSRect rect = [box frame];
|
|
rect.size.height += [[box titleFont] pointSize];
|
|
[box setFrame:rect];
|
|
}
|
|
|
|
ret += boxh;
|
|
}
|
|
|
|
//printf("For controlset %s/%s, returning ret=%d\n",
|
|
// s->pathname, s->boxname, ret);
|
|
return ret;
|
|
}
|
|
|
|
void select_panel(void *dv, struct controlbox *b, const char *name)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
int i, j, hidden;
|
|
struct controlset *s;
|
|
union control *ctrl;
|
|
struct fe_ctrl *c;
|
|
NSBox *box;
|
|
|
|
for (i = 0; i < b->nctrlsets; i++) {
|
|
s = b->ctrlsets[i];
|
|
|
|
if (*s->pathname) {
|
|
hidden = !strcmp(s->pathname, name) ? NO : YES;
|
|
|
|
if ((box = find_box(d, s)) != NULL) {
|
|
[box setHidden:hidden];
|
|
} else {
|
|
for (j = 0; j < s->ncontrols; j++) {
|
|
ctrl = s->ctrls[j];
|
|
c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
if (!c)
|
|
continue;
|
|
|
|
if (c->label)
|
|
[c->label setHidden:hidden];
|
|
if (c->button)
|
|
[c->button setHidden:hidden];
|
|
if (c->button2)
|
|
[c->button2 setHidden:hidden];
|
|
if (c->editbox)
|
|
[c->editbox setHidden:hidden];
|
|
if (c->combobox)
|
|
[c->combobox setHidden:hidden];
|
|
if (c->textview)
|
|
[c->textview setHidden:hidden];
|
|
if (c->tableview)
|
|
[c->tableview setHidden:hidden];
|
|
if (c->scrollview)
|
|
[c->scrollview setHidden:hidden];
|
|
if (c->popupbutton)
|
|
[c->popupbutton setHidden:hidden];
|
|
if (c->radiobuttons) {
|
|
int j;
|
|
for (j = 0; j < c->nradiobuttons; j++)
|
|
[c->radiobuttons[j] setHidden:hidden];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void dlg_radiobutton_set(union control *ctrl, void *dv, int whichbutton)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
int j;
|
|
|
|
assert(c->radiobuttons);
|
|
for (j = 0; j < c->nradiobuttons; j++)
|
|
[c->radiobuttons[j] setState:
|
|
(j == whichbutton ? NSOnState : NSOffState)];
|
|
}
|
|
|
|
int dlg_radiobutton_get(union control *ctrl, void *dv)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
int j;
|
|
|
|
assert(c->radiobuttons);
|
|
for (j = 0; j < c->nradiobuttons; j++)
|
|
if ([c->radiobuttons[j] state] == NSOnState)
|
|
return j;
|
|
|
|
return 0; /* should never reach here */
|
|
}
|
|
|
|
void dlg_checkbox_set(union control *ctrl, void *dv, int checked)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
assert(c->button);
|
|
[c->button setState:(checked ? NSOnState : NSOffState)];
|
|
}
|
|
|
|
int dlg_checkbox_get(union control *ctrl, void *dv)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
assert(c->button);
|
|
return ([c->button state] == NSOnState);
|
|
}
|
|
|
|
void dlg_editbox_set(union control *ctrl, void *dv, char const *text)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
if (c->editbox) {
|
|
[c->editbox setStringValue:[NSString stringWithCString:text]];
|
|
} else {
|
|
assert(c->combobox);
|
|
[c->combobox setStringValue:[NSString stringWithCString:text]];
|
|
}
|
|
}
|
|
|
|
void dlg_editbox_get(union control *ctrl, void *dv, char *buffer, int length)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
NSString *str;
|
|
|
|
if (c->editbox) {
|
|
str = [c->editbox stringValue];
|
|
} else {
|
|
assert(c->combobox);
|
|
str = [c->combobox stringValue];
|
|
}
|
|
if (!str)
|
|
str = @"";
|
|
|
|
/* The length parameter to this method doesn't include a trailing NUL */
|
|
[str getCString:buffer maxLength:length-1];
|
|
}
|
|
|
|
void dlg_listbox_clear(union control *ctrl, void *dv)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
if (c->tableview) {
|
|
[[c->tableview dataSource] clear];
|
|
[c->tableview reloadData];
|
|
} else {
|
|
[c->popupbutton removeAllItems];
|
|
}
|
|
}
|
|
|
|
void dlg_listbox_del(union control *ctrl, void *dv, int index)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
if (c->tableview) {
|
|
[[c->tableview dataSource] removestr:index];
|
|
[c->tableview reloadData];
|
|
} else {
|
|
[c->popupbutton removeItemAtIndex:index];
|
|
}
|
|
}
|
|
|
|
void dlg_listbox_addwithid(union control *ctrl, void *dv,
|
|
char const *text, int id)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
if (c->tableview) {
|
|
[[c->tableview dataSource] add:text withId:id];
|
|
[c->tableview reloadData];
|
|
} else {
|
|
[c->popupbutton addItemWithTitle:[NSString stringWithCString:text]];
|
|
[[c->popupbutton lastItem] setTag:id];
|
|
}
|
|
}
|
|
|
|
void dlg_listbox_add(union control *ctrl, void *dv, char const *text)
|
|
{
|
|
dlg_listbox_addwithid(ctrl, dv, text, -1);
|
|
}
|
|
|
|
int dlg_listbox_getid(union control *ctrl, void *dv, int index)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
if (c->tableview) {
|
|
return [[c->tableview dataSource] getid:index];
|
|
} else {
|
|
return [[c->popupbutton itemAtIndex:index] tag];
|
|
}
|
|
}
|
|
|
|
int dlg_listbox_index(union control *ctrl, void *dv)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
if (c->tableview) {
|
|
return [c->tableview selectedRow];
|
|
} else {
|
|
return [c->popupbutton indexOfSelectedItem];
|
|
}
|
|
}
|
|
|
|
int dlg_listbox_issel(union control *ctrl, void *dv, int index)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
if (c->tableview) {
|
|
return [c->tableview isRowSelected:index];
|
|
} else {
|
|
return [c->popupbutton indexOfSelectedItem] == index;
|
|
}
|
|
}
|
|
|
|
void dlg_listbox_select(union control *ctrl, void *dv, int index)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
if (c->tableview) {
|
|
[c->tableview selectRow:index byExtendingSelection:NO];
|
|
} else {
|
|
[c->popupbutton selectItemAtIndex:index];
|
|
}
|
|
}
|
|
|
|
void dlg_text_set(union control *ctrl, void *dv, char const *text)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
|
|
assert(c->textview);
|
|
[c->textview setString:[NSString stringWithCString:text]];
|
|
}
|
|
|
|
void dlg_label_change(union control *ctrl, void *dlg, char const *text)
|
|
{
|
|
/*
|
|
* This function is currently only used by the config box to
|
|
* switch the labels on the host and port boxes between serial
|
|
* and network modes. Since OS X does not (yet?) have a serial
|
|
* back end, this function can safely do nothing for the
|
|
* moment.
|
|
*/
|
|
}
|
|
|
|
void dlg_filesel_set(union control *ctrl, void *dv, Filename fn)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
void dlg_filesel_get(union control *ctrl, void *dv, Filename *fn)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
void dlg_fontsel_set(union control *ctrl, void *dv, FontSpec fn)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
void dlg_fontsel_get(union control *ctrl, void *dv, FontSpec *fn)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
void dlg_update_start(union control *ctrl, void *dv)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
void dlg_update_done(union control *ctrl, void *dv)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
void dlg_set_focus(union control *ctrl, void *dv)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
union control *dlg_last_focused(union control *ctrl, void *dv)
|
|
{
|
|
return NULL; /* FIXME */
|
|
}
|
|
|
|
void dlg_beep(void *dv)
|
|
{
|
|
NSBeep();
|
|
}
|
|
|
|
void dlg_error_msg(void *dv, char *msg)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
void dlg_end(void *dv, int value)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
[d->target performSelector:d->action
|
|
withObject:[NSNumber numberWithInt:value]];
|
|
}
|
|
|
|
void dlg_coloursel_start(union control *ctrl, void *dv,
|
|
int r, int g, int b)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
int dlg_coloursel_results(union control *ctrl, void *dv,
|
|
int *r, int *g, int *b)
|
|
{
|
|
return 0; /* FIXME */
|
|
}
|
|
|
|
void dlg_refresh(union control *ctrl, void *dv)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c;
|
|
|
|
if (ctrl) {
|
|
if (ctrl->generic.handler != NULL)
|
|
ctrl->generic.handler(ctrl, d, d->data, EVENT_REFRESH);
|
|
} else {
|
|
int i;
|
|
|
|
for (i = 0; (c = index234(d->byctrl, i)) != NULL; i++) {
|
|
assert(c->ctrl != NULL);
|
|
if (c->ctrl->generic.handler != NULL)
|
|
c->ctrl->generic.handler(c->ctrl, d,
|
|
d->data, EVENT_REFRESH);
|
|
}
|
|
}
|
|
}
|
|
|
|
void *dlg_get_privdata(union control *ctrl, void *dv)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
return c->privdata;
|
|
}
|
|
|
|
void dlg_set_privdata(union control *ctrl, void *dv, void *ptr)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
c->privdata = ptr;
|
|
c->privdata_needs_free = FALSE;
|
|
}
|
|
|
|
void *dlg_alloc_privdata(union control *ctrl, void *dv, size_t size)
|
|
{
|
|
struct fe_dlg *d = (struct fe_dlg *)dv;
|
|
struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
|
|
/*
|
|
* This is an internal allocation routine, so it's allowed to
|
|
* use smalloc directly.
|
|
*/
|
|
c->privdata = smalloc(size);
|
|
c->privdata_needs_free = TRUE;
|
|
return c->privdata;
|
|
}
|