📄 listbox.c
字号:
/*
COW : Character Oriented Windows
(COW USER DIALOG)
listbox.c : list boxes
Note : much of the math in the module is excessive (i.e. WORDs for BYTES)
Note : all far pointer control is performed with limited-lifetime pointers
*/
#define COW
#include <cow.h>
#include <udialog.h>
#include <uscroll.h>
#include <uwindow.h>
#include <uscreen.h>
#include <uisa.h>
#include <uevent.h>
#include <vkey.h>
#include <kinput.h>
#include <kkeyboar.h>
#include <kmem.h> /* kernel exports for memory management */
/* really integrated into SDM */
#include <sdmver.h>
#include <usdm.h>
#include <usdmtmpl.h>
#include "sdm.h"
#include "window.h"
#include "dialog.h"
#include "event.h"
#include "scroll.h"
#include "screen.h"
#include "util.h"
#include "case.h"
#include "listbox.h"
#include "_listbox.h"
#ifdef LISTBOX_LIMIT_SIZE
#define cchListTextMax 64
#else
#define cchListTextMax 256
#endif
#ifdef EXTRAS
#ifndef LISTBOX_HORIZ
#define LISTBOX_COLOR /* listbox content may contain color */
#endif
#endif
/* REVIEW: move this to WINDOW.H !!! */
#ifdef EXTRAS
#define chColorPrefix ((char) 0xfe) /* => special color prefix */
#endif
/* forward */
STATIC VOID DisplayListBox(PWND);
STATIC VOID FillListBox(PWND, RY, RY, WORD);
STATIC VOID ScrollListBox(PWND, short, BOOL);
STATIC VOID ScrollHorizListBox(PWND, short); /* LISTBOX_HORIZ only */
STATIC VOID HiliteListSel(PWND, BOOL);
STATIC VOID MoveSelection(PWND, WORD);
STATIC VOID MoveSelectionDown(PWND);
STATIC VOID MoveSelectionUp(PWND);
STATIC VOID MoveSelectionLeft(PWND, WORD); /* LISTBOX_HORIZ only */
STATIC VOID MoveSelectionRight(PWND, WORD); /* LISTBOX_HORIZ only */
STATIC VOID SetScrollWindow(PWND);
STATIC VOID ResetContent(PWND);
STATIC VOID AddListSz(PWND, char *, WORD);
STATIC VOID InsertSz(PWND, WORD, char *, BOOL, WORD);
STATIC VOID ReplaceSz(PWND, WORD, char *);
STATIC VOID DeleteSz(PWND, WORD, WORD);
STATIC VOID RevertToOomLb(PWND, WORD);
STATIC BOOL FLocateMatch(PWND, WORD);
STATIC VOID GetOnDemand(PWND, WORD, WORD FAR **, char FAR **, char *);
STATIC VOID FAR * LpvDeref(WORD);
#ifdef KANJI
STATIC int fdircmp(char FAR *, char FAR *);
STATIC int fjstrcmp(unsigned char FAR *, unsigned char FAR *);
#endif
PUBLIC DWORD FARPUBLIC /* WndProcs are PUBLIC */
ListBoxWndProc(pwnd, message, wParam, lParam)
/*
-- WndProc for List Boxes
-- handles all messages from SDM and Dialog manager
*/
REG PWND pwnd;
WORD message;
WORD wParam;
DWORD lParam;
{
WORD wSelect; /* indicates cause of selection */
WORD iszNew;
REG WORD iszCur;
MSP msp;
RRC rrc;
short dryLb; /* height of listbox */
#ifdef LISTBOX_HORIZ
BOOL fHoriz = pwnd->style & WS_HSCROLL;
#endif /*LISTBOX_HORIZ*/
Assert(pwnd->iszCurLb != iszNil);
#ifndef LISTBOX_HORIZ
Assert(pwnd->axCursor == AxOfRx(pwnd, rxListBoxMin));
#endif
GetClientRrc(pwnd,&rrc);
iszCur = FSelected(pwnd) ? pwnd->iszCurLb : iszNil;
dryLb = rrc.ryBottom;
wSelect = 0;
switch(message)
{
default:
/*case WM_ACTIVATE:*/
ReturnFalse:
return((DWORD) FALSE);
break;
case WM_PAINT:
#ifdef DEBUG
#ifndef LISTBOX_ONELINE
AssertSz(rrc.ryBottom - rrc.ryTop >= 2,
"Listbox too small");
#endif /*LISTBOX_ONELINE*/
#endif /*DEBUG*/
DrawBorder(pwnd, &boxSingle, pwnd->isaColor, NULL);
DisplayListBox(pwnd);
goto ReturnTrue;
case WM_WANTFOCUS:
return((DWORD) pwnd->cszLb); /* returns FALSE if empty */
/*break;*/
case WM_SETFOCUS:
Assert(pwnd->cszLb != 0);
if (pwnd->pwndParent != NULL)
SendMessage(pwnd->pwndParent, WM_DIALOG_SETFOCUS,
pwnd->id, 0L);
goto ReturnTrue;
case WM_KILLFOCUS:
if (pwnd->pwndParent != NULL)
SendMessage(pwnd->pwndParent, WM_DIALOG_KILLFOCUS,
pwnd->id, 0L);
goto ReturnTrue;
case LB_SETCURSEL:
if (wParam == iszNil)
{
/* unselect it */
HiliteListSel(pwnd, FALSE);
SetFSelected(pwnd, FALSE);
}
else if (wParam < pwnd->cszLb && wParam != iszCur)
{
MoveSelection(pwnd, wParam); /* move+draw */
}
else
{
/* invalid selection: if debugging give a warning */
AssertSz(wParam < pwnd->cszLb, "Invalid listbox selection");
goto ReturnFalse; /* selection not set */
}
goto ReturnTrue;
/*break;*/
case WM_MOUSEMOVE:
/* ignore non-capture & client moves */
if (!FCaptured(pwnd) || (wParam & MK_NONCLIENT))
goto ReturnTrue;
/* else : fall through */
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
msp.lParam = lParam;
/* listbox must be non-empty & left button down */
if (pwnd->cszLb == 0 || !(wParam & MK_LBUTTON))
goto ReturnFalse;
wSelect |= lbrMouse;
/* we should have the mouse captured */
if (!FCaptured(pwnd))
{
SetCapture(pwnd);
SetAlarm(pwnd, ctickRepScrollStart);
}
if (!(wParam & MK_NONCLIENT))
{
/* in middle of listbox */
SetFocus(pwnd);
iszNew = pwnd->iszTopLb + msp.s.ry;
#ifdef LISTBOX_HORIZ
if (fHoriz)
{
// bump extra width
/* which column are we in ? */
iszNew += ((msp.s.rx - rxListBoxMin) /
(pwnd->drxItemLb + 1)) * dryLb;
}
#endif /*LISTBOX_HORIZ*/
if (iszNew >= pwnd->cszLb)
{
wSelect = lbrOther;
if (FSelected(pwnd))
wSelect |= flbrReselect;
iszCur = iszNil; /* force message */
}
else if (message == WM_LBUTTONDBLCLK &&
iszNew == iszCur)
{
/* double click selection */
if (FCaptured(pwnd))
{
ReleaseCapture();
KillAlarm();
}
SendMessage(pwnd->pwndParent, WM_DIALOG,
pwnd->id, MAKELONG(0, LBN_DBLCLK));
goto ReturnTrue;
}
else
{
if (iszNew == iszCur)
wSelect |= flbrReselect;
iszCur = iszNil; /* force message */
MoveSelection(pwnd, iszNew);
}
}
else if (msp.s.ay < pwnd->arcClipping.ayTop)
MoveSelectionUp(pwnd);
else if (msp.s.ay >= pwnd->arcClipping.ayBottom)
MoveSelectionDown(pwnd);
#ifdef LISTBOX_HORIZ
/* special horizontal scroll */
else if (msp.s.ax < pwnd->arcClipping.axLeft)
MoveSelectionLeft(pwnd, 1);
else if (msp.s.ax >= pwnd->arcClipping.axRight)
MoveSelectionRight(pwnd, 1);
#endif /*LISTBOX_HORIZ*/
break;
case WM_ALARM:
if (FCaptured(pwnd))
{
wSelect |= lbrMouse;
SetAlarm(pwnd, pwnd->ctickRepLb);
if (ayMouse < pwnd->arcClipping.ayTop)
{
MoveSelectionUp(pwnd);
/* fast repeat ? */
if (ayMouse < pwnd->arcClipping.ayTop - 2)
SetAlarm(pwnd, pwnd->ctickRepLb / 2);
}
else if (ayMouse >= pwnd->arcClipping.ayBottom)
{
MoveSelectionDown(pwnd);
/* fast repeat ? */
if (ayMouse >= pwnd->arcClipping.ayBottom + 2)
SetAlarm(pwnd, pwnd->ctickRepLb / 2);
}
#ifdef LISTBOX_HORIZ
/* special horizontal scroll (1 speed) */
else if (axMouse < pwnd->arcClipping.axLeft)
MoveSelectionLeft(pwnd, 1);
else if (axMouse >= pwnd->arcClipping.axRight)
MoveSelectionRight(pwnd, 1);
#endif /*LISTBOX_HORIZ*/
}
break;
case WM_LBUTTONUP:
if (!FCaptured(pwnd))
goto ReturnFalse; /* ignore */
ReleaseCapture();
KillAlarm();
SendMessage(pwnd->pwndParent, WM_DIALOG, pwnd->id,
MAKELONG(0,LBN_SELECT_DONE));
goto ReturnTrue;
/* break; */
case WM_VSCROLL:
{
short dry = 0;
if (pwnd->cszLb == 0)
goto ReturnFalse; /* nothing for empty listboxes */
wSelect |= lbrScroll;
switch (wParam)
{
default:
break;
case SB_LINEDOWN:
dry++; /* 1 line down */
Assert(dry == 1);
break;
case SB_LINEUP:
dry--; /* 1 line up */
Assert(dry == -1);
break;
case SB_PAGEDOWN:
dry = dryLb;
break;
case SB_PAGEUP:
dry = -dryLb;
break;
case SB_THUMBPOSITION:
dry = LOWORD(lParam) - pwnd->iszTopLb;
break;
case SB_UPCLICK:
SendMessage(pwnd->pwndParent, WM_DIALOG, pwnd->id,
MAKELONG(0,LBN_SELECT_DONE));
break;
}
if (dry != 0)
ScrollListBox(pwnd, dry, TRUE);
}
break;
#ifdef LISTBOX_HORIZ
case WM_HSCROLL:
{
if (pwnd->cszLb == 0)
goto ReturnFalse; /* nothing for empty listboxes */
wSelect |= lbrScroll;
/* move selection (since scrolling makes no sense */
switch (wParam)
{
default:
break;
case SB_LINEDOWN:
MoveSelectionRight(pwnd, 1);
break;
case SB_LINEUP:
MoveSelectionLeft(pwnd, 1);
break;
case SB_PAGEDOWN:
MoveSelectionRight(pwnd, pwnd->citemWidthLb);
break;
case SB_PAGEUP:
MoveSelectionLeft(pwnd, pwnd->citemWidthLb);
break;
case SB_THUMBPOSITION:
{
WORD iszNew;
iszNew = LOWORD(lParam) * dryLb + iszCur % dryLb;
if (iszNew >= pwnd->cszLb)
iszNew = pwnd->cszLb - 1;
MoveSelection(pwnd, iszNew);
}
break;
}/*switch*/
}
break;
#endif /*LISTBOX_HORIZ*/
case LB_RESETCONTENT:
ResetContent(pwnd);
goto ReturnTrue;
/*break;*/
case LB_ADDSTRING:
AddListSz(pwnd, (char *) wParam, LOWORD(lParam));
goto ReturnTrue;
/*break;*/
case LB_INSERTSTRING:
InsertSz(pwnd, HIWORD(lParam), (char *) wParam, FALSE, LOWORD(lParam));
goto ReturnTrue;
/*break;*/
case LB_REPLACESTRING:
ReplaceSz(pwnd, HIWORD(lParam), (char *) wParam);
goto ReturnTrue;
/*break;*/
case LB_DELETESTRING:
DeleteSz(pwnd, HIWORD(lParam), LOWORD(lParam));
goto ReturnTrue;
/*break;*/
case LB_GETCURSEL:
return ((DWORD) iszCur);
/*break;*/
case LB_GETCOUNT:
return ((DWORD) pwnd->cszLb);
/*break;*/
case LB_GETTEXT:
return ((DWORD) GetListText(
pwnd, (char *) wParam, HIWORD(lParam)));
/*break;*/
#ifdef LISTBOX_HORIZ
case LB_SETWIDTH:
/* set # item width for horizontal listboxes
(wParam == citem) -- usually sent before repainting */
Assert(wParam != 0);
pwnd->citemWidthLb = wParam;
/* 1 extra at start and after each row */
Assert(rxListBoxMin == 1);
pwnd->drxItemLb = ((rrc.rxRight - 1) / wParam) - 1;
/* KLUDGE: convert vertical listbox into horizontal */
if (pwnd->style & WS_VSCROLL)
{
REG PWND pwndScrl;
pwndScrl = PwndChild(pwnd);
Assert(pwndScrl != NULL);
Assert(pwndScrl->style & SBS_VERT);
// switch scroll bar position (to bottom)
pwndScrl->arcWindow.axLeft = pwnd->arcWindow.axLeft +
daxBorder;
pwndScrl->arcWindow.axRight = pwnd->arcWindow.axRight -
daxBorder;
pwndScrl->arcWindow.ayTop =
(pwndScrl->arcWindow.ayBottom =
pwnd->arcWindow.ayBottom) - dayBorder;
// switch style & validate
pwndScrl->style = (pwndScrl->style & ~SBS_VERT) | SBS_HORZ;
ValidateWindow(pwndScrl);
// switch parent style
pwnd->style = (pwnd->style & ~WS_VSCROLL) | WS_HSCROLL;
ValidateWindow(pwnd);
}
goto ReturnTrue;
#endif /*LISTBOX_HORIZ*/
case WM_CHAR:
/* return FALSE if key ignored */
if ((HIWORD(lParam) & KK_MENU) || (!pwnd->fEnabled))
goto ReturnFalse; /* pass ALT keys through */
UndoRepeat(wParam, lParam);
wSelect |= lbrKeys;
switch(wParam)
{
default:
/* we should eat all non-control ascii keys
* (they may or may not do anything)
* control keys and non-ascii will not be eaten
*/
if (!FLocateMatch(pwnd, wParam) &&
(wParam < 0x20 || wParam >= VK_MIN))
{
/* control or non-ascii did not match */
goto ReturnFalse;
}
break;
case VK_LEFT:
#ifdef LISTBOX_HORIZ
MoveSelectionLeft(pwnd, 1);
break;
#endif /*LISTBOX_HORIZ*/
case VK_UP:
MoveSelectionUp(pwnd);
break;
case VK_RIGHT:
#ifdef LISTBOX_HORIZ
MoveSelectionRight(pwnd, 1);
break;
#endif /*LISTBOX_HORIZ*/
case VK_DOWN:
MoveSelectionDown(pwnd);
break;
case VK_NEXT:
#ifdef LISTBOX_HORIZ
if (fHoriz)
MoveSelectionRight(pwnd, pwnd->citemWidthLb);
else
#endif /*LISTBOX_HORIZ*/
ScrollListBox(pwnd, dryLb, TRUE);
break;
case VK_PRIOR:
#ifdef LISTBOX_HORIZ
if (fHoriz)
MoveSelectionLeft(pwnd, pwnd->citemWidthLb);
else
#endif /*LISTBOX_HORIZ*/
ScrollListBox(pwnd, -dryLb, TRUE);
break;
case ' ':
wSelect = flbrReselect | lbrSpace;
/* force message for change of selection */
iszCur = iszNil; /* force a change */
HiliteListSel(pwnd, TRUE);
break;
case VK_HOME:
if (pwnd->cszLb != 0)
MoveSelection(pwnd, iszMin);
break;
case VK_END:
if (pwnd->cszLb != 0)
MoveSelection(pwnd, pwnd->cszLb - 1);
break;
}
break;
}
if (FSelected(pwnd) && iszCur != pwnd->iszCurLb)
{
/* tell parent that selection has changed */
Assert((wSelect & lbrCause) != lbrNone)
SendMessage(pwnd->pwndParent, WM_DIALOG, pwnd->id,
MAKELONG(wSelect, LBN_SELCHANGE));
}
ReturnTrue:
return((DWORD) TRUE); /* default case */
}
STATIC VOID
DisplayListBox(pwnd)
/*
-- display the contents of the list box
*/
REG PWND pwnd;
{
RRC rrc;
Assert(fDrawItem);
GetClientRrc(pwnd, &rrc);
FillRrc(pwnd, &rrc, ' ', pwnd->isaColor);
if (pwnd->cszLb > 0)
FillListBox(pwnd, 0, rrc.ryBottom, pwnd->iszTopLb);
SetScrollWindow(pwnd);
}
STATIC VOID
SetScrollWindow(pwnd)
/*
-- sets the scroll range and position
-- vertical is simple, horizontal is a pain
*/
REG PWND pwnd;
{
RRC rrc;
/* check for no scrollbar */
#ifdef LISTBOX_ONELINE
if (!(pwnd->style & (WS_VSCROLL | WS_HSCROLL)))
return;
#else
Assert(pwnd->style & (WS_VSCROLL | WS_HSCROLL));
#endif /*LISTBOX_ONELINE*/
GetClientRrc(pwnd,&rrc);
#ifdef LISTBOX_HORIZ
if (pwnd->style & WS_HSCROLL)
{
REG short colMac;
BYTE dry;
dry = rrc.ryBottom;
colMac = (pwnd->cszLb - 1) / dry + 1;
SetScrollRange(pwnd->pwndChild, 0, colMac, FALSE);
SetScrollPos(pwnd->pwndChild, pwnd->iszCurLb / dry, TRUE);
}
else
#endif /*LISTBOX_HORIZ*/
{
// vertical listbox case
REG short iszBot;
iszBot = pwnd->cszLb - rrc.ryBottom;
if (iszBot < 1)
iszBot = 1; /* set range to 1 */
SetScrollRange(pwnd->pwndChild, 0, iszBot, FALSE);
SetScrollPos(pwnd->pwndChild, pwnd->iszTopLb, TRUE);
}
}
STATIC VOID
FillListBox(pwnd, ryTop, ryBottom, isz)
/*
-- fill in the specified lines in the listbox
-- ryTop is the top line, ryBottom is the bottom line
-- isz is the isz corresponding to the top line
*/
REG PWND pwnd;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -