Sebastian Kuschel reports that pfd_closing can be called for a socket
[u/mdw/putty] / macosx / osxctrls.m
1 /*
2 * osxctrls.m: OS X implementation of the dialog.h interface.
3 */
4
5 #import <Cocoa/Cocoa.h>
6 #include "putty.h"
7 #include "dialog.h"
8 #include "osxclass.h"
9 #include "tree234.h"
10
11 /*
12 * Still to be implemented:
13 *
14 * - file selectors (NSOpenPanel / NSSavePanel)
15 *
16 * - font selectors
17 * - colour selectors
18 * * both of these have a conceptual oddity in Cocoa that
19 * you're only supposed to have one per application. But I
20 * currently expect to be able to have multiple PuTTY config
21 * boxes on screen at once; what happens if you trigger the
22 * font selector in each one at the same time?
23 * * if it comes to that, the _font_ selector can probably be
24 * managed by other means: nobody is forcing me to implement
25 * a font selector using a `Change...' button. The portable
26 * dialog interface gives me the flexibility to do this how I
27 * want.
28 * * The colour selector interface, in its present form, is
29 * more interesting and _if_ a radical change of plan is
30 * required then it may stretch across the interface into the
31 * portable side.
32 * * Before I do anything rash I should start by looking at the
33 * Mac Classic port and see how it's done there, on the basis
34 * that Apple seem reasonably unlikely to have invented this
35 * crazy restriction specifically for OS X.
36 *
37 * - focus management
38 * * I tried using makeFirstResponder to give keyboard focus,
39 * but it appeared not to work. Try again, and work out how
40 * it should be done.
41 * * also look into tab order. Currently pressing Tab suggests
42 * that only edit boxes and list boxes can get the keyboard
43 * focus, and that buttons (in all their forms) are unable to
44 * be driven by the keyboard. Find out for sure.
45 *
46 * - dlg_error_msg
47 * * this may run into the usual aggro with modal dialog boxes.
48 */
49
50 /*
51 * For Cocoa control layout, I need a two-stage process. In stage
52 * one, I allocate all the controls and measure their natural
53 * sizes, which allows me to compute the _minimum_ width and height
54 * of a given section of dialog. Then, in stage two, I lay out the
55 * dialog box as a whole, decide how much each section of the box
56 * needs to receive, and assign it its final size.
57 */
58
59 /*
60 * As yet unsolved issues [FIXME]:
61 *
62 * - Sometimes the height returned from create_ctrls and the
63 * height returned from place_ctrls differ. Find out why. It may
64 * be harmless (e.g. results of NSTextView being odd), but I
65 * want to know.
66 *
67 * - NSTextViews are indented a bit. It'd be nice to put their
68 * left margin at the same place as everything else's.
69 *
70 * - I don't yet know whether we even _can_ support tab order or
71 * keyboard shortcuts. If we can't, then fair enough, we can't.
72 * But if we can, we should.
73 *
74 * - I would _really_ like to know of a better way to correct
75 * NSButton's stupid size estimates than by subclassing it and
76 * overriding sizeToFit with hard-wired sensible values!
77 *
78 * - Speaking of stupid size estimates, the amount by which I'm
79 * adjusting a titled NSBox (currently equal to the point size
80 * of its title font) looks as if it isn't _quite_ enough.
81 * Figure out what the real amount should be and use it.
82 *
83 * - I don't understand why there's always a scrollbar displayed
84 * in each list box. I thought I told it to autohide scrollers?
85 *
86 * - Why do I have to fudge list box heights by adding one? (Might
87 * it be to do with the missing header view?)
88 */
89
90 /*
91 * Subclass of NSButton which corrects the fact that the normal
92 * one's sizeToFit method persistently returns 32 as its height,
93 * which is simply a lie. I have yet to work out a better
94 * alternative than hard-coding the real heights.
95 */
96 @interface MyButton : NSButton
97 {
98 int minht;
99 }
100 @end
101 @implementation MyButton
102 - (id)initWithFrame:(NSRect)r
103 {
104 self = [super initWithFrame:r];
105 minht = 25;
106 return self;
107 }
108 - (void)setButtonType:(NSButtonType)t
109 {
110 if (t == NSRadioButton || t == NSSwitchButton)
111 minht = 18;
112 else
113 minht = 25;
114 [super setButtonType:t];
115 }
116 - (void)sizeToFit
117 {
118 NSRect r;
119 [super sizeToFit];
120 r = [self frame];
121 r.size.height = minht;
122 [self setFrame:r];
123 }
124 @end
125
126 /*
127 * Class used as the data source for NSTableViews.
128 */
129 @interface MyTableSource : NSObject
130 {
131 tree234 *tree;
132 }
133 - (id)init;
134 - (void)add:(const char *)str withId:(int)id;
135 - (int)getid:(int)index;
136 - (void)swap:(int)index1 with:(int)index2;
137 - (void)removestr:(int)index;
138 - (void)clear;
139 @end
140 @implementation MyTableSource
141 - (id)init
142 {
143 self = [super init];
144 tree = newtree234(NULL);
145 return self;
146 }
147 - (void)dealloc
148 {
149 char *p;
150 while ((p = delpos234(tree, 0)) != NULL)
151 sfree(p);
152 freetree234(tree);
153 [super dealloc];
154 }
155 - (void)add:(const char *)str withId:(int)id
156 {
157 addpos234(tree, dupprintf("%d\t%s", id, str), count234(tree));
158 }
159 - (int)getid:(int)index
160 {
161 char *p = index234(tree, index);
162 return atoi(p);
163 }
164 - (void)removestr:(int)index
165 {
166 char *p = delpos234(tree, index);
167 sfree(p);
168 }
169 - (void)swap:(int)index1 with:(int)index2
170 {
171 char *p1, *p2;
172
173 if (index1 > index2) {
174 int t = index1; index1 = index2; index2 = t;
175 }
176
177 /* delete later one first so it doesn't affect index of earlier one */
178 p2 = delpos234(tree, index2);
179 p1 = delpos234(tree, index1);
180
181 /* now insert earlier one before later one for the inverse reason */
182 addpos234(tree, p2, index1);
183 addpos234(tree, p1, index2);
184 }
185 - (void)clear
186 {
187 char *p;
188 while ((p = delpos234(tree, 0)) != NULL)
189 sfree(p);
190 }
191 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
192 {
193 return count234(tree);
194 }
195 - (id)tableView:(NSTableView *)aTableView
196 objectValueForTableColumn:(NSTableColumn *)aTableColumn
197 row:(int)rowIndex
198 {
199 int j = [[aTableColumn identifier] intValue];
200 char *p = index234(tree, rowIndex);
201
202 while (j >= 0) {
203 p += strcspn(p, "\t");
204 if (*p) p++;
205 j--;
206 }
207
208 return [NSString stringWithCString:p length:strcspn(p, "\t")];
209 }
210 @end
211
212 /*
213 * Object to receive messages from various control classes.
214 */
215 @class Receiver;
216
217 struct fe_dlg {
218 NSWindow *window;
219 NSObject *target;
220 SEL action;
221 tree234 *byctrl;
222 tree234 *bywidget;
223 tree234 *boxes;
224 void *data; /* passed to portable side */
225 Receiver *rec;
226 };
227
228 @interface Receiver : NSObject
229 {
230 struct fe_dlg *d;
231 }
232 - (id)initWithStruct:(struct fe_dlg *)aStruct;
233 @end
234
235 struct fe_ctrl {
236 union control *ctrl;
237 NSButton *button, *button2;
238 NSTextField *label, *editbox;
239 NSComboBox *combobox;
240 NSButton **radiobuttons;
241 NSTextView *textview;
242 NSPopUpButton *popupbutton;
243 NSTableView *tableview;
244 NSScrollView *scrollview;
245 int nradiobuttons;
246 void *privdata;
247 int privdata_needs_free;
248 };
249
250 static int fe_ctrl_cmp_by_ctrl(void *av, void *bv)
251 {
252 struct fe_ctrl *a = (struct fe_ctrl *)av;
253 struct fe_ctrl *b = (struct fe_ctrl *)bv;
254
255 if (a->ctrl < b->ctrl)
256 return -1;
257 if (a->ctrl > b->ctrl)
258 return +1;
259 return 0;
260 }
261
262 static int fe_ctrl_find_by_ctrl(void *av, void *bv)
263 {
264 union control *a = (union control *)av;
265 struct fe_ctrl *b = (struct fe_ctrl *)bv;
266
267 if (a < b->ctrl)
268 return -1;
269 if (a > b->ctrl)
270 return +1;
271 return 0;
272 }
273
274 struct fe_box {
275 struct controlset *s;
276 id box;
277 };
278
279 static int fe_boxcmp(void *av, void *bv)
280 {
281 struct fe_box *a = (struct fe_box *)av;
282 struct fe_box *b = (struct fe_box *)bv;
283
284 if (a->s < b->s)
285 return -1;
286 if (a->s > b->s)
287 return +1;
288 return 0;
289 }
290
291 static int fe_boxfind(void *av, void *bv)
292 {
293 struct controlset *a = (struct controlset *)av;
294 struct fe_box *b = (struct fe_box *)bv;
295
296 if (a < b->s)
297 return -1;
298 if (a > b->s)
299 return +1;
300 return 0;
301 }
302
303 struct fe_backwards { /* map Cocoa widgets back to fe_ctrls */
304 id widget;
305 struct fe_ctrl *c;
306 };
307
308 static int fe_backwards_cmp_by_widget(void *av, void *bv)
309 {
310 struct fe_backwards *a = (struct fe_backwards *)av;
311 struct fe_backwards *b = (struct fe_backwards *)bv;
312
313 if (a->widget < b->widget)
314 return -1;
315 if (a->widget > b->widget)
316 return +1;
317 return 0;
318 }
319
320 static int fe_backwards_find_by_widget(void *av, void *bv)
321 {
322 id a = (id)av;
323 struct fe_backwards *b = (struct fe_backwards *)bv;
324
325 if (a < b->widget)
326 return -1;
327 if (a > b->widget)
328 return +1;
329 return 0;
330 }
331
332 static struct fe_ctrl *fe_ctrl_new(union control *ctrl)
333 {
334 struct fe_ctrl *c;
335
336 c = snew(struct fe_ctrl);
337 c->ctrl = ctrl;
338
339 c->button = c->button2 = nil;
340 c->label = nil;
341 c->editbox = nil;
342 c->combobox = nil;
343 c->textview = nil;
344 c->popupbutton = nil;
345 c->tableview = nil;
346 c->scrollview = nil;
347 c->radiobuttons = NULL;
348 c->nradiobuttons = 0;
349 c->privdata = NULL;
350 c->privdata_needs_free = FALSE;
351
352 return c;
353 }
354
355 static void fe_ctrl_free(struct fe_ctrl *c)
356 {
357 if (c->privdata_needs_free)
358 sfree(c->privdata);
359 sfree(c->radiobuttons);
360 sfree(c);
361 }
362
363 static struct fe_ctrl *fe_ctrl_byctrl(struct fe_dlg *d, union control *ctrl)
364 {
365 return find234(d->byctrl, ctrl, fe_ctrl_find_by_ctrl);
366 }
367
368 static void add_box(struct fe_dlg *d, struct controlset *s, id box)
369 {
370 struct fe_box *b = snew(struct fe_box);
371 b->box = box;
372 b->s = s;
373 add234(d->boxes, b);
374 }
375
376 static id find_box(struct fe_dlg *d, struct controlset *s)
377 {
378 struct fe_box *b = find234(d->boxes, s, fe_boxfind);
379 return b ? b->box : NULL;
380 }
381
382 static void add_widget(struct fe_dlg *d, struct fe_ctrl *c, id widget)
383 {
384 struct fe_backwards *b = snew(struct fe_backwards);
385 b->widget = widget;
386 b->c = c;
387 add234(d->bywidget, b);
388 }
389
390 static struct fe_ctrl *find_widget(struct fe_dlg *d, id widget)
391 {
392 struct fe_backwards *b = find234(d->bywidget, widget,
393 fe_backwards_find_by_widget);
394 return b ? b->c : NULL;
395 }
396
397 void *fe_dlg_init(void *data, NSWindow *window, NSObject *target, SEL action)
398 {
399 struct fe_dlg *d;
400
401 d = snew(struct fe_dlg);
402 d->window = window;
403 d->target = target;
404 d->action = action;
405 d->byctrl = newtree234(fe_ctrl_cmp_by_ctrl);
406 d->bywidget = newtree234(fe_backwards_cmp_by_widget);
407 d->boxes = newtree234(fe_boxcmp);
408 d->data = data;
409 d->rec = [[Receiver alloc] initWithStruct:d];
410
411 return d;
412 }
413
414 void fe_dlg_free(void *dv)
415 {
416 struct fe_dlg *d = (struct fe_dlg *)dv;
417 struct fe_ctrl *c;
418 struct fe_box *b;
419
420 while ( (c = delpos234(d->byctrl, 0)) != NULL )
421 fe_ctrl_free(c);
422 freetree234(d->byctrl);
423
424 while ( (c = delpos234(d->bywidget, 0)) != NULL )
425 sfree(c);
426 freetree234(d->bywidget);
427
428 while ( (b = delpos234(d->boxes, 0)) != NULL )
429 sfree(b);
430 freetree234(d->boxes);
431
432 [d->rec release];
433
434 sfree(d);
435 }
436
437 @implementation Receiver
438 - (id)initWithStruct:(struct fe_dlg *)aStruct
439 {
440 self = [super init];
441 d = aStruct;
442 return self;
443 }
444 - (void)buttonPushed:(id)sender
445 {
446 struct fe_ctrl *c = find_widget(d, sender);
447
448 assert(c && c->ctrl->generic.type == CTRL_BUTTON);
449 c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION);
450 }
451 - (void)checkboxChanged:(id)sender
452 {
453 struct fe_ctrl *c = find_widget(d, sender);
454
455 assert(c && c->ctrl->generic.type == CTRL_CHECKBOX);
456 c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
457 }
458 - (void)radioChanged:(id)sender
459 {
460 struct fe_ctrl *c = find_widget(d, sender);
461 int j;
462
463 assert(c && c->radiobuttons);
464 for (j = 0; j < c->nradiobuttons; j++)
465 if (sender != c->radiobuttons[j])
466 [c->radiobuttons[j] setState:NSOffState];
467 c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
468 }
469 - (void)popupMenuSelected:(id)sender
470 {
471 struct fe_ctrl *c = find_widget(d, sender);
472 c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
473 }
474 - (void)controlTextDidChange:(NSNotification *)notification
475 {
476 id widget = [notification object];
477 struct fe_ctrl *c = find_widget(d, widget);
478 assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
479 c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
480 }
481 - (void)controlTextDidEndEditing:(NSNotification *)notification
482 {
483 id widget = [notification object];
484 struct fe_ctrl *c = find_widget(d, widget);
485 assert(c && c->ctrl->generic.type == CTRL_EDITBOX);
486 c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_REFRESH);
487 }
488 - (void)tableViewSelectionDidChange:(NSNotification *)notification
489 {
490 id widget = [notification object];
491 struct fe_ctrl *c = find_widget(d, widget);
492 assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
493 c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_SELCHANGE);
494 }
495 - (BOOL)tableView:(NSTableView *)aTableView
496 shouldEditTableColumn:(NSTableColumn *)aTableColumn
497 row:(int)rowIndex
498 {
499 return NO; /* no editing permitted */
500 }
501 - (void)listDoubleClicked:(id)sender
502 {
503 struct fe_ctrl *c = find_widget(d, sender);
504 assert(c && c->ctrl->generic.type == CTRL_LISTBOX);
505 c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION);
506 }
507 - (void)dragListButton:(id)sender
508 {
509 struct fe_ctrl *c = find_widget(d, sender);
510 int direction, row, nrows;
511 assert(c && c->ctrl->generic.type == CTRL_LISTBOX &&
512 c->ctrl->listbox.draglist);
513
514 if (sender == c->button)
515 direction = -1; /* up */
516 else
517 direction = +1; /* down */
518
519 row = [c->tableview selectedRow];
520 nrows = [c->tableview numberOfRows];
521
522 if (row + direction < 0 || row + direction >= nrows) {
523 NSBeep();
524 return;
525 }
526
527 [[c->tableview dataSource] swap:row with:row+direction];
528 [c->tableview reloadData];
529 [c->tableview selectRow:row+direction byExtendingSelection:NO];
530
531 c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE);
532 }
533 @end
534
535 void create_ctrls(void *dv, NSView *parent, struct controlset *s,
536 int *minw, int *minh)
537 {
538 struct fe_dlg *d = (struct fe_dlg *)dv;
539 int ccw[100]; /* cumulative column widths */
540 int cypos[100];
541 int ncols;
542 int wmin = 0, hmin = 0;
543 int i, j, cw, ch;
544 NSRect rect;
545 NSFont *textviewfont = nil;
546 int boxh = 0, boxw = 0;
547
548 if (!s->boxname && s->boxtitle) {
549 /* This controlset is a panel title. */
550
551 NSTextField *tf;
552
553 tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];
554 [tf setEditable:NO];
555 [tf setSelectable:NO];
556 [tf setBordered:NO];
557 [tf setDrawsBackground:NO];
558 [tf setStringValue:[NSString stringWithCString:s->boxtitle]];
559 [tf sizeToFit];
560 rect = [tf frame];
561 [parent addSubview:tf];
562
563 /*
564 * I'm going to store this NSTextField in the boxes tree,
565 * because I really can't face having a special tree234
566 * mapping controlsets to panel titles.
567 */
568 add_box(d, s, tf);
569
570 *minw = rect.size.width;
571 *minh = rect.size.height;
572
573 return;
574 }
575
576 if (*s->boxname) {
577 /*
578 * Create an NSBox to contain this subset of controls.
579 */
580 NSBox *box;
581 NSRect tmprect;
582
583 box = [[NSBox alloc] initWithFrame:NSMakeRect(0,0,1,1)];
584 if (s->boxtitle)
585 [box setTitle:[NSString stringWithCString:s->boxtitle]];
586 else
587 [box setTitlePosition:NSNoTitle];
588 add_box(d, s, box);
589 tmprect = [box frame];
590 [box setContentViewMargins:NSMakeSize(20,20)];
591 [box setFrameFromContentFrame:NSMakeRect(100,100,100,100)];
592 rect = [box frame];
593 [box setFrame:tmprect];
594 boxh = (int)(rect.size.height - 100);
595 boxw = (int)(rect.size.width - 100);
596 [parent addSubview:box];
597
598 if (s->boxtitle)
599 boxh += [[box titleFont] pointSize];
600
601 /*
602 * All subsequent controls will be placed within this box.
603 */
604 parent = box;
605 }
606
607 ncols = 1;
608 ccw[0] = 0;
609 ccw[1] = 100;
610 cypos[0] = 0;
611
612 /*
613 * Now iterate through the controls themselves, create them,
614 * and add their width and height to the overall width/height
615 * calculation.
616 */
617 for (i = 0; i < s->ncontrols; i++) {
618 union control *ctrl = s->ctrls[i];
619 struct fe_ctrl *c;
620 int colstart = COLUMN_START(ctrl->generic.column);
621 int colspan = COLUMN_SPAN(ctrl->generic.column);
622 int colend = colstart + colspan;
623 int ytop, wthis;
624
625 switch (ctrl->generic.type) {
626 case CTRL_COLUMNS:
627 for (j = 1; j < ncols; j++)
628 if (cypos[0] < cypos[j])
629 cypos[0] = cypos[j];
630
631 assert(ctrl->columns.ncols < lenof(ccw));
632
633 ccw[0] = 0;
634 for (j = 0; j < ctrl->columns.ncols; j++) {
635 ccw[j+1] = ccw[j] + (ctrl->columns.percentages ?
636 ctrl->columns.percentages[j] : 100);
637 cypos[j] = cypos[0];
638 }
639
640 ncols = ctrl->columns.ncols;
641
642 continue; /* no actual control created */
643 case CTRL_TABDELAY:
644 /*
645 * I'm currently uncertain that we can implement tab
646 * order in OS X.
647 */
648 continue; /* no actual control created */
649 }
650
651 c = fe_ctrl_new(ctrl);
652 add234(d->byctrl, c);
653
654 cw = ch = 0;
655
656 switch (ctrl->generic.type) {
657 case CTRL_BUTTON:
658 case CTRL_CHECKBOX:
659 {
660 NSButton *b;
661
662 b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];
663 [b setBezelStyle:NSRoundedBezelStyle];
664 if (ctrl->generic.type == CTRL_CHECKBOX)
665 [b setButtonType:NSSwitchButton];
666 [b setTitle:[NSString stringWithCString:ctrl->generic.label]];
667 if (ctrl->button.isdefault)
668 [b setKeyEquivalent:@"\r"];
669 else if (ctrl->button.iscancel)
670 [b setKeyEquivalent:@"\033"];
671 [b sizeToFit];
672 rect = [b frame];
673
674 [parent addSubview:b];
675
676 [b setTarget:d->rec];
677 if (ctrl->generic.type == CTRL_CHECKBOX)
678 [b setAction:@selector(checkboxChanged:)];
679 else
680 [b setAction:@selector(buttonPushed:)];
681 add_widget(d, c, b);
682
683 c->button = b;
684
685 cw = rect.size.width;
686 ch = rect.size.height;
687 }
688 break;
689 case CTRL_EDITBOX:
690 {
691 int editp = ctrl->editbox.percentwidth;
692 int labelp = editp == 100 ? 100 : 100 - editp;
693 NSTextField *tf;
694 NSComboBox *cb;
695
696 tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];
697 [tf setEditable:NO];
698 [tf setSelectable:NO];
699 [tf setBordered:NO];
700 [tf setDrawsBackground:NO];
701 [tf setStringValue:[NSString
702 stringWithCString:ctrl->generic.label]];
703 [tf sizeToFit];
704 rect = [tf frame];
705 [parent addSubview:tf];
706 c->label = tf;
707
708 cw = rect.size.width * 100 / labelp;
709 ch = rect.size.height;
710
711 if (ctrl->editbox.has_list) {
712 cb = [[NSComboBox alloc]
713 initWithFrame:NSMakeRect(0,0,1,1)];
714 [cb setStringValue:@"x"];
715 [cb sizeToFit];
716 rect = [cb frame];
717 [parent addSubview:cb];
718 c->combobox = cb;
719 } else {
720 if (ctrl->editbox.password)
721 tf = [NSSecureTextField alloc];
722 else
723 tf = [NSTextField alloc];
724
725 tf = [tf initWithFrame:NSMakeRect(0,0,1,1)];
726 [tf setEditable:YES];
727 [tf setSelectable:YES];
728 [tf setBordered:YES];
729 [tf setStringValue:@"x"];
730 [tf sizeToFit];
731 rect = [tf frame];
732 [parent addSubview:tf];
733 c->editbox = tf;
734
735 [tf setDelegate:d->rec];
736 add_widget(d, c, tf);
737 }
738
739 if (editp == 100) {
740 /* the edit box and its label are vertically separated */
741 ch += VSPACING + rect.size.height;
742 } else {
743 /* the edit box and its label are horizontally separated */
744 if (ch < rect.size.height)
745 ch = rect.size.height;
746 }
747
748 if (cw < rect.size.width * 100 / editp)
749 cw = rect.size.width * 100 / editp;
750 }
751 break;
752 case CTRL_TEXT:
753 {
754 NSTextView *tv;
755 int testwid;
756
757 if (!textviewfont) {
758 NSTextField *tf;
759 tf = [[NSTextField alloc] init];
760 textviewfont = [tf font];
761 [tf release];
762 }
763
764 testwid = (ccw[colend] - ccw[colstart]) * 3;
765
766 tv = [[NSTextView alloc]
767 initWithFrame:NSMakeRect(0,0,testwid,1)];
768 [tv setEditable:NO];
769 [tv setSelectable:NO];
770 //[tv setBordered:NO];
771 [tv setDrawsBackground:NO];
772 [tv setFont:textviewfont];
773 [tv setString:
774 [NSString stringWithCString:ctrl->generic.label]];
775 rect = [tv frame];
776 [tv sizeToFit];
777 [parent addSubview:tv];
778 c->textview = tv;
779
780 cw = rect.size.width;
781 ch = rect.size.height;
782 }
783 break;
784 case CTRL_RADIO:
785 {
786 NSTextField *tf;
787 int j;
788
789 if (ctrl->generic.label) {
790 tf = [[NSTextField alloc]
791 initWithFrame:NSMakeRect(0,0,1,1)];
792 [tf setEditable:NO];
793 [tf setSelectable:NO];
794 [tf setBordered:NO];
795 [tf setDrawsBackground:NO];
796 [tf setStringValue:
797 [NSString stringWithCString:ctrl->generic.label]];
798 [tf sizeToFit];
799 rect = [tf frame];
800 [parent addSubview:tf];
801 c->label = tf;
802
803 cw = rect.size.width;
804 ch = rect.size.height;
805 } else {
806 cw = 0;
807 ch = -VSPACING; /* compensate for next advance */
808 }
809
810 c->nradiobuttons = ctrl->radio.nbuttons;
811 c->radiobuttons = snewn(ctrl->radio.nbuttons, NSButton *);
812
813 for (j = 0; j < ctrl->radio.nbuttons; j++) {
814 NSButton *b;
815 int ncols;
816
817 b = [[MyButton alloc] initWithFrame:NSMakeRect(0,0,1,1)];
818 [b setBezelStyle:NSRoundedBezelStyle];
819 [b setButtonType:NSRadioButton];
820 [b setTitle:[NSString
821 stringWithCString:ctrl->radio.buttons[j]]];
822 [b sizeToFit];
823 rect = [b frame];
824 [parent addSubview:b];
825
826 c->radiobuttons[j] = b;
827
828 [b setTarget:d->rec];
829 [b setAction:@selector(radioChanged:)];
830 add_widget(d, c, b);
831
832 /*
833 * Add to the height every time we place a
834 * button in column 0.
835 */
836 if (j % ctrl->radio.ncolumns == 0) {
837 ch += rect.size.height + VSPACING;
838 }
839
840 /*
841 * Add to the width by working out how many
842 * columns this button spans.
843 */
844 if (j == ctrl->radio.nbuttons - 1)
845 ncols = (ctrl->radio.ncolumns -
846 (j % ctrl->radio.ncolumns));
847 else
848 ncols = 1;
849
850 if (cw < rect.size.width * ctrl->radio.ncolumns / ncols)
851 cw = rect.size.width * ctrl->radio.ncolumns / ncols;
852 }
853 }
854 break;
855 case CTRL_FILESELECT:
856 case CTRL_FONTSELECT:
857 {
858 NSTextField *tf;
859 NSButton *b;
860 int kh;
861
862 tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)];
863 [tf setEditable:NO];
864 [tf setSelectable:NO];
865 [tf setBordered:NO];
866 [tf setDrawsBackground:NO];
867 [tf setStringValue:[NSString
868 stringWithCString:ctrl->generic.label]];
869 [tf sizeToFit];
870 rect = [tf frame];
871 [parent addSubview:tf];
872 c->label = tf;
873
874 cw = rect.size.width;
875 ch = rect.size.height;
876
877 tf = [NSTextField alloc];
878 tf = [tf initWithFrame:NSMakeRect(0,0,1,1)];
879 if (ctrl->generic.type == CTRL_FILESELECT) {
880 [tf setEditable:YES];
881 [tf setSelectable:YES];
882 [tf setBordered:YES];
883 } else {
884 [tf setEditable:NO];
885 [tf setSelectable:NO];
886 [tf setBordered:NO];
887 [tf setDrawsBackground:NO];
888 }
889 [tf setStringValue:@"x"];
890 [tf sizeToFit];
891 rect = [tf frame];
892 [parent addSubview:tf];
893 c->editbox = tf;
894
895 kh = rect.size.height;
896 if (cw < rect.size.width * 4 / 3)
897 cw = rect.size.width * 4 / 3;
898
899 b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];
900 [b setBezelStyle:NSRoundedBezelStyle];
901 if (ctrl->generic.type == CTRL_FILESELECT)
902 [b setTitle:@"Browse..."];
903 else
904 [b setTitle:@"Change..."];
905 // [b setKeyEquivalent:somethingorother];
906 // [b setTarget:somethingorother];
907 // [b setAction:somethingorother];
908 [b sizeToFit];
909 rect = [b frame];
910 [parent addSubview:b];
911
912 c->button = b;
913
914 if (kh < rect.size.height)
915 kh = rect.size.height;
916 ch += VSPACING + kh;
917 if (cw < rect.size.width * 4)
918 cw = rect.size.width * 4;
919 }
920 break;
921 case CTRL_LISTBOX:
922 {
923 int listp = ctrl->listbox.percentwidth;
924 int labelp = listp == 100 ? 100 : 100 - listp;
925 NSTextField *tf;
926 NSPopUpButton *pb;
927 NSTableView *tv;
928 NSScrollView *sv;
929
930 if (ctrl->generic.label) {
931 tf = [[NSTextField alloc]
932 initWithFrame:NSMakeRect(0,0,1,1)];
933 [tf setEditable:NO];
934 [tf setSelectable:NO];
935 [tf setBordered:NO];
936 [tf setDrawsBackground:NO];
937 [tf setStringValue:
938 [NSString stringWithCString:ctrl->generic.label]];
939 [tf sizeToFit];
940 rect = [tf frame];
941 [parent addSubview:tf];
942 c->label = tf;
943
944 cw = rect.size.width;
945 ch = rect.size.height;
946 } else {
947 cw = 0;
948 ch = -VSPACING; /* compensate for next advance */
949 }
950
951 if (ctrl->listbox.height == 0) {
952 pb = [[NSPopUpButton alloc]
953 initWithFrame:NSMakeRect(0,0,1,1)];
954 [pb sizeToFit];
955 rect = [pb frame];
956 [parent addSubview:pb];
957 c->popupbutton = pb;
958
959 [pb setTarget:d->rec];
960 [pb setAction:@selector(popupMenuSelected:)];
961 add_widget(d, c, pb);
962 } else {
963 assert(listp == 100);
964 if (ctrl->listbox.draglist) {
965 int bi;
966
967 listp = 75;
968
969 for (bi = 0; bi < 2; bi++) {
970 NSButton *b;
971 b = [[MyButton alloc]
972 initWithFrame:NSMakeRect(0, 0, 1, 1)];
973 [b setBezelStyle:NSRoundedBezelStyle];
974 if (bi == 0)
975 [b setTitle:@"Up"];
976 else
977 [b setTitle:@"Down"];
978 [b sizeToFit];
979 rect = [b frame];
980 [parent addSubview:b];
981
982 if (bi == 0)
983 c->button = b;
984 else
985 c->button2 = b;
986
987 [b setTarget:d->rec];
988 [b setAction:@selector(dragListButton:)];
989 add_widget(d, c, b);
990
991 if (cw < rect.size.width * 4)
992 cw = rect.size.width * 4;
993 }
994 }
995
996 sv = [[NSScrollView alloc] initWithFrame:
997 NSMakeRect(20,20,10,10)];
998 [sv setBorderType:NSLineBorder];
999 tv = [[NSTableView alloc] initWithFrame:[sv frame]];
1000 [[tv headerView] setFrame:NSMakeRect(0,0,0,0)];
1001 [sv setDocumentView:tv];
1002 [parent addSubview:sv];
1003 [sv setHasVerticalScroller:YES];
1004 [sv setAutohidesScrollers:YES];
1005 [tv setAllowsColumnReordering:NO];
1006 [tv setAllowsColumnResizing:NO];
1007 [tv setAllowsMultipleSelection:ctrl->listbox.multisel];
1008 [tv setAllowsEmptySelection:YES];
1009 [tv setAllowsColumnSelection:YES];
1010 [tv setDataSource:[[MyTableSource alloc] init]];
1011 rect = [tv frame];
1012 /*
1013 * For some reason this consistently comes out
1014 * one short. Add one.
1015 */
1016 rect.size.height = (ctrl->listbox.height+1)*[tv rowHeight];
1017 [sv setFrame:rect];
1018 c->tableview = tv;
1019 c->scrollview = sv;
1020
1021 [tv setDelegate:d->rec];
1022 [tv setTarget:d->rec];
1023 [tv setDoubleAction:@selector(listDoubleClicked:)];
1024 add_widget(d, c, tv);
1025 }
1026
1027 if (c->tableview) {
1028 int ncols, *percentages;
1029 int hundred = 100;
1030
1031 if (ctrl->listbox.ncols) {
1032 ncols = ctrl->listbox.ncols;
1033 percentages = ctrl->listbox.percentages;
1034 } else {
1035 ncols = 1;
1036 percentages = &hundred;
1037 }
1038
1039 for (j = 0; j < ncols; j++) {
1040 NSTableColumn *col;
1041
1042 col = [[NSTableColumn alloc] initWithIdentifier:
1043 [NSNumber numberWithInt:j]];
1044 [c->tableview addTableColumn:col];
1045 }
1046 }
1047
1048 if (labelp == 100) {
1049 /* the list and its label are vertically separated */
1050 ch += VSPACING + rect.size.height;
1051 } else {
1052 /* the list and its label are horizontally separated */
1053 if (ch < rect.size.height)
1054 ch = rect.size.height;
1055 }
1056
1057 if (cw < rect.size.width * 100 / listp)
1058 cw = rect.size.width * 100 / listp;
1059 }
1060 break;
1061 }
1062
1063 /*
1064 * Update the width and height data for the control we've
1065 * just created.
1066 */
1067 ytop = 0;
1068
1069 for (j = colstart; j < colend; j++) {
1070 if (ytop < cypos[j])
1071 ytop = cypos[j];
1072 }
1073
1074 for (j = colstart; j < colend; j++)
1075 cypos[j] = ytop + ch + VSPACING;
1076
1077 if (hmin < ytop + ch)
1078 hmin = ytop + ch;
1079
1080 wthis = (cw + HSPACING) * 100 / (ccw[colend] - ccw[colstart]);
1081 wthis -= HSPACING;
1082
1083 if (wmin < wthis)
1084 wmin = wthis;
1085 }
1086
1087 if (*s->boxname) {
1088 /*
1089 * Add a bit to the width and height for the box.
1090 */
1091 wmin += boxw;
1092 hmin += boxh;
1093 }
1094
1095 //printf("For controlset %s/%s, returning w=%d h=%d\n",
1096 // s->pathname, s->boxname, wmin, hmin);
1097 *minw = wmin;
1098 *minh = hmin;
1099 }
1100
1101 int place_ctrls(void *dv, struct controlset *s, int leftx, int topy,
1102 int width)
1103 {
1104 struct fe_dlg *d = (struct fe_dlg *)dv;
1105 int ccw[100]; /* cumulative column widths */
1106 int cypos[100];
1107 int ncols;
1108 int i, j, ret;
1109 int boxh = 0, boxw = 0;
1110
1111 if (!s->boxname && s->boxtitle) {
1112 /* Size and place the panel title. */
1113
1114 NSTextField *tf = find_box(d, s);
1115 NSRect rect;
1116
1117 rect = [tf frame];
1118 [tf setFrame:NSMakeRect(leftx, topy-rect.size.height,
1119 width, rect.size.height)];
1120 return rect.size.height;
1121 }
1122
1123 if (*s->boxname) {
1124 NSRect rect, tmprect;
1125 NSBox *box = find_box(d, s);
1126
1127 assert(box != NULL);
1128 tmprect = [box frame];
1129 [box setFrameFromContentFrame:NSMakeRect(100,100,100,100)];
1130 rect = [box frame];
1131 [box setFrame:tmprect];
1132 boxw = rect.size.width - 100;
1133 boxh = rect.size.height - 100;
1134 if (s->boxtitle)
1135 boxh += [[box titleFont] pointSize];
1136 topy -= boxh;
1137 width -= boxw;
1138 }
1139
1140 ncols = 1;
1141 ccw[0] = 0;
1142 ccw[1] = 100;
1143 cypos[0] = topy;
1144 ret = 0;
1145
1146 /*
1147 * Now iterate through the controls themselves, placing them
1148 * appropriately.
1149 */
1150 for (i = 0; i < s->ncontrols; i++) {
1151 union control *ctrl = s->ctrls[i];
1152 struct fe_ctrl *c;
1153 int colstart = COLUMN_START(ctrl->generic.column);
1154 int colspan = COLUMN_SPAN(ctrl->generic.column);
1155 int colend = colstart + colspan;
1156 int xthis, ythis, wthis, ch;
1157 NSRect rect;
1158
1159 switch (ctrl->generic.type) {
1160 case CTRL_COLUMNS:
1161 for (j = 1; j < ncols; j++)
1162 if (cypos[0] > cypos[j])
1163 cypos[0] = cypos[j];
1164
1165 assert(ctrl->columns.ncols < lenof(ccw));
1166
1167 ccw[0] = 0;
1168 for (j = 0; j < ctrl->columns.ncols; j++) {
1169 ccw[j+1] = ccw[j] + (ctrl->columns.percentages ?
1170 ctrl->columns.percentages[j] : 100);
1171 cypos[j] = cypos[0];
1172 }
1173
1174 ncols = ctrl->columns.ncols;
1175
1176 continue; /* no actual control created */
1177 case CTRL_TABDELAY:
1178 continue; /* nothing to do here, move along */
1179 }
1180
1181 c = fe_ctrl_byctrl(d, ctrl);
1182
1183 ch = 0;
1184 ythis = topy;
1185
1186 for (j = colstart; j < colend; j++) {
1187 if (ythis > cypos[j])
1188 ythis = cypos[j];
1189 }
1190
1191 xthis = (width + HSPACING) * ccw[colstart] / 100;
1192 wthis = (width + HSPACING) * ccw[colend] / 100 - HSPACING - xthis;
1193 xthis += leftx;
1194
1195 switch (ctrl->generic.type) {
1196 case CTRL_BUTTON:
1197 case CTRL_CHECKBOX:
1198 rect = [c->button frame];
1199 [c->button setFrame:NSMakeRect(xthis,ythis-rect.size.height,wthis,
1200 rect.size.height)];
1201 ch = rect.size.height;
1202 break;
1203 case CTRL_EDITBOX:
1204 {
1205 int editp = ctrl->editbox.percentwidth;
1206 int labelp = editp == 100 ? 100 : 100 - editp;
1207 int lheight, theight, rheight, ynext, editw;
1208 NSControl *edit = (c->editbox ? c->editbox : c->combobox);
1209
1210 rect = [c->label frame];
1211 lheight = rect.size.height;
1212 rect = [edit frame];
1213 theight = rect.size.height;
1214
1215 if (editp == 100)
1216 rheight = lheight;
1217 else
1218 rheight = (lheight < theight ? theight : lheight);
1219
1220 [c->label setFrame:
1221 NSMakeRect(xthis, ythis-(rheight+lheight)/2,
1222 (wthis + HSPACING) * labelp / 100 - HSPACING,
1223 lheight)];
1224 if (editp == 100) {
1225 ynext = ythis - rheight - VSPACING;
1226 rheight = theight;
1227 } else {
1228 ynext = ythis;
1229 }
1230
1231 editw = (wthis + HSPACING) * editp / 100 - HSPACING;
1232
1233 [edit setFrame:
1234 NSMakeRect(xthis+wthis-editw, ynext-(rheight+theight)/2,
1235 editw, theight)];
1236
1237 ch = (ythis - ynext) + theight;
1238 }
1239 break;
1240 case CTRL_TEXT:
1241 [c->textview setFrame:NSMakeRect(xthis, 0, wthis, 1)];
1242 [c->textview sizeToFit];
1243 rect = [c->textview frame];
1244 [c->textview setFrame:NSMakeRect(xthis, ythis-rect.size.height,
1245 wthis, rect.size.height)];
1246 ch = rect.size.height;
1247 break;
1248 case CTRL_RADIO:
1249 {
1250 int j, ynext;
1251
1252 if (c->label) {
1253 rect = [c->label frame];
1254 [c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height,
1255 wthis,rect.size.height)];
1256 ynext = ythis - rect.size.height - VSPACING;
1257 } else
1258 ynext = ythis;
1259
1260 for (j = 0; j < ctrl->radio.nbuttons; j++) {
1261 int col = j % ctrl->radio.ncolumns;
1262 int ncols;
1263 int lx,rx;
1264
1265 if (j == ctrl->radio.nbuttons - 1)
1266 ncols = ctrl->radio.ncolumns - col;
1267 else
1268 ncols = 1;
1269
1270 lx = (wthis + HSPACING) * col / ctrl->radio.ncolumns;
1271 rx = ((wthis + HSPACING) *
1272 (col+ncols) / ctrl->radio.ncolumns) - HSPACING;
1273
1274 /*
1275 * Set the frame size.
1276 */
1277 rect = [c->radiobuttons[j] frame];
1278 [c->radiobuttons[j] setFrame:
1279 NSMakeRect(lx+xthis, ynext-rect.size.height,
1280 rx-lx, rect.size.height)];
1281
1282 /*
1283 * Advance to next line if we're in the last
1284 * column.
1285 */
1286 if (col + ncols == ctrl->radio.ncolumns)
1287 ynext -= rect.size.height + VSPACING;
1288 }
1289 ch = (ythis - ynext) - VSPACING;
1290 }
1291 break;
1292 case CTRL_FILESELECT:
1293 case CTRL_FONTSELECT:
1294 {
1295 int ynext, eh, bh, th, mx;
1296
1297 rect = [c->label frame];
1298 [c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height,
1299 wthis,rect.size.height)];
1300 ynext = ythis - rect.size.height - VSPACING;
1301
1302 rect = [c->editbox frame];
1303 eh = rect.size.height;
1304 rect = [c->button frame];
1305 bh = rect.size.height;
1306 th = (eh > bh ? eh : bh);
1307
1308 mx = (wthis + HSPACING) * 3 / 4 - HSPACING;
1309
1310 [c->editbox setFrame:
1311 NSMakeRect(xthis, ynext-(th+eh)/2, mx, eh)];
1312 [c->button setFrame:
1313 NSMakeRect(xthis+mx+HSPACING, ynext-(th+bh)/2,
1314 wthis-mx-HSPACING, bh)];
1315
1316 ch = (ythis - ynext) + th + VSPACING;
1317 }
1318 break;
1319 case CTRL_LISTBOX:
1320 {
1321 int listp = ctrl->listbox.percentwidth;
1322 int labelp = listp == 100 ? 100 : 100 - listp;
1323 int lheight, theight, rheight, ynext, listw, xlist;
1324 NSControl *list = (c->scrollview ? (id)c->scrollview :
1325 (id)c->popupbutton);
1326
1327 if (ctrl->listbox.draglist) {
1328 assert(listp == 100);
1329 listp = 75;
1330 }
1331
1332 rect = [list frame];
1333 theight = rect.size.height;
1334
1335 if (c->label) {
1336 rect = [c->label frame];
1337 lheight = rect.size.height;
1338
1339 if (labelp == 100)
1340 rheight = lheight;
1341 else
1342 rheight = (lheight < theight ? theight : lheight);
1343
1344 [c->label setFrame:
1345 NSMakeRect(xthis, ythis-(rheight+lheight)/2,
1346 (wthis + HSPACING) * labelp / 100 - HSPACING,
1347 lheight)];
1348 if (labelp == 100) {
1349 ynext = ythis - rheight - VSPACING;
1350 rheight = theight;
1351 } else {
1352 ynext = ythis;
1353 }
1354 } else {
1355 ynext = ythis;
1356 rheight = theight;
1357 }
1358
1359 listw = (wthis + HSPACING) * listp / 100 - HSPACING;
1360
1361 if (labelp == 100)
1362 xlist = xthis;
1363 else
1364 xlist = xthis+wthis-listw;
1365
1366 [list setFrame: NSMakeRect(xlist, ynext-(rheight+theight)/2,
1367 listw, theight)];
1368
1369 /*
1370 * Size the columns for the table view.
1371 */
1372 if (c->tableview) {
1373 int ncols, *percentages;
1374 int hundred = 100;
1375 int cpercent = 0, cpixels = 0;
1376 NSArray *cols;
1377
1378 if (ctrl->listbox.ncols) {
1379 ncols = ctrl->listbox.ncols;
1380 percentages = ctrl->listbox.percentages;
1381 } else {
1382 ncols = 1;
1383 percentages = &hundred;
1384 }
1385
1386 cols = [c->tableview tableColumns];
1387
1388 for (j = 0; j < ncols; j++) {
1389 NSTableColumn *col = [cols objectAtIndex:j];
1390 int newcpixels;
1391
1392 cpercent += percentages[j];
1393 newcpixels = listw * cpercent / 100;
1394 [col setWidth:newcpixels-cpixels];
1395 cpixels = newcpixels;
1396 }
1397 }
1398
1399 ch = (ythis - ynext) + theight;
1400
1401 if (c->button) {
1402 int b2height, centre;
1403 int bx, bw;
1404
1405 /*
1406 * Place the Up and Down buttons for a drag list.
1407 */
1408 assert(c->button2);
1409
1410 rect = [c->button frame];
1411 b2height = VSPACING + 2 * rect.size.height;
1412
1413 centre = ynext - rheight/2;
1414
1415 bx = (wthis + HSPACING) * 3 / 4;
1416 bw = wthis - bx;
1417 bx += leftx;
1418
1419 [c->button setFrame:
1420 NSMakeRect(bx, centre+b2height/2-rect.size.height,
1421 bw, rect.size.height)];
1422 [c->button2 setFrame:
1423 NSMakeRect(bx, centre-b2height/2,
1424 bw, rect.size.height)];
1425 }
1426 }
1427 break;
1428 }
1429
1430 for (j = colstart; j < colend; j++)
1431 cypos[j] = ythis - ch - VSPACING;
1432 if (ret < topy - (ythis - ch))
1433 ret = topy - (ythis - ch);
1434 }
1435
1436 if (*s->boxname) {
1437 NSBox *box = find_box(d, s);
1438 assert(box != NULL);
1439 [box sizeToFit];
1440
1441 if (s->boxtitle) {
1442 NSRect rect = [box frame];
1443 rect.size.height += [[box titleFont] pointSize];
1444 [box setFrame:rect];
1445 }
1446
1447 ret += boxh;
1448 }
1449
1450 //printf("For controlset %s/%s, returning ret=%d\n",
1451 // s->pathname, s->boxname, ret);
1452 return ret;
1453 }
1454
1455 void select_panel(void *dv, struct controlbox *b, const char *name)
1456 {
1457 struct fe_dlg *d = (struct fe_dlg *)dv;
1458 int i, j, hidden;
1459 struct controlset *s;
1460 union control *ctrl;
1461 struct fe_ctrl *c;
1462 NSBox *box;
1463
1464 for (i = 0; i < b->nctrlsets; i++) {
1465 s = b->ctrlsets[i];
1466
1467 if (*s->pathname) {
1468 hidden = !strcmp(s->pathname, name) ? NO : YES;
1469
1470 if ((box = find_box(d, s)) != NULL) {
1471 [box setHidden:hidden];
1472 } else {
1473 for (j = 0; j < s->ncontrols; j++) {
1474 ctrl = s->ctrls[j];
1475 c = fe_ctrl_byctrl(d, ctrl);
1476
1477 if (!c)
1478 continue;
1479
1480 if (c->label)
1481 [c->label setHidden:hidden];
1482 if (c->button)
1483 [c->button setHidden:hidden];
1484 if (c->button2)
1485 [c->button2 setHidden:hidden];
1486 if (c->editbox)
1487 [c->editbox setHidden:hidden];
1488 if (c->combobox)
1489 [c->combobox setHidden:hidden];
1490 if (c->textview)
1491 [c->textview setHidden:hidden];
1492 if (c->tableview)
1493 [c->tableview setHidden:hidden];
1494 if (c->scrollview)
1495 [c->scrollview setHidden:hidden];
1496 if (c->popupbutton)
1497 [c->popupbutton setHidden:hidden];
1498 if (c->radiobuttons) {
1499 int j;
1500 for (j = 0; j < c->nradiobuttons; j++)
1501 [c->radiobuttons[j] setHidden:hidden];
1502 }
1503 break;
1504 }
1505 }
1506 }
1507 }
1508 }
1509
1510 void dlg_radiobutton_set(union control *ctrl, void *dv, int whichbutton)
1511 {
1512 struct fe_dlg *d = (struct fe_dlg *)dv;
1513 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1514 int j;
1515
1516 assert(c->radiobuttons);
1517 for (j = 0; j < c->nradiobuttons; j++)
1518 [c->radiobuttons[j] setState:
1519 (j == whichbutton ? NSOnState : NSOffState)];
1520 }
1521
1522 int dlg_radiobutton_get(union control *ctrl, void *dv)
1523 {
1524 struct fe_dlg *d = (struct fe_dlg *)dv;
1525 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1526 int j;
1527
1528 assert(c->radiobuttons);
1529 for (j = 0; j < c->nradiobuttons; j++)
1530 if ([c->radiobuttons[j] state] == NSOnState)
1531 return j;
1532
1533 return 0; /* should never reach here */
1534 }
1535
1536 void dlg_checkbox_set(union control *ctrl, void *dv, int checked)
1537 {
1538 struct fe_dlg *d = (struct fe_dlg *)dv;
1539 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1540
1541 assert(c->button);
1542 [c->button setState:(checked ? NSOnState : NSOffState)];
1543 }
1544
1545 int dlg_checkbox_get(union control *ctrl, void *dv)
1546 {
1547 struct fe_dlg *d = (struct fe_dlg *)dv;
1548 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1549
1550 assert(c->button);
1551 return ([c->button state] == NSOnState);
1552 }
1553
1554 void dlg_editbox_set(union control *ctrl, void *dv, char const *text)
1555 {
1556 struct fe_dlg *d = (struct fe_dlg *)dv;
1557 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1558
1559 if (c->editbox) {
1560 [c->editbox setStringValue:[NSString stringWithCString:text]];
1561 } else {
1562 assert(c->combobox);
1563 [c->combobox setStringValue:[NSString stringWithCString:text]];
1564 }
1565 }
1566
1567 void dlg_editbox_get(union control *ctrl, void *dv, char *buffer, int length)
1568 {
1569 struct fe_dlg *d = (struct fe_dlg *)dv;
1570 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1571 NSString *str;
1572
1573 if (c->editbox) {
1574 str = [c->editbox stringValue];
1575 } else {
1576 assert(c->combobox);
1577 str = [c->combobox stringValue];
1578 }
1579 if (!str)
1580 str = @"";
1581
1582 /* The length parameter to this method doesn't include a trailing NUL */
1583 [str getCString:buffer maxLength:length-1];
1584 }
1585
1586 void dlg_listbox_clear(union control *ctrl, void *dv)
1587 {
1588 struct fe_dlg *d = (struct fe_dlg *)dv;
1589 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1590
1591 if (c->tableview) {
1592 [[c->tableview dataSource] clear];
1593 [c->tableview reloadData];
1594 } else {
1595 [c->popupbutton removeAllItems];
1596 }
1597 }
1598
1599 void dlg_listbox_del(union control *ctrl, void *dv, int index)
1600 {
1601 struct fe_dlg *d = (struct fe_dlg *)dv;
1602 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1603
1604 if (c->tableview) {
1605 [[c->tableview dataSource] removestr:index];
1606 [c->tableview reloadData];
1607 } else {
1608 [c->popupbutton removeItemAtIndex:index];
1609 }
1610 }
1611
1612 void dlg_listbox_addwithid(union control *ctrl, void *dv,
1613 char const *text, int id)
1614 {
1615 struct fe_dlg *d = (struct fe_dlg *)dv;
1616 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1617
1618 if (c->tableview) {
1619 [[c->tableview dataSource] add:text withId:id];
1620 [c->tableview reloadData];
1621 } else {
1622 [c->popupbutton addItemWithTitle:[NSString stringWithCString:text]];
1623 [[c->popupbutton lastItem] setTag:id];
1624 }
1625 }
1626
1627 void dlg_listbox_add(union control *ctrl, void *dv, char const *text)
1628 {
1629 dlg_listbox_addwithid(ctrl, dv, text, -1);
1630 }
1631
1632 int dlg_listbox_getid(union control *ctrl, void *dv, int index)
1633 {
1634 struct fe_dlg *d = (struct fe_dlg *)dv;
1635 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1636
1637 if (c->tableview) {
1638 return [[c->tableview dataSource] getid:index];
1639 } else {
1640 return [[c->popupbutton itemAtIndex:index] tag];
1641 }
1642 }
1643
1644 int dlg_listbox_index(union control *ctrl, void *dv)
1645 {
1646 struct fe_dlg *d = (struct fe_dlg *)dv;
1647 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1648
1649 if (c->tableview) {
1650 return [c->tableview selectedRow];
1651 } else {
1652 return [c->popupbutton indexOfSelectedItem];
1653 }
1654 }
1655
1656 int dlg_listbox_issel(union control *ctrl, void *dv, int index)
1657 {
1658 struct fe_dlg *d = (struct fe_dlg *)dv;
1659 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1660
1661 if (c->tableview) {
1662 return [c->tableview isRowSelected:index];
1663 } else {
1664 return [c->popupbutton indexOfSelectedItem] == index;
1665 }
1666 }
1667
1668 void dlg_listbox_select(union control *ctrl, void *dv, int index)
1669 {
1670 struct fe_dlg *d = (struct fe_dlg *)dv;
1671 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1672
1673 if (c->tableview) {
1674 [c->tableview selectRow:index byExtendingSelection:NO];
1675 } else {
1676 [c->popupbutton selectItemAtIndex:index];
1677 }
1678 }
1679
1680 void dlg_text_set(union control *ctrl, void *dv, char const *text)
1681 {
1682 struct fe_dlg *d = (struct fe_dlg *)dv;
1683 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1684
1685 assert(c->textview);
1686 [c->textview setString:[NSString stringWithCString:text]];
1687 }
1688
1689 void dlg_label_change(union control *ctrl, void *dlg, char const *text)
1690 {
1691 /*
1692 * This function is currently only used by the config box to
1693 * switch the labels on the host and port boxes between serial
1694 * and network modes. Since OS X does not (yet?) have a serial
1695 * back end, this function can safely do nothing for the
1696 * moment.
1697 */
1698 }
1699
1700 void dlg_filesel_set(union control *ctrl, void *dv, Filename fn)
1701 {
1702 /* FIXME */
1703 }
1704
1705 void dlg_filesel_get(union control *ctrl, void *dv, Filename *fn)
1706 {
1707 /* FIXME */
1708 }
1709
1710 void dlg_fontsel_set(union control *ctrl, void *dv, FontSpec fn)
1711 {
1712 /* FIXME */
1713 }
1714
1715 void dlg_fontsel_get(union control *ctrl, void *dv, FontSpec *fn)
1716 {
1717 /* FIXME */
1718 }
1719
1720 void dlg_update_start(union control *ctrl, void *dv)
1721 {
1722 /* FIXME */
1723 }
1724
1725 void dlg_update_done(union control *ctrl, void *dv)
1726 {
1727 /* FIXME */
1728 }
1729
1730 void dlg_set_focus(union control *ctrl, void *dv)
1731 {
1732 /* FIXME */
1733 }
1734
1735 union control *dlg_last_focused(union control *ctrl, void *dv)
1736 {
1737 return NULL; /* FIXME */
1738 }
1739
1740 void dlg_beep(void *dv)
1741 {
1742 NSBeep();
1743 }
1744
1745 void dlg_error_msg(void *dv, char *msg)
1746 {
1747 /* FIXME */
1748 }
1749
1750 void dlg_end(void *dv, int value)
1751 {
1752 struct fe_dlg *d = (struct fe_dlg *)dv;
1753 [d->target performSelector:d->action
1754 withObject:[NSNumber numberWithInt:value]];
1755 }
1756
1757 void dlg_coloursel_start(union control *ctrl, void *dv,
1758 int r, int g, int b)
1759 {
1760 /* FIXME */
1761 }
1762
1763 int dlg_coloursel_results(union control *ctrl, void *dv,
1764 int *r, int *g, int *b)
1765 {
1766 return 0; /* FIXME */
1767 }
1768
1769 void dlg_refresh(union control *ctrl, void *dv)
1770 {
1771 struct fe_dlg *d = (struct fe_dlg *)dv;
1772 struct fe_ctrl *c;
1773
1774 if (ctrl) {
1775 if (ctrl->generic.handler != NULL)
1776 ctrl->generic.handler(ctrl, d, d->data, EVENT_REFRESH);
1777 } else {
1778 int i;
1779
1780 for (i = 0; (c = index234(d->byctrl, i)) != NULL; i++) {
1781 assert(c->ctrl != NULL);
1782 if (c->ctrl->generic.handler != NULL)
1783 c->ctrl->generic.handler(c->ctrl, d,
1784 d->data, EVENT_REFRESH);
1785 }
1786 }
1787 }
1788
1789 void *dlg_get_privdata(union control *ctrl, void *dv)
1790 {
1791 struct fe_dlg *d = (struct fe_dlg *)dv;
1792 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1793 return c->privdata;
1794 }
1795
1796 void dlg_set_privdata(union control *ctrl, void *dv, void *ptr)
1797 {
1798 struct fe_dlg *d = (struct fe_dlg *)dv;
1799 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1800 c->privdata = ptr;
1801 c->privdata_needs_free = FALSE;
1802 }
1803
1804 void *dlg_alloc_privdata(union control *ctrl, void *dv, size_t size)
1805 {
1806 struct fe_dlg *d = (struct fe_dlg *)dv;
1807 struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl);
1808 /*
1809 * This is an internal allocation routine, so it's allowed to
1810 * use smalloc directly.
1811 */
1812 c->privdata = smalloc(size);
1813 c->privdata_needs_free = TRUE;
1814 return c->privdata;
1815 }