Rev 81629 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** GraphApp - Cross-Platform Graphics Programming Library.** File: buttons.c -- creating buttons, scrollbars, etc.* Platform: Windows Version: 2.41 Date: 1998/06/06** Version: 1.00 Changes: Original version by Lachlan Patrick.* Version: 1.60 Changes: Minor changes.* Version: 2.00 Changes: New object class system.* Version: 2.10 Changes: Radiogroups fully implemented.* Version: 2.20 Changes: Added non-native buttons.* Version: 2.21 Changes: 32-bit fix by Wim Rijnders.* Version: 2.22 Changes: Added newtextarea function.* Version: 2.23 Changes: Added newpassword function.* Version: 2.30 Changes: Added imagebuttons.* Version: 2.35 Changes: Added newpicture function.* Version: 2.40 Changes: Added new list controls.* Version: 2.41 Changes: Updated imagebuttons.* Version: 2.42 Changes: Fixed drop-down list height.*//* 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-8 The R FoundationChanges for R:set the system font for labelsadd newscrollbar, field_no_borderadd extended selections, pass on de-selectionsCopyright (C) 2022 The R Core Teamuse RichEdit20W to work in UTF-8allow to leave a dropfield (combo box) with TAB key*/#include "internal.h"#include "ga.h"#include <richedit.h>#define SHADOW_WIDTH 1#define isarmed(obj) ((obj)->state & GA_Armed)#define arm(obj) ((obj)->state |= GA_Armed)#define disarm(obj) ((obj)->state &= ~GA_Armed)#define hasfocus(obj) ((obj)->state & GA_Focus)#define setfocus(obj) SetFocus((obj)->handle)/** Ensure there is a current window, create one if there isn't.* If a window is created, it is also shown on the screen.*/static void ensure_window(void){if (! current_window) {current_window = simple_window();show(current_window);}}/** Save the winproc for later and set our own winproc instead.*/static void set_new_winproc(object obj){HWND hwnd;hwnd = obj->handle;#ifdef _WIN64obj->winproc = (WNDPROC) GetWindowLongPtr(hwnd, GWLP_WNDPROC);SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) app_control_proc);#elseobj->winproc = (WNDPROC) GetWindowLong(hwnd, GWL_WNDPROC);SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG) app_control_proc);#endif}/** Private object destructor.*/static void private_delcontrol(control obj){ShowWindow(obj->handle, SW_HIDE);#if USE_NATIVE_CONTROLSif (obj->bgbrush)DeleteObject(obj->bgbrush);#endifDestroyWindow(obj->handle);}/** Creating controls.*/control newcontrol(const char *text, rect r){control obj;ensure_window();obj = newwindow(text, r, ChildWindow | TrackMouse);if (obj) {obj->kind = UserObject;obj->die = private_delcontrol;}set_new_winproc(obj); /* set custom winproc */show(obj);return obj;}drawing newdrawing(rect r, drawfn fn){drawing obj = newcontrol(NULL, r);if (obj) {setredraw(obj, fn);show(obj);draw(obj);}return obj;}static object newchildwin(const char *kind, const char *text,unsigned long style, rect r, actionfn fn){HWND hwnd;object obj;ensure_window();r = rcanon(r);if(localeCP > 0 && (localeCP != GetACP())) {/* This seems not actually to work */wchar_t wkind[100], wc[1000];mbstowcs(wkind, kind, 100);mbstowcs(wc, text, 1000);hwnd = CreateWindowW(wkind, wc,(WS_CHILD | WS_VISIBLE) | style,r.x, r.y, r.width, r.height,current_window->handle,(HMENU) child_id, this_instance, NULL);} elsehwnd = CreateWindow(kind, text,(WS_CHILD | WS_VISIBLE) | style,r.x, r.y, r.width, r.height,current_window->handle,(HMENU) child_id, this_instance, NULL);obj = new_object(ControlObject, hwnd, current_window);if (! obj) {DestroyWindow(hwnd);return NULL;}obj->die = private_delcontrol;obj->rect = r;obj->id = child_id++;obj->action = fn;obj->state = (GA_Visible | GA_Enabled);obj->flags = ChildWindow;obj->text = new_string(text);set_new_winproc(obj); /* set custom winproc */settextfont(obj, SystemFont);return obj;}/** The old native version of newlabel.*/#if USE_NATIVE_LABELSlabel newlabel(const char *text, rect r, int alignment){label obj;unsigned long style = SS_LEFT;if ((alignment & AlignRight) == AlignRight)style = SS_RIGHT;if ((alignment & Center) == Center)style = SS_CENTER;obj = newchildwin("static", text, style, r, NULL);obj->kind = LabelObject;setbackground(obj, getbackground(parentwindow(obj)));return obj;}#else/** Private label redraw function.*/static void draw_label(control c, rect r){/* Draw the label. */if (!isenabled(c))setcolour(Grey);elsesetcolour(Black);setfont(gettextfont(c));drawtext(r, getvalue(c), getname(c));}/** Create a new static text label. Now implemented using* GraphApp code instead of native MS-Windows "static"* text. This gives more flexibility, better cross-platform* support and there are no 'look and feel' issues with* labels, so no problems are introduced by doing this.*/control newlabel(const char *text, rect r, int alignment){control obj = newcontrol(text, r);if (obj) {obj->kind = LabelObject;setredraw(obj, draw_label);setvalue(obj, alignment);setbackground(obj, getbackground(parentwindow(obj)));settextfont(obj, SystemFont);show(obj);}return obj;}#endif/** Uncheck any neighbouring radio buttons, up to any bounding* radiogroup objects, or to the start or end of the list of* siblings.*/static void uncheck_neighbours(object obj){object first = obj->parent->child;object last = first->prev;object which;for (which=obj; ; which=which->prev) {if (which->kind == RadiogroupObject)break;if ((which->kind == RadioObject) && (which != obj))uncheck(which);if (which == first)break;}for (which=obj; ; which=which->next) {if (which->kind == RadiogroupObject)break;if ((which->kind == RadioObject) && (which != obj))uncheck(which);if (which == last)break;}}/** A radiogroup separates radiobuttons from each other.*/object newradiogroup(void){object obj;ensure_window();obj = new_object(RadiogroupObject, NULL, current_window);return obj;}/** Draw a button shadow.*/static void draw_shadow(rect r, rgb col1, rgb col2, int size){rgb hue = current->hue;/* Draw top-left button border. */setcolour(col1);fillrect(rect(r.x,r.y,r.width,size));fillrect(rect(r.x,r.y,size,r.height));/* Draw bottom-right button border. */setcolour(col2);while (size > 0) {fillrect(rect(r.x+r.width-1,r.y,1,r.height));fillrect(rect(r.x,r.y+r.height-1,r.width,1));r = insetr(r,1);size--;}setcolour(hue);}/** Various button call-backs.*/static void button_mousedown(control c, int buttons, point xy){arm(c);highlight(c);}static void button_mousemove(control c, int buttons, point xy){if (!isarmed(c))return;if (buttons && ptinr(xy,getrect(c)))highlight(c);elseunhighlight(c);}static void button_mouseup(control c, int buttons, point xy){if (!isarmed(c))return;disarm(c);unhighlight(c);if (ptinr(xy, getrect(c)))activatecontrol(c);}static void button_keydown(control c, int ch){if (ch == ' ') {flashcontrol(c);activatecontrol(c);}}/** Various checkbox call-backs.*/static void checkbox_mousedown(control c, int buttons, point xy){arm(c);highlight(c);}static void checkbox_mousemove(control c, int buttons, point xy){if (!isarmed(c))return;if (buttons && ptinr(xy,getrect(c)))highlight(c);elseunhighlight(c);}static void checkbox_mouseup(control c, int buttons, point xy){if (! isenabled(c))return;disarm(c);unhighlight(c);if (! ptinr(xy, getrect(c)))return;if (ischecked(c))uncheck(c);elsecheck(c);activatecontrol(c);}static void checkbox_keydown(control c, int ch){if (ch == ' ') {if (ischecked(c))uncheck(c);elsecheck(c);activatecontrol(c);}}/** The imagebutton type:*/void setimage(control obj, image img){/* Change the stored image and draw it. */obj->img = img;draw(obj);}static void draw_image_button(button obj, rect r){image img;bitmap store = NULL;rect ir;rgb up, down;rgb old = currentcolour();img = obj->img;if (has_transparent_pixels(img)) {store = newbitmap(r.width, r.height, 0);drawto(store);setcolour(getbackground(obj));fillrect(r);}if (img) {ir = insetr(r,2);if (ishighlighted(obj)) /* button is pressed */ir.x += 1, ir.y += 1;/* Draw the button image. */if (ischecked(obj))drawdarker(img, ir, getrect(img));else if (isenabled(obj))drawimage(img, ir, getrect(img));elsedrawgreyscale(img, ir, getrect(img));if (ishighlighted(obj)) { /* fill the gap */ir.x -= 1, ir.y -= 1;setcolour(getbackground(obj));drawline(topleft(ir),topright(ir));drawline(topleft(ir),bottomleft(ir));}}/* Draw button border. */setcolour(getforeground(obj));setlinewidth(1);drawrect(r);/* Draw button shadow. */up = White, down = Grey;if (ishighlighted(obj))up = Grey, down = LightGrey;draw_shadow(insetr(r,1), up, down, 1);if (store != NULL) {drawto(obj);copyrect(store, pt(0,0), getrect(store));del(store);}setcolour(old);}button newimagebutton(image img, rect r, actionfn fn){button obj;obj = newcontrol(NULL, r);if (! obj)return NULL;setredraw(obj, draw_image_button);setmousedown(obj, button_mousedown);setmousemove(obj, button_mousemove);setmousedrag(obj, button_mousemove);setmouseup(obj, button_mouseup);setkeydown(obj, button_keydown);setaction(obj, fn);setbackground(obj, LightGrey);settextfont(obj, SystemFont);setimage(obj, img);show(obj);return obj;}static void draw_picture(control obj, rect r){image img;bitmap store = NULL;rgb old = currentcolour();img = obj->img;if (has_transparent_pixels(img)) {store = newbitmap(r.width, r.height, 0);drawto(store);setcolour(getbackground(obj));fillrect(r);}if (img) {/* Draw the button image. */if (ischecked(obj))drawdarker(img, r, getrect(img));else if (isenabled(obj))drawimage(img, r, getrect(img));elsedrawimage(img, r, getrect(img)); /* never grey */}if (store != NULL) {drawto(obj);copyrect(store, pt(0,0), getrect(store));del(store);}setcolour(old);}control newpicture(image img, rect r){control obj;obj = newcontrol(NULL, r);if (! obj)return NULL;setredraw(obj, draw_picture);setimage(obj, img);disable(obj);show(obj);return obj;}button newimagecheckbox(image img, rect r, actionfn fn){button obj;obj = newdrawing(r, draw_image_button);if (! obj)return NULL;setmousedown(obj, checkbox_mousedown);setmousemove(obj, checkbox_mousemove);setmousedrag(obj, checkbox_mousemove);setmouseup(obj, checkbox_mouseup);setkeydown(obj, checkbox_keydown);setaction(obj, fn);setbackground(obj, LightGrey);settextfont(obj, SystemFont);setimage(obj, img);show(obj);return obj;}#if USE_NATIVE_BUTTONSbutton newbutton(const char *text, rect r, actionfn fn){button obj;obj = newchildwin("button", text, BS_PUSHBUTTON, r, fn);if (obj)obj->kind = ButtonObject;return obj;}#elsestatic void draw_button(control c, rect r){rect textrect;rgb up, down;font f;rgb old = currentcolour();clear(c);/* Draw the button name. */if (isenabled(c))setcolour(getforeground(c));elsesetcolour(Grey);f = gettextfont(c);setfont(f);textrect = r;if (ishighlighted(c))textrect.x += 1, textrect.y += 1;drawtext(textrect, Center|VCenter, getname(c));/* Draw button border. */setlinewidth(1);drawrect(r);r = insetr(r,1);/* Draw button shadow. */up = White, down = Grey;if (ishighlighted(c))up = Grey, down = LightGrey;else if (hasfocus(c)) {setcolour(Black);drawrect(r);r = insetr(r,1);}draw_shadow(r, up, down, SHADOW_WIDTH);setcolour(old);}button newbutton(const char *text, rect r, actionfn fn){button obj = newcontrol(text, r);if (obj) {obj->kind = ButtonObject;setredraw(obj, draw_button);setmousedown(obj, button_mousedown);setmousemove(obj, button_mousemove);setmousedrag(obj, button_mousemove);setmouseup(obj, button_mouseup);setkeydown(obj, button_keydown);setaction(obj, fn);setbackground(obj, LightGrey);settextfont(obj, SystemFont);show(obj);}return obj;}#endif#if USE_NATIVE_TOGGLEScheckbox newcheckbox(const char *text, rect r, actionfn fn){checkbox obj = newchildwin("button", text, BS_CHECKBOX, r, fn);if (obj) {obj->kind = CheckboxObject;setbackground(obj, getbackground(parentwindow(obj)));}return obj;}radiobutton newradiobutton(const char *text, rect r, actionfn fn){radiobutton obj = newchildwin("button", text, BS_RADIOBUTTON, r, fn);if (obj) {obj->kind = RadioObject;setbackground(obj, getbackground(parentwindow(obj)));}return obj;}#elsestatic void draw_checkmark(rect r){int len;if (r.width < 8) {r = insetr(r,1);fillrect(r);return;}len = r.width/3;if (len < 3)len = 3;drawline(pt(r.x+1, r.y+r.height-len-2),pt(r.x+len, r.y+r.height-3));drawline(pt(r.x+2, r.y+r.height-len-2),pt(r.x+len, r.y+r.height-4));drawline(pt(r.x+len, r.y+r.height-3),pt(r.x+r.width-1, r.y+len-2));drawline(pt(r.x+len, r.y+r.height-4),pt(r.x+r.width-2, r.y+len-2));}static void draw_checkbox(control c, rect r){int w;rect box, textrect;char *name;int style = (AlignLeft | AlignTop);font f;rgb old = currentcolour();/* Calculate rectangles. */f = gettextfont(c);setfont(f);w = strwidth(f,"W");if (w > r.width) w = r.width;if (w > r.height) w = r.height;box = rect(r.x,r.y+1,w,w);if (w < getheight(f) - getdescent(f))box.y += getheight(f) - getdescent(f) - w;textrect = rect(r.x+w+w/2,r.y,r.width-(w+w/2),r.height);/* Clear check area. */setlinewidth(1);setcolour(White);fillrect(insetr(box,1));/* Draw check area */if (isenabled(c))setcolour(Black);elsesetcolour(Grey);drawrect(box);/* 'Pressed button' effect by black border in box. */if (ishighlighted(c))drawrect(insetr(box,1));/* Put tick in box if checked. */if (ischecked(c))draw_checkmark(insetr(box,1));name = getname(c);if (isenabled(c)) {/* if (hasfocus(c)) {style |= Underline;setlinewidth(2);} */setcolour(getforeground(c));}drawtext(textrect, style, name);setcolour(old);}checkbox newcheckbox(const char *text, rect r, actionfn fn){checkbox obj = newcontrol(text, r);if (obj) {obj->kind = CheckboxObject;setredraw(obj, draw_checkbox);setmousedown(obj, checkbox_mousedown);setmousemove(obj, checkbox_mousemove);setmousedrag(obj, checkbox_mousemove);setmouseup(obj, checkbox_mouseup);setkeydown(obj, checkbox_keydown);setaction(obj, fn);setbackground(obj, getbackground(parentwindow(obj)));settextfont(obj, SystemFont);show(obj);}return obj;}static void draw_radio(control c, rect r){int w;rect box, textrect;char *name;int style = (AlignLeft | AlignTop);font f;rgb old = currentcolour();/* Calculate rectangles. */f = gettextfont(c);setfont(f);w = strwidth(f,"W");if (w > r.width) w = r.width;if (w > r.height) w = r.height;box = rect(r.x,r.y+1,w,w);if (w < getheight(f) - getdescent(f))box.y += getheight(f) - getdescent(f) - w;textrect = rect(r.x+w+w/2,r.y,r.width-(w+w/2),r.height);/* Clear the check area. */setlinewidth(1);setcolour(White);fillellipse(insetr(box,1));/* Draw the check area */if (isenabled(c))setcolour(Black);elsesetcolour(Grey);drawellipse(box);/* Provide 'pressed button' effect by black border. */if (ishighlighted(c)) {setlinewidth(2);drawellipse(box);setlinewidth(1);}/* Put o in circle if checked. */if (ischecked(c))fillellipse(insetr(box,3));name = getname(c);if (isenabled(c)) {/* if (hasfocus(c)) {style |= Underline;setlinewidth(2);} */setcolour(getforeground(c));}drawtext(textrect, style, name);setcolour(old);}static void radio_hit(control c){if (!ischecked(c)) {uncheck_neighbours(c);check(c);activatecontrol(c);}}static void radio_mouseup(control c, int buttons, point xy){if (! isarmed(c))return;disarm(c);unhighlight(c);if (! ptinr(xy, getrect(c)))return;radio_hit(c);}static void radio_keydown(control c, int ch){if (ch == ' ')radio_hit(c);}radiobutton newradiobutton(const char *text, rect r, actionfn fn){radiobutton obj = newcontrol(text, r);if (obj) {obj->kind = RadioObject;setredraw(obj, draw_radio);setmousedown(obj, checkbox_mousedown);setmousemove(obj, checkbox_mousemove);setmousedrag(obj, checkbox_mousemove);setmouseup(obj, radio_mouseup);setkeydown(obj, radio_keydown);setaction(obj, fn);setbackground(obj, getbackground(parentwindow(obj)));settextfont(obj, SystemFont);show(obj);}return obj;}#endifvoid undotext(control obj) /* Why was this previously commented out? CJ */{if (! obj)return;if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))return;sendmessage(obj->handle, EM_UNDO, 0, 0L);}void cuttext(control obj){if (! obj)return;if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))return;sendmessage(obj->handle, WM_CUT, 0, 0L);}void copytext(control obj){if (! obj)return;if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))return;sendmessage(obj->handle, WM_COPY, 0, 0L);}void cleartext(control obj){if (! obj)return;if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))return;sendmessage(obj->handle, WM_CLEAR, 0, 0L);}void pastetext(control obj){if (! obj)return;if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))return;sendmessage(obj->handle, WM_PASTE, 0, 0L);}void inserttext(control obj, const char *text){if (! obj)return;if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))return;text = to_dos_string(text);sendmessage(obj->handle, EM_REPLACESEL, 0, (intptr_t) text);if (text)discard(text);}void selecttext(control obj, long start, long end){int left, right;long length;if (! obj)return;if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))return;length = GetWindowTextLength(obj->handle);left = (start < 0) ? length : start;right = (end < 0) ? length : end;#ifdef WIN32sendmessage(obj->handle, EM_SETSEL, left,right);#elsesendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(left,right));#endif}void textselection(control obj, long *start, long *end){unsigned long sel;if (! obj)return;if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))return;sel = (DWORD) sendmessage(obj->handle, EM_GETSEL, 0, 0);if (start) *start = LOWORD(sel);if (end) *end = HIWORD(sel);}field newfield(const char *text, rect r){field obj = newchildwin("edit", NULL,WS_BORDER | ES_LEFT | ES_AUTOHSCROLL,r, NULL);if (obj) {obj->kind = FieldObject;settext(obj, text);}return obj;}field newfield_no_border(const char *text, rect r){field obj = newchildwin("edit", NULL,ES_LEFT | ES_AUTOHSCROLL,r, NULL);if (obj) {obj->kind = FieldObject;settext(obj, text);}return obj;}field newpassword(const char *text, rect r){field obj = newchildwin("edit", NULL,WS_BORDER | ES_LEFT | ES_AUTOHSCROLL| ES_PASSWORD, r, NULL);if (obj) {obj->kind = FieldObject;settextfont(obj, SystemFont);settext(obj, text);}return obj;}textbox newtextbox(const char *text, rect r){textbox obj = newchildwin("edit", NULL,/* WS_HSCROLL | ES_AUTOHSCROLL | */WS_VSCROLL | ES_AUTOVSCROLL |WS_BORDER | ES_LEFT |ES_MULTILINE,r, NULL);if (obj) {obj->kind = TextboxObject;settext(obj, text);}return obj;}textbox newtextarea(const char *text, rect r){textbox obj = newchildwin("edit", NULL,WS_HSCROLL | ES_AUTOHSCROLL |WS_VSCROLL | ES_AUTOVSCROLL |WS_BORDER | ES_LEFT |ES_MULTILINE,r, NULL);if (obj) {obj->kind = TextboxObject;settext(obj, text);}return obj;}textbox newrichtextarea(const char *text, rect r){textbox obj;if (!LoadLibrary("riched20.dll")) /* RichEdit version 2.0, not included in Win95 */LoadLibrary("riched32.dll"); /* RichEdit version 1.0 *//* RichEdit20A uses the non-UTF-8 ANSI encoding even when Rgui uses UTF-8as ACP via its manifest. Only Unicode strings may be sent to RichEdit20Wwindow via messages, e.g. EM_FINDTEXTTEXW. */obj = newchildwin("RichEdit20W", NULL,WS_HSCROLL | ES_AUTOHSCROLL |WS_VSCROLL | ES_AUTOVSCROLL |ES_LEFT | ES_MULTILINE | ES_NOHIDESEL,r, NULL);if (obj) {sendmessage(obj->handle, EM_SETTEXTMODE, TM_PLAINTEXT, 0);obj->kind = TextboxObject;settext(obj, text);}return obj;}static SCROLLINFO si;scrollbar newscrollbar(rect r, int max, int pagesize, scrollfn fn){scrollbar obj;HWND hwnd;r = rcanon(r);obj = newchildwin("scrollbar", NULL,(r.width > r.height) ? SBS_HORZ : SBS_VERT,r, NULL);if (obj) {obj->kind = ScrollbarObject;obj->hit = fn;obj->value = 0;obj->max = max;obj->size = pagesize;hwnd = obj->handle;si.cbSize = sizeof(si);si.fMask = SIF_ALL;si.nMin = 0;si.nMax = max + pagesize - 1;si.nPage = pagesize;si.nPos = 0;SetScrollInfo(hwnd, SB_CTL, &si, 1);}return obj;}void changescrollbar(scrollbar obj, int where, int max, int pagesize){HWND hwnd;if (! obj)return;hwnd = obj->handle;obj->max = max;obj->size = pagesize;si.cbSize = sizeof(si);si.fMask = SIF_ALL;si.nMin = 0;si.nMax = max + pagesize - 1;si.nPage = pagesize;si.nPos = where;SetScrollInfo(hwnd, SB_CTL, &si, 1);}listbox newlistbox(const char *list[], rect r, scrollfn fn, actionfn dble){listbox obj;obj = newchildwin("listbox", NULL,LBS_NOTIFY | WS_BORDER |WS_VSCROLL | WS_HSCROLL,r, NULL);if (! obj)return obj;obj->kind = ListboxObject;obj->hit = fn;obj->dble = dble;changelistbox(obj, list);return obj;}listbox newmultilist(const char *list[], rect r, scrollfn fn, actionfn dble){listbox obj;obj = newchildwin("listbox", NULL,LBS_NOTIFY |LBS_MULTIPLESEL | LBS_EXTENDEDSEL |WS_BORDER |WS_VSCROLL | WS_HSCROLL,r, NULL);if (! obj)return obj;obj->kind = MultilistObject;obj->hit = fn;obj->dble = dble;changelistbox(obj, list);return obj;}listbox newdroplist(const char *list[], rect r, scrollfn fn){listbox obj;int h, i;initapp(0,0);h = getheight(SystemFont);r.height = h+h;for (i = 0; list && list[i]; i++)r.height += h;obj = newchildwin("combobox", NULL,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL |//CBS_DISABLENOSCROLL |WS_BORDER |WS_VSCROLL | WS_HSCROLL,r, NULL);if (! obj)return obj;obj->kind = DroplistObject;obj->hit = fn;changelistbox(obj, list);return obj;}listbox newdropfield(const char *list[], rect r, scrollfn fn){listbox obj;int h, i;initapp(0,0);h = getheight(SystemFont);r.height = h+h;for (i = 0; list && list[i]; i++)r.height += h;obj = newchildwin("combobox", NULL,CBS_DROPDOWN | CBS_AUTOHSCROLL |// CBS_DISABLENOSCROLL |WS_BORDER |WS_VSCROLL | WS_HSCROLL,r, NULL);if (! obj)return obj;/* subclass the edit control to handle TAB */HANDLE hwndCombo = obj->handle;COMBOBOXINFO cbInfo;cbInfo.cbSize = sizeof(COMBOBOXINFO);BOOL ret = GetComboBoxInfo(hwndCombo, &cbInfo);if (ret) {HANDLE hwndEdit = cbInfo.hwndItem; /* the edit control */if (GetParent(hwndEdit) == hwndCombo) {#ifdef _WIN64obj->edit_winproc = (WNDPROC) GetWindowLongPtr(hwndEdit,GWLP_WNDPROC);SetWindowLongPtr(hwndEdit, GWLP_WNDPROC,(LONG_PTR) edit_control_proc);#elseobj->edit_winproc = (WNDPROC) GetWindowLong(hwndEdit,GWL_WNDPROC);SetWindowLongPtr(hwndEdit, GWL_WNDPROC,(LONG) edit_control_proc);#endif}}obj->kind = DropfieldObject;obj->hit = fn;changelistbox(obj, list);return obj;}void setlistitem(listbox obj, int index){int count;if (! obj)return;if (index < 0)index = -1;switch (obj->kind){case ListboxObject:sendmessage(obj->handle, LB_SETCURSEL, index, 0L);break;case MultilistObject:if (index >= 0)sendmessage(obj->handle, LB_SETSEL, TRUE, MAKELPARAM(index, 0));else {count = (INT) sendmessage(obj->handle, LB_GETCOUNT, 0, 0L);sendmessage(obj->handle, LB_SELITEMRANGE, FALSE, MAKELPARAM(0,count-1));}case DroplistObject:case DropfieldObject:sendmessage(obj->handle, CB_SETCURSEL, index, 0L);break;default:break;}}int isselected(listbox obj, int index){if (! obj)return -1;switch (obj->kind){case ListboxObject:return (index == (INT) sendmessage(obj->handle, LB_GETCURSEL, 0, 0L));case MultilistObject:return (INT) sendmessage(obj->handle, LB_GETSEL, index, 0L);case DroplistObject:case DropfieldObject:return (index == (INT) sendmessage(obj->handle, CB_GETCURSEL, 0, 0L));default:return 0;}}int getlistitem(listbox obj){int index, count;if (! obj)return -1;switch (obj->kind){case ListboxObject:return (INT) sendmessage(obj->handle, LB_GETCURSEL, 0, 0L);case MultilistObject:count = (INT) sendmessage(obj->handle, LB_GETCOUNT, 0, 0L);for (index=0; index < count; index++)if (isselected(obj, index))return index;return -1;case DroplistObject:case DropfieldObject:return (INT) sendmessage(obj->handle, CB_GETCURSEL, 0, 0L);default:return -1;}}void changelistbox(listbox obj, const char **list){int i;HWND hwnd;UINT reset_msg, add_msg;if (! obj)return;hwnd = obj->handle;switch (obj->kind) {case ListboxObject: case MultilistObject:reset_msg = LB_RESETCONTENT;add_msg = LB_ADDSTRING;break;case DroplistObject: case DropfieldObject:reset_msg = CB_RESETCONTENT;add_msg = CB_ADDSTRING;break;default:return;}sendmessage(hwnd, WM_SETREDRAW, FALSE, 0L);sendmessage(hwnd, reset_msg, 0, 0L);for (i=0; list && list[i]; i++)sendmessage(hwnd, add_msg, 0, (LPSTR) list[i]);sendmessage(hwnd, WM_SETREDRAW, TRUE, 0L);if (obj->kind == ListboxObject)sendmessage(hwnd, LB_SETCURSEL, 0, 0L);}/** Activate a control's action function. We do several things here.* Checking to see what kind of control it is, we handle some* events and discard others. We automatically toggle the state of* checkboxes and radio buttons, and handle listbox changes and* allow text box update events to call the control's action.*/PROTECTEDvoid handle_control(HWND hwnd, UINT message){object obj;int index;obj = find_by_handle(hwnd);if ((! obj) || (! (obj->state & GA_Enabled)))return;/* Only let certain events cause activation. */switch (obj->kind){case CheckboxObject:if (obj->state & GA_Checked)uncheck(obj);elsecheck(obj);break;case RadioObject:if (!(obj->state & GA_Checked)) {uncheck_neighbours(obj);check(obj);}break;case ListboxObject:if (message == LBN_DBLCLK) {if(obj->dble) obj->dble(obj);return;}/* Ignore all but selection-change events. */if (message != LBN_SELCHANGE) return;index = (INT) sendmessage(hwnd, LB_GETCURSEL, 0, 0L);obj->value = index;break;case MultilistObject:if (message == LBN_DBLCLK) {if(obj->dble) obj->dble(obj);return;}/* Ignore all but selection-change events. */if (message != LBN_SELCHANGE)return;index = (INT) sendmessage(hwnd, LB_GETCARETINDEX, 0, 0L);/* We do want to see de-selection events tooif (! (INT) sendmessage(hwnd, LB_GETSEL, index, 0L))return;*/obj->value = index;break;case DroplistObject:case DropfieldObject:/* Ignore all but selection-change events. */if (message != CBN_SELCHANGE)return;index = (INT) sendmessage(hwnd, CB_GETCURSEL, 0, 0L);obj->value = index;break;case FieldObject:case TextboxObject:if (message == EN_MAXTEXT) {/* increase the character limit in the editor by 50%if the limit is reached, but this should nothappen */setlimittext(obj, 1.5 * getlimittext(obj));}/* Ignore everything else but killfocus. */else if (message != EN_KILLFOCUS)return;break;default:break;}/* activate the control's callback */activatecontrol(obj);}#include <commctrl.h>/* smooth != 0 gives continuous not segmented bar */progressbar newprogressbar(rect r, int pbmin, int pbmax, int incr, int smooth){HWND hwnd;progressbar obj;int sm;ensure_window();r = rcanon(r);sm = smooth ? PBS_SMOOTH : 0 ;hwnd = CreateWindowEx(0, PROGRESS_CLASS, NULL,(WS_CHILD | WS_VISIBLE | sm),r.x, r.y, r.width, r.height,current_window->handle,(HMENU) child_id, this_instance, NULL);obj = new_object(ControlObject, hwnd, current_window);if (! obj) {DestroyWindow(hwnd);return NULL;}obj->die = private_delcontrol;obj->rect = r;obj->id = child_id++;obj->action = NULL;obj->state = (GA_Visible | GA_Enabled);obj->flags = ChildWindow;set_new_winproc(obj); /* set custom winproc */settextfont(obj, SystemFont);obj->kind = ListboxObject;SendMessage(hwnd, PBM_SETRANGE32, (WPARAM) pbmin, (LPARAM) pbmax);SendMessage(hwnd, PBM_SETSTEP, (WPARAM) incr, 0);return obj;}void setprogressbar(progressbar obj, int n){if (! obj) return;SendMessage(obj->handle, PBM_SETPOS, (WPARAM) n, 0);}void stepprogressbar(progressbar obj, int n){if (! obj) return;SendMessage(obj->handle, PBM_STEPIT, 0, 0);}void setprogressbarrange(progressbar obj, int pbmin, int pbmax){if (! obj) return;SendMessage(obj->handle, PBM_SETRANGE32, (WPARAM) pbmin,(LPARAM) pbmax);}