📄 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 14
void 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -