mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-02-08 20:03:04 +08:00
590 lines
15 KiB
C
590 lines
15 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/blog/minigui-licensing-policy/>.
|
|
*/
|
|
/*
|
|
** listmodel.c: List data model.
|
|
**
|
|
** Create date: 2004/10/09
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "common.h"
|
|
|
|
#ifdef _MGCTRL_SCROLLVIEW
|
|
#include "minigui.h"
|
|
#include "gdi.h"
|
|
#include "window.h"
|
|
#include "ctrl/ctrlhelper.h"
|
|
#include "cliprect.h"
|
|
#include "internals.h"
|
|
#include "ctrlclass.h"
|
|
|
|
#include "ctrlmisc.h"
|
|
#include "listmodel.h"
|
|
#include "scrolled.h"
|
|
|
|
/*
|
|
* mglist_get_sort_index : Get sort index of an item
|
|
*/
|
|
int mglist_get_sort_index (MgList *mglst, MgItem *newci, BOOL bAdd)
|
|
{
|
|
list_t *me;
|
|
MgItem *pci = NULL;
|
|
int i = 0;
|
|
|
|
mglist_for_each (me, mglst) {
|
|
pci = mglist_entry (me);
|
|
if (mglst->itemCmp ((HITEM)newci, (HITEM)pci) < 0) {
|
|
if (bAdd)
|
|
list_add_tail (&newci->list, &pci->list);
|
|
return i;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (bAdd)
|
|
list_add_tail (&newci->list, &mglst->queue);
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* mgitem_add : add an item to the list
|
|
*/
|
|
int mglist_add_item (MgList *mglst, MgItem *ci, MgItem* preitem,
|
|
MgItem* nextitem, int nItem, int *idx)
|
|
{
|
|
int ret;
|
|
|
|
ci->flags = CTST_NORMAL;
|
|
|
|
if (mglst->iop.initItem) {
|
|
if (mglst->iop.initItem (mglst->hLst, (HITEM)ci) <0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (mglst->flags & CF_AUTOSORT && mglst->itemCmp) {
|
|
ret = mglist_get_sort_index (mglst, ci, TRUE);
|
|
}
|
|
else {
|
|
if (preitem) {
|
|
list_add (&ci->list, &preitem->list);
|
|
ret = -1;
|
|
}
|
|
else if (nextitem) {
|
|
list_add_tail (&ci->list, &nextitem->list);
|
|
ret = -1;
|
|
}
|
|
else {
|
|
ret = list_add_by_index (&ci->list, &mglst->queue, nItem);
|
|
}
|
|
}
|
|
|
|
mglst->nItemNr ++;
|
|
|
|
if (idx)
|
|
*idx = ( ret<0 ? mglst->nItemNr-1 : ret);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mglist_del_item (MgList *mglst, MgItem* pci)
|
|
{
|
|
if (mglst->iop.destroyItem) {
|
|
mglst->iop.destroyItem (mglst->hLst, (HITEM)pci);
|
|
}
|
|
|
|
list_del (&pci->list);
|
|
mglst->nItemNr --;
|
|
|
|
if (pci == mglst->pItemHilighted)
|
|
mglst->pItemHilighted = NULL;
|
|
|
|
if (pci->flags & CTST_SELECTED)
|
|
list_del (&pci->sel_list);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mglist_move_item (MgList *mglst, MgItem* pci, MgItem* preitem)
|
|
{
|
|
list_del (&pci->list);
|
|
|
|
if (preitem)
|
|
list_add_tail(&pci->list, &preitem->list);
|
|
else
|
|
list_add_tail(&pci->list, &mglst->queue);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mglist_adjust_items_height (HWND hWnd, MgList *mglst, PSCRDATA pscrdata, int diff)
|
|
{
|
|
mglst->nTotalItemH += diff;
|
|
|
|
if (mglist_is_frozen(mglst))
|
|
return 0;
|
|
|
|
scrolled_set_cont_height (hWnd, pscrdata, mglst->nTotalItemH + 1);//+1! where?
|
|
scrolled_refresh_content (pscrdata);
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------- */
|
|
|
|
int mglist_init (MgList *mglst, HWND hWnd)
|
|
{
|
|
INIT_LIST_HEAD(&mglst->queue);
|
|
INIT_LIST_HEAD(&mglst->sel_queue);
|
|
|
|
mglst->nItemNr = 0;
|
|
mglst->pItemHilighted = NULL;
|
|
mglst->nTotalItemH = 0;
|
|
|
|
mglst->itemCmp = NULL;
|
|
mglst->hLst = hWnd;
|
|
|
|
mglst->flags = 0;
|
|
|
|
memset (&mglst->iop, 0, sizeof(mglst->iop));
|
|
|
|
return 0;
|
|
}
|
|
|
|
GET_ENTRY_BY_INDEX(mlGetItemByIndex, MgItem, list)
|
|
GET_ENTRY_INDEX(mlGetItemIndex, MgItem, list)
|
|
|
|
MgItem* mglist_getitem_byindex (MgList *mglst, int index)
|
|
{
|
|
return mlGetItemByIndex(&mglst->queue, index);
|
|
}
|
|
|
|
int mglist_get_item_index (MgList *mglst, MgItem *item)
|
|
{
|
|
return mlGetItemIndex(&mglst->queue, item);
|
|
}
|
|
|
|
MgItem* mglist_get_next_item (MgList *mglst, MgItem* hitem)
|
|
{
|
|
list_t *me = hitem ? hitem->list.next : mglst->queue.next;
|
|
return ( (me == &mglst->queue) ? 0 : mglist_entry(me) );
|
|
}
|
|
|
|
MgItem* mglist_get_prev_item (MgList *mglst, MgItem* hitem)
|
|
{
|
|
list_t *me = hitem ? hitem->list.prev : mglst->queue.prev;
|
|
return ( (me == &mglst->queue) ? 0 : mglist_entry(me) );
|
|
}
|
|
|
|
void mglist_hilight_item (MgList *mglst, MgItem* hitem)
|
|
{
|
|
if (hitem) {
|
|
MgItem* oldhitem;
|
|
|
|
oldhitem = mglst->pItemHilighted;
|
|
mglst->pItemHilighted = hitem;
|
|
mglist_select_item (mglst, hitem, TRUE);
|
|
mglist_refresh_item (mglst, (HITEM)oldhitem);
|
|
mglist_refresh_item (mglst, (HITEM)hitem);
|
|
}
|
|
}
|
|
|
|
DWORD mglist_get_item_adddata (HITEM hitem)
|
|
{
|
|
return hitem ? ((MgItem *)hitem)->addData : 0;
|
|
}
|
|
|
|
DWORD mglist_set_item_adddata (HITEM hitem, DWORD adddata)
|
|
{
|
|
DWORD old;
|
|
|
|
if (!hitem)
|
|
return 0;
|
|
|
|
old = ((MgItem *)hitem)->addData;
|
|
((MgItem *)hitem)->addData = adddata;
|
|
return old;
|
|
}
|
|
|
|
void mglist_freeze (HWND hWnd, MgList *mglst, BOOL lock)
|
|
{
|
|
if (lock) {
|
|
mglst->flags |= MGLIST_ST_FROZEN;
|
|
}
|
|
else {
|
|
PSCRDATA pscrdata = (PSCRDATA)GetWindowAdditionalData2(hWnd);
|
|
mglst->flags &= ~MGLIST_ST_FROZEN;
|
|
scrolled_set_cont_height (hWnd, pscrdata, mglst->nTotalItemH);
|
|
//FIXME
|
|
scrolled_refresh_content (pscrdata);
|
|
}
|
|
}
|
|
|
|
static int mglstSortItems (MgList *mglst, void* pfn, int fn_type)
|
|
{
|
|
MgItem *pci, *pci2;
|
|
list_t *me, *me2;
|
|
MGITEM_CMP pcmp = NULL;
|
|
MGITEM_CMP_EX pcmp_ex = NULL;
|
|
int ret;
|
|
|
|
if (fn_type == MG_CMP_TYPE_NORMAL) {
|
|
if ( !(pcmp = (MGITEM_CMP)pfn) )
|
|
return -1;
|
|
}
|
|
else if (fn_type == MG_CMP_TYPE_EX) {
|
|
if ( !(pcmp_ex = (MGITEM_CMP_EX)pfn) )
|
|
return -1;
|
|
}
|
|
else
|
|
return -1;
|
|
|
|
mglist_for_each (me, mglst) {
|
|
pci = mglist_entry (me);
|
|
pci2 = NULL;
|
|
ret = 0;
|
|
mglist_for_each (me2, mglst) {
|
|
if (me == me2)
|
|
break;
|
|
pci2 = mglist_entry (me2);
|
|
if (pcmp)
|
|
ret = pcmp ((HITEM)pci, (HITEM)pci2);
|
|
else
|
|
ret = pcmp_ex (mglst, (HITEM)pci, (HITEM)pci2);
|
|
if (ret < 0)
|
|
break;
|
|
}
|
|
if (ret < 0) {
|
|
me = me->prev;
|
|
mglist_move_item (mglst, pci, pci2);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mglist_sort_items (MgList *mglst, void* pfn, int fn_type)
|
|
{
|
|
int ret;
|
|
|
|
mglist_freeze (mglst->hLst, mglst, TRUE);
|
|
ret = mglstSortItems (mglst, pfn, fn_type);
|
|
mglist_freeze (mglst->hLst, mglst, FALSE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void mglist_refresh_item_ex (MgList *mglst, HITEM hitem, const RECT *rcInv)
|
|
{
|
|
RECT rcItem, rcTmp;
|
|
|
|
if (!hitem || mglst->flags & MGLIST_ST_FROZEN)
|
|
return;
|
|
|
|
mglst->iop.getRect (mglst->hLst, hitem, &rcItem, TRUE);
|
|
rcTmp = *rcInv;
|
|
OffsetRect (&rcTmp, rcItem.left, rcItem.top);
|
|
|
|
if (IntersectRect(&rcTmp, &rcItem, &rcTmp))
|
|
InvalidateRect (mglst->hLst, &rcTmp, TRUE);
|
|
}
|
|
|
|
void mglist_refresh_item (MgList *mglst, HITEM hitem)
|
|
{
|
|
RECT rcItem;
|
|
|
|
if (!hitem || mglst->flags & MGLIST_ST_FROZEN)
|
|
return;
|
|
|
|
mglst->iop.getRect (mglst->hLst, hitem, &rcItem, TRUE);
|
|
|
|
InvalidateRect (mglst->hLst, &rcItem, TRUE);
|
|
}
|
|
|
|
void mglist_redraw_item (MgList *mglst, HITEM hivi)
|
|
{
|
|
RECT rcItem;
|
|
HDC hdc = GetClientDC (mglst->hLst);
|
|
|
|
if (!hivi)
|
|
return;
|
|
|
|
mglst->iop.getRect (mglst->hLst, hivi, &rcItem, TRUE);
|
|
mglst->iop.drawItem (mglst->hLst, hivi, hdc, &rcItem);
|
|
|
|
ReleaseDC (hdc);
|
|
}
|
|
|
|
BOOL mglist_make_item_visible (MgList *mglst, PSCRDATA pscrdata, HITEM hitem)
|
|
{
|
|
int pos_x, pos_y;
|
|
RECT rcItem;
|
|
int ret;
|
|
|
|
if (!hitem) return FALSE;
|
|
|
|
ret = mglst->iop.getRect (mglst->hLst, hitem, &rcItem, FALSE);
|
|
if (ret < 0)
|
|
return FALSE;
|
|
else if (ret == 1) { /* make y pos visible */
|
|
pos_x = -1;
|
|
}
|
|
else { /* x/y all visible */
|
|
pos_x = (rcItem.left >= pscrdata->nContX) ? rcItem.right : rcItem.left;
|
|
}
|
|
|
|
pos_y = (rcItem.bottom >= (pscrdata->nContY + pscrdata->visibleHeight)) ? rcItem.bottom : rcItem.top;
|
|
|
|
return scrolled_make_pos_visible (mglst->hLst, pscrdata, pos_x, pos_y);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
SVITEM_DRAWFUNC mglist_set_itemdraw (MgList *mglst, SVITEM_DRAWFUNC draw_func)
|
|
{
|
|
SVITEM_DRAWFUNC oldfn;
|
|
|
|
oldfn = mglst->iop.drawItem;
|
|
mglst->iop.drawItem = draw_func;
|
|
|
|
//FIXME, repaint ?
|
|
return oldfn;
|
|
}
|
|
|
|
void mglist_set_itemops (MgList *mglst, PMGITEMOPS iop)
|
|
{
|
|
mglst->iop = *iop;
|
|
}
|
|
|
|
int DefaultItemViewProc (HWND hWnd, int message, WPARAM wParam, LPARAM lParam,
|
|
PSCRDATA pscrdata, MgList *mglst)
|
|
{
|
|
switch (message) {
|
|
|
|
case MSG_RBUTTONDOWN:
|
|
case MSG_LBUTTONDOWN:
|
|
{
|
|
int mouseY = HISWORD (lParam);
|
|
int mouseX = LOSWORD (lParam);
|
|
int nItem;
|
|
RECT rcVis;
|
|
MgItem *hitem;
|
|
|
|
if ( !mglst->iop.isInItem )
|
|
break;
|
|
scrolled_get_visible_rect (pscrdata, &rcVis);
|
|
/* not in the visible area */
|
|
if (!PtInRect (&rcVis, mouseX, mouseY))
|
|
break;
|
|
scrolled_window_to_content (pscrdata, &mouseX, &mouseY);
|
|
if ( (nItem = mglst->iop.isInItem(mglst, mouseX, mouseY, &hitem, NULL)) >= 0 ) {
|
|
if ( !(GetWindowStyle(hWnd) & SVS_UPNOTIFY) )
|
|
NotifyParentEx (hWnd, GetDlgCtrlID(hWnd), SVN_CLICKED, (DWORD)hitem);
|
|
if (!mglist_is_item_hilight(mglst, hitem)) {
|
|
mglist_hilight_item (mglst, hitem);
|
|
mglist_make_item_visible (mglst, pscrdata, (HITEM)hitem);
|
|
NotifyParentEx (hWnd, GetDlgCtrlID(hWnd), SVN_SELCHANGED, (DWORD)hitem);
|
|
}
|
|
|
|
/* FIXME: should we auto-select the item? */
|
|
//mglist_select_item (mglst, hitem, TRUE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case MSG_FREEZECTRL:
|
|
mglist_freeze (hWnd, mglst, wParam);
|
|
return 0;
|
|
|
|
case SVM_SETITEMDRAW:
|
|
return (LRESULT)mglist_set_itemdraw (mglst, (SVITEM_DRAWFUNC)lParam);
|
|
|
|
case SVM_SETITEMINIT:
|
|
{
|
|
SVITEM_INITFUNC old_pfn;
|
|
old_pfn = mglst->iop.initItem;
|
|
mglst->iop.initItem = (SVITEM_INITFUNC)lParam;
|
|
return (LRESULT)old_pfn;
|
|
}
|
|
case SVM_SETITEMDESTROY:
|
|
{
|
|
SVITEM_DESTROYFUNC old_pfn;
|
|
old_pfn = mglst->iop.destroyItem;
|
|
mglst->iop.destroyItem = (SVITEM_DESTROYFUNC)lParam;
|
|
return (LRESULT)old_pfn;
|
|
}
|
|
|
|
case SVM_GETITEMADDDATA:
|
|
{
|
|
if (lParam)
|
|
return mglist_get_item_adddata((HITEM)lParam);
|
|
else {
|
|
HITEM hitem = (HITEM)mglist_getitem_byindex(mglst, wParam);
|
|
return mglist_get_item_adddata(hitem);
|
|
}
|
|
}
|
|
case SVM_SETITEMADDDATA:
|
|
{
|
|
HITEM hitem = (HITEM)mglist_getitem_byindex(mglst, wParam);
|
|
mglist_set_item_adddata (hitem, lParam);
|
|
return 0;
|
|
}
|
|
|
|
case SVM_REFRESHITEM:
|
|
{
|
|
if (lParam)
|
|
mglist_refresh_item (mglst, (HITEM)lParam);
|
|
else {
|
|
HITEM hitem = (HITEM) mglist_getitem_byindex(mglst, wParam);
|
|
mglist_refresh_item (mglst, hitem);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case SVM_SETITEMOPS:
|
|
#if 0
|
|
*((SVITEMOPS*)&mglst->iop) = *(SVITEMOPS*)lParam;
|
|
//mglist_set_itemops ((MgList *)&psvdata->svlist, (SVITEMOPS*)lParam);
|
|
#else
|
|
mglst->iop.initItem = ((SVITEMOPS*)lParam)->initItem;
|
|
mglst->iop.destroyItem = ((SVITEMOPS*)lParam)->destroyItem;
|
|
mglst->iop.drawItem = ((SVITEMOPS*)lParam)->drawItem;
|
|
#endif
|
|
return 0;
|
|
|
|
case SVM_GETCURSEL:
|
|
return mglist_get_item_index(mglst, mglist_get_hilighted_item(mglst));
|
|
|
|
case SVM_SETCURSEL:
|
|
{
|
|
HITEM hitem = (HITEM)mglist_getitem_byindex(mglst, wParam);
|
|
if (hitem) {
|
|
//FIXME, should we automatically refresh hilighted item ?
|
|
//mglist_set_hilighted_item (mglst, (MgItem *)hitem);
|
|
mglist_hilight_item (mglst, (MgItem *)hitem);
|
|
// make this item visible
|
|
if (lParam)
|
|
mglist_make_item_visible (mglst, pscrdata, hitem);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
case SVM_SHOWITEM:
|
|
{
|
|
if (lParam)
|
|
mglist_make_item_visible (mglst, pscrdata, (HITEM)lParam);
|
|
else {
|
|
HITEM hitem = (HITEM)mglist_getitem_byindex(mglst, wParam);
|
|
mglist_make_item_visible (mglst, pscrdata, hitem);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case SVM_SELECTITEM:
|
|
{
|
|
HITEM hitem = (HITEM)mglist_getitem_byindex(mglst, wParam);
|
|
if (hitem) {
|
|
mglist_select_item (mglst, (MgItem *)hitem, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case SVM_CHOOSEITEM:
|
|
{
|
|
HITEM hitem;
|
|
|
|
if (lParam)
|
|
hitem = (HITEM)lParam;
|
|
else
|
|
hitem = (HITEM)mglist_getitem_byindex(mglst, wParam);
|
|
|
|
if (!hitem)
|
|
return -1;
|
|
|
|
mglist_select_item (mglst, (MgItem *)hitem, TRUE);
|
|
mglist_make_item_visible (mglst, pscrdata, hitem);
|
|
return 0;
|
|
}
|
|
|
|
case SVM_SETITEMCMP:
|
|
{
|
|
SVITEM_CMP oldcmp = mglst->itemCmp;
|
|
mglst->itemCmp = (SVITEM_CMP)lParam;
|
|
return (LRESULT)oldcmp;
|
|
}
|
|
|
|
case SVM_SORTITEMS:
|
|
{
|
|
SVITEM_CMP pfn = (SVITEM_CMP)lParam;
|
|
|
|
if (mglist_sort_items (mglst, (void*)pfn, MG_CMP_TYPE_NORMAL) == 0) {
|
|
//FIXME, need ?
|
|
InvalidateRect (hWnd, NULL, TRUE);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case SVM_GETITEMCOUNT:
|
|
return mglist_get_item_count(mglst);
|
|
|
|
}/* end switch */
|
|
|
|
return DefaultScrolledProc (hWnd, message, wParam, lParam);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
#endif /* _MGCTRL_SCROLLVIEW */
|
|
|