Rev 31702 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** 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 PatrickThis file is part of GraphApp, a cross-platform C graphics library.GraphApp is free software; you can redistribute it and/or modify itunder the terms of the GNU Library General Public License.GraphApp is distributed in the hope that it will be useful, butWITHOUT ANY WARRANTY.See the file COPYLIB.TXT for details.*//* Copyright (C) 2004 The R FoundationChanges for R:sort out resize (confused screen and client coords)add printer and metafile handlingRemove assumption of current->dest being non-NULL*/#include "internal.h"/** 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;}/** 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;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 positioninghopelessly wrong. This version works for WindowObjects, but I would besuspicious 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 |= Visible;show_window(obj);break;default:ShowWindow(obj->handle, SW_SHOWNORMAL);SetFocus(obj->handle);UpdateWindow(obj->handle);}obj->state |= 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 &= ~Visible;}int isvisible(control obj){if (! obj)return 0;return (obj->state & 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 |= Enabled;draw(obj);}}obj->state |= 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 &= ~Enabled;draw(obj);}}obj->state &= ~Enabled;}int isenabled(control obj){if (! obj)return 0;return (obj->state & 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_BUTTONScase ButtonObject:sendmessage(obj->handle, BM_SETCHECK, 1, 0L);break;#endif#if USE_NATIVE_TOGGLEScase CheckboxObject: case RadioObject:sendmessage(obj->handle, BM_SETCHECK, 1, 0L);break;#endifdefault:if (! ischecked(obj)) {obj->state |= Checked;draw(obj);}}obj->state |= 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_BUTTONScase ButtonObject:sendmessage(obj->handle, BM_SETCHECK, 0, 0L);break;#endif#if USE_NATIVE_TOGGLEScase CheckboxObject: case RadioObject:sendmessage(obj->handle, BM_SETCHECK, 0, 0L);break;#endifdefault:if (ischecked(obj)) {obj->state &= ~Checked;draw(obj);}}obj->state &= ~Checked;}int ischecked(control obj){if (! obj)return 0;return (obj->state & 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_BUTTONScase ButtonObject:sendmessage(obj->handle, BM_SETSTATE, 1, 0L);break;#endif#if USE_NATIVE_TOGGLEScase CheckboxObject: case RadioObject:sendmessage(obj->handle, BM_SETSTATE, 1, 0L);break;#endifcase FieldObject: case TextboxObject:sendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(0,-1));break;default:if (! ishighlighted(obj)) {obj->state |= Highlighted;draw(obj);}}obj->state |= 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_BUTTONScase ButtonObject:sendmessage(obj->handle, BM_SETSTATE, 0, 0L);break;#endif#if USE_NATIVE_TOGGLEScase CheckboxObject: case RadioObject:sendmessage(obj->handle, BM_SETSTATE, 0, 0L);break;#endifcase FieldObject: case TextboxObject:sendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(0,0));break;default:if (ishighlighted(obj)) {obj->state &= ~Highlighted;draw(obj);}}obj->state &= ~Highlighted;}int ishighlighted(control obj){if (! obj)return 0;return (obj->state & 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 0void setstate(control obj, long state){if (obj)obj->state = state;}long getstate(control obj){if (obj)return obj->state;elsereturn 0;}#endifvoid setvalue(control obj, int value){if (obj)obj->value = value;}int getvalue(control obj){if (obj)return obj->value;elsereturn 0;}void setforeground(control obj, rgb fg){if (! obj)return;obj->fg = fg;InvalidateRect(obj->handle, NULL, TRUE);redraw(obj);}rgb getforeground(control obj){if (obj)return obj->fg;elsereturn Black;}void setbackground(control obj, rgb bg){#if USE_NATIVE_CONTROLSCOLORREF wincolour;wincolour = RGB((bg&gaRed)>>16,(bg&gaGreen)>>8,(bg&gaBlue));if (! obj)return;if (obj->bgbrush)DeleteObject(obj->bgbrush);obj->bgbrush = CreateSolidBrush(wincolour);#endifif (! obj)return;obj->bg = bg;InvalidateRect(obj->handle, NULL, TRUE);redraw(obj);}rgb getbackground(control obj){if (obj)return obj->bg;elsereturn Transparent;}void setdata(control obj, void *data){if (obj)obj->data = data;}void *getdata(control obj){if (obj)return obj->data;elsereturn NULL;}void _setextradata(control obj, void *data){if (obj)obj->extra = data;}void *_getextradata(control obj){if (obj)return obj->extra;elsereturn NULL;}/** 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, char *text){if (! obj)return;if (! text)text = "";if (strcmp(gettext(obj), 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);SetWindowText(obj->handle, text);discard(text);}if (obj->kind==MenuitemObject) {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 *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 mechanismdefault: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, (RECT *) &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;}}