| 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 | } |