--- /dev/null
+/*\r
+ * osxctrls.m: OS X implementation of the dialog.h interface.\r
+ */\r
+\r
+#import <Cocoa/Cocoa.h>\r
+#include "putty.h"\r
+#include "dialog.h"\r
+#include "osxclass.h"\r
+#include "tree234.h"\r
+\r
+/*\r
+ * Still to be implemented:\r
+ * \r
+ * - file selectors (NSOpenPanel / NSSavePanel)\r
+ * \r
+ * - font selectors\r
+ * - colour selectors\r
+ * * both of these have a conceptual oddity in Cocoa that\r
+ * you're only supposed to have one per application. But I\r
+ * currently expect to be able to have multiple PuTTY config\r
+ * boxes on screen at once; what happens if you trigger the\r
+ * font selector in each one at the same time?\r
+ * * if it comes to that, the _font_ selector can probably be\r
+ * managed by other means: nobody is forcing me to implement\r
+ * a font selector using a `Change...' button. The portable\r
+ * dialog interface gives me the flexibility to do this how I\r
+ * want.\r
+ * * The colour selector interface, in its present form, is\r
+ * more interesting and _if_ a radical change of plan is\r
+ * required then it may stretch across the interface into the\r
+ * portable side.\r
+ * * Before I do anything rash I should start by looking at the\r
+ * Mac Classic port and see how it's done there, on the basis\r
+ * that Apple seem reasonably unlikely to have invented this\r
+ * crazy restriction specifically for OS X.\r
+ * \r
+ * - focus management\r
+ * * I tried using makeFirstResponder to give keyboard focus,\r
+ * but it appeared not to work. Try again, and work out how\r
+ * it should be done.\r
+ * * also look into tab order. Currently pressing Tab suggests\r
+ * that only edit boxes and list boxes can get the keyboard\r
+ * focus, and that buttons (in all their forms) are unable to\r
+ * be driven by the keyboard. Find out for sure.\r
+ * \r
+ * - dlg_error_msg\r
+ * * this may run into the usual aggro with modal dialog boxes.\r
+ */\r
+\r
+/*\r
+ * For Cocoa control layout, I need a two-stage process. In stage\r
+ * one, I allocate all the controls and measure their natural\r
+ * sizes, which allows me to compute the _minimum_ width and height\r
+ * of a given section of dialog. Then, in stage two, I lay out the\r
+ * dialog box as a whole, decide how much each section of the box\r
+ * needs to receive, and assign it its final size.\r
+ */\r
+\r
+/*\r
+ * As yet unsolved issues [FIXME]:\r
+ * \r
+ * - Sometimes the height returned from create_ctrls and the\r
+ * height returned from place_ctrls differ. Find out why. It may\r
+ * be harmless (e.g. results of NSTextView being odd), but I\r
+ * want to know.\r
+ * \r
+ * - NSTextViews are indented a bit. It'd be nice to put their\r
+ * left margin at the same place as everything else's.\r
+ * \r
+ * - I don't yet know whether we even _can_ support tab order or\r
+ * keyboard shortcuts. If we can't, then fair enough, we can't.\r
+ * But if we can, we should.\r
+ * \r
+ * - I would _really_ like to know of a better way to correct\r
+ * NSButton's stupid size estimates than by subclassing it and\r
+ * overriding sizeToFit with hard-wired sensible values!\r
+ * \r
+ * - Speaking of stupid size estimates, the amount by which I'm\r
+ * adjusting a titled NSBox (currently equal to the point size\r
+ * of its title font) looks as if it isn't _quite_ enough.\r
+ * Figure out what the real amount should be and use it.\r
+ * \r
+ * - I don't understand why there's always a scrollbar displayed\r
+ * in each list box. I thought I told it to autohide scrollers?\r
+ * \r
+ * - Why do I have to fudge list box heights by adding one? (Might\r
+ * it be to do with the missing header view?)\r
+ */\r
+\r
+/*\r
+ * Subclass of NSButton which corrects the fact that the normal\r
+ * one's sizeToFit method persistently returns 32 as its height,\r
+ * which is simply a lie. I have yet to work out a better\r
+ * alternative than hard-coding the real heights.\r
+ */\r
+@interface MyButton : NSButton\r
+{\r
+ int minht;\r
+}\r
+@end\r
+@implementation MyButton\r
+- (id)initWithFrame:(NSRect)r\r
+{\r
+ self = [super initWithFrame:r];\r
+ minht = 25;\r
+ return self;\r
+}\r
+- (void)setButtonType:(NSButtonType)t\r
+{\r
+ if (t == NSRadioButton || t == NSSwitchButton)\r
+ minht = 18;\r
+ else\r
+ minht = 25;\r
+ [super setButtonType:t];\r
+}\r
+- (void)sizeToFit\r
+{\r
+ NSRect r;\r
+ [super sizeToFit];\r
+ r = [self frame];\r
+ r.size.height = minht;\r
+ [self setFrame:r];\r
+}\r
+@end\r
+\r
+/*\r
+ * Class used as the data source for NSTableViews.\r
+ */\r
+@interface MyTableSource : NSObject\r
+{\r
+ tree234 *tree;\r
+}\r
+- (id)init;\r
+- (void)add:(const char *)str withId:(int)id;\r
+- (int)getid:(int)index;\r
+- (void)swap:(int)index1 with:(int)index2;\r
+- (void)removestr:(int)index;\r
+- (void)clear;\r
+@end\r
+@implementation MyTableSource\r
+- (id)init\r
+{\r
+ self = [super init];\r
+ tree = newtree234(NULL);\r
+ return self;\r
+}\r
+- (void)dealloc\r
+{\r
+ char *p;\r
+ while ((p = delpos234(tree, 0)) != NULL)\r
+ sfree(p);\r
+ freetree234(tree);\r
+ [super dealloc];\r
+}\r
+- (void)add:(const char *)str withId:(int)id\r
+{\r
+ addpos234(tree, dupprintf("%d\t%s", id, str), count234(tree));\r
+}\r
+- (int)getid:(int)index\r
+{\r
+ char *p = index234(tree, index);\r
+ return atoi(p);\r
+}\r
+- (void)removestr:(int)index\r
+{\r
+ char *p = delpos234(tree, index);\r
+ sfree(p);\r
+}\r
+- (void)swap:(int)index1 with:(int)index2\r
+{\r
+ char *p1, *p2;\r
+\r
+ if (index1 > index2) {\r
+ int t = index1; index1 = index2; index2 = t;\r
+ }\r
+\r
+ /* delete later one first so it doesn't affect index of earlier one */\r
+ p2 = delpos234(tree, index2);\r
+ p1 = delpos234(tree, index1);\r
+\r
+ /* now insert earlier one before later one for the inverse reason */\r
+ addpos234(tree, p2, index1);\r
+ addpos234(tree, p1, index2);\r
+}\r
+- (void)clear\r
+{\r
+ char *p;\r
+ while ((p = delpos234(tree, 0)) != NULL)\r
+ sfree(p);\r
+}\r
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView\r
+{\r
+ return count234(tree);\r
+}\r
+- (id)tableView:(NSTableView *)aTableView\r
+ objectValueForTableColumn:(NSTableColumn *)aTableColumn\r
+ row:(int)rowIndex\r
+{\r
+ int j = [[aTableColumn identifier] intValue];\r
+ char *p = index234(tree, rowIndex);\r
+\r
+ while (j >= 0) {\r
+ p += strcspn(p, "\t");\r
+ if (*p) p++;\r
+ j--;\r
+ }\r
+\r
+ return [NSString stringWithCString:p length:strcspn(p, "\t")];\r
+}\r
+@end\r
+\r
+/*\r
+ * Object to receive messages from various control classes.\r
+ */\r
+@class Receiver;\r
+\r
+struct fe_dlg {\r
+ NSWindow *window;\r
+ NSObject *target;\r
+ SEL action;\r
+ tree234 *byctrl;\r
+ tree234 *bywidget;\r
+ tree234 *boxes;\r
+ void *data; /* passed to portable side */\r
+ Receiver *rec;\r
+};\r
+\r
+@interface Receiver : NSObject\r
+{\r
+ struct fe_dlg *d;\r
+}\r
+- (id)initWithStruct:(struct fe_dlg *)aStruct;\r
+@end\r
+\r
+struct fe_ctrl {\r
+ union control *ctrl;\r
+ NSButton *button, *button2;\r
+ NSTextField *label, *editbox;\r
+ NSComboBox *combobox;\r
+ NSButton **radiobuttons;\r
+ NSTextView *textview;\r
+ NSPopUpButton *popupbutton;\r
+ NSTableView *tableview;\r
+ NSScrollView *scrollview;\r
+ int nradiobuttons;\r
+ void *privdata;\r
+ int privdata_needs_free;\r
+};\r
+\r
+static int fe_ctrl_cmp_by_ctrl(void *av, void *bv)\r
+{\r
+ struct fe_ctrl *a = (struct fe_ctrl *)av;\r
+ struct fe_ctrl *b = (struct fe_ctrl *)bv;\r
+\r
+ if (a->ctrl < b->ctrl)\r
+ return -1;\r
+ if (a->ctrl > b->ctrl)\r
+ return +1;\r
+ return 0;\r
+}\r
+\r
+static int fe_ctrl_find_by_ctrl(void *av, void *bv)\r
+{\r
+ union control *a = (union control *)av;\r
+ struct fe_ctrl *b = (struct fe_ctrl *)bv;\r
+\r
+ if (a < b->ctrl)\r
+ return -1;\r
+ if (a > b->ctrl)\r
+ return +1;\r
+ return 0;\r
+}\r
+\r
+struct fe_box {\r
+ struct controlset *s;\r
+ id box;\r
+};\r
+\r
+static int fe_boxcmp(void *av, void *bv)\r
+{\r
+ struct fe_box *a = (struct fe_box *)av;\r
+ struct fe_box *b = (struct fe_box *)bv;\r
+\r
+ if (a->s < b->s)\r
+ return -1;\r
+ if (a->s > b->s)\r
+ return +1;\r
+ return 0;\r
+}\r
+\r
+static int fe_boxfind(void *av, void *bv)\r
+{\r
+ struct controlset *a = (struct controlset *)av;\r
+ struct fe_box *b = (struct fe_box *)bv;\r
+\r
+ if (a < b->s)\r
+ return -1;\r
+ if (a > b->s)\r
+ return +1;\r
+ return 0;\r
+}\r
+\r
+struct fe_backwards { /* map Cocoa widgets back to fe_ctrls */\r
+ id widget;\r
+ struct fe_ctrl *c;\r
+};\r
+\r
+static int fe_backwards_cmp_by_widget(void *av, void *bv)\r
+{\r
+ struct fe_backwards *a = (struct fe_backwards *)av;\r
+ struct fe_backwards *b = (struct fe_backwards *)bv;\r
+\r
+ if (a->widget < b->widget)\r
+ return -1;\r
+ if (a->widget > b->widget)\r
+ return +1;\r
+ return 0;\r
+}\r
+\r
+static int fe_backwards_find_by_widget(void *av, void *bv)\r
+{\r
+ id a = (id)av;\r
+ struct fe_backwards *b = (struct fe_backwards *)bv;\r
+\r
+ if (a < b->widget)\r
+ return -1;\r
+ if (a > b->widget)\r
+ return +1;\r
+ return 0;\r
+}\r
+\r
+static struct fe_ctrl *fe_ctrl_new(union control *ctrl)\r
+{\r
+ struct fe_ctrl *c;\r
+\r
+ c = snew(struct fe_ctrl);\r
+ c->ctrl = ctrl;\r
+\r
+ c->button = c->button2 = nil;\r
+ c->label = nil;\r
+ c->editbox = nil;\r
+ c->combobox = nil;\r
+ c->textview = nil;\r
+ c->popupbutton = nil;\r
+ c->tableview = nil;\r
+ c->scrollview = nil;\r
+ c->radiobuttons = NULL;\r
+ c->nradiobuttons = 0;\r
+ c->privdata = NULL;\r
+ c->privdata_needs_free = FALSE;\r
+\r
+ return c;\r
+}\r
+\r
+static void fe_ctrl_free(struct fe_ctrl *c)\r
+{\r
+ if (c->privdata_needs_free)\r
+ sfree(c->privdata);\r
+ sfree(c->radiobuttons);\r
+ sfree(c);\r
+}\r
+\r
+static struct fe_ctrl *fe_ctrl_byctrl(struct fe_dlg *d, union control *ctrl)\r
+{\r
+ return find234(d->byctrl, ctrl, fe_ctrl_find_by_ctrl);\r
+}\r
+\r
+static void add_box(struct fe_dlg *d, struct controlset *s, id box)\r
+{\r
+ struct fe_box *b = snew(struct fe_box);\r
+ b->box = box;\r
+ b->s = s;\r
+ add234(d->boxes, b);\r
+}\r
+\r
+static id find_box(struct fe_dlg *d, struct controlset *s)\r
+{\r
+ struct fe_box *b = find234(d->boxes, s, fe_boxfind);\r
+ return b ? b->box : NULL;\r
+}\r
+\r
+static void add_widget(struct fe_dlg *d, struct fe_ctrl *c, id widget)\r
+{\r
+ struct fe_backwards *b = snew(struct fe_backwards);\r
+ b->widget = widget;\r
+ b->c = c;\r
+ add234(d->bywidget, b);\r
+}\r
+\r
+static struct fe_ctrl *find_widget(struct fe_dlg *d, id widget)\r
+{\r
+ struct fe_backwards *b = find234(d->bywidget, widget,\r
+ fe_backwards_find_by_widget);\r
+ return b ? b->c : NULL;\r
+}\r
+\r
+void *fe_dlg_init(void *data, NSWindow *window, NSObject *target, SEL action)\r
+{\r
+ struct fe_dlg *d;\r
+\r
+ d = snew(struct fe_dlg);\r
+ d->window = window;\r
+ d->target = target;\r
+ d->action = action;\r
+ d->byctrl = newtree234(fe_ctrl_cmp_by_ctrl);\r
+ d->bywidget = newtree234(fe_backwards_cmp_by_widget);\r
+ d->boxes = newtree234(fe_boxcmp);\r
+ d->data = data;\r
+ d->rec = [[Receiver alloc] initWithStruct:d];\r
+\r
+ return d;\r
+}\r
+\r
+void fe_dlg_free(void *dv)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c;\r
+ struct fe_box *b;\r
+\r
+ while ( (c = delpos234(d->byctrl, 0)) != NULL )\r
+ fe_ctrl_free(c);\r
+ freetree234(d->byctrl);\r
+\r
+ while ( (c = delpos234(d->bywidget, 0)) != NULL )\r
+ sfree(c);\r
+ freetree234(d->bywidget);\r
+\r
+ while ( (b = delpos234(d->boxes, 0)) != NULL )\r
+ sfree(b);\r
+ freetree234(d->boxes);\r
+\r
+ [d->rec release];\r
+\r
+ sfree(d);\r
+}\r
+\r
+@implementation Receiver\r
+- (id)initWithStruct:(struct fe_dlg *)aStruct\r
+{\r
+ self = [super init];\r
+ d = aStruct;\r
+ return self;\r
+}\r
+- (void)buttonPushed:(id)sender\r
+{\r
+ struct fe_ctrl *c = find_widget(d, sender);\r
+\r
+ assert(c && c->ctrl->generic.type == CTRL_BUTTON);\r
+ c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION);\r
+}\r
+- (void)checkboxChanged:(id)sender\r
+{\r
+ struct fe_ctrl *c = find_widget(d, sender);\r
+\r
+ assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);\r
+ c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+- (void)radioChanged:(id)sender\r
+{\r
+ struct fe_ctrl *c = find_widget(d, sender);\r
+ int j;\r
+\r
+ assert(c && c->radiobuttons);\r
+ for (j = 0; j < c->nradiobuttons; j++)\r
+ if (sender != c->radiobuttons[j])\r
+ [c->radiobuttons[j] setState:NSOffState];\r
+ c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+- (void)popupMenuSelected:(id)sender\r
+{\r
+ struct fe_ctrl *c = find_widget(d, sender);\r
+ c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+- (void)controlTextDidChange:(NSNotification *)notification\r
+{\r
+ id widget = [notification object];\r
+ struct fe_ctrl *c = find_widget(d, widget);\r
+ assert(c && c->ctrl->generic.type == CTRL_EDITBOX);\r
+ c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+- (void)controlTextDidEndEditing:(NSNotification *)notification\r
+{\r
+ id widget = [notification object];\r
+ struct fe_ctrl *c = find_widget(d, widget);\r
+ assert(c && c->ctrl->generic.type == CTRL_EDITBOX);\r
+ c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_REFRESH);\r
+}\r
+- (void)tableViewSelectionDidChange:(NSNotification *)notification\r
+{\r
+ id widget = [notification object];\r
+ struct fe_ctrl *c = find_widget(d, widget);\r
+ assert(c && c->ctrl->generic.type == CTRL_LISTBOX);\r
+ c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_SELCHANGE);\r
+}\r
+- (BOOL)tableView:(NSTableView *)aTableView\r
+ shouldEditTableColumn:(NSTableColumn *)aTableColumn\r
+ row:(int)rowIndex\r
+{\r
+ return NO; /* no editing permitted */\r
+}\r
+- (void)listDoubleClicked:(id)sender\r
+{\r
+ struct fe_ctrl *c = find_widget(d, sender);\r
+ assert(c && c->ctrl->generic.type == CTRL_LISTBOX);\r
+ c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION);\r
+}\r
+- (void)dragListButton:(id)sender\r
+{\r
+ struct fe_ctrl *c = find_widget(d, sender);\r
+ int direction, row, nrows;\r
+ assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&\r
+ c->ctrl->listbox.draglist);\r
+\r
+ if (sender == c->button)\r
+ direction = -1; /* up */\r
+ else\r
+ direction = +1; /* down */\r
+\r
+ row = [c->tableview selectedRow];\r
+ nrows = [c->tableview numberOfRows];\r
+\r
+ if (row + direction < 0 || row + direction >= nrows) {\r
+ NSBeep();\r
+ return;\r
+ }\r
+\r
+ [[c->tableview dataSource] swap:row with:row+direction];\r
+ [c->tableview reloadData];\r
+ [c->tableview selectRow:row+direction byExtendingSelection:NO];\r
+\r
+ c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);\r
+}\r
+@end\r
+\r
+void create_ctrls(void *dv, NSView *parent, struct controlset *s,\r
+ int *minw, int *minh)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ int ccw[100]; /* cumulative column widths */\r
+ int cypos[100];\r
+ int ncols;\r
+ int wmin = 0, hmin = 0;\r
+ int i, j, cw, ch;\r
+ NSRect rect;\r
+ NSFont *textviewfont = nil;\r
+ int boxh = 0, boxw = 0;\r
+\r
+ if (!s->boxname && s->boxtitle) {\r
+ /* This controlset is a panel title. */\r
+\r
+ NSTextField *tf;\r
+\r
+ tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+ [tf setEditable:NO];\r
+ [tf setSelectable:NO];\r
+ [tf setBordered:NO];\r
+ [tf setDrawsBackground:NO];\r
+ [tf setStringValue:[NSString stringWithCString:s->boxtitle]];\r
+ [tf sizeToFit];\r
+ rect = [tf frame];\r
+ [parent addSubview:tf];\r
+\r
+ /*\r
+ * I'm going to store this NSTextField in the boxes tree,\r
+ * because I really can't face having a special tree234\r
+ * mapping controlsets to panel titles.\r
+ */\r
+ add_box(d, s, tf);\r
+\r
+ *minw = rect.size.width;\r
+ *minh = rect.size.height;\r
+\r
+ return;\r
+ }\r
+\r
+ if (*s->boxname) {\r
+ /*\r
+ * Create an NSBox to contain this subset of controls.\r
+ */\r
+ NSBox *box;\r
+ NSRect tmprect;\r
+\r
+ box = [[NSBox alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+ if (s->boxtitle)\r
+ [box setTitle:[NSString stringWithCString:s->boxtitle]];\r
+ else\r
+ [box setTitlePosition:NSNoTitle];\r
+ add_box(d, s, box);\r
+ tmprect = [box frame];\r
+ [box setContentViewMargins:NSMakeSize(20,20)];\r
+ [box setFrameFromContentFrame:NSMakeRect(100,100,100,100)];\r
+ rect = [box frame];\r
+ [box setFrame:tmprect];\r
+ boxh = (int)(rect.size.height - 100);\r
+ boxw = (int)(rect.size.width - 100);\r
+ [parent addSubview:box];\r
+\r
+ if (s->boxtitle)\r
+ boxh += [[box titleFont] pointSize];\r
+\r
+ /*\r
+ * All subsequent controls will be placed within this box.\r
+ */\r
+ parent = box;\r
+ }\r
+\r
+ ncols = 1;\r
+ ccw[0] = 0;\r
+ ccw[1] = 100;\r
+ cypos[0] = 0;\r
+\r
+ /*\r
+ * Now iterate through the controls themselves, create them,\r
+ * and add their width and height to the overall width/height\r
+ * calculation.\r
+ */\r
+ for (i = 0; i < s->ncontrols; i++) {\r
+ union control *ctrl = s->ctrls[i];\r
+ struct fe_ctrl *c;\r
+ int colstart = COLUMN_START(ctrl->generic.column);\r
+ int colspan = COLUMN_SPAN(ctrl->generic.column);\r
+ int colend = colstart + colspan;\r
+ int ytop, wthis;\r
+\r
+ switch (ctrl->generic.type) {\r
+ case CTRL_COLUMNS:\r
+ for (j = 1; j < ncols; j++)\r
+ if (cypos[0] < cypos[j])\r
+ cypos[0] = cypos[j];\r
+\r
+ assert(ctrl->columns.ncols < lenof(ccw));\r
+\r
+ ccw[0] = 0;\r
+ for (j = 0; j < ctrl->columns.ncols; j++) {\r
+ ccw[j+1] = ccw[j] + (ctrl->columns.percentages ?\r
+ ctrl->columns.percentages[j] : 100);\r
+ cypos[j] = cypos[0];\r
+ }\r
+\r
+ ncols = ctrl->columns.ncols;\r
+\r
+ continue; /* no actual control created */\r
+ case CTRL_TABDELAY:\r
+ /*\r
+ * I'm currently uncertain that we can implement tab\r
+ * order in OS X.\r
+ */\r
+ continue; /* no actual control created */\r
+ }\r
+\r
+ c = fe_ctrl_new(ctrl);\r
+ add234(d->byctrl, c);\r
+\r
+ cw = ch = 0;\r
+\r
+ switch (ctrl->generic.type) {\r
+ case CTRL_BUTTON:\r
+ case CTRL_CHECKBOX:\r
+ {\r
+ NSButton *b;\r
+\r
+ b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];\r
+ [b setBezelStyle:NSRoundedBezelStyle];\r
+ if (ctrl->generic.type == CTRL_CHECKBOX)\r
+ [b setButtonType:NSSwitchButton];\r
+ [b setTitle:[NSString stringWithCString:ctrl->generic.label]];\r
+ if (ctrl->button.isdefault)\r
+ [b setKeyEquivalent:@"\r"];\r
+ else if (ctrl->button.iscancel)\r
+ [b setKeyEquivalent:@"\033"];\r
+ [b sizeToFit];\r
+ rect = [b frame];\r
+\r
+ [parent addSubview:b];\r
+\r
+ [b setTarget:d->rec];\r
+ if (ctrl->generic.type == CTRL_CHECKBOX)\r
+ [b setAction:@selector(checkboxChanged:)];\r
+ else\r
+ [b setAction:@selector(buttonPushed:)];\r
+ add_widget(d, c, b);\r
+\r
+ c->button = b;\r
+\r
+ cw = rect.size.width;\r
+ ch = rect.size.height;\r
+ }\r
+ break;\r
+ case CTRL_EDITBOX:\r
+ {\r
+ int editp = ctrl->editbox.percentwidth;\r
+ int labelp = editp == 100 ? 100 : 100 - editp;\r
+ NSTextField *tf;\r
+ NSComboBox *cb;\r
+\r
+ tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+ [tf setEditable:NO];\r
+ [tf setSelectable:NO];\r
+ [tf setBordered:NO];\r
+ [tf setDrawsBackground:NO];\r
+ [tf setStringValue:[NSString\r
+ stringWithCString:ctrl->generic.label]];\r
+ [tf sizeToFit];\r
+ rect = [tf frame];\r
+ [parent addSubview:tf];\r
+ c->label = tf;\r
+\r
+ cw = rect.size.width * 100 / labelp;\r
+ ch = rect.size.height;\r
+\r
+ if (ctrl->editbox.has_list) {\r
+ cb = [[NSComboBox alloc]\r
+ initWithFrame:NSMakeRect(0,0,1,1)];\r
+ [cb setStringValue:@"x"];\r
+ [cb sizeToFit];\r
+ rect = [cb frame];\r
+ [parent addSubview:cb];\r
+ c->combobox = cb;\r
+ } else {\r
+ if (ctrl->editbox.password)\r
+ tf = [NSSecureTextField alloc];\r
+ else\r
+ tf = [NSTextField alloc];\r
+\r
+ tf = [tf initWithFrame:NSMakeRect(0,0,1,1)];\r
+ [tf setEditable:YES];\r
+ [tf setSelectable:YES];\r
+ [tf setBordered:YES];\r
+ [tf setStringValue:@"x"];\r
+ [tf sizeToFit];\r
+ rect = [tf frame];\r
+ [parent addSubview:tf];\r
+ c->editbox = tf;\r
+\r
+ [tf setDelegate:d->rec];\r
+ add_widget(d, c, tf);\r
+ }\r
+\r
+ if (editp == 100) {\r
+ /* the edit box and its label are vertically separated */\r
+ ch += VSPACING + rect.size.height;\r
+ } else {\r
+ /* the edit box and its label are horizontally separated */\r
+ if (ch < rect.size.height)\r
+ ch = rect.size.height;\r
+ }\r
+\r
+ if (cw < rect.size.width * 100 / editp)\r
+ cw = rect.size.width * 100 / editp;\r
+ }\r
+ break;\r
+ case CTRL_TEXT:\r
+ {\r
+ NSTextView *tv;\r
+ int testwid;\r
+\r
+ if (!textviewfont) {\r
+ NSTextField *tf;\r
+ tf = [[NSTextField alloc] init];\r
+ textviewfont = [tf font];\r
+ [tf release];\r
+ }\r
+\r
+ testwid = (ccw[colend] - ccw[colstart]) * 3;\r
+\r
+ tv = [[NSTextView alloc]\r
+ initWithFrame:NSMakeRect(0,0,testwid,1)];\r
+ [tv setEditable:NO];\r
+ [tv setSelectable:NO];\r
+ //[tv setBordered:NO];\r
+ [tv setDrawsBackground:NO];\r
+ [tv setFont:textviewfont];\r
+ [tv setString:\r
+ [NSString stringWithCString:ctrl->generic.label]];\r
+ rect = [tv frame];\r
+ [tv sizeToFit];\r
+ [parent addSubview:tv];\r
+ c->textview = tv;\r
+\r
+ cw = rect.size.width;\r
+ ch = rect.size.height;\r
+ }\r
+ break;\r
+ case CTRL_RADIO:\r
+ {\r
+ NSTextField *tf;\r
+ int j;\r
+\r
+ if (ctrl->generic.label) {\r
+ tf = [[NSTextField alloc]\r
+ initWithFrame:NSMakeRect(0,0,1,1)];\r
+ [tf setEditable:NO];\r
+ [tf setSelectable:NO];\r
+ [tf setBordered:NO];\r
+ [tf setDrawsBackground:NO];\r
+ [tf setStringValue:\r
+ [NSString stringWithCString:ctrl->generic.label]];\r
+ [tf sizeToFit];\r
+ rect = [tf frame];\r
+ [parent addSubview:tf];\r
+ c->label = tf;\r
+\r
+ cw = rect.size.width;\r
+ ch = rect.size.height;\r
+ } else {\r
+ cw = 0;\r
+ ch = -VSPACING; /* compensate for next advance */\r
+ }\r
+\r
+ c->nradiobuttons = ctrl->radio.nbuttons;\r
+ c->radiobuttons = snewn(ctrl->radio.nbuttons, NSButton *);\r
+\r
+ for (j = 0; j < ctrl->radio.nbuttons; j++) {\r
+ NSButton *b;\r
+ int ncols;\r
+\r
+ b = [[MyButton alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+ [b setBezelStyle:NSRoundedBezelStyle];\r
+ [b setButtonType:NSRadioButton];\r
+ [b setTitle:[NSString\r
+ stringWithCString:ctrl->radio.buttons[j]]];\r
+ [b sizeToFit];\r
+ rect = [b frame];\r
+ [parent addSubview:b];\r
+\r
+ c->radiobuttons[j] = b;\r
+\r
+ [b setTarget:d->rec];\r
+ [b setAction:@selector(radioChanged:)];\r
+ add_widget(d, c, b);\r
+\r
+ /*\r
+ * Add to the height every time we place a\r
+ * button in column 0.\r
+ */\r
+ if (j % ctrl->radio.ncolumns == 0) {\r
+ ch += rect.size.height + VSPACING;\r
+ }\r
+\r
+ /*\r
+ * Add to the width by working out how many\r
+ * columns this button spans.\r
+ */\r
+ if (j == ctrl->radio.nbuttons - 1)\r
+ ncols = (ctrl->radio.ncolumns -\r
+ (j % ctrl->radio.ncolumns));\r
+ else\r
+ ncols = 1;\r
+\r
+ if (cw < rect.size.width * ctrl->radio.ncolumns / ncols)\r
+ cw = rect.size.width * ctrl->radio.ncolumns / ncols;\r
+ }\r
+ }\r
+ break;\r
+ case CTRL_FILESELECT:\r
+ case CTRL_FONTSELECT:\r
+ {\r
+ NSTextField *tf;\r
+ NSButton *b;\r
+ int kh;\r
+\r
+ tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];\r
+ [tf setEditable:NO];\r
+ [tf setSelectable:NO];\r
+ [tf setBordered:NO];\r
+ [tf setDrawsBackground:NO];\r
+ [tf setStringValue:[NSString\r
+ stringWithCString:ctrl->generic.label]];\r
+ [tf sizeToFit];\r
+ rect = [tf frame];\r
+ [parent addSubview:tf];\r
+ c->label = tf;\r
+\r
+ cw = rect.size.width;\r
+ ch = rect.size.height;\r
+\r
+ tf = [NSTextField alloc];\r
+ tf = [tf initWithFrame:NSMakeRect(0,0,1,1)];\r
+ if (ctrl->generic.type == CTRL_FILESELECT) {\r
+ [tf setEditable:YES];\r
+ [tf setSelectable:YES];\r
+ [tf setBordered:YES];\r
+ } else {\r
+ [tf setEditable:NO];\r
+ [tf setSelectable:NO];\r
+ [tf setBordered:NO];\r
+ [tf setDrawsBackground:NO];\r
+ }\r
+ [tf setStringValue:@"x"];\r
+ [tf sizeToFit];\r
+ rect = [tf frame];\r
+ [parent addSubview:tf];\r
+ c->editbox = tf;\r
+\r
+ kh = rect.size.height;\r
+ if (cw < rect.size.width * 4 / 3)\r
+ cw = rect.size.width * 4 / 3;\r
+\r
+ b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];\r
+ [b setBezelStyle:NSRoundedBezelStyle];\r
+ if (ctrl->generic.type == CTRL_FILESELECT)\r
+ [b setTitle:@"Browse..."];\r
+ else\r
+ [b setTitle:@"Change..."];\r
+ // [b setKeyEquivalent:somethingorother];\r
+ // [b setTarget:somethingorother];\r
+ // [b setAction:somethingorother];\r
+ [b sizeToFit];\r
+ rect = [b frame];\r
+ [parent addSubview:b];\r
+\r
+ c->button = b;\r
+\r
+ if (kh < rect.size.height)\r
+ kh = rect.size.height;\r
+ ch += VSPACING + kh;\r
+ if (cw < rect.size.width * 4)\r
+ cw = rect.size.width * 4;\r
+ }\r
+ break;\r
+ case CTRL_LISTBOX:\r
+ {\r
+ int listp = ctrl->listbox.percentwidth;\r
+ int labelp = listp == 100 ? 100 : 100 - listp;\r
+ NSTextField *tf;\r
+ NSPopUpButton *pb;\r
+ NSTableView *tv;\r
+ NSScrollView *sv;\r
+\r
+ if (ctrl->generic.label) {\r
+ tf = [[NSTextField alloc]\r
+ initWithFrame:NSMakeRect(0,0,1,1)];\r
+ [tf setEditable:NO];\r
+ [tf setSelectable:NO];\r
+ [tf setBordered:NO];\r
+ [tf setDrawsBackground:NO];\r
+ [tf setStringValue:\r
+ [NSString stringWithCString:ctrl->generic.label]];\r
+ [tf sizeToFit];\r
+ rect = [tf frame];\r
+ [parent addSubview:tf];\r
+ c->label = tf;\r
+\r
+ cw = rect.size.width;\r
+ ch = rect.size.height;\r
+ } else {\r
+ cw = 0;\r
+ ch = -VSPACING; /* compensate for next advance */\r
+ }\r
+\r
+ if (ctrl->listbox.height == 0) {\r
+ pb = [[NSPopUpButton alloc]\r
+ initWithFrame:NSMakeRect(0,0,1,1)];\r
+ [pb sizeToFit];\r
+ rect = [pb frame];\r
+ [parent addSubview:pb];\r
+ c->popupbutton = pb;\r
+\r
+ [pb setTarget:d->rec];\r
+ [pb setAction:@selector(popupMenuSelected:)];\r
+ add_widget(d, c, pb);\r
+ } else {\r
+ assert(listp == 100);\r
+ if (ctrl->listbox.draglist) {\r
+ int bi;\r
+\r
+ listp = 75;\r
+\r
+ for (bi = 0; bi < 2; bi++) {\r
+ NSButton *b;\r
+ b = [[MyButton alloc]\r
+ initWithFrame:NSMakeRect(0, 0, 1, 1)];\r
+ [b setBezelStyle:NSRoundedBezelStyle];\r
+ if (bi == 0)\r
+ [b setTitle:@"Up"];\r
+ else\r
+ [b setTitle:@"Down"];\r
+ [b sizeToFit];\r
+ rect = [b frame];\r
+ [parent addSubview:b];\r
+\r
+ if (bi == 0)\r
+ c->button = b;\r
+ else\r
+ c->button2 = b;\r
+\r
+ [b setTarget:d->rec];\r
+ [b setAction:@selector(dragListButton:)];\r
+ add_widget(d, c, b);\r
+\r
+ if (cw < rect.size.width * 4)\r
+ cw = rect.size.width * 4;\r
+ }\r
+ }\r
+\r
+ sv = [[NSScrollView alloc] initWithFrame:\r
+ NSMakeRect(20,20,10,10)];\r
+ [sv setBorderType:NSLineBorder];\r
+ tv = [[NSTableView alloc] initWithFrame:[sv frame]];\r
+ [[tv headerView] setFrame:NSMakeRect(0,0,0,0)];\r
+ [sv setDocumentView:tv];\r
+ [parent addSubview:sv];\r
+ [sv setHasVerticalScroller:YES];\r
+ [sv setAutohidesScrollers:YES];\r
+ [tv setAllowsColumnReordering:NO];\r
+ [tv setAllowsColumnResizing:NO];\r
+ [tv setAllowsMultipleSelection:ctrl->listbox.multisel];\r
+ [tv setAllowsEmptySelection:YES];\r
+ [tv setAllowsColumnSelection:YES];\r
+ [tv setDataSource:[[MyTableSource alloc] init]];\r
+ rect = [tv frame];\r
+ /*\r
+ * For some reason this consistently comes out\r
+ * one short. Add one.\r
+ */\r
+ rect.size.height = (ctrl->listbox.height+1)*[tv rowHeight];\r
+ [sv setFrame:rect];\r
+ c->tableview = tv;\r
+ c->scrollview = sv;\r
+\r
+ [tv setDelegate:d->rec];\r
+ [tv setTarget:d->rec];\r
+ [tv setDoubleAction:@selector(listDoubleClicked:)];\r
+ add_widget(d, c, tv);\r
+ }\r
+\r
+ if (c->tableview) {\r
+ int ncols, *percentages;\r
+ int hundred = 100;\r
+\r
+ if (ctrl->listbox.ncols) {\r
+ ncols = ctrl->listbox.ncols;\r
+ percentages = ctrl->listbox.percentages;\r
+ } else {\r
+ ncols = 1;\r
+ percentages = &hundred;\r
+ }\r
+\r
+ for (j = 0; j < ncols; j++) {\r
+ NSTableColumn *col;\r
+\r
+ col = [[NSTableColumn alloc] initWithIdentifier:\r
+ [NSNumber numberWithInt:j]];\r
+ [c->tableview addTableColumn:col];\r
+ }\r
+ }\r
+\r
+ if (labelp == 100) {\r
+ /* the list and its label are vertically separated */\r
+ ch += VSPACING + rect.size.height;\r
+ } else {\r
+ /* the list and its label are horizontally separated */\r
+ if (ch < rect.size.height)\r
+ ch = rect.size.height;\r
+ }\r
+\r
+ if (cw < rect.size.width * 100 / listp)\r
+ cw = rect.size.width * 100 / listp;\r
+ }\r
+ break;\r
+ }\r
+\r
+ /*\r
+ * Update the width and height data for the control we've\r
+ * just created.\r
+ */\r
+ ytop = 0;\r
+\r
+ for (j = colstart; j < colend; j++) {\r
+ if (ytop < cypos[j])\r
+ ytop = cypos[j];\r
+ }\r
+\r
+ for (j = colstart; j < colend; j++)\r
+ cypos[j] = ytop + ch + VSPACING;\r
+\r
+ if (hmin < ytop + ch)\r
+ hmin = ytop + ch;\r
+\r
+ wthis = (cw + HSPACING) * 100 / (ccw[colend] - ccw[colstart]);\r
+ wthis -= HSPACING;\r
+\r
+ if (wmin < wthis)\r
+ wmin = wthis;\r
+ }\r
+\r
+ if (*s->boxname) {\r
+ /*\r
+ * Add a bit to the width and height for the box.\r
+ */\r
+ wmin += boxw;\r
+ hmin += boxh;\r
+ }\r
+\r
+ //printf("For controlset %s/%s, returning w=%d h=%d\n",\r
+ // s->pathname, s->boxname, wmin, hmin);\r
+ *minw = wmin;\r
+ *minh = hmin;\r
+}\r
+\r
+int place_ctrls(void *dv, struct controlset *s, int leftx, int topy,\r
+ int width)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ int ccw[100]; /* cumulative column widths */\r
+ int cypos[100];\r
+ int ncols;\r
+ int i, j, ret;\r
+ int boxh = 0, boxw = 0;\r
+\r
+ if (!s->boxname && s->boxtitle) {\r
+ /* Size and place the panel title. */\r
+\r
+ NSTextField *tf = find_box(d, s);\r
+ NSRect rect;\r
+\r
+ rect = [tf frame];\r
+ [tf setFrame:NSMakeRect(leftx, topy-rect.size.height,\r
+ width, rect.size.height)];\r
+ return rect.size.height;\r
+ }\r
+\r
+ if (*s->boxname) {\r
+ NSRect rect, tmprect;\r
+ NSBox *box = find_box(d, s);\r
+\r
+ assert(box != NULL);\r
+ tmprect = [box frame];\r
+ [box setFrameFromContentFrame:NSMakeRect(100,100,100,100)];\r
+ rect = [box frame];\r
+ [box setFrame:tmprect];\r
+ boxw = rect.size.width - 100;\r
+ boxh = rect.size.height - 100;\r
+ if (s->boxtitle)\r
+ boxh += [[box titleFont] pointSize];\r
+ topy -= boxh;\r
+ width -= boxw;\r
+ }\r
+\r
+ ncols = 1;\r
+ ccw[0] = 0;\r
+ ccw[1] = 100;\r
+ cypos[0] = topy;\r
+ ret = 0;\r
+\r
+ /*\r
+ * Now iterate through the controls themselves, placing them\r
+ * appropriately.\r
+ */\r
+ for (i = 0; i < s->ncontrols; i++) {\r
+ union control *ctrl = s->ctrls[i];\r
+ struct fe_ctrl *c;\r
+ int colstart = COLUMN_START(ctrl->generic.column);\r
+ int colspan = COLUMN_SPAN(ctrl->generic.column);\r
+ int colend = colstart + colspan;\r
+ int xthis, ythis, wthis, ch;\r
+ NSRect rect;\r
+\r
+ switch (ctrl->generic.type) {\r
+ case CTRL_COLUMNS:\r
+ for (j = 1; j < ncols; j++)\r
+ if (cypos[0] > cypos[j])\r
+ cypos[0] = cypos[j];\r
+\r
+ assert(ctrl->columns.ncols < lenof(ccw));\r
+\r
+ ccw[0] = 0;\r
+ for (j = 0; j < ctrl->columns.ncols; j++) {\r
+ ccw[j+1] = ccw[j] + (ctrl->columns.percentages ?\r
+ ctrl->columns.percentages[j] : 100);\r
+ cypos[j] = cypos[0];\r
+ }\r
+\r
+ ncols = ctrl->columns.ncols;\r
+\r
+ continue; /* no actual control created */\r
+ case CTRL_TABDELAY:\r
+ continue; /* nothing to do here, move along */\r
+ }\r
+\r
+ c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ ch = 0;\r
+ ythis = topy;\r
+\r
+ for (j = colstart; j < colend; j++) {\r
+ if (ythis > cypos[j])\r
+ ythis = cypos[j];\r
+ }\r
+\r
+ xthis = (width + HSPACING) * ccw[colstart] / 100;\r
+ wthis = (width + HSPACING) * ccw[colend] / 100 - HSPACING - xthis;\r
+ xthis += leftx;\r
+\r
+ switch (ctrl->generic.type) {\r
+ case CTRL_BUTTON:\r
+ case CTRL_CHECKBOX:\r
+ rect = [c->button frame];\r
+ [c->button setFrame:NSMakeRect(xthis,ythis-rect.size.height,wthis,\r
+ rect.size.height)];\r
+ ch = rect.size.height;\r
+ break;\r
+ case CTRL_EDITBOX:\r
+ {\r
+ int editp = ctrl->editbox.percentwidth;\r
+ int labelp = editp == 100 ? 100 : 100 - editp;\r
+ int lheight, theight, rheight, ynext, editw;\r
+ NSControl *edit = (c->editbox ? c->editbox : c->combobox);\r
+\r
+ rect = [c->label frame];\r
+ lheight = rect.size.height;\r
+ rect = [edit frame];\r
+ theight = rect.size.height;\r
+\r
+ if (editp == 100)\r
+ rheight = lheight;\r
+ else\r
+ rheight = (lheight < theight ? theight : lheight);\r
+\r
+ [c->label setFrame:\r
+ NSMakeRect(xthis, ythis-(rheight+lheight)/2,\r
+ (wthis + HSPACING) * labelp / 100 - HSPACING,\r
+ lheight)];\r
+ if (editp == 100) {\r
+ ynext = ythis - rheight - VSPACING;\r
+ rheight = theight;\r
+ } else {\r
+ ynext = ythis;\r
+ }\r
+\r
+ editw = (wthis + HSPACING) * editp / 100 - HSPACING;\r
+\r
+ [edit setFrame:\r
+ NSMakeRect(xthis+wthis-editw, ynext-(rheight+theight)/2,\r
+ editw, theight)];\r
+\r
+ ch = (ythis - ynext) + theight;\r
+ }\r
+ break;\r
+ case CTRL_TEXT:\r
+ [c->textview setFrame:NSMakeRect(xthis, 0, wthis, 1)];\r
+ [c->textview sizeToFit];\r
+ rect = [c->textview frame];\r
+ [c->textview setFrame:NSMakeRect(xthis, ythis-rect.size.height,\r
+ wthis, rect.size.height)];\r
+ ch = rect.size.height;\r
+ break;\r
+ case CTRL_RADIO:\r
+ {\r
+ int j, ynext;\r
+\r
+ if (c->label) {\r
+ rect = [c->label frame];\r
+ [c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height,\r
+ wthis,rect.size.height)];\r
+ ynext = ythis - rect.size.height - VSPACING;\r
+ } else\r
+ ynext = ythis;\r
+\r
+ for (j = 0; j < ctrl->radio.nbuttons; j++) {\r
+ int col = j % ctrl->radio.ncolumns;\r
+ int ncols;\r
+ int lx,rx;\r
+\r
+ if (j == ctrl->radio.nbuttons - 1)\r
+ ncols = ctrl->radio.ncolumns - col;\r
+ else\r
+ ncols = 1;\r
+\r
+ lx = (wthis + HSPACING) * col / ctrl->radio.ncolumns;\r
+ rx = ((wthis + HSPACING) *\r
+ (col+ncols) / ctrl->radio.ncolumns) - HSPACING;\r
+\r
+ /*\r
+ * Set the frame size.\r
+ */\r
+ rect = [c->radiobuttons[j] frame];\r
+ [c->radiobuttons[j] setFrame:\r
+ NSMakeRect(lx+xthis, ynext-rect.size.height,\r
+ rx-lx, rect.size.height)];\r
+\r
+ /*\r
+ * Advance to next line if we're in the last\r
+ * column.\r
+ */\r
+ if (col + ncols == ctrl->radio.ncolumns)\r
+ ynext -= rect.size.height + VSPACING;\r
+ }\r
+ ch = (ythis - ynext) - VSPACING;\r
+ }\r
+ break;\r
+ case CTRL_FILESELECT:\r
+ case CTRL_FONTSELECT:\r
+ {\r
+ int ynext, eh, bh, th, mx;\r
+\r
+ rect = [c->label frame];\r
+ [c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height,\r
+ wthis,rect.size.height)];\r
+ ynext = ythis - rect.size.height - VSPACING;\r
+\r
+ rect = [c->editbox frame];\r
+ eh = rect.size.height;\r
+ rect = [c->button frame];\r
+ bh = rect.size.height;\r
+ th = (eh > bh ? eh : bh);\r
+\r
+ mx = (wthis + HSPACING) * 3 / 4 - HSPACING;\r
+\r
+ [c->editbox setFrame:\r
+ NSMakeRect(xthis, ynext-(th+eh)/2, mx, eh)];\r
+ [c->button setFrame:\r
+ NSMakeRect(xthis+mx+HSPACING, ynext-(th+bh)/2,\r
+ wthis-mx-HSPACING, bh)];\r
+\r
+ ch = (ythis - ynext) + th + VSPACING;\r
+ }\r
+ break;\r
+ case CTRL_LISTBOX:\r
+ {\r
+ int listp = ctrl->listbox.percentwidth;\r
+ int labelp = listp == 100 ? 100 : 100 - listp;\r
+ int lheight, theight, rheight, ynext, listw, xlist;\r
+ NSControl *list = (c->scrollview ? (id)c->scrollview :\r
+ (id)c->popupbutton);\r
+\r
+ if (ctrl->listbox.draglist) {\r
+ assert(listp == 100);\r
+ listp = 75;\r
+ }\r
+\r
+ rect = [list frame];\r
+ theight = rect.size.height;\r
+\r
+ if (c->label) {\r
+ rect = [c->label frame];\r
+ lheight = rect.size.height;\r
+\r
+ if (labelp == 100)\r
+ rheight = lheight;\r
+ else\r
+ rheight = (lheight < theight ? theight : lheight);\r
+\r
+ [c->label setFrame:\r
+ NSMakeRect(xthis, ythis-(rheight+lheight)/2,\r
+ (wthis + HSPACING) * labelp / 100 - HSPACING,\r
+ lheight)];\r
+ if (labelp == 100) {\r
+ ynext = ythis - rheight - VSPACING;\r
+ rheight = theight;\r
+ } else {\r
+ ynext = ythis;\r
+ }\r
+ } else {\r
+ ynext = ythis;\r
+ rheight = theight;\r
+ }\r
+\r
+ listw = (wthis + HSPACING) * listp / 100 - HSPACING;\r
+\r
+ if (labelp == 100)\r
+ xlist = xthis;\r
+ else\r
+ xlist = xthis+wthis-listw;\r
+\r
+ [list setFrame: NSMakeRect(xlist, ynext-(rheight+theight)/2,\r
+ listw, theight)];\r
+\r
+ /*\r
+ * Size the columns for the table view.\r
+ */\r
+ if (c->tableview) {\r
+ int ncols, *percentages;\r
+ int hundred = 100;\r
+ int cpercent = 0, cpixels = 0;\r
+ NSArray *cols;\r
+\r
+ if (ctrl->listbox.ncols) {\r
+ ncols = ctrl->listbox.ncols;\r
+ percentages = ctrl->listbox.percentages;\r
+ } else {\r
+ ncols = 1;\r
+ percentages = &hundred;\r
+ }\r
+\r
+ cols = [c->tableview tableColumns];\r
+\r
+ for (j = 0; j < ncols; j++) {\r
+ NSTableColumn *col = [cols objectAtIndex:j];\r
+ int newcpixels;\r
+\r
+ cpercent += percentages[j];\r
+ newcpixels = listw * cpercent / 100;\r
+ [col setWidth:newcpixels-cpixels];\r
+ cpixels = newcpixels;\r
+ }\r
+ }\r
+\r
+ ch = (ythis - ynext) + theight;\r
+\r
+ if (c->button) {\r
+ int b2height, centre;\r
+ int bx, bw;\r
+\r
+ /*\r
+ * Place the Up and Down buttons for a drag list.\r
+ */\r
+ assert(c->button2);\r
+\r
+ rect = [c->button frame];\r
+ b2height = VSPACING + 2 * rect.size.height;\r
+\r
+ centre = ynext - rheight/2;\r
+\r
+ bx = (wthis + HSPACING) * 3 / 4;\r
+ bw = wthis - bx;\r
+ bx += leftx;\r
+\r
+ [c->button setFrame:\r
+ NSMakeRect(bx, centre+b2height/2-rect.size.height,\r
+ bw, rect.size.height)];\r
+ [c->button2 setFrame:\r
+ NSMakeRect(bx, centre-b2height/2,\r
+ bw, rect.size.height)];\r
+ }\r
+ }\r
+ break;\r
+ }\r
+\r
+ for (j = colstart; j < colend; j++)\r
+ cypos[j] = ythis - ch - VSPACING;\r
+ if (ret < topy - (ythis - ch))\r
+ ret = topy - (ythis - ch);\r
+ }\r
+\r
+ if (*s->boxname) {\r
+ NSBox *box = find_box(d, s);\r
+ assert(box != NULL);\r
+ [box sizeToFit];\r
+\r
+ if (s->boxtitle) {\r
+ NSRect rect = [box frame];\r
+ rect.size.height += [[box titleFont] pointSize];\r
+ [box setFrame:rect];\r
+ }\r
+\r
+ ret += boxh;\r
+ }\r
+\r
+ //printf("For controlset %s/%s, returning ret=%d\n",\r
+ // s->pathname, s->boxname, ret);\r
+ return ret;\r
+}\r
+\r
+void select_panel(void *dv, struct controlbox *b, const char *name)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ int i, j, hidden;\r
+ struct controlset *s;\r
+ union control *ctrl;\r
+ struct fe_ctrl *c;\r
+ NSBox *box;\r
+\r
+ for (i = 0; i < b->nctrlsets; i++) {\r
+ s = b->ctrlsets[i];\r
+\r
+ if (*s->pathname) {\r
+ hidden = !strcmp(s->pathname, name) ? NO : YES;\r
+\r
+ if ((box = find_box(d, s)) != NULL) {\r
+ [box setHidden:hidden];\r
+ } else {\r
+ for (j = 0; j < s->ncontrols; j++) {\r
+ ctrl = s->ctrls[j];\r
+ c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ if (!c)\r
+ continue;\r
+\r
+ if (c->label)\r
+ [c->label setHidden:hidden];\r
+ if (c->button)\r
+ [c->button setHidden:hidden];\r
+ if (c->button2)\r
+ [c->button2 setHidden:hidden];\r
+ if (c->editbox)\r
+ [c->editbox setHidden:hidden];\r
+ if (c->combobox)\r
+ [c->combobox setHidden:hidden];\r
+ if (c->textview)\r
+ [c->textview setHidden:hidden];\r
+ if (c->tableview)\r
+ [c->tableview setHidden:hidden];\r
+ if (c->scrollview)\r
+ [c->scrollview setHidden:hidden];\r
+ if (c->popupbutton)\r
+ [c->popupbutton setHidden:hidden];\r
+ if (c->radiobuttons) {\r
+ int j;\r
+ for (j = 0; j < c->nradiobuttons; j++)\r
+ [c->radiobuttons[j] setHidden:hidden];\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+void dlg_radiobutton_set(union control *ctrl, void *dv, int whichbutton)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+ int j;\r
+\r
+ assert(c->radiobuttons);\r
+ for (j = 0; j < c->nradiobuttons; j++)\r
+ [c->radiobuttons[j] setState:\r
+ (j == whichbutton ? NSOnState : NSOffState)];\r
+}\r
+\r
+int dlg_radiobutton_get(union control *ctrl, void *dv)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+ int j;\r
+\r
+ assert(c->radiobuttons);\r
+ for (j = 0; j < c->nradiobuttons; j++)\r
+ if ([c->radiobuttons[j] state] == NSOnState)\r
+ return j;\r
+\r
+ return 0; /* should never reach here */\r
+}\r
+\r
+void dlg_checkbox_set(union control *ctrl, void *dv, int checked)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ assert(c->button);\r
+ [c->button setState:(checked ? NSOnState : NSOffState)];\r
+}\r
+\r
+int dlg_checkbox_get(union control *ctrl, void *dv)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ assert(c->button);\r
+ return ([c->button state] == NSOnState);\r
+}\r
+\r
+void dlg_editbox_set(union control *ctrl, void *dv, char const *text)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ if (c->editbox) {\r
+ [c->editbox setStringValue:[NSString stringWithCString:text]];\r
+ } else {\r
+ assert(c->combobox);\r
+ [c->combobox setStringValue:[NSString stringWithCString:text]];\r
+ }\r
+}\r
+\r
+void dlg_editbox_get(union control *ctrl, void *dv, char *buffer, int length)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+ NSString *str;\r
+\r
+ if (c->editbox) {\r
+ str = [c->editbox stringValue];\r
+ } else {\r
+ assert(c->combobox);\r
+ str = [c->combobox stringValue];\r
+ }\r
+ if (!str)\r
+ str = @"";\r
+\r
+ /* The length parameter to this method doesn't include a trailing NUL */\r
+ [str getCString:buffer maxLength:length-1];\r
+}\r
+\r
+void dlg_listbox_clear(union control *ctrl, void *dv)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ if (c->tableview) {\r
+ [[c->tableview dataSource] clear];\r
+ [c->tableview reloadData];\r
+ } else {\r
+ [c->popupbutton removeAllItems];\r
+ }\r
+}\r
+\r
+void dlg_listbox_del(union control *ctrl, void *dv, int index)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ if (c->tableview) {\r
+ [[c->tableview dataSource] removestr:index];\r
+ [c->tableview reloadData];\r
+ } else {\r
+ [c->popupbutton removeItemAtIndex:index];\r
+ }\r
+}\r
+\r
+void dlg_listbox_addwithid(union control *ctrl, void *dv,\r
+ char const *text, int id)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ if (c->tableview) {\r
+ [[c->tableview dataSource] add:text withId:id];\r
+ [c->tableview reloadData];\r
+ } else {\r
+ [c->popupbutton addItemWithTitle:[NSString stringWithCString:text]];\r
+ [[c->popupbutton lastItem] setTag:id];\r
+ }\r
+}\r
+\r
+void dlg_listbox_add(union control *ctrl, void *dv, char const *text)\r
+{\r
+ dlg_listbox_addwithid(ctrl, dv, text, -1);\r
+}\r
+\r
+int dlg_listbox_getid(union control *ctrl, void *dv, int index)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ if (c->tableview) {\r
+ return [[c->tableview dataSource] getid:index];\r
+ } else {\r
+ return [[c->popupbutton itemAtIndex:index] tag];\r
+ }\r
+}\r
+\r
+int dlg_listbox_index(union control *ctrl, void *dv)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ if (c->tableview) {\r
+ return [c->tableview selectedRow];\r
+ } else {\r
+ return [c->popupbutton indexOfSelectedItem];\r
+ }\r
+}\r
+\r
+int dlg_listbox_issel(union control *ctrl, void *dv, int index)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ if (c->tableview) {\r
+ return [c->tableview isRowSelected:index];\r
+ } else {\r
+ return [c->popupbutton indexOfSelectedItem] == index;\r
+ }\r
+}\r
+\r
+void dlg_listbox_select(union control *ctrl, void *dv, int index)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ if (c->tableview) {\r
+ [c->tableview selectRow:index byExtendingSelection:NO];\r
+ } else {\r
+ [c->popupbutton selectItemAtIndex:index];\r
+ }\r
+}\r
+\r
+void dlg_text_set(union control *ctrl, void *dv, char const *text)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+\r
+ assert(c->textview);\r
+ [c->textview setString:[NSString stringWithCString:text]];\r
+}\r
+\r
+void dlg_label_change(union control *ctrl, void *dlg, char const *text)\r
+{\r
+ /*\r
+ * This function is currently only used by the config box to\r
+ * switch the labels on the host and port boxes between serial\r
+ * and network modes. Since OS X does not (yet?) have a serial\r
+ * back end, this function can safely do nothing for the\r
+ * moment.\r
+ */\r
+}\r
+\r
+void dlg_filesel_set(union control *ctrl, void *dv, Filename fn)\r
+{\r
+ /* FIXME */\r
+}\r
+\r
+void dlg_filesel_get(union control *ctrl, void *dv, Filename *fn)\r
+{\r
+ /* FIXME */\r
+}\r
+\r
+void dlg_fontsel_set(union control *ctrl, void *dv, FontSpec fn)\r
+{\r
+ /* FIXME */\r
+}\r
+\r
+void dlg_fontsel_get(union control *ctrl, void *dv, FontSpec *fn)\r
+{\r
+ /* FIXME */\r
+}\r
+\r
+void dlg_update_start(union control *ctrl, void *dv)\r
+{\r
+ /* FIXME */\r
+}\r
+\r
+void dlg_update_done(union control *ctrl, void *dv)\r
+{\r
+ /* FIXME */\r
+}\r
+\r
+void dlg_set_focus(union control *ctrl, void *dv)\r
+{\r
+ /* FIXME */\r
+}\r
+\r
+union control *dlg_last_focused(union control *ctrl, void *dv)\r
+{\r
+ return NULL; /* FIXME */\r
+}\r
+\r
+void dlg_beep(void *dv)\r
+{\r
+ NSBeep();\r
+}\r
+\r
+void dlg_error_msg(void *dv, char *msg)\r
+{\r
+ /* FIXME */\r
+}\r
+\r
+void dlg_end(void *dv, int value)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ [d->target performSelector:d->action\r
+ withObject:[NSNumber numberWithInt:value]];\r
+}\r
+\r
+void dlg_coloursel_start(union control *ctrl, void *dv,\r
+ int r, int g, int b)\r
+{\r
+ /* FIXME */\r
+}\r
+\r
+int dlg_coloursel_results(union control *ctrl, void *dv,\r
+ int *r, int *g, int *b)\r
+{\r
+ return 0; /* FIXME */\r
+}\r
+\r
+void dlg_refresh(union control *ctrl, void *dv)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c;\r
+\r
+ if (ctrl) {\r
+ if (ctrl->generic.handler != NULL)\r
+ ctrl->generic.handler(ctrl, d, d->data, EVENT_REFRESH);\r
+ } else {\r
+ int i;\r
+\r
+ for (i = 0; (c = index234(d->byctrl, i)) != NULL; i++) {\r
+ assert(c->ctrl != NULL);\r
+ if (c->ctrl->generic.handler != NULL)\r
+ c->ctrl->generic.handler(c->ctrl, d,\r
+ d->data, EVENT_REFRESH);\r
+ }\r
+ }\r
+}\r
+\r
+void *dlg_get_privdata(union control *ctrl, void *dv)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+ return c->privdata;\r
+}\r
+\r
+void dlg_set_privdata(union control *ctrl, void *dv, void *ptr)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+ c->privdata = ptr;\r
+ c->privdata_needs_free = FALSE;\r
+}\r
+\r
+void *dlg_alloc_privdata(union control *ctrl, void *dv, size_t size)\r
+{\r
+ struct fe_dlg *d = (struct fe_dlg *)dv;\r
+ struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);\r
+ /*\r
+ * This is an internal allocation routine, so it's allowed to\r
+ * use smalloc directly.\r
+ */\r
+ c->privdata = smalloc(size);\r
+ c->privdata_needs_free = TRUE;\r
+ return c->privdata;\r
+}\r