📄 winctrls.c
字号:
/* * winctrls.c: routines to self-manage the controls in a dialog * box. *//* * Possible TODO in new cross-platform config box stuff: * * - When lining up two controls alongside each other, I wonder if * we could conveniently arrange to centre them vertically? * Particularly ugly in the current setup is the `Add new * forwarded port:' static next to the rather taller `Remove' * button. */#include <assert.h>#include <ctype.h>#include "putty.h"#include "misc.h"#include "dialog.h"#include <commctrl.h>#define GAPBETWEEN 3#define GAPWITHIN 1#define GAPXBOX 7#define GAPYBOX 4#define DLGWIDTH 168#define STATICHEIGHT 8#define TITLEHEIGHT 12#define CHECKBOXHEIGHT 8#define RADIOHEIGHT 8#define EDITHEIGHT 12#define LISTHEIGHT 11#define LISTINCREMENT 8#define COMBOHEIGHT 12#define PUSHBTNHEIGHT 14#define PROGBARHEIGHT 14void ctlposinit(struct ctlpos *cp, HWND hwnd, int leftborder, int rightborder, int topborder){ RECT r, r2; cp->hwnd = hwnd; cp->font = SendMessage(hwnd, WM_GETFONT, 0, 0); cp->ypos = topborder; GetClientRect(hwnd, &r); r2.left = r2.top = 0; r2.right = 4; r2.bottom = 8; MapDialogRect(hwnd, &r2); cp->dlu4inpix = r2.right; cp->width = (r.right * 4) / (r2.right) - 2 * GAPBETWEEN; cp->xoff = leftborder; cp->width -= leftborder + rightborder;}HWND doctl(struct ctlpos *cp, RECT r, char *wclass, int wstyle, int exstyle, char *wtext, int wid){ HWND ctl; /* * Note nonstandard use of RECT. This is deliberate: by * transforming the width and height directly we arrange to * have all supposedly same-sized controls really same-sized. */ r.left += cp->xoff; MapDialogRect(cp->hwnd, &r); /* * We can pass in cp->hwnd == NULL, to indicate a dry run * without creating any actual controls. */ if (cp->hwnd) { ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle, r.left, r.top, r.right, r.bottom, cp->hwnd, (HMENU) wid, hinst, NULL); SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0)); if (!strcmp(wclass, "LISTBOX")) { /* * Bizarre Windows bug: the list box calculates its * number of lines based on the font it has at creation * time, but sending it WM_SETFONT doesn't cause it to * recalculate. So now, _after_ we've sent it * WM_SETFONT, we explicitly resize it (to the same * size it was already!) to force it to reconsider. */ SetWindowPos(ctl, NULL, 0, 0, r.right, r.bottom, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER); } } else ctl = NULL; return ctl;}/* * A title bar across the top of a sub-dialog. */void bartitle(struct ctlpos *cp, char *name, int id){ RECT r; r.left = GAPBETWEEN; r.right = cp->width; r.top = cp->ypos; r.bottom = STATICHEIGHT; cp->ypos += r.bottom + GAPBETWEEN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, name, id);}/* * Begin a grouping box, with or without a group title. */void beginbox(struct ctlpos *cp, char *name, int idbox){ cp->boxystart = cp->ypos; if (!name) cp->boxystart -= STATICHEIGHT / 2; if (name) cp->ypos += STATICHEIGHT; cp->ypos += GAPYBOX; cp->width -= 2 * GAPXBOX; cp->xoff += GAPXBOX; cp->boxid = idbox; cp->boxtext = name;}/* * End a grouping box. */void endbox(struct ctlpos *cp){ RECT r; cp->xoff -= GAPXBOX; cp->width += 2 * GAPXBOX; cp->ypos += GAPYBOX - GAPBETWEEN; r.left = GAPBETWEEN; r.right = cp->width; r.top = cp->boxystart; r.bottom = cp->ypos - cp->boxystart; doctl(cp, r, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 0, cp->boxtext ? cp->boxtext : "", cp->boxid); cp->ypos += GAPYBOX;}/* * Some edit boxes. Each one has a static above it. The percentages * of the horizontal space are provided. */void multiedit(struct ctlpos *cp, int password, ...){ RECT r; va_list ap; int percent, xpos; percent = xpos = 0; va_start(ap, password); while (1) { char *text; int staticid, editid, pcwidth; text = va_arg(ap, char *); if (!text) break; staticid = va_arg(ap, int); editid = va_arg(ap, int); pcwidth = va_arg(ap, int); r.left = xpos + GAPBETWEEN; percent += pcwidth; xpos = (cp->width + GAPBETWEEN) * percent / 100; r.right = xpos - r.left; r.top = cp->ypos; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid); r.top = cp->ypos + 8 + GAPWITHIN; r.bottom = EDITHEIGHT; doctl(cp, r, "EDIT", WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | (password ? ES_PASSWORD : 0), WS_EX_CLIENTEDGE, "", editid); } va_end(ap); cp->ypos += STATICHEIGHT + GAPWITHIN + EDITHEIGHT + GAPBETWEEN;}/* * A static line, followed by a full-width combo box. */void combobox(struct ctlpos *cp, char *text, int staticid, int listid){ RECT r; r.left = GAPBETWEEN; r.right = cp->width; r.top = cp->ypos; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid); r.top = cp->ypos + 8 + GAPWITHIN; r.bottom = COMBOHEIGHT * 10; doctl(cp, r, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid); cp->ypos += STATICHEIGHT + GAPWITHIN + COMBOHEIGHT + GAPBETWEEN;}struct radio { char *text; int id; };static void radioline_common(struct ctlpos *cp, char *text, int id, int nacross, struct radio *buttons, int nbuttons){ RECT r; int group; int i; int j; if (text) { r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = STATICHEIGHT; cp->ypos += r.bottom + GAPWITHIN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); } group = WS_GROUP; i = 0; for (j = 0; j < nbuttons; j++) { char *btext = buttons[j].text; int bid = buttons[j].id; if (i == nacross) { cp->ypos += r.bottom + (nacross > 1 ? GAPBETWEEN : GAPWITHIN); i = 0; } r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross; if (j < nbuttons-1) r.right = (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left; else r.right = cp->width - r.left; r.top = cp->ypos; r.bottom = RADIOHEIGHT; doctl(cp, r, "BUTTON", BS_NOTIFY | BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | group, 0, btext, bid); group = 0; i++; } cp->ypos += r.bottom + GAPBETWEEN;}/* * A set of radio buttons on the same line, with a static above * them. `nacross' dictates how many parts the line is divided into * (you might want this not to equal the number of buttons if you * needed to line up some 2s and some 3s to look good in the same * panel). * * There's a bit of a hack in here to ensure that if nacross * exceeds the actual number of buttons, the rightmost button * really does get all the space right to the edge of the line, so * you can do things like * * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle */void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...){ va_list ap; struct radio *buttons; int i, nbuttons; va_start(ap, nacross); nbuttons = 0; while (1) { char *btext = va_arg(ap, char *); int bid; if (!btext) break; bid = va_arg(ap, int); nbuttons++; } va_end(ap); buttons = snewn(nbuttons, struct radio); va_start(ap, nacross); for (i = 0; i < nbuttons; i++) { buttons[i].text = va_arg(ap, char *); buttons[i].id = va_arg(ap, int); } va_end(ap); radioline_common(cp, text, id, nacross, buttons, nbuttons); sfree(buttons);}/* * A set of radio buttons on the same line, without a static above * them. Otherwise just like radioline. */void bareradioline(struct ctlpos *cp, int nacross, ...){ va_list ap; struct radio *buttons; int i, nbuttons; va_start(ap, nacross); nbuttons = 0; while (1) { char *btext = va_arg(ap, char *); int bid; if (!btext) break; bid = va_arg(ap, int); } va_end(ap); buttons = snewn(nbuttons, struct radio); va_start(ap, nacross); for (i = 0; i < nbuttons; i++) { buttons[i].text = va_arg(ap, char *); buttons[i].id = va_arg(ap, int); } va_end(ap); radioline_common(cp, NULL, 0, nacross, buttons, nbuttons); sfree(buttons);}/* * A set of radio buttons on multiple lines, with a static above * them. */void radiobig(struct ctlpos *cp, char *text, int id, ...){ va_list ap; struct radio *buttons; int i, nbuttons; va_start(ap, id); nbuttons = 0; while (1) { char *btext = va_arg(ap, char *); int bid; if (!btext) break; bid = va_arg(ap, int); } va_end(ap); buttons = snewn(nbuttons, struct radio); va_start(ap, id); for (i = 0; i < nbuttons; i++) { buttons[i].text = va_arg(ap, char *); buttons[i].id = va_arg(ap, int); } va_end(ap); radioline_common(cp, text, id, 1, buttons, nbuttons); sfree(buttons);}/* * A single standalone checkbox. */void checkbox(struct ctlpos *cp, char *text, int id){ RECT r; r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = CHECKBOXHEIGHT; cp->ypos += r.bottom + GAPBETWEEN; doctl(cp, r, "BUTTON", BS_NOTIFY | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, text, id);}/* * Wrap a piece of text for a static text control. Returns the * wrapped text (a malloc'ed string containing \ns), and also * returns the number of lines required. */char *staticwrap(struct ctlpos *cp, HWND hwnd, char *text, int *lines){ HDC hdc = GetDC(hwnd); int lpx = GetDeviceCaps(hdc, LOGPIXELSX); int width, nlines, j; INT *pwidths, nfit; SIZE size; char *ret, *p, *q; RECT r; HFONT oldfont, newfont; ret = snewn(1+strlen(text), char); p = text; q = ret; pwidths = snewn(1+strlen(text), INT); /* * Work out the width the text will need to fit in, by doing * the same adjustment that the `statictext' function itself * will perform. */ SetMapMode(hdc, MM_TEXT); /* ensure logical units == pixels */ r.left = r.top = r.bottom = 0; r.right = cp->width; MapDialogRect(hwnd, &r); width = r.right; nlines = 1; /* * We must select the correct font into the HDC before calling * GetTextExtent*, or silly things will happen. */ newfont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); oldfont = SelectObject(hdc, newfont); while (*p) { if (!GetTextExtentExPoint(hdc, p, strlen(p), width, &nfit, pwidths, &size) || (size_t)nfit >= strlen(p)) { /* * Either GetTextExtentExPoint returned failure, or the * whole of the rest of the text fits on this line. * Either way, we stop wrapping, copy the remainder of * the input string unchanged to the output, and leave. */ strcpy(q, p); break; } /* * Now we search backwards along the string from `nfit', * looking for a space at which to break the line. If we * don't find one at all, that's fine - we'll just break * the line at `nfit'. */ for (j = nfit; j > 0; j--) { if (isspace((unsigned char)p[j])) { nfit = j; break; } } strncpy(q, p, nfit); q[nfit] = '\n'; q += nfit+1; p += nfit; while (*p && isspace((unsigned char)*p)) p++; nlines++; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -