#include "main.h" #include "./listWidget.h" #include "./listWidgetInternal.h" #include #include #define LISTWIDGET_OFFSET_LEFT_DLU 0 #define LISTWIDGET_OFFSET_TOP_DLU 1 #define LISTWIDGET_OFFSET_BOTTOM_DLU 2 #define LISTWIDGET_CATEGORY_OFFSET_TOP_DLU 0 #define LISTWIDGET_CATEGORY_OFFSET_LEFT_DLU 1 #define LISTWIDGET_CATEGORY_SPACING_DLU 2 #define LISTWIDGET_CATEGORY_SPACING_COLLAPSED_DLU 0 #define LISTWIDGET_ITEM_OFFSET_TOP_DLU 1 #define LISTWIDGET_ITEM_OFFSET_LEFT_DLU 2 #define LISTWIDGET_ITEM_SPACING_HORZ_DLU 2 #define LISTWIDGET_ITEM_SPACING_VERT_DLU 10 #define LISTWIDGET_ITEM_TITLE_MAX_LINES 3 #define LISTWIDGET_IMAGE_MIN_HEIGHT 48 #define LISTWIDGET_IMAGE_MAX_HEIGHT 256 #define LISTWIDGET_IMAGE_DEFAULT_HEIGHT 160//128 #define LISTWIDGET_IMAGE_DEFAULT_WIDTH 160//96 #define LISTWIDGETTIMER_SHOW_COMMANDS_ID 3 #define LISTWIDGETTIMER_SHOW_COMMANDS_DELAY 75 #define LISTWIDGETTIMER_PROGRESS_TICK_ID 4 #define LISTWIDGETTIMER_PROGRESS_TICK_DELAY 130 #define LISTWIDGETTIMER_EDIT_TITLE_ID 5 #define LISTWIDGET_CONNECTION_MIN_HEIGHT 20 #define LISTWIDGET_CONNECTION_MAX_HEIGHT 48 #define LISTWIDGET_CONNECTION_DEFAULT_HEIGHT 36 #define LISTWIDGET_PRIMARYCOMMAND_MIN_HEIGHT 16 #define LISTWIDGET_PRIMARYCOMMAND_MAX_HEIGHT 48 #define LISTWIDGET_PRIMARYCOMMAND_DEFAULT_HEIGHT 36 #define LISTWIDGET_SECONDARYCOMMAND_MIN_HEIGHT 14 #define LISTWIDGET_SECONDARYCOMMAND_MAX_HEIGHT 36 #define LISTWIDGET_SECONDARYCOMMAND_DEFAULT_HEIGHT 20 #define LISTWIDGET_ACTIVITY_MIN_HEIGHT 16 #define LISTWIDGET_ACTIVITY_MAX_HEIGHT 48 #define LISTWIDGET_ACTIVITY_DEFAULT_HEIGHT 36 #define LISTWIDGET_PROGRESS_MIN_HEIGHT 16 #define LISTWIDGET_PROGRESS_FRAME_COUNT 9/*12*/ typedef std::vector DeviceList; static ListWidgetCategory * ListWidget_CreateCategoryHelper(const char *name, int titleId, wchar_t *buffer, size_t bufferMax) { WASABI_API_LNGSTRINGW_BUF(titleId, buffer, bufferMax); return ListWidget_CreateCategory(name, buffer, Config_ReadBool("CollapsedCategories", name, FALSE)); } static void ListWidget_CreateDefaultCategories(ListWidget *self) { ListWidgetCategory *category; wchar_t buffer[512] = {0}; category = ListWidget_CreateCategoryHelper("attached", IDS_CATEGORY_ATTACHED, buffer, ARRAYSIZE(buffer)); if (NULL != category) { WASABI_API_LNGSTRINGW_BUF(IDS_CATEGORY_ATTACHED_EMPTY_TEXT, buffer, ARRAYSIZE(buffer)); ListWidget_SetCategoryEmptyText(category, buffer); self->categories.push_back(category); } category = ListWidget_CreateCategoryHelper("discovered", IDS_CATEGORY_DISCOVERED, buffer, ARRAYSIZE(buffer)); if (NULL != category) self->categories.push_back(category); } BOOL ListWidget_GetViewOrigin(HWND hwnd, POINT *pt) { SCROLLINFO scrollInfo; if (NULL == pt) return FALSE; scrollInfo.cbSize = sizeof(scrollInfo); scrollInfo.fMask = SIF_POS; if (FALSE == GetScrollInfo(hwnd, SB_HORZ, &scrollInfo)) return FALSE; pt->x = -scrollInfo.nPos; if (FALSE == GetScrollInfo(hwnd, SB_VERT, &scrollInfo)) return FALSE; pt->y = -scrollInfo.nPos; return TRUE; } static HBITMAP ListWidget_CreateSpacebarBitmap(HBITMAP sourceBitmap, HWND hwnd, long width, long height) { HDC windowDC, sourceDC, resultDC; HBITMAP resultBitmap; BITMAP sourceInfo; RECT resultRect, sourceRect; if (NULL == sourceBitmap || sizeof(sourceInfo) != GetObject(sourceBitmap, sizeof(sourceInfo), &sourceInfo)) { return FALSE; } windowDC = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); if (NULL == windowDC) return NULL; sourceDC = CreateCompatibleDC(windowDC); resultDC = CreateCompatibleDC(windowDC); resultBitmap = CreateCompatibleBitmap(windowDC, width, height*2); ReleaseDC(hwnd, windowDC); if (NULL != sourceDC && NULL != resultDC && NULL != resultBitmap) { HBITMAP prevSourceBitmap, prevResultBitmap; prevSourceBitmap = SelectBitmap(sourceDC, sourceBitmap); prevResultBitmap = SelectBitmap(resultDC, resultBitmap); SetRect(&resultRect, 0, 0, width, height); SetRect(&sourceRect, 0, 0, sourceInfo.bmWidth, ABS(sourceInfo.bmHeight)/2); Image_FillBorder(resultDC, &resultRect, sourceDC, &sourceRect, TRUE, 255); OffsetRect(&resultRect, 0, height); OffsetRect(&sourceRect, 0, RECTHEIGHT(sourceRect)); Image_FillBorder(resultDC, &resultRect, sourceDC, &sourceRect, TRUE, 255); SelectBitmap(sourceDC, prevSourceBitmap); SelectBitmap(resultDC, prevResultBitmap); } if (NULL != sourceDC) DeleteDC(sourceDC); if (NULL != resultDC) DeleteDC(resultDC); if (NULL != resultBitmap) { RECT imageRect; SetRect(&imageRect, 0, 0, width, height); Image_Premultiply(resultBitmap, &imageRect); } return resultBitmap; } HBITMAP ListWidget_GetSpacebarBitmap(ListWidget *self, WidgetStyle *style, HWND hwnd, long width, long height) { if (NULL == self) return NULL; if (NULL != self->spacebarBitmap) { BITMAP bi; if (sizeof(bi) != GetObject(self->spacebarBitmap, sizeof(bi), &bi) || bi.bmWidth != width || bi.bmHeight/2 != height) { DeleteObject(self->spacebarBitmap); self->spacebarBitmap = NULL; } } if (NULL == self->spacebarBitmap && NULL != style) { HBITMAP baseBitmap; baseBitmap = Image_Load(MAKEINTRESOURCE(IDR_SPACEBAR_IMAGE), SRC_TYPE_PNG, 0, 0, 0); //WIDGETSTYLE_IMAGE_BACK_COLOR(style), // WIDGETSTYLE_IMAGE_FRONT_COLOR(style), // WIDGETSTYLE_BACK_COLOR(style)); if (NULL != baseBitmap) { DIBSECTION bitmapData; if (sizeof(bitmapData) == GetObjectW(baseBitmap, sizeof(bitmapData), &bitmapData)) { BITMAP *bi; long bitmapHeight; void *pixels; WORD backHue, backLuma, backSat, frontHue, frontLuma, frontSat; bi = &bitmapData.dsBm; bitmapHeight = ABS(bi->bmHeight); pixels = ((BYTE*)bi->bmBits) + (bi->bmWidthBytes * (bitmapHeight - bitmapHeight/2)); ColorRGBToHLS(WIDGETSTYLE_IMAGE_BACK_COLOR(style), &backHue, &backLuma, &backSat); ColorRGBToHLS(WIDGETSTYLE_IMAGE_FRONT_COLOR(style), &frontHue, &frontLuma, &frontSat); if (backLuma > frontLuma) { COLORREF backColor; frontLuma = 25; backColor = ColorHLSToRGB(frontHue, frontLuma, frontSat); Image_FilterEx(pixels, bi->bmWidth, bitmapHeight/2, bi->bmBitsPixel, 0, backColor, WIDGETSTYLE_IMAGE_BACK_COLOR(style), WIDGETSTYLE_BACK_COLOR(style)); } else { Image_FilterEx(pixels, bi->bmWidth, bitmapHeight/2, bi->bmBitsPixel, 0, WIDGETSTYLE_IMAGE_BACK_COLOR(style), WIDGETSTYLE_IMAGE_FRONT_COLOR(style), WIDGETSTYLE_BACK_COLOR(style)); } } Image_Premultiply(baseBitmap, NULL); self->spacebarBitmap = ListWidget_CreateSpacebarBitmap(baseBitmap, hwnd, width, height); DeleteObject(baseBitmap); } } return self->spacebarBitmap; } static HBITMAP ListWidget_CreateBorderBitmap(HBITMAP sourceBitmap, HWND hwnd, long width, long height) { HDC windowDC, sourceDC, resultDC; HBITMAP resultBitmap; BITMAP sourceInfo; RECT resultRect, sourceRect; if (NULL == sourceBitmap || sizeof(sourceInfo) != GetObject(sourceBitmap, sizeof(sourceInfo), &sourceInfo)) { return FALSE; } SetRect(&resultRect, 0, 0, width, height); SetRect(&sourceRect, 0, 0, sourceInfo.bmWidth, ABS(sourceInfo.bmHeight)); windowDC = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); if (NULL == windowDC) return NULL; sourceDC = CreateCompatibleDC(windowDC); resultDC = CreateCompatibleDC(windowDC); resultBitmap = CreateCompatibleBitmap(windowDC, width, height); ReleaseDC(hwnd, windowDC); if (NULL != sourceDC && NULL != resultDC && NULL != resultBitmap) { HBITMAP prevSourceBitmap, prevResultBitmap; prevSourceBitmap = SelectBitmap(sourceDC, sourceBitmap); prevResultBitmap = SelectBitmap(resultDC, resultBitmap); Image_FillBorder(resultDC, &resultRect, sourceDC, &sourceRect, TRUE, 255); SelectBitmap(sourceDC, prevSourceBitmap); SelectBitmap(resultDC, prevResultBitmap); } if (NULL != sourceDC) DeleteDC(sourceDC); if (NULL != resultDC) DeleteDC(resultDC); return resultBitmap; } static HBITMAP ListWidget_GetBorderBitmap(HBITMAP bitmap, const wchar_t *path, WidgetStyle *style, HWND hwnd, long width, long height, BOOL disableSkin, COLORREF colorBack, COLORREF colorFront) { if (NULL != bitmap) { BITMAP bi; if (sizeof(bi) != GetObject(bitmap, sizeof(bi), &bi) || bi.bmWidth != width || bi.bmHeight != height) { DeleteObject(bitmap); bitmap = NULL; } } if (NULL == bitmap && NULL != style) { HBITMAP baseBitmap; if (FALSE == disableSkin) { baseBitmap = Image_LoadSkinned(path, SRC_TYPE_PNG, IMAGE_FILTER_NORMAL, 0, 0, colorBack, colorFront, WIDGETSTYLE_BACK_COLOR(style)); } else { baseBitmap = Image_Load(path, SRC_TYPE_PNG, 0, 0, 0); } if (NULL != baseBitmap) { Image_Premultiply(baseBitmap, NULL); bitmap = ListWidget_CreateBorderBitmap(baseBitmap, hwnd, width, height); DeleteObject(baseBitmap); } } return bitmap; } HBITMAP ListWidget_GetHoverBitmap(ListWidget *self, WidgetStyle *style, HWND hwnd, long width, long height) { if (NULL == self) return NULL; self->hoverBitmap = ListWidget_GetBorderBitmap(self->hoverBitmap, MAKEINTRESOURCE(IDR_ITEM_HOVER_IMAGE), style, hwnd, width, height, FALSE, WIDGETSTYLE_SELECT_BACK_COLOR(style), WIDGETSTYLE_SELECT_FRONT_COLOR(style)); return self->hoverBitmap; } HBITMAP ListWidget_GetSelectBitmap(ListWidget *self, WidgetStyle *style, HWND hwnd, long width, long height) { if (NULL == self) return NULL; self->selectBitmap = ListWidget_GetBorderBitmap(self->selectBitmap, MAKEINTRESOURCE(IDR_ITEM_SELECT_IMAGE), style, hwnd, width, height, FALSE, WIDGETSTYLE_SELECT_BACK_COLOR(style), WIDGETSTYLE_SELECT_FRONT_COLOR(style)); return self->selectBitmap; } HBITMAP ListWidget_GetInactiveSelectBitmap(ListWidget *self, WidgetStyle *style, HWND hwnd, long width, long height) { if (NULL == self) return NULL; self->inactiveSelectBitmap = ListWidget_GetBorderBitmap(self->inactiveSelectBitmap, MAKEINTRESOURCE(IDR_ITEM_SELECT_IMAGE), style, hwnd, width, height, FALSE, WIDGETSTYLE_INACTIVE_SELECT_BACK_COLOR(style), WIDGETSTYLE_INACTIVE_SELECT_FRONT_COLOR(style)); return self->inactiveSelectBitmap; } HBITMAP ListWidget_GetLargeBadgeBitmap(ListWidget *self, WidgetStyle *style, HWND hwnd, long width, long height) { if (NULL == self) return NULL; self->largeBadgeBitmap = ListWidget_GetBorderBitmap(self->largeBadgeBitmap, MAKEINTRESOURCE(IDR_COMMAND_BACKGROUND_IMAGE), style, hwnd, width, height, TRUE, WIDGETSTYLE_IMAGE_BACK_COLOR(style), WIDGETSTYLE_IMAGE_FRONT_COLOR(style)); return self->largeBadgeBitmap; } HBITMAP ListWidget_GetSmallBadgeBitmap(ListWidget *self, WidgetStyle *style, HWND hwnd, long width, long height) { if (NULL == self) return NULL; self->smallBadgeBitmap = ListWidget_GetBorderBitmap(self->smallBadgeBitmap, MAKEINTRESOURCE(IDR_COMMAND_SECONDARY_BACKGROUND_IMAGE), style, hwnd, width, height, TRUE, WIDGETSTYLE_IMAGE_BACK_COLOR(style), WIDGETSTYLE_IMAGE_FRONT_COLOR(style)); return self->smallBadgeBitmap; } HBITMAP ListWidget_GetArrowsBitmap(ListWidget *self, WidgetStyle *style, HWND hwnd) { if (NULL == self) return NULL; if (NULL == self->arrowsBitmap && NULL != style) { self->arrowsBitmap = Image_LoadSkinned(MAKEINTRESOURCE(IDR_CATEGORY_ARROWS_IMAGE), SRC_TYPE_PNG, IMAGE_FILTER_NORMAL, 0, 0, WIDGETSTYLE_CATEGORY_BACK_COLOR(style), WIDGETSTYLE_CATEGORY_TEXT_COLOR(style), WIDGETSTYLE_CATEGORY_BACK_COLOR(style)); if (NULL != self->arrowsBitmap) Image_Premultiply(self->arrowsBitmap, NULL); } return self->arrowsBitmap; } HBITMAP ListWidget_GetUnknownCommandLargeBitmap(ListWidget *self, WidgetStyle *style, long width, long height) { if (NULL == self) return NULL; if (NULL == self->unknownCommandLargeImage && NULL != style) { self->unknownCommandLargeImage = DeviceImageCache_GetImage(Plugin_GetImageCache(), MAKEINTRESOURCE(IDR_UNKNOWN_COMMAND_LARGE_IMAGE), width, height, NULL, NULL); } return DeviceImage_GetBitmap(self->unknownCommandLargeImage, DeviceImage_Normal); } HBITMAP ListWidget_GetUnknownCommandSmallBitmap(ListWidget *self, WidgetStyle *style, long width, long height) { if (NULL == self) return NULL; if (NULL == self->unknownCommandSmallImage && NULL != style) { self->unknownCommandSmallImage = DeviceImageCache_GetImage(Plugin_GetImageCache(), MAKEINTRESOURCE(IDR_UNKNOWN_COMMAND_LARGE_IMAGE), width, height, NULL, NULL); } return DeviceImage_GetBitmap(self->unknownCommandSmallImage, DeviceImage_Normal); } HBITMAP ListWidget_GetActivityProgressBitmap(ListWidget *self, WidgetStyle *style) { if (NULL == self) return NULL; if (NULL == self->activityProgressImage && NULL != style) { self->activityProgressImage = DeviceImageCache_GetImage(Plugin_GetImageCache(), MAKEINTRESOURCE(IDR_PROGRESS_SMALL_IMAGE), self->activityMetrics.progressWidth, self->activityMetrics.progressHeight * LISTWIDGET_PROGRESS_FRAME_COUNT, NULL, NULL); } return DeviceImage_GetBitmap(self->activityProgressImage, DeviceImage_Normal); } HBITMAP ListWidget_GetActivityBadgeBitmap(ListWidget *self, WidgetStyle *style, HWND hwnd, long width, long height) { if (NULL == self) return NULL; self->activityBadgeBitmap = ListWidget_GetBorderBitmap(self->activityBadgeBitmap, MAKEINTRESOURCE(IDR_ACTION_BACKGROUND_IMAGE), style, hwnd, width, height, TRUE, WIDGETSTYLE_IMAGE_BACK_COLOR(style), WIDGETSTYLE_IMAGE_FRONT_COLOR(style)); return self->activityBadgeBitmap; } static BOOL ListWidget_UpdateCommandsLayout(ListWidget *self, HWND hwnd) { WidgetStyle *style; ListWidgetItemMetric metrics; RECT rect; size_t index, indexMax; long spacing; if (NULL == self) return FALSE; style = WIDGET_GET_STYLE(hwnd); if (NULL == style) return FALSE; if (0 == self->commandsCount) return TRUE; if (FALSE == ListWidget_GetItemMetrics(style, &metrics)) return FALSE; indexMax = self->commandsCount; if ((NULL == self->hoveredItem || NULL == self->hoveredItem->activity) && FALSE != ListWidget_GetCommandPrimary(self->commands[0])) { rect.bottom = self->imageSize.cy /*+ metrics.imageOffsetBottom*/; rect.bottom += metrics.offsetTop + metrics.imageOffsetTop; rect.top = rect.bottom - self->primaryCommandSize.cy; rect.left = (self->itemWidth - self->primaryCommandSize.cx)/2; rect.right = rect.left + self->primaryCommandSize.cx; ListWidget_SetCommandRect(self->commands[0], &rect); indexMax--; } rect.top = metrics.offsetTop + metrics.imageOffsetTop; rect.bottom = rect.top + self->secondaryCommandSize.cy; rect.right = self->itemWidth - metrics.offsetRight - metrics.imageOffsetRight; rect.left = rect.right - self->secondaryCommandSize.cx; spacing = self->secondaryCommandSize.cx/16 + 1; for(index = 0; index < indexMax; index++) { ListWidget_SetCommandRect(self->commands[self->commandsCount - index - 1], &rect); OffsetRect(&rect, -(self->secondaryCommandSize.cx + spacing), 0); } return TRUE; } static BOOL ListWidget_UpdateActiveCommands(ListWidget *self, HWND hwnd) { ListWidgetCommand **clone; size_t cloneSize, index; BOOL invalidated; POINT origin, pt; RECT rect; if (NULL == self) return FALSE; if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) { origin.x = 0; origin.y = 0; } if (NULL == self->hoveredItem) { ListWidget_DestroyAllCommands(self->commands, self->commandsCount); self->commandsCount = 0; return TRUE; } origin.x += self->hoveredItem->rect.left; origin.y += self->hoveredItem->rect.top; invalidated = FALSE; if (0 != self->commandsCount) { for(index = 0; index < self->commandsCount; index++) { if (FALSE != ListWidget_GetCommandRect(self->commands[index], &rect)) { OffsetRect(&rect, origin.x, origin.y); if (FALSE != InvalidateRect(hwnd, &rect, FALSE)) invalidated = TRUE; } } clone = (ListWidgetCommand**)malloc(sizeof(ListWidgetCommand*) * self->commandsCount); if (NULL != clone) { cloneSize = self->commandsCount; CopyMemory(clone, self->commands, sizeof(ListWidgetCommand*) * cloneSize); } else { cloneSize = 0; ListWidget_DestroyAllCommands(self->commands, self->commandsCount); } } else { clone = NULL; cloneSize = 0; } self->commandsCount = ListWidget_GetItemCommands(self->hoveredItem, self->commands, self->commandsMax); if (0 != self->commandsCount) { ListWidget_UpdateCommandsLayout(self, hwnd); ListWidgetItem_SetInteractive(self->hoveredItem); for(index = 0; index < self->commandsCount; index++) { if (FALSE != ListWidget_GetCommandRect(self->commands[index], &rect)) { OffsetRect(&rect, origin.x, origin.y); if (FALSE != InvalidateRect(hwnd, &rect, FALSE)) invalidated = TRUE; } } } else ListWidgetItem_UnsetInteractive(self->hoveredItem); if (NULL != clone) { ListWidget_DestroyAllCommands(clone, cloneSize); free(clone); } if (FALSE != GetCursorPos(&pt)) { ListWidgetItem *tooltipItem; ListWidgetItemPart tooltipItemPart; RECT tooltipItemPartRect; tooltipItem = ListWidget_TooltipGetCurrent(self->tooltip, &tooltipItemPart, &tooltipItemPartRect); MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); ListWidget_UpdateHoverEx(self, hwnd, &pt); if (FALSE != ListWidget_TooltipGetChanged(self->tooltip, tooltipItem, tooltipItemPart, &tooltipItemPartRect)) { ListWidget_TooltipRelayMouseMessage(self->tooltip, WM_MOUSEMOVE, 0, &pt); } } if (FALSE != invalidated) UpdateWindow(hwnd); return TRUE; } static void CALLBACK ListWidget_ShowCommandTimerCb(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elpasedTime) { ListWidget *self; KillTimer(hwnd, eventId); self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self) return; if (NULL != self->hoveredItem && FALSE == ListWidgetItem_IsInteractive(self->hoveredItem) && NULL == self->activeMenu) { ListWidget_UpdateActiveCommands(self, hwnd); } } static void CALLBACK ListWidget_ProgressTickCb(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elpasedTime) { ListWidget *self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self) { KillTimer(hwnd, eventId); return; } size_t index = self->activeItems.size(); if (index > 0) { POINT origin; RECT rect; ListWidgetItemMetric metrics, *metrics_ptr; WidgetStyle *style; if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) { origin.x = 0; origin.y = 0; } style = WIDGET_GET_STYLE(hwnd); if (NULL != style && FALSE != ListWidget_GetItemMetrics(style, &metrics)) metrics_ptr = &metrics; else metrics_ptr = NULL; while(index--) { ListWidgetItem *item = self->activeItems[index]; item->activity->step++; if (item->activity->step >= LISTWIDGET_PROGRESS_FRAME_COUNT) item->activity->step = 1; if (FALSE != ListWidget_GetItemActivityProgressRect(self, NULL, item, metrics_ptr, &rect)) { OffsetRect(&rect, origin.x, origin.y); InvalidateRect(hwnd, &rect, FALSE); } } } else { KillTimer(hwnd, eventId); self->activityTimerEnabled = FALSE; } } static void CALLBACK ListWidget_BeginTitleEditTimerCb(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elpasedTime) { ListWidget *self; self = WIDGET_GET_SELF(hwnd, ListWidget); KillTimer(hwnd, eventId); if (NULL != self && NULL != self->titleEditItem) { if (self->titleEditItem == self->selectedItem) { if (NULL != self->titleEditor) { DestroyWindow(self->titleEditor); self->titleEditor = NULL; } self->titleEditor = ListWidget_BeginItemTitleEdit(self, hwnd, self->titleEditItem); } self->titleEditItem = NULL; } } BOOL ListWidget_UpdateHoverEx(ListWidget *self, HWND hwnd, const POINT *cursor) { ListWidgetItem *hoveredItem; ListWidgetItemMetric metrics, *metrics_ptr; WidgetStyle *style; ListWidgetItemPart hoveredPart = ListWidgetItemPart_None; RECT rect, hoveredPartRect; POINT pt, origin; if (NULL == cursor) return FALSE; metrics_ptr = NULL; pt = *cursor; if (NULL != self->pressedCategory || NULL != self->activeMenu || 0 != (0x8000 & GetAsyncKeyState(VK_LBUTTON)) || 0 != (0x8000 & GetAsyncKeyState(VK_RBUTTON))) { return FALSE; } if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) { origin.x = 0; origin.y = 0; } if (FALSE != GetClientRect(hwnd, &rect) && FALSE != PtInRect(&rect, pt)) { pt.x -= origin.x; pt.y -= origin.y; hoveredItem = ListWidget_GetItemFromPoint(self, pt); if (NULL != hoveredItem) { if (NULL == metrics_ptr) { style = WIDGET_GET_STYLE(hwnd); if (NULL != style && FALSE != ListWidget_GetItemMetrics(style, &metrics)) { metrics_ptr = &metrics; } } hoveredPart = ListWidgetItemPart_Frame | ListWidgetItemPart_Command | ListWidgetItemPart_Spacebar | ListWidgetItemPart_Title; hoveredPart = ListWidget_GetItemPartFromPoint(self, hoveredItem, metrics_ptr, pt, hoveredPart, &hoveredPartRect); } } else hoveredItem = NULL; if (NULL == hoveredItem) hoveredPart= ListWidgetItemPart_None; ListWidget_TooltipUpdate(self->tooltip, hoveredItem, hoveredPart, &hoveredPartRect); if (ListWidgetItemPart_None == ((ListWidgetItemPart_Frame | ListWidgetItemPart_Command | ListWidgetItemPart_Activity) & hoveredPart)) hoveredItem = NULL; if (self->hoveredItem == hoveredItem) return FALSE; if (NULL == metrics_ptr) { style = WIDGET_GET_STYLE(hwnd); if (NULL != style && FALSE != ListWidget_GetItemMetrics(style, &metrics)) { metrics_ptr = &metrics; } } if (NULL != self->hoveredItem) { ListWidgetItem_UnsetHovered(self->hoveredItem); ListWidgetItem_UnsetInteractive(self->hoveredItem); if (NULL == metrics_ptr || FALSE == ListWidget_GetItemFrameRect(self, self->hoveredItem, metrics_ptr, &rect)) { CopyRect(&rect, &self->hoveredItem->rect); } OffsetRect(&rect, origin.x, origin.y); InvalidateRect(hwnd, &rect, FALSE); } if (NULL != hoveredItem) { ListWidgetItem_SetHovered(hoveredItem); if (NULL == metrics_ptr || FALSE == ListWidget_GetItemFrameRect(self, hoveredItem, metrics_ptr, &rect)) { CopyRect(&rect, &hoveredItem->rect); } OffsetRect(&rect, origin.x, origin.y); InvalidateRect(hwnd, &rect, FALSE); SetTimer(hwnd, LISTWIDGETTIMER_SHOW_COMMANDS_ID, LISTWIDGETTIMER_SHOW_COMMANDS_DELAY, ListWidget_ShowCommandTimerCb); } else KillTimer(hwnd, LISTWIDGETTIMER_SHOW_COMMANDS_ID); self->hoveredItem = hoveredItem; return TRUE; } BOOL ListWidget_UpdateHover(ListWidget *self, HWND hwnd) { POINT pt; if (FALSE == GetCursorPos(&pt)) return FALSE; MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); return ListWidget_UpdateHoverEx(self, hwnd, &pt); } BOOL ListWidget_RemoveHover(ListWidget *self, HWND hwnd, BOOL invalidate) { if (NULL == self) return FALSE; if (NULL == self->hoveredItem) return FALSE; ListWidgetItem_UnsetHovered(self->hoveredItem); ListWidgetItem_UnsetInteractive(self->hoveredItem); KillTimer(hwnd, LISTWIDGETTIMER_SHOW_COMMANDS_ID); if (FALSE != invalidate) { RECT rect; POINT origin; ListWidgetItemMetric metrics; WidgetStyle *style; style = WIDGET_GET_STYLE(hwnd); if (NULL == style || FALSE == ListWidget_GetItemMetrics(style, &metrics) || FALSE == ListWidget_GetItemFrameRect(self, self->hoveredItem, &metrics, &rect)) { CopyRect(&rect, &self->hoveredItem->rect); } if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) OffsetRect(&rect, origin.x, origin.y); InvalidateRect(hwnd, &rect, FALSE); } self->hoveredItem = NULL; return TRUE; } void ListWidget_UpdateSelectionStatus(ListWidget *self, HWND hwnd, BOOL ensureVisible) { HWND statusWindow; if (NULL == self) return; statusWindow = GetParent(hwnd); if (NULL == statusWindow) return; statusWindow = MANAGERVIEW_GET_STATUS_BAR(statusWindow); if (NULL == statusWindow) return; if (NULL != self->selectedItem) { wchar_t buffer[2048] = {0}; const wchar_t *statusString; if (FALSE == ListWidget_FormatItemStatus(self, self->selectedItem, buffer, ARRAYSIZE(buffer)) || L'\0' == *buffer) { statusString = NULL; } else statusString = buffer; if (STATUS_ERROR == self->selectionStatus) { self->selectionStatus = STATUSBAR_ADD_STATUS(statusWindow, statusString); if (STATUS_ERROR != self->selectionStatus) { if (FALSE == ListWidget_FormatItemSpaceStatus(self, self->selectedItem, buffer, ARRAYSIZE(buffer)) || L'\0' == *buffer) { statusString = NULL; } else statusString = buffer; STATUSBAR_SET_STATUS_RTEXT(statusWindow, self->selectionStatus, statusString); } } else { STATUSBAR_SET_STATUS_TEXT(statusWindow, self->selectionStatus, statusString); if (FALSE == ListWidget_FormatItemSpaceStatus(self, self->selectedItem, buffer, ARRAYSIZE(buffer)) || L'\0' == *buffer) { statusString = NULL; } else statusString = buffer; STATUSBAR_SET_STATUS_RTEXT(statusWindow, self->selectionStatus, statusString); if (FALSE != ensureVisible) { STATUSBAR_MOVE_STATUS(statusWindow, self->selectionStatus, STATUS_MOVE_TOP); } } } else { if (STATUS_ERROR != self->selectionStatus) { STATUSBAR_REMOVE_STATUS(statusWindow, self->selectionStatus); self->selectionStatus = STATUS_ERROR; } } } void ListWidget_UpdateSelectionSpaceStatus(ListWidget *self, HWND hwnd, BOOL ensureVisible) { HWND statusWindow; if (NULL == self) return; statusWindow = GetParent(hwnd); if (NULL == statusWindow) return; statusWindow = MANAGERVIEW_GET_STATUS_BAR(statusWindow); if (NULL == statusWindow) return; if (NULL != self->selectedItem && STATUS_ERROR != self->selectionStatus) { wchar_t buffer[2048] = {0}; const wchar_t *statusString; if (FALSE == ListWidget_FormatItemSpaceStatus(self, self->selectedItem, buffer, ARRAYSIZE(buffer)) || L'\0' == *buffer) { statusString = NULL; } else statusString = buffer; STATUSBAR_SET_STATUS_RTEXT(statusWindow, self->selectionStatus, statusString); if (FALSE != ensureVisible) STATUSBAR_MOVE_STATUS(statusWindow, self->selectionStatus, STATUS_MOVE_TOP); } } BOOL ListWidget_SelectItem(ListWidget *self, HWND hwnd, ListWidgetItem *item, BOOL ensureVisible) { BOOL invalidated; if (NULL == self) return FALSE; invalidated = FALSE; if (self->selectedItem != item) { RECT rect; POINT origin; ListWidgetItemMetric metrics, *metrics_ptr; WidgetStyle *style; if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) { origin.x = 0; origin.y = 0; } style = WIDGET_GET_STYLE(hwnd); if (NULL != style && FALSE != ListWidget_GetItemMetrics(style, &metrics)) { metrics_ptr = &metrics; } else metrics_ptr = NULL; if (NULL != self->selectedItem) { ListWidgetItem_UnsetSelected(self->selectedItem); if (NULL == metrics_ptr || FALSE == ListWidget_GetItemFrameRect(self, self->selectedItem, metrics_ptr, &rect)) { CopyRect(&rect, &self->selectedItem->rect); } OffsetRect(&rect, origin.x, origin.y); if (FALSE != InvalidateRect(hwnd, &rect, FALSE)) invalidated = TRUE; } if (NULL != item) { ListWidgetItem_SetSelected(item); if (NULL == metrics_ptr || FALSE == ListWidget_GetItemFrameRect(self, item, metrics_ptr, &rect)) { CopyRect(&rect, &item->rect); } OffsetRect(&rect, origin.x, origin.y); if (FALSE != InvalidateRect(hwnd, &rect, FALSE)) invalidated = TRUE; } self->selectedItem = item; } if (FALSE != ensureVisible) { if (FALSE != ListWidget_EnsureItemVisisble(self, hwnd, item, VISIBLE_NORMAL)) invalidated = TRUE; } if (FALSE != invalidated) UpdateWindow(hwnd); ListWidget_UpdateSelectionStatus(self, hwnd, TRUE); return TRUE; } static void ListWidget_EnsureFocused(ListWidget *self, HWND hwnd, BOOL forceSelect) { HWND hDialog, hParent; if (NULL == self || NULL == hwnd) return; if (GetFocus() == hwnd) return; hDialog = NULL; hParent = hwnd; while(NULL != (hParent = GetAncestor(hParent, GA_PARENT))) { if (32770 != GetClassLongPtr(hParent, GCW_ATOM) || 0 == (WS_EX_CONTROLPARENT & GetWindowStyleEx(hParent))) { break; } hDialog = hParent; } self->flags |= ListWidgetFlag_NoFocusSelect; if (NULL != hDialog) SendMessage(hDialog, WM_NEXTDLGCTL, (WPARAM)hwnd, TRUE); else SetFocus(hwnd); self->flags &= ~ListWidgetFlag_NoFocusSelect; } static BOOL ListWidget_EnsureTopVisible(ListWidget *self, HWND hwnd) { POINT pt; if (NULL == self || NULL == hwnd) return FALSE; if (FALSE == ListWidget_GetViewOrigin(hwnd, &pt) || (0 == pt.x && 0 == pt.y)) { return FALSE; } if (FALSE == WIDGET_SCROLL(hwnd, pt.x, pt.y, TRUE)) return FALSE; ListWidget_UpdateHover(self, hwnd); return TRUE; } static BOOL ListWidget_EnsureBottomVisible(ListWidget *self, HWND hwnd) { SCROLLINFO scrollInfo; POINT pt; if (NULL == self || NULL == hwnd) return FALSE; scrollInfo.cbSize = sizeof(scrollInfo); scrollInfo.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; if (FALSE != GetScrollInfo(hwnd, SB_HORZ, &scrollInfo)) pt.x = -scrollInfo.nPos; else pt.x = 0; if (FALSE != GetScrollInfo(hwnd, SB_VERT, &scrollInfo) && 0 != scrollInfo.nPage) pt.y = (scrollInfo.nMax + 1 - scrollInfo.nPage) - scrollInfo.nPos; else pt.y = 0; if (0 == pt.x && 0 == pt.y) return FALSE; if (FALSE == WIDGET_SCROLL(hwnd, pt.x, pt.y, TRUE)) return FALSE; ListWidget_UpdateHover(self, hwnd); return TRUE; } BOOL ListWidget_UpdateLayout(HWND hwnd, ListWidgetLayoutFlags layoutFlags) { BOOL result; unsigned int flags; ListWidget *self; POINT anchorPoint; ListWidgetItem *anchorItem; SCROLLINFO scrollInfo; HWND managerWindow, focusWindow; POINT pt, menuAnchor, hoverAnchor; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self) return FALSE; if (NULL == self->activeMenu || NULL == self->selectedItem || FALSE == ListWidget_GetViewItemPos(hwnd, self->selectedItem, &menuAnchor)) { menuAnchor.x = 0x7FFFFFFF; menuAnchor.y = 0x7FFFFFFF; } if (NULL != self->hoveredItem || FALSE == ListWidget_GetViewItemPos(hwnd, self->hoveredItem, &hoverAnchor)) { hoverAnchor.x = 0x7FFFFFFF; hoverAnchor.y = 0x7FFFFFFF; } scrollInfo.cbSize = sizeof(scrollInfo); scrollInfo.fMask = SIF_POS; anchorItem = NULL; managerWindow = GetAncestor(hwnd, GA_PARENT); if (NULL == managerWindow) managerWindow = hwnd; focusWindow = GetFocus(); if (0 != (ListWidgetLayout_KeepStable & layoutFlags) && (managerWindow == focusWindow || IsChild(managerWindow, focusWindow))) { if (NULL != self->selectedItem) { RECT clientRect; GetClientRect(hwnd, &clientRect); anchorPoint.x = (FALSE != GetScrollInfo(hwnd, SB_HORZ, &scrollInfo)) ? -scrollInfo.nPos : 0; anchorPoint.y = (FALSE != GetScrollInfo(hwnd, SB_VERT, &scrollInfo)) ? -scrollInfo.nPos : 0; OffsetRect(&clientRect, -anchorPoint.x, -anchorPoint.y); if (clientRect.left <= self->selectedItem->rect.left && clientRect.top <= self->selectedItem->rect.top && clientRect.right >= self->selectedItem->rect.right && clientRect.bottom >= self->selectedItem->rect.bottom) { anchorItem = self->selectedItem; anchorPoint.x += self->selectedItem->rect.left; anchorPoint.y += self->selectedItem->rect.top; } } } flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOREDRAW; result = SetWindowPos(hwnd, NULL, 0, 0, 0, 0, flags); if (NULL != anchorItem) { long positionY; positionY = anchorItem->rect.top; if (FALSE != GetScrollInfo(hwnd, SB_VERT, &scrollInfo)) positionY -= scrollInfo.nPos; if (positionY != anchorPoint.y) WIDGET_SET_SCROLL_POS(hwnd, 0, positionY - anchorPoint.y, FALSE); } if (0x7FFFFFFF != menuAnchor.x && NULL != self->selectedItem && FALSE != ListWidget_GetViewItemPos(hwnd, self->selectedItem, &pt) && menuAnchor.x != pt.x && menuAnchor.y != pt.y) { if (NULL != self->activeMenu) EndMenu(); } if (0x7FFFFFFF != hoverAnchor.x && NULL != self->hoveredItem && FALSE != ListWidget_GetViewItemPos(hwnd, self->hoveredItem, &pt) && hoverAnchor.x != pt.x && hoverAnchor.y != pt.y) { ListWidget_UpdateHover(self, hwnd); } if (0 == (ListWidgetLayout_NoRedraw & layoutFlags)) { flags = RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME | RDW_ERASE; if (0 != (ListWidgetLayout_UpdateNow & layoutFlags)) flags |= (RDW_ERASENOW | RDW_UPDATENOW); RedrawWindow(hwnd, NULL, NULL, flags); } return result; } double ListWidget_GetZoomRatio(ListWidget *self) { if (NULL == self) return 1.0; if (self->imageSize.cy > LISTWIDGET_IMAGE_DEFAULT_HEIGHT) { return (1.0 + (double)(self->imageSize.cy - LISTWIDGET_IMAGE_DEFAULT_HEIGHT)/ (LISTWIDGET_IMAGE_MAX_HEIGHT - LISTWIDGET_IMAGE_DEFAULT_HEIGHT)); } return (double)(self->imageSize.cy - LISTWIDGET_IMAGE_MIN_HEIGHT)/ (LISTWIDGET_IMAGE_DEFAULT_HEIGHT - LISTWIDGET_IMAGE_MIN_HEIGHT); } long ListWidget_GetZoomedValue(double zoomRatio, long normal, long min, long max) { if (zoomRatio < 1.0) return min + (long)floor((double)(normal - min) * zoomRatio); else if (zoomRatio > 1.0) return normal + (long)ceil((double)(max - normal) * (zoomRatio - 1.0)); return normal; } static BOOL ListWidget_UpdateActivityLayout(ListWidget *self, HWND hwnd, double zoomRatio) { LOGFONTW lf; HDC windowDC; long elementHeight, titleMinWidth, workHeight; ListWidgetItem *item; ListWidgetActivityMetric *activityMetrics; // always reset items activity size cache size_t index = (self ? self->activeItems.size() : NULL); while(index--) { item = self->activeItems[index]; if (NULL != item) SetSizeEmpty(&item->activity->titleSize); } if (NULL == self) return FALSE; activityMetrics = &self->activityMetrics; activityMetrics->offsetLeft = self->activityMetrics.height/16; if (activityMetrics->offsetLeft < 1) activityMetrics->offsetLeft = 1; activityMetrics->offsetRight = activityMetrics->offsetLeft; activityMetrics->offsetTop = self->activityMetrics.height/9; if (activityMetrics->offsetTop < 3) activityMetrics->offsetTop = 3; activityMetrics->offsetBottom = activityMetrics->offsetTop; activityMetrics->spacing = 4 + self->activityMetrics.height/16; workHeight = activityMetrics->height - (activityMetrics->offsetTop + activityMetrics->offsetBottom); activityMetrics->fontHeight = workHeight/2; if (activityMetrics->fontHeight < 8) activityMetrics->fontHeight = 8; if (NULL == self->activityFont || sizeof(lf) != GetObject(self->activityFont, sizeof(lf), &lf) || lf.lfHeight != activityMetrics->fontHeight) { if (NULL != self->activityFont) DeleteObject(self->activityFont); lf.lfHeight = activityMetrics->fontHeight; lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = FW_NORMAL; lf.lfItalic = FALSE; lf.lfUnderline = FALSE; lf.lfStrikeOut = FALSE; lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = OUT_TT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = Graphics_GetSysFontQuality(); lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Arial"/*L"Times New Roman"*/); self->activityFont = CreateFontIndirect(&lf); } activityMetrics->titleHeight = workHeight; if (activityMetrics->titleHeight < 0) activityMetrics->titleHeight = 0; activityMetrics->titleWidth = 0; activityMetrics->percentWidth = 0; activityMetrics->percentHeight = 0; titleMinWidth = 0; windowDC = GetWindowDC(hwnd); if (NULL != windowDC) { SIZE textSize; HFONT prevFont; prevFont = SelectFont(windowDC, self->activityFont); if (FALSE == GetTextExtentPoint32(windowDC, L"00%", 3, &textSize)) SetSizeEmpty(&textSize); if (textSize.cy <= workHeight) { activityMetrics->fontHeight = textSize.cy; activityMetrics->percentHeight = textSize.cy; activityMetrics->percentWidth = textSize.cx; } titleMinWidth = Graphics_GetAveStrWidth(windowDC, 6); activityMetrics->spacing = Graphics_GetAveStrWidth(windowDC, 1) - 2; } if (NULL != self->activityBadgeBitmap) { DeleteObject(self->activityBadgeBitmap); self->activityBadgeBitmap = NULL; } elementHeight = (long)(18.0 * (double)workHeight/28.0); if (activityMetrics->progressWidth != elementHeight || activityMetrics->progressHeight != elementHeight) { activityMetrics->progressWidth = elementHeight; activityMetrics->progressHeight = elementHeight; if (NULL != self->activityProgressImage) { DeviceImage_Release(self->activityProgressImage); self->activityProgressImage = NULL; } } for(;;) { activityMetrics->titleWidth = activityMetrics->width - (activityMetrics->offsetLeft + activityMetrics->offsetRight); if(0 != activityMetrics->progressWidth) activityMetrics->titleWidth -= (activityMetrics->progressWidth + activityMetrics->spacing); if(0 != activityMetrics->percentWidth) activityMetrics->titleWidth -= (activityMetrics->percentWidth + activityMetrics->spacing); if (activityMetrics->titleWidth < titleMinWidth) { if (0 != activityMetrics->percentWidth) { activityMetrics->percentWidth = 0; activityMetrics->percentHeight = 0; continue; } if (0 != activityMetrics->progressWidth) { activityMetrics->progressWidth = 0; activityMetrics->progressHeight = 0; continue; } activityMetrics->titleWidth = 0; activityMetrics->titleHeight = 0; } break; } return TRUE; } BOOL ListWidget_SetImageSize(ListWidget *self, HWND hwnd, int imageWidth, int imageHeight, BOOL redraw) { size_t iCategory, iGroup, iItem; ListWidgetCategory *category; ListWidgetGroup *group; ListWidgetItem *item; long elementWidth, elementHeight; double zoomRatio; if (NULL == self) return FALSE; SetSize(&self->imageSize, imageWidth, imageHeight); zoomRatio = ListWidget_GetZoomRatio(self); for (iCategory = 0; iCategory < self->categories.size(); iCategory++) { category = self->categories[iCategory]; for(iGroup = 0; iGroup < category->groups.size(); iGroup++) { group = category->groups[iGroup]; for(iItem = 0; iItem < group->items.size(); iItem++) { item = group->items[iItem]; if (NULL != item->image) { DeviceImage_Release(item->image); item->image = NULL; } SetSize(&item->titleSize, -1, -1); } } } // connetion size elementHeight = ListWidget_GetZoomedValue(zoomRatio, LISTWIDGET_CONNECTION_DEFAULT_HEIGHT, LISTWIDGET_CONNECTION_MIN_HEIGHT, LISTWIDGET_CONNECTION_MAX_HEIGHT); elementWidth = elementHeight; if (self->connectionSize.cx != elementWidth || self->connectionSize.cy != elementHeight) { SetSize(&self->connectionSize, elementWidth, elementHeight); int index = (int)self->connections.size(); while (index--) { ListWidget_UpdateConnectionImageSize(self->connections[index], self->connectionSize.cx, self->connectionSize.cy); } } // primary command elementHeight = ListWidget_GetZoomedValue(zoomRatio, LISTWIDGET_PRIMARYCOMMAND_DEFAULT_HEIGHT, LISTWIDGET_PRIMARYCOMMAND_MIN_HEIGHT, LISTWIDGET_PRIMARYCOMMAND_MAX_HEIGHT); elementWidth = self->imageSize.cx; if (self->primaryCommandSize.cx != elementWidth || self->primaryCommandSize.cy != elementHeight) { SetSize(&self->primaryCommandSize, elementWidth, elementHeight); } // secondary command elementHeight = ListWidget_GetZoomedValue(zoomRatio, LISTWIDGET_SECONDARYCOMMAND_DEFAULT_HEIGHT, LISTWIDGET_SECONDARYCOMMAND_MIN_HEIGHT, LISTWIDGET_SECONDARYCOMMAND_MAX_HEIGHT); elementWidth = elementHeight; if (self->secondaryCommandSize.cx != elementWidth || self->secondaryCommandSize.cy != elementHeight) { SetSize(&self->secondaryCommandSize, elementWidth, elementHeight); } // activity elementHeight = ListWidget_GetZoomedValue(zoomRatio, LISTWIDGET_ACTIVITY_DEFAULT_HEIGHT, LISTWIDGET_ACTIVITY_MIN_HEIGHT, LISTWIDGET_ACTIVITY_MAX_HEIGHT); elementWidth = self->imageSize.cx; if (self->activityMetrics.width != elementWidth || self->activityMetrics.height != elementHeight) { self->activityMetrics.width = elementWidth; self->activityMetrics.height = elementHeight; } ListWidget_UpdateLayout(hwnd, ListWidgetLayout_NoRedraw); if (NULL != self->hoveredItem && FALSE != ListWidgetItem_IsInteractive(self->hoveredItem)) { ListWidget_UpdateActiveCommands(self, hwnd); } ListWidget_UpdateActivityLayout(self, hwnd, zoomRatio); if (FALSE != redraw) { RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ERASENOW | RDW_UPDATENOW | RDW_ALLCHILDREN); } return TRUE; } BOOL ListWidget_DisplayContextMenu(ListWidget *self, HWND hostWindow, POINT pt) { return FALSE; } BOOL ListWidget_RegisterActiveItem(ListWidget *self, HWND hwnd, ListWidgetItem *item) { size_t index; if (NULL == self || NULL == item) return FALSE; index = self->activeItems.size(); while(index--) { if (item == self->activeItems[index]) return FALSE; } self->activeItems.push_back(item); if (1 == self->activeItems.size()) { if (0 != SetTimer(hwnd, LISTWIDGETTIMER_PROGRESS_TICK_ID, LISTWIDGETTIMER_PROGRESS_TICK_DELAY, ListWidget_ProgressTickCb)) { self->activityTimerEnabled = TRUE; } } return TRUE; } BOOL ListWidget_UnregisterActiveItem(ListWidget *self, HWND hwnd, ListWidgetItem *item) { size_t index; if (NULL == self || NULL == item) return FALSE; index = self->activeItems.size(); while(index--) { if (item == self->activeItems[index]) { self->activeItems.erase(self->activeItems.begin() + index); if (0 == self->activeItems.size()) { KillTimer(hwnd, LISTWIDGETTIMER_PROGRESS_TICK_ID); self->activityTimerEnabled = FALSE; } return TRUE; } } return FALSE; } static void ListWidget_CallItemAction(ListWidget *self, HWND hwnd, ListWidgetCategory *category, ListWidgetItem *item) { ifc_device *device; if (NULL == self || NULL == item) return; if (NULL == category) { if (FALSE == ListWidget_GetItemOwner(self, item, &category) || NULL == category) { return; } } if (NULL == WASABI_API_DEVICES || S_OK != WASABI_API_DEVICES->DeviceFind(item->name, &device)) { return; } if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, category->name, -1, "discovered", -1)) { if (FALSE == device->GetAttached()) device->Attach(NULL); } if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, category->name, -1, "attached", -1)) { Navigation_SelectDevice(device->GetName()); } device->Release(); } static BOOL ListWidget_AddDevicesToCategory(ListWidget *self, ListWidgetCategory *category, DeviceList *list) { ListWidgetGroup *group; ListWidgetItem *item; ifc_device *device; size_t index, groupCount, categoryCount; if (NULL == list || NULL == category) return FALSE; categoryCount = category->groups.size(); index = list->size(); if (0 == index) return FALSE; do { index--; device = list->at(index); const char *groupName = device->GetType(); group = ListWidget_FindGroupEx(category, groupName, categoryCount); if (NULL == group) { group = ListWidget_CreateGroup(groupName); if (NULL != group) ListWidget_AddGroup(category, group); } if (NULL != group) { groupCount = group->items.size(); item = ListWidget_FindGroupItemEx(group, device->GetName(), groupCount); if (NULL == item) { item = ListWidget_CreateItemFromDevice(self, device); if (NULL != item) { ListWidget_AddItem(group, item); if (NULL != item->activity) self->activeItems.push_back(item); } } } else groupCount = 0; list->erase(list->begin() + index); device->Release(); while(index--) { device = list->at(index); if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, groupName, -1, device->GetType(), -1)) { if (NULL != group) { item = ListWidget_FindGroupItemEx(group, device->GetName(), groupCount); if (NULL == item) { item = ListWidget_CreateItemFromDevice(self, device); if (NULL != item) { ListWidget_AddItem(group, item); if (NULL != item->activity) { self->activeItems.push_back(item); } } } } list->erase(list->begin() + index); device->Release(); } } if (NULL != group) ListWidget_SortGroup(group); index = list->size(); } while(0 != index); ListWidget_SortCategory(category); return TRUE; } static void ListWidget_AddExistingDevices(ListWidget *self, HWND hwnd) { DeviceList discoveredList, attachedList; ifc_device *device; ListWidgetCategory *category; size_t index, activeItemsCount; BOOL devicesAdded; if (NULL == self) return; activeItemsCount = self->activeItems.size(); if (NULL != WASABI_API_DEVICES) { ifc_deviceobjectenum *enumerator; if (SUCCEEDED(WASABI_API_DEVICES->DeviceEnumerate(&enumerator))) { size_t reserveSize; ifc_deviceobject *object; if (SUCCEEDED(enumerator->GetCount(&reserveSize))) { discoveredList.reserve(reserveSize); attachedList.reserve(reserveSize); } while(S_OK == enumerator->Next(&object, 1, NULL)) { if (SUCCEEDED(object->QueryInterface(IFC_Device, (void**)&device))) { if (FALSE == device->GetHidden() && // excludes 'cloud' devices from appearing lstrcmpiA(device->GetConnection(), "cloud")) { if (FALSE == device->GetAttached()) discoveredList.push_back(device); else attachedList.push_back(device); } else device->Release(); } object->Release(); } enumerator->Release(); } } devicesAdded = FALSE; if (0 != attachedList.size()) { category = ListWidget_FindCategory(self, "attached"); if (NULL != category) { if (FALSE != ListWidget_AddDevicesToCategory(self, category, &attachedList)) { ListWidget_ResetCategoryCounter(category); devicesAdded = TRUE; } } index = attachedList.size(); while(index--) attachedList[index]->Release(); } if (0 != discoveredList.size()) { category = ListWidget_FindCategory(self, "discovered"); if (NULL != category) { if (FALSE != ListWidget_AddDevicesToCategory(self, category, &discoveredList)) { ListWidget_ResetCategoryCounter(category); devicesAdded = TRUE; } } index = discoveredList.size(); while(index--) discoveredList[index]->Release(); } if (self->activeItems.size() > 0) { if (0 == activeItemsCount) { if (0 != SetTimer(hwnd, LISTWIDGETTIMER_PROGRESS_TICK_ID, LISTWIDGETTIMER_PROGRESS_TICK_DELAY, ListWidget_ProgressTickCb)) { self->activityTimerEnabled = TRUE; } } } else { if (0 != activeItemsCount) { KillTimer(hwnd, LISTWIDGETTIMER_PROGRESS_TICK_ID); self->activityTimerEnabled = FALSE; } } if (FALSE != devicesAdded) { ListWidget_UpdateLayout(hwnd, ListWidgetLayout_Normal); } } void ListWidget_UpdateTitleEditorColors(HWND editor, WidgetStyle *style) { if (NULL == editor || NULL == style) return; EMBEDDEDEDITOR_SET_TEXT_COLOR(editor, WIDGETSTYLE_TEXT_COLOR(style)); EMBEDDEDEDITOR_SET_BACK_COLOR(editor, WIDGETSTYLE_BACK_COLOR(style)); EMBEDDEDEDITOR_SET_BORDER_COLOR(editor, WIDGETSTYLE_TEXT_EDITOR_BORDER_COLOR(style)); InvalidateRect(editor, NULL, TRUE); } static BOOL ListWidget_DeviceAdd(HWND hwnd, ifc_device *device, DeviceImage *deviceImage) { ListWidget *self; ListWidgetCategory *category; ListWidgetGroup *group; ListWidgetItem *item; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self) return FALSE; if (NULL == self || NULL == device || FALSE != device->GetHidden() && // excludes 'cloud' devices from appearing lstrcmpiA(device->GetConnection(), "cloud")) { return FALSE; } category = ListWidget_FindCategory(self, (FALSE != device->GetAttached()) ? "attached" : "discovered"); if (NULL == category) return FALSE; group = ListWidget_FindGroup(category, device->GetType()); if (NULL == group) { group = ListWidget_CreateGroup(device->GetType()); if (NULL == group) return FALSE; ListWidget_AddGroup(category, group); ListWidget_SortCategory(category); } item = ListWidget_FindGroupItem(group, device->GetName()); if (NULL != item) return FALSE; item = ListWidget_CreateItemFromDevice(self, device); if (NULL == item) return NULL; if (NULL == item->image && NULL != deviceImage) { item->image = deviceImage; DeviceImage_AddRef(item->image); } ListWidget_AddItem(group, item); ListWidget_SortGroup(group); if (NULL != item->activity) ListWidget_RegisterActiveItem(self, hwnd, item); ListWidget_ResetCategoryCounter(category); if (FALSE == category->collapsed) { ListWidget_UpdateLayout(hwnd, ListWidgetLayout_UpdateNow | ListWidgetLayout_KeepStable); } else { RECT rect; POINT origin; CopyRect(&rect, &category->rect); if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) OffsetRect(&rect, origin.x, origin.y); InvalidateRect(hwnd, &rect, FALSE); } return TRUE; } static BOOL ListWidget_DeviceRemove(HWND hwnd, ifc_device *device) { ListWidget *self; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; ListWidget_RemoveItem(self, hwnd, device->GetName()); ListWidget_UpdateHover(self, hwnd); return TRUE; } static BOOL ListWidget_DeviceAttach(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetCategory *category; ListWidgetItem *item; DeviceImage *image; BOOL result; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; image = NULL; category = ListWidget_FindCategory(self, "discovered"); if (NULL != category) { ListWidgetGroup *group = ListWidget_FindGroup(category, device->GetType()); if (NULL != group) { item = ListWidget_FindGroupItem(group, device->GetName()); if (NULL != item) { image = item->image; if (NULL != image) DeviceImage_AddRef(image); ListWidget_RemoveItem(self, hwnd, device->GetName()); } } } result = ListWidget_DeviceAdd(hwnd, device, image); if (NULL != image) DeviceImage_Release(image); return result; } static BOOL ListWidget_DeviceDetach(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetCategory *category; ListWidgetItem *item; DeviceImage *image; BOOL result; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; image = NULL; category = ListWidget_FindCategory(self, "attached"); if (NULL != category) { ListWidgetGroup *group = ListWidget_FindGroup(category, device->GetType()); if (NULL != group) { item = ListWidget_FindGroupItem(group, device->GetName()); if (NULL != item) { image = item->image; if (NULL != image) DeviceImage_AddRef(image); ListWidget_RemoveItem(self, hwnd, device->GetName()); } } } result = ListWidget_DeviceAdd(hwnd, device, image); if (NULL != image) DeviceImage_Release(image); return result; } static BOOL ListWidget_DeviceTitleChanged(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetItem *item; ListWidgetCategory *category; wchar_t buffer[1024] = {0}; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; item = ListWidget_FindItem(self, device->GetName(), &category, NULL); if (NULL == item) return FALSE; if (FAILED(device->GetDisplayName(buffer, ARRAYSIZE(buffer)))) return FALSE; ListWidget_SetItemTitle(item, buffer); if (NULL == category || FALSE == category->collapsed) { ListWidget_UpdateLayout(hwnd, ListWidgetLayout_UpdateNow | ListWidgetLayout_KeepStable); ListWidget_TooltipUpdateText(self, self->tooltip, item, Tooltip_DeviceTitleChanged); if (STATUS_ERROR != self->selectionStatus && item == self->selectedItem) { ListWidget_UpdateSelectionStatus(self, hwnd, FALSE); } } return TRUE; } static BOOL ListWidget_DeviceSpaceChanged(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetItem *item; ListWidgetCategory *category; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; item = ListWidget_FindItem(self, device->GetName(), &category, NULL); if (NULL == item) return FALSE; if (FAILED(device->GetTotalSpace(&item->spaceTotal))) return FALSE; if (FAILED(device->GetUsedSpace(&item->spaceUsed))) return FALSE; if (NULL == category || FALSE == category->collapsed) { RECT rect; POINT origin; ListWidgetItemMetric metrics; WidgetStyle *style; CopyRect(&rect, &item->rect); if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) OffsetRect(&rect, origin.x, origin.y); style = WIDGET_GET_STYLE(hwnd); if (NULL != style && FALSE != ListWidget_GetItemMetrics(style, &metrics)) { rect.top += metrics.offsetTop + metrics.imageOffsetTop + self->imageSize.cy + metrics.imageOffsetBottom + metrics.spacebarOffsetTop; rect.left += metrics.offsetLeft; rect.right -= metrics.offsetRight; rect.bottom = rect.top + metrics.spacebarHeight; } InvalidateRect(hwnd, &rect, FALSE); UpdateWindow(hwnd); ListWidget_TooltipUpdateText(self, self->tooltip, item, Tooltip_DeviceSpaceChanged); if (STATUS_ERROR != self->selectionStatus && item == self->selectedItem) { ListWidget_UpdateSelectionSpaceStatus(self, hwnd, FALSE); } } return TRUE; } static BOOL ListWidget_DeviceIconChanged(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetItem *item; ListWidgetCategory *category; DeviceImage *previousImage; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; item = ListWidget_FindItem(self, device->GetName(), &category, NULL); if (NULL == item) return FALSE; previousImage = item->image; item->image = NULL; if (NULL == category || FALSE == category->collapsed) { RECT rect; POINT origin; ListWidgetItemMetric metrics; WidgetStyle *style; style = WIDGET_GET_STYLE(hwnd); if (NULL == style || FALSE == ListWidget_GetItemMetrics(style, &metrics) || FALSE == ListWidget_GetItemImageRect(self, item, &metrics, &rect)) { CopyRect(&rect, &item->rect); } if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) OffsetRect(&rect, origin.x, origin.y); InvalidateRect(hwnd, &rect, FALSE); UpdateWindow(hwnd); } if (NULL != previousImage) DeviceImage_Release(previousImage); return TRUE; } static BOOL ListWidget_DeviceCommandChanged(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetItem *item; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; item = ListWidget_FindItem(self, device->GetName(), NULL, NULL); if (NULL == item) return FALSE; if (item == self->hoveredItem && NULL == self->activeMenu) { ListWidget_UpdateActiveCommands(self, hwnd); } return TRUE; } static BOOL ListWidget_DeviceActivityStarted(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetItem *item; ListWidgetCategory *category; size_t index; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; item = ListWidget_FindItem(self, device->GetName(), &category, NULL); if (NULL == item) return FALSE; index = self->activeItems.size(); while(index--) { if (item == self->activeItems[index]) return FALSE; } if (FALSE != ListWidget_CreateItemActivity(item)) { ifc_deviceactivity *activity; ListWidget_RegisterActiveItem(self, hwnd, item); if(S_OK == device->GetActivity(&activity) && NULL != activity) { ListWidget_UpdateItemActivity(item, activity); activity->Release(); } } if (NULL == category || FALSE == category->collapsed) { ListWidget_InvalidateItemImage(self, hwnd, item); if (STATUS_ERROR != self->selectionStatus && item == self->selectedItem) { ListWidget_UpdateSelectionStatus(self, hwnd, FALSE); } } if (item == self->hoveredItem && NULL == self->activeMenu) { ListWidget_UpdateActiveCommands(self, hwnd); } return TRUE; } static BOOL ListWidget_DeviceActivityFinished(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetItem *item; ListWidgetCategory *category; BOOL activityChanged; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; item = ListWidget_FindItem(self, device->GetName(), &category, NULL); if (NULL == item) return FALSE; activityChanged = FALSE; if (FALSE != ListWidget_UnregisterActiveItem(self, hwnd, item)) activityChanged = TRUE; if (FALSE != ListWidget_DeleteItemActivity(item)) activityChanged = TRUE; if (FALSE != activityChanged) { if (NULL == category || FALSE == category->collapsed) { ListWidget_InvalidateItemImage(self, hwnd, item); } if (item == self->hoveredItem && NULL == self->activeMenu) { ListWidget_UpdateActiveCommands(self, hwnd); } if (STATUS_ERROR != self->selectionStatus && item == self->selectedItem) { ListWidget_UpdateSelectionStatus(self, hwnd, FALSE); } } return TRUE; } static BOOL ListWidget_DeviceActivityChanged(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetItem *item; ListWidgetCategory *category; ifc_deviceactivity *activity; ListWidgetActivityChange changes; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; item = ListWidget_FindItem(self, device->GetName(), &category, NULL); if (NULL == item) return FALSE; if (S_OK == device->GetActivity(&activity) && NULL != activity) { changes = ListWidget_UpdateItemActivity(item, activity); activity->Release(); } else changes = ListWidgetActivityChanged_Nothing; //if (FALSE != self->activityTimerEnabled) // changes &= ~ListWidgetActivityChanged_Percent; if (ListWidgetActivityChanged_Nothing != changes && (NULL == category || FALSE == category->collapsed)) { ListWidget_InvalidateItemActivity(self, hwnd, item, changes); ListWidget_TooltipUpdateText(self, self->tooltip, item, Tooltip_DeviceActivityChanged); if (0 != (ListWidgetActivityChanged_Title & changes) && STATUS_ERROR != self->selectionStatus && item == self->selectedItem) { ListWidget_UpdateSelectionStatus(self, hwnd, FALSE); } } return TRUE; } static BOOL ListWidget_DeviceModelChanged(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetItem *item; ListWidgetCategory *category; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; item = ListWidget_FindItem(self, device->GetName(), &category, NULL); if (NULL == item) return FALSE; if (NULL == category || FALSE == category->collapsed) { ListWidget_TooltipUpdateText(self, self->tooltip, item, Tooltip_DeviceModelChanged); if (STATUS_ERROR != self->selectionStatus && item == self->selectedItem) { ListWidget_UpdateSelectionStatus(self, hwnd, FALSE); } } return TRUE; } static BOOL ListWidget_DeviceStatusChanged(HWND hwnd, ifc_device *device) { ListWidget *self; ListWidgetItem *item; ListWidgetCategory *category; self = WIDGET_GET_SELF(hwnd, ListWidget); if (NULL == self || NULL == device) return FALSE; item = ListWidget_FindItem(self, device->GetName(), &category, NULL); if (NULL == item) return FALSE; if (NULL == category || FALSE == category->collapsed) { ListWidget_TooltipUpdateText(self, self->tooltip, item, Tooltip_DeviceStatusChanged); if (STATUS_ERROR != self->selectionStatus && item == self->selectedItem) { ListWidget_UpdateSelectionStatus(self, hwnd, FALSE); } } return TRUE; } static void ListWidget_DeviceCb(ifc_device *device, DeviceEvent eventId, void *user) { HWND hwnd; hwnd = (HWND)user; switch(eventId) { case Event_DeviceAdded: ListWidget_DeviceAdd(hwnd, device, NULL); break; case Event_DeviceRemoved: ListWidget_DeviceRemove(hwnd, device); break; case Event_DeviceHidden: ListWidget_DeviceRemove(hwnd, device); break; case Event_DeviceShown: ListWidget_DeviceAdd(hwnd, device, NULL); break; case Event_DeviceAttached: ListWidget_DeviceAttach(hwnd, device); break; case Event_DeviceDetached: ListWidget_DeviceDetach(hwnd, device); break; case Event_DeviceDisplayNameChanged: ListWidget_DeviceTitleChanged(hwnd, device); break; case Event_DeviceTotalSpaceChanged: ListWidget_DeviceSpaceChanged(hwnd, device); break; case Event_DeviceUsedSpaceChanged: ListWidget_DeviceSpaceChanged(hwnd, device); break; case Event_DeviceIconChanged: ListWidget_DeviceIconChanged(hwnd, device); break; case Event_DeviceActivityStarted: ListWidget_DeviceActivityStarted(hwnd, device); break; case Event_DeviceActivityFinished: ListWidget_DeviceActivityFinished(hwnd, device); break; case Event_DeviceActivityChanged: ListWidget_DeviceActivityChanged(hwnd, device); break; case Event_DeviceCommandChanged: ListWidget_DeviceCommandChanged(hwnd, device); break; case Event_DeviceModelChanged: ListWidget_DeviceModelChanged(hwnd, device); break; case Event_DeviceStatusChanged: ListWidget_DeviceStatusChanged(hwnd, device); break; } } static BOOL ListWidget_RegisterDeviceHandler(ListWidget *self, HWND hwnd) { HWND eventRelay; DeviceEventCallbacks callbacks; if (NULL == self) return FALSE; if (0 != self->deviceHandler) return FALSE; eventRelay = Plugin_GetEventRelayWindow(); if (NULL == eventRelay) return FALSE; ZeroMemory(&callbacks, sizeof(callbacks)); callbacks.deviceCb = ListWidget_DeviceCb; self->deviceHandler = EVENTRELAY_REGISTER_HANDLER(eventRelay, &callbacks, hwnd); return (0 != self->deviceHandler); } static BOOL ListWidget_InitCb(HWND hwnd, void **object, void *param) { ListWidget *self; HWND sliderWindow; int imageHeight, imageWidth; self = new ListWidget(); if (NULL == self) return FALSE; self->flags = (ListWidgetFlags)0; self->hoveredItem = NULL; self->selectedItem = NULL; self->titleEditItem = NULL; self->pressedCategory = NULL; self->itemWidth = 0; self->spacebarBitmap = NULL; self->hoverBitmap = NULL; self->selectBitmap = NULL; self->inactiveSelectBitmap = NULL; self->largeBadgeBitmap = NULL; self->smallBadgeBitmap = NULL; self->arrowsBitmap = NULL; self->itemsPerLine = 0; self->deviceHandler = 0; self->activeMenu = NULL; self->previousMouse.x = -1; self->previousMouse.y = -1; self->commands = NULL; self->commandsCount = 0; self->commandsMax = 0; self->unknownCommandLargeImage = NULL; self->unknownCommandSmallImage = NULL; ZeroMemory(&self->activityMetrics, sizeof(ListWidgetActivityMetric)); self->activityFont = NULL; self->activityProgressImage = NULL; self->activityBadgeBitmap = NULL; self->activityTimerEnabled = FALSE; SetSizeEmpty(&self->connectionSize); SetSizeEmpty(&self->primaryCommandSize); SetSizeEmpty(&self->secondaryCommandSize); self->selectionStatus = STATUS_ERROR; self->titleEditor = NULL; BackBuffer_Initialize(&self->backBuffer, hwnd); imageHeight = Config_ReadInt("View", "imageHeight", LISTWIDGET_IMAGE_DEFAULT_HEIGHT); if (imageHeight < LISTWIDGET_IMAGE_MIN_HEIGHT) imageHeight = LISTWIDGET_IMAGE_MIN_HEIGHT; else if (imageHeight > LISTWIDGET_IMAGE_MAX_HEIGHT) imageHeight = LISTWIDGET_IMAGE_MAX_HEIGHT; imageWidth = (imageHeight * LISTWIDGET_IMAGE_DEFAULT_WIDTH)/LISTWIDGET_IMAGE_DEFAULT_HEIGHT; ListWidget_SetImageSize(self, hwnd, imageWidth, imageHeight, FALSE); ListWidget_CreateDefaultCategories(self); *object = self; sliderWindow = MANAGERVIEW_GET_ZOOM_SLIDER(GetParent(hwnd)); if (NULL != sliderWindow) { int pos; SendMessage(sliderWindow, TBM_SETPAGESIZE, 0, 10); SendMessage(sliderWindow, TBM_SETLINESIZE, 0, 10); SendMessage(sliderWindow, TBM_SETRANGE, TRUE, MAKELPARAM(-100, 100)); SendMessage(sliderWindow, TBM_SETTIC, 0, 0); if (imageHeight == LISTWIDGET_IMAGE_DEFAULT_HEIGHT) pos = 0; else if (imageHeight > LISTWIDGET_IMAGE_DEFAULT_HEIGHT) pos = (100 * (imageHeight- LISTWIDGET_IMAGE_DEFAULT_HEIGHT))/(LISTWIDGET_IMAGE_MAX_HEIGHT - LISTWIDGET_IMAGE_DEFAULT_HEIGHT); else pos = (100 * (imageHeight - LISTWIDGET_IMAGE_MIN_HEIGHT))/(LISTWIDGET_IMAGE_DEFAULT_HEIGHT - LISTWIDGET_IMAGE_MIN_HEIGHT) - 100; SendMessage(sliderWindow, TBM_SETPOS, TRUE, pos); } self->tooltip = ListWidget_TooltipCreate(hwnd); ListWidget_AddExistingDevices(self, hwnd); ListWidget_RegisterDeviceHandler(self, hwnd); return TRUE; } static void ListWidget_DestroyCb(ListWidget *self, HWND hwnd) { HWND sliderWindow; size_t index; if (NULL == self) return; Config_WriteInt("View", "imageHeight", self->imageSize.cy); if (0 != self->deviceHandler) { HWND eventRelay; eventRelay = Plugin_GetEventRelayWindow(); if (NULL != eventRelay) { EVENTRELAY_UNREGISTER_HANDLER(eventRelay, self->deviceHandler); } } index = self->activeItems.size(); if (0 != index) { KillTimer(hwnd, LISTWIDGETTIMER_PROGRESS_TICK_ID); self->activityTimerEnabled = FALSE; while(index--) ListWidget_DeleteItemActivity(self->activeItems[index]); } index = self->categories.size(); if (0 != index) { while(index--) { ListWidgetCategory *category = self->categories[index]; Config_WriteBool("CollapsedCategories", category->name, category->collapsed); ListWidget_DestroyCategory(category); } self->categories.clear(); } ListWidget_RemoveAllConnections(self); BackBuffer_Uninitialize(&self->backBuffer); if (NULL != self->spacebarBitmap) DeleteObject(self->spacebarBitmap); if (NULL != self->hoverBitmap) DeleteObject(self->hoverBitmap); if (NULL != self->selectBitmap) DeleteObject(self->selectBitmap); if (NULL != self->inactiveSelectBitmap) DeleteObject(self->inactiveSelectBitmap); if (NULL != self->largeBadgeBitmap) DeleteObject(self->largeBadgeBitmap); if (NULL != self->smallBadgeBitmap) DeleteObject(self->smallBadgeBitmap); if (NULL != self->unknownCommandLargeImage) DeviceImage_Release(self->unknownCommandLargeImage); if (NULL != self->unknownCommandSmallImage) DeviceImage_Release(self->unknownCommandSmallImage); if (NULL != self->arrowsBitmap) DeleteObject(self->arrowsBitmap); if (NULL != self->activityProgressImage) DeviceImage_Release(self->activityProgressImage); if (NULL != self->activityBadgeBitmap) DeleteObject(self->activityBadgeBitmap); if (NULL != self->activityFont) DeleteObject(self->activityFont); ListWidget_TooltipDestroy(self->tooltip); if (NULL != self->commands) { ListWidget_DestroyAllCommands(self->commands, self->commandsCount); free(self->commands); } free(self); sliderWindow = MANAGERVIEW_GET_ZOOM_SLIDER(GetParent(hwnd)); if (NULL != sliderWindow) { ShowWindow(sliderWindow, SW_HIDE); } } static void ListWidget_LayoutCb(ListWidget *self, HWND hwnd, WidgetStyle *style, const RECT *clientRect, SIZE *viewSize, BOOL redraw) { size_t iCategory, iGroup, iItem; ListWidgetCategory *category; ListWidgetGroup *group; ListWidgetItem *item; ListWidgetCategoryMetric categoryMetrics; size_t itemsInLine; long viewWidth, itemHeight, lineHeight; long itemTextWidth, itemTextHeightMax, textHeight, fontHeight; long categoryHeight, categorySpacing, categorySpacingCollapsed; SIZE itemSpacing, widgetSize, itemSize; POINT widgetOffset, categoryOffset, itemOffset; RECT elementRect; HDC targetDC; HFONT targetPrevFont; TEXTMETRIC textMetrics; if (NULL == style) return; viewWidth = RECTWIDTH(*clientRect); if (FALSE == ListWidget_CalculateItemBaseSize(self, style, &itemSize, &itemTextWidth)) SetSizeEmpty(&itemSize); self->itemWidth = itemSize.cx; WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(widgetOffset.x, style, LISTWIDGET_OFFSET_LEFT_DLU, 1); WIDGETSTYLE_DLU_TO_VERT_PX_MIN(widgetOffset.y, style, LISTWIDGET_OFFSET_TOP_DLU, 1); WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(categoryOffset.x, style, LISTWIDGET_CATEGORY_OFFSET_LEFT_DLU, 1); WIDGETSTYLE_DLU_TO_VERT_PX_MIN(categoryOffset.y, style, LISTWIDGET_CATEGORY_OFFSET_TOP_DLU, 1); WIDGETSTYLE_DLU_TO_VERT_PX_MIN(categorySpacing, style, LISTWIDGET_CATEGORY_SPACING_DLU, 1); WIDGETSTYLE_DLU_TO_VERT_PX_MIN(categorySpacingCollapsed, style, LISTWIDGET_CATEGORY_SPACING_COLLAPSED_DLU, 1); WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(itemOffset.x, style, LISTWIDGET_ITEM_OFFSET_LEFT_DLU, 1); WIDGETSTYLE_DLU_TO_VERT_PX_MIN(itemOffset.y, style, LISTWIDGET_ITEM_OFFSET_TOP_DLU, 1); WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(itemSpacing.cx, style, LISTWIDGET_ITEM_SPACING_HORZ_DLU, 1); WIDGETSTYLE_DLU_TO_VERT_PX_MIN(itemSpacing.cy, style, LISTWIDGET_ITEM_SPACING_VERT_DLU, 1); self->itemsPerLine = ((viewWidth - (widgetOffset.x + itemOffset.x))/* + itemSpacing.cx*/) / (itemSize.cx + itemSpacing.cx); self->itemsPerLine = MAX(self->itemsPerLine, 1); itemsInLine = 0; for (iCategory = 0; iCategory < self->categories.size(); iCategory++) { category = self->categories[iCategory]; if (FALSE == category->collapsed) { for(iGroup = 0; iGroup < category->groups.size(); iGroup++) { group = category->groups[iGroup]; if (itemsInLine < group->items.size()) itemsInLine = group->items.size(); } } } if (self->itemsPerLine < itemsInLine && self->itemsPerLine > 1) itemSpacing.cx = (LONG)(((viewWidth - (widgetOffset.x + itemOffset.x)) - (itemSize.cx * self->itemsPerLine))/self->itemsPerLine); if (itemsInLine < self->itemsPerLine) self->itemsPerLine = itemsInLine; widgetSize.cx = widgetOffset.x + itemOffset.x + itemSize.cx; widgetSize.cy = widgetOffset.y; targetDC = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); if (NULL != targetDC) { targetPrevFont = SelectFont(targetDC, WIDGETSTYLE_CATEGORY_FONT(style)); categoryHeight = Graphics_GetFontHeight(targetDC); SelectFont(targetDC, WIDGETSTYLE_TEXT_FONT(style)); fontHeight = Graphics_GetFontHeight(targetDC); itemTextHeightMax = LISTWIDGET_ITEM_TITLE_MAX_LINES * fontHeight; } else { targetPrevFont = NULL; itemTextHeightMax = 0; categoryHeight = 0; fontHeight = 0; } if (NULL == targetDC || FALSE == GetTextMetrics(targetDC, &textMetrics)) { ZeroMemory(&textMetrics, sizeof(textMetrics)); } if (FALSE != ListWidget_GetCategoryMetrics(style, &categoryMetrics)) { if (categoryHeight < categoryMetrics.minHeight) categoryHeight = categoryMetrics.minHeight; categoryHeight += categoryMetrics.offsetTop + categoryMetrics.offsetBottom + categoryMetrics.lineHeight + categoryMetrics.lineOffsetTop; } for (iCategory = 0; iCategory < self->categories.size(); iCategory++) { category = self->categories[iCategory]; SetRect(&category->rect, 0, 0, viewWidth, categoryHeight); long offsetX = clientRect->left + categoryOffset.x; long offsetY = clientRect->top + widgetSize.cy; if (0 == iCategory) offsetY += categoryOffset.y; else { if (FALSE == self->categories[iCategory - 1]->collapsed) offsetY += categorySpacing; else offsetY += categorySpacingCollapsed; } OffsetRect(&category->rect, offsetX, offsetY); widgetSize.cy = category->rect.bottom - clientRect->top; size_t itemsInCategory = 0; if (FALSE == category->collapsed) { for(iGroup = 0; iGroup < category->groups.size(); iGroup++) { group = category->groups[iGroup]; itemsInLine = 1; lineHeight = 0; if (0 != group->items.size()) { if (0 == iGroup) widgetSize.cy += itemOffset.y; else widgetSize.cy += itemSpacing.cy; for(iItem = 0; iItem < group->items.size(); iItem++, itemsInLine++) { item = group->items[iItem]; if (itemsInLine > self->itemsPerLine) { widgetSize.cy += lineHeight + itemSpacing.cy; lineHeight = 0; itemsInLine = 1; } itemHeight = itemSize.cy; itemsInCategory++; if (-1 == item->titleSize.cy) { if (FALSE == IS_STRING_EMPTY(item->title)) { SetRect(&elementRect, 0, 0, itemTextWidth - textMetrics.tmAveCharWidth/2, 0); if (FALSE != DrawText(targetDC, item->title, -1, &elementRect, DT_NOPREFIX | DT_CALCRECT | DT_EDITCONTROL | DT_WORDBREAK)) { SetSize(&item->titleSize, RECTWIDTH(elementRect), RECTHEIGHT(elementRect)); item->titleSize.cx += textMetrics.tmAveCharWidth/2; if (item->titleSize.cx > itemTextWidth) item->titleSize.cx = itemTextWidth; } else SetSizeEmpty(&item->titleSize); } else { SetSize(&item->titleSize, 0, textMetrics.tmHeight); } } textHeight = item->titleSize.cy; if (textHeight > itemTextHeightMax) { textHeight = itemTextHeightMax; ListWidgetItem_SetTextTruncated(item); } itemHeight += textHeight; SetRect(&item->rect, 0, 0, itemSize.cx, itemHeight); offsetX = long(clientRect->left + itemOffset.x + (itemsInLine - 1)*(itemSize.cx + itemSpacing.cx)); offsetY = clientRect->top + widgetSize.cy; OffsetRect(&item->rect, offsetX, offsetY); if (lineHeight < itemHeight) lineHeight = itemHeight; } if (0 != lineHeight) widgetSize.cy += lineHeight; } } if (0 == itemsInCategory && FALSE == IS_STRING_EMPTY(category->emptyText)) { SetRect(&category->emptyTextRect, 0, 0, viewWidth - textMetrics.tmAveCharWidth/2, 0); if (FALSE != DrawText(targetDC, category->emptyText, -1, &category->emptyTextRect, DT_NOPREFIX | DT_CALCRECT | DT_EDITCONTROL | DT_WORDBREAK)) { category->emptyTextRect.right += textMetrics.tmAveCharWidth/2; if (category->emptyTextRect.right > viewWidth) category->emptyTextRect.right = viewWidth; offsetX = clientRect->left + categoryOffset.x + (viewWidth - category->emptyTextRect.right)/2; offsetY = clientRect->top + widgetSize.cy + itemOffset.y + fontHeight/2; OffsetRect(&category->emptyTextRect, offsetX, offsetY); widgetSize.cy += RECTHEIGHT(category->emptyTextRect) + itemOffset.y + fontHeight; } else SetRectEmpty(&category->emptyTextRect); } } else { for(iGroup = 0; iGroup < category->groups.size(); iGroup++) { group = category->groups[iGroup]; for(iItem = 0; iItem < group->items.size(); iItem++, itemsInLine++) { item = group->items[iItem]; SetRectEmpty(&item->rect); itemsInCategory++; } } } } widgetSize.cy += WIDGETSTYLE_DLU_TO_VERT_PX(style, LISTWIDGET_OFFSET_BOTTOM_DLU); viewSize->cx = widgetSize.cx; viewSize->cy = widgetSize.cy; size_t commandsMax = 1; if (self->commandsMax != commandsMax) { if (NULL != self->commands) { ListWidget_DestroyAllCommands(self->commands, self->commandsCount); free(self->commands); } self->commandsCount = 0; self->commands = (ListWidgetCommand**)malloc(sizeof(ListWidgetCommand*) * commandsMax); if (NULL != self->commands) self->commandsMax = commandsMax; else self->commandsMax = 0; } if (NULL != targetDC) { SelectFont(targetDC, targetPrevFont); ReleaseDC(hwnd, targetDC); } } static BOOL ListWidget_PaintCb(ListWidget *self, HWND hwnd, WidgetStyle *style, HDC hdc, const RECT *paintRect, BOOL erase) { size_t iCategory, iGroup, iItem; ListWidgetCategory *category; ListWidgetGroup *group; ListWidgetItem *item; HFONT prevFont; FillRegion fillRegion; ListWidgetPaint paint; size_t itemsInCategory; if (FALSE == ListWidgetPaint_Initialize(&paint, self, style, hwnd, hdc, paintRect, erase)) return FALSE; SetTextColor(hdc, style->textColor); SetBkColor(hdc, style->backColor); SetBkMode(hdc, TRANSPARENT); prevFont = SelectFont(hdc, style->textFont); FillRegion_Init(&fillRegion, paintRect); if (FALSE != erase) { for (iCategory = 0; iCategory < self->categories.size(); iCategory++) { category = self->categories[iCategory]; FillRegion_ExcludeRect(&fillRegion, &category->rect); if (FALSE == category->collapsed) { itemsInCategory = 0; for(iGroup = 0; iGroup < category->groups.size(); iGroup++) { group = category->groups[iGroup]; for(iItem = 0; iItem < group->items.size(); iItem++) { item = group->items[iItem]; itemsInCategory++; FillRegion_ExcludeRect(&fillRegion, &item->rect); } } if (0 == itemsInCategory && FALSE == IS_STRING_EMPTY(category->emptyText)) { FillRegion_ExcludeRect(&fillRegion, &category->emptyTextRect); } } } FillRegion_BrushFill(&fillRegion, hdc, style->backBrush); FillRegion_SetEmpty(&fillRegion); } for (iCategory = 0; iCategory < self->categories.size(); iCategory++) { category = self->categories[iCategory]; if (FALSE == ListWidgetPaint_DrawCategory(&paint, category)) FillRegion_AppendRect(&fillRegion, &category->rect); if (FALSE == category->collapsed) { itemsInCategory = 0; for(iGroup = 0; iGroup < category->groups.size(); iGroup++) { group = category->groups[iGroup]; for(iItem = 0; iItem < group->items.size(); iItem++) { item = group->items[iItem]; itemsInCategory++; if (FALSE == ListWidgetPaint_DrawItem(&paint, item)) FillRegion_AppendRect(&fillRegion, &item->rect); } } if (0 == itemsInCategory && FALSE == IS_STRING_EMPTY(category->emptyText)) { if (FALSE == ListWidgetPaint_DrawEmptyCategoryText(&paint, category)) FillRegion_AppendRect(&fillRegion, &category->emptyTextRect); } } } FillRegion_BrushFill(&fillRegion, hdc, style->backBrush); FillRegion_Uninit(&fillRegion); SelectFont(hdc, prevFont); ListWidgetPaint_Uninitialize(&paint); return TRUE; } static void ListWidget_StyleColorChangedCb(ListWidget *self, HWND hwnd, WidgetStyle *style) { if (NULL == self) return; if (NULL != self->spacebarBitmap) { DeleteObject(self->spacebarBitmap); self->spacebarBitmap = NULL; } if (NULL != self->hoverBitmap) { DeleteObject(self->hoverBitmap); self->hoverBitmap = NULL; } if (NULL != self->selectBitmap) { DeleteObject(self->selectBitmap); self->selectBitmap = NULL; } if (NULL != self->inactiveSelectBitmap) { DeleteObject(self->inactiveSelectBitmap); self->inactiveSelectBitmap = NULL; } if (NULL != self->arrowsBitmap) { DeleteObject(self->arrowsBitmap); self->arrowsBitmap = NULL; } ListWidget_ResetConnnectionsColors(self, style); if (NULL != self->titleEditor) ListWidget_UpdateTitleEditorColors(self->titleEditor, style); } static void ListWidget_StyleFontChangedCb(ListWidget *self, HWND hwnd, WidgetStyle *style) { size_t iCategory, iGroup, iItem; ListWidgetCategory *category; ListWidgetGroup *group; ListWidgetItem *item; if (NULL == self) return; for (iCategory = 0; iCategory < self->categories.size(); iCategory++) { category = self->categories[iCategory]; category->countWidth = -1; category->titleWidth = -1; SetRect(&category->emptyTextRect, -1, -1, -1, -1); for(iGroup = 0; iGroup < category->groups.size(); iGroup++) { group = category->groups[iGroup]; for(iItem = 0; iItem < group->items.size(); iItem++) { item = group->items[iItem]; SetSize(&item->titleSize, -1, -1); } } } ListWidget_TooltipFontChanged(self->tooltip); if (NULL != self->titleEditor) SendMessage(self->titleEditor, WM_SETFONT, (WPARAM)WIDGETSTYLE_TEXT_FONT(style), TRUE); } static BOOL ListWidget_MouseMoveCb(ListWidget *self, HWND hwnd, unsigned int vKeys, const POINT *cursor) { if (self->previousMouse.x == cursor->x && self->previousMouse.y == cursor->y) { return TRUE; } self->previousMouse = *cursor; if (FALSE != ListWidget_UpdateHoverEx(self, hwnd, cursor)) UpdateWindow(hwnd); ListWidget_TooltipRelayMouseMessage(self->tooltip, WM_MOUSEMOVE, vKeys, cursor); return TRUE; } static BOOL ListWidget_LeftButtonDownCb(ListWidget *self, HWND hwnd, unsigned int vKeys, const POINT *cursor) { ListWidgetItem *selectedItem; ListWidgetCategory *pressedCategory; ListWidgetItemPart pressedPart; ListWidgetCommand *command, *pressedCommand; WidgetStyle *style; RECT rect, pressedPartRect; POINT pt, origin; size_t index; style = WIDGET_GET_STYLE(hwnd); if (NULL == style) return FALSE; pt = *cursor; if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) { origin.x = 0; origin.y = 0; } selectedItem = NULL; pressedCategory = NULL; pressedPart = ListWidgetItemPart_None; pressedCommand = NULL; if (FALSE != GetClientRect(hwnd, &rect) && FALSE != PtInRect(&rect, pt)) { pt.x -= origin.x; pt.y -= origin.y; pressedCategory = ListWidget_GetCategoryFromPoint(self, pt); if (NULL == pressedCategory) { selectedItem = ListWidget_GetItemFromPoint(self, pt); if (NULL != selectedItem) { ListWidgetItemMetric metrics; if (FALSE != ListWidget_GetItemMetrics(style, &metrics)) { pressedPart = ListWidgetItemPart_Command; if (selectedItem == self->selectedItem) pressedPart |= ListWidgetItemPart_Title; pressedPart = ListWidget_GetItemPartFromPoint(self, selectedItem, &metrics, pt, pressedPart, &pressedPartRect); if (ListWidgetItemPart_Title == pressedPart && self->titleEditItem != selectedItem && hwnd == GetFocus() && FALSE == ListWidgetItem_IsTextEdited(selectedItem)) { self->titleEditItem = selectedItem; SetTimer(hwnd, LISTWIDGETTIMER_EDIT_TITLE_ID, GetDoubleClickTime() + 1, ListWidget_BeginTitleEditTimerCb); } } } } } if (NULL != pressedCategory) { ListWidgetCategoryMetric metrics; if (FALSE == ListWidget_GetCategoryMetrics(style, &metrics)) SetRectEmpty(&rect); else { CopyRect(&rect, &pressedCategory->rect); rect.left += metrics.offsetLeft; rect.top += metrics.offsetTop; rect.right = rect.left + metrics.iconWidth; rect.bottom -= (metrics.offsetBottom + metrics.lineHeight + metrics.lineOffsetTop); } if (FALSE == PtInRect(&rect, pt)) pressedCategory = NULL; } self->pressedCategory = pressedCategory; ListWidget_SelectItem(self, hwnd, selectedItem, FALSE); ListWidget_EnsureFocused(self, hwnd, FALSE); if (ListWidgetItemPart_Command == pressedPart && NULL != selectedItem) { OffsetRect(&pressedPartRect, -selectedItem->rect.left, -selectedItem->rect.top); } index = self->commandsCount; while(index--) { command = self->commands[index]; if (NULL == pressedCommand && ListWidgetItemPart_Command == pressedPart && ListWidget_GetCommandRectEqual(command, &pressedPartRect)) { pressedCommand = command; } if (FALSE != ListWidget_GetCommandPressed(command) || pressedCommand == command) { if (FALSE != ListWidget_SetCommandPressed(pressedCommand, (pressedCommand == command)) && FALSE != ListWidget_GetCommandRect(command, &rect) && NULL != selectedItem) { OffsetRect(&rect, selectedItem->rect.left + origin.x, selectedItem->rect.top + origin.y); InvalidateRect(hwnd, &rect, FALSE); } } } if (NULL != pressedCommand) self->flags |= ListWidgetFlag_LButtonDownOnCommand; else self->flags &= ~ListWidgetFlag_LButtonDownOnCommand; if (GetCapture() != hwnd) SetCapture(hwnd); UpdateWindow(hwnd); ListWidget_TooltipHide(self->tooltip); return TRUE; } static BOOL ListWidget_LeftButtonUpCb(ListWidget *self, HWND hwnd, unsigned int vKeys, const POINT *cursor) { WidgetStyle *style; RECT rect; POINT pt, origin; ListWidgetCategory *pressedCategory; ListWidgetCommand *pressedCommand; size_t index; BOOL updateWindow; style = WIDGET_GET_STYLE(hwnd); pt = *cursor; if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) { origin.x = 0; origin.y = 0; } pressedCategory = NULL; pressedCommand = NULL; updateWindow = FALSE; if (FALSE != GetClientRect(hwnd, &rect) && FALSE != PtInRect(&rect, pt)) { pt.x -= origin.x; pt.y -= origin.y; pressedCategory = ListWidget_GetCategoryFromPoint(self, pt); if (NULL == pressedCategory) { if (NULL != self->selectedItem && FALSE != ListWidgetItem_IsInteractive(self->selectedItem)) { index = self->commandsCount; while(index--) { if (FALSE != ListWidget_GetCommandRect(self->commands[index], &rect)) { OffsetRect(&rect, self->selectedItem->rect.left, self->selectedItem->rect.top); if (PtInRect(&rect, pt)) { pressedCommand = self->commands[index]; if (FALSE != ListWidget_GetCommandDisabled(pressedCommand) || FALSE == ListWidget_GetCommandPressed(pressedCommand)) { pressedCommand = NULL; } break; } } } } } } if (NULL != self->pressedCategory) { if (NULL != pressedCategory) { ListWidgetCategoryMetric metrics; if (FALSE == ListWidget_GetCategoryMetrics(style, &metrics)) SetRectEmpty(&rect); else { CopyRect(&rect, &pressedCategory->rect); rect.left += metrics.offsetLeft; rect.top += metrics.offsetTop; rect.right = rect.left + metrics.iconWidth; rect.bottom -= (metrics.offsetBottom + metrics.lineHeight + metrics.lineOffsetTop); } if (FALSE == PtInRect(&rect, pt)) pressedCategory = NULL; } if(self->pressedCategory == pressedCategory) { ListWidget_ToggleCategory(pressedCategory, hwnd); } self->pressedCategory = NULL; } if (NULL != self->selectedItem) { if (NULL != pressedCommand && NULL != self->selectedItem) { ListWidget_SendItemCommand(self->selectedItem->name, ListWidget_GetCommandName(pressedCommand), hwnd, 0, TRUE); } index = self->commandsCount; while(index--) { if (FALSE != ListWidget_GetCommandPressed(self->commands[index])) { if (FALSE != ListWidget_SetCommandPressed(self->commands[index], FALSE) && FALSE != ListWidget_GetCommandRect(self->commands[index], &rect)) { OffsetRect(&rect, self->selectedItem->rect.left + origin.x, self->selectedItem->rect.top + origin.y); InvalidateRect(hwnd, &rect, FALSE); updateWindow = TRUE; } break; } } if (FALSE != ListWidget_EnsureItemVisisble(self, hwnd, self->selectedItem, VISIBLE_NORMAL)) updateWindow = TRUE; } if (FALSE != ListWidget_UpdateHoverEx(self, hwnd, cursor)) updateWindow = TRUE; if (GetCapture() == hwnd) ReleaseCapture(); if (FALSE != updateWindow) UpdateWindow(hwnd); ListWidget_TooltipRelayMouseMessage(self->tooltip, WM_LBUTTONUP, vKeys, cursor); return TRUE; } static BOOL ListWidget_LeftButtonDblClkCb(ListWidget *self, HWND hwnd, unsigned int vKeys, const POINT *cursor) { RECT rect; POINT pt = *cursor, origin; ListWidgetCategory *category; if (NULL != self->titleEditItem) { KillTimer(hwnd, LISTWIDGETTIMER_EDIT_TITLE_ID); self->titleEditItem = NULL; } if (0 != (ListWidgetFlag_LButtonDownOnCommand & self->flags)) return FALSE; if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) { origin.x = 0; origin.y = 0; } if (FALSE != GetClientRect(hwnd, &rect) && FALSE != PtInRect(&rect, pt)) { pt.x -= origin.x; pt.y -= origin.y; category = ListWidget_GetCategoryFromPoint(self, pt); if (NULL != category) { ListWidget_ToggleCategory(category, hwnd); self->pressedCategory = NULL; return TRUE; } ListWidgetItem *item = ListWidget_GetItemFromPointEx(self, pt, &category, NULL); if (NULL != item) { if (NULL != item && FALSE != ListWidgetItem_IsInteractive(item)) { size_t index; index = self->commandsCount; while(index--) { if (FALSE != ListWidget_GetCommandRect(self->commands[index], &rect)) { OffsetRect(&rect, item->rect.left, item->rect.top); if (PtInRect(&rect, pt)) { item = NULL; break; } } } } if (NULL != item) { ListWidget_CallItemAction(self, hwnd, category, item); return TRUE; } } } return FALSE; } static BOOL ListWidget_RightButtonDownCb(ListWidget *self, HWND hwnd, unsigned int vKeys, const POINT *cursor) { ListWidgetItem *selectedItem; RECT rect; POINT pt, origin; pt = *cursor; if (NULL != self->titleEditItem) { KillTimer(hwnd, LISTWIDGETTIMER_EDIT_TITLE_ID); self->titleEditItem = NULL; } if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) { origin.x = 0; origin.y = 0; } selectedItem = NULL; if (FALSE != GetClientRect(hwnd, &rect) && FALSE != PtInRect(&rect, pt)) { pt.x -= origin.x; pt.y -= origin.y; selectedItem = ListWidget_GetItemFromPoint(self, pt); } ListWidget_SelectItem(self, hwnd, selectedItem, TRUE); ListWidget_EnsureFocused(self, hwnd, FALSE); if (GetCapture() != hwnd) SetCapture(hwnd); UpdateWindow(hwnd); ListWidget_TooltipRelayMouseMessage(self->tooltip, WM_RBUTTONDOWN, vKeys, cursor); return FALSE; // allow defwindowproc to get this message } static BOOL ListWidget_RightButtonUpCb(ListWidget *self, HWND hwnd, unsigned int vKeys, const POINT *cursor) { RECT rect; if (FALSE != ListWidget_UpdateHoverEx(self, hwnd, cursor)) UpdateWindow(hwnd); if (GetCapture() == hwnd) ReleaseCapture(); ListWidget_TooltipRelayMouseMessage(self->tooltip, WM_RBUTTONUP, vKeys, cursor); if (FALSE != GetClientRect(hwnd, &rect) && FALSE != PtInRect(&rect, *cursor)) { ListWidgetItem *item; ListWidgetItemMetric metrics; POINT pt; if (FALSE == ListWidget_GetViewOrigin(hwnd, &pt)) { pt.x = 0; pt.y = 0; } pt.x = cursor->x - pt.x; pt.y = cursor->y - pt.y; item = ListWidget_GetItemFromPoint(self, pt); if (NULL != item) { WidgetStyle *style = WIDGET_GET_STYLE(hwnd); if (NULL != style && FALSE != ListWidget_GetItemMetrics(style, &metrics)) { ListWidgetItemPart part; part = ListWidgetItemPart_Command | ListWidgetItemPart_Activity; part = ListWidget_GetItemPartFromPoint(self, item, &metrics, pt, part, NULL); if (ListWidgetItemPart_None != part) return TRUE; } } } return FALSE; // allow defwindowproc to get this message } static BOOL ListWidget_KeyDownCb(ListWidget *self, HWND hwnd, unsigned int vKey, unsigned int flags) { ListWidgetItem *selectedItem; ListWidgetVisibleFlags visibleFlags; selectedItem = NULL; visibleFlags = VISIBLE_NORMAL/*VISIBLE_PARTIAL_OK*/; switch(vKey) { case VK_HOME: ListWidget_EnsureTopVisible(self, hwnd); visibleFlags |= VISIBLE_ALIGN_TOP | VISIBLE_ALIGN_ALWAYS; selectedItem = ListWidget_GetFirstItem(self); break; case VK_END: ListWidget_EnsureBottomVisible(self, hwnd); visibleFlags |= VISIBLE_ALIGN_BOTTOM | VISIBLE_ALIGN_ALWAYS; selectedItem = ListWidget_GetLastItem(self); break; case VK_LEFT: selectedItem = (NULL != self->selectedItem) ? ListWidget_GetPreviousItem(self, self->selectedItem) : ListWidget_GetLastItem(self); if (NULL == selectedItem) ListWidget_EnsureTopVisible(self, hwnd); break; case VK_RIGHT: selectedItem = (NULL != self->selectedItem) ? ListWidget_GetNextItem(self, self->selectedItem) : ListWidget_GetFirstItem(self); if (NULL == selectedItem) ListWidget_EnsureBottomVisible(self, hwnd); break; case VK_UP: selectedItem = (NULL != self->selectedItem) ? ListWidget_GetPreviousLineItem(self, self->selectedItem) : ListWidget_GetLastItem(self); if (NULL == selectedItem) ListWidget_EnsureTopVisible(self, hwnd); break; case VK_DOWN: selectedItem = (NULL != self->selectedItem) ? ListWidget_GetNextLineItem(self, self->selectedItem) : ListWidget_GetFirstItem(self); if (NULL == selectedItem) ListWidget_EnsureBottomVisible(self, hwnd); break; case VK_PRIOR: visibleFlags |= VISIBLE_ALIGN_BOTTOM; selectedItem = (NULL != self->selectedItem) ? ListWidget_GetPreviousPageItem(self, hwnd, self->selectedItem) : ListWidget_GetLastItem(self); if (NULL == selectedItem) ListWidget_EnsureTopVisible(self, hwnd); break; case VK_NEXT: visibleFlags |= VISIBLE_ALIGN_TOP; selectedItem = (NULL != self->selectedItem) ? ListWidget_GetNextPageItem(self, hwnd, self->selectedItem) : ListWidget_GetFirstItem(self); if (NULL == selectedItem) ListWidget_EnsureBottomVisible(self, hwnd); break; case VK_RETURN: if (NULL != self->selectedItem) { ListWidget_CallItemAction(self, hwnd, NULL, self->selectedItem); ListWidget_EnsureItemVisisble(self, hwnd, self->selectedItem, visibleFlags); } break; case VK_F2: if (NULL != self->selectedItem) { if (NULL != self->titleEditor) DestroyWindow(self->titleEditor); self->titleEditor = ListWidget_BeginItemTitleEdit(self, hwnd, self->selectedItem); } break; default: return FALSE; } if (NULL != selectedItem) { ListWidget_SelectItem(self, hwnd, selectedItem, FALSE); ListWidget_EnsureItemVisisble(self, hwnd, self->selectedItem, visibleFlags); } return TRUE; } static BOOL ListWidget_KeyUpCb(ListWidget *self, HWND hwnd, unsigned int vKey, unsigned int flags) { return FALSE; } static BOOL ListWidget_CharacterCb(ListWidget *self, HWND hwnd, unsigned int vKey, unsigned int flags) { return FALSE; } static INT ListWidget_InputRequestCb(ListWidget *self, HWND hwnd, unsigned int vKey, MSG *message) { INT result; if (NULL == message) return DLGC_WANTALLKEYS; ListWidget_TooltipHide(self->tooltip); result = DLGC_WANTCHARS; switch(vKey) { case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN: case VK_PRIOR: case VK_NEXT: case VK_END: case VK_HOME: case VK_RETURN: result |= DLGC_WANTALLKEYS; break; } return result; } static void ListWidget_FocusChangedCb(ListWidget *self, HWND hwnd, HWND focusWindow, BOOL focusReceived) { if (NULL == self) return; if (NULL == self->selectedItem) { if (FALSE != focusReceived && 0 == (ListWidgetFlag_NoFocusSelect & self->flags)) { BOOL disableSelect; disableSelect = FALSE; if (NULL != focusWindow) { HWND ancestorWindow; ancestorWindow = hwnd; while(NULL != ancestorWindow) { ancestorWindow = GetAncestor(ancestorWindow, GA_PARENT); if (focusWindow == ancestorWindow) { wchar_t buffer[64] = {0}; if (0 != GetClassName(focusWindow, buffer, ARRAYSIZE(buffer)) && CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, buffer, -1, L"Winamp Gen", -1)) { disableSelect = TRUE; } break; } } } if (FALSE == disableSelect) { ListWidgetItem *item; item = ListWidget_GetFirstItem(self); if (NULL != item) ListWidget_SelectItem(self, hwnd, item, FALSE); } } } else { POINT origin; RECT rect; CopyRect(&rect, &self->selectedItem->rect); if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) OffsetRect(&rect, origin.x, origin.y); InvalidateRect(hwnd, &rect, FALSE); UpdateWindow(hwnd); } } static BOOL ListWidget_ContextMenuCb(ListWidget *self, HWND hwnd, HWND targetWindow, const POINT *cursor) { POINT pt; ListWidgetItem *item; if (NULL == self) return FALSE; if (NULL == cursor || (-1 == cursor->x && -1 == cursor->y)) { if (hwnd != targetWindow) return FALSE; item = self->selectedItem; if (NULL != item) { POINT origin; pt.x = RECTWIDTH(item->rect); pt.x = item->rect.left + pt.x/2 + pt.x%2; pt.y = RECTHEIGHT(item->rect); pt.y = item->rect.top + pt.y/2 + pt.y%2; if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) { pt.x += origin.x; pt.y += origin.y; } } else { RECT rect; GetClientRect(hwnd, &rect); pt.x = rect.left + 2; pt.y = rect.top + 2; } MapWindowPoints(hwnd, HWND_DESKTOP, &pt, 1); } else { POINT test; pt = *cursor; if (FALSE == ListWidget_GetViewOrigin(hwnd, &test)) { test = pt; } else { test.x = pt.x - test.x; test.y = pt.y - test.y; } MapWindowPoints(HWND_DESKTOP, hwnd, &test, 1); item = ListWidget_GetItemFromPoint(self, test); } if (NULL == item) ListWidget_DisplayContextMenu(self, hwnd, pt); else ListWidget_DisplayItemContextMenu(self, hwnd, item, pt); return TRUE; }; static void ListWidget_ZoomChangingCb(ListWidget *self, HWND hwnd, NMTRBTHUMBPOSCHANGING *zoomInfo) { double pos, height, width; int cx, cy; if (NULL == self) return; pos = (double)(int)zoomInfo->dwPos; if (0 == pos) { height = LISTWIDGET_IMAGE_DEFAULT_HEIGHT; } else if (pos < 0) { height = ((LISTWIDGET_IMAGE_DEFAULT_HEIGHT - LISTWIDGET_IMAGE_MIN_HEIGHT) * (100.0 + pos))/100.0; height += LISTWIDGET_IMAGE_MIN_HEIGHT; height = floor(height); } else { height = ((LISTWIDGET_IMAGE_MAX_HEIGHT - LISTWIDGET_IMAGE_DEFAULT_HEIGHT) * pos)/100.0; height += LISTWIDGET_IMAGE_DEFAULT_HEIGHT; height = ceil(height); } width = (height * LISTWIDGET_IMAGE_DEFAULT_WIDTH)/ LISTWIDGET_IMAGE_DEFAULT_HEIGHT; cx = (int)(width + 0.5); cy = (int)(height + 0.5); if (self->imageSize.cx == cx && self->imageSize.cy == cy) { return; } ListWidget_SetImageSize(self, hwnd, cx, cy, TRUE); // aTRACE_FMT("zoom changing: pos = %d, width = %d, height = %d\r\n", zoomInfo->dwPos, cx, cy); } static void ListWidget_ScrollCb(ListWidget *self, HWND hwnd, int *dx, int *dy) { if (NULL != self->titleEditor) { DestroyWindow(self->titleEditor); self->titleEditor = NULL; } if (FALSE != ListWidget_UpdateHover(self, hwnd)) UpdateWindow(hwnd); ListWidget_TooltipHide(self->tooltip); } static BOOL ListWidget_NotifyCb(ListWidget *self, HWND hwnd, NMHDR *pnmh, LRESULT *result) { if (FALSE != ListWidget_TooltipProcessNotification(self, self->tooltip, pnmh, result)) return TRUE; return FALSE; } HWND ListWidget_CreateWindow(HWND parentWindow, int x, int y, int width, int height, BOOL border, unsigned int controlId) { const static WidgetInterface widgetInterface = { (WidgetInitCallback)ListWidget_InitCb, (WidgetDestroyCallback)ListWidget_DestroyCb, (WidgetLayoutCallback)ListWidget_LayoutCb, (WidgetPaintCallback)ListWidget_PaintCb, (WidgetStyleCallback)ListWidget_StyleColorChangedCb, (WidgetStyleCallback)ListWidget_StyleFontChangedCb, (WidgetMouseCallback)ListWidget_MouseMoveCb, (WidgetMouseCallback)ListWidget_LeftButtonDownCb, (WidgetMouseCallback)ListWidget_LeftButtonUpCb, (WidgetMouseCallback)ListWidget_LeftButtonDblClkCb, (WidgetMouseCallback)ListWidget_RightButtonDownCb, (WidgetMouseCallback)ListWidget_RightButtonUpCb, (WidgetKeyCallback)ListWidget_KeyDownCb, (WidgetKeyCallback)ListWidget_KeyUpCb, (WidgetKeyCallback)ListWidget_CharacterCb, (WidgetInputCallback)ListWidget_InputRequestCb, (WidgetFocusCallback)ListWidget_FocusChangedCb, (WidgetMenuCallback)ListWidget_ContextMenuCb, (WidgetZoomCallback)ListWidget_ZoomChangingCb, (WidgetScrollCallback)NULL, /*scrollBefore*/ (WidgetScrollCallback)ListWidget_ScrollCb, (WidgetNotifyCallback)ListWidget_NotifyCb, }; return Widget_CreateWindow(WIDGET_TYPE_LIST, &widgetInterface, NULL, (FALSE != border) ? WS_EX_CLIENTEDGE : 0, WS_TABSTOP, x, y, width, height, parentWindow, controlId, 0L); }