| /* |
| * GraphApp - Cross-Platform Graphics Programming Library. |
| * |
| * File: controls.c -- manipulating scrollbars, buttons, etc. |
| * Platform: Windows Version: 2.40 Date: 1998/04/04 |
| * |
| * Version: 1.00 Changes: Original version by Lachlan Patrick. |
| * Version: 1.60 Changes: Added clear(), draw(), flashcontrol(). |
| * Version: 2.00 Changes: New object class system. |
| * Version: 2.15 Changes: Transparent backgrounds added. |
| * Version: 2.20 Changes: Non-native buttons supported. |
| * Version: 2.35 Changes: New reference count technique. |
| * Version: 2.40 Changes: Support for new controls. |
| */ |
| |
| /* Copyright (C) 1993-1998 Lachlan Patrick |
| |
| This file is part of GraphApp, a cross-platform C graphics library. |
| |
| GraphApp is free software; you can redistribute it and/or modify it |
| under the terms of the GNU Library General Public License. |
| GraphApp is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY. |
| |
| See the file COPYLIB.TXT for details. |
| */ |
| |
| /* Copyright (C) 2004 The R Foundation |
| |
| Changes for R: |
| |
| sort out resize (confused screen and client coords) |
| add printer and metafile handling |
| Remove assumption of current->dest being non-NULL |
| |
| */ |
| |
| #include "internal.h" |
| #include <richedit.h> |
| |
| # define alloca(x) __builtin_alloca((x)) |
| |
| /* |
| * Setting control call-backs. |
| */ |
| void setaction(control obj, actionfn fn) |
| { |
| if (obj) |
| obj->action = fn; |
| } |
| |
| void sethit(control obj, intfn fn) |
| { |
| if (obj) |
| obj->hit = fn; |
| } |
| |
| void setdel(control obj, actionfn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->die = fn; |
| } |
| |
| void setclose(control obj, actionfn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->close = fn; |
| } |
| |
| void setredraw(control obj, drawfn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->redraw = fn; |
| } |
| |
| void setresize(control obj, drawfn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->resize = fn; |
| } |
| |
| void setkeydown(control obj, keyfn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->keydown = fn; |
| } |
| |
| void setkeyaction(control obj, keyfn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->keyaction = fn; |
| } |
| |
| void setmousedown(control obj, mousefn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->mousedown = fn; |
| } |
| |
| void setmouseup(control obj, mousefn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->mouseup = fn; |
| } |
| |
| void setmousemove(control obj, mousefn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->mousemove = fn; |
| } |
| |
| void setmousedrag(control obj, mousefn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->mousedrag = fn; |
| } |
| |
| void setmouserepeat(control obj, mousefn fn) |
| { |
| if (obj) |
| if (obj->call) |
| obj->call->mouserepeat = fn; |
| } |
| |
| void setdrop(control obj, dropfn fn) |
| { |
| if (obj) |
| if (obj->call) { |
| DragAcceptFiles(obj->handle, TRUE); |
| obj->call->drop = fn; |
| } |
| } |
| |
| void setonfocus(control obj, actionfn fn) |
| { |
| if (obj) |
| obj->call->focus = fn; |
| } |
| |
| void setim(control obj, imfn fn) |
| { |
| if (obj) |
| obj->call->im = fn; |
| } |
| |
| /* |
| * Drawing controls and windows. |
| */ |
| void clear(control obj) |
| { |
| drawing prev; |
| rgb old; |
| |
| if (! obj) |
| return; |
| if (! isvisible(obj)) |
| return; |
| if (! isvisible(parentwindow(obj))) |
| return; |
| if (obj->bg == Transparent) |
| return; |
| prev = current->dest; |
| drawto(obj); |
| old = currentrgb(); |
| setrgb(obj->bg); |
| fillrect(getrect(obj)); |
| if (prev) { |
| setrgb(old); |
| drawto(prev); |
| } |
| } |
| |
| void draw(control obj) |
| { |
| drawing prev; |
| drawstate old = NULL; |
| |
| if (! obj) |
| return; |
| if (obj->kind == MenubarObject) { |
| DrawMenuBar(obj->parent->handle); |
| return; |
| } |
| |
| if (! isvisible(obj)) |
| return; |
| if (! isvisible(parentwindow(obj))) |
| return; |
| if ((obj->call == NULL) || (obj->call->redraw == NULL)) |
| return; |
| prev = current->dest; |
| drawto(obj); |
| if (prev) old = copydrawstate(); |
| moveto(pt(0,0)); |
| obj->call->redraw(obj, getrect(obj)); |
| if (prev) { |
| restoredrawstate(old); |
| drawto(prev); |
| } |
| } |
| |
| void redraw(control obj) |
| { |
| clear(obj); |
| draw(obj); |
| } |
| |
| /* void getscreenrect(control obj, rect *r) */ |
| /* { */ |
| /* RECT W; */ |
| /* GetWindowRect(obj->handle, &W); */ |
| /* r->x = W.left; */ |
| /* r->y = W.top; */ |
| /* r->width = W.right - W.left; */ |
| /* r->height = W.bottom - W.top; */ |
| /* } */ |
| |
| |
| /* The original here used GetWindowRect (which used screen coordinates) |
| and MoveWindow (which uses client coordinates) so got the positioning |
| hopelessly wrong. This version works for WindowObjects, but I would be |
| suspicious of it for other cases. BDR 2000/04/05 |
| */ |
| void resize(control obj, rect r) |
| { |
| RECT R; |
| WINDOWPLACEMENT W; |
| int dw, dh, dx, dy; |
| |
| if (! obj) |
| return; |
| r = rcanon(r); |
| if (obj->kind == WindowObject) { |
| W.length = sizeof(WINDOWPLACEMENT); |
| r.x = obj->rect.x; |
| r.y = obj->rect.y; |
| if (!equalr(r, obj->rect)) { |
| GetWindowPlacement(obj->handle, &W); |
| if (!isvisible(obj)) W.showCmd = SW_HIDE; /* stops the resize from revealing the window */ |
| dx = r.x - obj->rect.x; |
| dy = r.y - obj->rect.y; |
| /* don't believe current sizes! |
| dw = r.width - obj->rect.width; |
| dh = r.height - obj->rect.height; |
| Rprintf("dw %d dh %d\n", dw, dh); */ |
| GetClientRect(obj->handle, &R); |
| dw = r.width - (R.right - R.left); |
| dh = r.height - (R.bottom - R.top); |
| W.rcNormalPosition.left += dx; |
| W.rcNormalPosition.top += dy; |
| W.rcNormalPosition.right += dx + dw; |
| W.rcNormalPosition.bottom += dy + dh; |
| SetWindowPlacement(obj->handle, &W); |
| } |
| } |
| else { |
| if (! equalr(r, obj->rect)) |
| MoveWindow(obj->handle, r.x, r.y, r.width, r.height, 1); |
| obj->rect.x = r.x; |
| obj->rect.y = r.y; |
| } |
| obj->rect.width = r.width; |
| obj->rect.height = r.height; |
| } |
| |
| /* |
| * Showing and hiding controls and windows. |
| */ |
| void show(control obj) |
| { |
| if (! obj) |
| return; |
| switch (obj->kind) { |
| case CursorObject: case FontObject: case BitmapObject: |
| case MenubarObject: case MenuObject: case MenuitemObject: |
| break; |
| case WindowObject: |
| obj->state |= GA_Visible; |
| show_window(obj); |
| break; |
| default: |
| ShowWindow(obj->handle, SW_SHOWNORMAL); |
| SetFocus(obj->handle); |
| UpdateWindow(obj->handle); |
| } |
| obj->state |= GA_Visible; |
| } |
| |
| void hide(control obj) |
| { |
| if (! obj) |
| return; |
| switch (obj->kind) { |
| case CursorObject: case FontObject: case BitmapObject: |
| case MenubarObject: case MenuObject: case MenuitemObject: |
| break; |
| case WindowObject: |
| hide_window(obj); |
| break; |
| default: |
| ShowWindow(obj->handle, SW_HIDE); |
| } |
| obj->state &= ~GA_Visible; |
| } |
| |
| int isvisible(control obj) |
| { |
| if (! obj) |
| return 0; |
| return (obj->state & GA_Visible) ? 1 : 0; |
| } |
| |
| /* |
| * Enabling and disabling controls and windows. |
| */ |
| void enable(control obj) |
| { |
| if (! obj) |
| return; |
| switch (obj->kind) { |
| case CursorObject: case FontObject: case BitmapObject: |
| case MenubarObject: |
| break; |
| case MenuObject: case MenuitemObject: |
| EnableMenuItem(obj->parent->handle, |
| obj->id, MF_ENABLED | MF_BYCOMMAND); |
| break; |
| case FieldObject: case TextboxObject: |
| sendmessage(obj->handle, EM_SETREADONLY, FALSE, 0L); |
| break; |
| default: |
| if (! isenabled(obj)) { |
| EnableWindow(obj->handle, 1); |
| obj->state |= GA_Enabled; |
| draw(obj); |
| } |
| } |
| obj->state |= GA_Enabled; |
| } |
| |
| void disable(control obj) |
| { |
| if (! obj) |
| return; |
| switch (obj->kind) { |
| case CursorObject: case FontObject: case BitmapObject: |
| case MenubarObject: case MenuObject: |
| break; |
| case MenuitemObject: |
| EnableMenuItem(obj->parent->handle, |
| obj->id, MF_GRAYED | MF_BYCOMMAND); |
| break; |
| case FieldObject: case TextboxObject: |
| sendmessage(obj->handle, EM_SETREADONLY, TRUE, 0L); |
| break; |
| default: |
| if (isenabled(obj)) { |
| EnableWindow(obj->handle, 0); |
| obj->state &= ~GA_Enabled; |
| draw(obj); |
| } |
| } |
| obj->state &= ~GA_Enabled; |
| } |
| |
| int isenabled(control obj) |
| { |
| if (! obj) |
| return 0; |
| return (obj->state & GA_Enabled) ? 1 : 0; |
| } |
| |
| /* |
| * Checking and unchecking controls. |
| */ |
| void check(control obj) |
| { |
| if (! obj) |
| return; |
| switch (obj->kind) { |
| case CursorObject: case FontObject: case BitmapObject: |
| case MenubarObject: case MenuObject: |
| break; |
| case MenuitemObject: |
| CheckMenuItem(obj->parent->handle, obj->id, |
| MF_CHECKED | MF_BYCOMMAND); |
| break; |
| #if USE_NATIVE_BUTTONS |
| case ButtonObject: |
| sendmessage(obj->handle, BM_SETCHECK, 1, 0L); |
| break; |
| #endif |
| #if USE_NATIVE_TOGGLES |
| case CheckboxObject: case RadioObject: |
| sendmessage(obj->handle, BM_SETCHECK, 1, 0L); |
| break; |
| #endif |
| default: |
| if (! ischecked(obj)) { |
| obj->state |= GA_Checked; |
| draw(obj); |
| } |
| } |
| obj->state |= GA_Checked; |
| |
| } |
| |
| void uncheck(control obj) |
| { |
| if (! obj) |
| return; |
| switch (obj->kind) { |
| case CursorObject: case FontObject: case BitmapObject: |
| case MenubarObject: case MenuObject: |
| break; |
| case MenuitemObject: |
| CheckMenuItem(obj->parent->handle, obj->id, |
| MF_UNCHECKED | MF_BYCOMMAND); |
| break; |
| #if USE_NATIVE_BUTTONS |
| case ButtonObject: |
| sendmessage(obj->handle, BM_SETCHECK, 0, 0L); |
| break; |
| #endif |
| #if USE_NATIVE_TOGGLES |
| case CheckboxObject: case RadioObject: |
| sendmessage(obj->handle, BM_SETCHECK, 0, 0L); |
| break; |
| #endif |
| default: |
| if (ischecked(obj)) { |
| obj->state &= ~GA_Checked; |
| draw(obj); |
| } |
| } |
| obj->state &= ~GA_Checked; |
| } |
| |
| int ischecked(control obj) |
| { |
| if (! obj) |
| return 0; |
| return (obj->state & GA_Checked) ? 1 : 0; |
| } |
| |
| /* |
| * Highlighting and unhighlighting controls. |
| */ |
| void highlight(control obj) |
| { |
| if (! obj) |
| return; |
| switch (obj->kind) { |
| case CursorObject: case FontObject: case BitmapObject: |
| case MenubarObject: case MenuObject: case MenuitemObject: |
| break; |
| #if USE_NATIVE_BUTTONS |
| case ButtonObject: |
| sendmessage(obj->handle, BM_SETSTATE, 1, 0L); |
| break; |
| #endif |
| #if USE_NATIVE_TOGGLES |
| case CheckboxObject: case RadioObject: |
| sendmessage(obj->handle, BM_SETSTATE, 1, 0L); |
| break; |
| #endif |
| case FieldObject: case TextboxObject: |
| sendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(0,-1)); |
| break; |
| default: |
| if (! ishighlighted(obj)) { |
| obj->state |= GA_Highlighted; |
| draw(obj); |
| } |
| } |
| obj->state |= GA_Highlighted; |
| } |
| |
| void unhighlight(control obj) |
| { |
| if (! obj) |
| return; |
| switch (obj->kind) { |
| case CursorObject: case FontObject: case BitmapObject: |
| case MenubarObject: case MenuObject: case MenuitemObject: |
| break; |
| #if USE_NATIVE_BUTTONS |
| case ButtonObject: |
| sendmessage(obj->handle, BM_SETSTATE, 0, 0L); |
| break; |
| #endif |
| #if USE_NATIVE_TOGGLES |
| case CheckboxObject: case RadioObject: |
| sendmessage(obj->handle, BM_SETSTATE, 0, 0L); |
| break; |
| #endif |
| case FieldObject: case TextboxObject: |
| sendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(0,0)); |
| break; |
| default: |
| if (ishighlighted(obj)) { |
| obj->state &= ~GA_Highlighted; |
| draw(obj); |
| } |
| } |
| obj->state &= ~GA_Highlighted; |
| } |
| |
| int ishighlighted(control obj) |
| { |
| if (! obj) |
| return 0; |
| return (obj->state & GA_Highlighted) ? 1 : 0; |
| } |
| |
| /* |
| * The flashcontrol function highlights a control as if the user |
| * just used it, e.g. a button will become pressed down for a |
| * moment. Used in conjunction with activatecontrol() below, |
| * it can simulate a mouse-click on a button. |
| */ |
| void flashcontrol(control obj) |
| { |
| highlight(obj); |
| delay(150); |
| unhighlight(obj); |
| } |
| |
| /* |
| * The activatecontrol function is really useful. It causes |
| * a variety of controls to call their 'action functions.' |
| * |
| * There are two types of action functions which a control can use. |
| * The first (obj->action) takes one argument, the control itself, |
| * and is used in buttons, checkboxes and the like as a response |
| * to a user clicking on these controls. The second function, |
| * (obj->hit) function, takes two arguments: the control and its |
| * current 'value'. A scrollbar's value changes as the user scrolls, |
| * and so its hit function is called whenever that happens. |
| * |
| * The activatecontrol function tries to call the action function, |
| * and if that doesn't exist, calls the hit function, passing the |
| * object's value to it. |
| */ |
| void activatecontrol(control obj) |
| { |
| if (! obj) |
| return; |
| drawto(obj); |
| if (obj->action != NULL) |
| obj->action(obj); |
| else if (obj->hit != NULL) |
| obj->hit(obj, obj->value); |
| } |
| |
| /* |
| * Changing and determining the state of an object. |
| * These functions do not automatically redraw the object. |
| */ |
| #if 0 |
| void setstate(control obj, long state) |
| { |
| if (obj) |
| obj->state = state; |
| } |
| |
| long getstate(control obj) |
| { |
| if (obj) |
| return obj->state; |
| else |
| return 0; |
| } |
| #endif |
| |
| void setvalue(control obj, int value) |
| { |
| if (obj) |
| obj->value = value; |
| } |
| |
| int getvalue(control obj) |
| { |
| if (obj) |
| return obj->value; |
| else |
| return 0; |
| } |
| |
| void setforeground(control obj, rgb fg) |
| { |
| if (! obj) |
| return; |
| obj->fg = fg; |
| if (obj->kind == TextboxObject) { |
| if (obj->handle) { |
| CHARFORMAT format; |
| COLORREF wincolour = RGB((fg&gaRed)>>16,(fg&gaGreen)>>8,(fg&gaBlue)); |
| format.cbSize = sizeof(format); |
| format.dwMask = CFM_COLOR; |
| format.dwEffects = 0; |
| format.crTextColor = wincolour; |
| |
| sendmessage(obj->handle, EM_SETCHARFORMAT, 0, (LPARAM)&format); |
| } |
| } else { |
| InvalidateRect(obj->handle, NULL, TRUE); |
| redraw(obj); |
| } |
| } |
| |
| rgb getforeground(control obj) |
| { |
| if (obj) |
| return obj->fg; |
| else |
| return Black; |
| } |
| |
| void setbackground(control obj, rgb bg) |
| { |
| COLORREF wincolour = RGB((bg&gaRed)>>16,(bg&gaGreen)>>8,(bg&gaBlue)); |
| |
| if (! obj) |
| return; |
| obj->bg = bg; |
| if (obj->kind == TextboxObject) { |
| if (obj->handle) |
| sendmessage(obj->handle, EM_SETBKGNDCOLOR, 0, wincolour); |
| } else { |
| if (obj->bgbrush) |
| DeleteObject(obj->bgbrush); |
| obj->bgbrush = CreateSolidBrush(wincolour); |
| |
| InvalidateRect(obj->handle, NULL, TRUE); |
| redraw(obj); |
| } |
| } |
| |
| rgb getbackground(control obj) |
| { |
| if (obj) |
| return obj->bg; |
| else |
| return Transparent; |
| } |
| |
| void setdata(control obj, void *data) |
| { |
| if (obj) |
| obj->data = data; |
| } |
| |
| void *getdata(control obj) |
| { |
| if (obj) |
| return obj->data; |
| else |
| return NULL; |
| } |
| |
| /* These two are in none of the headers */ |
| #ifdef UNUSED |
| void _setextradata(control obj, void *data) |
| { |
| if (obj) |
| obj->extra = data; |
| } |
| |
| void *_getextradata(control obj) |
| { |
| if (obj) |
| return obj->extra; |
| else |
| return NULL; |
| } |
| #endif |
| |
| /* |
| * Set the text of an object. This will set the names appearing |
| * in a window's title bar, a button, checkbox or radio button, |
| * or the value in a textbox or a text field. |
| */ |
| void settext(control obj, const char *text) |
| { |
| char *old_text; |
| |
| if (! obj) |
| return; |
| if (! text) |
| text = ""; |
| old_text = GA_gettext(obj); |
| if (old_text && strcmp(old_text, text) == 0) |
| return; /* no changes to be made */ |
| if (obj->text) { |
| /* discard prior information */ |
| discard(obj->text); |
| obj->text = NULL; |
| } |
| /* Set the new text. */ |
| obj->text = new_string(text); |
| if (text) { |
| if (obj->kind & ControlObject) { |
| text = to_dos_string(text); |
| if(localeCP > 0 && (localeCP != GetACP())) { |
| /* This seems not actually to work */ |
| wchar_t *wc; |
| int nc = strlen(text) + 1; |
| wc = (wchar_t*) alloca(nc*sizeof(wchar_t)); |
| mbstowcs(wc, text, nc); |
| SetWindowTextW(obj->handle, wc); |
| } else SetWindowText(obj->handle, text); |
| discard(text); |
| } |
| if (obj->kind == MenuitemObject) { |
| if(localeCP > 0 && (localeCP != GetACP())) { |
| /* But this does */ |
| wchar_t wc[1000]; |
| mbstowcs(wc, text, 1000); |
| ModifyMenuW(obj->parent->handle, obj->id, |
| MF_BYCOMMAND|MF_STRING, obj->id, wc); |
| |
| } else |
| ModifyMenu(obj->parent->handle, obj->id, |
| MF_BYCOMMAND|MF_STRING, obj->id, text); |
| } |
| |
| } |
| /* Redraw it if it's a redrawable object. */ |
| if (obj->call && obj->call->redraw) |
| redraw(obj); |
| } |
| |
| /* |
| * Get the text string from a window's title bar or from a |
| * control. This may be a button's name, for example, or |
| * the value inside a text field. |
| */ |
| char *GA_gettext(control obj) |
| { |
| static char *empty = ""; |
| char *text; |
| int length, index; |
| HWND hwnd; |
| UINT len_msg, gettext_msg; |
| WPARAM arg1, arg2; |
| |
| if (! obj) |
| return empty; |
| if ((obj->kind & ControlObject) == 0) |
| return obj->text ? obj->text : empty; |
| |
| hwnd = obj->handle; |
| |
| switch (obj->kind) { |
| case ListboxObject: |
| case MultilistObject: |
| len_msg = LB_GETTEXTLEN; |
| gettext_msg = LB_GETTEXT; |
| index = getlistitem(obj); |
| if (index < 0) return empty; |
| arg1 = arg2 = index; |
| break; |
| case DroplistObject: |
| len_msg = CB_GETLBTEXTLEN; |
| gettext_msg = CB_GETLBTEXT; |
| index = getlistitem(obj); |
| if (index < 0) return empty; |
| arg1 = arg2 = index; |
| break; |
| case DropfieldObject: |
| // use default mechanism |
| default: |
| len_msg = WM_GETTEXTLENGTH; |
| gettext_msg = WM_GETTEXT; |
| arg1 = 0; |
| arg2 = sendmessage(hwnd, len_msg, 0, 0L)+1; |
| break; |
| } |
| |
| /* Free any previous information. */ |
| if (obj->text) |
| discard(obj->text); |
| /* Find the length of the string. */ |
| length = sendmessage(obj->handle, len_msg, arg1, 0L); |
| if (length == 0) |
| return (obj->text = new_string(NULL)); |
| /* Copy the text from the object. */ |
| text = array(length+2, char); |
| sendmessage(obj->handle, gettext_msg, arg2, (LPCSTR) text); |
| obj->text = to_c_string(text); |
| discard(text); |
| /* Return the resultant string. */ |
| if (! obj->text) |
| obj->text = new_string(NULL); |
| return obj->text; |
| } |
| |
| /* |
| * Set the font used by a control. |
| */ |
| void settextfont(object obj, font f) |
| { |
| if (! obj) |
| return; |
| if (! f) |
| f = SystemFont; |
| if (obj->drawstate) { |
| decrease_refcount(obj->drawstate->fnt); |
| obj->drawstate->fnt = f; |
| increase_refcount(f); |
| } |
| else { |
| sendmessage(obj->handle, WM_SETFONT, f->handle, 0L); |
| } |
| } |
| |
| /* |
| * Get the font used by a control. |
| */ |
| font gettextfont(object obj) |
| { |
| font f = NULL; |
| if (obj) { |
| if (obj->drawstate) |
| f = obj->drawstate->fnt; |
| } |
| if (! f) |
| f = SystemFont; |
| return f; |
| } |
| |
| /* |
| * Control and window functions. |
| * Parentwindow of a window is itself. |
| */ |
| window parentwindow(control obj) |
| { |
| while (obj) { |
| if (obj->kind == WindowObject) |
| break; |
| obj = obj->parent; |
| } |
| return (window) obj; |
| } |
| |
| /* |
| * Polymorphic functions: |
| */ |
| rect objrect(object obj) |
| { |
| rect r; |
| image img; |
| |
| if (! obj) |
| return rect(0,0,0,0); |
| |
| switch (obj->kind) |
| { |
| case Image8: case Image32: |
| img = (image) obj; |
| r = rect(0,0,img->width,img->height); |
| break; |
| case BitmapObject: |
| case FontObject: |
| case CursorObject: |
| case PrinterObject: |
| r = obj->rect; |
| break; |
| case MetafileObject: |
| r = obj->rect; |
| break; |
| default: |
| GetClientRect(obj->handle, rect2RECT(&r)); |
| break; |
| } |
| return r; |
| } |
| |
| int objwidth(object obj) |
| { |
| rect r = objrect(obj); |
| return r.width; |
| } |
| |
| int objheight(object obj) |
| { |
| rect r = objrect(obj); |
| return r.height; |
| } |
| |
| int objdepth(object obj) |
| { |
| HDC screendc; |
| int depth; |
| |
| if (! obj) |
| return 0; |
| |
| switch (obj->kind) |
| { |
| case Image8: case Image32: |
| depth = ((image)obj)->depth; |
| break; |
| case BitmapObject: |
| case FontObject: case CursorObject: |
| depth = obj->depth; |
| break; |
| default: |
| screendc = GetDC(NULL); |
| depth = GetDeviceCaps(screendc, BITSPIXEL) * |
| GetDeviceCaps(screendc, PLANES); |
| ReleaseDC(NULL, screendc); |
| break; |
| } |
| |
| return depth; |
| } |
| |
| void delobj(object obj) |
| { |
| if (! obj) |
| return; |
| switch (obj->kind) |
| { |
| case Image8: |
| case Image32: |
| delimage((image)obj); |
| break; |
| default: |
| /* if (obj->refcount == 1) why would this test be here?? */ |
| decrease_refcount(obj); |
| break; |
| } |
| } |
| |
| void setcaret(object obj, int x, int y, int width, int height) |
| { |
| if (! obj) |
| return; |
| if (width != obj->caretwidth || height != obj->caretheight) { |
| if (obj->caretwidth > 0 && (obj->state & GA_Focus)) DestroyCaret(); |
| obj->caretwidth = width; |
| obj->caretheight = height; |
| if (width > 0) { |
| if (obj->state & GA_Focus) |
| CreateCaret(obj->handle, (HBITMAP) NULL, width, height); |
| obj->caretshowing = 0; |
| } |
| } |
| if (obj->state & GA_Focus) |
| SetCaretPos(x, y); |
| } |
| |
| void showcaret(object obj, int showing) |
| { |
| if (! obj || showing == obj->caretshowing) |
| return; |
| obj->caretshowing = showing; |
| if (showing) |
| ShowCaret(obj->handle); |
| else |
| HideCaret(obj->handle); |
| } |