1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 09:58:01 +00:00
putty-source/macosx/osxctrls.m
Simon Tatham 46bfde32e8 Initial checkin of a native Mac OS X port, sharing most of its code
with the Unix port and layering a Cocoa GUI on top. The basics all
work: there's a configuration panel and a terminal window, the
timing interface works and the select interface functions. The same
application can run both SSH (or other network) connections and
local pty sessions, and multiple sessions in the same process are
fully supported.

However, it's horribly unfinished in a wide variety of other ways;
anyone interested is invited to read README.OSX and wince at the
length and content of its `unfinished' list.

[originally from svn r5308]
2005-02-15 21:45:50 +00:00

1805 lines
43 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_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;
}