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

📄 xlistbox.cpp

📁 俄罗斯人开发的大名鼎鼎的Pocket Pc 阅读器haaliread的源代码,visual c
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*
 * Copyright (c) 2001,2002,2003 Mike Matsnev.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice immediately at the beginning of the file, without modification,
 *    this list of conditions, and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Absolutely no warranty of function or purpose is made by the author
 *    Mike Matsnev.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * $Id: XListBox.cpp,v 1.1.2.33 2004/10/21 14:53:27 mike Exp $
 * 
 */

#include <afxwin.h>
#include <afxcmn.h>

#include "resource.h"
#include "config.h"
#include "XListBox.h"

#define	DEFAULT_FONT_SIZE     11
#define	UICON_PAD	      1
#define	COLOR0		      RGB(0,0,0)
#define	COLOR1		      RGB(255,255,255)
#define	COLOR2		      RGB(251,242,233)
#define	COLOR3		      RGB(128,128,128)
#define	COLOR4		      RGB(255,255,255)
#define	COLOR5		      RGB(16,32,255)

#define	INDENT_PER_LEVEL      5

// string compare
#define	CmpI(s1,s2) \
    (::CompareString(LOCALE_USER_DEFAULT,NORM_IGNORECASE, \
    (s1),-1,(s2),-1)-2)

struct XLBItem {
  TCHAR	  *text1;
  TCHAR	  *text2;
  int	  icon_index;
  int	  level;
  int	  flags;
  LONG    user_data;
};

#define	XIF_COLLAPSED	1

struct XLB {
  HWND		  hWnd;

  int		  num_items;
  int		  max_items;
  XLBItem	  *items;

  int		  visible_items;
  XLBItem	  **vitems;

  int		  selection; // selection is I
  int		  top_offset;
  int		  scroll_page;

  int		  item_height;
  int		  line_height;

  HIMAGELIST	  icons;
  bool		  icons_shared;

  HFONT		  font;
  int		  dots_width;

  HIMAGELIST	  tree_icons;

  int		  flags;

  XLB_GetText	  gtf;
  void		  *gtdata;

  int		  uicon_w;
  int		  ticon_w;
};

#define	XLF_TREE  1

static bool   XLB_GrowList(XLB *x) {
  int	incr=x->num_items;

  if (incr<16)
    incr=16;
  if (incr>4096)
    incr=4096;

  int	newsize=x->num_items+incr;

  XLBItem   *newitems=(XLBItem*)realloc(x->items,newsize*sizeof(XLBItem));
  if (newitems==NULL)
    return false;

  x->items=newitems;

  XLBItem   **newvitems=(XLBItem**)realloc(x->vitems,newsize*sizeof(XLBItem*));
  if (newvitems==NULL)
    return false;

  x->vitems=newvitems;

  x->max_items=newsize;

  return true;
}

static void  XLB_UpdateVisibleItems(XLB *x) {
  XLBItem *ii=x->items;
  XLBItem *jj=ii+x->num_items;
  XLBItem **kk=x->vitems;

  int	  level = 0;
  int	  flags = 0;

  while (ii<jj) {
    if (ii->level > level)
      flags=XLF_TREE;

    level = ii->level;

    *kk++=ii;

    if (ii++->flags & XIF_COLLAPSED) {
      // skip all following children
      while (ii<jj && ii->level>level) {
	++ii;
	flags=XLF_TREE;
      }
    }
  }

  x->visible_items = kk - x->vitems;

  x->flags |= flags;
}

static int  XLB_GetIFromV(XLB *x,int v) {
  return x->vitems[v] - x->items;
}

static int  XLB_GetVFromI(XLB *x,int i) {
  int	    top=x->visible_items-1;
  int	    bottom=0;
  XLBItem   *ii=&x->items[i];
  XLBItem   **kk=x->vitems;

  while (bottom<=top) {
    int	  middle=(bottom+top)>>1;

    if (kk[middle]==ii)
      return middle;

    if (kk[middle]>ii)
      top=middle-1;
    else
      bottom=middle+1;
  }

  return -1;
}

