#include <commctrl.h>
#include "winstuff.h"
+#include "puttymem.h"
+
+#include "putty.h"
#define GAPBETWEEN 3
#define GAPWITHIN 1
cp->width -= leftborder + rightborder;
}
-void doctl(struct ctlpos *cp, RECT r,
+HWND doctl(struct ctlpos *cp, RECT r,
char *wclass, int wstyle, int exstyle, char *wtext, int wid)
{
HWND ctl;
r.left, r.top, r.right, r.bottom,
cp->hwnd, (HMENU) wid, hinst, NULL);
SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(TRUE, 0));
+ return ctl;
}
/*
WS_EX_CLIENTEDGE, "", editid);
}
va_end(ap);
- cp->ypos += 8 + GAPWITHIN + 12 + GAPBETWEEN;
+ cp->ypos += STATICHEIGHT + GAPWITHIN + EDITHEIGHT + 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
+ * A static line, followed by a full-width combo box.
*/
-void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...)
+void combobox(struct ctlpos *cp, char *text, int staticid, int listid)
{
RECT r;
- va_list ap;
- int group;
- int i;
- char *btext;
r.left = GAPBETWEEN;
- r.top = cp->ypos;
r.right = cp->width;
+
+ r.top = cp->ypos;
r.bottom = STATICHEIGHT;
- cp->ypos += r.bottom + GAPWITHIN;
- doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
- va_start(ap, nacross);
+ 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;
+}
+
+static void radioline_common(struct ctlpos *cp, int nacross, va_list ap)
+{
+ RECT r;
+ int group;
+ int i;
+ char *btext;
+
group = WS_GROUP;
i = 0;
btext = va_arg(ap, char *);
i++;
btext = nextbtext;
}
- va_end(ap);
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, ...)
+{
+ RECT r;
+ va_list ap;
+
+ 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);
+ va_start(ap, nacross);
+ radioline_common(cp, nacross, ap);
+ va_end(ap);
+}
+
+/*
+ * 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;
+
+ va_start(ap, nacross);
+ radioline_common(cp, nacross, ap);
+ va_end(ap);
+}
+
+/*
* A set of radio buttons on multiple lines, with a static above
* them.
*/
/*
* A single standalone static text control.
*/
-void statictext(struct ctlpos *cp, char *text, int id)
+void statictext(struct ctlpos *cp, char *text, int lines, int id)
{
RECT r;
r.left = GAPBETWEEN;
r.top = cp->ypos;
r.right = cp->width;
- r.bottom = STATICHEIGHT;
+ r.bottom = STATICHEIGHT * lines;
cp->ypos += r.bottom + GAPBETWEEN;
doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id);
}
}
/*
+ * Like staticbtn, but two buttons.
+ */
+void static2btn(struct ctlpos *cp, char *stext, int sid,
+ char *btext1, int bid1, char *btext2, int bid2)
+{
+ const int height = (PUSHBTNHEIGHT > STATICHEIGHT ?
+ PUSHBTNHEIGHT : STATICHEIGHT);
+ RECT r;
+ int lwid, rwid1, rwid2, rpos1, rpos2;
+
+ rpos1 = GAPBETWEEN + (cp->width + GAPBETWEEN) / 2;
+ rpos2 = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4;
+ lwid = rpos1 - 2 * GAPBETWEEN;
+ rwid1 = rpos2 - rpos1 - GAPBETWEEN;
+ rwid2 = cp->width + GAPBETWEEN - rpos2;
+
+ r.left = GAPBETWEEN;
+ r.top = cp->ypos + (height - STATICHEIGHT) / 2;
+ r.right = lwid;
+ r.bottom = STATICHEIGHT;
+ doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
+
+ r.left = rpos1;
+ r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
+ r.right = rwid1;
+ r.bottom = PUSHBTNHEIGHT;
+ doctl(cp, r, "BUTTON",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+ 0, btext1, bid1);
+
+ r.left = rpos2;
+ r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2;
+ r.right = rwid2;
+ r.bottom = PUSHBTNHEIGHT;
+ doctl(cp, r, "BUTTON",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+ 0, btext2, bid2);
+
+ cp->ypos += height + GAPBETWEEN;
+}
+
+/*
* An edit control on the right hand side, with a static to its left.
*/
static void staticedit_internal(struct ctlpos *cp, char *stext,
}
/*
+ * A drop-down list box on the right hand side, with a static to
+ * its left.
+ */
+void staticddl(struct ctlpos *cp, char *stext,
+ int sid, int lid, int percentlist)
+{
+ const int height = (COMBOHEIGHT > STATICHEIGHT ?
+ COMBOHEIGHT : STATICHEIGHT);
+ RECT r;
+ int lwid, rwid, rpos;
+
+ rpos =
+ GAPBETWEEN + (100 - percentlist) * (cp->width + GAPBETWEEN) / 100;
+ lwid = rpos - 2 * GAPBETWEEN;
+ rwid = cp->width + GAPBETWEEN - rpos;
+
+ r.left = GAPBETWEEN;
+ r.top = cp->ypos + (height - STATICHEIGHT) / 2;
+ r.right = lwid;
+ r.bottom = STATICHEIGHT;
+ doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid);
+
+ r.left = rpos;
+ r.top = cp->ypos + (height - EDITHEIGHT) / 2;
+ r.right = rwid;
+ r.bottom = COMBOHEIGHT*4;
+ doctl(cp, r, "COMBOBOX",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP |
+ CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid);
+
+ cp->ypos += height + GAPBETWEEN;
+}
+
+/*
* A big multiline edit control with a static labelling it.
*/
void bigeditctrl(struct ctlpos *cp, char *stext,
PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
const static int percents[] = { 30, 40, 30 };
int i, xpos, percent;
- const int LISTHEIGHT = 66;
+ const int LISTHEIGHT = 52;
/* The static control. */
r.left = GAPBETWEEN;
}
/*
+ * A special control for manipulating an ordered preference list
+ * (eg. for cipher selection).
+ * XXX: this is a rough hack and could be improved.
+ */
+void prefslist(struct prefslist *hdl, struct ctlpos *cp, char *stext,
+ int sid, int listid, int upbid, int dnbid)
+{
+ const static int percents[] = { 5, 75, 20 };
+ RECT r;
+ int xpos, percent = 0, i;
+ const int DEFLISTHEIGHT = 52; /* XXX configurable? */
+ const int BTNSHEIGHT = 2*PUSHBTNHEIGHT + GAPBETWEEN;
+ int totalheight;
+
+ /* Squirrel away IDs. */
+ hdl->listid = listid;
+ hdl->upbid = upbid;
+ hdl->dnbid = dnbid;
+
+ /* The static label. */
+ 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, stext, sid);
+
+ /* XXX it'd be nice to centre the buttons wrt the listbox
+ * but we'd have to find out how high the latter actually is. */
+ if (DEFLISTHEIGHT > BTNSHEIGHT) {
+ totalheight = DEFLISTHEIGHT;
+ } else {
+ totalheight = BTNSHEIGHT;
+ }
+
+ for (i=0; i<3; i++) {
+ int left, wid;
+ xpos = (cp->width + GAPBETWEEN) * percent / 100;
+ left = xpos + GAPBETWEEN;
+ percent += percents[i];
+ xpos = (cp->width + GAPBETWEEN) * percent / 100;
+ wid = xpos - left;
+
+ switch (i) {
+ case 1:
+ /* The drag list box. */
+ r.left = left; r.right = wid;
+ r.top = cp->ypos; r.bottom = totalheight;
+ {
+ HWND ctl;
+ ctl = doctl(cp, r, "LISTBOX",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP |
+ WS_VSCROLL | LBS_HASSTRINGS,
+ WS_EX_CLIENTEDGE,
+ "", listid);
+ MakeDragList(ctl);
+ }
+ break;
+
+ case 2:
+ /* The "Up" and "Down" buttons. */
+ /* XXX worry about accelerators if we have more than one
+ * prefslist on a panel */
+ r.left = left; r.right = wid;
+ r.top = cp->ypos; r.bottom = PUSHBTNHEIGHT;
+ doctl(cp, r, "BUTTON",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+ 0, "&Up", upbid);
+
+ r.left = left; r.right = wid;
+ r.top = cp->ypos + PUSHBTNHEIGHT + GAPBETWEEN;
+ r.bottom = PUSHBTNHEIGHT;
+ doctl(cp, r, "BUTTON",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+ 0, "&Down", dnbid);
+
+ break;
+
+ }
+ }
+
+ cp->ypos += totalheight + GAPBETWEEN;
+
+}
+
+/*
+ * Helper function for prefslist: move item in list box.
+ */
+static void pl_moveitem(HWND hwnd, int listid, int src, int dst)
+{
+ int tlen, val;
+ char *txt;
+ /* Get the item's data. */
+ tlen = SendDlgItemMessage (hwnd, listid, LB_GETTEXTLEN, src, 0);
+ txt = smalloc(tlen+1);
+ SendDlgItemMessage (hwnd, listid, LB_GETTEXT, src, (LPARAM) txt);
+ val = SendDlgItemMessage (hwnd, listid, LB_GETITEMDATA, src, 0);
+ /* Deselect old location. */
+ SendDlgItemMessage (hwnd, listid, LB_SETSEL, FALSE, src);
+ /* Delete it at the old location. */
+ SendDlgItemMessage (hwnd, listid, LB_DELETESTRING, src, 0);
+ /* Insert it at new location. */
+ SendDlgItemMessage (hwnd, listid, LB_INSERTSTRING, dst,
+ (LPARAM) txt);
+ SendDlgItemMessage (hwnd, listid, LB_SETITEMDATA, dst,
+ (LPARAM) val);
+ /* Set selection. */
+ SendDlgItemMessage (hwnd, listid, LB_SETCURSEL, dst, 0);
+ sfree (txt);
+}
+
+int pl_itemfrompt(HWND hwnd, POINT cursor, BOOL scroll)
+{
+ int ret;
+ POINT uppoint, downpoint;
+ int updist, downdist, upitem, downitem, i;
+
+ /*
+ * Ghastly hackery to try to figure out not which
+ * _item_, but which _gap between items_, the user
+ * is pointing at. We do this by first working out
+ * which list item is under the cursor, and then
+ * working out how far the cursor would have to
+ * move up or down before the answer was different.
+ * Then we put the insertion point _above_ the
+ * current item if the upper edge is closer than
+ * the lower edge, or _below_ it if vice versa.
+ */
+ ret = LBItemFromPt(hwnd, cursor, scroll);
+ if (ret == -1)
+ return ret;
+ ret = LBItemFromPt(hwnd, cursor, FALSE);
+ updist = downdist = 0;
+ for (i = 1; i < 4096 && (!updist || !downdist); i++) {
+ uppoint = downpoint = cursor;
+ uppoint.y -= i;
+ downpoint.y += i;
+ upitem = LBItemFromPt(hwnd, uppoint, FALSE);
+ downitem = LBItemFromPt(hwnd, downpoint, FALSE);
+ if (!updist && upitem != ret)
+ updist = i;
+ if (!downdist && downitem != ret)
+ downdist = i;
+ }
+ if (downdist < updist)
+ ret++;
+ return ret;
+}
+
+/*
+ * Handler for prefslist above.
+ */
+int handle_prefslist(struct prefslist *hdl,
+ int *array, int maxmemb,
+ int is_dlmsg, HWND hwnd,
+ WPARAM wParam, LPARAM lParam)
+{
+ int i;
+ int ret;
+
+ if (is_dlmsg) {
+
+ if ((int)wParam == hdl->listid) {
+ DRAGLISTINFO *dlm = (DRAGLISTINFO *)lParam;
+ int dest;
+ switch (dlm->uNotification) {
+ case DL_BEGINDRAG:
+ hdl->dummyitem =
+ SendDlgItemMessage(hwnd, hdl->listid,
+ LB_ADDSTRING, 0, (LPARAM) "");
+
+ hdl->srcitem = LBItemFromPt(dlm->hWnd, dlm->ptCursor, TRUE);
+ hdl->dragging = 0;
+ /* XXX hack Q183115 */
+ SetWindowLong(hwnd, DWL_MSGRESULT, TRUE);
+ ret = 1; break;
+ case DL_CANCELDRAG:
+ DrawInsert(hwnd, dlm->hWnd, -1); /* Clear arrow */
+ SendDlgItemMessage(hwnd, hdl->listid,
+ LB_DELETESTRING, hdl->dummyitem, 0);
+ hdl->dragging = 0;
+ ret = 1; break;
+ case DL_DRAGGING:
+ hdl->dragging = 1;
+ dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
+ if (dest > hdl->dummyitem) dest = hdl->dummyitem;
+ DrawInsert (hwnd, dlm->hWnd, dest);
+ if (dest >= 0)
+ SetWindowLong(hwnd, DWL_MSGRESULT, DL_MOVECURSOR);
+ else
+ SetWindowLong(hwnd, DWL_MSGRESULT, DL_STOPCURSOR);
+ ret = 1; break;
+ case DL_DROPPED:
+ if (hdl->dragging) {
+ dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, TRUE);
+ if (dest > hdl->dummyitem) dest = hdl->dummyitem;
+ DrawInsert (hwnd, dlm->hWnd, -1);
+ }
+ SendDlgItemMessage(hwnd, hdl->listid,
+ LB_DELETESTRING, hdl->dummyitem, 0);
+ if (hdl->dragging) {
+ hdl->dragging = 0;
+ if (dest >= 0) {
+ /* Correct for "missing" item. */
+ if (dest > hdl->srcitem) dest--;
+ pl_moveitem(hwnd, hdl->listid, hdl->srcitem, dest);
+ }
+ }
+ ret = 1; break;
+ }
+ }
+
+ } else {
+
+ ret = 0;
+ if (((LOWORD(wParam) == hdl->upbid) ||
+ (LOWORD(wParam) == hdl->dnbid)) &&
+ ((HIWORD(wParam) == BN_CLICKED) ||
+ (HIWORD(wParam) == BN_DOUBLECLICKED))) {
+ /* Move an item up or down the list. */
+ /* Get the current selection, if any. */
+ int selection = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCURSEL, 0, 0);
+ if (selection == LB_ERR) {
+ MessageBeep(0);
+ } else {
+ int nitems;
+ /* Get the total number of items. */
+ nitems = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCOUNT, 0, 0);
+ /* Should we do anything? */
+ if (LOWORD(wParam) == hdl->upbid && (selection > 0))
+ pl_moveitem(hwnd, hdl->listid, selection, selection - 1);
+ else if (LOWORD(wParam) == hdl->dnbid && (selection < nitems - 1))
+ pl_moveitem(hwnd, hdl->listid, selection, selection + 1);
+ }
+
+ }
+
+ }
+
+ /* Update array to match the list box. */
+ for (i=0; i < maxmemb; i++)
+ array[i] = SendDlgItemMessage (hwnd, hdl->listid, LB_GETITEMDATA,
+ i, 0);
+
+ return ret;
+
+}
+
+/*
* A progress bar (from Common Controls). We like our progress bars
* to be smooth and unbroken, without those ugly divisions; some
* older compilers may not support that, but that's life.
#endif
, WS_EX_CLIENTEDGE, "", id);
}
+
+/*
+ * Another special control: the forwarding options setter. First a
+ * list box; next a static header line, introducing a pair of edit
+ * boxes with associated statics, another button, and a radio
+ * button pair.
+ */
+void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
+ char *e1stext, int e1sid, int e1id,
+ char *e2stext, int e2sid, int e2id,
+ char *btext, int bid)
+{
+ RECT r;
+ const int height = (STATICHEIGHT > EDITHEIGHT
+ && STATICHEIGHT >
+ PUSHBTNHEIGHT ? STATICHEIGHT : EDITHEIGHT >
+ PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT);
+ const static int percents[] = { 25, 35, 15, 25 };
+ int i, j, xpos, percent;
+ const int LISTHEIGHT = 42;
+
+ /* The list box. */
+ r.left = GAPBETWEEN;
+ r.top = cp->ypos;
+ r.right = cp->width;
+ r.bottom = LISTHEIGHT;
+ cp->ypos += r.bottom + GAPBETWEEN;
+ doctl(cp, r, "LISTBOX",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS
+ | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid);
+
+ /* The static control. */
+ 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, stext, sid);
+
+ /* The statics+edits+buttons. */
+ for (j = 0; j < 2; j++) {
+ percent = 0;
+ for (i = 0; i < (j ? 2 : 4); i++) {
+ xpos = (cp->width + GAPBETWEEN) * percent / 100;
+ r.left = xpos + GAPBETWEEN;
+ percent += percents[i];
+ if (j==1 && i==1) percent = 100;
+ xpos = (cp->width + GAPBETWEEN) * percent / 100;
+ r.right = xpos - r.left;
+ r.top = cp->ypos;
+ r.bottom = (i == 0 ? STATICHEIGHT :
+ i == 1 ? EDITHEIGHT : PUSHBTNHEIGHT);
+ r.top += (height - r.bottom) / 2;
+ if (i == 0) {
+ doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0,
+ j == 0 ? e1stext : e2stext, j == 0 ? e1sid : e2sid);
+ } else if (i == 1) {
+ doctl(cp, r, "EDIT",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
+ WS_EX_CLIENTEDGE, "", j == 0 ? e1id : e2id);
+ } else if (i == 3) {
+ doctl(cp, r, "BUTTON",
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
+ 0, btext, bid);
+ }
+ }
+ cp->ypos += height + GAPWITHIN;
+ }
+}