winamp/Src/Wasabi/api/wnd/wndclass/listwnd.cpp

2151 lines
50 KiB
C++
Raw Normal View History

2024-09-24 12:54:57 +00:00
#pragma warning(disable:4355)
#include <precomp.h>
#include "listwnd.h"
#include <bfc/wasabi_std.h>
#include <api/wnd/usermsg.h>
#include <bfc/ptrlist.h>
#include <tataki/color/skinclr.h>
#include <api/wnd/notifmsg.h>
#include <bfc/assert.h>
#include <api/locales/xlatstr.h>
#include <api/wnd/accessible.h>
#include <bfc/string/StringW.h>
#include <new>
#define DEF_TEXT_SIZE 14 // Default text size
#define ITEMLIST_INC 4092
#define LISTWND_DRAG_TIMERID 0x8972
#define LISTWND_DRAG_MARGIN 12
#define LISTWND_DRAG_TIMERDELAY 100
#define DRAGSKIP_START 5
#define X_SHIFT 2
#define Y_SHIFT 2
#define DRAG_THRESHOLD 4
#define COLUMNS_DEFAULT_HEIGHT 12
#define COLUMNS_DEFAULT_WIDTH 96
#define COLUMNS_MARGIN 2
#define COLUMNS_RESIZE_THRESHOLD 4
#define COLUMNS_MIN_WIDTH 1
#define COLSEPHEIGHT 1
static SkinColor textColor(L"studio.list.text");// todo: have own color in skin.cpp
static SkinColor bgcolor(L"studio.list.background");// todo: have own color in skin.cpp
static SkinColor color_item_selected_fg(L"studio.list.item.selected.fg"); // RGB(0, 0, 128)
static SkinColor color_item_selected(L"studio.list.item.selected"); // RGB(0, 0, 128)
static SkinColor color_item_focused(L"studio.list.item.focused");// RGB(0, 128, 128)
static SkinColor color_item_focusrect(L"studio.list.item.focused");// RGB(0, 128, 128)
static SkinColor color_headers(L"studio.list.column.background");//RGB(0, 152, 208)
static SkinColor columnTextColor(L"studio.list.column.text");
static SkinColor columnSepColor(L"studio.list.column.separator");
typedef struct
{
wchar_t *label;
int column;
} listSubitemStruct;
class listItem
{
friend ListWnd;
public:
ListWnd *getList() const { return listwnd; }
protected:
listItem()
{
data = 0;
subitems = NULL;
listwnd = NULL;
}
~listItem()
{
if (subitems != NULL)
{
for (int i=0;i<subitems->getNumItems();i++)
{
listSubitemStruct *subitem = subitems->enumItem(i);
if (subitem->label)
FREE(subitem->label);
}
subitems->freeAll();
delete subitems;
}
}
void setList(ListWnd *newlist) { listwnd=newlist; }
StringW label;
LPARAM data;
PtrList<listSubitemStruct> *subitems;
ListWnd *listwnd;
AutoSkinBitmap icon;
};
class CompareListItem {
public:
static int compareItem(listItem *p1, listItem *p2);
};
int CompareListItem::compareItem(listItem *p1, listItem *p2) {
return p1->getList()->sortCompareItem(p1, p2);
}
XMLParamPair ListWnd::params[] = {
{LIST_ANTIALIAS, L"ANTIALIAS"},
{LIST_BACKGROUND, L"BACKGROUND"},
{LIST_TILE, L"TILE"},
{LIST_NOCOLHEADER, L"NOCOLHEADER"},
};
ListWnd::ListWnd()
: selItemList(this)
{
xuihandle = newXuiHandle();
CreateXMLParameters(xuihandle);
if (WASABI_API_SKIN->skin_getVersion() >= 1) {
textColor.setElementName(L"wasabi.list.text");
textColor.setColorGroup(L"");
bgcolor.setElementName(L"wasabi.list.background");
bgcolor.setColorGroup(L"");
color_item_selected_fg.setElementName(L"wasabi.list.text.selected");
color_item_selected_fg.setColorGroup(L"");
color_item_selected.setElementName(L"wasabi.list.text.selected.background");
color_item_selected.setColorGroup(L"");
color_headers.setElementName(L"wasabi.list.column.background");
color_headers.setColorGroup(L"");
columnTextColor.setElementName(L"wasabi.list.column.text");
columnTextColor.setColorGroup(L"");
columnSepColor.setElementName(L"wasabi.list.column.frame.bottom");
columnSepColor.setColorGroup(L"");
color_item_focused.setColorGroup(L"");
color_item_focusrect.setColorGroup(L"");
}
selectonupdown = 1;
setAutoSort(FALSE);
setOwnerDraw(FALSE);
setSortDirection(0);
setSortColumn(0);
lastcolsort=-1;
dragtimeron = 0;
dragskip = DRAGSKIP_START;
dragskipcount = 0;
item_invalidate_border = 0;
metrics_ok = FALSE;
setFontSize(DEF_TEXT_SIZE);
redraw = TRUE;
columnsHeight = COLUMNS_DEFAULT_HEIGHT;
lastItemFocused = NULL;
lastItemFocusedPos = -1;
lastAddedItem = NULL;
selectionStart = -1;
colresize = -1;
resizing_col = FALSE;
processbup = FALSE;
bdown = FALSE;
nodrag = FALSE;
showColumnsHeaders = TRUE;
rectselecting=0;
preventMultipleSelection = 0;
//CUT autoresizecol0 = 0;
wantautodeselect = 1;
registerAcceleratorSection(L"listwnd");
hoverselect = 0;
firstItemVisible = -1;
lastItemVisible = -1;
showicons = 0;
iconWidth = -1; // If it's still negative use itemHeight instead -- better user getIconWidth()
iconHeight = -1;
antialias=1;
hasUserBg = false;
}
void ListWnd::CreateXMLParameters(int master_handle)
{
//LISTWND_PARENT::CreateXMLParameters(master_handle);
int numParams = sizeof(params) / sizeof(params[0]);
hintNumberOfParams(xuihandle, numParams);
for (int i = 0;i < numParams;i++)
addParam(xuihandle, params[i], XUI_ATTRIBUTE_IMPLIED);
}
ListWnd::~ListWnd() {
deleteAllItems();
columnsList.deleteAll();
nodrag=FALSE;
}
int ListWnd::setXuiParam(int _xuihandle, int xmlattributeid, const wchar_t *xmlattributename, const wchar_t *value)
{
if (_xuihandle != xuihandle)
return ScrlBkgWnd::setXuiParam(_xuihandle, xmlattributeid, xmlattributename, value);
switch (xmlattributeid)
{
case LIST_ANTIALIAS:
antialias = WTOI(value);
break;
case LIST_BACKGROUND:
{
SkinBitmap __bmp(value);
if (!__bmp.isInvalid())
{
this->setBgBitmap(value);
hasUserBg = true;
}
else
{
this->setBgBitmap(L"wasabi.list.background");
hasUserBg = false;
}
}
this->invalidate();
break;
case LIST_TILE:
this->wantTileBg = WTOI(value)?1:0;
this->invalidate();
break;
case LIST_NOCOLHEADER:
setShowColumnsHeaders(!WTOI(value));
break;
default:
return 0;
}
return 1;
}
int ListWnd::onInit() {
LISTWND_PARENT::onInit();
if (!hasUserBg) setBgBitmap(L"wasabi.list.background");
setLineHeight(getItemHeight());
return 1;
}
int ListWnd::onPostOnInit() {
LISTWND_PARENT::onPostOnInit();
recalcHeaders();
return 1;
}
int ListWnd::onPaint(Canvas *canvas)
{
PaintCanvas paintcanvas;
PaintBltCanvas paintbcanvas;
if (canvas == NULL) {
if (needDoubleBuffer()) {
if (!paintbcanvas.beginPaintNC(this)) return 0;
canvas = &paintbcanvas;
} else {
if (!paintcanvas.beginPaint(this)) return 0;
canvas = &paintcanvas;
}
}
LISTWND_PARENT::onPaint(canvas);
RegionI clip;
canvas->getClipRgn(&clip);
api_region *orig;
orig = clip.clone();
drawColumnHeaders(canvas);
if (getColumnsHeight() > 0) {
RECT cr;
getClientRect(&cr);
cr.bottom = cr.top;
cr.top -= getColumnsHeight();
clip.subtractRect(&cr);
canvas->selectClipRgn(&clip);
}
firstItemVisible = -1;
lastItemVisible = -1;
//drawSubItems(canvas, x, &y, items, r.top, r.bottom, 0);
drawItems(canvas);
canvas->selectClipRgn(orig); // reset cliping region;
clip.disposeClone(orig);
return 1;
}
int ListWnd::onResize() {
LISTWND_PARENT::onResize();
recalcHeaders();
return 1;
}
int ListWnd::getNumItems(void) {
return itemList.getNumItems();
}
void ListWnd::drawItems(Canvas *canvas) {
RECT r, c;
getClientRect(&r);
RegionI clip;
if (!canvas->getClipRgn(&clip))
{
clip.setRect(&r);
}
if (GetClipBox(canvas->getHDC(), &c) == NULLREGION) {
getClientRect(&c);
}
// DebugString("%d %d %d %d\n", c.left, c.top, c.right, c.bottom);
// float f,l;
calcBounds();
//int first, last;
/* RECT s=c;
s.bottom = min(s.bottom, r.bottom);
s.top = max(s.top, getLabelHeight()+(showColumnsHeaders ? getColumnsHeight() : 0));
f = ((float)(s.top - getLabelHeight() - (showColumnsHeaders ? getColumnsHeight() : 0) - getYShift() + getScrollY())) / (float)itemHeight;
l = ((float)(s.bottom - getLabelHeight() - (showColumnsHeaders ? getColumnsHeight() : 0) - getYShift() + getScrollY())) / (float)itemHeight;
first = (int)f;
first = max(0,first);
last = (int)l;
if ((float)((int)l) != l) {
last++;
}*/
int g=0;
for (int i=firstItemVisible;i<=lastItemVisible && i<itemList.getNumItems();i++) {
RECT ir={0,0,0,0};
getItemRect(i, &ir);
int a=ir.right;
ir.right = r.right;
if (!clip.doesIntersectRect(&ir))
continue;
ir.right=a;
g++;
LPARAM itemdata = getItemData(i);
int itemselected = getItemSelected(i);
int itemfocused = getItemFocused(i);
onPreItemDraw(canvas, i, &ir, itemdata, itemselected, itemfocused);
int sel = getItemSelected(i);
canvas->pushPen(PS_SOLID, 1, getColumnSepColor());
if (!ownerDraw(canvas, i, &ir, itemdata, itemselected, itemfocused))
{
Wasabi::FontInfo fontInfo;
fontInfo.antialias = getTextAntialias(itemdata);
fontInfo.bold = getTextBold(itemdata);
fontInfo.italic = !!getTextItalic(itemdata);
fontInfo.opaque = false;
fontInfo.color = sel ? getSelFgColor(itemdata) : getTextColor(itemdata);
fontInfo.pointSize = getFontSize();
int x = -getScrollX();
if (sel)
canvas->fillRect(&ir, getSelBgColor(itemdata));
if (getItemFocused(i))
canvas->fillRect(&ir, getFocusColor(itemdata));
if (needFocusRect(itemdata))
canvas->drawRect(&ir, 1, getFocusRectColor(itemdata));
if (showicons)
{
SkinBitmap *icon = getItemIcon(i);
if (icon)
{
RECT dst={x+X_SHIFT+r.left+1, ir.top+1, x+X_SHIFT+r.left+getIconWidth()+1, ir.top+getIconHeight()+1};
icon->stretchToRect(canvas, &dst);
}
x += getIconWidth()+1;
}
int xsep = wantColSepOnItems()?COLSEPHEIGHT:0;
for (int j=0;j<columnsList.getNumItems();j++) {
ListColumn *col = columnsList[j];
RECT cr=ir;
cr.left = x+X_SHIFT+r.left;
cr.right = cr.left + col->getWidth()-X_SHIFT*2+xsep;
if (j > 0 && wantColSepOnItems()) {
canvas->moveTo(x, ir.top);
canvas->lineTo(x, ir.top+getItemHeight());
}
switch (col->getAlignment()) {
case COL_LEFTALIGN:
canvas->textOutEllipsed(cr.left+2, cr.top, cr.right-cr.left-4, cr.bottom-cr.top, getSubitemText(i, j), &fontInfo);
break;
case COL_CENTERALIGN: {
RECT _cr = {cr.left+2, cr.top, cr.right-4, cr.bottom};
canvas->textOutCentered(&_cr, getSubitemText(i, j), &fontInfo);
break;
}
case COL_RIGHTALIGN: {
const wchar_t *txt = getSubitemText(i, j);
int __x = cr.left;
int __y = cr.top;
int fw = canvas->getTextWidth(txt, &fontInfo);
int aw = cr.right-cr.left-4;
__x -= fw-aw;
canvas->textOut(__x, __y, txt, &fontInfo);
break;
}
}
x += col->getWidth()+xsep;
}
if (wantColSepOnItems()) {
canvas->moveTo(x, ir.top);
canvas->lineTo(x, ir.top+getItemHeight());
}
}
canvas->popPen();
onPostItemDraw(canvas, i, &ir, itemdata, itemselected, itemfocused);
}
/*
OutputDebugString("%d items draw\n", g);
*/
}
int ListWnd::wantColSepOnItems() {
return 0;
}
int ListWnd::getXShift() {
if (wantColSepOnItems()) return X_SHIFT; else return 0;
}
int ListWnd::getFirstItemSelected() {
return getNextItemSelected(-1);
}
int ListWnd::getNextItemSelected(int lastpos) {
if (lastpos < -1) lastpos = -1;
for (int i=lastpos+1;i<itemList.getNumItems();i++)
if (getItemSelected(i))
return i;
return -1;
}
void ListWnd::calcBounds() {
lastComplete = TRUE;
firstComplete = TRUE;
float f,l;
RECT r;
getClientRect(&r);
f = ((float)(getScrollY()-Y_SHIFT) / getItemHeight());
l = ((float)(getScrollY()-Y_SHIFT+(r.bottom-r.top)) / getItemHeight()) - 1.0f;
firstItemVisible = (int)f;
lastItemVisible = (int)l;
if ((float)((int)l) != l) {
lastItemVisible++;
lastComplete = FALSE;
}
if ((float)((int)f) != f && f >= 0) {
firstComplete = FALSE;
}
}
// Draws tiled background
void ListWnd::drawBackground(Canvas *canvas)
{
LISTWND_PARENT::drawBackground(canvas);
drawColumnHeaders(canvas);
}
void ListWnd::drawColumnHeaders(Canvas *c)
{
if (columnsList.getNumItems() == 0 || !showColumnsHeaders) return;
RECT r;
getClientRect(&r);
r.top -= getColumnsHeight();
r.bottom = r.top + getColumnsHeight();
if (renderRatioActive())
r.left -= (int)((double)getScrollX()*getRenderRatio());
else
r.left-=getScrollX();
c->fillRect(&r, color_headers);
int x = r.left + X_SHIFT/*+ 1*/;
if (showicons)
x += getIconWidth()+1 + 2;
Wasabi::FontInfo fontInfo;
fontInfo.color = columnTextColor;
fontInfo.opaque = false;
fontInfo.pointSize = getColumnsHeight();
c->pushPen(PS_SOLID, 1, getColumnSepColor());
for (int i=0;i<columnsList.getNumItems();i++)
{
ListColumn *col = columnsList[i];
int width = col->getWidth();
if (i > 0) {
c->moveTo(x, r.top);
c->lineTo(x, r.top+getColumnsHeight());
}
RECT ch;
ch.left = x+COLUMNS_MARGIN-((i==0)?X_SHIFT:0);
ch.top = r.top;
ch.right = ch.left + col->getWidth()-1-COLUMNS_MARGIN*2+((i==0)?X_SHIFT:0);
ch.bottom = ch.top + getColumnsHeight()-1;
col->customDrawHeader(c, &ch, &fontInfo);
x+=width/*+1*/;
}
c->moveTo(x, r.top);
c->lineTo(x, r.top+getColumnsHeight());
c->popPen();
}
ARGB32 ListWnd::getColumnSepColor() {
return columnSepColor;
}
int ListWnd::getHeaderHeight() {
return (showColumnsHeaders && columnsList.getNumItems() > 0) ? getColumnsHeight() : 0;
}
// Returns the current tree width in pixels
int ListWnd::getContentsWidth() {
return getColumnsWidth()+X_SHIFT;
}
// Returns the current tree height in pixels
int ListWnd::getContentsHeight() {
return itemList.getNumItems()*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0))+Y_SHIFT*2;
}
void ListWnd::setAutoSort(bool dosort) {
autosort = dosort;
itemList.setAutoSort(dosort);
}
void ListWnd::setOwnerDraw(bool doownerdraw) {
ownerdraw = doownerdraw;
}
int ListWnd::setFontSize(int size)
{
LISTWND_PARENT::setFontSize(size);
if (size >= 0) textsize = size;
TextInfoCanvas c(this);
Wasabi::FontInfo fontInfo;
fontInfo.pointSize = getFontSize();
setItemHeight(c.getTextHeight(&fontInfo)+2, false);
redraw = TRUE;
metrics_ok = FALSE;
invalidate();
return 1;
}
int ListWnd::getFontSize() {
#ifndef WASABINOMAINAPI
return textsize+api->metrics_getDelta();
#else
//MULTIAPI-FIXME: not handling delta
return textsize;
#endif
}
void ListWnd::onSetVisible(int show) {
LISTWND_PARENT::onSetVisible(show);
if (show) invalidate();
}
int ListWnd::fullyVisible(int pos) {
return (((lastComplete && pos <= lastItemVisible) || (!lastComplete && pos < lastItemVisible)) && ((firstComplete && pos >= firstItemVisible) || (!firstComplete && pos > firstItemVisible)));
}
void ListWnd::ensureItemVisible(int pos) {
if (pos >= itemList.getNumItems()) pos = itemList.getNumItems()-1;
if (pos < 0) pos = 0;
if (fullyVisible(pos)) return;
RECT c;
int y=pos*getItemHeight();
getClientRect(&c);
int showing_height = c.bottom - c.top;
if (getScrollY() < y) // scrolling up
y = (y - showing_height) + getItemHeight()*3;
else
y -= getItemHeight()*2;
if (y < 0) y = 0;
else if (y + showing_height > getContentsHeight()) {
// just show bottom pane
y = getContentsHeight()-showing_height;
}
scrollToY(y);
}
void ListWnd::scrollToItem(int pos) {
if (!isInited()) return;
scrollToY(Y_SHIFT+pos*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0)));
}
int ListWnd::getNumColumns() {
return columnsList.getNumItems();
}
ListColumn *ListWnd::getColumn(int n) {
return columnsList.enumItem(n);
}
int ListWnd::getColumnWidth(int c) {
ListColumn *col = columnsList[c];
ASSERT(col != NULL);
return col->getWidth();
}
int ListWnd::selectAll(int cb) {
int i;
if (preventMultipleSelection) return 1;
for (i=0;i<itemList.getNumItems();i++) {
selItemList.setSelected(i, TRUE, cb);
}
if (cb)
notifySelChanged();
return 1;
}
int ListWnd::deselectAll(int cb) {
int lif = getItemFocused();
if (lif != -1)
invalidateItem(lif);
lastItemFocused = NULL;
for (int i = 0; i < itemList.getNumItems(); i++) {
selItemList.setSelected(i, FALSE, cb);
}
if (cb)
notifySelChanged();
return 1;
}
void ListWnd::notifySelChanged(int item, int sel) {
if (!getRedraw()) return;
if (item == -1)
notifyParent(ChildNotify::LISTWND_SELCHANGED);
else
notifyParent(ChildNotify::LISTWND_ITEMSELCHANGED, item, sel);
}
int ListWnd::invertSelection(int cb) {
if (preventMultipleSelection) return 1;
for (int i = 0; i < itemList.getNumItems(); i++) {
toggleSelection(i, FALSE, cb);
}
return 1;
}
int ListWnd::invalidateItem(int pos) {
RECT r;
if (!isInited()) return 0;
int rv = getItemRect(pos, &r);
r.top -= item_invalidate_border;
r.bottom += item_invalidate_border;
if (rv)
invalidateRect(&r);
return rv;
}
int ListWnd::onLeftButtonDblClk(int x, int y) {
// check for column dblclick
int colhit;
if ((colhit = hitTestColumns(Wasabi::Std::makePoint(x, y))) >= 0) {
return onColumnDblClick(colhit, x, y);
}
if (itemList.getNumItems() == 0) return 0;
POINT p={x,y};
int i = hitTest(p);
if (i > -1) {
notifyParent(ChildNotify::LISTWND_DBLCLK, i, 0);
onDoubleClick(i);
return 1;
}
return 0;
}
int ListWnd::onRightButtonDown(int x, int y) {
nodrag=TRUE;
return 1;
}
int ListWnd::onRightButtonUp(int x, int y) {
nodrag=FALSE;
int i = hitTest(x,y);
if (i >= 0) { // did hit something
setItemFocused(i); // it always gets the focus
if (!getItemSelected(i)) { // reselect the item out of the cur selection
ListWnd::onLeftButtonDown(x,y); // don't call inherited!
ListWnd::onLeftButtonUp(x,y); // don't call inherited!
}
onRightClick(i);
} else {
if (wantAutoDeselect())
deselectAll();
}
onContextMenu(x,y);
return 1;
}
int ListWnd::onRightClick(int itemnum) {
return 0;
}
int ListWnd::onLeftButtonDown(int x, int y) {
if (colresize != -1) {
resizing_col = TRUE;
drawXorLine(colresizept.x);
}
processbup = FALSE;
bdown = TRUE;
bdownx = x;
bdowny = y;
if (!resizing_col) {
POINT p={x,y};
int i = hitTest(p);
if (i >= 0) {
if (Std::keyDown(VK_SHIFT)) {
if (getItemSelected(i))
processbup=TRUE;
else
setSelectionEnd(i);
} else
if (Std::keyDown(VK_CONTROL)) {
if (getItemSelected(i))
processbup=TRUE;
else
toggleSelection(i);
} else {
if (getItemSelected(i))
processbup = TRUE;
else
setSelectionStart(i);
}
} else {
if (wantAutoDeselect())
deselectAll();
/*rectselecting=1;
selectStart.x = x;
selectStart.y = y;
selectLast.x = x;
selectLast.y = y;
drawRect(selectStart.x, selectStart.y, x, y);
beginCapture();*/
}
}
return 1;
}
int ListWnd::onLeftButtonUp(int x, int y) {
bdown = FALSE;
if (resizing_col) {
resizing_col = FALSE;
drawXorLine(colresizept.x);
calcNewColWidth(colresize, colresizept.x);
recalcHeaders();
return 1;
}
// check for column label click
int colhit;
if ((colhit = hitTestColumnsLabel(Wasabi::Std::makePoint(x, y))) >= 0) {
ListColumn *lc = getColumn(colhit);
int ret = lc->onHeaderClick();
if (!ret) ret = onColumnLabelClick(colhit, x, y);
return ret;
}
POINT p={x,y};
int i = hitTest(p);
if (rectselecting || (processbup && !resizing_col) || hoverselect) {
if (i >= 0) {
if (Std::keyDown(VK_SHIFT)) {
if (getItemSelected(i))
setSelectionStart(i);
else
setSelectionEnd(i);
} else {
if (Std::keyDown(VK_CONTROL) || !wantAutoDeselect()) {
toggleSelection(i);
selectionStart = i;
} else {
setSelectionStart(i);
}
}
} else {
/* if (rectselecting) {
drawRect(selectStart.x, selectStart.y, selectLast.x, selectLast.y);
endCapture();
rectselecting=0;
selectRect(selectStart.x, selectStart.y, x, y);
} else*/
if (wantAutoDeselect())
deselectAll();
}
}
if (i >= 0) {
int cn = hitTestColumnClient(x);
int r = 0;
if (cn >= 0) {
ListColumn *lc = getColumn(cn);
ASSERT(lc != NULL);
r = lc->onColumnLeftClick(i);
}
if (!r)
{
// Add 1px clickable border to our icon
if (x < getIconWidth()+2) r = onIconLeftClick(i,x,y-(i*getItemHeight()) - getHeaderHeight());
if (!r) onLeftClick(i);
}
}
return 1;
}
int ListWnd::onMouseMove(int x, int y) {
LISTWND_PARENT::onMouseMove(x,y);
if (!bdown && (Std::keyDown(MK_RBUTTON) || Std::keyDown(MK_RBUTTON))) {
bdown = TRUE;
processbup = TRUE;
_enterCapture();
}
if (!rectselecting && bdown && !resizing_col && !nodrag && (ABS(x-bdownx) >= 4 || ABS(y-bdowny) >= 4)) {
processbup = FALSE;
bdown = FALSE;
int i = hitTest(x, y);
if (i != -1) {
onBeginDrag(i);
return 1;
}
}
/* if (rectselecting) {
drawRect(selectStart.x, selectStart.y, selectLast.x, selectLast.y);
selectLast.x = x;
selectLast.y = y;
drawRect(selectStart.x, selectStart.y, selectLast.x, selectLast.y);
return 1;
}*/
POINT p={x,y};
if (wantResizeCols()) {
if (!resizing_col) {
int c = hitTestColumns(p, &colresizeo);
if (c != -1) {
if (colresize != c) {
SetCursor(LoadCursor(NULL, IDC_SIZEWE)); // NONPORTABLE
if (!getCapture())
beginCapture();
colresize = c;
colresizept = p;
}
} else {
if (colresize != -1) {
SetCursor(LoadCursor(NULL, IDC_ARROW)); // NONPORTABLE
endCapture();
colresize = -1;
}
}
} else {
if (p.x + getScrollX() < colresizeo + COLUMNS_MIN_WIDTH) {
p.x = colresizeo + COLUMNS_MIN_WIDTH - getScrollX();
}
drawXorLine(colresizept.x);
colresizept = p;
drawXorLine(colresizept.x);
}
}
if (hoverselect) {
int i = hitTest(x, y);
if (i >= 0 && !getItemSelected(i)) {
deselectAll(0);
setSelected(i, 1, 0);
wchar_t t[256]=L"";
getItemLabel(i, 0, t, 255);
foreach(tempselectnotifies)
sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t);
endfor;
}
}
return 1;
}
// NONPORTABLE / Todo: implement necessary stuff in canvas
void ListWnd::drawXorLine(int x) {
HDC dc = GetDC(gethWnd());
HBRUSH brush = CreateSolidBrush(0xFFFFFF);
HPEN pen = CreatePen(PS_SOLID,0,0xFFFFFF);
HBRUSH oldB = (HBRUSH)SelectObject(dc, brush);
HPEN oldP = (HPEN)SelectObject(dc, pen);
int mix = SetROP2(dc,R2_XORPEN);
RECT r;
getClientRect(&r);
r.top -= getColumnsHeight();
r.bottom = r.top + getColumnsHeight();
if (renderRatioActive()) {
multRatio(&x);
multRatio(&r);
}
MoveToEx(dc, x, r.top, NULL);
LineTo(dc, x, r.bottom);
SelectObject(dc, oldB);
SelectObject(dc, oldP);
SetROP2(dc, mix);
DeleteObject(pen);
DeleteObject(brush);
ReleaseDC(gethWnd(), dc);
}
void ListWnd::calcNewColWidth(int c, int px) {
RECT r;
getClientRect(&r);
r.top -= getColumnsHeight();
r.bottom = r.top + getColumnsHeight();
px += getScrollX();
int x = r.left+X_SHIFT;
for (int i=0;i<columnsList.getNumItems();i++) {
ListColumn *col = columnsList[i];
if (col->getIndex() == c) {
int w = px - x;
col->setWidth(w);
setSlidersPosition();
return;
}
x += col->getWidth();
}
return;
}
int ListWnd::hitTestColumns(POINT p, int *origin) {
RECT r;
if (!showColumnsHeaders) return -1;
int best=-1, besto = 0, bestd=9999;
getClientRect(&r);
r.top -= getColumnsHeight();
r.bottom = r.top + getColumnsHeight();
p.x += getScrollX();
if (p.y > r.top && p.y < r.top + getColumnsHeight()) {
int x = r.left+X_SHIFT;
for (int i=0;i<columnsList.getNumItems();i++) {
ListColumn *col = columnsList[i];
x += col->getWidth();
if (p.x > x-COLUMNS_RESIZE_THRESHOLD && p.x < x+COLUMNS_RESIZE_THRESHOLD) {
int d = ABS(p.x-x);
if (d < bestd) {
bestd = d;
besto = x - col->getWidth();
best = col->getIndex();
}
}
}
}
if (best != -1)
if (origin != NULL) *origin = besto;
return best;
}
int ListWnd::hitTestColumnClient(int x) {
RECT cr = clientRect();
int x1 = cr.left+X_SHIFT;
foreach(columnsList)
int x2 = x1 + columnsList.getfor()->getWidth();
if (x >= x1 && x <= x2) return foreach_index;
x1 = x2;
endfor
return -1;
}
void ListWnd::invalidateColumns() {
RECT r;
getClientRect(&r);
r.top -= getColumnsHeight();
r.bottom = r.top + getColumnsHeight();
invalidateRect(&r);
}
int ListWnd::hitTestColumnsLabel(POINT p) {
RECT r;
if (!showColumnsHeaders) return -1;
getClientRect(&r);
r.top -= getColumnsHeight();
r.bottom = r.top + getColumnsHeight();
p.x += getScrollX();
if (p.y > r.top && p.y < r.top + getColumnsHeight()) {
int x = X_SHIFT;
for (int i=0;i<columnsList.getNumItems();i++) {
ListColumn *col = columnsList[i];
if (p.x >= x && p.x <= x+col->getWidth())
return i;
x += col->getWidth();
}
}
return -1;
}
void ListWnd::setSelectionStart(int pos, int wantcb) {
if (wantAutoDeselect())
deselectAll(wantcb);
if (!selItemList.isSelected(pos)) {
selItemList.setSelected(pos, TRUE, wantcb);
lastItemFocused = itemList[pos];
lastItemFocusedPos = pos;
invalidateItem(pos);
repaint();
}
selectionStart = pos;
notifySelChanged();
}
void ListWnd::setSelectionEnd(int pos) {
if (itemList.getNumItems() == 0) return;
if (selectionStart == -1) selectionStart = 0;
if (wantAutoDeselect())
deselectAll();
int inc = (selectionStart > pos) ? -1 : 1;
int i = selectionStart;
while (1) {
if (!selItemList.isSelected(i)) {
selItemList.setSelected(i, TRUE);
lastItemFocused = itemList[i];
lastItemFocusedPos = i;
invalidateItem(i);
}
if (i == pos) break;
i += inc;
}
notifySelChanged();
}
void ListWnd::setSelected(int pos, int selected, int cb) {
selItemList.setSelected(pos, selected, cb);
}
void ListWnd::toggleSelection(int pos, int setfocus, int cb) {
if (!selItemList.isSelected(pos)) {
selItemList.setSelected(pos, TRUE, cb);
if (setfocus) {
if (selItemList.getNumSelected() > 1) {
for (int i=0;i<itemList.getNumItems();i++)
if (selItemList.isSelected(i))
invalidateItem(i);
}
lastItemFocused = itemList[pos];
lastItemFocusedPos = pos;
}
} else {
selItemList.setSelected(pos, FALSE, cb);
if (setfocus) {
lastItemFocused = NULL;
lastItemFocusedPos = -1;
}
}
invalidateItem(pos);
if (cb)
notifySelChanged();
}
int ListWnd::onBeginDrag(int iItem) {
// nothing by default
lastItemFocused = NULL;
lastItemFocusedPos = -1;
invalidateItem(iItem);
return 0;
}
int ListWnd::dragOver(int x, int y, ifc_window *sourceWnd) {
int rt = LISTWND_PARENT::dragOver(x, y, sourceWnd);
if (dragtimeron) return rt;
POINT pos={x,y};
screenToClient(&pos);
int item = hitTest(pos);
if (item == LW_HT_BELOW || item == LW_HT_ABOVE) {
dragtimeron = 1;
dragskip = DRAGSKIP_START;
dragskipcount = DRAGSKIP_START-1; // start right away
setTimer(LISTWND_DRAG_TIMERID, LISTWND_DRAG_TIMERDELAY);
}
return rt;
}
void ListWnd::onDragTimer() {
POINT pos;
Wasabi::Std::getMousePos(&pos);
screenToClient(&pos);
int item = hitTest(pos);
if (item == LW_HT_BELOW || item == LW_HT_ABOVE) {
dragskipcount++;
if (dragskipcount >= dragskip) {
switch (item) {
case LW_HT_BELOW:
scrollDown();
break;
case LW_HT_ABOVE:
scrollUp();
break;
}
dragskipcount = 0;
if (dragskip > 0) dragskip--;
}
} else {
killTimer(LISTWND_DRAG_TIMERID);
dragtimeron = 0;
}
}
void ListWnd::timerCallback(int id) {
switch (id) {
case LISTWND_DRAG_TIMERID:
onDragTimer();
return;
}
LISTWND_PARENT::timerCallback(id);
}
void ListWnd::onSelectAll() {
}
void ListWnd::onDelete() {
// do nothing by default
}
void ListWnd::onDoubleClick(int itemnum) {
// do nothing by default
}
void ListWnd::onLeftClick(int itemnum) {
// do nothing by default
}
int ListWnd::onIconLeftClick (int itemnum, int x, int y)
{
return 0;
}
void ListWnd::onSecondLeftClick(int itemnum) {
// do nothing by default
}
int ListWnd::scrollAbsolute(int x) {
scrollToX(x);
return getScrollX();
}
int ListWnd::scrollRelative(int x) {
scrollToX(getScrollX() + x);
return getScrollX();
}
int ListWnd::getItemFocused() {
if (lastItemFocused == NULL) return -1;
if (itemList[lastItemFocusedPos] == lastItemFocused)
return lastItemFocusedPos;
else {
lastItemFocusedPos = itemList.searchItem(lastItemFocused);
return lastItemFocusedPos;
}
}
void ListWnd::setItemFocused(int pos, int ensure_visible) {
invalidateItem(lastItemFocusedPos);
lastItemFocused = itemList[pos];
lastItemFocusedPos = -1;
if (lastItemFocused != NULL) lastItemFocusedPos = pos;
invalidateItem(lastItemFocusedPos);
if (ensure_visible) ensureItemVisible(pos);
}
int ListWnd::getItemRect(int pos, RECT *r) {
MEMSET(r, 0, sizeof(RECT));
if (pos < 0 || pos >= itemList.getNumItems()) return 0;
RECT cr={0,0,0,0};
getClientRect(&cr);
r->left = -getScrollX() + X_SHIFT + cr.left;
r->right = cr.left + getColumnsWidth();
if (showicons) r->right += getItemHeight();
r->top = -getScrollY() + pos * (getItemHeight() + (wantColSepOnItems() ? COLSEPHEIGHT : 0)) + Y_SHIFT + cr.top;
r->bottom = r->top + getItemHeight();
// clip!
if (r->top > cr.bottom || r->bottom < cr.top) return 0;
return 1;
}
int ListWnd::locateData(LPARAM data) { // linear search
for (int i=0;i<itemList.getNumItems();i++) {
if (itemList[i]->data == data)
return i;
}
return -1;
}
int ListWnd::getItemFocused(int pos) {
if (pos >= itemList.getNumItems()) return 0;
return getItemFocused() == pos;
}
int ListWnd::getItemSelected(int pos) {
listItem *item = itemList[pos];
return (item && selItemList.isSelected(pos));
}
int ListWnd::hitTest(POINT pos, int drag) {
RECT r;
getClientRect(&r);
if (pos.y < r.top) return LW_HT_ABOVE;
if (getScrollY() > 0 && drag && pos.y < r.top + LISTWND_DRAG_MARGIN) return LW_HT_ABOVE;
if (pos.y > r.bottom) return LW_HT_BELOW;
if (getContentsHeight() > (getScrollY() + r.bottom-r.top) && drag && pos.y > r.bottom - LISTWND_DRAG_MARGIN) return LW_HT_BELOW;
if (pos.x > r.left + getColumnsWidth() - getScrollX()) return LW_HT_DONTKNOW;
if (pos.x < r.left + getScrollX()) return LW_HT_DONTKNOW;
int i = (pos.y - r.top + getScrollY() - Y_SHIFT) / (getItemHeight() + (wantColSepOnItems() ? COLSEPHEIGHT : 0));
if (i >= itemList.getNumItems()) return LW_HT_DONTKNOW;
return i;
}
int ListWnd::hitTest(int x, int y, int drag) {
POINT pt={x, y};
return hitTest(pt, drag);
}
void ListWnd::selectRect(int x1, int y1, int x2, int y2) {
}
void ListWnd::drawRect(int x1, int y1, int x2, int y2) {
HDC dc = GetDC(gethWnd());
RECT r={x1,y1,x2,y2};
DrawFocusRect(dc, &r);
ReleaseDC(gethWnd(), dc);
}
LPARAM ListWnd::getItemData(int pos) {
if (pos >= itemList.getNumItems()) return 0;
listItem *item = itemList[pos];
if (item) return item->data;
return NULL;
}
int ListWnd::getItemLabel(int pos, int subpos, wchar_t *txt, int textmax)
{
const wchar_t *t = getSubitemText(pos, subpos);
if (t)
{
WCSCPYN(txt, t, textmax);
return 1;
}
return 0;
}
void ListWnd::setItemLabel(int pos, const wchar_t *text)
{
if (pos >= itemList.getNumItems()) return;
listItem *item = itemList[pos];
if (!item) return;
item->label = text;
invalidateItem(pos);
}
void ListWnd::resort() {
itemList.sort(TRUE);
invalidate();
}
int ListWnd::getSortDirection() {
return sortdir;
}
int ListWnd::getSortColumn() {
return sortcol;
}
void ListWnd::setSortDirection(int dir) {
sortdir=dir;
}
void ListWnd::setSortColumn(int col) {
sortcol=col;
}
int ListWnd::sortCompareItem(listItem *p1, listItem *p2)
{
const wchar_t *l1=p1->label;
const wchar_t *l2=p2->label;
int i;
if(sortcol!=0)
{
if (p1->subitems != NULL)
{
for (i=0;i<p1->subitems->getNumItems();i++)
{
listSubitemStruct *subitem = p1->subitems->enumItem(i);
if (subitem->column == sortcol)
{
l1=subitem->label;
break;
}
}
} else {
l1 = L"";
}
if (p2->subitems != NULL) {
for (i=0;i<p2->subitems->getNumItems();i++) {
listSubitemStruct *subitem = p2->subitems->enumItem(i);
if (subitem->column == sortcol) {
l2=subitem->label;
break;
}
}
} else {
l2 = L"";
}
}
if(!columnsList.enumItem(sortcol)->getNumeric()) {
if(!sortdir) return(WCSICMP(l1, l2));
else return(WCSICMP(l2, l1));
} else {
int a=WTOI(l1),b=WTOI(l2);
if(!sortdir) return a-b;
else return b-a;
}
}
int ListWnd::findItemByParam(LPARAM param) {
for (int i=0;i<itemList.getNumItems();i++) {
if (itemList[i]->data == param)
return i;
}
return -1;
}
void ListWnd::setItemParam(int pos, LPARAM param) {
if (pos >= itemList.getNumItems()) return;
itemList[pos]->data = param;
invalidateItem(pos);
}
int ListWnd::deleteByPos(int pos) {
listItem *item = itemList[pos];
if (item == NULL) return 0;
// selItemList.setSelected(pos, FALSE); // If you do not delete the item from the selItemList, you corrupt it for all item positions downstream of this one.
selItemList.deleteByPos(pos);
itemList.removeByPos(pos);
onItemDelete(item->data);
deleteListItem(item); item=NULL;
if (redraw) {
invalidate();
setSlidersPosition();
}
return 1;
}
void ListWnd::deleteAllItems() {
bool sav = getRedraw();
deselectAll();
setRedraw(FALSE);
selItemList.deselectAll(); //force desel so as to avoid the MEMCPY
while (itemList.getNumItems()) {
deleteByPos(0);
}
setRedraw(sav);
// setSlidersPosition();
}
void ListWnd::setSubItem(int pos, int subpos, const wchar_t *txt)
{
if (pos >= itemList.getNumItems()) return;
listItem *item = itemList[pos];
if (!item) return;
if (!item->subitems)
item->subitems = new PtrList<listSubitemStruct>;
for (int i=0;i<item->subitems->getNumItems();i++) {
listSubitemStruct *subitem = item->subitems->enumItem(i);
if (subitem->column == subpos) {
if (subitem->label) {
FREE(subitem->label);
subitem->label = NULL;
}
if (txt)
subitem->label = WCSDUP(txt);
invalidateItem(pos);
return;
}
}
listSubitemStruct *subitem = (listSubitemStruct *) MALLOC(sizeof(listSubitemStruct));
item->subitems->addItem(subitem);
subitem->label = WCSDUP(txt);
subitem->column = subpos;
invalidateItem(pos);
}
SkinBitmap *ListWnd::getItemIcon(int pos)
{
if (pos >= itemList.getNumItems()) return NULL;
listItem *item = itemList[pos];
return item->icon;
}
void ListWnd::setItemIcon(int pos, const wchar_t *bitmapid) {
if (pos >= itemList.getNumItems()) return;
listItem *item = itemList[pos];
item->icon = bitmapid;
invalidateItem(pos);
}
const wchar_t *ListWnd::getSubitemText(int pos, int subpos) {
if (pos >= itemList.getNumItems()) return NULL;
listItem *item = itemList[pos];
if (!item) return NULL;
if (subpos == 0) {
return item->label;
}
if (!item->subitems) return NULL;
for (int i=0;i<item->subitems->getNumItems();i++) {
listSubitemStruct *subitem = item->subitems->enumItem(i);
if (subitem->column == subpos) {
return subitem->label;
}
}
return NULL;
}
listItem *ListWnd::createListItem() {
listItem *item = listItem_freelist.getRecord();
new(item) listItem();
item->setList(this);
return item;
}
void ListWnd::deleteListItem(listItem *item) {
if (item == NULL) return;
item->~listItem();
listItem_freelist.freeRecord(item);
}
ListColumn *ListWnd::enumListColumn(int pos) {
return columnsList[pos];
}
int ListWnd::getColumnPosByName(const wchar_t *name)
{
for (int i = 0; i < columnsList.getNumItems(); i++)
{
const wchar_t *name2 = columnsList[i]->getName();
if (name2 == NULL) continue;
if (!wcscmp(name, name2)) return i;
}
return -1;
}
int ListWnd::delColumnByPos(int pos) {
if (pos < 0 || pos >= columnsList.getNumItems()) return 0;
delete columnsList[pos];
columnsList.removeByPos(pos);
recalcHeaders();
return 1;
}
void ListWnd::recalcHeaders() {
if (getNumColumns() <= 0) return;
int wid = 0, ndynamic = 0;
for (int i = 0; i < getNumColumns(); i++) {
ListColumn *col = enumListColumn(i);
if (!col->isDynamic()) wid += col->getWidth()+COLUMNS_MARGIN-1;
else ndynamic++;
}
if (ndynamic == 0) return;
RECT r = clientRect();
int wwidth = (r.right - r.left) - 2;
int leftover = wwidth - wid;
if (leftover <= 1) return;
leftover--;
leftover /= ndynamic;
for (int i = 0; i < getNumColumns(); i++) {
ListColumn *col = enumListColumn(i);
if (col->isDynamic()) col->setWidth(leftover);
}
//BU note: we could probably find a way to not invalidate everything
invalidate();
}
void ListWnd::itemSelection(int itemnum, int selected) {
notifySelChanged(itemnum, selected);
onItemSelection(itemnum, selected);
}
void ListWnd::onItemSelection(int itemnum, int selected) {
if (selected) {
Accessible *a = getAccessibleObject();
if (a != NULL)
a->onGetFocus(itemnum);
}
}
int ListWnd::doAddItem(const wchar_t *label, LPARAM lParam, int pos)
{
listItem *item = createListItem();
item->label = label;
item->data = lParam;
itemList.addItem(item, pos, ITEMLIST_INC);
lastAddedItem = item;
if (redraw)
{
invalidate(); // todo: optimize to invalidate only if necessary
setSlidersPosition();
}
if (isInited()) calcBounds();
int p = (pos == POS_LAST) ? itemList.getNumItems()-1 : pos;
if (p <= selectionStart) selectionStart++;
if (autosort) {
itemList.sort();
p = itemList.searchItem(item);
}
return p;
}
void ListWnd::setMinimumSize(int size) {
if (size > 0) itemList.setMinimumSize(size);
}
int ListWnd::addItem(const wchar_t *label, LPARAM lParam)
{
return doAddItem(label, lParam, POS_LAST);
}
int ListWnd::insertItem(int pos, const wchar_t *label, LPARAM lParam)
{
return doAddItem(label, lParam, pos);
}
int ListWnd::getLastAddedItemPos() {
if (lastAddedItem == NULL) return -1;
return itemList.searchItem(lastAddedItem);
}
int ListWnd::getColumnsHeight() {
return columnsHeight;
}
int ListWnd::getColumnsWidth() {
int i, x=0;
for (i=0;i<columnsList.getNumItems();i++) x+= columnsList[i]->getWidth();
return x+1;
}
int ListWnd::insertColumn(ListColumn *col, int pos, int alignment)
{
ASSERT(col != NULL);
ASSERT(pos >= -1);
if (pos < 0) col->setIndex(columnsList.getNumItems());
else col->setIndex(pos);
col->setList(this);
col->setAlignment(alignment);
columnsList.addItem(col);
if (pos >= 0) {
columnsList.moveItem(columnsList.getNumItems()-1, pos);
}
if (redraw && isInited()) {
invalidate();
setSlidersPosition();
}
recalcHeaders();
return columnsList.getNumItems();
}
void ListWnd::deleteAllColumns() {
columnsList.deleteAll();
if (redraw && isInited()) {
invalidate();
setSlidersPosition();
}
recalcHeaders();
}
int ListWnd::addColumn(const wchar_t *name, int width, int numeric, int align)
{
ListColumn *col = new ListColumn();
col->setWidth(width);
col->setLabel(name);
col->setNumeric(numeric);
col->setAlignment(align);
return insertColumn(col, -1, align);
}
bool ListWnd::setRedraw(bool _redraw) {
bool prev = redraw;
if (!redraw && _redraw) {
invalidate();
setSlidersPosition();
notifySelChanged();
}
redraw = _redraw;
return prev;
}
bool ListWnd::getRedraw() {
return redraw;
}
int ListWnd::onChar(unsigned int c) {
//CT> Commented this out so shortcuts work in playlist editor
/*char b = TOUPPER(c);
if (b >= 'A' && b <= 'Z') {
jumpToNext(b);
return 1;
}*/
return LISTWND_PARENT::onChar(c);
}
int ListWnd::onKeyDown(int keyCode) {
switch (keyCode) {
case VK_DOWN:
next(selectonupdown);
return 1;
case VK_UP:
previous(selectonupdown);
return 1;
case VK_PRIOR:
pageup(selectonupdown);
return 1;
case VK_NEXT:
pagedown(selectonupdown);
return 1;
case VK_HOME:
home(selectonupdown);
return 1;
case VK_END:
end(selectonupdown);
return 1;
case VK_DELETE:
onDelete();
return 1;
case VK_RETURN: {
int i=getItemFocused();
//setSelected(i, 0, 0);
//setSelected(i, 1, 1);
if(i!=-1)
onDoubleClick(i);
return 1;
}
}
return LISTWND_PARENT::onKeyDown(keyCode);
}
void ListWnd::next(int wantcb) {
int from=getItemFocused();
/* if (selItemList.getNumItems() > 0)
for (int i=0;i<itemList.getNumItems();i++)
if (getItemSelected(i)) {
from = i;
break;
}*/
int to = from + 1;
if (to < itemList.getNumItems() && to >= 0) {
setSelectionStart(to, wantcb);
if (!fullyVisible(to)) {
RECT c;
getClientRect(&c);
scrollToY((Y_SHIFT*2+(to+1)*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0)))-(c.bottom-c.top));
}
if (!wantcb)
{
wchar_t t[256]=L"";
getItemLabel(to, 0, t, 255);
foreach(tempselectnotifies)
sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t);
endfor;
}
}
}
void ListWnd::selectCurrent() {
int from=getItemFocused();
int to = from;
if (to < itemList.getNumItems() && to >= 0) {
setSelectionStart(to);
if (!fullyVisible(to)) {
RECT c;
getClientRect(&c);
scrollToY((Y_SHIFT*2+(to+1)*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0)))-(c.bottom-c.top));
}
}
}
void ListWnd::selectFirstEntry(int wantcb) {
setSelectionStart(0, wantcb);
ensureItemVisible(0);
}
void ListWnd::previous(int wantcb) {
int from=0;
/* if (selItemList.getNumItems() > 0)
for (int i=0;i<itemList.getNumItems();i++)
if (getItemSelected(i)) {
from = i;
break;
}*/
from = getItemFocused();
int to = from - 1;
if (to < itemList.getNumItems() && to >= 0) {
setSelectionStart(to, wantcb);
ensureItemVisible(to);
if (!wantcb) {
wchar_t t[256]=L"";
getItemLabel(to, 0, t, 255);
foreach(tempselectnotifies)
sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t);
endfor;
}
}
}
void ListWnd::pagedown(int wantcb) {
int from=-1,to;
if (selItemList.getNumSelected())
for (int i=0;i<itemList.getNumItems();i++)
if (getItemSelected(i)) {
from = i;
break;
}
if(from==-1) to = 0;
else to = from + getLinesPerPage();
to=MIN(to,itemList.getNumItems()-1);
if(to>=0) {
setSelectionStart(to, wantcb);
if (!fullyVisible(to)) {
RECT c;
getClientRect(&c);
scrollToY((Y_SHIFT*2+(to+1)*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0)))-(c.bottom-c.top));
}
if (!wantcb) {
wchar_t t[256]=L"";
getItemLabel(to, 0, t, 255);
foreach(tempselectnotifies)
sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t);
endfor;
}
}
}
void ListWnd::pageup(int wantcb) {
int from=-1,to;
if (selItemList.getNumSelected())
for (int i=0;i<itemList.getNumItems();i++)
if (getItemSelected(i)) {
from = i;
break;
}
if(from==-1) to = 0;
else to = from - getLinesPerPage();
to=MAX(to,0);
to=MIN(to,itemList.getNumItems()-1);
if(to>=0) {
setSelectionStart(to, wantcb);
ensureItemVisible(to);
if (!wantcb) {
wchar_t t[256]=L"";
getItemLabel(to, 0, t, 255);
foreach(tempselectnotifies)
sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t);
endfor;
}
}
}
void ListWnd::home(int wantcb) {
if(!itemList.getNumItems()) return;
setSelectionStart(0, wantcb);
ensureItemVisible(0);
if (!wantcb) {
wchar_t t[256]=L"";
getItemLabel(0, 0, t, 255);
foreach(tempselectnotifies)
sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t);
endfor;
}
}
void ListWnd::end(int wantcb) {
if(!itemList.getNumItems()) return;
int i=itemList.getNumItems()-1;
setSelectionStart(i, wantcb);
if (!fullyVisible(i)) {
RECT c;
getClientRect(&c);
scrollToY((Y_SHIFT*2+(i+1)*(getItemHeight()+(wantColSepOnItems()?COLSEPHEIGHT:0)))-(c.bottom-c.top));
}
if (!wantcb) {
wchar_t t[256]=L"";
getItemLabel(i, 0, t, 255);
foreach(tempselectnotifies)
sendAction(tempselectnotifies.getfor(), L"tempselectnotify", t);
endfor;
}
}
void ListWnd::jumpToNext(wchar_t c)
{
if (doJumpToNext(c, FALSE)) return;
doJumpToNext(c, TRUE);
}
int ListWnd::doJumpToNext(wchar_t c, bool fromtop)
{
int from = 0;
if (!fromtop && selItemList.getNumSelected())
{
for (int i=0;i<itemList.getNumItems();i++)
if (getItemSelected(i))
{
from = i+1;
break;
}
}
for (int j=from;j<itemList.getNumItems();j++)
{
listItem *item = itemList[j];
if (item->label != NULL) {
wchar_t z = TOUPPERW(*(item->label));
if (z == c)
{
setSelectionStart(j);
ensureItemVisible(j);
return 1;
}
}
}
return 0;
}
void ListWnd::reset() {
columnsList.deleteAll();
deleteAllItems();
}
void ListWnd::setShowColumnsHeaders(int show) {
int prev = showColumnsHeaders;
showColumnsHeaders = !!show;
if (prev != show) {
invalidate();
}
}
int ListWnd::onContextMenu (int x, int y) {
return notifyParent(ChildNotify::LISTWND_POPUPMENU, x, y);
}
void ListWnd::scrollUp(int lines) {
scrollToY(MAX(0, getScrollY()-getItemHeight()*lines));
}
void ListWnd::scrollLeft(int lines) {
scrollToX(MAX(0, getScrollX()-getItemHeight()*lines));
}
void ListWnd::scrollDown(int lines) {
scrollToY(MIN(getMaxScrollY(), getScrollY()+getItemHeight()*lines));
}
void ListWnd::scrollRight(int lines) {
scrollToX(MIN(getMaxScrollX(), getScrollX()+getItemHeight()*lines));
}
int ListWnd::onMouseWheelUp(int clicked, int lines) {
lines *= Wasabi::Std::osparam_getScrollLines();
if (!clicked)
scrollUp(lines);
else
scrollLeft(lines);
return 1;
}
int ListWnd::onMouseWheelDown(int clicked, int lines) {
lines *= Wasabi::Std::osparam_getScrollLines();
if (!clicked)
scrollDown(lines);
else
scrollRight(lines);
return 1;
}
int ListWnd::onColumnLabelClick(int col, int x, int y)
{
if(lastcolsort==col) {
setSortDirection(1);
lastcolsort=-1;
} else {
setSortDirection(0);
lastcolsort=col;
}
setSortColumn(col);
resort();
return 1;
}
ARGB32 ListWnd::getTextColor(LPARAM lParam) {
return textColor;
}
ARGB32 ListWnd::getSelBgColor(LPARAM LParam) {
return color_item_selected;
}
ARGB32 ListWnd::getSelFgColor(LPARAM LParam) {
ARGB32 r = color_item_selected_fg;
if (r == 0xFFFFFFFF)
return color_item_selected_fg;
return r;
}
ARGB32 ListWnd::getBgColor() {
return bgcolor;
}
ARGB32 ListWnd::getFocusColor(LPARAM LParam) {
return color_item_focused;
}
ARGB32 ListWnd::getFocusRectColor(LPARAM lParam) {
return color_item_focusrect;
}
void ListWnd::moveItem(int from, int to) {
itemList.moveItem(from,to);
invalidate();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ListColumn
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ListColumn::ListColumn(const wchar_t *name, int isdynamic)
: NamedW(name), dynamic(isdynamic)
{
align=COL_LEFTALIGN;
index = -1;
width = COLUMNS_DEFAULT_WIDTH;
list = NULL;
numeric = 0;
}
void ListColumn::setLabel(const wchar_t *newlabel) {
setName(newlabel);
}
const wchar_t *ListColumn::getLabel() {
return getName();
}
void ListColumn::setIndex(int newindex) {
index = newindex;
}
int ListColumn::getIndex() {
return index;
}
void ListColumn::setWidth(int _width) {
width = _width;
if (list && list->getRedraw()) {
list->invalidate();
}
}
int ListColumn::getWidth() {
return width;
}
int ListColumn::customDrawHeader(Canvas *c, RECT *r, const Wasabi::FontInfo *fontInfo)
{
int y = (r->bottom-r->top-c->getTextHeight(fontInfo)) / 2;
c->textOutEllipsed(r->left, y, r->right-r->left, c->getTextHeight(fontInfo), _(getName()), fontInfo);
return 1;
}
void ListColumn::setDynamic(int isdynamic)
{
int prev = dynamic;
dynamic = !!isdynamic;
if (prev != dynamic && dynamic && list != NULL)
list->recalcHeaders();
}
void ListColumn::setList(ListWnd *_list) {
list = _list;
}
ListWnd *ListColumn::getList() {
return list;
}
int ListWnd::wantAutoContextMenu() {
return 0;
}
int ListWnd::onAcceleratorEvent(const wchar_t *name) {
if(!_wcsicmp(name, L"selectall")) {
selectAll();
return 1;
}
return 0;
}
int ListWnd::onAction(const wchar_t *action, const wchar_t *param, int x, int y, intptr_t p1, intptr_t p2, void *data, size_t datalen, ifc_window *source) {
int r = LISTWND_PARENT::onAction(action, param, x, y, p1, p2, data, datalen, source);
if (WCSCASEEQLSAFE(action, L"register_tempselectnotify")) {
tempselectnotifies.addItem(source);
}
else if (WCSCASEEQLSAFE(action, L"up")) {
previous((int)p1);
}
else if (WCSCASEEQLSAFE(action, L"down")) {
next((int)p1);
}
else if (WCSCASEEQLSAFE(action, L"home")) {
home((int)p1);
}
else if (WCSCASEEQLSAFE(action, L"end")) {
end((int)p1);
}
else if (WCSCASEEQLSAFE(action, L"pageup")) {
pageup((int)p1);
}
else if (WCSCASEEQLSAFE(action, L"pagedown")) {
pagedown((int)p1);
}
else if (WCSCASEEQLSAFE(action, L"select_current")) {
selectCurrent();
}
else if (WCSCASEEQLSAFE(action, L"doubleclick")) {
int pos = getItemFocused();
if (pos >= 0) onDoubleClick(pos);
return 1;
}
return r;
}
void ListWnd::setShowIcons(int icons)
{
showicons = icons;
invalidate();
}
int ListWnd::getShowIcons ()
{
return showicons;
}
int ListWnd::getIconWidth ()
{
// The old behaviour used the itemheight as value, so we return this for backwards compatibility if iconWidth is negative
return (iconWidth < 0 ? itemHeight-2 : iconWidth);
}
void ListWnd::setIconWidth (int width)
{
iconWidth = width;
invalidate();
}
int ListWnd::getIconHeight ()
{
// The old behaviour used the itemheight as value, so we return this for backwards compatibility if iconWidth is negative
return (iconHeight < 0 ? itemHeight-2 : iconHeight);
}
void ListWnd::setIconHeight (int height)
{
iconHeight = height;
invalidate();
}
int ListWnd::getItemHeight ()
{
return (itemHeight < getIconHeight()+2) ? getIconHeight()+2 : itemHeight;
}
void ListWnd::setItemHeight (int height, bool forceInvalidate /*true*/)
{
itemHeight = height;
if (forceInvalidate) invalidate();
}