static int  XLB_GetParent(XLB *x,int i) {
  XLBItem   *jj=x->items;
  XLBItem   *ii=jj+i;
  int	    level=ii->level;

  while (ii-->jj)
    if (ii->level < level)
      return ii - jj;

  return -1;
}

static void XLB_UpdateScrollbar(XLB *x);

static void XLB_Restore(XLB *x,int item) {
  bool	changed=false;

  for (;;) {
    item=XLB_GetParent(x,item);

    if (item<0)
      break;

    if (x->items[item].flags & XIF_COLLAPSED) {
      x->items[item].flags &= ~XIF_COLLAPSED;
      changed=true;
    }
  }

  if (changed)
    XLB_UpdateVisibleItems(x);
}

static bool XLB_HasChildren(XLB *x,int item) {
   return item < x->num_items-1 && x->items[item+1].level > x->items[item].level;
}

#define	SPC(x)	((x)==_T(' ') || (x)==_T('\t') || (x)==_T('\r') || (x)==_T('\n'))

static void XLB_PaintItem(XLB *x,int item,int vitem,HDC hDC,RECT& rc) {
  // cache an item pointer
  XLBItem   *ii=&x->items[item];

  // query text1 and text2 if they are NULL
  const TCHAR	  *text1=ii->text1;
  const TCHAR	  *text2=ii->text2;

  if (text1==NULL && x->gtf) {
    CString t(x->gtf(x->gtdata,0,item,ii->user_data));
    text1=ii->text1=_tcsdup(t);
  }

  if (text2==NULL && x->gtf) {
    CString t(x->gtf(x->gtdata,1,item,ii->user_data));
    text2=ii->text2=_tcsdup(t);
  }

  // set text and bg colors
  COLORREF  text1_color,text2_color,bk_color;
  if (item==x->selection)
    text1_color=text2_color=COLOR4;
  else {
    text1_color=COLOR0;
    text2_color=COLOR3;
  }
  bk_color=
    item==x->selection ? COLOR5 :
      vitem&1 ? COLOR1 : COLOR2;
  ::SetBkColor(hDC,bk_color);

  // calculate indentation and paint it
  int	    indent=ii->level*INDENT_PER_LEVEL;
  if (indent > (rc.right-rc.left)/2)
    indent=(rc.right-rc.left)/2;

  RECT rci=rc;
  rci.right=rci.left;

  if (indent)
    rci.right+=indent;

  if (x->flags & XLF_TREE)
    rci.right+=x->ticon_w;

  if (ii->icon_index>=0)
    rci.right+=x->uicon_w;

  if (rci.right!=rci.left)
    ::ExtTextOut(hDC,rci.left,rci.top,ETO_OPAQUE,&rci,NULL,0,NULL);

  // top and bottom text rectangles
  RECT	    rt,rb;
  rt=rc;
  rt.bottom=rt.top+x->line_height;
  rt.left+=indent;
  rb=rt;
  rb.top+=x->line_height;
  rb.bottom+=x->line_height;

  // paint a tree icon
  if (x->flags & XLF_TREE) { // tree mode
    // reduce text width
    rci=rc;
    rci.left=rt.left;
    rci.right=rci.left+x->ticon_w;

    rt.left+=x->ticon_w;
    rb.left+=x->ticon_w;

    // paint the icon itself
    ::ImageList_DrawEx(x->tree_icons,
      XLB_HasChildren(x,item) ? (ii->flags & XIF_COLLAPSED ? 0 : 1) : 2,
      hDC,
      rci.left,rci.top,0,0,
      bk_color,CLR_NONE,
      ILD_NORMAL);
  }

  // paint an icon
  if (ii->icon_index>=0) {
    // calc icon rectangle
    rci=rc;
    rci.left+=rt.left;
    rci.right=rci.left+x->uicon_w;

    // reduce text width by icon size
    rt.left+=x->uicon_w;
    rb.left+=x->uicon_w;

    // paint the icon itself
    ::ImageList_DrawEx(x->icons,
      ii->icon_index,
      hDC,
      rci.left+UICON_PAD,rci.top+UICON_PAD,0,0,
      bk_color,CLR_NONE,
      ILD_NORMAL);
  }

  // paint text2
  if (text2) {
    int	  tl=_tcslen(text2);

    // get text2 width
    SIZE    sz;
    ::GetTextExtentPoint(hDC,text2,tl,&sz);

    // calc text box
    rci=rb;

    // adjust bottom rectangle
    if (sz.cx<rb.right-rb.left)
      rb.right-=sz.cx;
    else
      rb.right=rb.left;
    rb.right-=5;

    if (rb.right<rb.left)
      rb.right=rb.left;

    rci.left=rb.right;

    // paint text
    ::SetTextColor(hDC,text2_color);
    ::ExtTextOut(hDC,rci.left+3,rci.top,ETO_OPAQUE|ETO_CLIPPED,&rci,text2,tl,NULL);
  }

  int	tl=text1 ? _tcslen(text1) : 0;

  // get size of top part
  SIZE	sz;
  int	top_len=0;
  ::GetTextExtentExPoint(hDC,text1,tl,rt.right-rt.left,&top_len,NULL,&sz);

  // do some simple word wrapping
  int	i;
  for (i=0;i<top_len;++i)
    if (text1[i]==_T('\n') || text1[i]==_T('\r'))
      break;
  top_len=i;
  if (top_len!=tl && !SPC(text1[top_len])) {
    while (i>0 && !SPC(text1[i-1]))
      --i;
    if (i>0)
      top_len=i;
  }

  // back off until spaces end
  while (top_len>0 && SPC(text1[top_len-1]))
    --top_len;

  // set text1 color
  ::SetTextColor(hDC,text1_color);

  // paint top part
  ::ExtTextOut(hDC,rt.left,rt.top,ETO_OPAQUE|ETO_CLIPPED,&rt,text1,top_len,NULL);

  // skip spaces here
  while (top_len<tl && SPC(text1[top_len]))
    ++top_len;

  // paint bottom part
  if (rb.right>rb.left) {
    // check size
    ::GetTextExtentExPoint(hDC,text1+top_len,tl-top_len,rb.right-rb.left,&i,NULL,&sz);
    if (i!=tl-top_len) { // overflow, will need to append "..."
      ::GetTextExtentExPoint(hDC,text1+top_len,tl-top_len,rb.right-rb.left-x->dots_width,&i,NULL,&sz);
      ::GetTextExtentPoint(hDC,text1+top_len,i,&sz);
    }
    ::ExtTextOut(hDC,rb.left,rb.top,ETO_OPAQUE|ETO_CLIPPED,&rb,text1+top_len,i,NULL);
    if (i!=tl-top_len)
      ::ExtTextOut(hDC,rb.left+sz.cx,rb.top,0,NULL,_T("..."),3,NULL);
  }
}

static void XLB_PaintAll(XLB *x,HDC hDC) {
  // load tree icons if we are in tree mode
  if (x->flags & XLF_TREE && !x->tree_icons) {
    x->tree_icons=::ImageList_LoadBitmap(
      ::AfxGetResourceHandle(),
      MAKEINTRESOURCE(IDB_TREE),
      13, // XXX hardcoded
      0,
      RGB(255,0,255));
    int   dummy;
    ::ImageList_GetIconSize(x->tree_icons,&x->ticon_w,&dummy);
  }

  // select our font
  HGDIOBJ   obj=NULL;
  if (x->font)
    obj=::SelectObject(hDC,x->font);

  // get client rect
  RECT	cli;
  ::GetClientRect(x->hWnd,&cli);

  // get update rect
  RECT	dirty;
  if (!::GetUpdateRect(x->hWnd,&dirty,FALSE))
    dirty=cli;

  // shift update rect to (0,0)
  dirty.top-=cli.top;
  dirty.bottom-=cli.top;
  dirty.left-=cli.left;
  dirty.right-=cli.left;

  // repaint all items from top to bottom
  int	top=(x->top_offset+dirty.top)/x->item_height;
  int	bottom=(x->top_offset+dirty.bottom-1)/x->item_height;
  cli.top+=top*x->item_height-x->top_offset;
  for (int i=top;i<=bottom;++i,cli.top+=x->item_height) {
    cli.bottom=cli.top+x->item_height;
    if (i>=0 && i<x->visible_items)
      XLB_PaintItem(x,XLB_GetIFromV(x,i),i,hDC,cli);
    else {
      // draw a blank rectangle
      ::SetBkColor(hDC,COLOR1);
      ::ExtTextOut(hDC,cli.left,cli.top,ETO_OPAQUE,&cli,NULL,0,NULL);
    }
  }

  // restore font
  if (obj)
    ::SelectObject(hDC,obj);
}

static void   XLB_InitFont(XLB *x,HDC hDC) {
  LOGFONT lf;

  memset(&lf,0,sizeof(lf));
  lf.lfHeight=-(DEFAULT_FONT_SIZE*GetDeviceCaps(hDC,LOGPIXELSY))/96;
  lf.lfWeight=FW_BOLD;
  lf.lfCharSet=ANSI_CHARSET;
  lf.lfOutPrecision=OUT_DEFAULT_PRECIS;
  lf.lfClipPrecision=CLIP_DEFAULT_PRECIS;
#ifdef _WIN32_WCE
  lf.lfQuality=CLEARTYPE_QUALITY;
#else
  lf.lfQuality=DEFAULT_QUALITY;
#endif
  _tcscpy(lf.lfFaceName,_T("Tahoma"));

  x->font=::CreateFontIndirect(&lf);

  TEXTMETRIC	tm;
  HGDIOBJ	obj=::SelectObject(hDC,x->font);
  ::GetTextMetrics(hDC,&tm);
  SIZE		sz;
  ::GetTextExtentPoint(hDC,_T("..."),3,&sz);
  ::SelectObject(hDC,obj);

  x->line_height=tm.tmAscent+tm.tmDescent;
  x->item_height=x->line_height*2;
  x->dots_width=sz.cx;
}

static void	XLB_ScrollTo(XLB *x,int top) {
  int	delta=x->top_offset - top;

  x->top_offset=top;

  ::SetScrollPos(x->hWnd,SB_VERT,top,TRUE);

  ::ScrollWindowEx(x->hWnd,0,delta,NULL,NULL,NULL,NULL,SW_INVALIDATE);
}

static void	XLB_EnsureVisible2(XLB *x,int item) {
  int	v=XLB_GetVFromI(x,item);

  int   cvis=x->scroll_page/x->item_height; // completely visible items
  if (cvis==0)
    cvis=1;

  int item_offset=v*x->item_height-x->top_offset;
  // if the item is not fully visible, then
  // scroll and repaint completely
  if (item_offset<0) {
    x->top_offset=v*x->item_height;
    ::SetScrollPos(x->hWnd,SB_VERT,x->top_offset,TRUE);
  } else if (item_offset+x->item_height>x->scroll_page) {
    x->top_offset=(v-cvis+1)*x->item_height;
    ::SetScrollPos(x->hWnd,SB_VERT,x->top_offset,TRUE);
  }
}

static LRESULT CALLBACK XLB_WndProc(HWND hWnd,UINT uMsg,
				    WPARAM wParam,LPARAM lParam)
{
  XLB	*x=(XLB*)::GetWindowLong(hWnd,0);
  int	i,j;
  HDC	hDC;

  switch (uMsg) {
  case WM_CREATE:
    x=(XLB*)malloc(sizeof(*x));
    if (x==NULL)
      return -1;
    memset(x,0,sizeof(*x));
    ::SetWindowLong(hWnd,0,(LONG)x);

    x->hWnd=hWnd;
    x->selection=-1;

    hDC=::GetDC(hWnd);
    XLB_InitFont(x,hDC);
    ::ReleaseDC(hWnd,hDC);

    break;

  case WM_DESTROY:
    if (x->icons && !x->icons_shared)
      ::ImageList_Destroy(x->icons);
    if (x->tree_icons)
      ::ImageList_Destroy(x->tree_icons);
    if (x->font)
      ::DeleteObject(x->font);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -