Files
MiniGUI/src/control/listbox.c
T

2283 lines
66 KiB
C

///////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT NOTICE
//
// The following open source license statement does not apply to any
// entity in the Exception List published by FMSoft.
//
// For more information, please visit:
//
// https://www.fmsoft.cn/exception-list
//
//////////////////////////////////////////////////////////////////////////////
/*
* This file is part of MiniGUI, a mature cross-platform windowing
* and Graphics User Interface (GUI) support system for embedded systems
* and smart IoT devices.
*
* Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd.
* Copyright (C) 1998~2002, WEI Yongming
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Or,
*
* As this program is a library, any link to this program must follow
* GNU General Public License version 3 (GPLv3). If you cannot accept
* GPLv3, you need to be licensed from FMSoft.
*
* If you have got a commercial license of this program, please use it
* under the terms and conditions of the commercial license.
*
* For more information about the commercial license, please refer to
* <http://www.minigui.com/en/about/licensing-policy/>.
*/
/*
** listbox.c: the List Box Control module.
**
** Created by Jiao Libo at 1999/8/31
**
** TODO:
** * Multiple columns support.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#ifdef _MGCTRL_LISTBOX
#include "minigui.h"
#include "gdi.h"
#include "window.h"
#include "ctrl/ctrlhelper.h"
#include "ctrl/listbox.h"
#include "ctrl/scrollview.h"
#include "cliprect.h"
#include "internals.h"
#include "ctrlclass.h"
#include "ctrlmisc.h"
#include "listbox_impl.h"
#define _USE_FIXSTR 1
#define ITEM_BOTTOM(x) (x->itemTop + x->itemVisibles - 1)
#define LST_INTER_BMPTEXT 2
/** minimum height of checkmark */
#define LFRDR_LB_CHECKBMP_MIN 6
static LRESULT ListboxCtrlProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL RegisterListboxControl (void)
{
WNDCLASS WndClass;
WndClass.spClassName = CTRL_LISTBOX;
WndClass.dwStyle = WS_NONE;
WndClass.dwExStyle = WS_EX_NONE;
WndClass.hCursor = GetSystemCursor (0);
WndClass.iBkColor = GetWindowElementPixel (HWND_NULL, WE_BGC_WINDOW);
WndClass.WinProc = ListboxCtrlProc;
return AddNewControlClass (&WndClass) == ERR_OK;
}
/*
* Get height of listbox checkmark bmp
*
* Author : NuohuaZhou
* Date : 2007-11-29
*/
static int CheckMarkHeight (int item_height)
{
int checkmark_height =
item_height - LFRDR_LB_CHECKBMP_MIN;
if (0 < checkmark_height)
checkmark_height += checkmark_height >> 2;
return checkmark_height;
}
static BOOL lstInitListBoxData (HWND hwnd, PCONTROL pCtrl, LISTBOXDATA* pData, int len)
{
int i;
PLISTBOXITEM plbi;
pData->itemHeight = pCtrl->pLogFont->size;
pData->itemHilighted = -1;
pData->selItem = pData->itemHilighted;
pData->itemCount = 0;
pData->str_cmp = strncmp;
/* init item buffer. */
if (!(pData->buffStart = malloc (len * sizeof (LISTBOXITEM))))
return FALSE;
pData->buffLen = len;
pData->buffEnd = pData->buffStart + len;
pData->freeList = pData->buffStart;
plbi = pData->freeList;
for (i = 0; i < len - 1; i++) {
plbi->next = plbi + 1;
plbi ++;
}
plbi->next = NULL;
if (GetWindowStyle (hwnd) & LBS_SBALWAYS)
pData->sbPolicy = SB_POLICY_ALWAYS;
else
pData->sbPolicy = SB_POLICY_AUTOMATIC;
return TRUE;
}
static void lstListBoxCleanUp (LISTBOXDATA* pData)
{
PLISTBOXITEM plbi;
PLISTBOXITEM next;
plbi = pData->head;
while (plbi) {
#if _USE_FIXSTR
FreeFixStr (plbi->key);
#else
free (plbi->key);
#endif
next = plbi->next;
if (plbi < pData->buffStart || plbi > pData->buffEnd)
free (plbi);
plbi = next;
}
free (pData->buffStart);
}
static void lstResetListBoxContent (PLISTBOXDATA pData)
{
int i;
PLISTBOXITEM plbi, next;
pData->itemLeft = 0;
pData->itemWidth = 0;
pData->itemMaxWidth = 0;
pData->itemCount = 0;
pData->itemTop = 0;
pData->itemHilighted = -1;
pData->selItem = pData->itemHilighted;
plbi = pData->head;
while (plbi) {
#if _USE_FIXSTR
FreeFixStr (plbi->key);
#else
free (plbi->key);
#endif
next = plbi->next;
if (plbi < pData->buffStart || plbi > pData->buffEnd)
free (plbi);
plbi = next;
}
pData->head = NULL;
pData->freeList = pData->buffStart;
plbi = pData->freeList;
for (i = 0; i < pData->buffLen - 1; i++) {
plbi->next = plbi + 1;
plbi ++;
}
plbi->next = NULL;
return;
}
static PLISTBOXITEM lstAllocItem (PLISTBOXDATA pData)
{
PLISTBOXITEM plbi;
if (pData->freeList) {
plbi = pData->freeList;
pData->freeList = plbi->next;
}
else
plbi = (PLISTBOXITEM) malloc (sizeof (LISTBOXITEM));
return plbi;
}
static void lstFreeItem (PLISTBOXDATA pData, PLISTBOXITEM plbi)
{
if (plbi < pData->buffStart || plbi > pData->buffEnd)
free (plbi);
else {
plbi->next = pData->freeList;
pData->freeList = plbi;
}
}
static int lstGetItemWidth (HWND hwnd, PLISTBOXITEM plbi, int item_height)
{
PCONTROL pCtrl;
DWORD dwStyle;
int x = 0;
pCtrl = gui_Control (hwnd);
dwStyle = pCtrl->dwStyle;
x = LST_INTER_BMPTEXT;
if (dwStyle & LBS_CHECKBOX)
x += CheckMarkHeight (item_height) + LST_INTER_BMPTEXT;
if (dwStyle & LBS_USEICON && plbi->dwImage) {
if (plbi->dwFlags &LBIF_USEBITMAP)
x += ((PBITMAP)(plbi->dwImage))->bmWidth;
else {
int width;
GetIconSize ((HICON) plbi->dwImage, &width, NULL);
x += width;
}
x += LST_INTER_BMPTEXT;
}
if (plbi->key && plbi->key[0] != '\0') {
SIZE size;
HDC hdc;
hdc = GetClientDC (hwnd);
GetTabbedTextExtent (hdc, plbi->key, -1, &size);
ReleaseDC (hdc);
x += size.cx + LST_INTER_BMPTEXT;
}
return x;
}
static int lstGetMaxWidth(HWND hwnd, PLISTBOXDATA pData)
{
PLISTBOXITEM plbi;
int max = 0;
int width;
plbi = pData->head;
while (plbi) {
width = lstGetItemWidth (hwnd, plbi, pData->itemHeight);
max = MAX(max, width);
plbi = plbi->next;
}
return max;
}
static int lstAddNewItem (DWORD dwStyle,
PLISTBOXDATA pData, PLISTBOXITEM newItem, int pos)
{
PLISTBOXITEM plbi;
PLISTBOXITEM insPosItem = NULL;
int insPos = 0;
newItem->next = NULL;
if (!pData->head)
insPosItem = NULL;
else if (dwStyle & LBS_SORT) {
plbi = pData->head;
if (pData->str_cmp (newItem->key, plbi->key, (size_t)-1) < 0) {
insPosItem = NULL;
insPos = 0;
}
else {
while (plbi->next) {
if (pData->str_cmp (newItem->key, plbi->next->key, (size_t)-1) <= 0)
break;
plbi = plbi->next;
insPos ++;
}
insPosItem = plbi;
}
}
else {
plbi = pData->head;
if (pos < 0) {
while (plbi->next) {
plbi = plbi->next;
insPos ++;
}
insPosItem = plbi;
}
else if (pos == 0) {
insPosItem = NULL;
insPos = 0;
}
else {
int index = 1;
while (plbi) {
if (pos == index)
break;
plbi = plbi->next;
index ++;
insPos ++;
}
insPosItem = plbi;
}
}
if (insPosItem) {
plbi = insPosItem->next;
insPosItem->next = newItem;
newItem->next = plbi;
insPos ++;
}
else {
plbi = pData->head;
pData->head = newItem;
newItem->next = plbi;
}
pData->itemCount ++;
return insPos;
}
static PLISTBOXITEM lstRemoveItem (PLISTBOXDATA pData, int* pos)
{
int index = 0;
PLISTBOXITEM plbi, prev;
if (!pData->head)
return NULL;
if (*pos < 0 || *pos >= pData->itemCount)
return NULL;
/*
if (*pos < 0) {
prev = pData->head;
plbi = pData->head;
while (plbi->next) {
prev = plbi;
plbi = plbi->next;
index ++;
}
if (plbi == pData->head) {
pData->head = pData->head->next;
*pos = 0;
return plbi;
}
else {
prev->next = plbi->next;
*pos = index;
return plbi;
}
}
else
*/
if (*pos == 0) {
plbi = pData->head;
pData->head = plbi->next;
return plbi;
}
else {
index = 0;
prev = pData->head;
plbi = pData->head;
while (plbi->next) {
if (*pos == index)
break;
prev = plbi;
plbi = plbi->next;
index ++;
}
if (plbi == pData->head) {
pData->head = pData->head->next;
*pos = 0;
return plbi;
}
else {
prev->next = plbi->next;
*pos = index;
return plbi;
}
}
return NULL;
}
static void lstGetItemsRect (PLISTBOXDATA pData,
int start, int end, RECT* prc)
{
if (start < 0)
start = 0;
prc->top = (start - pData->itemTop)*pData->itemHeight;
if (end >= 0)
prc->bottom = (end - pData->itemTop + 1)*pData->itemHeight;
}
static void lstInvalidateItem (HWND hwnd, PLISTBOXDATA pData, int pos)
{
RECT rcInv;
if (pos < pData->itemTop || pos > (pData->itemTop + pData->itemVisibles))
return;
GetClientRect (hwnd, &rcInv);
rcInv.top = (pos - pData->itemTop)*pData->itemHeight;
rcInv.bottom = rcInv.top + pData->itemHeight;
InvalidateRect (hwnd, &rcInv, TRUE);
}
static BOOL
lstInvalidateUnderMultiItem (HWND hwnd,
PLISTBOXDATA pData, int start, int end)
{
RECT rcInv;
int pos;
int max;
if (start < 0) start = 0;
if (end < 0) end = 0;
pos = start > end ? end : start;
max = start > end ? start : end;
if (pos > (pData->itemTop + pData->itemVisibles))
return FALSE;
if (max < pData->itemTop)
return FALSE;
GetClientRect (hwnd, &rcInv);
lstGetItemsRect (pData, pos, max, &rcInv);
if (rcInv.top < rcInv.bottom) {
InvalidateRect (hwnd, &rcInv, TRUE);
}
return TRUE;
}
static BOOL
lstInvalidateUnderItem (HWND hwnd, PLISTBOXDATA pData, int pos)
{
RECT rcInv;
if (pos > (pData->itemTop + pData->itemVisibles))
return FALSE;
if (pos < pData->itemTop)
return FALSE;
GetClientRect (hwnd, &rcInv);
lstGetItemsRect (pData, pos, -1, &rcInv);
if (rcInv.top < rcInv.bottom)
InvalidateRect (hwnd, &rcInv, TRUE);
return TRUE;
}
static PLISTBOXITEM lstGetItem (PLISTBOXDATA pData, int pos)
{
int i;
PLISTBOXITEM plbi;
plbi = pData->head;
for (i=0; i < pos && plbi; i++)
plbi = plbi->next;
return plbi;
}
static int lstFindItem (PLISTBOXDATA pData, int start, char* key, BOOL bExact)
{
PLISTBOXITEM plbi;
int keylen = strlen (key);
if (start >= pData->itemCount)
start = 0;
else if (start < 0)
start = 0;
plbi = lstGetItem (pData, start);
while (plbi)
{
if (bExact && (keylen != strlen (plbi->key))) {
plbi = plbi->next;
start ++;
continue;
}
if (pData->str_cmp (key, plbi->key, keylen) == 0)
return start;
plbi = plbi->next;
start ++;
}
return LB_ERR;
}
static void lstOnDrawSListBoxItems (HWND hWnd, HDC hdc,
PLISTBOXDATA pData, int width)
{
DWORD dwStyle = GetWindowStyle (hWnd);
PLISTBOXITEM plbi;
PCONTROL pWin;
RECT rect;
int i;
int x = 0, y = 0;
int checkmark_height; /** checkmark bmp height */
int checkmark_status; /** checkmark bmp status */
gal_pixel gp_hilite_bgc;
gal_pixel gp_hilite_fgc;
gal_pixel gp_sign_bgc;
gal_pixel gp_sign_fgc;
gal_pixel gp_disabled_bgc;
gal_pixel gp_disabled_fgc;
gal_pixel gp_normal_bgc;
gal_pixel gp_normal_fgc;
gal_pixel gp_bkcolor;
pWin = (PCONTROL) hWnd;
if (NULL == pWin) return;
if (!pWin->we_rdr)
{
_WRN_PRINTF ("NULL LFRDR.");
return;
}
checkmark_height = CheckMarkHeight (pData->itemHeight);
plbi = lstGetItem (pData, pData->itemTop);
gp_hilite_bgc = GetWindowElementPixelEx
(hWnd, hdc, WE_BGC_HIGHLIGHT_ITEM);
gp_hilite_fgc = GetWindowElementPixelEx
(hWnd, hdc, WE_FGC_HIGHLIGHT_ITEM);
gp_sign_bgc = GetWindowElementPixelEx
(hWnd, hdc, WE_BGC_SIGNIFICANT_ITEM);
gp_sign_fgc = GetWindowElementPixelEx
(hWnd, hdc, WE_FGC_SIGNIFICANT_ITEM);
gp_disabled_bgc = GetWindowElementPixelEx
(hWnd, hdc, WE_BGC_DISABLED_ITEM);
gp_disabled_fgc = GetWindowElementPixelEx
(hWnd, hdc, WE_FGC_DISABLED_ITEM);
gp_normal_bgc = GetWindowElementPixelEx
(hWnd, hdc, WE_BGC_WINDOW);
gp_normal_fgc = GetWindowElementPixelEx
(hWnd, hdc, WE_FGC_WINDOW);
(void)SetBkMode (hdc, BM_TRANSPARENT);
gp_bkcolor = GetBkColor (hdc);
for (i = 0; plbi && i < (pData->itemVisibles + 1); i++) {
rect.left = 0;
rect.top = y;
rect.right = width;
rect.bottom = rect.top + pData->itemHeight;
if (plbi->dwFlags & LBIF_DISABLE) {
SetBkColor (hdc, gp_disabled_fgc);
SetTextColor (hdc, gp_disabled_fgc);
SetBrushColor (hdc, gp_disabled_bgc);
/** render disabled item */
pWin->we_rdr->draw_disabled_item (hWnd, hdc, &rect,
GetWindowElementAttr (hWnd, WE_BGC_DISABLED_ITEM));
}
else if (plbi->dwFlags & LBIF_SELECTED) {
SetBkColor (hdc, gp_hilite_bgc);
SetTextColor (hdc, gp_hilite_fgc);
SetBrushColor (hdc, gp_hilite_bgc);
/** render hilited item */
pWin->we_rdr->draw_hilite_item (hWnd, hdc, &rect,
GetWindowElementAttr (hWnd, WE_BGC_HIGHLIGHT_ITEM));
}
else {
//SetBkColor (hdc, gp_normal_fgc);
SetTextColor (hdc, gp_normal_fgc);
SetBrushColor (hdc, gp_normal_bgc);
/** render normal item */
if (!(pWin->dwExStyle & WS_EX_TRANSPARENT))
pWin->we_rdr->draw_normal_item (hWnd, hdc, &rect,
GetWindowElementAttr (hWnd, WE_BGC_WINDOW));
}
/** render significant item */
if ( (plbi->dwFlags & LBIF_SIGNIFICANT) &&
!(plbi->dwFlags & LBIF_DISABLE)
) {
SetBkColor (hdc, gp_sign_bgc);
SetTextColor (hdc, gp_sign_fgc);
SetBrushColor (hdc, gp_sign_bgc);
pWin->we_rdr->draw_significant_item (hWnd, hdc, &rect,
GetWindowElementAttr (hWnd, WE_BGC_SIGNIFICANT_ITEM));
}
if (pData->lst_font != INV_LOGFONT) {
if (plbi->dwFlags & LBIF_BOLDSTYLE)
pData->lst_font->style |= FS_WEIGHT_BOLD;
else
pData->lst_font->style &= ~FS_WEIGHT_BOLD;
}
x = LST_INTER_BMPTEXT - pData->itemLeft;
if (dwStyle & LBS_CHECKBOX) {
checkmark_status = LFRDR_MARK_HAVESHELL;
rect.left = x;
rect.top = y + ((pData->itemHeight - checkmark_height) >> 1);
rect.right = x + checkmark_height;
rect.bottom = rect.top + checkmark_height;
if (plbi->dwFlags & LBIF_CHECKED)
checkmark_status |= LFRDR_MARK_ALL_SELECTED;
else if (plbi->dwFlags & LBIF_PARTCHECKED)
checkmark_status |= LFRDR_MARK_HALF_SELECTED;
gp_bkcolor = SetBkColor (hdc, gp_bkcolor);
pWin->we_rdr->draw_checkmark (hdc, &rect,
GetWindowElementAttr (hWnd, WE_FGC_WINDOW),
checkmark_status);
gp_bkcolor = SetBkColor (hdc, gp_bkcolor);
x += checkmark_height + LST_INTER_BMPTEXT;
}
if (dwStyle & LBS_USEICON && plbi->dwImage) {
if (plbi->dwFlags & LBIF_USEBITMAP) {
FillBoxWithBitmap (hdc, x, y, 0, pData->itemHeight,
(PBITMAP) plbi->dwImage);
x += ((PBITMAP)(plbi->dwImage))->bmWidth;
}
else {
int width;
DrawIcon (hdc, x, y, 0, pData->itemHeight,
(HICON) plbi->dwImage);
GetIconSize ((HICON) plbi->dwImage, &width, NULL);
x += width;
}
x += LST_INTER_BMPTEXT;
}
TextOut (hdc, x, y + ((pData->itemHeight - GetCurFont (hdc)->size) >> 1), plbi->key);
y += pData->itemHeight;
plbi = plbi->next;
}
SetBkColor (hdc, gp_bkcolor);
}
static int lstSelectItem (HWND hwnd, PLISTBOXDATA pData, int newSel)
{
PLISTBOXITEM plbi, newItem;
int index;
newItem = lstGetItem (pData, newSel);
if (newItem->dwFlags & LBIF_DISABLE)
return newSel;
#ifdef _DEBUG
if (!newItem)
_WRN_PRINTF ("return value of lstGetItem is NULL.");
#endif
if (GetWindowStyle (hwnd) & LBS_MULTIPLESEL) {
newItem->dwFlags ^= LBIF_SELECTED;
return newSel;
}
index = 0;
plbi = pData->head;
while (plbi) {
if (plbi->dwFlags & LBIF_SELECTED) {
if (index != newSel) {
plbi->dwFlags &= ~LBIF_SELECTED;
newItem->dwFlags |= LBIF_SELECTED;
return index;
}
break;
}
plbi = plbi->next;
index ++;
}
newItem->dwFlags |= LBIF_SELECTED;
return pData->itemHilighted;
}
static int lstCancelSelected (HWND hwnd, PLISTBOXDATA pData)
{
PLISTBOXITEM plbi;
int index;
index = 0;
plbi = pData->head;
while (plbi) {
if (plbi->dwFlags & LBIF_SELECTED) {
RECT rc;
plbi->dwFlags &= ~LBIF_SELECTED;
lstGetItemsRect (pData, index, index, &rc);
InvalidateRect (hwnd, &rc, TRUE);
return index;
}
plbi = plbi->next;
index ++;
}
return -1;
}
static void lstDrawFocusRect (HWND hwnd, HDC hdc, PLISTBOXDATA pData)
{
RECT rc;
PCONTROL pWin;
DWORD light_dword;
pWin = (PCONTROL) hwnd;
if (!pWin || !pWin->we_rdr) return;
if (pData->itemHilighted < pData->itemTop
|| pData->itemHilighted > (pData->itemTop + pData->itemVisibles))
return;
if (pData->dwFlags & LBF_FOCUS) {
GetClientRect (hwnd, &rc);
lstGetItemsRect (pData,
pData->itemHilighted, pData->itemHilighted,
&rc);
InflateRect (&rc, -1, -1);
light_dword = pWin->we_rdr->calc_3dbox_color
(GetWindowElementAttr (hwnd, WE_BGC_WINDOW),
LFRDR_3DBOX_COLOR_LIGHTEST);
pWin->we_rdr->draw_focus_frame (hdc, &rc, light_dword);
}
}
static void lstCalcParams (const RECT* rcClient, PLISTBOXDATA pData)
{
pData->itemVisibles = (RECTHP (rcClient)) / pData->itemHeight;
pData->itemWidth = RECTWP(rcClient);
}
static void change_scrollbar (HWND hwnd, PLISTBOXDATA pData, BOOL bShow, int iSBar)
{
if (pData->sbPolicy == SB_POLICY_ALWAYS) {
if (iSBar != SB_VERT)
EnableScrollBar (hwnd, iSBar, bShow);
}
else if (pData->sbPolicy == SB_POLICY_AUTOMATIC)
ShowScrollBar (hwnd, iSBar, bShow);
}
static void lstSetVScrollInfo (HWND hwnd, PLISTBOXDATA pData, BOOL fRedraw)
{
SCROLLINFO si={0};
if (pData->sbPolicy == SB_POLICY_NEVER) {
ShowScrollBar (hwnd, SB_VERT, FALSE);
return;
}
if (pData->itemVisibles >= pData->itemCount) {
SetScrollPos (hwnd, SB_VERT, 0);
if (pData->sbPolicy == SB_POLICY_ALWAYS) {
si.fMask = SIF_PAGE;
si.nMax = 1;
si.nMin = 0;
si.nPage = 0;
SetScrollInfo (hwnd, SB_VERT, &si, fRedraw);
}
change_scrollbar (hwnd, pData, FALSE, SB_VERT);
return;
}
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMax = pData->itemCount*pData->itemHeight - 1;
if (si.nMax < 0) si.nMax = 0; /*when Init. itemcount is 0*/
si.nMin = 0;
si.nPage = MIN (pData->itemVisibles, pData->itemCount)*pData->itemHeight;
si.nPos = pData->itemTop*pData->itemHeight;
SetScrollInfo (hwnd, SB_VERT, &si, fRedraw);
EnableScrollBar (hwnd, SB_VERT, TRUE);
change_scrollbar (hwnd, pData, TRUE, SB_VERT);
}
static void lstSetHScrollInfo (HWND hwnd, PLISTBOXDATA pData, BOOL fRedraw)
{
SCROLLINFO si={0};
if (pData->sbPolicy == SB_POLICY_NEVER) {
ShowScrollBar (hwnd, SB_HORZ, FALSE);
return;
}
if (pData->itemWidth >= pData->itemMaxWidth) {
SetScrollPos (hwnd, SB_HORZ, 0);
change_scrollbar (hwnd, pData, FALSE, SB_HORZ);
return;
}
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMax = pData->itemMaxWidth;
si.nMin = 0;
si.nPage = pData->itemWidth;
si.nPos = pData->itemLeft;
SetScrollInfo (hwnd, SB_HORZ, &si, fRedraw);
EnableScrollBar (hwnd, SB_HORZ, TRUE);
change_scrollbar (hwnd, pData, TRUE, SB_HORZ);
}
static void lstSetScrollInfo (HWND hwnd, PLISTBOXDATA pData, BOOL fRedraw)
{
lstSetVScrollInfo (hwnd, pData, fRedraw);
lstSetHScrollInfo (hwnd, pData, fRedraw);
}
static LRESULT ListboxCtrlProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PCONTROL pCtrl;
PLISTBOXDATA pData;
DWORD dwStyle;
#if _USE_FIXSTR
int len;
#endif
pCtrl = gui_Control(hwnd);
dwStyle = pCtrl->dwStyle;
switch (message)
{
case MSG_CREATE:
pData = (LISTBOXDATA*) calloc (1, sizeof(LISTBOXDATA));
if (pData == NULL)
return -1;
pData->lst_font = CreateLogFontIndirect(
(PLOGFONT)GetWindowElementAttr (hwnd, WE_FONT_MENU));
pCtrl->dwAddData2 = (DWORD)pData;
if (!lstInitListBoxData (hwnd, pCtrl, pData, DEF_LB_BUFFER_LEN)) {
free (pData);
return -1;
}
lstSetScrollInfo (hwnd, pData, FALSE);
break;
case MSG_SIZECHANGED:
{
int oldvisible, oldwidth;
const RECT *rc = (const RECT*)lParam;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
oldvisible = pData->itemVisibles;
oldwidth = pData->itemWidth;
/*from mpeer data is abnormal*/
if ( (rc->left >= rc->right) ||
(rc->top >= rc->bottom))
break;
lstCalcParams (rc, pData);
if (oldvisible != pData->itemVisibles ||
oldwidth != pData->itemWidth )
lstSetScrollInfo (hwnd, pData, FALSE);
break;
}
case MSG_DESTROY:
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if(pData->lst_font != INV_LOGFONT)
DestroyLogFont(pData->lst_font);
lstListBoxCleanUp (pData);
free (pData);
break;
case LB_SETSTRCMPFUNC:
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->itemCount == 0 && lParam) {
pData->str_cmp = (STRCMP)lParam;
return LB_OKAY;
}
return LB_ERR;
case LB_RESETCONTENT:
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
lstResetListBoxContent (pData);
lstSetScrollInfo (hwnd, pData, TRUE);
InvalidateRect (hwnd, NULL, TRUE);
return 0;
case LB_ADDSTRING:
case LB_INSERTSTRING:
{
char* string = NULL;
PLISTBOXITEMINFO plbii = NULL;
PLISTBOXITEM newItem;
int itemWidth;
int pos;
if (dwStyle & LBS_CHECKBOX || dwStyle & LBS_USEICON) {
plbii = (PLISTBOXITEMINFO)lParam;
if (!plbii)
return LB_ERR;
string = plbii->string;
}
else {
string = (char*)lParam;
if (string == NULL || string [0] == '\0')
return LB_ERR;
}
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
newItem = lstAllocItem (pData);
if (!newItem) {
if (dwStyle & LBS_NOTIFY)
NotifyParent (hwnd, pCtrl->id, LBN_ERRSPACE);
return LB_ERRSPACE;
}
#if _USE_FIXSTR
len = strlen (string);
newItem->key = FixStrAlloc (len);
if (!newItem->key) {
lstFreeItem (pData, newItem);
return LB_ERRSPACE;
}
if (len > 0)
strcpy (newItem->key, string);
#else
newItem->key = strdup (string);
if (!newItem->key) {
lstFreeItem (pData, newItem);
return LB_ERRSPACE;
}
#endif
newItem->dwFlags = LBIF_NORMAL;
if (plbii) {
if (plbii->cmFlag & CMFLAG_CHECKED)
newItem->dwFlags |= LBIF_CHECKED;
else if (plbii->cmFlag & CMFLAG_PARTCHECKED)
newItem->dwFlags |= LBIF_PARTCHECKED;
if (plbii->cmFlag & IMGFLAG_BITMAP)
newItem->dwFlags |= LBIF_USEBITMAP;
if (plbii->cmFlag & LBIS_SIGNIFICANT)
newItem->dwFlags |= LBIF_SIGNIFICANT;
if (dwStyle & LBS_USEICON)
newItem->dwImage = (DWORD)plbii->hIcon;
else
newItem->dwImage = 0L;
}
newItem->dwAddData = 0L;
if (message == LB_ADDSTRING)
pos = lstAddNewItem (dwStyle, pData, newItem, -1);
else
pos = lstAddNewItem (dwStyle, pData, newItem, (int)wParam);
itemWidth = lstGetItemWidth (hwnd, newItem, pData->itemHeight);
if (pData->itemMaxWidth < itemWidth)
pData->itemMaxWidth = itemWidth;
lstInvalidateUnderItem (hwnd, pData, pos);
lstSetScrollInfo (hwnd, pData, TRUE);
return pos;
}
case LB_DELETESTRING:
{
PLISTBOXITEM removed;
int delete;
delete = (int)wParam;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
removed = lstRemoveItem (pData, &delete);
if (removed) {
int width;
width = lstGetItemWidth (hwnd, removed, pData->itemHeight);
if (width >= pData->itemMaxWidth)
pData->itemMaxWidth = lstGetMaxWidth(hwnd, pData);
#if _USE_FIXSTR
FreeFixStr (removed->key);
#else
free (removed->key);
#endif
lstFreeItem (pData, removed);
pData->itemCount --;
if (pData->itemTop != 0
&& pData->itemCount <= pData->itemVisibles) {
pData->itemTop = 0;
InvalidateRect (hwnd, NULL, TRUE);
}
else {
lstInvalidateUnderItem (hwnd, pData, delete);
if (delete <= pData->itemTop) {
pData->itemTop --;
if (pData->itemTop < 0)
pData->itemTop = 0;
}
}
if (pData->itemHilighted >= pData->itemCount) {
pData->itemHilighted = pData->itemCount - 1;
if (pData->itemHilighted < 0)
pData->itemHilighted = 0;
}
if (pData->itemHilighted < pData->itemTop)
pData->itemHilighted = pData->itemTop;
if (pData->itemHilighted > ITEM_BOTTOM (pData))
pData->itemHilighted = ITEM_BOTTOM (pData);
pData->selItem = pData->itemHilighted;
lstSetScrollInfo (hwnd, pData, TRUE);
return LB_OKAY;
}
return LB_ERR;
}
case LB_FINDSTRING:
if( *(char*)lParam == '\0' )
return LB_ERR;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
return lstFindItem(pData, (int)wParam, (char*)lParam, FALSE);
case LB_FINDSTRINGEXACT:
if( *(char*)lParam == '\0' )
return LB_ERR;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
return lstFindItem(pData, (int)wParam, (char*)lParam, TRUE);
case LB_SETTOPINDEX:
{
int newTop = (int) wParam;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (newTop <0)
newTop = 0;
else if (newTop > pData->itemCount - pData->itemVisibles)
newTop = pData->itemCount - pData->itemVisibles;
if (pData->itemTop != newTop) {
pData->itemTop = newTop;
if (pData->itemHilighted < pData->itemTop)
pData->itemHilighted = pData->itemTop;
if (pData->itemHilighted > ITEM_BOTTOM (pData))
pData->itemHilighted = ITEM_BOTTOM (pData);
pData->selItem = pData->itemHilighted;
lstSetScrollInfo (hwnd, pData, TRUE);
InvalidateRect (hwnd, NULL, TRUE);
}
return 0;
}
case LB_SETCURSEL:
case LB_SETCARETINDEX:
{
int new = (int)wParam;
int old, newTop;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (new < 0 || new > pData->itemCount - 1) {
if (dwStyle & LBS_MULTIPLESEL)
return LB_ERR;
else
return lstCancelSelected (hwnd, pData);
}
old = pData->itemHilighted;
if (new >= 0 && new != old) {
if (pData->itemCount - new >= pData->itemVisibles)
newTop = new;
else
newTop = MAX (pData->itemCount - pData->itemVisibles, 0);
pData->itemTop = newTop;
pData->itemHilighted = new;
pData->selItem = pData->itemHilighted;
lstSetScrollInfo (hwnd, pData, TRUE);
}
if (!(dwStyle & LBS_MULTIPLESEL))
{
PLISTBOXITEM newItem = lstGetItem (pData, new);
if (newItem->dwFlags & LBIF_DISABLE) {
lstCancelSelected(hwnd, pData);
return old;
}
else
lstSelectItem (hwnd, pData, new);
}
InvalidateRect (hwnd, NULL, TRUE);
return old;
}
case LB_GETCOUNT:
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
return pData->itemCount;
case LB_GETCURSEL:
{
PLISTBOXITEM plbi;
int index = 0;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (dwStyle & LBS_MULTIPLESEL)
return pData->itemHilighted;
plbi = pData->head;
while (plbi) {
if (plbi->dwFlags & LBIF_SELECTED)
return index;
index ++;
plbi = plbi->next;
}
return LB_ERR;
}
case LB_GETSELCOUNT:
{
int nSel;
PLISTBOXITEM plbi;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
nSel = 0;
plbi = pData->head;
while (plbi) {
if (plbi->dwFlags & LBIF_SELECTED)
nSel ++;
plbi = plbi->next;
}
return nSel;
}
case LB_GETTOPINDEX:
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
return pData->itemTop;
case LB_GETCARETINDEX:
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
return pData->itemHilighted;
case LB_GETTEXTLEN:
{
PLISTBOXITEM plbi;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
plbi = lstGetItem (pData, (int)wParam);
if (plbi)
return strlen (plbi->key);
return LB_ERR;
}
case LB_GETTEXT:
{
PLISTBOXITEM plbi;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
plbi = lstGetItem (pData, (int)wParam);
if (plbi) {
strcpy ((char*)lParam, plbi->key);
return LB_OKAY;
}
return LB_ERR;
}
case LB_SETTEXT:
{
PLISTBOXITEM plbi;
char* newStr;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
plbi = lstGetItem (pData, (int)wParam);
if (plbi) {
#if _USE_FIXSTR
len = strlen ((char*)lParam);
newStr = FixStrAlloc (len);
if (newStr) {
FreeFixStr (plbi->key);
plbi->key = newStr;
if (len > 0)
strcpy (plbi->key, (char*)lParam);
#else
newStr = strdup ((const char*)lParam);
if (newStr) {
free (plbi->key);
plbi->key = newStr;
#endif
lstInvalidateItem (hwnd, pData, (int)wParam);
}
else
return LB_ERR;
}
return LB_ERR;
}
case LB_GETITEMDATA:
{
PLISTBOXITEM plbi;
PLISTBOXITEMINFO plbii;
if (!(dwStyle & LBS_CHECKBOX || dwStyle & LBS_USEICON)) {
return LB_ERR;
}
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (!(plbi = lstGetItem (pData, (int)wParam)))
return LB_ERR;
plbii = (PLISTBOXITEMINFO)lParam;
if (!plbii)
return LB_ERR;
if (plbi->dwFlags & LBIF_CHECKED)
plbii->cmFlag = CMFLAG_CHECKED;
else if (plbi->dwFlags & LBIF_PARTCHECKED)
plbii->cmFlag = CMFLAG_PARTCHECKED;
else
plbii->cmFlag = CMFLAG_BLANK;
if (plbi->dwFlags & LBIF_USEBITMAP)
plbii->cmFlag |= IMGFLAG_BITMAP;
if (plbi->dwFlags & LBIF_SIGNIFICANT)
plbii->cmFlag |= LBIS_SIGNIFICANT;
plbii->hIcon = (HICON)plbi->dwImage;
return LB_OKAY;
}
case LB_SETITEMDATA:
{
PLISTBOXITEM plbi;
PLISTBOXITEMINFO plbii;
if (!(dwStyle & LBS_CHECKBOX || dwStyle & LBS_USEICON)) {
return LB_ERR;
}
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (!(plbi = lstGetItem (pData, (int)wParam)))
return LB_ERR;
plbii = (PLISTBOXITEMINFO)lParam;
if (!plbii)
return LB_ERR;
plbi->dwFlags &= ~LBIF_CHECKMARKMASK;
if (plbii->cmFlag & CMFLAG_CHECKED)
plbi->dwFlags |= LBIF_CHECKED;
if (plbii->cmFlag & CMFLAG_PARTCHECKED)
plbi->dwFlags |= LBIF_PARTCHECKED;
if (plbii->cmFlag & IMGFLAG_BITMAP)
plbi->dwFlags |= LBIF_USEBITMAP;
if (plbii->cmFlag & LBIS_SIGNIFICANT)
plbi->dwFlags |= LBIF_SIGNIFICANT;
if (dwStyle & LBS_USEICON)
plbi->dwImage = (DWORD)plbii->hIcon;
else
plbi->dwImage = 0;
if (plbii->string && plbii->string[0] != '\0') {
int oldlen = strlen (plbi->key);
int newlen = strlen (plbii->string);
if (oldlen >= newlen) {
memcpy (plbi->key, plbii->string, newlen);
plbi->key[newlen] = '\0';
}
else {
#if _USE_FIXSTR
FreeFixStr (plbi->key);
plbi->key = FixStrAlloc (newlen + 1);
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (!plbi->key) {
lstFreeItem (pData, plbi);
return LB_ERRSPACE;
}
strcpy (plbi->key, plbii->string);
plbi->key[newlen] = '\0';
#else
free (plbi->key);
plbi->key = strdup (plbii->string);
if (!plbi->key) {
lstFreeItem (pData, plbi);
return LB_ERRSPACE;
}
#endif
}
}
lstInvalidateItem (hwnd, pData, (int)wParam);
return LB_OKAY;
}
case LB_GETITEMADDDATA:
{
PLISTBOXITEM plbi;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (!(plbi = lstGetItem (pData, (int)wParam)))
return LB_ERR;
return plbi->dwAddData;
}
case LB_SETITEMADDDATA:
{
PLISTBOXITEM plbi;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (!(plbi = lstGetItem (pData, (int)wParam)))
return LB_ERR;
plbi->dwAddData = (DWORD)lParam;
return LB_OKAY;
}
case LB_GETCHECKMARK:
{
PLISTBOXITEM plbi;
if (!(dwStyle & LBS_CHECKBOX))
return LB_ERR;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (!(plbi = lstGetItem (pData, (int)wParam)))
return LB_ERR;
if (plbi->dwFlags & LBIF_CHECKED)
return CMFLAG_CHECKED;
if (plbi->dwFlags & LBIF_PARTCHECKED)
return CMFLAG_PARTCHECKED;
return CMFLAG_BLANK;
}
case LB_SETCHECKMARK:
{
PLISTBOXITEM plbi;
if (!(dwStyle & LBS_CHECKBOX))
return LB_ERR;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (!(plbi = lstGetItem (pData, (int)wParam)))
return LB_ERR;
plbi->dwFlags &= ~LBIF_CHECKMARKMASK;
switch (lParam) {
case CMFLAG_CHECKED:
plbi->dwFlags |= LBIF_CHECKED;
break;
case CMFLAG_PARTCHECKED:
plbi->dwFlags |= LBIF_PARTCHECKED;
break;
}
lstInvalidateItem (hwnd, pData, (int)wParam);
return LB_OKAY;
}
case LB_GETSELITEMS:
{
int nItem;
int nSel = 0;
int index = 0;
int* pInt;
PLISTBOXITEM plbi;
nItem = (int)wParam;
pInt = (int*)lParam;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
plbi = pData->head;
while (plbi) {
if (plbi->dwFlags & LBIF_SELECTED) {
if (pInt) {
if (nSel < nItem)
*(pInt + nSel) = index;
else
return nItem;
}
nSel ++;
}
plbi = plbi->next;
index ++;
}
return nSel;
}
break;
case LB_GETSEL:
{
PLISTBOXITEM plbi;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
plbi = lstGetItem (pData, (int)wParam);
if (plbi)
return plbi->dwFlags & LBIF_SELECTED;
return LB_ERR;
}
case LB_SETSEL:
{
PLISTBOXITEM plbi;
if (!(dwStyle & LBS_MULTIPLESEL))
return LB_ERR;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
plbi = lstGetItem (pData, (int)lParam);
if (plbi) {
if (plbi->dwFlags & LBIF_DISABLE)
return LB_ERR;
if (wParam == -1)
plbi->dwFlags ^= LBIF_SELECTED;
else if (wParam == 0)
plbi->dwFlags &= ~LBIF_SELECTED;
else
plbi->dwFlags |= LBIF_SELECTED;
lstInvalidateItem (hwnd, pData, (int)lParam);
}
return LB_ERR;
}
case LB_GETITEMHEIGHT:
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
return pData->itemHeight;
case LB_SETITEMHEIGHT:
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->itemHeight != LOWORD (lParam)) {
RECT rcClient;
pData->itemHeight = LOWORD (lParam);
if (pData->itemHeight < pCtrl->pLogFont->size)
pData->itemHeight = pCtrl->pLogFont->size;
GetClientRect (hwnd, &rcClient);
lstCalcParams (&rcClient, pData);
lstSetScrollInfo (hwnd, pData, TRUE);
InvalidateRect (hwnd, NULL, TRUE);
}
return pData->itemHeight;
case MSG_SETFOCUS:
{
RECT rc;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->dwFlags & LBF_FOCUS)
break;
pData->dwFlags |= LBF_FOCUS;
if (pData->itemHilighted >= 0) {
GetClientRect (hwnd, &rc);
lstGetItemsRect (pData, pData->itemHilighted, pData->itemHilighted, &rc);
InvalidateRect (hwnd, &rc, TRUE);
}
if (dwStyle & LBS_NOTIFY)
NotifyParent (hwnd, pCtrl->id, LBN_SETFOCUS);
break;
}
case MSG_KILLFOCUS:
{
RECT rc;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->itemHilighted >= 0) {
GetClientRect (hwnd, &rc);
lstGetItemsRect (pData, pData->itemHilighted, pData->itemHilighted, &rc);
InvalidateRect (hwnd, &rc, TRUE);
}
pData->dwFlags &= ~LBF_FOCUS;
if (dwStyle & LBS_NOTIFY)
{
NotifyParent (hwnd, pCtrl->id, LBN_SELCANCEL);
NotifyParent (hwnd, pCtrl->id, LBN_KILLFOCUS);
}
break;
}
case MSG_GETDLGCODE:
return DLGC_WANTARROWS | DLGC_WANTCHARS | DLGC_WANTENTER;
case MSG_FONTCHANGED:
{
RECT rcClient;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->itemHeight < pCtrl->pLogFont->size)
pData->itemHeight = pCtrl->pLogFont->size;
if (pData->lst_font != INV_LOGFONT) {
DestroyLogFont(pData->lst_font);
pData->lst_font = INV_LOGFONT;
}
pData->lst_font = CreateLogFontIndirect(GetWindowFont(hwnd));
GetClientRect (hwnd, &rcClient);
lstCalcParams (&rcClient, pData);
lstSetScrollInfo (hwnd, pData, TRUE);
InvalidateRect (hwnd, NULL, TRUE);
return 0;
}
case MSG_GETTEXTLENGTH:
case MSG_GETTEXT:
case MSG_SETTEXT:
return -1;
case MSG_PAINT:
{
RECT rc;
hdc = BeginPaint (hwnd);
GetClientRect (hwnd, &rc);
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->lst_font != INV_LOGFONT) {
SelectFont(hdc, pData->lst_font);
}
lstOnDrawSListBoxItems (hwnd, hdc, pData, RECTW (rc));
lstDrawFocusRect (hwnd, hdc, pData);
EndPaint (hwnd, hdc);
}
return 0;
case MSG_LBUTTONDBLCLK:
if (dwStyle & LBS_NOTIFY)
NotifyParent (hwnd, pCtrl->id, LBN_DBLCLK);
break;
case MSG_MOUSEMOVE:
case MSG_LBUTTONDOWN:
if ((dwStyle & LBS_MULTIPLESEL)
|| !(dwStyle & LBS_MOUSEFOLLOW))
break;
case MSG_LBUTTONUP:
{
int oldSel, mouseX, mouseY, hit;
RECT rcInv;
BOOL click_mark = FALSE;
PLISTBOXITEM hitItem;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->itemCount == 0)
break;
mouseX = LOSWORD (lParam);
mouseY = HISWORD (lParam);
hit = mouseY / pData->itemHeight;
hit += pData->itemTop;
if (hit >= pData->itemCount)
break;
hitItem = lstGetItem (pData, hit);
if (hitItem->dwFlags & LBIF_DISABLE)
break;
oldSel = lstSelectItem (hwnd, pData, hit);
if (dwStyle & LBS_CHECKBOX) {
if (mouseX > 0 && mouseX <
CheckMarkHeight (pData->itemHeight)) {
click_mark = TRUE;
if (message == MSG_LBUTTONUP && dwStyle & LBS_NOTIFY)
NotifyParent (hwnd, pCtrl->id, LBN_CLICKCHECKMARK);
if (dwStyle & LBS_AUTOCHECK) {
if (message == MSG_LBUTTONUP) {
PLISTBOXITEM plbi;
plbi = lstGetItem (pData, hit);
switch (plbi->dwFlags & LBIF_CHECKMARKMASK) {
case LBIF_CHECKED:
plbi->dwFlags &= ~LBIF_CHECKMARKMASK;
if (message == MSG_LBUTTONUP && dwStyle & LBS_NOTIFY)
NotifyParent (hwnd, pCtrl->id, LBN_SELCANCEL);
break;
default:
plbi->dwFlags &= ~LBIF_CHECKMARKMASK;
plbi->dwFlags |= LBIF_CHECKED;
break;
}
}
lstInvalidateItem (hwnd, pData, hit);
}
}
}
GetClientRect (hwnd, &rcInv);
if (oldSel >= 0) {
if (oldSel >= pData->itemTop
&& (oldSel <= pData->itemTop + pData->itemVisibles)) {
lstGetItemsRect (pData, oldSel, oldSel, &rcInv);
InvalidateRect (hwnd, &rcInv, TRUE);
}
}
lstGetItemsRect (pData, pData->itemHilighted, pData->itemHilighted, &rcInv);
InvalidateRect (hwnd, &rcInv, TRUE);
pData->itemHilighted = hit;
lstGetItemsRect (pData, hit, hit, &rcInv);
InvalidateRect (hwnd, &rcInv, TRUE);
lstSetScrollInfo (hwnd, pData, TRUE);
if ( message == MSG_LBUTTONUP && (dwStyle & LBS_NOTIFY) ) {
if (dwStyle & LBS_MOUSEFOLLOW) {
oldSel = pData->selItem;
pData->selItem = hit;
}
if (oldSel != hit)
NotifyParent (hwnd, pCtrl->id, LBN_SELCHANGE);
if (!click_mark)
NotifyParent (hwnd, pCtrl->id, LBN_CLICKED);
}
}
return 0;
case LB_GETITEMRECT:
{
RECT rcClient, *rcItem;
int index;
rcItem = (RECT*)lParam;
index = (int)wParam;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->itemCount == 0 || index > pData->itemCount)
return LB_ERR;
GetClientRect ( hwnd, &rcClient );
rcItem->right = rcClient.right;
rcItem->left = rcClient.left;
rcItem->top = (index-pData->itemTop)*pData->itemHeight;
rcItem->bottom = (index-pData->itemTop+1)*(pData->itemHeight);
return LB_OKAY;
}
case MSG_KEYDOWN:
{
int oldSel, newSel, newTop;
RECT rcInv;
PLISTBOXITEM pnewItem;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->itemCount == 0)
break;
newTop = pData->itemTop;
newSel = pData->itemHilighted;
switch (LOWORD (wParam))
{
case SCANCODE_HOME:
newSel = 0;
newTop = 0;
break;
case SCANCODE_END:
newSel = pData->itemCount - 1;
if (pData->itemCount > pData->itemVisibles)
newTop = pData->itemCount - pData->itemVisibles;
else
newTop = 0;
break;
case SCANCODE_CURSORBLOCKDOWN:
newSel ++;
if (newSel >= pData->itemCount)
return 0;
if (newSel > ITEM_BOTTOM (pData))
newTop ++;
#if 0
newTop = newSel-pData->itemVisibles+1; /* sure to newsel is at bottom */
#endif
break;
case SCANCODE_CURSORBLOCKUP:
newSel --;
if (newSel < 0)
return 0;
if (newSel < pData->itemTop)
newTop --;
#if 0
newTop = newSel;
#endif
break;
case SCANCODE_PAGEDOWN:
newSel += pData->itemVisibles;
if (newSel >= pData->itemCount)
newSel = pData->itemCount - 1;
if (pData->itemCount - newSel >= pData->itemVisibles)
newTop = newSel;
else
newTop = MAX (pData->itemCount-pData->itemVisibles, 0);
break;
case SCANCODE_PAGEUP:
newSel -= pData->itemVisibles;
if (newSel < 0)
newSel = 0;
newTop -= pData->itemVisibles;
if (newTop < 0)
newTop = 0;
break;
case SCANCODE_KEYPADENTER:
case SCANCODE_ENTER:
if (dwStyle & LBS_NOTIFY)
NotifyParent (hwnd, pCtrl->id, LBN_ENTER);
return 0;
default:
return 0;
}
GetClientRect (hwnd, &rcInv);
if (pData->itemHilighted != newSel) {
if (pData->itemTop != newTop) {
pData->itemTop = newTop;
pData->itemHilighted = newSel;
pData->selItem = pData->itemHilighted;
if (!(dwStyle & LBS_MULTIPLESEL)) {
pnewItem = lstGetItem(pData, newSel);
if(pnewItem->dwFlags&LBIF_DISABLE)
lstCancelSelected(hwnd, pData);
else
{
oldSel = lstSelectItem (hwnd, pData, newSel);
if ((dwStyle & LBS_NOTIFY) && (oldSel != newSel))
NotifyParent (hwnd, pCtrl->id, LBN_SELCHANGE);
}
}
InvalidateRect (hwnd, NULL, TRUE);
}
else {
if (!(dwStyle & LBS_MULTIPLESEL)) {
pnewItem = lstGetItem(pData, newSel);
if(pnewItem->dwFlags&LBIF_DISABLE) {
lstCancelSelected(hwnd, pData);
InvalidateRect(hwnd, NULL, TRUE);
}
else
{
oldSel = lstSelectItem (hwnd, pData, newSel);
if ((dwStyle & LBS_NOTIFY) && (oldSel != newSel))
NotifyParent (hwnd, pCtrl->id, LBN_SELCHANGE);
if (oldSel >= 0) {
if (oldSel >= pData->itemTop
&& oldSel <= (ITEM_BOTTOM (pData) + 1)) {
lstGetItemsRect (pData, oldSel, oldSel, &rcInv);
InvalidateRect (hwnd, &rcInv, TRUE);
}
}
}
if (newSel < newTop) {
pData->itemHilighted = newSel;
pData->selItem = pData->itemHilighted;
break;
}
pData->itemHilighted = newSel;
pData->selItem = pData->itemHilighted;
lstGetItemsRect (pData, newSel, newSel, &rcInv);
InvalidateRect (hwnd, &rcInv, TRUE);
}
else {
pnewItem = lstGetItem(pData, newSel);
if(pnewItem->dwFlags&LBIF_DISABLE)
break;
lstGetItemsRect (pData, pData->itemHilighted, pData->itemHilighted, &rcInv);
InvalidateRect (hwnd, &rcInv, TRUE);
pData->itemHilighted = newSel;
lstGetItemsRect (pData, newSel, newSel, &rcInv);
InvalidateRect (hwnd, &rcInv, TRUE);
}
}
lstSetScrollInfo (hwnd, pData, TRUE);
}
}
break;
case MSG_CHAR:
{
unsigned char head [2];
int index;
int newTop;
PLISTBOXITEM plbi;
if (HIBYTE (wParam))
break;
head [0] = LOBYTE (wParam);
head [1] = '\0';
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (head[0] == ' ') {
plbi = lstGetItem (pData, pData->itemHilighted);
if (plbi->dwFlags & LBIF_DISABLE)
break;
if (dwStyle & LBS_MULTIPLESEL) {
RECT rcInv;
GetClientRect (hwnd, &rcInv);
lstSelectItem (hwnd, pData, pData->itemHilighted);
lstGetItemsRect (pData,
pData->itemHilighted,
pData->itemHilighted,
&rcInv);
InvalidateRect (hwnd, &rcInv, TRUE);
}
else if (dwStyle & LBS_CHECKBOX) {
if (dwStyle & LBS_NOTIFY)
NotifyParent (hwnd, pCtrl->id, LBN_CLICKCHECKMARK);
if (dwStyle & LBS_AUTOCHECK) {
#if 0
PLISTBOXITEM plbi;
plbi = lstGetItem (pData, pData->itemHilighted);
#endif
switch (plbi->dwFlags & LBIF_CHECKMARKMASK) {
case LBIF_CHECKED:
plbi->dwFlags &= ~LBIF_CHECKMARKMASK;
if (dwStyle & LBS_NOTIFY)
NotifyParent (hwnd, pCtrl->id, LBN_SELCANCEL);
break;
default:
plbi->dwFlags &= ~LBIF_CHECKMARKMASK;
plbi->dwFlags |= LBIF_CHECKED;
break;
}
lstInvalidateItem (hwnd, pData, pData->itemHilighted);
}
}
break;
}
index = lstFindItem (pData, pData->itemHilighted + 1, (char*)head, FALSE);
if (index < 0) {
index = lstFindItem (pData, 0, (char*)head, FALSE);
}
if (index >= 0) {
if (pData->itemCount - index >= pData->itemVisibles)
newTop = index;
else
newTop = MAX (pData->itemCount - pData->itemVisibles, 0);
pData->itemTop = newTop;
pData->itemHilighted = index;
pData->selItem = pData->itemHilighted;
if (!(dwStyle & LBS_MULTIPLESEL)) {
plbi = lstGetItem(pData, index);
if(plbi->dwFlags & LBIF_DISABLE)
lstCancelSelected(hwnd, pData);
else {
int oldSel = lstSelectItem (hwnd, pData, index);
if ((dwStyle & LBS_NOTIFY) && (oldSel != index))
NotifyParent (hwnd, pCtrl->id, LBN_SELCHANGE);
}
}
InvalidateRect (hwnd, NULL, TRUE);
lstSetScrollInfo (hwnd, pData, TRUE);
}
}
break;
case MSG_VSCROLL:
{
int newTop;
int scrollHeight = 0;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
newTop = pData->itemTop;
switch(wParam)
{
case SB_LINEDOWN:
if (ITEM_BOTTOM (pData) < (pData->itemCount - 1)) {
newTop ++;
scrollHeight = -pData->itemHeight;
}
break;
case SB_LINEUP:
if (pData->itemTop > 0) {
newTop --;
scrollHeight = pData->itemHeight;
}
break;
case SB_PAGEDOWN:
if ((pData->itemTop + (pData->itemVisibles << 1)) <=
pData->itemCount)
newTop += pData->itemVisibles;
else
newTop = pData->itemCount - pData->itemVisibles;
if (newTop < 0)
return 0;
scrollHeight = -(newTop - pData->itemTop)
*pData->itemHeight;
break;
case SB_PAGEUP:
if (pData->itemTop >= pData->itemVisibles)
newTop -= pData->itemVisibles;
else
newTop = 0;
scrollHeight = (pData->itemTop - newTop)*pData->itemHeight;
break;
case SB_THUMBTRACK:
newTop = MIN ((int)lParam/pData->itemHeight,
(pData->itemCount - pData->itemVisibles));
scrollHeight = (pData->itemTop - newTop)*pData->itemHeight;
break;
}
if (scrollHeight) {
pData->itemTop = newTop;
lstSetScrollInfo (hwnd, pData, TRUE);
InvalidateRect (hwnd, NULL, TRUE);
return 0;
}
}
break;
case MSG_HSCROLL:
{
int newLeft;
int scrollWidth = 0;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
newLeft = pData->itemLeft;
switch (wParam)
{
case SB_LINERIGHT:
if (pData->itemMaxWidth > (pData->itemWidth + newLeft)) {
newLeft++;
scrollWidth = 1;
}
break;
case SB_LINELEFT:
if (newLeft > 0) {
newLeft--;
scrollWidth = 1;
}
break;
case SB_PAGELEFT:
if (newLeft >= pData->itemWidth) {
newLeft -= pData->itemWidth;
scrollWidth = 1;
}
else if (newLeft > 0) {
newLeft = 0;
scrollWidth = 1;
}
break;
case SB_PAGERIGHT:
if (newLeft + (pData->itemWidth << 1) <= pData->itemMaxWidth) {
newLeft += pData->itemWidth;
scrollWidth = 1;
}
else {
newLeft = pData->itemMaxWidth - pData->itemWidth;
scrollWidth = 1;
}
break;
case SB_THUMBTRACK:
newLeft = MIN((int)lParam, (pData->itemMaxWidth - pData->itemWidth));
scrollWidth = (pData->itemLeft - newLeft);
break;
}
if (scrollWidth) {
pData->itemLeft = newLeft;
lstSetScrollInfo (hwnd, pData, TRUE);
InvalidateRect (hwnd, NULL, TRUE);
return 0;
}
}
break;
case LB_SETITEMDISABLE:
{
PLISTBOXITEM item;
int index = (int)wParam;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->itemCount <= 0 || index < 0 || index > pData->itemCount)
return LB_ERR;
item = lstGetItem (pData, index);
if (lParam)
item->dwFlags |= LBIF_DISABLE;
else
item->dwFlags &=~LBIF_DISABLE;
return lstInvalidateUnderItem (hwnd, pData, index);
}
case LB_GETITEMDISABLE:
{
PLISTBOXITEM item;
int index = (int)wParam;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if (pData->itemCount <= 0 || index < 0 || index > pData->itemCount)
return LB_ERR;
item = lstGetItem (pData, index);
return item->dwFlags&LBIF_DISABLE;
}
break;
case LB_SETITEMBOLD:
{
PLISTBOXITEM item;
int index = (int) wParam;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
if(pData->lst_font == INV_LOGFONT)
return LB_ERR;
if (pData->itemCount <= 0 || index < 0 || index > pData->itemCount)
return LB_ERR;
item = lstGetItem (pData, index);
if (lParam)
item->dwFlags |= LBIF_BOLDSTYLE;
else
item->dwFlags &=~LBIF_BOLDSTYLE;
return lstInvalidateUnderItem (hwnd, pData, index);
}
case LB_MULTIADDITEM:
{
char* string = NULL;
PLISTBOXITEMINFO plbii = NULL;
char** stringarray = NULL;
PLISTBOXITEM newItem;
int itemWidth;
int start, pos = -1;
int i;
pData = (PLISTBOXDATA)pCtrl->dwAddData2;
start = pData->itemCount;
if (dwStyle & LBS_CHECKBOX || dwStyle & LBS_USEICON) {
plbii = (PLISTBOXITEMINFO)lParam;
if (!plbii)
return LB_ERR;
}
else {
stringarray = (char* *)lParam;
if (stringarray == NULL)
return LB_ERR;
}
for (i = 0; i < (int)wParam; i++)
{
if (dwStyle & LBS_CHECKBOX || dwStyle & LBS_USEICON)
string = plbii[i].string;
else
string = stringarray[i];
if(string == NULL || string[0] == '\0')
break;
newItem = lstAllocItem (pData);
if (!newItem) {
if (dwStyle & LBS_NOTIFY)
NotifyParent (hwnd, pCtrl->id, LBN_ERRSPACE);
return LB_ERRSPACE;
}
#if _USE_FIXSTR
len = strlen (string);
newItem->key = FixStrAlloc (len);
if (!newItem->key) {
lstFreeItem (pData, newItem);
return LB_ERRSPACE;
}
if (len > 0)
strcpy (newItem->key, string);
#else
newItem->key = strdup (string);
if (!newItem->key) {
lstFreeItem (pData, newItem);
return LB_ERRSPACE;
}
#endif
newItem->dwFlags = LBIF_NORMAL;
if (dwStyle & LBS_CHECKBOX || dwStyle & LBS_USEICON) {
if (plbii[i].cmFlag & CMFLAG_CHECKED)
newItem->dwFlags |= LBIF_CHECKED;
else if (plbii[i].cmFlag & CMFLAG_PARTCHECKED)
newItem->dwFlags |= LBIF_PARTCHECKED;
if (plbii[i].cmFlag & IMGFLAG_BITMAP)
newItem->dwFlags |= LBIF_USEBITMAP;
if (plbii[i].cmFlag & LBIS_SIGNIFICANT)
newItem->dwFlags |= LBIF_SIGNIFICANT;
if (dwStyle & LBS_USEICON)
newItem->dwImage = (DWORD)plbii[i].hIcon;
else
newItem->dwImage = 0L;
}
newItem->dwAddData = 0L;
pos = lstAddNewItem (dwStyle, pData, newItem, -1);
itemWidth = lstGetItemWidth (hwnd, newItem, pData->itemHeight);
if (pData->itemMaxWidth < itemWidth)
pData->itemMaxWidth = itemWidth;
}
lstInvalidateUnderMultiItem (hwnd, pData, start, pos);
lstSetScrollInfo (hwnd, pData, TRUE);
return pos;
}
default:
break;
}
return DefaultControlProc (hwnd, message, wParam, lParam);
}
#endif /* _MGCTRL_LISTBOX */