⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 winctrls.c

📁 远程登陆工具软件源码 用于远程登陆unix
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * 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 + -