2013-03-15 13:11:33 +00:00
|
|
|
namespace phoenix {
|
|
|
|
|
2012-06-25 12:49:39 +00:00
|
|
|
unsigned ListView_GetColumnCount(HWND hwnd) {
|
|
|
|
unsigned count = 0;
|
|
|
|
LVCOLUMN column;
|
|
|
|
column.mask = LVCF_WIDTH;
|
|
|
|
while(ListView_GetColumn(hwnd, count++, &column));
|
|
|
|
return --count;
|
|
|
|
}
|
|
|
|
|
2012-08-11 02:18:19 +00:00
|
|
|
void ListView_SetImage(HWND hwnd, HIMAGELIST imageList, unsigned row, unsigned column, unsigned imageID) {
|
|
|
|
//if this is the first image assigned, set image list now
|
|
|
|
//do not set sooner, or image blocks will appear in a list with no images
|
|
|
|
if(ListView_GetImageList(hwnd, LVSIL_SMALL) != imageList) {
|
|
|
|
ListView_SetImageList(hwnd, imageList, LVSIL_SMALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
LVITEM item;
|
|
|
|
item.mask = LVIF_IMAGE;
|
|
|
|
item.iItem = row;
|
|
|
|
item.iSubItem = column;
|
|
|
|
item.iImage = imageID;
|
|
|
|
ListView_SetItem(hwnd, &item);
|
|
|
|
}
|
|
|
|
|
2013-05-02 11:25:45 +00:00
|
|
|
void pListView::append(const lstring& list) {
|
2011-02-24 09:25:20 +00:00
|
|
|
wchar_t empty[] = L"";
|
|
|
|
unsigned row = ListView_GetItemCount(hwnd);
|
|
|
|
LVITEM item;
|
2012-06-25 12:49:39 +00:00
|
|
|
item.mask = LVIF_TEXT;
|
2011-02-24 09:25:20 +00:00
|
|
|
item.iItem = row;
|
|
|
|
item.iSubItem = 0;
|
|
|
|
item.pszText = empty;
|
|
|
|
locked = true;
|
|
|
|
ListView_InsertItem(hwnd, &item);
|
|
|
|
locked = false;
|
2012-06-25 12:49:39 +00:00
|
|
|
for(unsigned column = 0; column < list.size(); column++) {
|
|
|
|
utf16_t wtext(list(column, ""));
|
|
|
|
ListView_SetItemText(hwnd, row, column, wtext);
|
2011-02-24 09:25:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-27 09:05:10 +00:00
|
|
|
void pListView::autoSizeColumns() {
|
2012-06-25 12:49:39 +00:00
|
|
|
unsigned columns = ListView_GetColumnCount(hwnd);
|
|
|
|
for(unsigned n = 0; n < columns; n++) {
|
2011-02-24 09:25:20 +00:00
|
|
|
ListView_SetColumnWidth(hwnd, n, LVSCW_AUTOSIZE_USEHEADER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::remove(unsigned selection) {
|
|
|
|
ListView_DeleteItem(hwnd, selection);
|
2012-06-25 12:49:39 +00:00
|
|
|
}
|
|
|
|
|
2011-02-24 09:25:20 +00:00
|
|
|
void pListView::reset() {
|
|
|
|
ListView_DeleteAllItems(hwnd);
|
2012-08-11 02:18:19 +00:00
|
|
|
buildImageList(); //free previously allocated images
|
2011-02-24 09:25:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pListView::setCheckable(bool checkable) {
|
2012-06-25 12:49:39 +00:00
|
|
|
ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | (checkable ? LVS_EX_CHECKBOXES : 0));
|
2011-02-24 09:25:20 +00:00
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::setChecked(unsigned selection, bool checked) {
|
2011-02-24 09:25:20 +00:00
|
|
|
locked = true;
|
2013-11-28 10:29:01 +00:00
|
|
|
ListView_SetCheckState(hwnd, selection, checked);
|
2011-02-24 09:25:20 +00:00
|
|
|
locked = false;
|
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::setGeometry(Geometry geometry) {
|
|
|
|
pWidget::setGeometry(geometry);
|
|
|
|
autoSizeColumns();
|
|
|
|
}
|
|
|
|
|
2013-05-02 11:25:45 +00:00
|
|
|
void pListView::setHeaderText(const lstring& list) {
|
2011-02-24 09:25:20 +00:00
|
|
|
while(ListView_DeleteColumn(hwnd, 0));
|
|
|
|
|
|
|
|
lstring headers = list;
|
|
|
|
if(headers.size() == 0) headers.append(""); //must have at least one column
|
|
|
|
|
2011-09-27 11:55:02 +00:00
|
|
|
for(unsigned n = 0; n < headers.size(); n++) {
|
2011-02-24 09:25:20 +00:00
|
|
|
LVCOLUMN column;
|
|
|
|
column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM;
|
|
|
|
column.fmt = LVCFMT_LEFT;
|
|
|
|
column.iSubItem = n;
|
2011-09-27 11:55:02 +00:00
|
|
|
utf16_t headerText(headers[n]);
|
2011-02-24 09:25:20 +00:00
|
|
|
column.pszText = headerText;
|
|
|
|
ListView_InsertColumn(hwnd, n, &column);
|
|
|
|
}
|
2011-02-27 09:05:10 +00:00
|
|
|
autoSizeColumns();
|
2011-02-24 09:25:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pListView::setHeaderVisible(bool visible) {
|
|
|
|
SetWindowLong(
|
|
|
|
hwnd, GWL_STYLE,
|
|
|
|
(GetWindowLong(hwnd, GWL_STYLE) & ~LVS_NOCOLUMNHEADER) |
|
|
|
|
(visible ? 0 : LVS_NOCOLUMNHEADER)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::setImage(unsigned selection, unsigned position, const image& image) {
|
2012-08-11 02:18:19 +00:00
|
|
|
//assign existing image
|
|
|
|
for(unsigned n = 0; n < images.size(); n++) {
|
|
|
|
if(images[n] == image) {
|
2013-11-28 10:29:01 +00:00
|
|
|
imageMap(selection)(position) = n;
|
|
|
|
return ListView_SetImage(hwnd, imageList, selection, position, n);
|
2012-08-11 02:18:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//append and assign new image
|
2013-11-28 10:29:01 +00:00
|
|
|
imageMap(selection)(position) = images.size();
|
2012-08-11 02:18:19 +00:00
|
|
|
images.append(image);
|
2013-11-28 10:32:53 +00:00
|
|
|
ImageList_Append(imageList, image, 15);
|
2013-11-28 10:29:01 +00:00
|
|
|
ListView_SetImage(hwnd, imageList, selection, position, imageMap(selection)(position));
|
2012-06-18 10:13:51 +00:00
|
|
|
}
|
|
|
|
|
2011-02-27 09:05:10 +00:00
|
|
|
void pListView::setSelected(bool selected) {
|
|
|
|
locked = true;
|
2011-10-24 11:35:34 +00:00
|
|
|
lostFocus = false;
|
2011-02-27 09:05:10 +00:00
|
|
|
if(selected == false) {
|
|
|
|
ListView_SetItemState(hwnd, -1, 0, LVIS_FOCUSED | LVIS_SELECTED);
|
|
|
|
} else {
|
|
|
|
setSelection(listView.state.selection);
|
2011-02-24 09:25:20 +00:00
|
|
|
}
|
2011-02-27 09:05:10 +00:00
|
|
|
locked = false;
|
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::setSelection(unsigned selection) {
|
2011-02-27 09:05:10 +00:00
|
|
|
locked = true;
|
2011-10-24 11:35:34 +00:00
|
|
|
lostFocus = false;
|
2013-11-28 10:29:01 +00:00
|
|
|
ListView_SetItemState(hwnd, selection, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
|
2011-02-27 09:05:10 +00:00
|
|
|
locked = false;
|
2011-02-24 09:25:20 +00:00
|
|
|
}
|
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::setText(unsigned selection, unsigned position, string text) {
|
|
|
|
utf16_t wtext(text);
|
|
|
|
ListView_SetItemText(hwnd, selection, position, wtext);
|
|
|
|
}
|
|
|
|
|
2011-02-24 09:25:20 +00:00
|
|
|
void pListView::constructor() {
|
|
|
|
lostFocus = false;
|
|
|
|
hwnd = CreateWindowEx(
|
|
|
|
WS_EX_CLIENTEDGE, WC_LISTVIEW, L"",
|
2011-09-05 03:48:23 +00:00
|
|
|
WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_NOCOLUMNHEADER,
|
2013-11-28 10:29:01 +00:00
|
|
|
0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0
|
2011-02-24 09:25:20 +00:00
|
|
|
);
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&listView);
|
|
|
|
setDefaultFont();
|
|
|
|
setHeaderText(listView.state.headerText);
|
|
|
|
setHeaderVisible(listView.state.headerVisible);
|
|
|
|
setCheckable(listView.state.checkable);
|
2013-05-02 11:25:45 +00:00
|
|
|
for(auto& text : listView.state.text) append(text);
|
2011-09-27 11:55:02 +00:00
|
|
|
for(unsigned n = 0; n < listView.state.checked.size(); n++) setChecked(n, listView.state.checked[n]);
|
2012-08-11 02:18:19 +00:00
|
|
|
buildImageList();
|
2011-02-27 09:05:10 +00:00
|
|
|
if(listView.state.selected) setSelection(listView.state.selection);
|
|
|
|
autoSizeColumns();
|
2011-09-05 03:48:23 +00:00
|
|
|
synchronize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pListView::destructor() {
|
|
|
|
DestroyWindow(hwnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pListView::orphan() {
|
|
|
|
destructor();
|
|
|
|
constructor();
|
|
|
|
}
|
|
|
|
|
2012-08-11 02:18:19 +00:00
|
|
|
void pListView::buildImageList() {
|
2013-05-02 11:25:45 +00:00
|
|
|
auto& list = listView.state.image;
|
2012-08-11 02:18:19 +00:00
|
|
|
unsigned columns = listView.state.text.size();
|
|
|
|
unsigned rows = max(1u, listView.state.headerText.size());
|
2012-06-25 12:49:39 +00:00
|
|
|
|
2012-08-11 02:18:19 +00:00
|
|
|
ListView_SetImageList(hwnd, NULL, LVSIL_SMALL);
|
|
|
|
if(imageList) ImageList_Destroy(imageList);
|
|
|
|
imageList = ImageList_Create(15, 15, ILC_COLOR32, 1, 0);
|
2012-06-18 10:13:51 +00:00
|
|
|
|
2012-08-11 02:18:19 +00:00
|
|
|
imageMap.reset();
|
|
|
|
images.reset();
|
|
|
|
images.append(nall::image()); //empty icon for cells without an image assigned (I_IMAGENONE does not work)
|
|
|
|
|
|
|
|
//create a vector of unique images from all images used (many cells may use the same image)
|
|
|
|
for(unsigned y = 0; y < list.size(); y++) {
|
|
|
|
for(unsigned x = 0; x < list[y].size(); x++) {
|
|
|
|
bool found = false;
|
|
|
|
for(unsigned z = 0; z < images.size(); z++) {
|
|
|
|
if(list[y][x] == images[z]) {
|
|
|
|
found = true;
|
|
|
|
imageMap(y)(x) = z;
|
|
|
|
break;
|
|
|
|
}
|
2012-06-25 12:49:39 +00:00
|
|
|
}
|
|
|
|
|
2012-08-11 02:18:19 +00:00
|
|
|
if(found == false) {
|
|
|
|
imageMap(y)(x) = images.size();
|
|
|
|
images.append(list[y][x]);
|
|
|
|
}
|
2012-06-18 10:13:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-11 02:18:19 +00:00
|
|
|
//build image list
|
2013-11-28 10:32:53 +00:00
|
|
|
for(auto& imageItem : images) ImageList_Append(imageList, imageItem, 15);
|
2012-08-11 02:18:19 +00:00
|
|
|
if(images.size() <= 1) return;
|
|
|
|
|
|
|
|
//set images for all cells
|
|
|
|
for(unsigned y = 0; y < columns; y++) {
|
|
|
|
for(unsigned x = 0; x < rows; x++) {
|
|
|
|
ListView_SetImage(hwnd, imageList, y, x, imageMap(y)(x));
|
2012-06-25 12:49:39 +00:00
|
|
|
}
|
|
|
|
}
|
2012-06-18 10:13:51 +00:00
|
|
|
}
|
2013-03-15 13:11:33 +00:00
|
|
|
|
2013-11-28 10:29:01 +00:00
|
|
|
void pListView::onActivate(LPARAM lparam) {
|
|
|
|
LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam;
|
|
|
|
if(listView.state.text.empty() || !listView.state.selected) return;
|
2013-12-21 10:45:58 +00:00
|
|
|
//LVN_ITEMACTIVATE is not re-entrant until DispatchMessage() completes
|
|
|
|
//if(listView.onActivate) listView.onActivate();
|
|
|
|
messageQueue.append({Message::Type::ListView_OnActivate, (Object*)&listView});
|
2013-11-28 10:29:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pListView::onChange(LPARAM lparam) {
|
|
|
|
LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam;
|
|
|
|
if(!(nmlistview->uChanged & LVIF_STATE)) return;
|
|
|
|
|
|
|
|
unsigned selection = nmlistview->iItem;
|
|
|
|
unsigned imagemask = ((nmlistview->uNewState & LVIS_STATEIMAGEMASK) >> 12) - 1;
|
|
|
|
if(imagemask == 0 || imagemask == 1) {
|
|
|
|
if(!locked) {
|
|
|
|
listView.state.checked[selection] = !listView.state.checked[selection];
|
|
|
|
if(listView.onToggle) listView.onToggle(selection);
|
|
|
|
}
|
|
|
|
} else if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) {
|
|
|
|
lostFocus = true;
|
2013-12-21 10:45:58 +00:00
|
|
|
listView.state.selected = false;
|
|
|
|
listView.state.selection = 0;
|
2013-11-28 10:29:01 +00:00
|
|
|
} else if(!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED)) {
|
|
|
|
lostFocus = false;
|
|
|
|
listView.state.selected = true;
|
|
|
|
listView.state.selection = selection;
|
|
|
|
if(!locked && listView.onChange) listView.onChange();
|
|
|
|
} else if(!lostFocus && !listView.state.selected) {
|
|
|
|
lostFocus = false;
|
|
|
|
listView.state.selected = false;
|
|
|
|
listView.state.selection = 0;
|
|
|
|
if(!locked && listView.onChange) listView.onChange();
|
2013-12-21 10:45:58 +00:00
|
|
|
} else if(listView.selected() && ListView_GetSelectedCount(hwnd) == 0) {
|
|
|
|
listView.state.selected = false;
|
|
|
|
listView.state.selection = 0;
|
|
|
|
if(!locked && listView.onChange) listView.onChange();
|
2013-11-28 10:29:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT pListView::onCustomDraw(LPARAM lparam) {
|
|
|
|
LPNMLVCUSTOMDRAW lvcd = (LPNMLVCUSTOMDRAW)lparam;
|
|
|
|
|
|
|
|
switch(lvcd->nmcd.dwDrawStage) {
|
|
|
|
case CDDS_PREPAINT:
|
|
|
|
return CDRF_NOTIFYITEMDRAW;
|
|
|
|
case CDDS_ITEMPREPAINT:
|
|
|
|
if(listView.state.headerText.size() >= 2) {
|
|
|
|
//draw alternating row colors of there are two or more columns
|
|
|
|
if(lvcd->nmcd.dwItemSpec % 2) lvcd->clrTextBk = GetSysColor(COLOR_WINDOW) ^ 0x070707;
|
|
|
|
}
|
|
|
|
return CDRF_DODEFAULT;
|
|
|
|
default:
|
|
|
|
return CDRF_DODEFAULT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-15 13:11:33 +00:00
|
|
|
}
|