The R Project SVN R

Rev

Rev 83479 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4394 ripley 1
/*
2
 * GraphApp - Cross-Platform Graphics Programming Library.
3
 *
4
 * File: controls.c -- manipulating scrollbars, buttons, etc.
5
 * Platform: Windows  Version: 2.40  Date: 1998/04/04
6
 *
7
 * Version: 1.00  Changes: Original version by Lachlan Patrick.
8
 * Version: 1.60  Changes: Added clear(), draw(), flashcontrol().
9
 * Version: 2.00  Changes: New object class system.
10
 * Version: 2.15  Changes: Transparent backgrounds added.
11
 * Version: 2.20  Changes: Non-native buttons supported.
12
 * Version: 2.35  Changes: New reference count technique.
13
 * Version: 2.40  Changes: Support for new controls.
14
 */
15
 
16
/* Copyright (C) 1993-1998 Lachlan Patrick
17
 
18
   This file is part of GraphApp, a cross-platform C graphics library.
19
 
20
   GraphApp is free software; you can redistribute it and/or modify it
24283 ripley 21
   under the terms of the GNU Library General Public License.
4394 ripley 22
   GraphApp is distributed in the hope that it will be useful, but
23
   WITHOUT ANY WARRANTY.
24
 
25
   See the file COPYLIB.TXT for details.
26
*/
27
 
45017 ripley 28
/*  Copyright (C) 2004	The R Foundation
83479 kalibera 29
    Copyright (C) 2022  The R Core Team
9184 ripley 30
 
28991 murdoch 31
    Changes for R:
32
 
9184 ripley 33
    sort out resize (confused screen and client coords)
34
    add printer and metafile handling
31702 murdoch 35
    Remove assumption of current->dest being non-NULL
83479 kalibera 36
    Improve caret handling (destroy when not in focus, do not hide/show
37
      non-existent caret, prevent against corruption via recursive
83482 kalibera 38
      redraw while not in focus, preserve coordinates when not in focus,
39
      ensure caret exists when a new window first gets focus)
9184 ripley 40
 
41
 */
42
 
4394 ripley 43
#include "internal.h"
46784 murdoch 44
#include <richedit.h>
45
 
39516 ripley 46
# define alloca(x) __builtin_alloca((x))
4394 ripley 47
 
48
/*
49
 *  Setting control call-backs.
50
 */
51
void setaction(control obj, actionfn fn)
52
{
45017 ripley 53
    if (obj)
54
	obj->action = fn;
4394 ripley 55
}
56
 
57
void sethit(control obj, intfn fn)
58
{
45017 ripley 59
    if (obj)
60
	obj->hit = fn;
4394 ripley 61
}
62
 
63
void setdel(control obj, actionfn fn)
64
{
45017 ripley 65
    if (obj)
66
	if (obj->call)
67
	    obj->call->die = fn;
4394 ripley 68
}
69
 
70
void setclose(control obj, actionfn fn)
71
{
45017 ripley 72
    if (obj)
73
	if (obj->call)
74
	    obj->call->close = fn;
4394 ripley 75
}
76
 
77
void setredraw(control obj, drawfn fn)
78
{
45017 ripley 79
    if (obj)
80
	if (obj->call)
81
	    obj->call->redraw = fn;
4394 ripley 82
}
83
 
84
void setresize(control obj, drawfn fn)
85
{
45017 ripley 86
    if (obj)
87
	if (obj->call)
88
	    obj->call->resize = fn;
4394 ripley 89
}
90
 
91
void setkeydown(control obj, keyfn fn)
92
{
45017 ripley 93
    if (obj)
94
	if (obj->call)
95
	    obj->call->keydown = fn;
4394 ripley 96
}
97
 
98
void setkeyaction(control obj, keyfn fn)
99
{
45017 ripley 100
    if (obj)
101
	if (obj->call)
102
	    obj->call->keyaction = fn;
4394 ripley 103
}
104
 
105
void setmousedown(control obj, mousefn fn)
106
{
45017 ripley 107
    if (obj)
108
	if (obj->call)
109
	    obj->call->mousedown = fn;
4394 ripley 110
}
111
 
112
void setmouseup(control obj, mousefn fn)
113
{
45017 ripley 114
    if (obj)
115
	if (obj->call)
116
	    obj->call->mouseup = fn;
4394 ripley 117
}
118
 
119
void setmousemove(control obj, mousefn fn)
120
{
45017 ripley 121
    if (obj)
122
	if (obj->call)
123
	    obj->call->mousemove = fn;
4394 ripley 124
}
125
 
126
void setmousedrag(control obj, mousefn fn)
127
{
45017 ripley 128
    if (obj)
129
	if (obj->call)
130
	    obj->call->mousedrag = fn;
4394 ripley 131
}
132
 
133
void setmouserepeat(control obj, mousefn fn)
134
{
45017 ripley 135
    if (obj)
136
	if (obj->call)
137
	    obj->call->mouserepeat = fn;
4394 ripley 138
}
139
 
14086 ripley 140
void setdrop(control obj, dropfn fn)
141
{
45017 ripley 142
    if (obj)
143
	if (obj->call) {
144
	    DragAcceptFiles(obj->handle, TRUE);
145
	    obj->call->drop = fn;
146
	}
14086 ripley 147
}
148
 
28991 murdoch 149
void setonfocus(control obj, actionfn fn)
150
{
45017 ripley 151
    if (obj)
152
	obj->call->focus = fn;
28991 murdoch 153
}
14086 ripley 154
 
43908 ripley 155
void setim(control obj, imfn fn)
156
{
45017 ripley 157
    if (obj)
158
	obj->call->im = fn;
43908 ripley 159
}
28991 murdoch 160
 
4394 ripley 161
/*
162
 *  Drawing controls and windows.
163
 */
164
void clear(control obj)
165
{
45017 ripley 166
    drawing prev;
167
    rgb old;
4394 ripley 168
 
45017 ripley 169
    if (! obj)
170
	return;
171
    if (! isvisible(obj))
172
	return;
173
    if (! isvisible(parentwindow(obj)))
174
	return;
175
    if (obj->bg == Transparent)
176
	return;
177
    prev = current->dest;
178
    drawto(obj);
179
    old = currentrgb();
180
    setrgb(obj->bg);
181
    fillrect(getrect(obj));
182
    if (prev) {
183
	setrgb(old);
184
	drawto(prev);
185
    }
4394 ripley 186
}
187
 
188
void draw(control obj)
189
{
45017 ripley 190
    drawing prev;
191
    drawstate old = NULL;
4394 ripley 192
 
45017 ripley 193
    if (! obj)
194
	return;
195
    if (obj->kind == MenubarObject) {
196
	DrawMenuBar(obj->parent->handle);
197
	return;
198
    }
4394 ripley 199
 
45017 ripley 200
    if (! isvisible(obj))
201
	return;
202
    if (! isvisible(parentwindow(obj)))
203
	return;
204
    if ((obj->call == NULL) || (obj->call->redraw == NULL))
205
	return;
206
    prev = current->dest;
207
    drawto(obj);
208
    if (prev) old = copydrawstate();
209
    moveto(pt(0,0));
210
    obj->call->redraw(obj, getrect(obj));
211
    if (prev) {
212
	restoredrawstate(old);
213
	drawto(prev);
214
    }
4394 ripley 215
}
216
 
217
void redraw(control obj)
218
{
45017 ripley 219
    clear(obj);
220
    draw(obj);
4394 ripley 221
}
222
 
9236 ripley 223
/*  void getscreenrect(control obj, rect *r) */
224
/*  { */
225
/*      RECT W; */
226
/*      GetWindowRect(obj->handle, &W); */
227
/*      r->x = W.left; */
228
/*      r->y = W.top; */
229
/*      r->width = W.right - W.left; */
230
/*      r->height = W.bottom - W.top; */
231
/*  } */
9044 ripley 232
 
233
 
8706 ripley 234
/* The original here used GetWindowRect (which used screen coordinates)
235
   and MoveWindow (which uses client coordinates) so got the positioning
236
   hopelessly wrong.  This version works for WindowObjects, but I would be
237
   suspicious of it for other cases.  BDR 2000/04/05
238
*/
4394 ripley 239
void resize(control obj, rect r)
240
{
45017 ripley 241
    RECT R;
242
    WINDOWPLACEMENT W;
243
    int dw, dh, dx, dy;
4394 ripley 244
 
45017 ripley 245
    if (! obj)
246
	return;
247
    r = rcanon(r);
248
    if (obj->kind == WindowObject) {
249
	W.length = sizeof(WINDOWPLACEMENT);
250
	r.x = obj->rect.x;
251
	r.y = obj->rect.y;
252
	if (!equalr(r, obj->rect)) {
253
	    GetWindowPlacement(obj->handle, &W);
254
	    if (!isvisible(obj)) W.showCmd = SW_HIDE;  /* stops the resize from revealing the window */
255
	    dx = r.x - obj->rect.x;
256
	    dy = r.y - obj->rect.y;
257
	    /* don't believe current sizes!
258
	       dw = r.width - obj->rect.width;
259
	       dh = r.height - obj->rect.height;
260
	       Rprintf("dw %d dh %d\n", dw, dh); */
261
	    GetClientRect(obj->handle, &R);
262
	    dw = r.width - (R.right - R.left);
263
	    dh = r.height - (R.bottom - R.top);
264
	    W.rcNormalPosition.left += dx;
265
	    W.rcNormalPosition.top += dy;
266
	    W.rcNormalPosition.right += dx + dw;
267
	    W.rcNormalPosition.bottom += dy + dh;
268
	    SetWindowPlacement(obj->handle, &W);
4394 ripley 269
	}
45017 ripley 270
    }
271
    else {
272
	if (! equalr(r, obj->rect))
273
	    MoveWindow(obj->handle, r.x, r.y, r.width, r.height, 1);
274
	obj->rect.x = r.x;
275
	obj->rect.y = r.y;
276
    }
277
    obj->rect.width = r.width;
278
    obj->rect.height = r.height;
4394 ripley 279
}
280
 
281
/*
282
 *  Showing and hiding controls and windows.
283
 */
284
void show(control obj)
285
{
45017 ripley 286
    if (! obj)
287
	return;
288
    switch (obj->kind) {
289
    case CursorObject: case FontObject: case BitmapObject:
290
    case MenubarObject: case MenuObject: case MenuitemObject:
291
	break;
292
    case WindowObject:
76745 kalibera 293
	obj->state |= GA_Visible;
45017 ripley 294
	show_window(obj);
295
	break;
296
    default:
297
	ShowWindow(obj->handle, SW_SHOWNORMAL);
298
	SetFocus(obj->handle);
299
	UpdateWindow(obj->handle);
300
    }
76745 kalibera 301
    obj->state |= GA_Visible;
4394 ripley 302
}
303
 
304
void hide(control obj)
305
{
45017 ripley 306
    if (! obj)
307
	return;
308
    switch (obj->kind) {
309
    case CursorObject: case FontObject: case BitmapObject:
310
    case MenubarObject: case MenuObject: case MenuitemObject:
311
	break;
312
    case WindowObject:
313
	hide_window(obj);
314
	break;
315
    default:
316
	ShowWindow(obj->handle, SW_HIDE);
317
    }
76745 kalibera 318
    obj->state &= ~GA_Visible;
4394 ripley 319
}
320
 
321
int isvisible(control obj)
322
{
45017 ripley 323
    if (! obj)
324
	return 0;
76745 kalibera 325
    return (obj->state & GA_Visible) ? 1 : 0;
4394 ripley 326
}
327
 
328
/*
329
 *  Enabling and disabling controls and windows.
330
 */
331
void enable(control obj)
332
{
45017 ripley 333
    if (! obj)
334
	return;
335
    switch (obj->kind) {
336
    case CursorObject: case FontObject: case BitmapObject:
337
    case MenubarObject:
338
	break;
339
    case MenuObject: case MenuitemObject:
340
	EnableMenuItem(obj->parent->handle,
341
		       obj->id, MF_ENABLED | MF_BYCOMMAND);
342
	break;
343
    case FieldObject: case TextboxObject:
344
	sendmessage(obj->handle, EM_SETREADONLY, FALSE, 0L);
345
	break;
346
    default:
347
	if (! isenabled(obj)) {
348
	    EnableWindow(obj->handle, 1);
76745 kalibera 349
	    obj->state |= GA_Enabled;
45017 ripley 350
	    draw(obj);
4394 ripley 351
	}
45017 ripley 352
    }
76745 kalibera 353
    obj->state |= GA_Enabled;
4394 ripley 354
}
355
 
356
void disable(control obj)
357
{
45017 ripley 358
    if (! obj)
359
	return;
360
    switch (obj->kind) {
361
    case CursorObject: case FontObject: case BitmapObject:
362
    case MenubarObject: case MenuObject:
363
	break;
364
    case MenuitemObject:
365
	EnableMenuItem(obj->parent->handle,
366
		       obj->id, MF_GRAYED | MF_BYCOMMAND);
367
	break;
368
    case FieldObject: case TextboxObject:
369
	sendmessage(obj->handle, EM_SETREADONLY, TRUE, 0L);
370
	break;
371
    default:
372
	if (isenabled(obj)) {
373
	    EnableWindow(obj->handle, 0);
76745 kalibera 374
	    obj->state &= ~GA_Enabled;
45017 ripley 375
	    draw(obj);
4394 ripley 376
	}
45017 ripley 377
    }
76745 kalibera 378
    obj->state &= ~GA_Enabled;
4394 ripley 379
}
380
 
381
int isenabled(control obj)
382
{
45017 ripley 383
    if (! obj)
384
	return 0;
76745 kalibera 385
    return (obj->state & GA_Enabled) ? 1 : 0;
4394 ripley 386
}
387
 
388
/*
389
 *  Checking and unchecking controls.
390
 */
391
void check(control obj)
392
{
45017 ripley 393
    if (! obj)
394
	return;
395
    switch (obj->kind) {
396
    case CursorObject: case FontObject: case BitmapObject:
397
    case MenubarObject: case MenuObject:
398
	break;
399
    case MenuitemObject:
400
	CheckMenuItem(obj->parent->handle, obj->id,
401
		      MF_CHECKED | MF_BYCOMMAND);
402
	break;
4394 ripley 403
#if USE_NATIVE_BUTTONS
45017 ripley 404
    case ButtonObject:
405
	sendmessage(obj->handle, BM_SETCHECK, 1, 0L);
406
	break;
4394 ripley 407
#endif
408
#if USE_NATIVE_TOGGLES
45017 ripley 409
    case CheckboxObject: case RadioObject:
410
	sendmessage(obj->handle, BM_SETCHECK, 1, 0L);
411
	break;
4394 ripley 412
#endif
45017 ripley 413
    default:
414
	if (! ischecked(obj)) {
76745 kalibera 415
	    obj->state |= GA_Checked;
45017 ripley 416
	    draw(obj);
4394 ripley 417
	}
45017 ripley 418
    }
76745 kalibera 419
    obj->state |= GA_Checked;
420
 
4394 ripley 421
}
422
 
423
void uncheck(control obj)
424
{
45017 ripley 425
    if (! obj)
426
	return;
427
    switch (obj->kind) {
428
    case CursorObject: case FontObject: case BitmapObject:
429
    case MenubarObject: case MenuObject:
430
	break;
431
    case MenuitemObject:
432
	CheckMenuItem(obj->parent->handle, obj->id,
433
		      MF_UNCHECKED | MF_BYCOMMAND);
434
	break;
4394 ripley 435
#if USE_NATIVE_BUTTONS
45017 ripley 436
    case ButtonObject:
437
	sendmessage(obj->handle, BM_SETCHECK, 0, 0L);
438
	break;
4394 ripley 439
#endif
440
#if USE_NATIVE_TOGGLES
45017 ripley 441
    case CheckboxObject: case RadioObject:
442
	sendmessage(obj->handle, BM_SETCHECK, 0, 0L);
443
	break;
4394 ripley 444
#endif
45017 ripley 445
    default:
446
	if (ischecked(obj)) {
76745 kalibera 447
	    obj->state &= ~GA_Checked;
45017 ripley 448
	    draw(obj);
4394 ripley 449
	}
45017 ripley 450
    }
76745 kalibera 451
    obj->state &= ~GA_Checked;
4394 ripley 452
}
453
 
454
int ischecked(control obj)
455
{
45017 ripley 456
    if (! obj)
457
	return 0;
76745 kalibera 458
    return (obj->state & GA_Checked) ? 1 : 0;
4394 ripley 459
}
460
 
461
/*
462
 *  Highlighting and unhighlighting controls.
463
 */
464
void highlight(control obj)
465
{
45017 ripley 466
    if (! obj)
467
	return;
468
    switch (obj->kind) {
469
    case CursorObject: case FontObject: case BitmapObject:
470
    case MenubarObject: case MenuObject: case MenuitemObject:
471
	break;
4394 ripley 472
#if USE_NATIVE_BUTTONS
45017 ripley 473
    case ButtonObject:
474
	sendmessage(obj->handle, BM_SETSTATE, 1, 0L);
475
	break;
4394 ripley 476
#endif
477
#if USE_NATIVE_TOGGLES
45017 ripley 478
    case CheckboxObject: case RadioObject:
479
	sendmessage(obj->handle, BM_SETSTATE, 1, 0L);
480
	break;
4394 ripley 481
#endif
45017 ripley 482
    case FieldObject: case TextboxObject:
483
	sendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(0,-1));
484
	break;
485
    default:
486
	if (! ishighlighted(obj)) {
76745 kalibera 487
	    obj->state |= GA_Highlighted;
45017 ripley 488
	    draw(obj);
4394 ripley 489
	}
45017 ripley 490
    }
76745 kalibera 491
    obj->state |= GA_Highlighted;
4394 ripley 492
}
493
 
494
void unhighlight(control obj)
495
{
45017 ripley 496
    if (! obj)
497
	return;
498
    switch (obj->kind) {
499
    case CursorObject: case FontObject: case BitmapObject:
500
    case MenubarObject: case MenuObject: case MenuitemObject:
501
	break;
4394 ripley 502
#if USE_NATIVE_BUTTONS
45017 ripley 503
    case ButtonObject:
504
	sendmessage(obj->handle, BM_SETSTATE, 0, 0L);
505
	break;
4394 ripley 506
#endif
507
#if USE_NATIVE_TOGGLES
45017 ripley 508
    case CheckboxObject: case RadioObject:
509
	sendmessage(obj->handle, BM_SETSTATE, 0, 0L);
510
	break;
4394 ripley 511
#endif
45017 ripley 512
    case FieldObject: case TextboxObject:
513
	sendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(0,0));
514
	break;
515
    default:
516
	if (ishighlighted(obj)) {
76745 kalibera 517
	    obj->state &= ~GA_Highlighted;
45017 ripley 518
	    draw(obj);
4394 ripley 519
	}
45017 ripley 520
    }
76745 kalibera 521
    obj->state &= ~GA_Highlighted;
4394 ripley 522
}
523
 
524
int ishighlighted(control obj)
525
{
45017 ripley 526
    if (! obj)
527
	return 0;
76745 kalibera 528
    return (obj->state & GA_Highlighted) ? 1 : 0;
4394 ripley 529
}
530
 
531
/*
532
 *  The flashcontrol function highlights a control as if the user
533
 *  just used it, e.g. a button will become pressed down for a
534
 *  moment. Used in conjunction with activatecontrol() below,
535
 *  it can simulate a mouse-click on a button.
536
 */
537
void flashcontrol(control obj)
538
{
45017 ripley 539
    highlight(obj);
540
    delay(150);
541
    unhighlight(obj);
4394 ripley 542
}
543
 
544
/*
545
 *  The activatecontrol function is really useful. It causes
546
 *  a variety of controls to call their 'action functions.'
547
 *
548
 *  There are two types of action functions which a control can use.
549
 *  The first (obj->action) takes one argument, the control itself,
550
 *  and is used in buttons, checkboxes and the like as a response
551
 *  to a user clicking on these controls. The second function,
552
 *  (obj->hit) function, takes two arguments: the control and its
553
 *  current 'value'. A scrollbar's value changes as the user scrolls,
554
 *  and so its hit function is called whenever that happens.
555
 *
556
 *  The activatecontrol function tries to call the action function,
557
 *  and if that doesn't exist, calls the hit function, passing the
558
 *  object's value to it.
559
 */
560
void activatecontrol(control obj)
561
{
45017 ripley 562
    if (! obj)
563
	return;
564
    drawto(obj);
565
    if (obj->action != NULL)
566
	obj->action(obj);
567
    else if (obj->hit != NULL)
568
	obj->hit(obj, obj->value);
4394 ripley 569
}
570
 
571
/*
572
 *  Changing and determining the state of an object.
573
 *  These functions do not automatically redraw the object.
574
 */
575
#if 0
576
void setstate(control obj, long state)
577
{
45017 ripley 578
    if (obj)
579
	obj->state = state;
4394 ripley 580
}
581
 
582
long getstate(control obj)
583
{
45017 ripley 584
    if (obj)
585
	return obj->state;
586
    else
587
	return 0;
4394 ripley 588
}
589
#endif
590
 
591
void setvalue(control obj, int value)
592
{
45017 ripley 593
    if (obj)
594
	obj->value = value;
4394 ripley 595
}
596
 
597
int getvalue(control obj)
598
{
45017 ripley 599
    if (obj)
600
	return obj->value;
601
    else
602
	return 0;
4394 ripley 603
}
604
 
605
void setforeground(control obj, rgb fg)
606
{
45017 ripley 607
    if (! obj)
608
	return;
609
    obj->fg = fg;
46784 murdoch 610
    if (obj->kind == TextboxObject) {
611
    	if (obj->handle) {    
612
    	    CHARFORMAT format;
613
    	    COLORREF wincolour = RGB((fg&gaRed)>>16,(fg&gaGreen)>>8,(fg&gaBlue)); 
614
    	    format.cbSize = sizeof(format);
615
    	    format.dwMask = CFM_COLOR;
60023 murdoch 616
    	    format.dwEffects = 0;
46784 murdoch 617
    	    format.crTextColor = wincolour;
618
 
619
	    sendmessage(obj->handle, EM_SETCHARFORMAT, 0, (LPARAM)&format);
620
	}
621
    } else {
622
    	InvalidateRect(obj->handle, NULL, TRUE);
623
    	redraw(obj);
624
    }
4394 ripley 625
}
626
 
627
rgb getforeground(control obj)
628
{
45017 ripley 629
    if (obj)
630
	return obj->fg;
631
    else
632
	return Black;
4394 ripley 633
}
634
 
635
void setbackground(control obj, rgb bg)
636
{
46784 murdoch 637
    COLORREF wincolour = RGB((bg&gaRed)>>16,(bg&gaGreen)>>8,(bg&gaBlue));
4394 ripley 638
 
45017 ripley 639
    if (! obj)
640
	return;
641
    obj->bg = bg;
46784 murdoch 642
    if (obj->kind == TextboxObject) {
643
    	if (obj->handle)
644
	    sendmessage(obj->handle, EM_SETBKGNDCOLOR, 0, wincolour);
645
    } else {
646
    	if (obj->bgbrush)
647
	    DeleteObject(obj->bgbrush);
648
    	obj->bgbrush = CreateSolidBrush(wincolour);
649
 
650
    	InvalidateRect(obj->handle, NULL, TRUE);
651
    	redraw(obj);
652
    }
4394 ripley 653
}
654
 
655
rgb getbackground(control obj)
656
{
45017 ripley 657
    if (obj)
658
	return obj->bg;
659
    else
660
	return Transparent;
4394 ripley 661
}
662
 
663
void setdata(control obj, void *data)
664
{
45017 ripley 665
    if (obj)
666
	obj->data = data;
4394 ripley 667
}
668
 
669
void *getdata(control obj)
670
{
45017 ripley 671
    if (obj)
672
	return obj->data;
673
    else
674
	return NULL;
4394 ripley 675
}
676
 
38705 ripley 677
/* These two are in none of the headers */
678
#ifdef UNUSED
4394 ripley 679
void _setextradata(control obj, void *data)
680
{
45017 ripley 681
    if (obj)
682
	obj->extra = data;
4394 ripley 683
}
684
 
685
void *_getextradata(control obj)
686
{
45017 ripley 687
    if (obj)
688
	return obj->extra;
689
    else
690
	return NULL;
4394 ripley 691
}
38705 ripley 692
#endif
4394 ripley 693
 
694
/*
695
 *  Set the text of an object. This will set the names appearing
696
 *  in a window's title bar, a button, checkbox or radio button,
697
 *  or the value in a textbox or a text field.
698
 */
41793 ripley 699
void settext(control obj, const char *text)
4394 ripley 700
{
45017 ripley 701
    char *old_text;
45014 ripley 702
 
45017 ripley 703
    if (! obj)
704
	return;
705
    if (! text)
706
	text = "";
707
    old_text = GA_gettext(obj);
708
    if (old_text && strcmp(old_text, text) == 0)
709
	return; /* no changes to be made */
710
    if (obj->text) {
711
	/* discard prior information */
712
	discard(obj->text);
713
	obj->text = NULL;
714
    }
715
    /* Set the new text. */
716
    obj->text = new_string(text);
717
    if (text) {
718
	if (obj->kind & ControlObject) {
719
	    text = to_dos_string(text);
720
	    if(localeCP > 0 && (localeCP != GetACP())) {
721
		/* This seems not actually to work */
722
		wchar_t *wc;
723
		int nc = strlen(text) + 1;
724
		wc = (wchar_t*) alloca(nc*sizeof(wchar_t));
725
		mbstowcs(wc, text, nc);
726
		SetWindowTextW(obj->handle, wc);
727
	    } else SetWindowText(obj->handle, text);
728
	    discard(text);
4394 ripley 729
	}
45017 ripley 730
	if (obj->kind == MenuitemObject) {
731
	    if(localeCP > 0 && (localeCP != GetACP())) {
732
		/* But this does */
733
		wchar_t wc[1000];
734
		mbstowcs(wc, text, 1000);
735
		ModifyMenuW(obj->parent->handle, obj->id,
736
			    MF_BYCOMMAND|MF_STRING, obj->id, wc);
4394 ripley 737
 
45017 ripley 738
	    } else
739
		ModifyMenu(obj->parent->handle, obj->id,
740
			   MF_BYCOMMAND|MF_STRING, obj->id, text);
4394 ripley 741
	}
45017 ripley 742
 
743
    }
744
    /* Redraw it if it's a redrawable object. */
745
    if (obj->call && obj->call->redraw)
746
	redraw(obj);
4394 ripley 747
}
748
 
749
/*
750
 *  Get the text string from a window's title bar or from a
751
 *  control. This may be a button's name, for example, or
752
 *  the value inside a text field.
753
 */
39274 ripley 754
char *GA_gettext(control obj)
4394 ripley 755
{
45017 ripley 756
    static char *empty = "";
757
    char *text;
758
    int length, index;
759
    HWND hwnd;
760
    UINT len_msg, gettext_msg;
761
    WPARAM arg1, arg2;
4394 ripley 762
 
45017 ripley 763
    if (! obj)
764
	return empty;
765
    if ((obj->kind & ControlObject) == 0)
766
	return obj->text ? obj->text : empty;
4394 ripley 767
 
45017 ripley 768
    hwnd = obj->handle;
4394 ripley 769
 
45017 ripley 770
    switch (obj->kind) {
771
    case ListboxObject:
772
    case MultilistObject:
773
	len_msg = LB_GETTEXTLEN;
774
	gettext_msg = LB_GETTEXT;
775
	index = getlistitem(obj);
776
	if (index < 0) return empty;
777
	arg1 = arg2 = index;
778
	break;
779
    case DroplistObject:
780
	len_msg = CB_GETLBTEXTLEN;
781
	gettext_msg = CB_GETLBTEXT;
782
	index = getlistitem(obj);
783
	if (index < 0) return empty;
784
	arg1 = arg2 = index;
785
	break;
786
    case DropfieldObject:
787
	// use default mechanism
788
    default:
789
	len_msg = WM_GETTEXTLENGTH;
790
	gettext_msg = WM_GETTEXT;
791
	arg1 = 0;
82774 kalibera 792
	/* FIXME: INT in wine/riched20, unsigned types in other */
793
	arg2 = (LRESULT) sendmessage(hwnd, len_msg, 0, 0L)+1;
45017 ripley 794
	break;
795
    }
4394 ripley 796
 
45017 ripley 797
    /* Free any previous information. */
798
    if (obj->text)
799
	discard(obj->text);
800
    /* Find the length of the string. */
82774 kalibera 801
    /* FIXME: properly cast the result */
802
    length = (LRESULT) sendmessage(obj->handle, len_msg, arg1, 0L);
45017 ripley 803
    if (length == 0)
804
	return (obj->text = new_string(NULL));
805
    /* Copy the text from the object. */
806
    text = array(length+2, char);
807
    sendmessage(obj->handle, gettext_msg, arg2, (LPCSTR) text);
808
    obj->text = to_c_string(text);
809
    discard(text);
810
    /* Return the resultant string. */
811
    if (! obj->text)
812
	obj->text = new_string(NULL);
813
    return obj->text;
4394 ripley 814
}
815
 
816
/*
817
 *  Set the font used by a control.
818
 */
819
void settextfont(object obj, font f)
820
{
45017 ripley 821
    if (! obj)
822
	return;
823
    if (! f)
824
	f = SystemFont;
825
    if (obj->drawstate) {
826
	decrease_refcount(obj->drawstate->fnt);
827
	obj->drawstate->fnt = f;
828
	increase_refcount(f);
829
    }
830
    else {
831
	sendmessage(obj->handle, WM_SETFONT, f->handle, 0L);
832
    }
4394 ripley 833
}
834
 
835
/*
836
 *  Get the font used by a control.
837
 */
838
font gettextfont(object obj)
839
{
45017 ripley 840
    font f = NULL;
841
    if (obj) {
842
	if (obj->drawstate)
843
	    f = obj->drawstate->fnt;
844
    }
845
    if (! f)
846
	f = SystemFont;
847
    return f;
4394 ripley 848
}
849
 
850
/*
851
 *  Control and window functions.
852
 *  Parentwindow of a window is itself.
853
 */
854
window parentwindow(control obj)
855
{
45017 ripley 856
    while (obj) {
857
	if (obj->kind == WindowObject)
858
	    break;
859
	obj = obj->parent;
860
    }
861
    return (window) obj;
4394 ripley 862
}
863
 
864
/*
865
 *  Polymorphic functions:
866
 */
867
rect objrect(object obj)
868
{
45017 ripley 869
    rect r;
870
    image img;
4394 ripley 871
 
45017 ripley 872
    if (! obj)
873
	return rect(0,0,0,0);
4394 ripley 874
 
45017 ripley 875
    switch (obj->kind)
876
    {
877
    case Image8: case Image32:
878
	img = (image) obj;
879
	r = rect(0,0,img->width,img->height);
880
	break;
881
    case BitmapObject:
882
    case FontObject:
883
    case CursorObject:
884
    case PrinterObject:
885
	r = obj->rect;
886
	break;
887
    case MetafileObject:
888
	r = obj->rect;
889
	break;
890
    default:
891
	GetClientRect(obj->handle, rect2RECT(&r));
892
	break;
893
    }
894
    return r;
4394 ripley 895
}
896
 
897
int objwidth(object obj)
898
{
45017 ripley 899
    rect r = objrect(obj);
900
    return r.width;
4394 ripley 901
}
902
 
903
int objheight(object obj)
904
{
45017 ripley 905
    rect r = objrect(obj);
906
    return r.height;
4394 ripley 907
}
908
 
909
int objdepth(object obj)
910
{
45017 ripley 911
    HDC screendc;
912
    int depth;
4394 ripley 913
 
45017 ripley 914
    if (! obj)
915
	return 0;
4394 ripley 916
 
45017 ripley 917
    switch (obj->kind)
918
    {
919
    case Image8: case Image32:
920
	depth = ((image)obj)->depth;
921
	break;
922
    case BitmapObject:
923
    case FontObject: case CursorObject:
924
	depth = obj->depth;
925
	break;
926
    default:
927
	screendc = GetDC(NULL);
928
	depth = GetDeviceCaps(screendc, BITSPIXEL) *
929
	    GetDeviceCaps(screendc, PLANES);
930
	ReleaseDC(NULL, screendc);
931
	break;
932
    }
4394 ripley 933
 
45017 ripley 934
    return depth;
4394 ripley 935
}
936
 
937
void delobj(object obj)
938
{
45017 ripley 939
    if (! obj)
940
	return;
941
    switch (obj->kind)
942
    {
943
    case Image8:
944
    case Image32:
945
	delimage((image)obj);
946
	break;
947
    default:
948
	/* if (obj->refcount == 1)   why would this test be here?? */
949
	decrease_refcount(obj);
950
	break;
951
    }
4394 ripley 952
}
51948 murdoch 953
 
954
void setcaret(object obj, int x, int y, int width, int height)
955
{
956
    if (! obj)
957
    	return;
83479 kalibera 958
    if (width > 0 && !(obj->state & GA_Focus)) {
959
	/* prevent against accidental corruption of caret data while not in focus,
960
	   such as during a screen redraw triggered by disabling the window when
961
	   using the menu (e.g. when accessing GUI preferences from the console) */
962
	return;
963
    }
51948 murdoch 964
    if (width != obj->caretwidth || height != obj->caretheight) {
83479 kalibera 965
	if (obj->caretwidth > 0) {
966
	  if (obj->caretshowing) /* preserve caretshowing */
967
              HideCaret(obj->handle);
968
	  /* we destroy the WinAPI caret also when loosing focus, as suggested */
969
	  /* in Microsoft documentation */
970
	  DestroyCaret();
971
	  obj->caretexists = 0;
972
        }
51948 murdoch 973
	obj->caretwidth = width;
974
	obj->caretheight = height;
975
	if (width > 0) {
83479 kalibera 976
	    if (obj->state & GA_Focus) {
53861 murdoch 977
		CreateCaret(obj->handle, (HBITMAP) NULL, width, height);
83479 kalibera 978
		obj->caretexists = 1;
979
	        if (obj->caretshowing)
980
		    /* match preserved caretshowing */
981
		    ShowCaret(obj->handle);
982
            }
51948 murdoch 983
	}
984
    }
83479 kalibera 985
    if (obj->state & GA_Focus) {
53861 murdoch 986
    	SetCaretPos(x, y);
83479 kalibera 987
	obj->caretx = x;
988
	obj->carety = y;
989
    }
51948 murdoch 990
}
991
 
992
void showcaret(object obj, int showing)
993
{
83479 kalibera 994
    if (! obj || ! obj->caretexists || showing == obj->caretshowing)
51948 murdoch 995
    	return;
996
    obj->caretshowing = showing;
997
    if (showing)
998
    	ShowCaret(obj->handle);
999
    else
1000
    	HideCaret(obj->handle);
1001
}