diff options
Diffstat (limited to 'Src/Plugins/Library/ml_devices')
172 files changed, 33873 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_devices/backBuffer.cpp b/Src/Plugins/Library/ml_devices/backBuffer.cpp new file mode 100644 index 00000000..881c37bf --- /dev/null +++ b/Src/Plugins/Library/ml_devices/backBuffer.cpp @@ -0,0 +1,276 @@ +#include "main.h" +#include "./backBuffer.h" + + +BOOL +BackBuffer_Initialize(BackBuffer *self, HWND hwnd) +{ + if (NULL == self) + return FALSE; + + ZeroMemory(self, sizeof(BackBuffer)); + + self->hwnd = hwnd; + + return TRUE; +} + +void +BackBuffer_Uninitialize(BackBuffer *self) +{ + BackBuffer_Reset(self); +} + +void +BackBuffer_Reset(BackBuffer *self) +{ + if (NULL == self) + return; + + if (NULL != self->hdc) + { + if (NULL != self->previous) + SelectBitmap(self->hdc, self->previous); + + DeleteDC(self->hdc); + } + + if (NULL != self->bitmap) + { + DeleteObject(self->bitmap); + } + + ZeroMemory(self, sizeof(BackBuffer)); +} + +BOOL +BackBuffer_EnsureSize(BackBuffer *self, long width, long height) +{ + return BackBuffer_EnsureSizeEx(self, width, height, width, height); +} + +BOOL +BackBuffer_EnsureSizeEx(BackBuffer *self, long width, long height, long allocWidth, long allocHeight) +{ + BOOL result; + HDC windowDC; + HBITMAP bitmap; + long bitmapWidth, bitmapHeight; + + if (NULL == self) + return FALSE; + + if (width < 0) + width = 0; + + if (height < 0) + height = 0; + + if (NULL != self->bitmap) + { + BITMAP bitmapInfo; + if (sizeof(bitmapInfo) == GetObject(self->bitmap, sizeof(bitmapInfo), &bitmapInfo)) + { + if (bitmapInfo.bmWidth >= width && bitmapInfo.bmHeight >= height) + return TRUE; + + bitmapWidth = bitmapInfo.bmWidth; + bitmapHeight = bitmapInfo.bmHeight; + } + else + { + bitmapWidth = 0; + bitmapHeight = 0; + } + } + else + { + bitmapWidth = 0; + bitmapHeight = 0; + } + + result = FALSE; + bitmap = NULL; + + windowDC = GetDCEx(self->hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if(NULL != windowDC) + { + if (allocWidth < width) + allocWidth = width; + + if (allocWidth < bitmapWidth) + allocWidth = bitmapWidth; + + if (allocHeight < height) + allocHeight = height; + + if (allocHeight < bitmapHeight) + allocHeight = bitmapHeight; + + bitmap = CreateCompatibleBitmap(windowDC, allocWidth, allocHeight); + ReleaseDC(self->hwnd, windowDC); + } + + if (NULL != bitmap) + { + if (NULL != self->hdc) + SelectBitmap(self->hdc, bitmap); + + if (NULL != self->bitmap) + DeleteObject(self->bitmap); + + self->bitmap = bitmap; + result = TRUE; + } + + return result; +} + +HDC +BackBuffer_GetDC(BackBuffer *self) +{ + if (NULL == self) + return FALSE; + + if (NULL == self->hdc) + { + HDC windowDC; + windowDC = GetDCEx(self->hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != windowDC) + { + self->hdc = CreateCompatibleDC(windowDC); + ReleaseDC(self->hwnd, windowDC); + + if (NULL != self->hdc) + { + if (NULL != self->bitmap) + self->previous = SelectBitmap(self->hdc, self->bitmap); + else + self->previous = GetCurrentBitmap(self->hdc); + } + } + } + + return self->hdc; +} + +BOOL +BackBuffer_Copy(BackBuffer *self, HDC hdc, long x, long y, long width, long height) +{ + HDC sourceDC; + + if (NULL == self || NULL == self->bitmap) + return FALSE; + + sourceDC = BackBuffer_GetDC(self); + if (NULL == sourceDC) + return FALSE; + + return BitBlt(hdc, x, y, width, height, sourceDC, 0, 0, SRCCOPY); +} + +BOOL +BackBuffer_DrawTextEx(BackBuffer *self, HDC hdc, const wchar_t *string, + int length, RECT *rect, unsigned int format, + HFONT font, COLORREF backColor, COLORREF textColor, int backMode) +{ + BOOL result = FALSE; + RECT bufferRect; + + if (NULL == hdc || NULL == rect) + return FALSE; + + SetRect(&bufferRect, 0, 0, RECTWIDTH(*rect), RECTHEIGHT(*rect)); + + if (NULL != self && + FALSE != BackBuffer_EnsureSize(self, bufferRect.right, bufferRect.bottom)) + { + HDC bufferDC = BackBuffer_GetDC(self); + if (NULL != bufferDC) + { + HFONT prevFont; + prevFont = SelectFont(bufferDC, font); + SetTextColor(bufferDC, textColor); + SetBkColor(bufferDC, backColor); + SetBkMode(bufferDC, backMode); + + if (OPAQUE == backMode) + ExtTextOut(bufferDC, 0, 0, ETO_OPAQUE, &bufferRect, NULL, 0, NULL); + + if (FALSE != DrawText(bufferDC, string, length, &bufferRect, format)) + { + result = BackBuffer_Copy(self, hdc, rect->left, rect->top, + bufferRect.right, bufferRect.bottom); + } + + SelectFont(bufferDC, prevFont); + } + } + + + if (FALSE == result) + { + HFONT prevFont; + COLORREF prevBackColor, prevTextColor; + int prevBkMode; + + prevFont = SelectFont(hdc, font); + prevTextColor = SetTextColor(hdc, textColor); + prevBackColor = SetBkColor(hdc, backColor); + prevBkMode= SetBkMode(hdc, backMode); + + result = DrawText(hdc, string, length, rect, format); + + SelectFont(hdc, prevFont); + SetTextColor(hdc, prevTextColor); + SetBkColor(hdc, prevBackColor); + SetBkMode(hdc, prevBkMode); + } + + return result; +} + +BOOL +BackBuffer_DrawText(BackBuffer *self, HDC hdc, const wchar_t *string, + int length, RECT *rect, unsigned int format) +{ + BOOL result = FALSE; + RECT bufferRect; + + if (NULL == hdc || NULL == rect) + return FALSE; + + SetRect(&bufferRect, 0, 0, RECTWIDTH(*rect), RECTHEIGHT(*rect)); + + if (NULL != self && + FALSE != BackBuffer_EnsureSize(self, bufferRect.right, bufferRect.bottom)) + { + HDC bufferDC = BackBuffer_GetDC(self); + if (NULL != bufferDC) + { + HFONT prevFont; + int backMode; + prevFont = SelectFont(bufferDC, GetCurrentFont(hdc)); + SetTextColor(bufferDC, GetTextColor(hdc)); + SetBkColor(bufferDC, GetBkColor(hdc)); + backMode = GetBkMode(hdc); + SetBkMode(bufferDC, backMode); + + if (OPAQUE == backMode) + ExtTextOut(bufferDC, 0, 0, ETO_OPAQUE, &bufferRect, NULL, 0, NULL); + + if (FALSE != DrawText(bufferDC, string, length, &bufferRect, format)) + { + result = BackBuffer_Copy(self, hdc, rect->left, rect->top, + bufferRect.right, bufferRect.bottom); + } + + SelectFont(bufferDC, prevFont); + } + } + + if (FALSE == result) + result = DrawText(hdc, string, length, rect, format); + + return result; +} diff --git a/Src/Plugins/Library/ml_devices/backBuffer.h b/Src/Plugins/Library/ml_devices/backBuffer.h new file mode 100644 index 00000000..6a0031f8 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/backBuffer.h @@ -0,0 +1,64 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_BACK_BUFFER_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_BACK_BUFFER_HEADER + +typedef struct BackBuffer +{ + HBITMAP bitmap; + HWND hwnd; + HDC hdc; + HBITMAP previous; +} BackBuffer; + +BOOL +BackBuffer_Initialize(BackBuffer *self, HWND hwnd); + +void +BackBuffer_Uninitialize(BackBuffer *self); + +BOOL +BackBuffer_EnsureSize(BackBuffer *self, + long width, + long height); + +BOOL +BackBuffer_EnsureSizeEx(BackBuffer *self, + long width, + long height, + long allocWidth, + long allocHeight); + +HDC +BackBuffer_GetDC(BackBuffer *self); + +BOOL +BackBuffer_Copy(BackBuffer *self, + HDC hdc, + long x, + long y, + long width, + long height); + +void +BackBuffer_Reset(BackBuffer *self); + +BOOL +BackBuffer_DrawText(BackBuffer *self, + HDC hdc, + const wchar_t *string, + int length, + RECT *rect, + unsigned int format); + +BOOL +BackBuffer_DrawTextEx(BackBuffer *self, + HDC hdc, + const wchar_t *string, + int length, + RECT *rect, + unsigned int format, + HFONT font, + COLORREF backColor, + COLORREF textColor, + int backMode); + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_BACK_BUFFER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/common.h b/Src/Plugins/Library/ml_devices/common.h new file mode 100644 index 00000000..8713a2bd --- /dev/null +++ b/Src/Plugins/Library/ml_devices/common.h @@ -0,0 +1,76 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_COMMON_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_COMMON_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) + +#ifdef __cplusplus + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) ::SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#else + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#endif // __cplusplus + +#define SENDMLIPC(__hwndML, __ipcMsgId, __param) SENDMSG((__hwndML), WM_ML_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) + +#define SENDWAIPC(__hwndWA, __ipcMsgId, __param) SENDMSG((__hwndWA), WM_WA_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) + +#define SENDCMD(__hwnd, __ctrlId, __eventId, __hctrl) (SENDMSG((__hwnd), WM_COMMAND, MAKEWPARAM(__ctrlId, __eventId), (LPARAM)(__hctrl))) + +#define DIALOG_RESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWLP_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; } + +#ifndef GetWindowStyle + #define GetWindowStyle(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_STYLE)) +#endif //GetWindowStyle + +#ifndef SetWindowStyle + #define SetWindowStyle(__hwnd, __style) (SetWindowLongPtr((__hwnd), GWL_STYLE, (__style))) +#endif //SetWindowStyle + +#ifndef GetWindowStyleEx + #define GetWindowStyleEx(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_EXSTYLE)) +#endif // GetWindowStyleEx + +#ifndef SetWindowStyleEx + #define SetWindowStyleEx(__hwnd, __style) (SetWindowLongPtr((__hwnd), GWL_EXSTYLE, (__style))) +#endif //SetWindowStyle + +#ifndef RECTWIDTH + #define RECTWIDTH(__r) ((__r).right - (__r).left) +#endif + +#ifndef RECTHEIGHT + #define RECTHEIGHT(__r) ((__r).bottom - (__r).top) +#endif + +#undef CLAMP +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +#ifndef ARRAYSIZE + #define ARRAYSIZE(_a) (sizeof(_a)/sizeof((_a)[0])) +#endif + +#ifndef ABS + #define ABS(x) (((x) > 0) ? (x) : (-x)) +#endif + +#ifndef MIN + #define MIN(v1, v2) (((v1) < (v2)) ? (v1) : (v2)) +#endif + +#ifndef MAX + #define MAX(v1, v2) (((v1) > (v2)) ? (v1) : (v2)) +#endif + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_COMMON_HEADER diff --git a/Src/Plugins/Library/ml_devices/config.cpp b/Src/Plugins/Library/ml_devices/config.cpp new file mode 100644 index 00000000..e2abd6bb --- /dev/null +++ b/Src/Plugins/Library/ml_devices/config.cpp @@ -0,0 +1,119 @@ +#include "main.h" +#include "./config.h" + +#include <strsafe.h> + +#define CONFIG_SUFFIX L"Plugins\\ml" +#define CONFIG_FILE L"ml_devices.ini" + +static const char * +Config_GetPath(BOOL ensureExist) +{ + static const char *configPath = NULL; + if (NULL == configPath) + { + const wchar_t *userPath; + wchar_t buffer[MAX_PATH * 2] = {0}; + + if (NULL == WASABI_API_APP) + return NULL; + + userPath = WASABI_API_APP->path_getUserSettingsPath(); + if (NULL == userPath) + return NULL; + + if (0 != PathCombine(buffer, userPath, CONFIG_SUFFIX)) + { + if ((FALSE == ensureExist || SUCCEEDED(Plugin_EnsurePathExist(buffer))) && + FALSE != PathAppend(buffer,CONFIG_FILE)) + { + configPath = String_ToAnsi(CP_UTF8, 0, buffer, -1, NULL, NULL); + } + } + } + + return configPath; +} + + +unsigned long +Config_ReadString(const char *section, const char *key, const char *defaultValue, char *returnedString, unsigned long size) +{ + return GetPrivateProfileStringA(section, key, defaultValue, returnedString, size, Config_GetPath(FALSE)); +} + +unsigned int +Config_ReadInt(const char *section, const char *key, int defaultValue) +{ + return GetPrivateProfileIntA(section, key, defaultValue, Config_GetPath(FALSE)); +} + +BOOL +Config_ReadBool(const char *section, const char *key, BOOL defaultValue) +{ + char buffer[32] = {0}; + int length = Config_ReadString(section, key, NULL, buffer, ARRAYSIZE(buffer)); + + if (0 == length) + return defaultValue; + + if (1 == length) + { + switch(*buffer) + { + case '0': + case 'n': + case 'f': + return FALSE; + case '1': + case 'y': + case 't': + return TRUE; + } + } + else + { + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "yes", -1, buffer, length) || + CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "true", -1, buffer, length)) + { + return TRUE; + } + + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "no", -1, buffer, length) || + CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "false", -1, buffer, length)) + { + return FALSE; + } + } + + if (FALSE != StrToIntExA(buffer, STIF_SUPPORT_HEX, &length)) + return (0 != length); + + return defaultValue; +} + +BOOL +Config_WriteString(const char *section, const char *key, const char *value) +{ + const char *configPath = Config_GetPath(TRUE); + if (NULL == configPath || '\0' == *configPath) + return FALSE; + + return (0 != WritePrivateProfileStringA(section, key, value, configPath)); +} + +BOOL +Config_WriteInt(const char *section, const char *key, int value) +{ + char buffer[32] = {0}; + if (FAILED(StringCchPrintfA(buffer, ARRAYSIZE(buffer), "%d", value))) + return FALSE; + + return Config_WriteString(section, key, buffer); +} + +BOOL +Config_WriteBool(const char *section, const char *key, BOOL value) +{ + return Config_WriteString(section, key, (FALSE != value) ? "yes" : "no"); +} diff --git a/Src/Plugins/Library/ml_devices/config.h b/Src/Plugins/Library/ml_devices/config.h new file mode 100644 index 00000000..a2b17978 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/config.h @@ -0,0 +1,44 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_CONFIG_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_CONFIG_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + + +unsigned long +Config_ReadString(const char *section, + const char *key, + const char *defaultValue, + char *returnedStrign, + unsigned long size); + +unsigned int +Config_ReadInt(const char *section, + const char *key, + int defaultValue); + +BOOL +Config_ReadBool(const char *section, + const char *key, + BOOL defaultValue); + +BOOL +Config_WriteString(const char *section, + const char *key, + const char *value); + + +BOOL +Config_WriteInt(const char *section, + const char *key, + int value); + +BOOL +Config_WriteBool(const char *section, + const char *key, + BOOL value); + +#endif // _NULLSOFT_WINAMP_ML_DEVICES_CONFIG_HEADER diff --git a/Src/Plugins/Library/ml_devices/deviceCommands.cpp b/Src/Plugins/Library/ml_devices/deviceCommands.cpp new file mode 100644 index 00000000..2f3ba8e4 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/deviceCommands.cpp @@ -0,0 +1,207 @@ +#include "main.h" +#include "deviceCommands.h" + + +typedef struct DeviceCommandInfo +{ + const char *name; + unsigned int title; + unsigned int description; + unsigned int smallIcon; + unsigned int largeIcon; +} DeviceCommandInfo; + + +static DeviceCommandInfo registeredCommands[] = +{ + { "sync", + IDS_SYNC_COMMAND_TITLE, + IDS_SYNC_COMMAND_DESC, + IDR_SYNC_COMMAND_SMALL_IMAGE, + IDR_SYNC_COMMAND_LARGE_IMAGE }, + { "cancel_sync", + IDS_CANCEL_SYNC_COMMAND_TITLE, + IDS_CANCEL_SYNC_COMMAND_DESC, + IDR_CANCEL_SYNC_COMMAND_SMALL_IMAGE, + IDR_CANCEL_SYNC_COMMAND_LARGE_IMAGE }, + { "attach", + IDS_ATTACH_COMMAND_TITLE, + IDS_ATTACH_COMMAND_DESC, + IDR_ATTACH_COMMAND_SMALL_IMAGE, + IDR_ATTACH_COMMAND_LARGE_IMAGE }, + { "detach", + IDS_DETACH_COMMAND_TITLE, + IDS_DETACH_COMMAND_DESC, + IDR_DETACH_COMMAND_SMALL_IMAGE, + IDR_DETACH_COMMAND_LARGE_IMAGE }, + { "eject", + IDS_EJECT_COMMAND_TITLE, + IDS_EJECT_COMMAND_DESC, + IDR_EJECT_COMMAND_SMALL_IMAGE, + IDR_EJECT_COMMAND_LARGE_IMAGE }, + { "rename", + IDS_RENAME_COMMAND_TITLE, + IDS_RENAME_COMMAND_DESC, + IDR_RENAME_COMMAND_SMALL_IMAGE, + IDR_RENAME_COMMAND_LARGE_IMAGE }, + { "view_open", + IDS_VIEW_OPEN_COMMAND_TITLE, + IDS_VIEW_OPEN_COMMAND_DESC, + IDR_VIEW_OPEN_COMMAND_SMALL_IMAGE, + IDR_VIEW_OPEN_COMMAND_LARGE_IMAGE }, + { "preferences", + IDS_PREFERENCES_COMMAND_TITLE, + IDS_PREFERENCES_COMMAND_DESC, + IDR_PREFERENCES_COMMAND_SMALL_IMAGE, + IDR_PREFERENCES_COMMAND_LARGE_IMAGE }, + { "playlist_create", + IDS_PLAYLIST_CREATE_COMMAND_TITLE, + IDS_PLAYLIST_CREATE_COMMAND_DESC, + IDR_PLAYLIST_CREATE_COMMAND_SMALL_IMAGE, + IDR_PLAYLIST_CREATE_COMMAND_LARGE_IMAGE }, +}; + +static ifc_devicecommand * _cdecl +DeviceCommands_RegisterCommandCb(const char *name, void *user) +{ + size_t index; + wchar_t buffer[2048] = {0}; + DeviceCommandInfo *commandInfo; + ifc_devicecommand *command; + ifc_devicecommandeditor *editor; + + commandInfo = NULL; + for(index = 0; index < ARRAYSIZE(registeredCommands); index++) + { + if (name == registeredCommands[index].name) + { + commandInfo = ®isteredCommands[index]; + break; + } + } + + if (NULL == commandInfo) + return NULL; + + if (NULL == WASABI_API_DEVICES) + return NULL; + + if (FAILED(WASABI_API_DEVICES->CreateCommand(commandInfo->name, &command))) + return NULL; + + if (FAILED(command->QueryInterface(IFC_DeviceCommandEditor, (void**)&editor))) + { + command->Release(); + return NULL; + } + + if (0 != commandInfo->title) + { + WASABI_API_LNGSTRINGW_BUF(commandInfo->title, buffer, ARRAYSIZE(buffer)); + editor->SetDisplayName(buffer); + } + + if (0 != commandInfo->description) + { + WASABI_API_LNGSTRINGW_BUF(commandInfo->description, buffer, ARRAYSIZE(buffer)); + editor->SetDescription(buffer); + } + + if (0 != commandInfo->smallIcon || 0 != commandInfo->largeIcon) + { + ifc_deviceiconstore *iconStore; + if (SUCCEEDED(editor->GetIconStore(&iconStore))) + { + if (0 != commandInfo->smallIcon) + { + if (FALSE != Plugin_GetResourceString(MAKEINTRESOURCE(commandInfo->smallIcon), RT_RCDATA, buffer, ARRAYSIZE(buffer))) + iconStore->Add(buffer, 16, 16, TRUE); + } + + if (0 != commandInfo->largeIcon) + { + if (FALSE != Plugin_GetResourceString(MAKEINTRESOURCE(commandInfo->largeIcon), RT_RCDATA, buffer, ARRAYSIZE(buffer))) + iconStore->Add(buffer, 43, 24, TRUE); + } + + iconStore->Release(); + } + } + + editor->Release(); + return command; +} + +BOOL +DeviceCommands_Register() +{ + const char *commands[ARRAYSIZE(registeredCommands)]; + size_t index; + + if (NULL == WASABI_API_DEVICES) + return FALSE; + + for(index = 0; index < ARRAYSIZE(commands); index++) + { + commands[index] = registeredCommands[index].name; + } + + WASABI_API_DEVICES->CommandRegisterIndirect(commands, ARRAYSIZE(commands), DeviceCommands_RegisterCommandCb, NULL); + + return TRUE; +} + + +BOOL +DeviceCommand_GetSupported(ifc_device *device, const char *name, DeviceCommandContext context, + ifc_devicesupportedcommand **commandOut) +{ + ifc_devicesupportedcommandenum *enumerator; + ifc_devicesupportedcommand *command; + BOOL foundCommand; + + if (NULL == device || + NULL == name || + FAILED(device->EnumerateCommands(&enumerator, context))) + { + return FALSE; + } + + foundCommand = FALSE; + + while (S_OK == enumerator->Next(&command, 1, NULL)) + { + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, name, -1, command->GetName(), -1)) + { + foundCommand = TRUE; + + if (NULL != commandOut) + *commandOut = command; + else + command->Release(); + + break; + } + command->Release(); + } + + enumerator->Release(); + return foundCommand; +} + +BOOL +DeviceCommand_GetEnabled(ifc_device *device, const char *name, DeviceCommandContext context) +{ + ifc_devicesupportedcommand *command; + DeviceCommandFlags flags; + + if (FALSE == DeviceCommand_GetSupported(device, name, context, &command)) + return FALSE; + + if (FAILED(command->GetFlags(&flags))) + flags = DeviceCommandFlag_Disabled; + + command->Release(); + + return (0 == (DeviceCommandFlag_Disabled & flags)); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/deviceCommands.h b/Src/Plugins/Library/ml_devices/deviceCommands.h new file mode 100644 index 00000000..bd021364 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/deviceCommands.h @@ -0,0 +1,24 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_DEVICE_COMMANDS_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_DEVICE_COMMANDS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +BOOL +DeviceCommands_Register(); + +BOOL +DeviceCommand_GetSupported(ifc_device *device, + const char *name, + DeviceCommandContext context, + ifc_devicesupportedcommand **commandOut); + +BOOL +DeviceCommand_GetEnabled(ifc_device *device, + const char *name, + DeviceCommandContext context); + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_DEVICE_COMMANDS_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/deviceHandler.cpp b/Src/Plugins/Library/ml_devices/deviceHandler.cpp new file mode 100644 index 00000000..7140f662 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/deviceHandler.cpp @@ -0,0 +1,206 @@ +#include "main.h" +#include "./deviceHandler.h" + +DeviceHandler::DeviceHandler() + : ref(1), relayWindow(NULL) +{ +} + +DeviceHandler::~DeviceHandler() +{ +} + +HRESULT DeviceHandler::CreateInstance(DeviceHandler **instance) +{ + if (NULL == instance) + return E_POINTER; + + *instance = new DeviceHandler(); + + if (NULL == *instance) + return E_OUTOFMEMORY; + + return S_OK; +} + +size_t DeviceHandler::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t DeviceHandler::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int DeviceHandler::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) return E_POINTER; + + if (IsEqualIID(interface_guid, IFC_DeviceEvent)) + *object = static_cast<ifc_deviceevent*>(this); + else + { + *object = NULL; + return E_NOINTERFACE; + } + + if (NULL == *object) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + + +void DeviceHandler::IconChanged(ifc_device *device) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceIconChanged); +} + +void DeviceHandler::DisplayNameChanged(ifc_device *device, const wchar_t *displayName) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceDisplayNameChanged); +} + +void DeviceHandler::AttachmentChanged(ifc_device *device, BOOL attached) +{ + if (NULL != relayWindow) + { + if (FALSE != attached) + { + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceAttached); + } + else + { + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceDetached); + } + } +} + +void DeviceHandler::VisibilityChanged(ifc_device *device, BOOL visible) +{ + if (NULL != relayWindow) + { + if (FALSE == visible) + { + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceHidden); + } + else + { + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceShown); + } + } +} + +void DeviceHandler::TotalSpaceChanged(ifc_device *device, size_t space) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceTotalSpaceChanged); +} + +void DeviceHandler::UsedSpaceChanged(ifc_device *device, size_t space) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceUsedSpaceChanged); +} + +void DeviceHandler::CommandChanged(ifc_device *device) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceCommandChanged); +} + + +void DeviceHandler::ActivityStarted(ifc_device *device, ifc_deviceactivity *activity) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceActivityStarted); +} + +void DeviceHandler::ActivityFinished(ifc_device *device, ifc_deviceactivity *activity) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceActivityFinished); +} + +void DeviceHandler::ActivityChanged(ifc_device *device, ifc_deviceactivity *activity) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceActivityChanged); +} + +void DeviceHandler::ModelChanged(ifc_device *device, const wchar_t *model) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceModelChanged); +} + +void DeviceHandler::StatusChanged(ifc_device *device, const wchar_t *status) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceStatusChanged); +} + +HRESULT DeviceHandler::SetRelayWindow(HWND hwnd) +{ + relayWindow = hwnd; + return S_OK; +} + +HRESULT DeviceHandler::Advise(ifc_device *device) +{ + HRESULT hr; + + if (NULL == device) + return E_INVALIDARG; + + hr = device->Advise(this); + if (FAILED(hr)) + return hr; + + return hr; +} + +HRESULT DeviceHandler::Unadvise(ifc_device *device) +{ + HRESULT hr; + + if (NULL == device) + return E_INVALIDARG; + + hr = device->Unadvise(this); + if (FAILED(hr)) + return hr; + + return hr; +} + +#define CBCLASS DeviceHandler +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +VCB(API_ICONCHANGED, IconChanged) +VCB(API_DISPLAYNAMECHANGED, DisplayNameChanged) +VCB(API_ATTACHMENTCHANGED, AttachmentChanged) +VCB(API_VISIBILITYCHANGED, VisibilityChanged) +VCB(API_TOTALSPACECHANGED, TotalSpaceChanged) +VCB(API_USEDSPACECHANGED, UsedSpaceChanged) +VCB(API_COMMANDCHANGED, CommandChanged) +VCB(API_ACTIVITYSTARTED, ActivityStarted) +VCB(API_ACTIVITYFINISHED, ActivityFinished) +VCB(API_ACTIVITYCHANGED, ActivityChanged) +VCB(API_MODELCHANGED, ModelChanged) +VCB(API_STATUSCHANGED, StatusChanged) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/deviceHandler.h b/Src/Plugins/Library/ml_devices/deviceHandler.h new file mode 100644 index 00000000..67d6bc00 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/deviceHandler.h @@ -0,0 +1,56 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_DEVICE_HANDLER_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_DEVICE_HANDLER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#include "../devices/ifc_deviceevent.h" + +class DeviceHandler: public ifc_deviceevent +{ +protected: + DeviceHandler(); + ~DeviceHandler(); + +public: + static HRESULT CreateInstance(DeviceHandler **instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /* ifc_deviceevent */ + void IconChanged(ifc_device *device); + void DisplayNameChanged(ifc_device *device, const wchar_t *displayName); + void AttachmentChanged(ifc_device *device, BOOL attached); + void VisibilityChanged(ifc_device *device, BOOL visible); + void TotalSpaceChanged(ifc_device *device, size_t space); + void UsedSpaceChanged(ifc_device *device, size_t space); + void CommandChanged(ifc_device *device); + void ActivityStarted(ifc_device *device, ifc_deviceactivity *activity); + void ActivityFinished(ifc_device *device, ifc_deviceactivity *activity); + void ActivityChanged(ifc_device *device, ifc_deviceactivity *activity); + void ModelChanged(ifc_device *device, const wchar_t *model); + void StatusChanged(ifc_device *device, const wchar_t *status); + +public: + HRESULT SetRelayWindow(HWND hwnd); + HRESULT Advise(ifc_device *device); + HRESULT Unadvise(ifc_device *device); + + +protected: + size_t ref; + HWND relayWindow; + +protected: + RECVS_DISPATCH; +}; + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_DEVICE_HANDLER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/deviceManagerHandler.cpp b/Src/Plugins/Library/ml_devices/deviceManagerHandler.cpp new file mode 100644 index 00000000..7bcbfe5d --- /dev/null +++ b/Src/Plugins/Library/ml_devices/deviceManagerHandler.cpp @@ -0,0 +1,174 @@ +#include "main.h" +#include "./deviceManagerHandler.h" + +DeviceManagerHandler::DeviceManagerHandler() + : ref(1), relayWindow(NULL) +{ +} + +DeviceManagerHandler::~DeviceManagerHandler() +{ +} + +HRESULT DeviceManagerHandler::CreateInstance(DeviceManagerHandler **instance) +{ + if (NULL == instance) + return E_POINTER; + + *instance = new DeviceManagerHandler(); + + if (NULL == *instance) + return E_OUTOFMEMORY; + + return S_OK; +} + +size_t DeviceManagerHandler::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t DeviceManagerHandler::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int DeviceManagerHandler::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) return E_POINTER; + + if (IsEqualIID(interface_guid, IFC_DeviceManagerEvent)) + *object = static_cast<ifc_devicemanagerevent*>(this); + else + { + *object = NULL; + return E_NOINTERFACE; + } + + if (NULL == *object) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +void DeviceManagerHandler::TypeAdded(api_devicemanager *manager, ifc_devicetype *type) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_TYPE(relayWindow, type, Event_TypeRegistered); +} + +void DeviceManagerHandler::TypeRemoved(api_devicemanager *manager, ifc_devicetype *type) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_TYPE(relayWindow, type, Event_TypeUnregistered); +} + +void DeviceManagerHandler::ConnectionAdded(api_devicemanager *manager, ifc_deviceconnection *connection) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_CONNECTION(relayWindow, connection, Event_ConnectionRegistered); +} + +void DeviceManagerHandler::ConnectionRemoved(api_devicemanager *manager, ifc_deviceconnection *connection) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_CONNECTION(relayWindow, connection, Event_ConnectionUnregistered); +} + +void DeviceManagerHandler::CommandAdded(api_devicemanager *manager, ifc_devicecommand *command) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_COMMAND(relayWindow, command, Event_CommandRegistered); +} + +void DeviceManagerHandler::CommandRemoved(api_devicemanager *manager, ifc_devicecommand *command) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_COMMAND(relayWindow, command, Event_CommandUnregistered); +} + +void DeviceManagerHandler::DeviceAdded(api_devicemanager *manager, ifc_device *device) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceAdded); +} + +void DeviceManagerHandler::DeviceRemoved(api_devicemanager *manager, ifc_device *device) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DEVICE(relayWindow, device, Event_DeviceRemoved); +} + +void DeviceManagerHandler::DiscoveryStarted(api_devicemanager *manager) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DISCOVERY(relayWindow, manager, Event_DiscoveryStarted); +} + +void DeviceManagerHandler::DiscoveryFinished(api_devicemanager *manager) +{ + if (NULL != relayWindow) + EVENTRELAY_NOTIFY_DISCOVERY(relayWindow, manager, Event_DiscoveryFinished); +} + +HRESULT DeviceManagerHandler::SetRelayWindow(HWND hwnd) +{ + relayWindow = hwnd; + return S_OK; +} + +HRESULT DeviceManagerHandler::Advise(api_devicemanager *manager) +{ + HRESULT hr; + + if (NULL == manager) + return E_INVALIDARG; + + hr = manager->Advise(this); + if (FAILED(hr)) + return hr; + + return hr; +} + +HRESULT DeviceManagerHandler::Unadvise(api_devicemanager *manager) +{ + HRESULT hr; + + if (NULL == manager) + return E_INVALIDARG; + + hr = manager->Unadvise(this); + if (FAILED(hr)) + return hr; + + + return hr; +} + + +#define CBCLASS DeviceManagerHandler +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +VCB(API_TYPEADDED, TypeAdded) +VCB(API_TYPEREMOVED, TypeRemoved) +VCB(API_CONNECTIONADDED, ConnectionAdded) +VCB(API_CONNECTIONREMOVED, ConnectionRemoved) +VCB(API_COMMANDADDED, CommandAdded) +VCB(API_COMMANDREMOVED, CommandRemoved) +VCB(API_DEVICEADDED, DeviceAdded) +VCB(API_DEVICEREMOVED, DeviceRemoved) +VCB(API_DISCOVERYSTARTED, DiscoveryStarted) +VCB(API_DISCOVERYFINISHED, DiscoveryFinished) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/deviceManagerHandler.h b/Src/Plugins/Library/ml_devices/deviceManagerHandler.h new file mode 100644 index 00000000..568627fa --- /dev/null +++ b/Src/Plugins/Library/ml_devices/deviceManagerHandler.h @@ -0,0 +1,54 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_DEVICE_MANAGER_HANDLER_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_DEVICE_MANAGER_HANDLER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../devices/api_devicemanager.h" +//#include <ifc_devicemanagerevent.h> + +class DeviceManagerHandler: public ifc_devicemanagerevent +{ +protected: + DeviceManagerHandler(); + ~DeviceManagerHandler(); + +public: + static HRESULT CreateInstance(DeviceManagerHandler **instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /* ifc_devicemanagerevent */ + void TypeAdded(api_devicemanager *manager, ifc_devicetype *type); + void TypeRemoved(api_devicemanager *manager, ifc_devicetype *type); + void ConnectionAdded(api_devicemanager *manager, ifc_deviceconnection *connection); + void ConnectionRemoved(api_devicemanager *manager, ifc_deviceconnection *connection); + void CommandAdded(api_devicemanager *manager, ifc_devicecommand *command); + void CommandRemoved(api_devicemanager *manager, ifc_devicecommand *command); + void DeviceAdded(api_devicemanager *manager, ifc_device *device); + void DeviceRemoved(api_devicemanager *manager, ifc_device *device); + void DiscoveryStarted(api_devicemanager *manager); + void DiscoveryFinished(api_devicemanager *manager); + +public: + HRESULT SetRelayWindow(HWND hwnd); + HRESULT Advise(api_devicemanager *manager); + HRESULT Unadvise(api_devicemanager *manager); + + +protected: + size_t ref; + HWND relayWindow; + +protected: + RECVS_DISPATCH; +}; + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_DEVICE_MANAGER_HANDLER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/embeddedEditor.cpp b/Src/Plugins/Library/ml_devices/embeddedEditor.cpp new file mode 100644 index 00000000..a465cc07 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/embeddedEditor.cpp @@ -0,0 +1,1240 @@ +#include "main.h" +#include "./embeddedEditor.h" +#include <vector> + +#define EMBEDDEDEDITOR_FRAME_LEFT 1 +#define EMBEDDEDEDITOR_FRAME_TOP 1 +#define EMBEDDEDEDITOR_FRAME_RIGHT 1 +#define EMBEDDEDEDITOR_FRAME_BOTTOM 1 + +#define EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT 1 +#define EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP 1 +#define EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT 1 +#define EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM 1 + +#define EMBEDDEDEDITOR_FRAME_INNER_SPACE_LEFT 0 +#define EMBEDDEDEDITOR_FRAME_INNER_SPACE_TOP 0 +#define EMBEDDEDEDITOR_FRAME_INNER_SPACE_RIGHT 0 +#define EMBEDDEDEDITOR_FRAME_INNER_SPACE_BOTTOM 0 + +#define EMBEDDEDEDITOR_MARGIN_LEFT 4 +#define EMBEDDEDEDITOR_MARGIN_TOP 1 +#define EMBEDDEDEDITOR_MARGIN_RIGHT 4 +#define EMBEDDEDEDITOR_MARGIN_BOTTOM 1 + + +#define EMBEDDEDEDITOR_BORDER_LEFT (EMBEDDEDEDITOR_FRAME_LEFT + \ + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT + \ + EMBEDDEDEDITOR_FRAME_INNER_SPACE_LEFT) + +#define EMBEDDEDEDITOR_BORDER_TOP (EMBEDDEDEDITOR_FRAME_TOP + \ + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP + \ + EMBEDDEDEDITOR_FRAME_INNER_SPACE_TOP) + +#define EMBEDDEDEDITOR_BORDER_RIGHT (EMBEDDEDEDITOR_FRAME_RIGHT + \ + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT + \ + EMBEDDEDEDITOR_FRAME_INNER_SPACE_RIGHT) + +#define EMBEDDEDEDITOR_BORDER_BOTTOM (EMBEDDEDEDITOR_FRAME_BOTTOM + \ + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM + \ + EMBEDDEDEDITOR_FRAME_INNER_SPACE_BOTTOM) + + +typedef struct EmbeddedEditor +{ + WNDPROC originalProc; + COLORREF textColor; + COLORREF backColor; + COLORREF borderColor; + HBRUSH backBrush; + HBRUSH borderBrush; + EmbeddedEditorFinishCb callback; + void *user; + POINT anchorPoint; + SIZE maximumSize; + wchar_t *buffer; + size_t bufferSize; + long spacing; + long lineHeight; +} EmbeddedEditor; + +typedef std::vector<HWND> WindowList; + +typedef struct EmbeddedEditorParent +{ + WNDPROC originalProc; + WindowList editorList; +} EmbeddedEditorParent; + + +typedef struct EmbeddedEditorThread +{ + HHOOK hook; + HWND window; +} EmbeddedEditorThread; + +static size_t editorTls = ((size_t)-1); + +static ATOM EMBEDDEDEDITOR_PROP = 0; + +#define EMBEDDEDEDITOR(_hwnd) ((EmbeddedEditor*)GetPropW((_hwnd), MAKEINTATOM(EMBEDDEDEDITOR_PROP))) +#define EMBEDDEDEDITOR_RET_VOID(_self, _hwnd) { (_self) = EMBEDDEDEDITOR((_hwnd)); if (NULL == (_self)) return; } +#define EMBEDDEDEDITOR_RET_VAL(_self, _hwnd, _error) { (_self) = EMBEDDEDEDITOR((_hwnd)); if (NULL == (_self)) return (_error); } + +#define EMBEDDEDEDITOR_PARENT(_hwnd) ((EmbeddedEditorParent*)GetPropW((_hwnd), MAKEINTATOM(EMBEDDEDEDITOR_PROP))) + +static LRESULT WINAPI +EmbeddedEditor_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +static LRESULT WINAPI +EmbeddedEditorParent_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +static LRESULT CALLBACK +EmbeddidEditorThread_MouseProc(int code, unsigned int messageId, MOUSEHOOKSTRUCT *mouse); + +static BOOL +EmbeddidEditorThread_BeginMouseMonitor(HWND editorWindow) +{ + EmbeddedEditorThread *threadData; + + if (NULL == WASABI_API_APP || + NULL == editorWindow) + { + return FALSE; + } + + if ((size_t)-1 == editorTls) + { + editorTls = WASABI_API_APP->AllocateThreadStorage(); + if ((size_t)-1 == editorTls) + return FALSE; + + threadData = NULL; + } + else + { + threadData = (EmbeddedEditorThread*)WASABI_API_APP->GetThreadStorage(editorTls); + WASABI_API_APP->SetThreadStorage(editorTls, NULL); + } + + if (NULL != threadData) + { + if (NULL != threadData->window) + DestroyWindow(threadData->window); + } + else + { + threadData = (EmbeddedEditorThread*)malloc(sizeof(EmbeddedEditorThread)); + if (NULL == threadData) + return FALSE; + + threadData->hook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)EmbeddidEditorThread_MouseProc, NULL, GetCurrentThreadId()); + if (NULL == threadData->hook) + { + free(threadData); + return FALSE; + } + + } + + threadData->window = editorWindow; + WASABI_API_APP->SetThreadStorage(editorTls, threadData); + + return TRUE; +} + +static void +EmbeddidEditorThread_EndMouseMonitor(HWND editorWindow) +{ + EmbeddedEditorThread *threadData; + + if (NULL == WASABI_API_APP || + (size_t)-1 == editorTls || + NULL == editorWindow) + { + return; + } + + threadData = (EmbeddedEditorThread*)WASABI_API_APP->GetThreadStorage(editorTls); + WASABI_API_APP->SetThreadStorage(editorTls, NULL); + + if (NULL != threadData) + { + if (NULL != threadData->hook) + UnhookWindowsHookEx(threadData->hook); + + free(threadData); + } +} + +static BOOL +EmbeddedEditorParent_AddChild(HWND hwnd, HWND child) +{ + EmbeddedEditorParent *self; + + if (NULL == hwnd || NULL == child) + return FALSE; + + self = EMBEDDEDEDITOR_PARENT(hwnd); + + if (NULL == self) + { + self = new EmbeddedEditorParent(); + if (NULL == self) + return FALSE; + + self->originalProc = (WNDPROC)(LONG_PTR)SetWindowLongPtr(hwnd, GWLP_WNDPROC, + (LONGX86)(LONG_PTR)EmbeddedEditorParent_WindowProc); + + if (NULL != self->originalProc && + FALSE == SetProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP), self)) + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc); + delete self; + return FALSE; + } + } + else + { + size_t index; + index = self->editorList.size(); + while(index--) + { + if (self->editorList[index] == child) + return FALSE; + } + } + + self->editorList.push_back(child); + return TRUE; +} + +static BOOL +EmbeddedEditorParent_RemoveChild(HWND hwnd, HWND child) +{ + size_t index; + EmbeddedEditorParent *self; + + if (NULL == hwnd || NULL == child) + return FALSE; + + self = EMBEDDEDEDITOR_PARENT(hwnd); + if (NULL == self) + return FALSE; + + index = self->editorList.size(); + while(index--) + { + if (self->editorList[index] == child) + break; + } + + if (((size_t)-1) == index) + return FALSE; + + self->editorList.erase(self->editorList.begin() + index); + if (0 == self->editorList.size()) + { + RemoveProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP)); + + if (NULL != self->originalProc) + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc); + + delete self; + } + + return TRUE; + +} + +BOOL +EmbeddedEditor_Attach(HWND hwnd, EmbeddedEditorFinishCb callback, void *user) +{ + HWND parent; + EmbeddedEditor *self; + + if (NULL == hwnd) + return FALSE; + + if (0 == EMBEDDEDEDITOR_PROP) + { + EMBEDDEDEDITOR_PROP = GlobalAddAtom(TEXT("EmdeddedEditorProp")); + if (0 == EMBEDDEDEDITOR_PROP) + return FALSE; + } + + self = (EmbeddedEditor*)malloc(sizeof(EmbeddedEditor)); + if (NULL == self) + return FALSE; + + memset(self, 0, sizeof(EmbeddedEditor)); + + self->originalProc = (WNDPROC)(LONG_PTR)SetWindowLongPtr(hwnd, GWLP_WNDPROC, + (LONGX86)(LONG_PTR)EmbeddedEditor_WindowProc); + + if (NULL != self->originalProc && + FALSE == SetProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP), self)) + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc); + free(self); + return FALSE; + } + + self->callback = callback; + self->user = user; + + self->backColor = RGB(0, 0, 255); //GetSysColor(COLOR_WINDOW); + self->backBrush = NULL; + self->textColor = RGB(255, 255, 0); //GetSysColor(COLOR_WINDOWTEXT); + self->borderColor = RGB(255, 0, 0); //GetSysColor(COLOR_WINDOWFRAME); + self->borderBrush = NULL; + self->spacing = -1; + self->lineHeight = -1; + + parent = GetAncestor(hwnd, GA_PARENT); + if(NULL != parent) + EmbeddedEditorParent_AddChild(parent, hwnd); + + EmbeddidEditorThread_BeginMouseMonitor(hwnd); + + RECT rect, formatRect; + GetWindowRect(hwnd, &rect); + SetRect(&formatRect, 0, 0, RECTWIDTH(rect) - 6, RECTHEIGHT(rect) - 6); + SendMessage(hwnd, EM_SETRECTNP, 0, (LPARAM)&formatRect); + + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED); + + + return TRUE; +} + +BOOL +EmbeddedEditor_AdjustWindowRectEx(RECT *rect, unsigned long styleEx, unsigned long style) +{ + if (NULL == rect) + return FALSE; + + if (0 != ((WS_EX_STATICEDGE | WS_EX_CLIENTEDGE) & styleEx) || + 0 != (WS_BORDER & style)) + { + rect->left -= (EMBEDDEDEDITOR_BORDER_LEFT + EMBEDDEDEDITOR_MARGIN_LEFT); + rect->top -= (EMBEDDEDEDITOR_BORDER_TOP + EMBEDDEDEDITOR_MARGIN_TOP); + rect->right += (EMBEDDEDEDITOR_BORDER_RIGHT + EMBEDDEDEDITOR_MARGIN_RIGHT); + rect->bottom += (EMBEDDEDEDITOR_BORDER_BOTTOM + EMBEDDEDEDITOR_MARGIN_BOTTOM); + } + + return TRUE; +} + +static HBRUSH +EmbeddedEditor_GetBackBrush(EmbeddedEditor *self) +{ + if (NULL == self->backBrush) + self->backBrush = CreateSolidBrush(self->backColor); + + return self->backBrush; +} + +static HBRUSH +EmbeddedEditor_GetBorderBrush(EmbeddedEditor *self) +{ + if (NULL == self->borderBrush) + self->borderBrush = CreateSolidBrush(self->borderColor); + + return self->borderBrush; +} + +static BOOL +EmbdeddedEditor_GetBorderEnabled(HWND hwnd) +{ + unsigned long style; + + style = GetWindowStyleEx(hwnd); + if (0 != ((WS_EX_STATICEDGE | WS_EX_CLIENTEDGE) & style)) + return TRUE; + + style = GetWindowStyle(hwnd); + if (0 != (WS_BORDER & style)) + return TRUE; + + return FALSE; +} + + +static BOOL +EmbdeddedEditor_DrawBorder(EmbeddedEditor *self, HWND hwnd, HDC hdc) +{ + RECT windowRect, clientRect; + HRGN borderRgn; + POINT polygons[16] = {0}; + const int polygonsPointCount[] = {4, 4, 4, 4}; + + if (FALSE == GetWindowRect(hwnd, &windowRect) || + FALSE == GetClientRect(hwnd, &clientRect)) + { + return FALSE; + } + + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&clientRect, 2); + + OffsetRect(&clientRect, -windowRect.left, -windowRect.top); + OffsetRect(&windowRect, -windowRect.left, -windowRect.top); + + MakeRectPolygon(&polygons[0], windowRect.left, windowRect.top, windowRect.right, clientRect.top); + MakeRectPolygon(&polygons[4], clientRect.right, clientRect.top, windowRect.right, clientRect.bottom); + MakeRectPolygon(&polygons[8], windowRect.left, clientRect.bottom, windowRect.right, windowRect.bottom); + MakeRectPolygon(&polygons[12], windowRect.left, clientRect.top, clientRect.left, clientRect.bottom); + + borderRgn = CreatePolyPolygonRgn(polygons, + polygonsPointCount, + ARRAYSIZE(polygonsPointCount), + WINDING); + + if (NULL != borderRgn) + { + MakeRectPolygon(&polygons[0], + windowRect.left + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT, + windowRect.top + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP, + windowRect.right - EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT, + windowRect.top + (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP + EMBEDDEDEDITOR_FRAME_TOP)); + MakeRectPolygon(&polygons[4], + windowRect.right - (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT + EMBEDDEDEDITOR_FRAME_RIGHT), + windowRect.top + (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP + EMBEDDEDEDITOR_FRAME_TOP), + windowRect.right - EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT, + windowRect.bottom - (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM + EMBEDDEDEDITOR_FRAME_BOTTOM)); + MakeRectPolygon(&polygons[8], + windowRect.left + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT, + windowRect.bottom - (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM + EMBEDDEDEDITOR_FRAME_BOTTOM), + windowRect.right - EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT, + windowRect.bottom - EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM); + MakeRectPolygon(&polygons[12], + windowRect.left + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT, + windowRect.top + (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP + EMBEDDEDEDITOR_FRAME_TOP), + windowRect.left + (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT + EMBEDDEDEDITOR_FRAME_LEFT), + windowRect.bottom - (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM + EMBEDDEDEDITOR_FRAME_BOTTOM)); + + HRGN frameRgn = CreatePolyPolygonRgn(polygons, + polygonsPointCount, + ARRAYSIZE(polygonsPointCount), + WINDING); + if (NULL != frameRgn) + { + int combineResult = CombineRgn(frameRgn, borderRgn, frameRgn, RGN_AND); + if (NULLREGION != combineResult && ERROR != combineResult) + { + FillRgn(hdc, frameRgn, EmbeddedEditor_GetBorderBrush(self)); + combineResult = CombineRgn(borderRgn, borderRgn, frameRgn, RGN_DIFF); + } + else + combineResult = COMPLEXREGION; + + if (NULLREGION != combineResult && ERROR != combineResult) + FillRgn(hdc, borderRgn, EmbeddedEditor_GetBackBrush(self)); + + DeleteObject(frameRgn); + } + DeleteObject(borderRgn); + } + + + + return TRUE; +} + +static BOOL +EmbeddedEditor_InvokeCallback(EmbeddedEditor *self, HWND hwnd, BOOL cancelMode, BOOL removeCallback) +{ + wchar_t *text; + unsigned int length; + EmbeddedEditorFinishCb callback; + + if (NULL == self || NULL == self->callback) + return FALSE; + + + length = (unsigned int)SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0L); + text = String_Malloc(++length); + if (NULL == text) + return FALSE; + + SendMessage(hwnd, WM_GETTEXT, (WPARAM)length, (LPARAM)text); + + callback = self->callback; + if (FALSE != removeCallback) + self->callback = NULL; + + callback(hwnd, cancelMode, text, self->user); + + return TRUE; +} + + +static const wchar_t * +EmbeddedEditor_GetWindowText(EmbeddedEditor *self, HWND hwnd, size_t *lengthOut) +{ + size_t length; + + length = (size_t)SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0L); + if (length >= self->bufferSize) + { + size_t size; + + size = (((length + 1)/128) + 1) * 128; + + String_Free(self->buffer); + self->buffer = String_Malloc(size); + if (NULL == self->buffer) + return FALSE; + + self->bufferSize = size; + } + + if (0 != length) + length = SendMessage(hwnd, WM_GETTEXT, (WPARAM)self->bufferSize, (LPARAM)self->buffer); + else + self->buffer[0] = L'\0'; + + if (NULL != lengthOut) + *lengthOut = length; + + return self->buffer; +} + +static BOOL +EmbeddedEditor_Resize(EmbeddedEditor *self, HWND hwnd, BOOL redraw) +{ + size_t textLength; + const wchar_t *text; + HDC hdc; + SIZE maximumSize; + BOOL borderEnabled; + HWND parentWindow; + RECT rect, parentRect, marginRect; + SIZE parentSize; + BOOL result; + unsigned int windowStyle; + HFONT font, prevFont; + + parentWindow = GetAncestor(hwnd, GA_PARENT); + if (NULL == parentWindow || + FALSE == GetClientRect(parentWindow, &parentRect)) + { + return FALSE; + } + + parentSize.cx = RECTWIDTH(parentRect); + parentSize.cy = RECTHEIGHT(parentRect); + + windowStyle = GetWindowStyle(hwnd); + borderEnabled = EmbdeddedEditor_GetBorderEnabled(hwnd); + + text = EmbeddedEditor_GetWindowText(self, hwnd, &textLength); + if (NULL == text) + return FALSE; + + hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL == hdc) + return FALSE; + + result = FALSE; + + font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L); + prevFont = SelectFont(hdc, font); + + if (-1 == self->spacing || + -1 == self->lineHeight) + { + TEXTMETRIC textMetrics; + if (FALSE != GetTextMetrics(hdc, &textMetrics)) + { + self->spacing = textMetrics.tmAveCharWidth; + self->lineHeight = textMetrics.tmHeight; + } + } + + if (0 == (ES_MULTILINE & windowStyle)) + { + unsigned long margins; + margins = (unsigned long)SendMessage(hwnd, EM_GETMARGINS, 0, 0L); + SetRect(&marginRect, (short)LOWORD(margins), 0, (short)HIWORD(margins), 0); + } + else + { + RECT clientRect; + if (FALSE == GetClientRect(hwnd, &clientRect)) + SetRectEmpty(&clientRect); + + if (clientRect.right < clientRect.left) + clientRect.right = clientRect.left; + if (clientRect.bottom < (clientRect.top + self->lineHeight)) + clientRect.bottom = clientRect.top + self->lineHeight; + + SendMessage(hwnd, EM_GETRECT, 0, (LPARAM)&marginRect); + + marginRect.left = (marginRect.left > clientRect.left) ? + (marginRect.left - clientRect.left) : 1; + + marginRect.top = (marginRect.top > clientRect.top) ? + (marginRect.top - clientRect.top) : 1; + + marginRect.right = (marginRect.right < clientRect.right) ? + (clientRect.right - marginRect.right) : 1; + + marginRect.bottom = (marginRect.bottom < clientRect.bottom) ? + (clientRect.bottom - marginRect.bottom) : 1; + + if (SendMessage(hwnd, EM_GETLINECOUNT, 0, 0L) > 1) + { + if (marginRect.bottom >= (self->lineHeight - 1)) + marginRect.bottom -= (self->lineHeight - 2); + } + + SetRect(&marginRect, EMBEDDEDEDITOR_MARGIN_LEFT, EMBEDDEDEDITOR_MARGIN_TOP, + EMBEDDEDEDITOR_MARGIN_RIGHT, EMBEDDEDEDITOR_MARGIN_BOTTOM); + } + + maximumSize.cx = (self->maximumSize.cx > 0) ? MIN(parentSize.cx, self->maximumSize.cx) : parentSize.cx; + maximumSize.cx -= (marginRect.left + marginRect.right); + if (FALSE != borderEnabled) + maximumSize.cx -= (EMBEDDEDEDITOR_BORDER_LEFT + EMBEDDEDEDITOR_BORDER_RIGHT); + + if (maximumSize.cx < 0) + maximumSize.cx = 0; + + maximumSize.cy = (self->maximumSize.cy > 0) ? MIN(parentSize.cy, self->maximumSize.cy) : parentSize.cy; + maximumSize.cy -= (marginRect.top + marginRect.bottom); + if (FALSE != borderEnabled) + maximumSize.cy -= (EMBEDDEDEDITOR_BORDER_TOP + EMBEDDEDEDITOR_BORDER_BOTTOM); + + if (maximumSize.cy < 0) + maximumSize.cy = 0; + + if (0 == textLength) + { + SetRectEmpty(&rect); + result = TRUE; + } + else + { + SetRect(&rect, 0, 0, maximumSize.cx, maximumSize.cy); + result = DrawText(hdc, text, (int)textLength, &rect, + DT_CALCRECT | DT_LEFT | DT_TOP | DT_WORDBREAK | + DT_EDITCONTROL | DT_NOPREFIX); + } + + SelectFont(hdc, prevFont); + ReleaseDC(hwnd, hdc); + + + if (FALSE != result) + { + unsigned int flags; + + rect.right += 2 * self->spacing; + + if (RECTHEIGHT(rect) < self->lineHeight) + rect.bottom = rect.top + self->lineHeight; + else if (RECTHEIGHT(rect) > self->lineHeight) + rect.right = rect.left + maximumSize.cx; + + rect.right += (marginRect.left + marginRect.right); + rect.bottom += (marginRect.top + marginRect.bottom); + + if (FALSE != borderEnabled) + { + rect.right += (EMBEDDEDEDITOR_BORDER_LEFT + EMBEDDEDEDITOR_BORDER_RIGHT); + rect.bottom += (EMBEDDEDEDITOR_BORDER_TOP + EMBEDDEDEDITOR_BORDER_BOTTOM); + } + + flags = SWP_NOACTIVATE | SWP_NOZORDER; + if (FALSE == redraw) + flags |= SWP_NOREDRAW; + + OffsetRect(&rect, self->anchorPoint.x, self->anchorPoint.y); + + long offsetX = 0, offsetY = 0; + + if (0 != (ES_CENTER & windowStyle)) + { + if (self->maximumSize.cx > 0) + { + offsetX += (self->maximumSize.cx - RECTWIDTH(rect))/2; + } + } + + if (rect.right > parentSize.cx) + offsetX -= (rect.right - parentSize.cx); + + if (rect.bottom > parentSize.cy) + offsetY -= (rect.bottom - parentSize.cy); + + OffsetRect(&rect, offsetX, offsetY); + + result = SetWindowPos(hwnd, NULL, rect.left, rect.top, RECTWIDTH(rect), RECTHEIGHT(rect), flags); + } + + return result; +} + +static void +EmbdeddedEditor_OnDestroy(EmbeddedEditor *self, HWND hwnd) +{ + HWND parent; + + if (NULL != self && + NULL != self->callback) + { + EmbeddedEditor_InvokeCallback(self, hwnd, TRUE, TRUE); + } + + RemoveProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP)); + if (NULL == self) + return; + + if (NULL != self->originalProc) + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc); + CallWindowProc(self->originalProc, hwnd, WM_DESTROY, 0, 0L); + } + + if(NULL != self->backBrush) + DeleteObject(self->backBrush); + + if (NULL != self->borderBrush) + DeleteObject(self->borderBrush); + + String_Free(self->buffer); + + free(self); + + parent = GetAncestor(hwnd, GA_PARENT); + if(NULL != parent) + EmbeddedEditorParent_RemoveChild(parent, hwnd); + + EmbeddidEditorThread_EndMouseMonitor(hwnd); +} + +static BOOL +EmbdeddedEditor_OnNcCalcSize(EmbeddedEditor *self, HWND hwnd, BOOL calcValidRects, + NCCALCSIZE_PARAMS *params, LRESULT *result) +{ + + if (FALSE != EmbdeddedEditor_GetBorderEnabled(hwnd)) + { + if (FALSE != calcValidRects) + { + SetRect(¶ms->rgrc[0], + params->lppos->x + EMBEDDEDEDITOR_BORDER_LEFT, + params->lppos->y + EMBEDDEDEDITOR_BORDER_TOP, + params->lppos->x + params->lppos->cx - EMBEDDEDEDITOR_BORDER_RIGHT, + params->lppos->y + params->lppos->cy - EMBEDDEDEDITOR_BORDER_BOTTOM); + + *result = WVR_ALIGNTOP | WVR_ALIGNLEFT; + } + else + { + if (FALSE != GetWindowRect(hwnd, ¶ms->rgrc[0])) + { + params->rgrc[0].left += EMBEDDEDEDITOR_BORDER_LEFT; + params->rgrc[0].top += EMBEDDEDEDITOR_BORDER_TOP; + params->rgrc[0].right -= EMBEDDEDEDITOR_BORDER_RIGHT; + params->rgrc[0].bottom -= EMBEDDEDEDITOR_BORDER_BOTTOM; + } + } + } + + return TRUE; +} +static BOOL +EmbdeddedEditor_OnNcPaint(EmbeddedEditor *self, HWND hwnd, HRGN updateRegion) +{ + BOOL result; + + if (FALSE != EmbdeddedEditor_GetBorderEnabled(hwnd)) + { + HDC hdc; + unsigned int flags; + + flags = DCX_PARENTCLIP | DCX_CACHE | DCX_WINDOW | DCX_CLIPSIBLINGS | + DCX_INTERSECTUPDATE | DCX_VALIDATE; + + hdc = GetDCEx(hwnd, ((HRGN)NULLREGION != updateRegion) ? updateRegion : NULL, flags); + if (NULL != hdc) + { + result = EmbdeddedEditor_DrawBorder(self, hwnd, hdc); + ReleaseDC(hwnd, hdc); + } + else + result = FALSE; + } + else + result = TRUE; + + return result; +} + +static BOOL +EmbeddedEditor_OnSetCursor(EmbeddedEditor *self, HWND hwnd, HWND cursorWindow, int hitTest, int mouseMessage, LRESULT *result) +{ + HCURSOR cursor; + + if (cursorWindow != hwnd || + HTCLIENT != hitTest) + { + return FALSE; + } + + cursor = LoadCursor(NULL, IDC_IBEAM); + if (NULL == cursor) + return FALSE; + + SetCursor(cursor); + + *result = TRUE; + return TRUE; +} + + +static BOOL +EmbeddedEditor_OnGetDlgCode(EmbeddedEditor *self, HWND hwnd, unsigned int vKey, MSG *message, LRESULT *result) +{ + if (NULL != message) + { + switch(vKey) + { + case VK_TAB: + case VK_ESCAPE: + EmbeddedEditor_InvokeCallback(self, hwnd, TRUE, TRUE); + DestroyWindow(hwnd); + *result = DLGC_WANTMESSAGE; + return TRUE; + case VK_RETURN: + EmbeddedEditor_InvokeCallback(self, hwnd, FALSE, TRUE); + DestroyWindow(hwnd); + *result = DLGC_WANTMESSAGE; + return TRUE; + } + } + + if (NULL != self->originalProc) + *result = CallWindowProc(self->originalProc, hwnd, WM_GETDLGCODE, (WPARAM)vKey, (LPARAM)message); + else + *result = 0; + + if (NULL == message) + *result |= DLGC_WANTMESSAGE; + + + return TRUE; +} + +static BOOL +EmbeddedEditor_OnKillFocus(EmbeddedEditor *self, HWND hwnd, HWND focusedWindow) +{ + EmbeddedEditor_InvokeCallback(self, hwnd, FALSE, TRUE); + DestroyWindow(hwnd); + return TRUE; +} + +static BOOL +EmbeddedEditor_OnMouseWheel(EmbeddedEditor *self, HWND hwnd, int virtualKeys, int distance, long pointer_s) +{ + HWND parentWindow; + + parentWindow = GetAncestor(hwnd, GA_PARENT); + + EmbeddedEditor_InvokeCallback(self, hwnd, TRUE, TRUE); + DestroyWindow(hwnd); + + if (NULL != parentWindow) + { + SendMessage(parentWindow, WM_MOUSEWHEEL, + MAKEWPARAM(virtualKeys, distance), (LPARAM)pointer_s); + } + + return TRUE; +} + +static void +EmbeddedEditor_OnCommand(EmbeddedEditor *self, HWND hwnd, int eventId) +{ + switch(eventId) + { + case EN_UPDATE: + EmbeddedEditor_Resize(self, hwnd, TRUE); + break; + } +} +static BOOL +EmbeddedEditor_OnSetFont(EmbeddedEditor *self, HWND hwnd, HFONT font, BOOL redraw) +{ + if (NULL != self->originalProc) + CallWindowProc(self->originalProc, hwnd, WM_SETFONT, (WPARAM)font, MAKELPARAM(redraw, 0)); + + self->spacing = -1; + self->lineHeight = -1; + + EmbeddedEditor_Resize(self, hwnd, redraw); + return TRUE; +} + + +static BOOL +EmbeddedEditor_OnWindowPosChanged(EmbeddedEditor *self, HWND hwnd, WINDOWPOS *pwp) +{ + if (SWP_NOSIZE != ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) + { + RECT formatRect; + if (FALSE != GetClientRect(hwnd, &formatRect)) + { + formatRect.left += EMBEDDEDEDITOR_MARGIN_LEFT; + formatRect.top += EMBEDDEDEDITOR_MARGIN_TOP; + formatRect.right -= EMBEDDEDEDITOR_MARGIN_RIGHT; + formatRect.bottom -= EMBEDDEDEDITOR_MARGIN_BOTTOM; + + if (formatRect.right < formatRect.left) + formatRect.right = formatRect.left; + + if (formatRect.bottom < formatRect.top) + formatRect.bottom = formatRect.top; + + SendMessage(hwnd, EM_SETRECTNP, 0, (LPARAM)&formatRect); + } + } + + if (NULL != self->originalProc) + CallWindowProc(self->originalProc, hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)pwp); + + return TRUE; +} + +static void +EmbeddedEditor_OnSetTextColor(EmbeddedEditor *self, HWND hwnd, COLORREF color, LRESULT *result) +{ + *result = (LRESULT)self->textColor; + if (self->textColor != color) + self->textColor = color; +} + +static void +EmbeddedEditor_OnGetTextColor(EmbeddedEditor *self, HWND hwnd, LRESULT *result) +{ + *result = (LRESULT)self->textColor; +} + +static void +EmbeddedEditor_OnSetBackColor(EmbeddedEditor *self, HWND hwnd, COLORREF color, LRESULT *result) +{ + *result = (LRESULT)self->backColor; + if (self->backColor != color) + { + self->backColor = color; + if (NULL != self->backBrush) + { + DeleteObject(self->backBrush); + self->backBrush = NULL; + } + } +} + +static void +EmbeddedEditor_OnGetBackColor(EmbeddedEditor *self, HWND hwnd, LRESULT *result) +{ + *result = (LRESULT)self->backColor; +} + +static void +EmbeddedEditor_OnSetBorderColor(EmbeddedEditor *self, HWND hwnd, COLORREF color, LRESULT *result) +{ + *result = (LRESULT)self->borderColor; + if (self->borderColor != color) + { + self->borderColor = color; + if (NULL != self->borderBrush) + { + DeleteObject(self->borderBrush); + self->borderBrush = NULL; + } + } +} + +static void +EmbeddedEditor_OnGetBorderColor(EmbeddedEditor *self, HWND hwnd, LRESULT *result) +{ + *result = (LRESULT)self->borderColor; +} + +static void +EmbeddedEditor_OnSetUserData(EmbeddedEditor *self, HWND hwnd, void *user, LRESULT *result) +{ + *result = (LRESULT)self->user; + self->user = user; +} + +static void +EmbeddedEditor_OnGetUserData(EmbeddedEditor *self, HWND hwnd, LRESULT *result) +{ + *result = (LRESULT)self->user; +} + +static void +EmbeddedEditor_OnSetAnchorPoint(EmbeddedEditor *self, HWND hwnd, long x, long y, LRESULT *result) +{ + self->anchorPoint.x = x; + self->anchorPoint.y = y; + *result = TRUE; +} + +static void +EmbeddedEditor_OnGetAnchorPoint(EmbeddedEditor *self, HWND hwnd, long *x, long *y, LRESULT *result) +{ + if (NULL != x) + *x = self->anchorPoint.x; + + if (NULL != y) + *y = self->anchorPoint.y; + + *result = TRUE; +} + +static void +EmbeddedEditor_OnSetMaxSize(EmbeddedEditor *self, HWND hwnd, long width, long height, LRESULT *result) +{ + self->maximumSize.cx = width; + self->maximumSize.cy = height; + *result = TRUE; +} + +static void +EmbeddedEditor_OnGetMaxSize(EmbeddedEditor *self, HWND hwnd, long *width, long *height, LRESULT *result) +{ + if (NULL != width) + *width = self->maximumSize.cx; + + if (NULL != height) + *height = self->maximumSize.cy; + + *result = TRUE; +} + +static void +EmbeddedEditor_OnEndEditing(EmbeddedEditor *self, HWND hwnd, BOOL cancel) +{ + EmbeddedEditor_InvokeCallback(self, hwnd, cancel, TRUE); + DestroyWindow(hwnd); +} + +static BOOL +EmbeddedEditor_MessageProc(EmbeddedEditor *self, HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam, LRESULT *result) +{ + + switch(uMsg) + { + case WM_DESTROY: + EmbdeddedEditor_OnDestroy(self, hwnd); + return TRUE; + case WM_NCCALCSIZE: + return EmbdeddedEditor_OnNcCalcSize(self, hwnd, (BOOL)wParam, (NCCALCSIZE_PARAMS*)lParam, result); + case WM_NCPAINT: + return EmbdeddedEditor_OnNcPaint(self, hwnd, (HRGN)wParam); + case WM_SETCURSOR: + return EmbeddedEditor_OnSetCursor(self, hwnd, (HWND)wParam, + LOWORD(lParam), HIWORD(lParam), result); + case WM_GETDLGCODE: + return EmbeddedEditor_OnGetDlgCode(self, hwnd, (unsigned int)wParam, (MSG*)lParam, result); + case WM_KILLFOCUS: + EmbeddedEditor_OnKillFocus(self, hwnd, (HWND)wParam); + return 0; + case WM_MOUSEWHEEL: + return EmbeddedEditor_OnMouseWheel(self, hwnd, LOWORD(wParam), (short)HIWORD(wParam), (LONG)lParam); + case WM_SETFONT: + return EmbeddedEditor_OnSetFont(self, hwnd, (HFONT)wParam, (BOOL)LOWORD(lParam)); + case WM_WINDOWPOSCHANGED: + return EmbeddedEditor_OnWindowPosChanged(self, hwnd, (WINDOWPOS*)lParam); + + case EMBEDDEDEDITOR_WM_SET_TEXT_COLOR: EmbeddedEditor_OnSetTextColor(self, hwnd, (COLORREF)lParam, result); return TRUE; + case EMBEDDEDEDITOR_WM_GET_TEXT_COLOR: EmbeddedEditor_OnGetTextColor(self, hwnd, result); return TRUE; + case EMBEDDEDEDITOR_WM_SET_BACK_COLOR: EmbeddedEditor_OnSetBackColor(self, hwnd, (COLORREF)lParam, result); return TRUE; + case EMBEDDEDEDITOR_WM_GET_BACK_COLOR: EmbeddedEditor_OnGetBackColor(self, hwnd, result); return TRUE; + case EMBEDDEDEDITOR_WM_SET_BORDER_COLOR: EmbeddedEditor_OnSetBorderColor(self, hwnd, (COLORREF)lParam, result); return TRUE; + case EMBEDDEDEDITOR_WM_GET_BORDER_COLOR: EmbeddedEditor_OnGetBorderColor(self, hwnd, result); return TRUE; + case EMBEDDEDEDITOR_WM_SET_USER_DATA: EmbeddedEditor_OnSetUserData(self, hwnd, (void*)lParam, result); return TRUE; + case EMBEDDEDEDITOR_WM_GET_USER_DATA: EmbeddedEditor_OnGetUserData(self, hwnd, result); return TRUE; + case EMBEDDEDEDITOR_WM_SET_ANCHOR_POINT: EmbeddedEditor_OnSetAnchorPoint(self, hwnd, (long)wParam, (long)lParam, result); return TRUE; + case EMBEDDEDEDITOR_WM_GET_ANCHOR_POINT: EmbeddedEditor_OnGetAnchorPoint(self, hwnd, (long*)wParam, (long*)lParam, result); return TRUE; + case EMBEDDEDEDITOR_WM_SET_MAX_SIZE: EmbeddedEditor_OnSetMaxSize(self, hwnd, (long)wParam, (long)lParam, result); return TRUE; + case EMBEDDEDEDITOR_WM_GET_MAX_SIZE: EmbeddedEditor_OnGetMaxSize(self, hwnd, (long*)wParam, (long*)lParam, result); return TRUE; + case EMBEDDEDEDITOR_WM_END_EDITING: EmbeddedEditor_OnEndEditing(self, hwnd, (BOOL)wParam); return TRUE; + + } + return FALSE; +} + +static LRESULT WINAPI +EmbeddedEditor_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + EmbeddedEditor *self; + LRESULT result; + + self = EMBEDDEDEDITOR(hwnd); + + if (NULL == self || + NULL == self->originalProc) + { + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + result = 0; + if (FALSE != EmbeddedEditor_MessageProc(self, hwnd, uMsg, wParam, lParam, &result)) + { + return result; + } + + return CallWindowProc(self->originalProc, hwnd, uMsg, wParam, lParam); +} + + +static void +EmbdeddedEditorParent_OnDestroy(EmbeddedEditorParent *self, HWND hwnd) +{ + RemoveProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP)); + if (NULL == self) + return; + + if (NULL != self->originalProc) + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc); + CallWindowProc(self->originalProc, hwnd, WM_DESTROY, 0, 0L); + } + + delete self; +} + +static BOOL +EmbdeddedEditorParent_OnGetEditColors(EmbeddedEditorParent *self, HWND hwnd, HDC hdc, HWND control, LRESULT *result) +{ + size_t index; + index = self->editorList.size(); + while(index--) + { + if (control == self->editorList[index]) + { + EmbeddedEditor *editor = EMBEDDEDEDITOR(control); + if (NULL != editor) + { + SetTextColor(hdc, editor->textColor); + SetBkColor(hdc, editor->backColor); + *result = (LRESULT)EmbeddedEditor_GetBackBrush(editor); + return TRUE; + } + } + } + + return FALSE; +} + +static void +EmbdeddedEditorParent_OnCommand(EmbeddedEditorParent *self, HWND hwnd, int commandId, int eventId, HWND control) +{ + size_t index; + + if (NULL == control) + return; + + index = self->editorList.size(); + while(index--) + { + if (control == self->editorList[index]) + { + EmbeddedEditor *editor = EMBEDDEDEDITOR(control); + if (NULL != editor) + { + EmbeddedEditor_OnCommand(editor, control, eventId); + } + } + } + +} + +static BOOL +EmbeddedEditorParent_MessageProc(EmbeddedEditorParent *self, HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam, LRESULT *result) +{ + + switch(uMsg) + { + case WM_DESTROY: + EmbdeddedEditorParent_OnDestroy(self, hwnd); + return TRUE; + + case WM_CTLCOLOREDIT: + return EmbdeddedEditorParent_OnGetEditColors(self, hwnd, (HDC)wParam, (HWND)lParam, result); + + case WM_COMMAND: + EmbdeddedEditorParent_OnCommand(self, hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); + break; + } + return FALSE; +} + +static LRESULT WINAPI +EmbeddedEditorParent_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + EmbeddedEditorParent *self; + LRESULT result; + + self = EMBEDDEDEDITOR_PARENT(hwnd); + + if (NULL == self || + NULL == self->originalProc) + { + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + result = 0; + if (FALSE != EmbeddedEditorParent_MessageProc(self, hwnd, uMsg, wParam, lParam, &result)) + { + return result; + } + + return CallWindowProc(self->originalProc, hwnd, uMsg, wParam, lParam); +} + + +static LRESULT CALLBACK +EmbeddidEditorThread_MouseProc(int code, unsigned int messageId, MOUSEHOOKSTRUCT *mouse) +{ + if ((size_t)-1 != editorTls) + { + EmbeddedEditorThread *threadData; + threadData = (EmbeddedEditorThread*)WASABI_API_APP->GetThreadStorage(editorTls); + + if (NULL != threadData) + { + LRESULT result; + if (NULL != threadData->hook) + result = CallNextHookEx(threadData->hook, code, (WPARAM)messageId, (LPARAM)mouse); + else + result = 0; + + if (code >= 0) + { + if ((messageId >= WM_LBUTTONDOWN && messageId <= 0x20E && mouse->hwnd != threadData->window) || + (messageId >= WM_NCLBUTTONDOWN && messageId <= 0x00AD)) + { + + HWND editorWindow; + editorWindow = threadData->window; + if (NULL != editorWindow) + PostMessage(editorWindow, EMBEDDEDEDITOR_WM_END_EDITING, FALSE, 0L); + } + } + + return result; + } + } + + return 0; +} diff --git a/Src/Plugins/Library/ml_devices/embeddedEditor.h b/Src/Plugins/Library/ml_devices/embeddedEditor.h new file mode 100644 index 00000000..939348ed --- /dev/null +++ b/Src/Plugins/Library/ml_devices/embeddedEditor.h @@ -0,0 +1,79 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_EMBEDED_EDITOR_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_EMBEDED_EDITOR_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + + +typedef void (CALLBACK *EmbeddedEditorFinishCb)(HWND /*editorWindow*/, BOOL /*canceled*/, + const wchar_t * /*text*/, void * /*user*/); + + +BOOL +EmbeddedEditor_Attach(HWND hwnd, + EmbeddedEditorFinishCb callback, + void *user); + +BOOL +EmbeddedEditor_AdjustWindowRectEx(RECT *rect, + unsigned long styleEx, + unsigned long style); + + +#define EMBEDDEDEDITOR_WM_FIRST (WM_USER + 10) + +#define EMBEDDEDEDITOR_WM_SET_TEXT_COLOR (EMBEDDEDEDITOR_WM_FIRST + 0) +#define EMBEDDEDEDITOR_SET_TEXT_COLOR(/*HWND*/ _hwnd, /*COLORREF*/ _color)\ + ((COLORREF)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_SET_TEXT_COLOR, 0, (LPARAM)(_color))) + +#define EMBEDDEDEDITOR_WM_GET_TEXT_COLOR (EMBEDDEDEDITOR_WM_FIRST + 1) +#define EMBEDDEDEDITOR_GET_TEXT_COLOR(/*HWND*/ _hwnd)\ + ((COLORREF)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_GET_TEXT_COLOR, 0, 0L)) + +#define EMBEDDEDEDITOR_WM_SET_BACK_COLOR (EMBEDDEDEDITOR_WM_FIRST + 2) +#define EMBEDDEDEDITOR_SET_BACK_COLOR(/*HWND*/ _hwnd, /*COLORREF*/ _color)\ + ((COLORREF)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_SET_BACK_COLOR, 0, (LPARAM)(_color))) + +#define EMBEDDEDEDITOR_WM_GET_BACK_COLOR (EMBEDDEDEDITOR_WM_FIRST + 3) +#define EMBEDDEDEDITOR_GET_BACK_COLOR(/*HWND*/ _hwnd)\ + ((COLORREF)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_GET_BACK_COLOR, 0, 0L)) + +#define EMBEDDEDEDITOR_WM_SET_BORDER_COLOR (EMBEDDEDEDITOR_WM_FIRST + 4) +#define EMBEDDEDEDITOR_SET_BORDER_COLOR(/*HWND*/ _hwnd, /*COLORREF*/ _color)\ + ((COLORREF)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_SET_BORDER_COLOR, 0, (LPARAM)(_color))) + +#define EMBEDDEDEDITOR_WM_GET_BORDER_COLOR (EMBEDDEDEDITOR_WM_FIRST + 5) +#define EMBEDDEDEDITOR_GET_BORDER_COLOR(/*HWND*/ _hwnd)\ + ((COLORREF)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_GET_BORDER_COLOR, 0, 0L)) + +#define EMBEDDEDEDITOR_WM_SET_USER_DATA (EMBEDDEDEDITOR_WM_FIRST + 6) +#define EMBEDDEDEDITOR_SET_USER_DATA(/*HWND*/ _hwnd, /*void* */ _user)\ + ((void*)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_SET_USER_DATA, 0, (LPARAM)(_user))) + +#define EMBEDDEDEDITOR_WM_GET_USER_DATA (EMBEDDEDEDITOR_WM_FIRST + 7) +#define EMBEDDEDEDITOR_GET_USER_DATA(/*HWND*/ _hwnd)\ + ((void*)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_GET_USER_DATA, 0, 0L)) + +#define EMBEDDEDEDITOR_WM_SET_ANCHOR_POINT (EMBEDDEDEDITOR_WM_FIRST + 8) +#define EMBEDDEDEDITOR_SET_ANCHOR_POINT(/*HWND*/ _hwnd, /*long*/ _x, /*long*/ _y)\ + ((BOOL)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_SET_ANCHOR_POINT, (WPARAM)(_x), (LPARAM)(_y))) + +#define EMBEDDEDEDITOR_WM_GET_ANCHOR_POINT (EMBEDDEDEDITOR_WM_FIRST + 9) +#define EMBEDDEDEDITOR_GET_ANCHOR_POINT(/*HWND*/ _hwnd, /*long* */ _x, /*long* */ _y)\ + ((BOOL)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_GET_ANCHOR_POINT, (WPARAM)(_x), (LPARAM)(_y))) + +#define EMBEDDEDEDITOR_WM_SET_MAX_SIZE (EMBEDDEDEDITOR_WM_FIRST + 10) +#define EMBEDDEDEDITOR_SET_MAX_SIZE(/*HWND*/ _hwnd, /*long*/ _width, /*long*/ _height)\ + ((BOOL)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_SET_MAX_SIZE, (WPARAM)(_width), (LPARAM)(_height))) + +#define EMBEDDEDEDITOR_WM_GET_MAX_SIZE (EMBEDDEDEDITOR_WM_FIRST + 11) +#define EMBEDDEDEDITOR_GET_MAX_SIZE(/*HWND*/ _hwnd, /*long* */ _width, /*long* */ _height)\ + ((BOOL)SendMessageW((_hwnd), EMBEDDEDEDITOR_GET_MAX_SIZE, (WPARAM)(_width), (LPARAM)(_height))) + +#define EMBEDDEDEDITOR_WM_END_EDITING (EMBEDDEDEDITOR_WM_FIRST + 12) +#define EMBEDDEDEDITOR_END_EDITING(/*HWND*/ _hwnd, /*BOOL*/ _cancel)\ + ((BOOL)SendMessageW((_hwnd), EMBEDDEDEDITOR_WM_END_EDITING, (WPARAM)(_cancel), 0L)) +#endif //_NULLSOFT_WINAMP_ML_DEVICES_EMBEDED_EDITOR_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/eventRelay.cpp b/Src/Plugins/Library/ml_devices/eventRelay.cpp new file mode 100644 index 00000000..38226887 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/eventRelay.cpp @@ -0,0 +1,465 @@ +#include "main.h" +#include "./eventRelay.h" +#include <vector> + +#define EVENT_RELAY_WINDOW_CLASS L"NullsoftEventRelay" + +typedef struct EventHandler +{ + size_t cookie; + DeviceEventCallbacks callbacks; + void *user; +} EventHandler; + +typedef std::vector<EventHandler*> EventHandlerList; + +typedef struct EventRelay +{ + EventHandlerList handlerList; + DeviceManagerHandler *managerHandler; + DeviceHandler *deviceHandler; +} EventRelay; + +#define EVENTRELAY(_hwnd) ((EventRelay*)(LONGX86)GetWindowLongPtrW((_hwnd), 0)) +#define EVENTRELAY_RET_VOID(_self, _hwnd) {(_self) = EVENTRELAY((_hwnd)); if (NULL == (_self)) return;} +#define EVENTRELAY_RET_VAL(_self, _hwnd, _error) {(_self) = EVENTRELAY((_hwnd)); if (NULL == (_self)) return (_error);} + + +static LRESULT CALLBACK +EventRelay_WindowProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam); + + +static ATOM +EventRelay_GetClassAtom(HINSTANCE instance) +{ + WNDCLASSEXW klass; + ATOM klassAtom; + + klassAtom = (ATOM)GetClassInfoExW(instance, EVENT_RELAY_WINDOW_CLASS, &klass); + if (0 != klassAtom) + return klassAtom; + + memset(&klass, 0, sizeof(klass)); + klass.cbSize = sizeof(klass); + klass.style = 0; + klass.lpfnWndProc = EventRelay_WindowProc; + klass.cbClsExtra = 0; + klass.cbWndExtra = sizeof(EventRelay*); + klass.hInstance = instance; + klass.hIcon = NULL; + klass.hCursor = NULL; + klass.hbrBackground = NULL; + klass.lpszMenuName = NULL; + klass.lpszClassName = EVENT_RELAY_WINDOW_CLASS; + klass.hIconSm = NULL; + klassAtom = RegisterClassExW(&klass); + + return klassAtom; +} + +HWND +EventRelay_CreateWindow() +{ + HINSTANCE instance; + ATOM klassAtom; + HWND hwnd; + + instance = GetModuleHandleW(NULL); + klassAtom = EventRelay_GetClassAtom(instance); + if (0 == klassAtom) + return NULL; + + hwnd = CreateWindowEx(WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY, + MAKEINTATOM(klassAtom), + NULL, + WS_OVERLAPPED, + 0, 0, 0, 0, + HWND_MESSAGE, + NULL, + instance, + NULL); + + return hwnd; +} + + +static size_t +EventRelay_GenerateCookie(EventRelay *self) +{ + size_t cookie; + EventHandler *handler; + + + if (NULL == self) + return 0; + + cookie = self->handlerList.size() + 1; + + for(;;) + { + size_t index = self->handlerList.size(); + while(index--) + { + handler = self->handlerList[index]; + if (cookie == handler->cookie) + { + cookie++; + break; + } + } + + if (((size_t)-1) == index) + return cookie; + } + + return cookie; +} + +static EventHandler * +EventRelay_CreateEventHandler(EventRelay *self, DeviceEventCallbacks *callbacks, void *user) +{ + EventHandler *handler; + size_t cookie; + + if (NULL == self || NULL == callbacks) + return NULL; + + cookie = EventRelay_GenerateCookie(self); + if (0 == cookie) + return NULL; + + handler = (EventHandler*)malloc(sizeof(EventHandler)); + if (NULL == handler) + return NULL; + + handler->user = user; + handler->cookie = cookie; + handler->callbacks.deviceCb = callbacks->deviceCb; + handler->callbacks.typeCb = callbacks->typeCb; + handler->callbacks.connectionCb = callbacks->connectionCb; + handler->callbacks.commandCb = callbacks->commandCb; + handler->callbacks.discoveryCb = callbacks->discoveryCb; + + return handler; +} + +static void +EventRelay_DestroyEventHandler(EventHandler *handler) +{ + if (NULL == handler) + return; + + free(handler); +} + +static LRESULT +EventRelay_OnCreate(HWND hwnd, CREATESTRUCT *createStruct) +{ + EventRelay *self; + ifc_deviceobjectenum *enumerator; + ifc_deviceobject *object; + ifc_device *device; + + if (NULL == WASABI_API_DEVICES) + return -1; + + self = new EventRelay(); + if (NULL == self) + return -1; + + self->deviceHandler = NULL; + self->managerHandler = NULL; + + SetLastError(ERROR_SUCCESS); + if (!SetWindowLongPtr(hwnd, 0, (LONGX86)self) && ERROR_SUCCESS != GetLastError()) + return -1; + + if (FAILED(DeviceHandler::CreateInstance(&self->deviceHandler))) + return -1; + + self->deviceHandler->SetRelayWindow(hwnd); + + if (SUCCEEDED(WASABI_API_DEVICES->DeviceEnumerate(&enumerator))) + { + while(S_OK == enumerator->Next(&object, 1, NULL)) + { + if (SUCCEEDED(object->QueryInterface(IFC_Device, (void**)&device))) + { + self->deviceHandler->Advise(device); + device->Release(); + } + object->Release(); + } + enumerator->Release(); + } + + if (FAILED(DeviceManagerHandler::CreateInstance(&self->managerHandler))) + return -1; + + self->managerHandler->SetRelayWindow(hwnd); + if (FAILED(self->managerHandler->Advise(WASABI_API_DEVICES))) + return -1; + + return 0; +} + +static void +EventRelay_OnDestroy(HWND hwnd) +{ + EventRelay *self; + MSG msg; + + self = EVENTRELAY(hwnd); + SetWindowLongPtr(hwnd, 0, 0); + + if (NULL == self) + return; + + size_t index = self->handlerList.size(); + while(index--) + { + EventHandler *handler = self->handlerList[index]; + EventRelay_DestroyEventHandler(handler); + } + + if (NULL != self->managerHandler) + { + self->managerHandler->SetRelayWindow(NULL); + + if (NULL != WASABI_API_DEVICES) + self->managerHandler->Unadvise(WASABI_API_DEVICES); + + self->managerHandler->Release(); + } + + if (NULL != self->deviceHandler) + { + self->deviceHandler->SetRelayWindow(NULL); + + if (NULL != WASABI_API_DEVICES) + { + ifc_deviceobjectenum *enumerator; + ifc_deviceobject *object; + ifc_device *device; + + if (SUCCEEDED(WASABI_API_DEVICES->DeviceEnumerate(&enumerator))) + { + while(S_OK == enumerator->Next(&object, 1, NULL)) + { + if (SUCCEEDED(object->QueryInterface(IFC_Device, (void**)&device))) + { + self->deviceHandler->Unadvise(device); + device->Release(); + } + object->Release(); + } + enumerator->Release(); + } + } + + self->deviceHandler->Release(); + } + + delete self; + + // finish pumping messages + while(FALSE != PeekMessage(&msg, hwnd, EVENTRELAY_WM_FIRST, EVENTRELAY_WM_LAST, PM_REMOVE)) + { + EventRelay_WindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } +} + +static LRESULT +EventRelay_OnRegisterHandler(HWND hwnd, DeviceEventCallbacks *callbacks, void *user) +{ + EventRelay *self; + EventHandler *handler; + + EVENTRELAY_RET_VAL(self, hwnd, 0); + + handler = EventRelay_CreateEventHandler(self, callbacks, user); + if(NULL == handler) + return 0; + + self->handlerList.push_back(handler); + return (LRESULT)handler->cookie; +} + +static LRESULT +EventRelay_OnUnregisterHandler(HWND hwnd, size_t cookie) +{ + EventRelay *self; + + EVENTRELAY_RET_VAL(self, hwnd, FALSE); + + size_t index = self->handlerList.size(); + while(index--) + { + EventHandler *handler = self->handlerList[index]; + if (handler->cookie == cookie) + { + self->handlerList.erase(self->handlerList.begin() + index); + EventRelay_DestroyEventHandler(handler); + return TRUE; + } + } + + return FALSE; +} + +static void +EventRelay_OnNotifyDevice(HWND hwnd, ifc_device *device, DeviceEvent eventId) +{ + ReplyMessage(0); + + if (NULL != device) + { + EventRelay *self; + self = EVENTRELAY(hwnd); + + if (NULL != self) + { + switch(eventId) + { + case Event_DeviceAdded: + if (NULL != self->deviceHandler) + self->deviceHandler->Advise(device); + break; + + case Event_DeviceRemoved: + if (NULL != self->deviceHandler) + self->deviceHandler->Unadvise(device); + break; + } + + size_t index = self->handlerList.size(); + while(index--) + { + EventHandler *handler = self->handlerList[index]; + if (NULL != handler->callbacks.deviceCb) + handler->callbacks.deviceCb(device, eventId, handler->user); + } + } + + device->Release(); + } +} + +static void +EventRelay_OnNotifyDiscovery(HWND hwnd, api_devicemanager *manager, DeviceDiscoveryEvent eventId) +{ + ReplyMessage(0); + + if (NULL != manager) + { + EventRelay *self; + self = EVENTRELAY(hwnd); + + if (NULL != self) + { + size_t index = self->handlerList.size(); + while(index--) + { + EventHandler *handler = self->handlerList[index]; + if (NULL != handler->callbacks.discoveryCb) + handler->callbacks.discoveryCb(manager, eventId, handler->user); + } + } + + manager->Release(); + } +} + +static void +EventRelay_OnNotifyType(HWND hwnd, ifc_devicetype *type, DeviceTypeEvent eventId) +{ + ReplyMessage(0); + + if (NULL != type) + { + EventRelay *self; + self = EVENTRELAY(hwnd); + + if (NULL != self) + { + size_t index = self->handlerList.size(); + while(index--) + { + EventHandler *handler = self->handlerList[index]; + if (NULL != handler->callbacks.typeCb) + handler->callbacks.typeCb(type, eventId, handler->user); + } + } + + type->Release(); + } +} + +static void +EventRelay_OnNotifyConnection(HWND hwnd, ifc_deviceconnection *connection, DeviceConnectionEvent eventId) +{ + ReplyMessage(0); + + if (NULL != connection) + { + EventRelay *self; + self = EVENTRELAY(hwnd); + + if (NULL != self) + { + size_t index = self->handlerList.size(); + while(index--) + { + EventHandler *handler = self->handlerList[index]; + if (NULL != handler->callbacks.connectionCb) + handler->callbacks.connectionCb(connection, eventId, handler->user); + } + } + + connection->Release(); + } +} + +static void +EventRelay_OnNotifyCommand(HWND hwnd, ifc_devicecommand *command, DeviceCommandEvent eventId) +{ + ReplyMessage(0); + + if (NULL != command) + { + EventRelay *self; + self = EVENTRELAY(hwnd); + + if (NULL != self) + { + size_t index = self->handlerList.size(); + while(index--) + { + EventHandler *handler = self->handlerList[index]; + if (NULL != handler->callbacks.commandCb) + handler->callbacks.commandCb(command, eventId, handler->user); + } + } + + command->Release(); + } +} + +static LRESULT CALLBACK +EventRelay_WindowProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return EventRelay_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: EventRelay_OnDestroy(hwnd); return 0; + + case EVENTRELAY_WM_REGISTER_HANDLER: return EventRelay_OnRegisterHandler(hwnd, (DeviceEventCallbacks*)lParam, (void*)wParam); + case EVENTRELAY_WM_UNREGISTER_HANDLER: return EventRelay_OnUnregisterHandler(hwnd, (size_t)lParam); + case EVENTRELAY_WM_NOTIFY_DEVICE: EventRelay_OnNotifyDevice(hwnd, (ifc_device*)lParam, (DeviceEvent)wParam); return 0; + case EVENTRELAY_WM_NOTIFY_DISCOVERY: EventRelay_OnNotifyDiscovery(hwnd, (api_devicemanager*)lParam, (DeviceDiscoveryEvent)wParam); return 0; + case EVENTRELAY_WM_NOTIFY_TYPE: EventRelay_OnNotifyType(hwnd, (ifc_devicetype*)lParam, (DeviceTypeEvent)wParam); return 0; + case EVENTRELAY_WM_NOTIFY_CONNECTION: EventRelay_OnNotifyConnection(hwnd, (ifc_deviceconnection*)lParam, (DeviceConnectionEvent)wParam); return 0; + case EVENTRELAY_WM_NOTIFY_COMMAND: EventRelay_OnNotifyCommand(hwnd, (ifc_devicecommand*)lParam, (DeviceCommandEvent)wParam); return 0; + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} diff --git a/Src/Plugins/Library/ml_devices/eventRelay.h b/Src/Plugins/Library/ml_devices/eventRelay.h new file mode 100644 index 00000000..572af8a3 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/eventRelay.h @@ -0,0 +1,116 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_EVENT_RELAY_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_EVENT_RELAY_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +typedef enum DeviceEvent +{ + Event_DeviceAdded = 1, + Event_DeviceRemoved = 2, + Event_DeviceIconChanged = 3, + Event_DeviceDisplayNameChanged = 4, + Event_DeviceAttached = 5, + Event_DeviceDetached = 6, + Event_DeviceHidden = 7, + Event_DeviceShown = 8, + Event_DeviceTotalSpaceChanged = 9, + Event_DeviceUsedSpaceChanged = 10, + Event_DeviceCommandChanged = 11, + Event_DeviceActivityStarted = 12, + Event_DeviceActivityFinished = 13, + Event_DeviceActivityChanged = 14, + Event_DeviceModelChanged = 15, + Event_DeviceStatusChanged = 16, +} DeviceEvent; + +typedef enum DeviceTypeEvent +{ + Event_TypeRegistered = 1, + Event_TypeUnregistered = 2, +} DeviceTypeEvent; + +typedef enum DeviceConnectionEvent +{ + Event_ConnectionRegistered = 1, + Event_ConnectionUnregistered = 2, +} DeviceConnectionEvent; + +typedef enum DeviceCommandEvent +{ + Event_CommandRegistered = 1, + Event_CommandUnregistered = 2, +} DeviceCommandEvent; + +typedef enum DeviceDiscoveryEvent +{ + Event_DiscoveryStarted = 1, + Event_DiscoveryFinished = 2, +} DeviceDiscoveryEvent; + +typedef void (*DeviceEventCb)(ifc_device *device, DeviceEvent eventId, void *user); +typedef void (*DeviceTypeEventCb)(ifc_devicetype *type, DeviceTypeEvent eventId, void *user); +typedef void (*DeviceConnectionEventCb)(ifc_deviceconnection *connection, DeviceConnectionEvent eventId, void *user); +typedef void (*DeviceCommandEventCb)(ifc_devicecommand *command, DeviceCommandEvent eventId, void *user); +typedef void (*DeviceDiscoveryEventCb)(api_devicemanager *manager, DeviceDiscoveryEvent eventId, void *user); + +typedef struct DeviceEventCallbacks +{ + DeviceEventCb deviceCb; + DeviceTypeEventCb typeCb; + DeviceConnectionEventCb connectionCb; + DeviceCommandEventCb commandCb; + DeviceDiscoveryEventCb discoveryCb; +} DeviceEventCallbacks; + +HWND +EventRelay_CreateWindow(); + + +#define EVENTRELAY_WM_FIRST (WM_USER + 10) + +#define EVENTRELAY_WM_REGISTER_HANDLER (EVENTRELAY_WM_FIRST + 0) +#define EVENTRELAY_REGISTER_HANDLER(/*HWND*/ _hwnd, /*DeviceEventCallbacks*/ _handler, /*void* */ _user)\ + ((size_t)SendMessageW((_hwnd), EVENTRELAY_WM_REGISTER_HANDLER, (WPARAM)(_user), (LPARAM)(_handler))) + +#define EVENTRELAY_WM_UNREGISTER_HANDLER (EVENTRELAY_WM_FIRST + 1) +#define EVENTRELAY_UNREGISTER_HANDLER(/*HWND*/ _hwnd, /*size_t*/ _handlerCookie)\ + ((BOOL)SendMessageW((_hwnd), EVENTRELAY_WM_UNREGISTER_HANDLER, 0, (LPARAM)(_handlerCookie))) + + +#define EVENTRELAY_WM_NOTIFY_DEVICE (EVENTRELAY_WM_FIRST + 2) +#define EVENTRELAY_NOTIFY_DEVICE(/*HWND*/ _hwnd, /*ifc_device* */ _device, /*DeviceEvent*/ _eventId)\ + { ifc_device *_d = (_device); if (NULL != _d && NULL != (_hwnd)) { _d->AddRef(); \ + if (FALSE == ((BOOL)PostMessageW((_hwnd), EVENTRELAY_WM_NOTIFY_DEVICE,\ + (WPARAM)(_eventId), (LPARAM)(_d)))) { _d->Release(); }}} + +#define EVENTRELAY_WM_NOTIFY_DISCOVERY (EVENTRELAY_WM_FIRST + 3) +#define EVENTRELAY_NOTIFY_DISCOVERY(/*HWND*/ _hwnd, /*api_devicemanager* */ _manager, /*DeviceDiscoveryEvent*/ _eventId)\ + { api_devicemanager *_m = (_manager); if (NULL != _m && NULL != (_hwnd)) { _m->AddRef(); \ + if (FALSE == ((BOOL)PostMessageW((_hwnd), EVENTRELAY_WM_NOTIFY_DISCOVERY,\ + (WPARAM)(_eventId), (LPARAM)(_m)))) { _m->Release(); }}} + +#define EVENTRELAY_WM_NOTIFY_TYPE (EVENTRELAY_WM_FIRST + 4) +#define EVENTRELAY_NOTIFY_TYPE(/*HWND*/ _hwnd, /*ifc_devicetype* */ _type, /*DeviceTypeEvent*/ _eventId)\ + { ifc_devicetype *_t = (_type); if (NULL != _t && NULL != (_hwnd)) { _t->AddRef(); \ + if (FALSE == ((BOOL)PostMessageW((_hwnd), EVENTRELAY_WM_NOTIFY_TYPE,\ + (WPARAM)(_eventId), (LPARAM)(_t)))) { _t->Release(); }}} + +#define EVENTRELAY_WM_NOTIFY_CONNECTION (EVENTRELAY_WM_FIRST + 5) +#define EVENTRELAY_NOTIFY_CONNECTION(/*HWND*/ _hwnd, /*ifc_deviceconnection* */ _connection, /*DeviceConnectionEvent*/ _eventId)\ + { ifc_deviceconnection *_c = (_connection); if (NULL != _c && NULL != (_hwnd)) { _c->AddRef(); \ + if (FALSE == ((BOOL)PostMessageW((_hwnd), EVENTRELAY_WM_NOTIFY_CONNECTION,\ + (WPARAM)(_eventId), (LPARAM)(_c)))) { _c->Release(); }}} + +#define EVENTRELAY_WM_NOTIFY_COMMAND (EVENTRELAY_WM_FIRST + 6) +#define EVENTRELAY_NOTIFY_COMMAND(/*HWND*/ _hwnd, /*ifc_devicecommand* */ _command, /*DeviceCommandEvent*/ _eventId)\ + { ifc_devicecommand *_c = (_command); if (NULL != _c && NULL != (_hwnd)) { _c->AddRef(); \ + if (FALSE == ((BOOL)PostMessageW((_hwnd), EVENTRELAY_WM_NOTIFY_COMMAND,\ + (WPARAM)(_eventId), (LPARAM)(_c)))) { _c->Release(); }}} + +#define EVENTRELAY_WM_LAST EVENTRELAY_WM_NOTIFY_COMMAND + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_EVENT_RELAY_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/fillRegion.cpp b/Src/Plugins/Library/ml_devices/fillRegion.cpp new file mode 100644 index 00000000..68f0ee23 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/fillRegion.cpp @@ -0,0 +1,134 @@ +#include "main.h" +#include "./fillRegion.h" + +static BOOL +FillRegion_TempRegionFromRect(FillRegion *region, const RECT *rect) +{ + if (NULL == region || NULL == rect) + return FALSE; + + if (NULL == region->tmp) + { + region->tmp = CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom); + if (NULL == region->tmp) + return FALSE; + } + else + { + if (FALSE == SetRectRgn(region->tmp, rect->left, rect->top, rect->right, rect->bottom)) + return FALSE; + } + + return TRUE; +} + +BOOL +FillRegion_Init(FillRegion *region, const RECT *rect) +{ + if (NULL == region) + return FALSE; + + region->fill = (NULL != rect) ? + CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom) : + NULL; + + region->tmp = NULL; + + if (NULL == region->fill) + return FALSE; + + return TRUE; +} + +void +FillRegion_Uninit(FillRegion *region) +{ + if (NULL != region) + { + if (NULL != region->fill) + { + DeleteObject(region->fill); + region->fill = NULL; + } + + if (NULL != region->tmp) + { + DeleteObject(region->tmp); + region->tmp = NULL; + } + } +} + +BOOL +FillRegion_ExcludeRect(FillRegion *region, const RECT *rect) +{ + if (NULL == region || NULL == rect) + return FALSE; + + return (FALSE != FillRegion_TempRegionFromRect(region, rect) && + ERROR != CombineRgn(region->fill, region->fill, region->tmp, RGN_DIFF)); +} + +BOOL +FillRegion_ExcludeRgn(FillRegion *region, HRGN rgn) +{ + if (NULL == region || NULL == rgn) + return FALSE; + + return (ERROR != CombineRgn(region->fill, region->fill, rgn, RGN_DIFF)); +} + +BOOL +FillRegion_AppendRect(FillRegion *region, const RECT *rect) +{ + if (NULL == region || NULL == rect) + return FALSE; + + return (FALSE != FillRegion_TempRegionFromRect(region, rect) && + ERROR != CombineRgn(region->fill, region->fill, region->tmp, RGN_OR)); +} + +BOOL +FillRegion_AppendRgn(FillRegion *region, HRGN rgn) +{ + if (NULL == region || NULL == rgn) + return FALSE; + + return (ERROR != CombineRgn(region->fill, region->fill, rgn, RGN_OR)); +} + +BOOL +FillRegion_BrushFill(FillRegion *region, HDC hdc, HBRUSH brush) +{ + if (NULL == region) + return FALSE; + + return FillRgn(hdc, region->fill, brush); +} + +BOOL +FillRegion_Offset(FillRegion *region, long x, long y) +{ + if (NULL == region) + return FALSE; + + return (ERROR != OffsetRgn(region->fill, x, y)); +} + +BOOL +FillRegion_SetRect(FillRegion *region, const RECT *rect) +{ + if (NULL == region || NULL == rect) + return FALSE; + + return SetRectRgn(region->fill, rect->left, rect->top, rect->right, rect->bottom); +} + +BOOL +FillRegion_SetEmpty(FillRegion *region) +{ + if (NULL == region) + return FALSE; + + return SetRectRgn(region->fill, 0, 0, 0, 0); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/fillRegion.h b/Src/Plugins/Library/ml_devices/fillRegion.h new file mode 100644 index 00000000..6337ad62 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/fillRegion.h @@ -0,0 +1,51 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_FILL_REGION_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_FILL_REGION_HEADER + + +typedef struct FillRegion +{ + HRGN fill; + HRGN tmp; +} FillRegion; + +BOOL +FillRegion_Init(FillRegion *region, + const RECT *rect); + +void +FillRegion_Uninit(FillRegion *region); + +BOOL +FillRegion_ExcludeRect(FillRegion *region, + const RECT *rect); + +BOOL +FillRegion_ExcludeRgn(FillRegion *region, + HRGN rgn); + +BOOL +FillRegion_AppendRect(FillRegion *region, + const RECT *rect); + +BOOL +FillRegion_AppendRgn(FillRegion *region, + HRGN rgn); + +BOOL +FillRegion_BrushFill(FillRegion *region, + HDC hdc, + HBRUSH brush); + +BOOL +FillRegion_Offset(FillRegion *region, + long x, + long y); + +BOOL +FillRegion_SetRect(FillRegion *region, + const RECT *rect); + +BOOL +FillRegion_SetEmpty(FillRegion *region); + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_FILL_REGION_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/common.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/common.h new file mode 100644 index 00000000..311ef665 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/common.h @@ -0,0 +1,76 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_COMMON_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_COMMON_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) + +#ifdef __cplusplus + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) ::SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#else + #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#endif // __cplusplus + +#define SENDMLIPC(__hwndML, __ipcMsgId, __param) SENDMSG((__hwndML), WM_ML_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) + +#define SENDWAIPC(__hwndWA, __ipcMsgId, __param) SENDMSG((__hwndWA), WM_WA_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) + +#define SENDCMD(__hwnd, __ctrlId, __eventId, __hctrl) (SENDMSG((__hwnd), WM_COMMAND, MAKEWPARAM(__ctrlId, __eventId), (LPARAM)(__hctrl))) + +#define DIALOG_RESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWLP_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; } + +#ifndef GetWindowStyle + #define GetWindowStyle(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_STYLE)) +#endif //GetWindowStyle + +#ifndef SetWindowStyle + #define SetWindowStyle(__hwnd, __style) (SetWindowLongPtr((__hwnd), GWL_STYLE, (__style))) +#endif //SetWindowStyle + +#ifndef GetWindowStyleEx + #define GetWindowStyleEx(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_EXSTYLE)) +#endif // GetWindowStyleEx + +#ifndef SetWindowStyleEx + #define SetWindowStyleEx(__hwnd, __style) (SetWindowLongPtr((__hwnd), GWL_EXSTYLE, (__style))) +#endif //SetWindowStyle + +#ifndef RECTWIDTH + #define RECTWIDTH(__r) ((__r).right - (__r).left) +#endif + +#ifndef RECTHEIGHT + #define RECTHEIGHT(__r) ((__r).bottom - (__r).top) +#endif + +#undef CLAMP +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +#ifndef ARRAYSIZE + #define ARRAYSIZE(_a) (sizeof(_a)/sizeof((_a)[0])) +#endif + +#ifndef ABS + #define ABS(x) (((x) > 0) ? (x) : (-x)) +#endif + +#ifndef MIN + #define MIN(v1, v2) (((v1) < (v2)) ? (v1) : (v2)) +#endif + +#ifndef MAX + #define MAX(v1, v2) (((v1) > (v2)) ? (v1) : (v2)) +#endif + + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_COMMON_HEADER diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/device.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/device.cpp new file mode 100644 index 00000000..3097e5a1 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/device.cpp @@ -0,0 +1,871 @@ +#include "main.h" +#include "./device.h" + +#include <strsafe.h> + +Device::Device() + : ref(1), name(NULL), type(NULL), connection(NULL), displayName(NULL), + totalSpace(0), usedSpace(0), attached(FALSE), hidden(FALSE), + connected(FALSE), activity(NULL), model(NULL), status(NULL) +{ + + InitializeCriticalSection(&lock); + + if (NULL == WASABI_API_DEVICES || + FAILED(WASABI_API_DEVICES->CreateDeviceEventManager(&eventManager))) + { + eventManager = NULL; + } + + if (NULL == WASABI_API_DEVICES || + FAILED(WASABI_API_DEVICES->CreateIconStore(&iconStore))) + { + iconStore = NULL; + } + + if (NULL == WASABI_API_DEVICES || + FAILED(WASABI_API_DEVICES->CreateSupportedCommandStore(&commands))) + { + commands = NULL; + } + +} + +Device::~Device() +{ + DeviceActivity *activityCopy; + + Lock(); + + if (NULL != activity) + { + activity->SetUser(NULL); + activityCopy = activity; + activityCopy->AddRef(); + } + else + activityCopy = NULL; + + AnsiString_Free(name); + AnsiString_Free(type); + AnsiString_Free(connection); + String_Free(displayName); + String_Free(model); + String_Free(status); + + if (NULL != commands) + commands->Release(); + + if (NULL != iconStore) + iconStore->Release(); + + if (NULL != eventManager) + eventManager->Release(); + + Unlock(); + + if (NULL != activityCopy) + { + + activityCopy->Stop(); + activityCopy->Release(); + } + + DeleteCriticalSection(&lock); +} + +HRESULT Device::CreateInstance(const char *name, const char *type, const char *connection, Device**instance) +{ + Device *self; + + if (NULL == instance) + return E_POINTER; + + *instance = NULL; + + self = new Device(); + if (NULL == self) + return E_OUTOFMEMORY; + + self->name = AnsiString_Duplicate(name); + self->type = AnsiString_Duplicate(type); + self->connection = AnsiString_Duplicate(connection); + + *instance = self; + return S_OK; +} + +size_t Device::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t Device::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int Device::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) + return E_POINTER; + + if (IsEqualIID(interface_guid, IFC_Device)) + *object = static_cast<ifc_device*>(this); + else + { + *object = NULL; + return E_NOINTERFACE; + } + + if (NULL == *object) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +void Device::Lock() +{ + EnterCriticalSection(&lock); +} + +void Device::Unlock() +{ + LeaveCriticalSection(&lock); +} + +const char *Device::GetName() +{ + return name; +} + +const char *Device::GetType() +{ + return type; +} + +const char *Device::GetConnection() +{ + return connection; +} + +HRESULT Device::GetIcon(wchar_t *buffer, size_t bufferSize, int width, int height) +{ + if (NULL == buffer) + return E_POINTER; + + if (NULL == iconStore) + return E_UNEXPECTED; + + return iconStore->Get(buffer, bufferSize, width, height); +} + +HRESULT Device::GetDisplayName(wchar_t *buffer, size_t bufferSize) +{ + HRESULT hr; + + if (NULL == buffer) + return E_POINTER; + + Lock(); + + if (0 == String_CopyTo(buffer, displayName, bufferSize) && + FALSE == IS_STRING_EMPTY(displayName)) + { + hr = E_FAIL; + } + else + hr = S_OK; + + Unlock(); + + return hr; +} + +BOOL Device::GetHidden() +{ + return hidden; +} + +HRESULT Device::GetTotalSpace(uint64_t *size) +{ + if (NULL == size) + return E_POINTER; + + Lock(); + + *size = totalSpace; + + Unlock(); + + return S_OK; +} + +HRESULT Device::GetUsedSpace(uint64_t *size) +{ + if (NULL == size) + return E_POINTER; + + Lock(); + + *size = usedSpace; + + Unlock(); + + return S_OK; +} + +BOOL Device::GetAttached() +{ + return attached; +} + +HRESULT Device::Attach(HWND hostWindow) +{ + HRESULT hr; + + Lock(); + + if (FALSE != attached) + hr = S_FALSE; + else + { + attached = TRUE; + hr = S_OK; + } + + Unlock(); + + if (S_OK == hr && NULL != eventManager) + eventManager->Notify_AttachmentChanged(this, attached); + + return hr; +} + +HRESULT Device::Detach(HWND hostWindow) +{ + HRESULT hr; + + Lock(); + + if (FALSE == attached) + hr = S_FALSE; + else + { + attached = FALSE; + hr = S_OK; + } + + Unlock(); + + if (S_OK == hr && NULL != eventManager) + eventManager->Notify_AttachmentChanged(this, attached); + + return hr; +} + +HRESULT Device::EnumerateCommands(ifc_devicesupportedcommandenum **enumerator, DeviceCommandContext context) +{ + if (NULL == commands) + return E_UNEXPECTED; + + return commands->Enumerate(enumerator); +} + +HRESULT Device::SendCommand(const char *command, HWND hostWindow, ULONG_PTR param) +{ + const wchar_t *commandName; + wchar_t message[1024]; + + if (NULL == command) + return E_POINTER; + + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, command, -1, "sync", -1)) + { + StartSyncActivity(hostWindow); + return S_OK; + } + else if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, command, -1, "eject", -1)) + commandName = L"Eject"; + else if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, command, -1, "detach", -1)) + { + Detach(hostWindow); + return S_OK; + } + else if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, command, -1, "settings", -1)) + commandName = L"Settings"; + else + return E_NOTIMPL; + + StringCchPrintf(message, ARRAYSIZE(message), L"%s command received", commandName); + MessageBox(hostWindow, message, L"Device command test", MB_OK | MB_ICONINFORMATION); + + + return S_OK; +} + +HRESULT Device::GetCommandFlags(const char *command, DeviceCommandFlags *flags) +{ + if (NULL == commands) + return E_UNEXPECTED; + + return commands->GetFlags(command, flags); +} + +HRESULT Device::GetActivity(ifc_deviceactivity **activityOut) +{ + HRESULT hr; + + if (NULL == activityOut) + return E_POINTER; + + Lock(); + + *activityOut = activity; + + if (NULL != activity) + { + activity->AddRef(); + hr = S_OK; + } + else + hr = S_FALSE; + + Unlock(); + + return hr; +} + +HRESULT Device::Advise(ifc_deviceevent *handler) +{ + if (NULL == eventManager) + return E_UNEXPECTED; + + return eventManager->Advise(handler); +} + +HRESULT Device::Unadvise(ifc_deviceevent *handler) +{ + if (NULL == eventManager) + return E_UNEXPECTED; + + return eventManager->Unadvise(handler); +} + +HWND Device::CreateView(HWND parentWindow) +{ + return DeviceView_CreateWindow(parentWindow, this); +} + +void Device::SetNavigationItem(void *navigationItem) +{ +} + +HRESULT Device::GetModel(wchar_t *buffer, size_t bufferSize) +{ + HRESULT hr; + + if (NULL == buffer) + return E_POINTER; + + Lock(); + + if (0 == String_CopyTo(buffer, model, bufferSize) && + FALSE == IS_STRING_EMPTY(model)) + { + hr = E_FAIL; + } + else + hr = S_OK; + + Unlock(); + + return hr; +} +HRESULT Device::GetStatus(wchar_t *buffer, size_t bufferSize) +{ + HRESULT hr; + + if (NULL == buffer) + return E_POINTER; + + Lock(); + + if (0 == String_CopyTo(buffer, status, bufferSize) && + FALSE == IS_STRING_EMPTY(status)) + { + hr = E_FAIL; + } + else + hr = S_OK; + + Unlock(); + + return hr; +} +HRESULT Device::SetConnection(const char *con) +{ + Lock(); + + AnsiString_Free(connection); + connection = AnsiString_Duplicate(con); + + Unlock(); + + return S_OK; +} + +HRESULT Device::SetDisplayName(const wchar_t *name) +{ + HRESULT hr; + + Lock(); + + if (NULL == name && NULL == displayName) + hr = S_FALSE; + else + { + if (NULL != displayName && + CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, 0, name, -1, displayName, -1)) + { + hr = S_FALSE; + } + else + { + wchar_t *string; + + string = String_Duplicate(name); + if (NULL == string && NULL != name) + hr = E_FAIL; + else + { + String_Free(displayName); + displayName = string; + + if (NULL != eventManager) + eventManager->Notify_DisplayNameChanged(this, displayName); + + hr = S_OK; + } + } + } + + Unlock(); + + return hr; +} + +HRESULT Device::SetTotalSpace(uint64_t size) +{ + Lock(); + + totalSpace = size; + + if (NULL != eventManager) + eventManager->Notify_TotalSpaceChanged(this, totalSpace); + + Unlock(); + + return S_OK; +} + +HRESULT Device::SetUsedSpace(uint64_t size) +{ + Lock(); + + usedSpace = size; + + if (NULL != eventManager) + eventManager->Notify_UsedSpaceChanged(this, usedSpace); + + Unlock(); + + return S_OK; +} + +HRESULT Device::SetModel(const wchar_t *deviceModel) +{ + HRESULT hr; + + Lock(); + + if (NULL == deviceModel && NULL == model) + hr = S_FALSE; + else + { + if (NULL != model && + CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, 0, deviceModel, -1, model, -1)) + { + hr = S_FALSE; + } + else + { + wchar_t *string; + + string = String_Duplicate(deviceModel); + if (NULL == string && NULL != deviceModel) + hr = E_FAIL; + else + { + String_Free(model); + model = string; + + if (NULL != eventManager) + eventManager->Notify_ModelChanged(this, model); + + hr = S_OK; + } + } + } + + Unlock(); + + return hr; +} + +HRESULT Device::SetStatus(const wchar_t *deviceStatus) +{ + HRESULT hr; + + Lock(); + + if (NULL == deviceStatus && NULL == status) + hr = S_FALSE; + else + { + if (NULL != status && + CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, 0, deviceStatus, -1, status, -1)) + { + hr = S_FALSE; + } + else + { + wchar_t *string; + + string = String_Duplicate(deviceStatus); + if (NULL == string && NULL != deviceStatus) + hr = E_FAIL; + else + { + String_Free(status); + status = string; + + if (NULL != eventManager) + eventManager->Notify_StatusChanged(this, status); + + hr = S_OK; + } + } + } + + Unlock(); + + return hr; +} + +HRESULT Device::AddIcon(const wchar_t *path, unsigned int width, unsigned int height) +{ + HRESULT hr; + + if (NULL == iconStore) + return E_UNEXPECTED; + + hr = iconStore->Add(path, width, height, TRUE); + + if (SUCCEEDED(hr)) + { + if (NULL != eventManager) + eventManager->Notify_IconChanged(this); + } + + return hr; +} + +HRESULT Device::EnumerateIcons(ifc_deviceiconstore::EnumeratorCallback callback, void *user) +{ + if (NULL == iconStore) + return E_UNEXPECTED; + + return iconStore->Enumerate(callback, user); +} + + +HRESULT Device::RemoveIcon(unsigned int width, unsigned int height) +{ + HRESULT hr; + + if (NULL == iconStore) + return E_UNEXPECTED; + + hr = iconStore->Remove(width, height); + + if (SUCCEEDED(hr)) + { + if (NULL != eventManager) + eventManager->Notify_IconChanged(this); + } + + return hr; +} + + +HRESULT Device::SetHidden(BOOL hiddenState) +{ + HRESULT hr; + + Lock(); + + if (hidden == (FALSE != hiddenState)) + hr = S_FALSE; + else + { + hidden = (FALSE != hiddenState); + hr = S_OK; + } + + Unlock(); + + if (S_OK == hr && NULL != eventManager) + eventManager->Notify_VisibilityChanged(this, TRUE); + + return hr; +} + +HRESULT Device::IsConnected() +{ + HRESULT hr; + + Lock(); + hr = (FALSE != connected) ? S_OK : S_FALSE; + Unlock(); + + return hr; +} + +HRESULT Device::Connect() +{ + HRESULT hr; + + Lock(); + + if (FALSE != connected) + hr = S_FALSE; + else + { + connected = TRUE; + hr = S_OK; + } + + Unlock(); + + return hr; +} + +HRESULT Device::Disconnect() +{ + HRESULT hr; + + Lock(); + + if (FALSE == connected) + hr = S_FALSE; + else + { + connected = FALSE; + hr = S_OK; + } + + Unlock(); + + return hr; +} + +HRESULT Device::CopyTo(Device *target) +{ + if (NULL == target) + return E_POINTER; + + Lock(); + + target->SetDisplayName(displayName); + + if (NULL != target->iconStore) + target->iconStore->Release(); + + if (NULL == iconStore || FAILED(iconStore->Clone(&target->iconStore))) + target->iconStore = NULL; + + target->usedSpace = usedSpace; + target->totalSpace = totalSpace; + target->hidden = hidden; + target->attached = attached; + target->connected = connected; + + if (NULL != target->commands) + target->commands->Release(); + + if (NULL == commands || FAILED(commands->Clone(&target->commands, TRUE))) + target->commands = NULL; + + Unlock(); + + return S_OK; +} + +HRESULT Device::SetIconBase(const wchar_t *path) +{ + if (NULL == iconStore) + return E_UNEXPECTED; + + return iconStore->SetBasePath(path); +} + +HRESULT Device::AddCommand(const char *command, DeviceCommandFlags flags) +{ + if (NULL == commands) + return E_UNEXPECTED; + + return commands->Add(command, flags); +} + +HRESULT Device::RemoveCommand(const char *command) +{ + if (NULL == commands) + return E_UNEXPECTED; + + return commands->Remove(command); + +} + +HRESULT Device::SetCommandFlags(const char *command, DeviceCommandFlags mask, DeviceCommandFlags flags) +{ + if (NULL == commands) + return E_UNEXPECTED; + + return commands->SetFlags(command, mask, flags); +} + +void Device::ActivityStartedCb(DeviceActivity *activity) +{ + Device *device; + + if(FAILED(activity->GetUser((void**)&device)) || NULL == device) + return; + + if (NULL != device->eventManager) + device->eventManager->Notify_ActivityStarted(device, activity); +} + +void Device::ActivityFinishedCb(DeviceActivity *activity) +{ + Device *device; + + if(FAILED(activity->GetUser((void**)&device)) || NULL == device) + return; + + device->Lock(); + + if (activity == device->activity) + device->activity = NULL; + + + device->Unlock(); + + if (NULL != device->eventManager) + device->eventManager->Notify_ActivityFinished(device, activity); + + activity->Release(); +} + +void Device::ActivityProgressCb(DeviceActivity *activity, unsigned int progress, unsigned int duration) +{ + Device *device; + uint64_t space; + + if(FAILED(activity->GetUser((void**)&device)) || NULL == device) + return; + + device->Lock(); + + space = device->usedSpace; + space++; + if (space > device->totalSpace) + space = 0; + + device->Unlock(); + + device->SetUsedSpace(space); + + if (NULL != device->eventManager) + device->eventManager->Notify_ActivityChanged(device, activity); + +} + +HRESULT Device::StartSyncActivity(HWND hostWindow) +{ + HRESULT hr; + + Lock(); + + if (NULL != activity) + hr = E_PENDING; + else + { + hr = DeviceActivity::CreateInstance(DeviceActivityFlag_Cancelable | DeviceActivityFlag_SupportProgress, + ActivityStartedCb, ActivityFinishedCb, ActivityProgressCb, + this, &activity); + + if (SUCCEEDED(hr)) + { + activity->SetDisplayName(L"Synchronizing..."); + + activity->SetStatus(L"Performing synchronization..."); + hr = activity->Start(60000, 20); + if (FAILED(hr)) + { + activity->Release(); + activity = NULL; + } + } + } + Unlock(); + return S_OK; +} + +#define CBCLASS Device +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +CB(API_GETNAME, GetName) +CB(API_GETTYPE, GetType) +CB(API_GETCONNECTION, GetConnection) +CB(API_GETICON, GetIcon) +CB(API_GETDISPLAYNAME, GetDisplayName) +CB(API_GETHIDDEN, GetHidden) +CB(API_GETTOTALSPACE, GetTotalSpace) +CB(API_GETUSEDSPACE, GetUsedSpace) +CB(API_GETATTACHED, GetAttached) +CB(API_ATTACH, Attach) +CB(API_DETACH, Detach) +CB(API_ENUMERATECOMMANDS, EnumerateCommands) +CB(API_SENDCOMMAND, SendCommand) +CB(API_GETCOMMANDFLAGS, GetCommandFlags) +CB(API_GETACTIVITY, GetActivity) +CB(API_ADVISE, Advise) +CB(API_UNADVISE, Unadvise) +CB(API_CREATEVIEW, CreateView) +VCB(API_SETNAVIGATIONITEM, SetNavigationItem) +CB(API_SETDISPLAYNAME, SetDisplayName) +CB(API_GETMODEL, GetModel) +CB(API_GETSTATUS, GetStatus) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/device.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/device.h new file mode 100644 index 00000000..05b5947c --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/device.h @@ -0,0 +1,114 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class DeviceActivity; + +class Device: public ifc_device +{ + +protected: + Device(); + ~Device(); +public: + static HRESULT CreateInstance(const char *name, + const char *type, + const char *connection, + Device**instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /* ifc_device */ + const char *GetName(); + const char *GetType(); + const char *GetConnection(); + HRESULT GetIcon(wchar_t *buffer, size_t bufferSize, int width, int height); + HRESULT GetDisplayName(wchar_t *buffer, size_t bufferSize); + BOOL GetHidden(); + HRESULT GetTotalSpace(uint64_t *size); + HRESULT GetUsedSpace(uint64_t *size); + BOOL GetAttached(); + HRESULT Attach(HWND hostWindow); + HRESULT Detach(HWND hostWindow); + HRESULT EnumerateCommands(ifc_devicesupportedcommandenum **enumerator, DeviceCommandContext context); + HRESULT SendCommand(const char *command, HWND hostWindow, ULONG_PTR param); + HRESULT GetCommandFlags(const char *command, DeviceCommandFlags *flags); + HRESULT GetActivity(ifc_deviceactivity **activity); + HRESULT Advise(ifc_deviceevent *handler); + HRESULT Unadvise(ifc_deviceevent *handler); + + HWND CreateView(HWND parentWindow); + void SetNavigationItem(void *navigationItem); + + HRESULT GetModel(wchar_t *buffer, size_t bufferSize); + HRESULT GetStatus(wchar_t *buffer, size_t bufferSize); + +public: + HRESULT SetConnection(const char *connection); + HRESULT SetDisplayName(const wchar_t *name); + HRESULT SetTotalSpace(uint64_t size); + HRESULT SetUsedSpace(uint64_t size); + HRESULT SetHidden(BOOL hiddenState); + HRESULT SetModel(const wchar_t *deviceModel); + HRESULT SetStatus(const wchar_t *deviceStatus); + + HRESULT AddIcon(const wchar_t *path, unsigned int width, unsigned int height); + HRESULT EnumerateIcons(ifc_deviceiconstore::EnumeratorCallback callback, void *user); + HRESULT RemoveIcon(unsigned int width, unsigned int height); + + HRESULT AddCommand(const char *command, DeviceCommandFlags flags); + HRESULT RemoveCommand(const char *command); + HRESULT SetCommandFlags(const char *command, DeviceCommandFlags mask, DeviceCommandFlags flags); + + HRESULT IsConnected(); + HRESULT Connect(); + HRESULT Disconnect(); + + HRESULT CopyTo(Device *target); + HRESULT SetIconBase(const wchar_t *path); + + HRESULT StartSyncActivity(HWND hostWindow); + + +protected: + void Lock(); + void Unlock(); + + static void ActivityStartedCb(DeviceActivity *activity); + static void ActivityFinishedCb(DeviceActivity *activity); + static void ActivityProgressCb(DeviceActivity *activity, unsigned int progress, unsigned int duration); + + +protected: + size_t ref; + char *name; + char *type; + char *connection; + wchar_t *displayName; + wchar_t *model; + wchar_t *status; + uint64_t totalSpace; + uint64_t usedSpace; + BOOL attached; + BOOL hidden; + BOOL connected; + ifc_deviceiconstore *iconStore; + ifc_deviceeventmanager *eventManager; + ifc_devicesupportedcommandstore *commands; + DeviceActivity *activity; + CRITICAL_SECTION lock; +protected: + RECVS_DISPATCH; +}; + + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceActivity.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceActivity.cpp new file mode 100644 index 00000000..d4d6a093 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceActivity.cpp @@ -0,0 +1,519 @@ +#include "main.h" +#include "./deviceActivity.h" + +#include <strsafe.h> + +typedef struct DeviceActivityThreadParam +{ + DeviceActivity *activity; + unsigned int duration; + unsigned int interval; + HANDLE readyEvent; +}DeviceActivityThreadParam; + +DeviceActivity::DeviceActivity(DeviceActivityFlags flags, + DeviceActivityCallback startCb, DeviceActivityCallback finishCb, + DeviceActivityProgressCallback progressCb, void *user) + : ref(1), displayName(NULL), status(NULL), activityThread(FALSE), cancelEvent(NULL), progress(0) +{ + InitializeCriticalSection(&lock); + + this->flags = flags; + callbackStart = startCb; + callbackFinish = finishCb; + callbackProgress = progressCb; + this->user = user; +} + +DeviceActivity::~DeviceActivity() +{ + Stop(); + + String_Free(displayName); + String_Free(status); + + DeleteCriticalSection(&lock); +} + + +HRESULT DeviceActivity::CreateInstance(DeviceActivityFlags flags, + DeviceActivityCallback startCb, DeviceActivityCallback finishCb, + DeviceActivityProgressCallback progressCb, void *user, + DeviceActivity **instance) +{ + DeviceActivity *self; + + if (NULL == instance) + return E_POINTER; + + *instance = NULL; + + self = new DeviceActivity(flags, startCb, finishCb, progressCb, user); + if (NULL == self) + return E_OUTOFMEMORY; + + *instance = self; + return S_OK; + +} + +size_t DeviceActivity::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t DeviceActivity::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int DeviceActivity::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) + return E_POINTER; + + if (IsEqualIID(interface_guid, IFC_DeviceActivity)) + *object = static_cast<ifc_deviceactivity*>(this); + else + { + *object = NULL; + return E_NOINTERFACE; + } + + if (NULL == *object) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +void DeviceActivity::Lock() +{ + EnterCriticalSection(&lock); +} + +void DeviceActivity::Unlock() +{ + LeaveCriticalSection(&lock); +} + +BOOL DeviceActivity::GetActive() +{ + BOOL running; + + Lock(); + running = (NULL != activityThread); + Unlock(); + + return running; +} + +BOOL DeviceActivity::GetCancelable() +{ + BOOL cancelable; + + Lock(); + cancelable = (0 != (DeviceActivityFlag_Cancelable & flags)); + Unlock(); + + return cancelable; +} + +HRESULT DeviceActivity::GetProgress(unsigned int *percentCompleted) +{ + + if (NULL == percentCompleted) + return E_POINTER; + + Lock(); + + *percentCompleted = progress; + + Unlock(); + + return S_OK; +} + +HRESULT DeviceActivity::GetDisplayName(wchar_t *buffer, size_t bufferMax) +{ + HRESULT hr; + + if (NULL == buffer) + return E_POINTER; + + Lock(); + + if (0 == String_CopyTo(buffer, displayName, bufferMax) && + FALSE == IS_STRING_EMPTY(displayName)) + { + hr = E_FAIL; + } + else + hr = S_OK; + + Unlock(); + + return hr; +} + +HRESULT DeviceActivity::GetStatus(wchar_t *buffer, size_t bufferMax) +{ + HRESULT hr; + + if (NULL == buffer) + return E_POINTER; + + Lock(); + + if (0 == String_CopyTo(buffer, status, bufferMax) && + FALSE == IS_STRING_EMPTY(status)) + { + hr = E_FAIL; + } + else + hr = S_OK; + + Unlock(); + + return hr; +} + +HRESULT DeviceActivity::Cancel(HWND hostWindow) +{ + HRESULT hr; + + Lock(); + + if (0 == (DeviceActivityFlag_Cancelable & flags)) + hr = E_NOTIMPL; + else + hr = E_FAIL; + + Unlock(); + + return hr; +} + + +HRESULT DeviceActivity::Start(unsigned int duration, unsigned int interval) +{ + HRESULT hr; + + Lock(); + + if (NULL != activityThread) + hr = E_PENDING; + else + { + hr = S_OK; + + if (NULL == cancelEvent) + { + cancelEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (NULL == cancelEvent) + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + { + DeviceActivityThreadParam param; + + param.activity = this; + param.duration = duration; + param.interval = interval; + param.readyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (NULL == param.readyEvent) + hr = E_FAIL; + else + { + DWORD threadId; + + activityThread = CreateThread(NULL, 0, DeviceActivity_ActivityThreadStarter, + ¶m, 0, &threadId); + if (NULL == activityThread) + hr = E_FAIL; + else + WaitForSingleObject(param.readyEvent, INFINITE); + + CloseHandle(param.readyEvent); + } + } + + if (FAILED(hr)) + Stop(); + } + + Unlock(); + + return hr; +} + +HRESULT DeviceActivity::Stop() +{ + HRESULT hr; + HANDLE threadHandle, eventHandle; + + Lock(); + + threadHandle = activityThread; + eventHandle = cancelEvent; + + activityThread = NULL; + cancelEvent = NULL; + + Unlock(); + + if (NULL != threadHandle) + { + if (NULL != eventHandle) + SetEvent(eventHandle); + + WaitForSingleObject(threadHandle, INFINITE); + CloseHandle(threadHandle); + hr = S_OK; + } + else + hr = S_FALSE; + + if (NULL != eventHandle) + CloseHandle(eventHandle); + + return hr; +} + +HRESULT DeviceActivity::SetDisplayName(const wchar_t *name) +{ + HRESULT hr; + + Lock(); + + if (NULL == name && NULL == displayName) + hr = S_FALSE; + else + { + if (NULL != displayName && + CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, 0, name, -1, displayName, -1)) + { + hr = S_FALSE; + } + else + { + wchar_t *string; + + string = String_Duplicate(name); + if (NULL == string && NULL != name) + hr = E_FAIL; + else + { + String_Free(displayName); + displayName = string; + hr = S_OK; + } + } + } + + Unlock(); + + return hr; +} + +HRESULT DeviceActivity::SetStatus(const wchar_t *newStatus) +{ + HRESULT hr; + + if (NULL == newStatus && NULL == status) + return S_FALSE; + + Lock(); + + if (NULL != status && + CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, 0, newStatus, -1, status, -1)) + { + hr = S_FALSE; + } + else + { + wchar_t *string; + + string = String_Duplicate(newStatus); + if (NULL == string && NULL != newStatus) + hr = E_FAIL; + else + { + String_Free(status); + status = string; + hr = S_OK; + } + } + + Unlock(); + + return hr; +} + +HRESULT DeviceActivity::SetUser(void *data) +{ + Lock(); + user = data; + Unlock(); + + return S_OK; + +} +HRESULT DeviceActivity::GetUser(void **data) +{ + if (NULL == data) + return E_POINTER; + + Lock(); + + *data = user; + + Unlock(); + + return S_OK; +} + +DWORD DeviceActivity::ActivityThread(unsigned int duration, unsigned int interval) +{ + DWORD waitResult, waitTime; + HANDLE cancelEventCopy; + unsigned int position; + + if (interval > duration) + interval = duration; + + + position = 0; + + Lock(); + + progress = 0; + + if (NULL == cancelEvent || + 0 == DuplicateHandle(GetCurrentProcess(), cancelEvent, GetCurrentProcess(), + &cancelEventCopy, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + cancelEventCopy = NULL; + } + + Unlock(); + + if(NULL == cancelEventCopy) + return -3; + + Lock(); + + if (NULL != callbackStart) + callbackStart(this); + + if (NULL != callbackProgress) + callbackProgress(this, position, duration); + + Unlock(); + + for(;;) + { + waitTime = interval; + if ((position + waitTime) > duration) + waitTime = duration - position; + + waitResult = WaitForSingleObject(cancelEventCopy, waitTime); + if (WAIT_TIMEOUT == waitResult) + { + position += waitTime; + + Lock(); + + if (duration != 0) + { + progress = 100 * position / duration; + if (progress > 100) + progress = 100; + } + else + progress = 100; + + + if (NULL != callbackProgress) + callbackProgress(this, position, duration); + Unlock(); + + if (position >= duration) + break; + } + else + break; + } + + AddRef(); + + Lock(); + + if (NULL != activityThread) + { + CloseHandle(activityThread); + activityThread = NULL; + } + + if (NULL != cancelEvent) + { + CloseHandle(cancelEvent); + cancelEvent = NULL; + } + + if (NULL != callbackFinish) + callbackFinish(this); + + Unlock(); + + Release(); + + return 0; +} + +static DWORD CALLBACK +DeviceActivity_ActivityThreadStarter(void *user) +{ + DeviceActivityThreadParam *param; + DeviceActivity *activity; + unsigned int duration, interval; + DWORD result; + + param = (DeviceActivityThreadParam*)user; + activity = param->activity; + duration = param->duration; + interval = param->interval; + + if (NULL != param->readyEvent) + SetEvent(param->readyEvent); + + if (NULL != activity) + result = activity->ActivityThread(duration, interval); + else + result = -2; + + return result; +} + +#define CBCLASS DeviceActivity +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +CB(API_GETACTIVE, GetActive) +CB(API_GETCANCELABLE, GetCancelable) +CB(API_GETPROGRESS, GetProgress) +CB(API_GETDISPLAYNAME, GetDisplayName) +CB(API_GETSTATUS, GetStatus) +CB(API_CANCEL, Cancel) +END_DISPATCH; +#undef CBCLASS diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceActivity.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceActivity.h new file mode 100644 index 00000000..f523ac55 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceActivity.h @@ -0,0 +1,91 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_ACTIVITY_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_ACTIVITY_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +typedef enum DeviceActivityFlags +{ + DeviceActivityFlag_Cancelable = (1 << 0), + DeviceActivityFlag_SupportProgress = (1 << 0), +} DeviceActivityFlags; +DEFINE_ENUM_FLAG_OPERATORS(DeviceActivityFlags); + +typedef void (*DeviceActivityCallback)(DeviceActivity * /*activity*/); +typedef void (*DeviceActivityProgressCallback)(DeviceActivity * /*activity*/, unsigned int /*position*/, unsigned int /*total*/); + + +class DeviceActivity: public ifc_deviceactivity +{ + +protected: + DeviceActivity(DeviceActivityFlags flags, + DeviceActivityCallback startCb, + DeviceActivityCallback finishCb, + DeviceActivityProgressCallback progressCb, + void *user); + + ~DeviceActivity(); +public: + static HRESULT CreateInstance(DeviceActivityFlags flags, + DeviceActivityCallback startCb, + DeviceActivityCallback finishCb, + DeviceActivityProgressCallback progressCb, + void *user, + DeviceActivity **instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /* ifc_deviceactivity */ + BOOL GetActive(); + BOOL GetCancelable(); + HRESULT GetProgress(unsigned int *percentCompleted); + HRESULT GetDisplayName(wchar_t *buffer, size_t bufferMax); + HRESULT GetStatus(wchar_t *buffer, size_t bufferMax); + HRESULT Cancel(HWND hostWindow); + +public: + void Lock(); + void Unlock(); + + HRESULT Start(unsigned int duration, unsigned int interval); + HRESULT Stop(); + + HRESULT SetDisplayName(const wchar_t *displayName); + HRESULT SetStatus(const wchar_t *status); + + HRESULT SetUser(void *data); + HRESULT GetUser(void **data); + +protected: + DWORD ActivityThread(unsigned int duration, unsigned int interval); + friend static DWORD CALLBACK DeviceActivity_ActivityThreadStarter(void *param); + +protected: + size_t ref; + DeviceActivityFlags flags; + DeviceActivityCallback callbackStart; + DeviceActivityCallback callbackFinish; + DeviceActivityProgressCallback callbackProgress; + void *user; + wchar_t *displayName; + wchar_t *status; + HANDLE activityThread; + HANDLE cancelEvent; + unsigned int progress; + CRITICAL_SECTION lock; + + +protected: + RECVS_DISPATCH; +}; + + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_ACTIVITY_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandNodeParser.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandNodeParser.cpp new file mode 100644 index 00000000..8c55b6e2 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandNodeParser.cpp @@ -0,0 +1,75 @@ +#include "main.h" +#include "./DeviceCommandNodeParser.h" +#include "../../xml/obj_xml.h" + +DeviceCommandNodeParser::DeviceCommandNodeParser() + : reader(NULL), test(NULL) +{ +} + +DeviceCommandNodeParser::~DeviceCommandNodeParser() +{ + End(); +} + + +BOOL DeviceCommandNodeParser::Begin(obj_xml *xmlReader, TestSuite *testSuite) +{ + if (NULL != reader || NULL != test) + return FALSE; + + if (NULL == xmlReader || NULL == testSuite) + return FALSE; + + reader = xmlReader; + reader->AddRef(); + + test = testSuite; + + reader->xmlreader_registerCallback(L"testprovider\fcommands\fcommand", this); + + return TRUE; +} + +void DeviceCommandNodeParser::End() +{ + if (NULL != reader) + { + reader->xmlreader_unregisterCallback(this); + reader->Release(); + reader = NULL; + } + + if (NULL != test) + test = NULL; +} + + +void DeviceCommandNodeParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementParser.Begin(reader, params); +} + +void DeviceCommandNodeParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + ifc_devicecommand *result; + if (FALSE != elementParser.End(reader, &result)) + { + if (NULL != test) + test->AddCommand(result); + + result->Release(); + } +} + +void DeviceCommandNodeParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ +} + +#define CBCLASS DeviceCommandNodeParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandNodeParser.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandNodeParser.h new file mode 100644 index 00000000..5b4b4287 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandNodeParser.h @@ -0,0 +1,39 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_COMMAND_NODE_PARSER_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_COMMAND_NODE_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./deviceCommandParser.h" + +class obj_xml; + +class DeviceCommandNodeParser : public ifc_xmlreadercallback +{ + +public: + DeviceCommandNodeParser(); + ~DeviceCommandNodeParser(); + +public: + BOOL Begin(obj_xml *xmlReader, TestSuite *testSuite); + void End(); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + obj_xml *reader; + DeviceCommandParser elementParser; + TestSuite *test; + +protected: + RECVS_DISPATCH; +}; + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_COMMAND_NODE_PARSER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandParser.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandParser.cpp new file mode 100644 index 00000000..0bfd0365 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandParser.cpp @@ -0,0 +1,192 @@ +#include "main.h" +#include "./deviceCommandParser.h" + +#include "../../xml/obj_xml.h" + +typedef void (*COMMANDTAGCALLBACK)(DeviceCommandParser* /*self*/, ifc_devicecommandeditor* /*editor*/, const wchar_t* /*value*/); + +typedef struct COMMANDTAG +{ + const wchar_t *name; + BOOL multiEntry; + COMMANDTAGCALLBACK callback; +} COMMANDTAG; + +static void +DeviceCommandParser_DisplayNameCb(DeviceCommandParser *self, ifc_devicecommandeditor *editor, const wchar_t *value) +{ + editor->SetDisplayName(value); +} + +static void +DeviceCommandParser_IconCb(DeviceCommandParser *self, ifc_devicecommandeditor *editor, const wchar_t *value) +{ + ifc_deviceiconstore *iconStore; + if (SUCCEEDED(editor->GetIconStore(&iconStore))) + { + iconStore->Add(value, self->iconSize.cx, self->iconSize.cy, TRUE); + iconStore->Release(); + } +} + +static void +DeviceCommandParser_DescirptionCb(DeviceCommandParser *self, ifc_devicecommandeditor *editor, const wchar_t *value) +{ + editor->SetDescription(value); +} + +static const COMMANDTAG knownTags[COMMAND_TAG_MAX] = +{ + {L"displayName", FALSE, DeviceCommandParser_DisplayNameCb}, + {L"icon", TRUE, DeviceCommandParser_IconCb}, + {L"description", FALSE, DeviceCommandParser_DescirptionCb}, +}; + +DeviceCommandParser::DeviceCommandParser() + : editor(NULL) +{ +} + +DeviceCommandParser::~DeviceCommandParser() +{ + if (NULL != editor) + editor->Release(); +} + +BOOL DeviceCommandParser::Begin(obj_xml *reader, ifc_xmlreaderparams *params) +{ + const wchar_t *name; + ifc_devicecommand *command; + char *nameAnsi; + + if (NULL != editor) + return FALSE; + + if (NULL == reader || NULL == params) + return FALSE; + + name = params->getItemValue(L"name"); + if (NULL == name) + return FALSE; + + nameAnsi = String_ToAnsi(CP_UTF8, 0, name, -1, NULL, NULL); + if (NULL == nameAnsi) + return FALSE; + + if (NULL != WASABI_API_DEVICES && + SUCCEEDED(WASABI_API_DEVICES->CreateCommand(nameAnsi, &command))) + { + if(FAILED(command->QueryInterface(IFC_DeviceCommandEditor, (void**)&editor))) + editor = NULL; + + command->Release(); + } + + AnsiString_Free(nameAnsi); + + if (NULL == editor) + return FALSE; + + reader->xmlreader_registerCallback(L"testprovider\fcommands\fcommand\fdisplayName", this); + reader->xmlreader_registerCallback(L"testprovider\fcommands\fcommand\fdescription", this); + reader->xmlreader_registerCallback(L"testprovider\fcommands\fcommand\ficon", this); + ZeroMemory(hitList, sizeof(hitList)); + return TRUE; +} + +BOOL DeviceCommandParser::End(obj_xml *reader, ifc_devicecommand **command) +{ + BOOL result; + + if (NULL != reader) + reader->xmlreader_unregisterCallback(this); + + if (NULL == command) + return FALSE; + + if (NULL == editor) + return FALSE; + + if (NULL != command) + { + if (FAILED(editor->QueryInterface(IFC_DeviceCommand, (void**)command))) + result = FALSE; + else + result = TRUE; + } + else + result = TRUE; + + editor->Release(); + editor = NULL; + + return result; +} + +void DeviceCommandParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementString.Clear(); + + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, L"icon", -1, xmltag, -1)) + { + const wchar_t *sVal; + int iVal; + + sVal = params->getItemValue(L"width"); + if (NULL == sVal || + FALSE == StrToIntEx(sVal, STIF_DEFAULT, &iVal)) + { + iVal = 0; + } + + iconSize.cx = iVal; + + sVal = params->getItemValue(L"height"); + if (NULL == sVal || + FALSE == StrToIntEx(sVal, STIF_DEFAULT, &iVal)) + { + iVal = 0; + } + + iconSize.cy = iVal; + } +} + +void DeviceCommandParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + if (NULL == editor) + return; + + for (size_t i = 0; i < COMMAND_TAG_MAX; i++) + { + if (FALSE == hitList[i] && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, knownTags[i].name, -1, xmltag, -1)) + { + knownTags[i].callback(this, editor, elementString.Get()); + + if (FALSE == knownTags[i].multiEntry) + hitList[i] = TRUE; + + break; + } + } +} + +void DeviceCommandParser::Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value) +{ + elementString.Append(value); +} + +void DeviceCommandParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + elementString.Clear(); +} + +#define CBCLASS DeviceCommandParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONCHARDATA, Event_XmlCharData) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandParser.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandParser.h new file mode 100644 index 00000000..2bd8151e --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandParser.h @@ -0,0 +1,47 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_COMMAND_PARSER_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_COMMAND_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" + +class obj_xml; + +#define COMMAND_TAG_MAX 3 + +class DeviceCommandParser : public ifc_xmlreadercallback +{ +public: + DeviceCommandParser(); + ~DeviceCommandParser(); +public: + BOOL Begin(obj_xml *reader, ifc_xmlreaderparams *params); + BOOL End(obj_xml *reader, ifc_devicecommand **command); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + friend static void DeviceCommandParser_DisplayNameCb(DeviceCommandParser *self, ifc_devicecommandeditor *editor, const wchar_t *value); + friend static void DeviceCommandParser_IconCb(DeviceCommandParser *self, ifc_devicecommandeditor *editor, const wchar_t *value); + friend static void DeviceCommandParser_DescirptionCb(DeviceCommandParser *self, ifc_devicecommandeditor *editor, const wchar_t *value); + +protected: + StringBuilder elementString; + ifc_devicecommandeditor *editor; + BOOL hitList[COMMAND_TAG_MAX]; + SIZE iconSize; + +protected: + RECVS_DISPATCH; + +}; + + +#endif // _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_COMMAND_PARSER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionNodeParser.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionNodeParser.cpp new file mode 100644 index 00000000..a36f592a --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionNodeParser.cpp @@ -0,0 +1,75 @@ +#include "main.h" +#include "./DeviceConnectionNodeParser.h" +#include "../../xml/obj_xml.h" + +DeviceConnectionNodeParser::DeviceConnectionNodeParser() + : reader(NULL), test(NULL) +{ +} + +DeviceConnectionNodeParser::~DeviceConnectionNodeParser() +{ + End(); +} + + +BOOL DeviceConnectionNodeParser::Begin(obj_xml *xmlReader, TestSuite *testSuite) +{ + if (NULL != reader || NULL != test) + return FALSE; + + if (NULL == xmlReader || NULL == testSuite) + return FALSE; + + reader = xmlReader; + reader->AddRef(); + + test = testSuite; + + reader->xmlreader_registerCallback(L"testprovider\fconnections\fconnection", this); + + return TRUE; +} + +void DeviceConnectionNodeParser::End() +{ + if (NULL != reader) + { + reader->xmlreader_unregisterCallback(this); + reader->Release(); + reader = NULL; + } + + if (NULL != test) + test = NULL; +} + + +void DeviceConnectionNodeParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementParser.Begin(reader, params); +} + +void DeviceConnectionNodeParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + ifc_deviceconnection *result; + if (FALSE != elementParser.End(reader, &result)) + { + if (NULL != test) + test->AddConnection(result); + + result->Release(); + } +} + +void DeviceConnectionNodeParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ +} + +#define CBCLASS DeviceConnectionNodeParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionNodeParser.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionNodeParser.h new file mode 100644 index 00000000..8c340f5b --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionNodeParser.h @@ -0,0 +1,39 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_CONNECTION_NODE_PARSER_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_CONNECTION_NODE_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./deviceConnectionParser.h" + +class obj_xml; + +class DeviceConnectionNodeParser : public ifc_xmlreadercallback +{ + +public: + DeviceConnectionNodeParser(); + ~DeviceConnectionNodeParser(); + +public: + BOOL Begin(obj_xml *xmlReader, TestSuite *testSuite); + void End(); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + obj_xml *reader; + DeviceConnectionParser elementParser; + TestSuite *test; + +protected: + RECVS_DISPATCH; +}; + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_CONNECTION_NODE_PARSER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionParser.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionParser.cpp new file mode 100644 index 00000000..daaf0fc4 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionParser.cpp @@ -0,0 +1,181 @@ +#include "main.h" +#include "./deviceConnectionParser.h" + +#include "../../xml/obj_xml.h" + +typedef void (*CONNECTIONTAGCALLBACK)(DeviceConnectionParser* /*self*/, ifc_deviceconnectioneditor * /*editor*/, const wchar_t* /*value*/); + +typedef struct CONNECTIONTAG +{ + const wchar_t *name; + BOOL multiEntry; + CONNECTIONTAGCALLBACK callback; +} CONNECTIONTAG; + +static void +DeviceConnectionParser_DisplayNameCb(DeviceConnectionParser *self, ifc_deviceconnectioneditor *editor, const wchar_t *value) +{ + editor->SetDisplayName(value); +} + +static void +DeviceConnectionParser_IconCb(DeviceConnectionParser *self, ifc_deviceconnectioneditor *editor, const wchar_t *value) +{ + ifc_deviceiconstore *iconStore; + if (SUCCEEDED(editor->GetIconStore(&iconStore))) + { + iconStore->Add(value, self->iconSize.cx, self->iconSize.cy, TRUE); + iconStore->Release(); + } +} + +static const CONNECTIONTAG knownTags[CONNECTION_TAG_MAX] = +{ + {L"displayName", FALSE, DeviceConnectionParser_DisplayNameCb}, + {L"icon", TRUE, DeviceConnectionParser_IconCb}, +}; + +DeviceConnectionParser::DeviceConnectionParser() + : editor(NULL) +{ +} + +DeviceConnectionParser::~DeviceConnectionParser() +{ + if (NULL != editor) + editor->Release(); +} + +BOOL DeviceConnectionParser::Begin(obj_xml *reader, ifc_xmlreaderparams *params) +{ + const wchar_t *name; + ifc_deviceconnection *connection; + char *nameAnsi; + + if (NULL != editor) + return FALSE; + + if (NULL == reader || NULL == params) + return FALSE; + + + name = params->getItemValue(L"name"); + if (NULL == name) + return FALSE; + + nameAnsi = String_ToAnsi(CP_UTF8, 0, name, -1, NULL, NULL); + if (NULL == nameAnsi) + return FALSE; + + if (NULL != WASABI_API_DEVICES && + SUCCEEDED(WASABI_API_DEVICES->CreateConnection(nameAnsi, &connection))) + { + if(FAILED(connection->QueryInterface(IFC_DeviceConnectionEditor, (void**)&editor))) + editor = NULL; + + connection->Release(); + } + AnsiString_Free(nameAnsi); + + if (NULL == editor) + return FALSE; + + reader->xmlreader_registerCallback(L"testprovider\fconnections\fconnection\fdisplayName", this); + reader->xmlreader_registerCallback(L"testprovider\fconnections\fconnection\ficon", this); + ZeroMemory(hitList, sizeof(hitList)); + return TRUE; +} + +BOOL DeviceConnectionParser::End(obj_xml *reader, ifc_deviceconnection **connection) +{ + BOOL result; + + if (NULL != reader) + reader->xmlreader_unregisterCallback(this); + + if (NULL == editor) + return FALSE; + + if (NULL != connection) + { + if (FAILED(editor->QueryInterface(IFC_DeviceConnection, (void**)connection))) + result = FALSE; + else + result = TRUE; + } + else + result = TRUE; + + editor->Release(); + editor = NULL; + + return result; +} + +void DeviceConnectionParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementString.Clear(); + + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, L"icon", -1, xmltag, -1)) + { + const wchar_t *sVal; + int iVal; + + sVal = params->getItemValue(L"width"); + if (NULL == sVal || + FALSE == StrToIntEx(sVal, STIF_DEFAULT, &iVal)) + { + iVal = 0; + } + + iconSize.cx = iVal; + + sVal = params->getItemValue(L"height"); + if (NULL == sVal || + FALSE == StrToIntEx(sVal, STIF_DEFAULT, &iVal)) + { + iVal = 0; + } + + iconSize.cy = iVal; + } +} + +void DeviceConnectionParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + if (NULL == editor) + return; + + for (size_t i = 0; i < CONNECTION_TAG_MAX; i++) + { + if (FALSE == hitList[i] && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, knownTags[i].name, -1, xmltag, -1)) + { + knownTags[i].callback(this, editor, elementString.Get()); + + if (FALSE == knownTags[i].multiEntry) + hitList[i] = TRUE; + + break; + } + } +} + +void DeviceConnectionParser::Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value) +{ + elementString.Append(value); +} + +void DeviceConnectionParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + elementString.Clear(); +} + +#define CBCLASS DeviceConnectionParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONCHARDATA, Event_XmlCharData) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionParser.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionParser.h new file mode 100644 index 00000000..9b8da274 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionParser.h @@ -0,0 +1,46 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_CONNECTION_PARSER_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_CONNECTION_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" + +class obj_xml; + +#define CONNECTION_TAG_MAX 2 + +class DeviceConnectionParser : public ifc_xmlreadercallback +{ +public: + DeviceConnectionParser(); + ~DeviceConnectionParser(); +public: + BOOL Begin(obj_xml *reader, ifc_xmlreaderparams *params); + BOOL End(obj_xml *reader, ifc_deviceconnection **connection); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + friend static void DeviceConnectionParser_DisplayNameCb(DeviceConnectionParser *self, ifc_deviceconnectioneditor *editor, const wchar_t *value); + friend static void DeviceConnectionParser_IconCb(DeviceConnectionParser *self, ifc_deviceconnectioneditor *editor, const wchar_t *value); + +protected: + StringBuilder elementString; + ifc_deviceconnectioneditor *editor; + BOOL hitList[CONNECTION_TAG_MAX]; + SIZE iconSize; + +protected: + RECVS_DISPATCH; + +}; + + +#endif // _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_CONNECTION_PARSER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceIconEditor.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceIconEditor.cpp new file mode 100644 index 00000000..41344276 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceIconEditor.cpp @@ -0,0 +1,178 @@ +#include "main.h" +#include "./deviceIconEditor.h" + + +#define DEVICEICONEDITOR_PROP L"NullsoftDevicesIconEditorProp" + + +static INT_PTR +DeviceIconEditor_DialogProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam); + +INT_PTR +DeviceIconEditor_Show(HWND parentWindow, DeviceIconInfo *iconInfo) +{ + if (NULL == iconInfo) + return -1; + + return WASABI_API_DIALOGBOXPARAMW((INT_PTR)IDD_ICON_EDITOR, parentWindow, + DeviceIconEditor_DialogProc, (LPARAM)iconInfo); + +} + +static void +DeviceIconEditor_UpdateInfo(HWND hwnd) +{ + DeviceIconInfo *iconInfo; + HWND controlWindow; + wchar_t *string; + + iconInfo = (DeviceIconInfo*)GetProp(hwnd, DEVICEICONEDITOR_PROP); + if (NULL == iconInfo) + return; + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_PATH); + if (NULL != controlWindow) + { + String_Free(iconInfo->path); + iconInfo->path = String_FromWindow(controlWindow); + } + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_WIDTH); + if (NULL != controlWindow) + { + string = String_FromWindow(controlWindow); + if (NULL == string || + FALSE == StrToIntEx(string, STIF_DEFAULT, &iconInfo->width)) + { + iconInfo->width = 0; + } + String_Free(string); + } + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_HEIGHT); + if (NULL != controlWindow) + { + string = String_FromWindow(controlWindow); + if (NULL == string || + FALSE == StrToIntEx(string, STIF_DEFAULT, &iconInfo->height)) + { + iconInfo->height = 0; + } + String_Free(string); + } +} + +static INT_PTR +DeviceIconEditor_OnInitDialog(HWND hwnd, HWND focusWindow, LPARAM param) +{ + DeviceIconInfo *iconInfo; + HWND controlWindow; + + iconInfo = (DeviceIconInfo*)param; + SetProp(hwnd, DEVICEICONEDITOR_PROP, iconInfo); + + if (NULL != iconInfo) + { + wchar_t buffer[64]; + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_PATH); + if (NULL != controlWindow) + SetWindowText(controlWindow, iconInfo->path); + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_WIDTH); + if (NULL != controlWindow) + { + _itow_s(iconInfo->width, buffer, 10); + SetWindowText(controlWindow, buffer); + } + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_HEIGHT); + if (NULL != controlWindow) + { + _itow_s(iconInfo->height, buffer, 10); + SetWindowText(controlWindow, buffer); + } + + } + return 0; +} + +static void +DeviceIconEditor_DisplayFileOpen(HWND hwnd) +{ + wchar_t buffer[MAX_PATH * 2]; + OPENFILENAME ofn; + HWND controlWindow; + + buffer[0] = L'\0'; + + ZeroMemory(&ofn, sizeof(ofn)); + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hwnd; + ofn.lpstrFilter = L"Portable Network Graphics\0" L"*.png\0" + L"\0"; + ofn.lpstrFile = buffer; + ofn.nMaxFile = ARRAYSIZE(buffer); + ofn.lpstrTitle = L"Load Icon"; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + + if (FALSE == GetOpenFileName(&ofn)) + return; + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_PATH); + if (NULL != controlWindow) + SetWindowText(controlWindow, buffer); +} + +static void +DeviceIconEditor_OnDestroy(HWND hwnd) +{ + RemoveProp(hwnd, DEVICEICONEDITOR_PROP); +} + +static void +DeviceIconEditor_OnCommand(HWND hwnd, INT commandId, INT eventId, HWND controlWindow) +{ + switch(commandId) + { + case IDOK: + switch(eventId) + { + case BN_CLICKED: + DeviceIconEditor_UpdateInfo(hwnd); + EndDialog(hwnd, IDOK); + break; + } + break; + case IDCANCEL: + switch(eventId) + { + case BN_CLICKED: + EndDialog(hwnd, IDCANCEL); + break; + } + break; + case IDC_BUTTON_BROWSE: + switch(eventId) + { + case BN_CLICKED: + DeviceIconEditor_DisplayFileOpen(hwnd); + break; + } + break; + + } +} + +static INT_PTR +DeviceIconEditor_DialogProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: return DeviceIconEditor_OnInitDialog(hwnd, (HWND)wParam, lParam); + case WM_DESTROY: DeviceIconEditor_OnDestroy(hwnd); return TRUE; + case WM_COMMAND: DeviceIconEditor_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); return TRUE; + } + return 0; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceIconEditor.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceIconEditor.h new file mode 100644 index 00000000..c08da395 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceIconEditor.h @@ -0,0 +1,22 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_ICON_EDITOR_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_ICON_EDITOR_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +typedef struct DeviceIconInfo +{ + int width; + int height; + wchar_t *path; +} DeviceIconInfo; + +INT_PTR +DeviceIconEditor_Show(HWND parentWindow, + DeviceIconInfo *iconInfo); + + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_ICON_EDITOR_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceNodeParser.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceNodeParser.cpp new file mode 100644 index 00000000..6582d0c1 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceNodeParser.cpp @@ -0,0 +1,75 @@ +#include "main.h" +#include "./DeviceNodeParser.h" +#include "../../xml/obj_xml.h" + +DeviceNodeParser::DeviceNodeParser() + : reader(NULL), test(NULL) +{ +} + +DeviceNodeParser::~DeviceNodeParser() +{ + End(); +} + + +BOOL DeviceNodeParser::Begin(obj_xml *xmlReader, TestSuite *testSuite) +{ + if (NULL != reader || NULL != test) + return FALSE; + + if (NULL == xmlReader || NULL == testSuite) + return FALSE; + + reader = xmlReader; + reader->AddRef(); + + test = testSuite; + + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice", this); + + return TRUE; +} + +void DeviceNodeParser::End() +{ + if (NULL != reader) + { + reader->xmlreader_unregisterCallback(this); + reader->Release(); + reader = NULL; + } + + if (NULL != test) + test = NULL; +} + + +void DeviceNodeParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementParser.Begin(reader, params); +} + +void DeviceNodeParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + Device *result; + if (FALSE != elementParser.End(reader, &result)) + { + if (NULL != test) + test->AddDevice(result); + + result->Release(); + } +} + +void DeviceNodeParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ +} + +#define CBCLASS DeviceNodeParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceNodeParser.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceNodeParser.h new file mode 100644 index 00000000..667f0e14 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceNodeParser.h @@ -0,0 +1,39 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_NODE_PARSER_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_NODE_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./deviceParser.h" + +class obj_xml; + +class DeviceNodeParser : public ifc_xmlreadercallback +{ + +public: + DeviceNodeParser(); + ~DeviceNodeParser(); + +public: + BOOL Begin(obj_xml *xmlReader, TestSuite *testSuite); + void End(); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + obj_xml *reader; + DeviceParser elementParser; + TestSuite *test; + +protected: + RECVS_DISPATCH; +}; + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_NODE_PARSER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceParser.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceParser.cpp new file mode 100644 index 00000000..d844641c --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceParser.cpp @@ -0,0 +1,306 @@ +#include "main.h" +#include "./deviceParser.h" + +#include "../../xml/obj_xml.h" + +typedef void (*DEVICETAGCALLBACK)(DeviceParser* /*self*/, Device* /*device*/, const wchar_t* /*value*/); + +typedef struct DEVICETAG +{ + const wchar_t *name; + BOOL multiEntry; + DEVICETAGCALLBACK callback; +} DEVICETAG; + + +static void +DeviceParser_ConnectionCb(DeviceParser *self, Device *device, const wchar_t *value) +{ + char *connectionAnsi; + + connectionAnsi = (NULL != value) ? + String_ToAnsi(CP_UTF8, 0, value, -1, NULL, NULL) : + NULL; + + device->SetConnection(connectionAnsi); + AnsiString_Free(connectionAnsi); +} + + +static void +DeviceParser_DisplayNameCb(DeviceParser *self, Device *device, const wchar_t *value) +{ + device->SetDisplayName(value); +} + +static void +DeviceParser_IconCb(DeviceParser *self, Device *device, const wchar_t *value) +{ + device->AddIcon(value, self->iconSize.cx, self->iconSize.cy); +} + +static void +DeviceParser_TotalSpaceCb(DeviceParser *self, Device *device, const wchar_t *value) +{ + size_t size; + + if (NULL == value) + size = -1; + else + { + LONGLONG lval; + if (FALSE == StrToInt64Ex(value, STIF_DEFAULT, &lval)) + return; + + size = (size_t)lval; + } + device->SetTotalSpace(size); +} + +static void +DeviceParser_UsedSpaceCb(DeviceParser *self, Device *device, const wchar_t *value) +{ + size_t size; + + if (NULL == value) + size = -1; + else + { + LONGLONG lval; + if (FALSE == StrToInt64Ex(value, STIF_DEFAULT, &lval)) + return; + + size = (size_t)lval; + } + device->SetUsedSpace(size); +} + +static void +DeviceParser_HiddenCb(DeviceParser *self, Device *device, const wchar_t *value) +{ + int hidden; + if (FALSE != StrToIntEx(value, STIF_DEFAULT, &hidden)) + device->SetHidden(0 != hidden); +} + +static void +DeviceParser_CommandCb(DeviceParser *self, Device *device, const wchar_t *value) +{ + char *command; + + if (FALSE != IS_STRING_EMPTY(value)) + return; + command = String_ToAnsi(CP_UTF8, 0, value, -1, NULL, NULL); + if (NULL != command) + { + device->AddCommand(command, self->commandFlags); + AnsiString_Free(command); + } +} + + +static void +DeviceParser_ModelCb(DeviceParser *self, Device *device, const wchar_t *value) +{ + device->SetModel(value); +} + +static void +DeviceParser_StatusCb(DeviceParser *self, Device *device, const wchar_t *value) +{ + device->SetStatus(value); +} + +static const DEVICETAG knownTags[DEVICE_TAG_MAX] = +{ + {L"connection", FALSE, DeviceParser_ConnectionCb}, + {L"displayName", FALSE, DeviceParser_DisplayNameCb}, + {L"icon", TRUE, DeviceParser_IconCb}, + {L"totalSpace", FALSE, DeviceParser_TotalSpaceCb}, + {L"usedSpace", FALSE, DeviceParser_UsedSpaceCb}, + {L"hidden", FALSE, DeviceParser_HiddenCb}, + {L"command", TRUE, DeviceParser_CommandCb}, + {L"model", FALSE, DeviceParser_ModelCb}, + {L"status", FALSE, DeviceParser_StatusCb}, + +}; + +DeviceParser::DeviceParser() + : device(NULL) +{ +} + +DeviceParser::~DeviceParser() +{ + if (NULL != device) + device->Release(); +} + +BOOL DeviceParser::Begin(obj_xml *reader, ifc_xmlreaderparams *params) +{ + const wchar_t *value; + char *nameAnsi, *typeAnsi; + if (NULL != device) + return FALSE; + + if (NULL == reader || NULL == params) + return FALSE; + + value = params->getItemValue(L"name"); + nameAnsi = (NULL != value) ? String_ToAnsi(CP_UTF8, 0, value, -1, NULL, NULL) : NULL; + + value = params->getItemValue(L"type"); + typeAnsi = (NULL != value) ? String_ToAnsi(CP_UTF8, 0, value, -1, NULL, NULL) : NULL; + + if (NULL == nameAnsi || + NULL == typeAnsi || + FAILED(Device::CreateInstance(nameAnsi, typeAnsi, NULL, &device))) + { + device = NULL; + } + + AnsiString_Free(nameAnsi); + AnsiString_Free(typeAnsi); + + if (NULL == device) + return FALSE; + + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice\fconnection", this); + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice\fdisplayName", this); + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice\ficon", this); + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice\ftotalSpace", this); + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice\fusedSpace", this); + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice\fhidden", this); + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice\fcommand", this); + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice\fmodel", this); + reader->xmlreader_registerCallback(L"testprovider\fdevices\fdevice\fstatus", this); + ZeroMemory(hitList, sizeof(hitList)); + return TRUE; +} + +BOOL DeviceParser::End(obj_xml *reader, Device **result) +{ + if (NULL != reader) + reader->xmlreader_unregisterCallback(this); + + if (NULL == device) + return FALSE; + + if (NULL != result) + { + *result = device; + device->AddRef(); + } + + device->Release(); + device = NULL; + + return TRUE; +} + +void DeviceParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementString.Clear(); + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, L"icon", -1, xmltag, -1)) + { + const wchar_t *sVal; + int iVal; + + sVal = params->getItemValue(L"width"); + if (NULL == sVal || + FALSE == StrToIntEx(sVal, STIF_DEFAULT, &iVal)) + { + iVal = 0; + } + + iconSize.cx = iVal; + + sVal = params->getItemValue(L"height"); + if (NULL == sVal || + FALSE == StrToIntEx(sVal, STIF_DEFAULT, &iVal)) + { + iVal = 0; + } + + iconSize.cy = iVal; + } + else if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, L"command", -1, xmltag, -1)) + { + const wchar_t *sVal; + int iVal; + + commandFlags = DeviceCommandFlag_None; + + sVal = params->getItemValue(L"primary"); + if (FALSE == IS_STRING_EMPTY(sVal) && + FALSE != StrToIntEx(sVal, STIF_DEFAULT, &iVal) && + 0 != iVal) + { + commandFlags |= DeviceCommandFlag_Primary; + } + + sVal = params->getItemValue(L"group"); + if (FALSE == IS_STRING_EMPTY(sVal) && + FALSE != StrToIntEx(sVal, STIF_DEFAULT, &iVal) && + 0 != iVal) + { + commandFlags |= DeviceCommandFlag_Group; + } + + sVal = params->getItemValue(L"disabled"); + if (FALSE == IS_STRING_EMPTY(sVal) && + FALSE != StrToIntEx(sVal, STIF_DEFAULT, &iVal) && + 0 != iVal) + { + commandFlags |= DeviceCommandFlag_Disabled; + } + + sVal = params->getItemValue(L"hidden"); + if (FALSE == IS_STRING_EMPTY(sVal) && + FALSE != StrToIntEx(sVal, STIF_DEFAULT, &iVal) && + 0 != iVal) + { + commandFlags |= DeviceCommandFlag_Hidden; + } + } + +} + +void DeviceParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + if (NULL == device) + return; + + for (size_t i = 0; i < DEVICE_TAG_MAX; i++) + { + if (FALSE == hitList[i] && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, knownTags[i].name, -1, xmltag, -1)) + { + knownTags[i].callback(this, device, elementString.Get()); + + if (FALSE == knownTags[i].multiEntry) + hitList[i] = TRUE; + + break; + } + } +} + +void DeviceParser::Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value) +{ + elementString.Append(value); +} + +void DeviceParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + elementString.Clear(); +} + +#define CBCLASS DeviceParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONCHARDATA, Event_XmlCharData) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceParser.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceParser.h new file mode 100644 index 00000000..26164690 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceParser.h @@ -0,0 +1,54 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_PARSER_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./device.h" + +class obj_xml; + +#define DEVICE_TAG_MAX 9 + +class DeviceParser : public ifc_xmlreadercallback +{ +public: + DeviceParser(); + ~DeviceParser(); +public: + BOOL Begin(obj_xml *reader, ifc_xmlreaderparams *params); + BOOL End(obj_xml *reader, Device **result); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); +protected: + friend static void DeviceParser_IconCb(DeviceParser *self, Device *device, const wchar_t *value); + friend static void DeviceParser_ConnectionCb(DeviceParser *self, Device *device, const wchar_t *value); + friend static void DeviceParser_DisplayNameCb(DeviceParser *self, Device *device, const wchar_t *value); + friend static void DeviceParser_TotalSpaceCb(DeviceParser *self, Device *device, const wchar_t *value); + friend static void DeviceParser_UsedSpaceCb(DeviceParser *self, Device *device, const wchar_t *value); + friend static void DeviceParser_HiddenCb(DeviceParser *self, Device *device, const wchar_t *value); + friend static void DeviceParser_CommandCb(DeviceParser *self, Device *device, const wchar_t *value); + friend static void DeviceParser_ModelCb(DeviceParser *self, Device *device, const wchar_t *value); + friend static void DeviceParser_StatusCb(DeviceParser *self, Device *device, const wchar_t *value); + +protected: + StringBuilder elementString; + Device *device; + BOOL hitList[DEVICE_TAG_MAX]; + SIZE iconSize; + DeviceCommandFlags commandFlags; + +protected: + RECVS_DISPATCH; + +}; + + +#endif // _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_PARSER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeNodeParser.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeNodeParser.cpp new file mode 100644 index 00000000..11f598ea --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeNodeParser.cpp @@ -0,0 +1,75 @@ +#include "main.h" +#include "./DeviceTypeNodeParser.h" +#include "../../xml/obj_xml.h" + +DeviceTypeNodeParser::DeviceTypeNodeParser() + : reader(NULL), test(NULL) +{ +} + +DeviceTypeNodeParser::~DeviceTypeNodeParser() +{ + End(); +} + + +BOOL DeviceTypeNodeParser::Begin(obj_xml *xmlReader, TestSuite *testSuite) +{ + if (NULL != reader || NULL != test) + return FALSE; + + if (NULL == xmlReader || NULL == testSuite) + return FALSE; + + reader = xmlReader; + reader->AddRef(); + + test = testSuite; + + reader->xmlreader_registerCallback(L"testprovider\ftypes\ftype", this); + + return TRUE; +} + +void DeviceTypeNodeParser::End() +{ + if (NULL != reader) + { + reader->xmlreader_unregisterCallback(this); + reader->Release(); + reader = NULL; + } + + if (NULL != test) + test = NULL; +} + + +void DeviceTypeNodeParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementParser.Begin(reader, params); +} + +void DeviceTypeNodeParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + ifc_devicetype *result; + if (FALSE != elementParser.End(reader, &result)) + { + if (NULL != test) + test->AddType(result); + + result->Release(); + } +} + +void DeviceTypeNodeParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ +} + +#define CBCLASS DeviceTypeNodeParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeNodeParser.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeNodeParser.h new file mode 100644 index 00000000..f464c213 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeNodeParser.h @@ -0,0 +1,39 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_TYPE_NODE_PARSER_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_TYPE_NODE_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include "./deviceTypeParser.h" + +class obj_xml; + +class DeviceTypeNodeParser : public ifc_xmlreadercallback +{ + +public: + DeviceTypeNodeParser(); + ~DeviceTypeNodeParser(); + +public: + BOOL Begin(obj_xml *xmlReader, TestSuite *testSuite); + void End(); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + obj_xml *reader; + DeviceTypeParser elementParser; + TestSuite *test; + +protected: + RECVS_DISPATCH; +}; + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_TYPE_NODE_PARSER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeParser.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeParser.cpp new file mode 100644 index 00000000..3dba9447 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeParser.cpp @@ -0,0 +1,182 @@ +#include "main.h" +#include "./deviceTypeParser.h" + +#include "../../xml/obj_xml.h" + +typedef void (*TYPETAGCALLBACK)(DeviceTypeParser* /*self*/, ifc_devicetypeeditor* /*editor*/, const wchar_t* /*value*/); + +typedef struct TYPETAG +{ + const wchar_t *name; + BOOL multiEntry; + TYPETAGCALLBACK callback; +} TYPETAG; + +static void +DeviceTypeParser_DisplayNameCb(DeviceTypeParser *self, ifc_devicetypeeditor *editor, const wchar_t *value) +{ + editor->SetDisplayName(value); +} + +static void +DeviceTypeParser_IconCb(DeviceTypeParser *self, ifc_devicetypeeditor *editor, const wchar_t *value) +{ + ifc_deviceiconstore *iconStore; + if (SUCCEEDED(editor->GetIconStore(&iconStore))) + { + iconStore->Add(value, self->iconSize.cx, self->iconSize.cy, TRUE); + iconStore->Release(); + } +} + +static const TYPETAG knownTags[TYPE_TAG_MAX] = +{ + {L"displayName", FALSE, DeviceTypeParser_DisplayNameCb}, + {L"icon", TRUE, DeviceTypeParser_IconCb}, +}; + +DeviceTypeParser::DeviceTypeParser() + : editor(NULL) +{ +} + +DeviceTypeParser::~DeviceTypeParser() +{ + if (NULL != editor) + editor->Release(); +} + +BOOL DeviceTypeParser::Begin(obj_xml *reader, ifc_xmlreaderparams *params) +{ + const wchar_t *name; + ifc_devicetype *type; + char *nameAnsi; + + if (NULL != editor) + return FALSE; + + if (NULL == reader || NULL == params) + return FALSE; + + + name = params->getItemValue(L"name"); + if (NULL == name) + return FALSE; + + nameAnsi = String_ToAnsi(CP_UTF8, 0, name, -1, NULL, NULL); + if (NULL == nameAnsi) + return FALSE; + + if (NULL != WASABI_API_DEVICES && + SUCCEEDED(WASABI_API_DEVICES->CreateType(nameAnsi, &type))) + { + if(FAILED(type->QueryInterface(IFC_DeviceTypeEditor, (void**)&editor))) + editor = NULL; + + type->Release(); + } + + AnsiString_Free(nameAnsi); + + if (NULL == editor) + return FALSE; + + reader->xmlreader_registerCallback(L"testprovider\ftypes\ftype\fdisplayName", this); + reader->xmlreader_registerCallback(L"testprovider\ftypes\ftype\ficon", this); + ZeroMemory(hitList, sizeof(hitList)); + return TRUE; +} + +BOOL DeviceTypeParser::End(obj_xml *reader, ifc_devicetype **deviceType) +{ + BOOL result; + + if (NULL != reader) + reader->xmlreader_unregisterCallback(this); + + if (NULL == editor) + return FALSE; + + if (NULL != deviceType) + { + if (FAILED(editor->QueryInterface(IFC_DeviceType, (void**)deviceType))) + result = FALSE; + else + result = TRUE; + } + else + result = TRUE; + + editor->Release(); + editor = NULL; + + return result; +} + +void DeviceTypeParser::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementString.Clear(); + + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, L"icon", -1, xmltag, -1)) + { + const wchar_t *sVal; + int iVal; + + sVal = params->getItemValue(L"width"); + if (NULL == sVal || + FALSE == StrToIntEx(sVal, STIF_DEFAULT, &iVal)) + { + iVal = 0; + } + + iconSize.cx = iVal; + + sVal = params->getItemValue(L"height"); + if (NULL == sVal || + FALSE == StrToIntEx(sVal, STIF_DEFAULT, &iVal)) + { + iVal = 0; + } + + iconSize.cy = iVal; + } +} + +void DeviceTypeParser::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + if (NULL == editor) + return; + + for (size_t i = 0; i < TYPE_TAG_MAX; i++) + { + if (FALSE == hitList[i] && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, knownTags[i].name, -1, xmltag, -1)) + { + knownTags[i].callback(this, editor, elementString.Get()); + + if (FALSE == knownTags[i].multiEntry) + hitList[i] = TRUE; + + break; + } + } +} + +void DeviceTypeParser::Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value) +{ + elementString.Append(value); +} + +void DeviceTypeParser::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + elementString.Clear(); +} + +#define CBCLASS DeviceTypeParser +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONCHARDATA, Event_XmlCharData) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeParser.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeParser.h new file mode 100644 index 00000000..7584eb54 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeParser.h @@ -0,0 +1,46 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_TYPE_PARSER_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_TYPE_PARSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" + +class obj_xml; + +#define TYPE_TAG_MAX 2 + +class DeviceTypeParser : public ifc_xmlreadercallback +{ +public: + DeviceTypeParser(); + ~DeviceTypeParser(); +public: + BOOL Begin(obj_xml *reader, ifc_xmlreaderparams *params); + BOOL End(obj_xml *reader, ifc_devicetype **deviceType); + +protected: + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + friend static void DeviceTypeParser_DisplayNameCb(DeviceTypeParser *self, ifc_devicetypeeditor *editor, const wchar_t *value); + friend static void DeviceTypeParser_IconCb(DeviceTypeParser *self, ifc_devicetypeeditor *editor, const wchar_t *value); + +protected: + StringBuilder elementString; + ifc_devicetypeeditor *editor; + BOOL hitList[TYPE_TAG_MAX]; + SIZE iconSize; + +protected: + RECVS_DISPATCH; + +}; + + +#endif // _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_TYPE_PARSER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceView.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceView.cpp new file mode 100644 index 00000000..86ab3cc4 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceView.cpp @@ -0,0 +1,994 @@ +#include "main.h" +#include "./deviceView.h" +#include <wincodec.h> + +#include <commctrl.h> +#include <strsafe.h> +#include <vector> +#include <algorithm> + +#define DEVICEVIEW_PROP L"NullsoftDevicesViewProp" +static ATOM DEVICEVIEW_ATOM = 0; + +typedef struct DeviceView +{ + Device *device; +} DeviceView; + + +typedef std::vector<DeviceIconInfo*> DeviceIconInfoList; + +static INT_PTR +DeviceView_DialogProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam); + + +#define DEVICEVIEW(_hwnd) ((DeviceView*)GetPropW((_hwnd), MAKEINTATOM(DEVICEVIEW_ATOM))) +#define DEVICEVIEW_RET_VOID(_view, _hwnd) { (_view) = DEVICEVIEW((_hwnd)); if (NULL == (_view)) return; } +#define DEVICEVIEW_RET_VAL(_view, _hwnd, _error) { (_view) = DEVICEVIEW((_hwnd)); if (NULL == (_view)) return (_error); } + + + +static HBITMAP +DeviceView_HBitmapFromWicSource(IWICBitmapSource *wicSource) +{ + HRESULT hr; + HBITMAP bitmap; + BITMAPINFO bitmapInfo; + unsigned int width, height; + void *pixelData = NULL; + HDC windowDC; + unsigned int strideSize, imageSize; + + if (NULL == wicSource) + return NULL; + + hr = wicSource->GetSize(&width, &height); + if (FAILED(hr)) + return NULL; + + ZeroMemory(&bitmapInfo, sizeof(bitmapInfo)); + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = width; + bitmapInfo.bmiHeader.biHeight = -(LONG)height; + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + + windowDC = GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE); + + bitmap = CreateDIBSection(windowDC, &bitmapInfo, DIB_RGB_COLORS, &pixelData, NULL, 0); + + if (NULL != windowDC) + ReleaseDC(NULL, windowDC); + + if (NULL == bitmap) + return NULL; + + hr = UIntMult(width, sizeof(DWORD), &strideSize); + if (SUCCEEDED(hr)) + { + hr = UIntMult(strideSize, height, &imageSize); + if (SUCCEEDED(hr)) + hr = wicSource->CopyPixels(NULL, strideSize, imageSize, (BYTE*)pixelData); + } + + if (FAILED(hr)) + { + DeleteObject(bitmap); + bitmap = NULL; + } + + return bitmap; +} + +static HBITMAP +DeviceView_LoadIcon(const wchar_t *path) +{ + IWICImagingFactory *wicFactory; + IWICBitmapDecoder *wicDecoder; + + HRESULT hr; + HBITMAP bitmap; + + hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&wicFactory)); + + if (FAILED(hr)) + return NULL; + + bitmap = NULL; + + hr = wicFactory->CreateDecoderFromFilename(path, NULL, GENERIC_READ, + WICDecodeMetadataCacheOnDemand, &wicDecoder); + + if (SUCCEEDED(hr)) + { + IWICBitmapFrameDecode *wicFrame; + hr = wicDecoder->GetFrame(0, &wicFrame); + if(SUCCEEDED(hr)) + { + IWICBitmapSource *wicSource; + hr = wicFrame->QueryInterface(IID_IWICBitmapSource, + reinterpret_cast<void **>(&wicSource)); + if (SUCCEEDED(hr)) + { + WICPixelFormatGUID pixelFormat; + hr = wicSource->GetPixelFormat(&pixelFormat); + if (FAILED(hr) || + (GUID_WICPixelFormat32bppPBGRA != pixelFormat && + GUID_WICPixelFormat32bppBGR != pixelFormat && + GUID_WICPixelFormat32bppBGRA != pixelFormat && + GUID_WICPixelFormat32bppRGBA != pixelFormat && + GUID_WICPixelFormat32bppPRGBA != pixelFormat)) + { + // try to convert + IWICFormatConverter *wicConverter; + hr = wicFactory->CreateFormatConverter(&wicConverter); + if (SUCCEEDED(hr)) + { + hr = wicConverter->Initialize(wicSource, GUID_WICPixelFormat32bppPBGRA, + WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeCustom); + + if (SUCCEEDED(hr)) + { + IWICBitmapSource *wicConvertedSource; + hr = wicConverter->QueryInterface(IID_IWICBitmapSource, + reinterpret_cast<void **>(&wicConvertedSource)); + if (SUCCEEDED(hr)) + { + wicSource->Release(); + wicSource = wicConvertedSource; + } + } + wicConverter->Release(); + } + } + + if (SUCCEEDED(hr)) + bitmap = DeviceView_HBitmapFromWicSource(wicSource); + + wicSource->Release(); + } + wicFrame->Release(); + } + wicDecoder->Release(); + } + + + wicFactory->Release(); + return bitmap; + +} + +HWND +DeviceView_CreateWindow(HWND parentWindow, Device *device) +{ + HWND hwnd; + + if (NULL == device) + return NULL; + + if (0 == DEVICEVIEW_ATOM) + { + DEVICEVIEW_ATOM = GlobalAddAtom(DEVICEVIEW_PROP); + if (0 == DEVICEVIEW_ATOM) + return NULL; + + } + + hwnd = WASABI_API_CREATEDIALOGPARAMW((INT_PTR)IDD_DEVICE_VIEW, parentWindow, + DeviceView_DialogProc, (LPARAM)device); + + return hwnd; +} + +static void +DeviceView_InitCapacity(HWND hwnd) +{ + DeviceView *self; + HWND controlWindow; + uint64_t totalSpace, usedSpace; + + DEVICEVIEW_RET_VOID(self, hwnd); + + if (NULL != self->device) + { + if (FAILED(self->device->GetTotalSpace(&totalSpace))) + totalSpace = 0; + + if (FAILED(self->device->GetUsedSpace(&usedSpace))) + usedSpace = 0; + } + else + { + totalSpace = 0; + usedSpace = 0; + } + + controlWindow = GetDlgItem(hwnd, IDC_SPIN_TOTALSPACE); + if (NULL != controlWindow) + { + SendMessage(controlWindow, UDM_SETRANGE32, (WPARAM)0, (LPARAM)0xFFFFFF); + SendMessage(controlWindow, UDM_SETPOS32, (WPARAM)0, (LPARAM)totalSpace); + } + + controlWindow = GetDlgItem(hwnd, IDC_SPIN_USEDSPACE); + if (NULL != controlWindow) + { + SendMessage(controlWindow, UDM_SETRANGE32, (WPARAM)0, (LPARAM)totalSpace); + SendMessage(controlWindow, UDM_SETPOS32, (WPARAM)0, (LPARAM)usedSpace); + } + +} + +static int +DeviceView_CompareDeviceIconInfo(const void *elem1, const void *elem2) +{ + DeviceIconInfo *info1; + DeviceIconInfo *info2; + + info1 = *((DeviceIconInfo**)elem1); + info2 = *((DeviceIconInfo**)elem2); + + if (NULL == info1 || NULL == info2) + return (int)(info1 - info2); + if (info1->width != info2->width) + return (int)(info1->width - info2->width); + + if (info1->height != info2->height) + return (int)(info1->height - info2->height); + + if (NULL == info1->path || NULL == info2->path) + return (int)(info1->path - info2->path); + + return CompareString(CSTR_INVARIANT, NORM_IGNORECASE, info1->path, -1, info2->path, -1) - 2; +} + +static int +DeviceView_CompareDeviceIconInfo_V2(const void* elem1, const void* elem2) +{ + return DeviceView_CompareDeviceIconInfo(elem1, elem2) < 0; +} + +static BOOL +DeviceView_EnumerateIcons(const wchar_t *path, unsigned int width, unsigned int height, void *user) +{ + DeviceIconInfo *info; + + DeviceIconInfoList *list = (DeviceIconInfoList*)user; + if( NULL == list) + return FALSE; + + info = (DeviceIconInfo*)malloc(sizeof(DeviceIconInfo)); + if (NULL != info) + { + info->height = height; + info->width = width; + info->path = String_Duplicate(path); + + list->push_back(info); + } + + + return TRUE; +} + +static void +DeviceView_DestroyIcons(HWND hwnd) +{ + HWND controlWindow; + int count, index; + DeviceIconInfo *info; + + + controlWindow = GetDlgItem(hwnd, IDC_COMBO_ICONS); + if (NULL == controlWindow) + return; + + count = (int)SendMessage(controlWindow, CB_GETCOUNT, 0, 0L); + for(index = 0; index < count; index++) + { + info = (DeviceIconInfo*)SendMessage(controlWindow, CB_GETITEMDATA, index, 0L); + if (CB_ERR != (INT_PTR)info) + { + String_Free(info->path); + free(info); + } + } + + SendMessage(controlWindow, CB_RESETCONTENT, 0, 0L); + + controlWindow = GetDlgItem(hwnd, IDC_STATIC_PREVIEWICON); + if (NULL != controlWindow) + { + HBITMAP bitmap; + bitmap = (HBITMAP)SendMessage(controlWindow, STM_GETIMAGE, IMAGE_BITMAP, 0L); + if(NULL != bitmap) + DeleteObject(bitmap); + } + +} + +static void +DeviceView_InitIcons(HWND hwnd, const wchar_t *selectPath) +{ + DeviceView *self; + DeviceIconInfoList list; + HWND controlWindow; + wchar_t buffer[2048]; + + DEVICEVIEW_RET_VOID(self, hwnd); + + DeviceView_DestroyIcons(hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_COMBO_ICONS); + if (NULL != controlWindow) + { + size_t index, count; + int iItem, iSelect; + DeviceIconInfo *info; + + if (NULL != self->device) + { + self->device->EnumerateIcons(DeviceView_EnumerateIcons, &list); + } + + count = list.size(); + //qsort(list.begin(), count, sizeof(DeviceIconInfo**), DeviceView_CompareDeviceIconInfo); + std::sort(list.begin(), list.end(), DeviceView_CompareDeviceIconInfo_V2); + + iSelect = 0; + + for(index = 0; index < count; index++) + { + info = list[index]; + if (1 == info->width && 1 == info->height) + { + StringCchPrintf(buffer, ARRAYSIZE(buffer), L"%s", + info->path); + } + else + { + StringCchPrintf(buffer, ARRAYSIZE(buffer), L"[%dx%d] - %s", + info->width, info->height, info->path); + } + + iItem = (int)SendMessage(controlWindow, CB_ADDSTRING, 0, (LPARAM)buffer); + if (CB_ERR != iItem) + { + if (CB_ERR == (int)SendMessage(controlWindow, CB_SETITEMDATA, index, (LPARAM)info)) + { + SendMessage(controlWindow, CB_DELETESTRING, index, 0L); + iItem = CB_ERR; + } + } + + if (CB_ERR == iItem) + { + free(info); + } + else if (NULL != selectPath && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, + info->path, -1, selectPath, -1)) + { + iSelect = iItem; + } + } + + iItem = (int)SendMessage(controlWindow, CB_GETCOUNT, 0, 0L); + if (CB_ERR == iItem) + iItem = 0; + EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_EDITICON), (0 != iItem)); + EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_REMOVEICON),(0 != iItem)); + + + SendMessage(controlWindow, CB_SETCURSEL, iSelect, 0L); + PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_COMBO_ICONS, CBN_SELENDOK), (LPARAM)controlWindow); + } +} + +static void +DeviceView_NewIcon(HWND hwnd) +{ + DeviceView *self; + DeviceIconInfo info; + INT_PTR result; + + DEVICEVIEW_RET_VOID(self, hwnd); + + ZeroMemory(&info, sizeof(info)); + + result = DeviceIconEditor_Show(hwnd, &info); + if (IDOK == result) + { + if (NULL != self->device && + SUCCEEDED(self->device->AddIcon(info.path, info.width, info.height))) + { + DeviceView_InitIcons(hwnd, info.path); + } + + String_Free(info.path); + } +} + +static void +DeviceView_RemoveIcon(HWND hwnd) +{ + DeviceView *self; + DeviceIconInfo *info; + int index, count; + HWND controlWindow; + + DEVICEVIEW_RET_VOID(self, hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_COMBO_ICONS); + if (NULL == controlWindow) + return; + + index = (int)SendMessage(controlWindow, CB_GETCURSEL, 0, 0L); + if (CB_ERR == index) + return; + + info = (DeviceIconInfo*)SendMessage(controlWindow, CB_GETITEMDATA, index, 0L); + if (CB_ERR != (INT_PTR)info) + { + if (NULL != self->device) + self->device->RemoveIcon(info->width, info->height); + + String_Free(info->path); + free(info); + } + + SendMessage(controlWindow, CB_DELETESTRING, index, 0L); + + count = (int)SendMessage(controlWindow, CB_GETCOUNT, 0, 0L); + if (count > 0) + { + if (index > count) + index = count - 1; + SendMessage(controlWindow, CB_SETCURSEL, index, 0L); + } + else + { + SendMessage(controlWindow, CB_SETCURSEL, -1, 0L); + EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_EDITICON),FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_REMOVEICON),FALSE); + } + + PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_COMBO_ICONS, CBN_SELENDOK), (LPARAM)controlWindow); + + UpdateWindow(controlWindow); +} + +static void +DeviceView_EditIcon(HWND hwnd) +{ + DeviceView *self; + DeviceIconInfo *info; + int index; + unsigned int width, height; + HWND controlWindow; + INT_PTR result; + + DEVICEVIEW_RET_VOID(self, hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_COMBO_ICONS); + if (NULL == controlWindow) + return; + + index = (int)SendMessage(controlWindow, CB_GETCURSEL, 0, 0L); + if (CB_ERR == index) + return; + + info = (DeviceIconInfo*)SendMessage(controlWindow, CB_GETITEMDATA, index, 0L); + if (CB_ERR == (INT_PTR)info) + return; + width = info->width; + height = info->height; + + result = DeviceIconEditor_Show(hwnd, info); + if (IDOK == result) + { + if (NULL != self->device) + { + self->device->RemoveIcon(width, height); + self->device->AddIcon(info->path, info->width, info->height); + DeviceView_InitIcons(hwnd, info->path); + } + } + + UpdateWindow(controlWindow); +} + +static void +DeviceView_InitView(HWND hwnd) +{ + DeviceView *self; + HWND controlWindow; + wchar_t buffer[1024]; + + DEVICEVIEW_RET_VOID(self, hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_NAME); + if (NULL != controlWindow) + { + if (NULL == self->device || + 0 == MultiByteToWideChar(CP_UTF8, 0, self->device->GetName(), -1, buffer, ARRAYSIZE(buffer))) + { + StringCchCopy(buffer, ARRAYSIZE(buffer), L"<unknown>"); + } + SetWindowText(controlWindow, buffer); + } + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_TITLE); + if (NULL != controlWindow) + { + if (NULL == self->device || + FAILED(self->device->GetDisplayName(buffer, ARRAYSIZE(buffer)))) + { + StringCchCopy(buffer, ARRAYSIZE(buffer), L"<unknown>"); + } + SetWindowText(controlWindow, buffer); + } + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_TYPE); + if (NULL != controlWindow) + { + buffer[0] = L'\0'; + + if (NULL != self->device) + { + const char *typeName; + ifc_devicetype *type; + + typeName = self->device->GetType(); + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->TypeFind(typeName, &type)) + { + if (FAILED(type->GetDisplayName(buffer, ARRAYSIZE(buffer)))) + buffer[0] = L'\0'; + + type->Release(); + } + + if (L'\0' == *buffer) + MultiByteToWideChar(CP_UTF8, 0, typeName, -1, buffer, ARRAYSIZE(buffer)); + } + + if (L'\0' == *buffer) + StringCchCopy(buffer, ARRAYSIZE(buffer), L"<unknown>"); + + SetWindowText(controlWindow, buffer); + } + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_CONNECTION); + if (NULL != controlWindow) + { + buffer[0] = L'\0'; + + if (NULL != self->device) + { + const char *connectionName; + ifc_deviceconnection *connection; + + connectionName = self->device->GetConnection(); + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->ConnectionFind(connectionName, &connection)) + { + if (FAILED(connection->GetDisplayName(buffer, ARRAYSIZE(buffer)))) + buffer[0] = L'\0'; + + connection->Release(); + } + + if (L'\0' == *buffer) + MultiByteToWideChar(CP_UTF8, 0, connectionName, -1, buffer, ARRAYSIZE(buffer)); + } + + if (L'\0' == *buffer) + StringCchCopy(buffer, ARRAYSIZE(buffer), L"<unknown>"); + + SetWindowText(controlWindow, buffer); + } + + controlWindow = GetDlgItem(hwnd, IDC_COMBO_ATTACHED); + if (NULL != controlWindow) + { + SendMessage(controlWindow, CB_RESETCONTENT, 0, 0L); + + if (NULL != self->device) + { + const wchar_t *searchString; + + SendMessage(controlWindow, CB_ADDSTRING, 0, (LPARAM)L"Yes"); + SendMessage(controlWindow, CB_ADDSTRING, 0, (LPARAM)L"No"); + + if (FALSE == self->device->GetAttached()) + searchString = L"No"; + else + searchString = L"Yes"; + + SendMessage(controlWindow, CB_SELECTSTRING, -1, (LPARAM)searchString); + } + else + { + SendMessage(controlWindow, CB_ADDSTRING, 0, (LPARAM)L"<unknown>"); + SendMessage(controlWindow, CB_SETCURSEL, 0, 0L); + } + + } + + controlWindow = GetDlgItem(hwnd, IDC_COMBO_VISIBLE); + if (NULL != controlWindow) + { + SendMessage(controlWindow, CB_RESETCONTENT, 0, 0L); + + if (NULL != self->device) + { + const wchar_t *searchString; + + SendMessage(controlWindow, CB_ADDSTRING, 0, (LPARAM)L"Yes"); + SendMessage(controlWindow, CB_ADDSTRING, 0, (LPARAM)L"No"); + + if (FALSE != self->device->GetHidden()) + searchString = L"No"; + else + searchString = L"Yes"; + + SendMessage(controlWindow, CB_SELECTSTRING, -1, (LPARAM)searchString); + } + else + { + SendMessage(controlWindow, CB_ADDSTRING, 0, (LPARAM)L"<unknown>"); + SendMessage(controlWindow, CB_SETCURSEL, 0, 0L); + } + } + + DeviceView_InitCapacity(hwnd); + DeviceView_InitIcons(hwnd, NULL); + +} + +static INT_PTR +DeviceView_OnInitDialog(HWND hwnd, HWND focusWindow, LPARAM param) +{ + DeviceView *self; + + self = (DeviceView*)malloc(sizeof(DeviceView)); + if (NULL != self) + { + ZeroMemory(self, sizeof(DeviceView)); + if (FALSE == SetProp(hwnd, MAKEINTATOM(DEVICEVIEW_ATOM), self)) + { + free(self); + self = NULL; + } + } + + if (NULL == self) + { + DestroyWindow(hwnd); + return 0; + } + + self->device = (Device*)param; + if (NULL != self->device) + self->device->AddRef(); + + DeviceView_InitView(hwnd); + + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED); + + return 0; +} + +static void +DeviceView_OnDestroy(HWND hwnd) +{ + DeviceView *self; + + DeviceView_DestroyIcons(hwnd); + + self = DEVICEVIEW(hwnd); + RemoveProp(hwnd, MAKEINTATOM(DEVICEVIEW_ATOM)); + + if (NULL == self) + return; + + if (NULL != self->device) + self->device->Release(); + + free(self); +} + +static void +DeviceView_OnTitleEditChanged(HWND hwnd) +{ + DeviceView *self; + HWND controlWindow; + wchar_t buffer[1024]; + + DEVICEVIEW_RET_VOID(self, hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_EDIT_TITLE); + if (NULL == controlWindow) + return; + + if (NULL == self->device) + return; + + GetWindowText(controlWindow, buffer, ARRAYSIZE(buffer)); + + self->device->SetDisplayName(buffer); + +} + +static void +DeviceView_OnAttachedComboChanged(HWND hwnd) +{ + DeviceView *self; + HWND controlWindow; + wchar_t buffer[1024]; + int index; + + DEVICEVIEW_RET_VOID(self, hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_COMBO_ATTACHED); + if (NULL == controlWindow) + return; + + if (NULL == self->device) + return; + + index = (int)SendMessage(controlWindow, CB_GETCURSEL, 0, 0); + if (CB_ERR != index && + CB_ERR != SendMessage(controlWindow, CB_GETLBTEXT, index, (LPARAM)buffer)) + { + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, buffer, -1, L"Yes", -1)) + { + self->device->Attach(NULL); + } + else if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, buffer, -1, L"No", -1)) + { + self->device->Detach(NULL); + } + } +} + +static void +DeviceView_OnVisibleComboChanged(HWND hwnd) +{ + DeviceView *self; + HWND controlWindow; + wchar_t buffer[1024]; + int index; + + DEVICEVIEW_RET_VOID(self, hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_COMBO_VISIBLE); + if (NULL == controlWindow) + return; + + if (NULL == self->device) + return; + + index = (int)SendMessage(controlWindow, CB_GETCURSEL, 0, 0); + if (-1 != index && + CB_ERR != SendMessage(controlWindow, CB_GETLBTEXT, index, (LPARAM)buffer)) + { + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, buffer, -1, L"Yes", -1)) + { + self->device->SetHidden(FALSE); + } + else if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, buffer, -1, L"No", -1)) + { + self->device->SetHidden(TRUE); + } + } +} + +static void +DeviceView_OnIconsComboChanged(HWND hwnd) +{ + DeviceView *self; + HWND controlWindow, pictureWindow; + int index; + HBITMAP bitmap, previousBitmap; + + DEVICEVIEW_RET_VOID(self, hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_COMBO_ICONS); + if (NULL == controlWindow) + return; + + if (NULL == self->device) + return; + + pictureWindow = GetDlgItem(hwnd, IDC_STATIC_PREVIEWICON); + if (NULL == pictureWindow) + return; + + bitmap = NULL; + index = (int)SendMessage(controlWindow, CB_GETCURSEL, 0, 0);\ + if (CB_ERR != index) + { + DeviceIconInfo *info; + info = (DeviceIconInfo*)SendMessage(controlWindow, CB_GETITEMDATA, index, 0L); + if (CB_ERR != (INT_PTR)info && NULL != info->path) + { + bitmap = DeviceView_LoadIcon(info->path); + } + } + + previousBitmap = (HBITMAP)SendMessage(pictureWindow, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmap); + if (NULL != previousBitmap) + DeleteObject(previousBitmap); + + previousBitmap = (HBITMAP)SendMessage(pictureWindow, STM_GETIMAGE, IMAGE_BITMAP, 0L); + if(previousBitmap != bitmap) + { + if (NULL != bitmap) + DeleteObject(bitmap); + } + + +} + +static void +DeviceView_OnTotalSpaceEditChanged(HWND hwnd) +{ + DeviceView *self; + HWND controlWindow; + uint64_t totalSpace; + BOOL error; + + DEVICEVIEW_RET_VOID(self, hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_SPIN_TOTALSPACE); + if (NULL == controlWindow) + return; + + if (NULL == self->device) + return; + + totalSpace = (size_t)SendMessage(controlWindow, UDM_GETPOS32, 0, (LPARAM)&error); + if (FALSE != error) + return; + + if (FAILED(self->device->SetTotalSpace(totalSpace))) + { + if (FAILED(self->device->GetTotalSpace(&totalSpace))) + totalSpace = 0; + } + + controlWindow = GetDlgItem(hwnd, IDC_SPIN_USEDSPACE); + if (NULL != controlWindow) + { + SendMessage(controlWindow, UDM_SETRANGE32, (WPARAM)0, (LPARAM)totalSpace); + } +} + +static void +DeviceView_OnUsedSpaceEditChanged(HWND hwnd) +{ + DeviceView *self; + HWND controlWindow; + uint64_t usedSpace; + BOOL error; + + DEVICEVIEW_RET_VOID(self, hwnd); + + controlWindow = GetDlgItem(hwnd, IDC_SPIN_USEDSPACE); + if (NULL == controlWindow) + return; + + if (NULL == self->device) + return; + + usedSpace = (size_t)SendMessage(controlWindow, UDM_GETPOS32, 0, (LPARAM)&error); + if (FALSE != error) + return; + + if (FAILED(self->device->SetUsedSpace(usedSpace))) + { + if (FAILED(self->device->GetTotalSpace(&usedSpace))) + usedSpace = 0; + } +} + +static void +DeviceView_OnCommand(HWND hwnd, INT commandId, INT eventId, HWND controlWindow) +{ + switch(commandId) + { + case IDC_EDIT_TITLE: + switch(eventId) + { + case EN_CHANGE: + DeviceView_OnTitleEditChanged(hwnd); + break; + } + break; + case IDC_COMBO_ATTACHED: + switch(eventId) + { + case CBN_SELENDOK: + DeviceView_OnAttachedComboChanged(hwnd); + break; + } + break; + + case IDC_COMBO_VISIBLE: + switch(eventId) + { + case CBN_SELENDOK: + DeviceView_OnVisibleComboChanged(hwnd); + break; + } + break; + case IDC_COMBO_ICONS: + switch(eventId) + { + case CBN_SELENDOK: + DeviceView_OnIconsComboChanged(hwnd); + break; + } + break; + case IDC_EDIT_TOTALSPACE: + switch(eventId) + { + case EN_CHANGE: + DeviceView_OnTotalSpaceEditChanged(hwnd); + break; + } + break; + case IDC_EDIT_USEDSPACE: + switch(eventId) + { + case EN_CHANGE: + DeviceView_OnUsedSpaceEditChanged(hwnd); + break; + } + break; + case IDC_BUTTON_NEWICON: + switch(eventId) + { + case BN_CLICKED: + DeviceView_NewIcon(hwnd); + break; + } + break; + case IDC_BUTTON_REMOVEICON: + switch(eventId) + { + case BN_CLICKED: + DeviceView_RemoveIcon(hwnd); + break; + } + break; + case IDC_BUTTON_EDITICON: + switch(eventId) + { + case BN_CLICKED: + DeviceView_EditIcon(hwnd); + break; + } + break; + } +} + + +static INT_PTR +DeviceView_DialogProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: return DeviceView_OnInitDialog(hwnd, (HWND)wParam, lParam); + case WM_DESTROY: DeviceView_OnDestroy(hwnd); return TRUE; + case WM_COMMAND: DeviceView_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); return TRUE; + } + return 0; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceView.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceView.h new file mode 100644 index 00000000..394a9978 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceView.h @@ -0,0 +1,15 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_VIEW_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_VIEW_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +HWND +DeviceView_CreateWindow(HWND parentWindow, + Device *device); + + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_DEVICE_VIEW_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.sln b/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.sln Binary files differnew file mode 100644 index 00000000..9b1c4e3f --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.sln diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcproj b/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcproj new file mode 100644 index 00000000..24f71f98 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcproj @@ -0,0 +1,415 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="gen_deviceprovider" + ProjectGUID="{A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}" + RootNamespace="gen_deviceprovider" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../..;../../wasabi;../../agave" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GEN_DEVICEPROVIDER_EXPORTS" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + PrecompiledHeaderThrough="" + WarningLevel="3" + DebugInformationFormat="4" + EnablePREfast="false" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="comctl32.lib shlwapi.lib windowscodecs.lib" + OutputFile="$(ProgramFiles)\winamp\plugins\gen_deviceprovider.dll" + LinkIncremental="2" + IgnoreDefaultLibraryNames="msvcprt.lib" + GenerateDebugInformation="true" + ProgramDatabaseFile="$(OutDir)\$(TargetName).pdb" + SubSystem="2" + SupportUnloadOfDelayLoadedDLL="true" + ImportLibrary="$(OutDir)\$(TargetName).lib" + TargetMachine="1" + Profile="false" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + OutputDocumentFile="$(OutDir)\$(TargetName).xml" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="2" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../..;../../wasabi;../../agave" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GEN_DEVICEPROVIDER_EXPORTS" + RuntimeLibrary="2" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="2" + PrecompiledHeaderThrough="main.h" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="comctl32.lib shlwapi.lib windowscodecs.lib" + OutputFile="$(ProgramFiles)\winamp\plugins\gen_deviceprovider.dll" + LinkIncremental="1" + IgnoreDefaultLibraryNames="msvcprt.lib" + GenerateDebugInformation="true" + ProgramDatabaseFile="$(OutDir)\$(TargetName).pdb" + SubSystem="2" + OptimizeReferences="2" + EnableCOMDATFolding="2" + SupportUnloadOfDelayLoadedDLL="true" + ImportLibrary="$(OutDir)\$(TargetName).lib" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + OutputDocumentFile="$(OutDir)\$(TargetName).xml" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="nu" + > + <File + RelativePath="..\..\nu\PtrList.cpp" + > + <FileConfiguration + Name="Release|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\..\nu\PtrList.h" + > + </File> + <File + RelativePath="..\..\nu\trace.cpp" + > + <FileConfiguration + Name="Release|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="0" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\..\nu\trace.h" + > + </File> + </Filter> + <File + RelativePath=".\common.h" + > + </File> + <File + RelativePath=".\device.cpp" + > + </File> + <File + RelativePath=".\device.h" + > + </File> + <File + RelativePath=".\deviceActivity.cpp" + > + </File> + <File + RelativePath=".\deviceActivity.h" + > + </File> + <File + RelativePath=".\deviceCommandNodeParser.cpp" + > + </File> + <File + RelativePath=".\deviceCommandNodeParser.h" + > + </File> + <File + RelativePath=".\deviceCommandParser.cpp" + > + </File> + <File + RelativePath=".\deviceCommandParser.h" + > + </File> + <File + RelativePath=".\deviceConnectionNodeParser.cpp" + > + </File> + <File + RelativePath=".\deviceConnectionNodeParser.h" + > + </File> + <File + RelativePath=".\deviceConnectionParser.cpp" + > + </File> + <File + RelativePath=".\deviceConnectionParser.h" + > + </File> + <File + RelativePath=".\deviceIconEditor.cpp" + > + </File> + <File + RelativePath=".\deviceIconEditor.h" + > + </File> + <File + RelativePath=".\deviceNodeParser.cpp" + > + </File> + <File + RelativePath=".\deviceNodeParser.h" + > + </File> + <File + RelativePath=".\deviceParser.cpp" + > + </File> + <File + RelativePath=".\deviceParser.h" + > + </File> + <File + RelativePath=".\deviceTypeNodeParser.cpp" + > + </File> + <File + RelativePath=".\deviceTypeNodeParser.h" + > + </File> + <File + RelativePath=".\deviceTypeParser.cpp" + > + </File> + <File + RelativePath=".\deviceTypeParser.h" + > + </File> + <File + RelativePath=".\deviceView.cpp" + > + </File> + <File + RelativePath=".\deviceView.h" + > + </File> + <File + RelativePath=".\iconStore.cpp" + > + </File> + <File + RelativePath=".\iconStore.h" + > + </File> + <File + RelativePath=".\main.cpp" + > + <FileConfiguration + Name="Release|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1" + /> + </FileConfiguration> + </File> + <File + RelativePath=".\main.h" + > + </File> + <File + RelativePath=".\plugin.cpp" + > + </File> + <File + RelativePath=".\plugin.h" + > + </File> + <File + RelativePath=".\provider.cpp" + > + </File> + <File + RelativePath=".\provider.h" + > + </File> + <File + RelativePath=".\resource.h" + > + </File> + <File + RelativePath=".\resources.rc" + > + </File> + <File + RelativePath=".\stringBuilder.cpp" + > + </File> + <File + RelativePath=".\stringBuilder.h" + > + </File> + <File + RelativePath=".\strings.cpp" + > + </File> + <File + RelativePath=".\strings.h" + > + </File> + <File + RelativePath=".\testSuite.cpp" + > + </File> + <File + RelativePath=".\testSuite.h" + > + </File> + <File + RelativePath=".\testSuiteLoader.cpp" + > + </File> + <File + RelativePath=".\testSuiteLoader.h" + > + </File> + <File + RelativePath=".\wasabi.cpp" + > + </File> + <File + RelativePath=".\wasabi.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcxproj b/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcxproj new file mode 100644 index 00000000..947430cd --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcxproj @@ -0,0 +1,335 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}</ProjectGuid> + <RootNamespace>gen_deviceprovider</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>false</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>../..;..\..\..\;..\..\..\..\wasabi;..\..\..\..\agave;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;GEN_DEVICEPROVIDER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <EnablePREfast>false</EnablePREfast> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + <FunctionLevelLinking>true</FunctionLevelLinking> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <Profile>false</Profile> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <Xdcmake> + <OutputFile>$(IntDir)$(TargetName).xml</OutputFile> + </Xdcmake> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>../..;..\..\..\..\;..\..\..\..\wasabi;..\..\..\..\agave;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;GEN_DEVICEPROVIDER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <EnablePREfast>false</EnablePREfast> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + <FunctionLevelLinking>true</FunctionLevelLinking> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <Profile>false</Profile> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <Xdcmake> + <OutputFile>$(IntDir)$(TargetName).xml</OutputFile> + </Xdcmake> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MaxSpeed</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <AdditionalIncludeDirectories>../..;..\..\..\..\;..\..\..\..\wasabi;..\..\..\..\agave;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;GEN_DEVICEPROVIDER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <StringPooling>true</StringPooling> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <Xdcmake> + <OutputFile>$(IntDir)$(TargetName).xml</OutputFile> + </Xdcmake> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MaxSpeed</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <AdditionalIncludeDirectories>../..;..\..\..\..\;..\..\..\..\wasabi;..\..\..\..\agave;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;GEN_DEVICEPROVIDER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <StringPooling>true</StringPooling> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <Xdcmake> + <OutputFile>$(IntDir)$(TargetName).xml</OutputFile> + </Xdcmake> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\..\..\nu\PtrList.cpp" /> + <ClCompile Include="..\..\..\..\nu\trace.cpp" /> + <ClCompile Include="device.cpp" /> + <ClCompile Include="deviceActivity.cpp" /> + <ClCompile Include="deviceCommandNodeParser.cpp" /> + <ClCompile Include="deviceCommandParser.cpp" /> + <ClCompile Include="deviceConnectionNodeParser.cpp" /> + <ClCompile Include="deviceConnectionParser.cpp" /> + <ClCompile Include="deviceIconEditor.cpp" /> + <ClCompile Include="deviceNodeParser.cpp" /> + <ClCompile Include="deviceParser.cpp" /> + <ClCompile Include="deviceTypeNodeParser.cpp" /> + <ClCompile Include="deviceTypeParser.cpp" /> + <ClCompile Include="deviceView.cpp" /> + <ClCompile Include="iconStore.cpp" /> + <ClCompile Include="main.cpp" /> + <ClCompile Include="plugin.cpp" /> + <ClCompile Include="provider.cpp" /> + <ClCompile Include="stringBuilder.cpp" /> + <ClCompile Include="strings.cpp" /> + <ClCompile Include="testSuite.cpp" /> + <ClCompile Include="testSuiteLoader.cpp" /> + <ClCompile Include="wasabi.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\..\nu\trace.h" /> + <ClInclude Include="common.h" /> + <ClInclude Include="device.h" /> + <ClInclude Include="deviceActivity.h" /> + <ClInclude Include="deviceCommandNodeParser.h" /> + <ClInclude Include="deviceCommandParser.h" /> + <ClInclude Include="deviceConnectionNodeParser.h" /> + <ClInclude Include="deviceConnectionParser.h" /> + <ClInclude Include="deviceIconEditor.h" /> + <ClInclude Include="deviceNodeParser.h" /> + <ClInclude Include="deviceParser.h" /> + <ClInclude Include="deviceTypeNodeParser.h" /> + <ClInclude Include="deviceTypeParser.h" /> + <ClInclude Include="deviceView.h" /> + <ClInclude Include="iconStore.h" /> + <ClInclude Include="main.h" /> + <ClInclude Include="plugin.h" /> + <ClInclude Include="provider.h" /> + <ClInclude Include="resource.h" /> + <ClInclude Include="stringBuilder.h" /> + <ClInclude Include="strings.h" /> + <ClInclude Include="testSuite.h" /> + <ClInclude Include="testSuiteLoader.h" /> + <ClInclude Include="wasabi.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="resources.rc" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcxproj.filters b/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcxproj.filters new file mode 100644 index 00000000..517379d9 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcxproj.filters @@ -0,0 +1,164 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="device.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceActivity.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceCommandNodeParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceCommandParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceConnectionNodeParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceConnectionParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceIconEditor.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceNodeParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceTypeNodeParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceTypeParser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceView.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="iconStore.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="plugin.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="provider.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="stringBuilder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="strings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="testSuite.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="testSuiteLoader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="wasabi.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\..\nu\trace.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\..\nu\PtrList.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="common.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="device.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceActivity.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceCommandNodeParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceCommandParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceConnectionNodeParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceConnectionParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceIconEditor.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceNodeParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceTypeNodeParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceTypeParser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceView.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="iconStore.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="main.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="plugin.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="provider.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="stringBuilder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="strings.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="testSuite.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="testSuiteLoader.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="wasabi.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\..\nu\trace.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{e264ea99-159e-4062-a78a-a85da3815f37}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{f5beaf0a-f21d-4ae9-8221-adde13e2b65d}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{207087f5-3450-4826-bae5-39064e9da43f}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="resources.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/iconStore.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/iconStore.cpp new file mode 100644 index 00000000..28e916d8 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/iconStore.cpp @@ -0,0 +1,289 @@ +#include "main.h" +#include "./iconStore.h" + +#include <vector> + + +typedef struct IconStoreRecord +{ + unsigned int width; + unsigned int height; + wchar_t *path; +} IconStoreRecord; + +typedef std::vector<IconStoreRecord> RecodList; + +struct IconStore +{ + RecodList list; + wchar_t *basePath; +}; + +IconStore * +IconStore_Create() +{ + IconStore *self; + + self = new IconStore(); + if (NULL == self) + return NULL; + + self->basePath = NULL; + return self; +} + +void +IconStore_Destroy(IconStore *self) +{ + size_t index; + IconStoreRecord *record; + + if (NULL == self) + return; + + index = self->list.size(); + while(index--) + { + record = &self->list[index]; + String_Free(record->path); + } + + String_Free(self->basePath); +} + +BOOL +IconStore_Add(IconStore *self, const wchar_t *path, unsigned int width, unsigned int height) +{ + IconStoreRecord record, *prec; + size_t index; + + if (NULL == self) + return FALSE; + + if(width < 1) + width = 1; + + if(height < 1) + height = 1; + + index = self->list.size(); + while(index--) + { + prec = &self->list[index]; + if (width == prec->width && + height == prec->height) + { + String_Free(prec->path); + prec->path = String_Duplicate(path); + return TRUE; + } + } + + record.path = String_Duplicate(path); + record.width = width; + record.height = height; + + self->list.push_back(record); + + return TRUE; +} + +BOOL +IconStore_RemovePath(IconStore *self, const wchar_t *path) +{ + size_t index; + IconStoreRecord *record; + + if (NULL == self) + return FALSE; + + index = self->list.size(); + while(index--) + { + record = &self->list[index]; + if(CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, record->path, -1, path, -1)) + { + self->list.eraseAt(index); + } + } + + return TRUE; +} + +BOOL +IconStore_Remove(IconStore *self, unsigned int width, unsigned int height) +{ + size_t index; + IconStoreRecord *record; + + if (NULL == self) + return FALSE; + + index = self->list.size(); + while(index--) + { + record = &self->list[index]; + if(record->width == width && + record->height == height) + { + self->list.eraseAt(index); + } + } + + return TRUE; +} + +static BOOL +IconStore_GetFullPath(IconStore *self, const wchar_t *path, wchar_t *buffer, size_t bufferMax) +{ + if (NULL == buffer) + return FALSE; + + if (NULL == self) + return FALSE; + + if (FALSE != IS_STRING_EMPTY(path)) + { + *buffer = L'\0'; + return TRUE; + } + + if (FALSE == PathIsRelative(path) || + IS_STRING_EMPTY(self->basePath)) + { + if (0 == String_CopyTo(buffer, path, bufferMax) && + FALSE == IS_STRING_EMPTY(path)) + return FALSE; + else + return TRUE; + } + + if (NULL == PathCombine(buffer, self->basePath, path)) + return FALSE; + + return TRUE; +} + +BOOL +IconStore_Get(IconStore *self, wchar_t *buffer, size_t bufferMax, unsigned int width, unsigned int height) +{ + const wchar_t *icon; + IconStoreRecord *record; + size_t index; + double widthDbl, heightDbl; + double scaleMin, scaleHorz, scaleVert; + + if (NULL == self) + return FALSE; + + if (NULL == buffer) + return FALSE; + + icon = NULL; + + widthDbl = width; + heightDbl = height; + + index = self->list.size(); + if (index > 0) + { + record = &self->list[--index]; + scaleHorz = widthDbl/record->width; + scaleVert = heightDbl/record->height; + scaleMin = (scaleHorz < scaleVert) ? scaleHorz : scaleVert; + icon = record->path; + if (1.0 != scaleMin) + { + scaleMin = fabs(1.0 - scaleMin); + while(index--) + { + record = &self->list[index]; + scaleHorz = widthDbl/record->width; + scaleVert = heightDbl/record->height; + if (scaleHorz > scaleVert) + scaleHorz = scaleVert; + + if (1.0 == scaleHorz) + { + icon = record->path; + break; + } + + scaleHorz = fabs(1.0 - scaleHorz); + if (scaleHorz < scaleMin) + { + scaleMin = scaleHorz; + icon = record->path; + } + } + } + } + + return IconStore_GetFullPath(self, icon, buffer, bufferMax); +} + +BOOL +IconStore_SetBasePath(IconStore *self, const wchar_t *path) +{ + if (NULL == self) + return FALSE; + + String_Free(self->basePath); + self->basePath = String_Duplicate(path); + + return TRUE; +} + +IconStore * +IconStore_Clone(IconStore *self) +{ + size_t index; + IconStore *clone; + IconStoreRecord targetRec; + const IconStoreRecord *sourceRec; + + if (NULL == self) + return NULL; + + clone = IconStore_Create(); + if (NULL == clone) + return NULL; + + clone->basePath = String_Duplicate(self->basePath); + for(index = 0; index < self->list.size(); index++) + { + sourceRec = &self->list[index]; + targetRec.path = String_Duplicate(sourceRec->path); + targetRec.width = sourceRec->width; + targetRec.height = sourceRec->height; + clone->list.push_back(targetRec); + } + + return clone; +} + + +BOOL +IconStore_Enumerate(IconStore *self, IconEnumerator callback, void *user) +{ + size_t index; + wchar_t buffer[MAX_PATH*2]; + IconStoreRecord *record; + + if (NULL == self || NULL == callback) + return FALSE; + + index = self->list.size(); + while(index--) + { + record = &self->list[index]; + if (FALSE != IconStore_GetFullPath(self, record->path, buffer, ARRAYSIZE(buffer))) + { + if (FALSE == callback(buffer, record->width, record->height, user)) + return TRUE; + } + } + + return TRUE; + +} diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/iconStore.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/iconStore.h new file mode 100644 index 00000000..6134d1fe --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/iconStore.h @@ -0,0 +1,53 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_ICON_STORE_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_ICON_STORE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +typedef struct IconStore IconStore; +typedef BOOL (*IconEnumerator)(const wchar_t *path, unsigned int width, unsigned int height, void *user); + +IconStore * +IconStore_Create(); + +void +IconStore_Destroy(IconStore *self); + +BOOL +IconStore_Add(IconStore *self, + const wchar_t *path, + unsigned int width, + unsigned int height); + +BOOL +IconStore_RemovePath(IconStore *self, + const wchar_t *path); + +BOOL +IconStore_Remove(IconStore *self, + unsigned int width, + unsigned int height); + +BOOL +IconStore_Get(IconStore *self, + wchar_t *buffer, + size_t bufferMax, + unsigned int width, + unsigned int height); + +BOOL +IconStore_SetBasePath(IconStore *self, + const wchar_t *path); + +IconStore * +IconStore_Clone(IconStore *self); + +BOOL +IconStore_Enumerate(IconStore *self, + IconEnumerator callback, + void *user); + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_ICON_STORE_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/main.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/main.cpp new file mode 100644 index 00000000..6894dc4a --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/main.cpp @@ -0,0 +1,2 @@ +#include <initguid.h> +#include "main.h" diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/main.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/main.h new file mode 100644 index 00000000..ab1aea70 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/main.h @@ -0,0 +1,31 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_MAIN_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_MAIN_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#include "./common.h" +#include "./plugin.h" +#include "./strings.h" +#include "./stringBuilder.h" +#include "./wasabi.h" +#include "./provider.h" +#include "./resource.h" +#include "./device.h" +#include "./deviceActivity.h" +#include "./deviceView.h" +#include "./deviceIconEditor.h" +#include "./testSuite.h" +#include "./testSuiteLoader.h" +#include "./iconStore.h" + +#include "../../winamp/wa_ipc.h" +#include "../../nu/trace.h" + +#include <math.h> +#include <shlwapi.h> + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_MAIN_HEADER diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/plugin.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/plugin.cpp new file mode 100644 index 00000000..f9365033 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/plugin.cpp @@ -0,0 +1,114 @@ +#include "main.h" +#include "../../winamp/gen.h" +#include <strsafe.h> + +static INT +Plugin_Init(void); + +static void +Plugin_Quit(void); + +static void +Plugin_Config(void); + + +extern "C" winampGeneralPurposePlugin plugin = +{ + GPPHDR_VER, + 0, + Plugin_Init, + Plugin_Config, + Plugin_Quit, +}; + +static DeviceProvider *deviceProvider = NULL; + +HINSTANCE +Plugin_GetInstance(void) +{ + return plugin.hDllInstance; +} + +HWND +Plugin_GetWinampWindow(void) +{ + return plugin.hwndParent; +} + +static void +Plugin_SetDescription() +{ + WCHAR szBuffer[256], szTemplate[256]; + + if (0 != plugin.description) + AnsiString_Free(plugin.description); + + if (NULL != WASABI_API_LNG) + WASABI_API_LNGSTRINGW_BUF(IDS_PLUGIN_NAME, szTemplate, ARRAYSIZE(szTemplate)); + else + szTemplate[0] = L'\0'; + + StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), + ((L'\0' != szTemplate[0]) ? szTemplate : L"Nullsoft Test Device Provider v%d.%d"), + PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR); + + plugin.description = String_ToAnsi(CP_ACP, 0, szBuffer, -1, NULL, NULL); +} + +static INT +Plugin_Init(void) +{ + + if (FALSE == Wasabi_InitializeFromWinamp(plugin.hDllInstance, plugin.hwndParent)) + return 1; + + Wasabi_LoadDefaultServices(); + + Plugin_SetDescription(); + + if (NULL == deviceProvider) + { + if (FAILED(DeviceProvider::CreateInstance(&deviceProvider))) + { + Wasabi_Release(); + return 2; + } + + deviceProvider->Register(WASABI_API_DEVICES); + } + + return 0; +} + +static void +Plugin_Quit(void) +{ + if (0 != plugin.description) + { + AnsiString_Free(plugin.description); + plugin.description = 0; + } + + if (NULL != deviceProvider) + { + deviceProvider->Unregister(WASABI_API_DEVICES); + deviceProvider->Release(); + } + + Wasabi_Release(); +} + +static void +Plugin_Config(void) +{ +} + +EXTERN_C __declspec(dllexport) winampGeneralPurposePlugin * +winampGetGeneralPurposePlugin() +{ + if (0 == plugin.description) + { + Plugin_SetDescription(); + } + return &plugin; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/plugin.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/plugin.h new file mode 100644 index 00000000..955f5cf0 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/plugin.h @@ -0,0 +1,21 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_PLUGIN_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_PLUGIN_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#define PLUGIN_VERSION_MAJOR 1 +#define PLUGIN_VERSION_MINOR 1 + +// {51B54A05-B711-4509-80AE-5A0AAA502FA5} +DEFINE_GUID(PLUGIN_LANGUAGE_ID, +0x51b54a05, 0xb711, 0x4509, 0x80, 0xae, 0x5a, 0xa, 0xaa, 0x50, 0x2f, 0xa5); + + +HINSTANCE Plugin_GetInstance(void); +HWND Plugin_GetWinampWindow(void); + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_PLUGIN_HEADER diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/provider.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/provider.cpp new file mode 100644 index 00000000..c4146a88 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/provider.cpp @@ -0,0 +1,336 @@ +#include "main.h" +#include "./provider.h" + +typedef struct DeviceProviderThreadParam +{ + api_devicemanager *manager; + DeviceProvider *provider; + HANDLE readyEvent; + +}DeviceProviderThreadParam; + +DeviceProvider::DeviceProvider() + : ref(1), discoveryThread(NULL), cancelEvent(NULL) +{ + wchar_t buffer[MAX_PATH * 2]; + HINSTANCE module; + + TestSuiteLoader loader; + + InitializeCriticalSection(&lock); + + module = Plugin_GetInstance(); + if (0 == GetModuleFileName(module, buffer, ARRAYSIZE(buffer)) || + FALSE == PathRemoveFileSpec(buffer)) + { + buffer[0] = L'\0'; + } + PathAppend(buffer, L"testprovider.xml"); + loader.Load(buffer, &testSuite); +} + +DeviceProvider::~DeviceProvider() +{ + CancelDiscovery(); + DeleteCriticalSection(&lock); +} + +HRESULT DeviceProvider::CreateInstance(DeviceProvider **instance) +{ + if (NULL == instance) + return E_POINTER; + + *instance = new DeviceProvider(); + + if (NULL == *instance) + return E_OUTOFMEMORY; + + return S_OK; +} + +size_t DeviceProvider::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t DeviceProvider::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int DeviceProvider::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) + return E_POINTER; + + if (IsEqualIID(interface_guid, IFC_DeviceProvider)) + *object = static_cast<ifc_deviceprovider*>(this); + else + { + *object = NULL; + return E_NOINTERFACE; + } + + if (NULL == *object) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +DWORD DeviceProvider::DiscoveryThread(api_devicemanager *manager) +{ + DWORD waitResult; + DWORD deviceCount; + DWORD threadId; + DWORD sleepTime; + LARGE_INTEGER perfCounter; + + deviceCount = 0; + + threadId = GetCurrentThreadId(); +// aTRACE_FMT("[test provider] device discovery started (0x%X).\r\n", threadId); + + manager->SetProviderActive(this, TRUE); + + for(;;) + { + if (FALSE != QueryPerformanceCounter(&perfCounter)) + srand(perfCounter.LowPart); + else + srand(GetTickCount()); + + sleepTime = (DWORD)((double)rand()/(RAND_MAX) * 0); + + waitResult = WaitForSingleObject(cancelEvent, sleepTime); + if (WAIT_OBJECT_0 == waitResult) + break; + else if (WAIT_TIMEOUT == waitResult) + { + Device *device; + + deviceCount++; +// aTRACE_FMT("[test provider] creating new device[%d] (0x%X).\r\n", deviceCount, threadId); + + device = testSuite.GetRandomDevice(); + if (NULL != device) + { + device->Connect(); + if (0 == manager->DeviceRegister((ifc_device**)&device, 1)) + device->Disconnect(); + } + + if (4 == deviceCount) + break; + } + else + { +// aTRACE_FMT("[test provider] error (0x%X).\r\n", threadId); + break; + } + } + + EnterCriticalSection(&lock); + + if (NULL != discoveryThread) + { + CloseHandle(discoveryThread); + discoveryThread = NULL; + } + + if (NULL != cancelEvent) + { + CloseHandle(cancelEvent); + cancelEvent = NULL; + } + +// aTRACE_FMT("[test provider] device discovery finished (0x%X).\r\n", threadId); + LeaveCriticalSection(&lock); + + manager->SetProviderActive(this, FALSE); + + return 0; +} + +static DWORD CALLBACK DeviceProvider_DiscoveryThreadStarter(void *user) +{ + DeviceProviderThreadParam *param; + DeviceProvider *provider; + api_devicemanager *manager; + DWORD result; + + param = (DeviceProviderThreadParam*)user; + manager = param->manager; + provider = param->provider; + + if (NULL != manager) + manager->AddRef(); + + if (NULL != param->readyEvent) + SetEvent(param->readyEvent); + + if (NULL == manager) + return -1; + + if (NULL != provider) + result = provider->DiscoveryThread(manager); + else + result = -2; + + manager->Release(); + + return result; +} + +HRESULT DeviceProvider::BeginDiscovery(api_devicemanager *manager) +{ + HRESULT hr; + + if (NULL == manager) + return E_INVALIDARG; + + EnterCriticalSection(&lock); + + if (NULL != discoveryThread) + hr = E_PENDING; + else + { + hr = S_OK; + if (NULL == cancelEvent) + { + cancelEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (NULL == cancelEvent) + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + { + DeviceProviderThreadParam param; + + param.provider = this; + param.manager = manager; + param.readyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (NULL == param.readyEvent) + hr = E_FAIL; + else + { + DWORD threadId; + + discoveryThread = CreateThread(NULL, 0, DeviceProvider_DiscoveryThreadStarter, ¶m, 0, &threadId); + if (NULL == discoveryThread) + hr = E_FAIL; + else + WaitForSingleObject(param.readyEvent, INFINITE); + + CloseHandle(param.readyEvent); + } + } + + if (FAILED(hr)) + CancelDiscovery(); + } + + LeaveCriticalSection(&lock); + + return hr; +} + +HRESULT DeviceProvider::CancelDiscovery() +{ + HRESULT hr; + HANDLE threadHandle, eventHandle; + + EnterCriticalSection(&lock); + + threadHandle = discoveryThread; + eventHandle = cancelEvent; + + discoveryThread = NULL; + cancelEvent = NULL; + + LeaveCriticalSection(&lock); + + if (NULL != threadHandle) + { + if (NULL != eventHandle) + SetEvent(eventHandle); + + WaitForSingleObject(threadHandle, INFINITE); + CloseHandle(threadHandle); + hr = S_OK; + } + else + hr = S_FALSE; + + if (NULL != eventHandle) + CloseHandle(eventHandle); + + return hr; +} + +HRESULT DeviceProvider::GetActive() +{ + HRESULT hr; + + EnterCriticalSection(&lock); + + hr = (NULL != discoveryThread) ? S_OK : S_FALSE; + + LeaveCriticalSection(&lock); + + return hr; +} + +HRESULT DeviceProvider::Register(api_devicemanager *manager) +{ + HRESULT hr; + + if (NULL == manager) + return E_POINTER; + + hr = manager->RegisterProvider(this); + if (SUCCEEDED(hr)) + { + testSuite.RegisterCommands(manager); + testSuite.RegisterTypes(manager); + testSuite.RegisterConnections(manager); + testSuite.RegisterDevices(manager); + } + + return hr; +} + +HRESULT DeviceProvider::Unregister(api_devicemanager *manager) +{ + HRESULT hr; + + if (NULL == manager) + return E_POINTER; + + hr = manager->UnregisterProvider(this); + testSuite.UnregisterTypes(manager); + testSuite.UnregisterConnections(manager); + testSuite.UnregisterCommands(manager); + testSuite.UnregisterDevices(manager); + + return hr; +} + +#define CBCLASS DeviceProvider +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +CB(API_BEGINDISCOVERY, BeginDiscovery) +CB(API_CANCELDISCOVERY, CancelDiscovery) +CB(API_GETACTIVE, GetActive) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/provider.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/provider.h new file mode 100644 index 00000000..27afc72a --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/provider.h @@ -0,0 +1,50 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_IMPLEMENTATION_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_IMPLEMENTATION_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../devices/ifc_deviceprovider.h" +#include "./testSuite.h" + +class DeviceProvider : public ifc_deviceprovider +{ +protected: + DeviceProvider(); + ~DeviceProvider(); +public: + static HRESULT CreateInstance(DeviceProvider **instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /* ifc_deviceprovider */ + HRESULT BeginDiscovery(api_devicemanager *manager); + HRESULT CancelDiscovery(); + HRESULT GetActive(); +public: + HRESULT Register(api_devicemanager *manager); + HRESULT Unregister(api_devicemanager *manager); + +private: + DWORD DiscoveryThread(api_devicemanager *manager); + friend static DWORD CALLBACK DeviceProvider_DiscoveryThreadStarter(void *param); + +protected: + size_t ref; + HANDLE discoveryThread; + HANDLE cancelEvent; + TestSuite testSuite; + CRITICAL_SECTION lock; + +protected: + RECVS_DISPATCH; +}; + + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_IMPLEMENTATION_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/resource.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/resource.h new file mode 100644 index 00000000..61e9b42c --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/resource.h @@ -0,0 +1,39 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resources.rc +// +#define IDS_PLUGIN_NAME 101 +#define IDD_DEVICE_VIEW 102 +#define IDD_ICON_EDITOR 104 +#define IDC_EDIT_NAME 1001 +#define IDC_EDIT_TITLE 1002 +#define IDC_EDIT_TYPE 1003 +#define IDC_EDIT_CONNECTION 1004 +#define IDC_COMBO_ATTACHED 1005 +#define IDC_COMBO_VISIBLE 1006 +#define IDC_EDIT_TOTALSPACE 1007 +#define IDC_SPIN_TOTALSPACE 1008 +#define IDC_EDIT_USEDSPACE 1009 +#define IDC_SPIN_USEDSPACE 1010 +#define IDC_COMBO_ICONS 1011 +#define IDC_BUTTON_NEWICON 1012 +#define IDC_BUTTON_REMOVEICON 1013 +#define IDC_STATIC_PREVIEWICON 1014 +#define IDC_EDIT_WIDTH 1015 +#define IDC_EDIT_HEIGHT 1016 +#define IDC_EDIT_PATH 1017 +#define IDC_BUTTON_BROWSE 1018 +#define IDC_BUTTON1 1019 +#define IDC_BUTTON_EDITICON 1019 +#define IDS_STRING102 65535 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1020 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/resources.rc b/Src/Plugins/Library/ml_devices/gen_deviceprovider/resources.rc new file mode 100644 index 00000000..3abf6c71 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/resources.rc @@ -0,0 +1,186 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DEVICE_VIEW DIALOGEX 0, 0, 397, 282 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_NOPARENTNOTIFY | WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_EDIT_NAME,57,23,101,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_EDIT_TYPE,57,36,101,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_EDIT_CONNECTION,57,50,101,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_EDIT_TITLE,57,70,101,12,ES_AUTOHSCROLL + COMBOBOX IDC_COMBO_ATTACHED,57,85,101,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_VISIBLE,57,101,101,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_EDIT_TOTALSPACE,57,117,101,16,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SPIN_TOTALSPACE,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,149,118,9,12,WS_EX_TRANSPARENT + EDITTEXT IDC_EDIT_USEDSPACE,57,134,101,16,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SPIN_USEDSPACE,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,149,136,9,12,WS_EX_TRANSPARENT + COMBOBOX IDC_COMBO_ICONS,169,34,221,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "New",IDC_BUTTON_NEWICON,264,49,40,14 + PUSHBUTTON "Edit",IDC_BUTTON_EDITICON,307,49,40,14 + PUSHBUTTON "Remove",IDC_BUTTON_REMOVEICON,350,49,40,14 + CONTROL "",IDC_STATIC_PREVIEWICON,"Static",SS_BITMAP | SS_CENTERIMAGE,169,70,221,170,WS_EX_STATICEDGE + LTEXT "Device View",IDC_STATIC,7,7,39,8 + CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,7,16,383,1 + LTEXT "Name:",IDC_STATIC,15,24,22,8 + LTEXT "Title:",IDC_STATIC,15,71,17,8 + LTEXT "Type:",IDC_STATIC,15,37,20,8 + LTEXT "Connection:",IDC_STATIC,15,51,40,8 + LTEXT "Attached:",IDC_STATIC,15,87,33,8 + LTEXT "Visible:",IDC_STATIC,15,103,23,8 + LTEXT "Capacity:",IDC_STATIC,15,119,32,8 + LTEXT "Used Space:",IDC_STATIC,15,136,41,8 + LTEXT "Icons:",IDC_STATIC,169,22,21,8 +END + +IDD_ICON_EDITOR DIALOGEX 0, 0, 290, 79 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Device Icon" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,180,58,50,14 + PUSHBUTTON "Cancel",IDCANCEL,233,58,50,14 + LTEXT "Width:",IDC_STATIC,8,34,35,8 + LTEXT "Height:",IDC_STATIC,47,33,29,8 + EDITTEXT IDC_EDIT_WIDTH,7,44,35,14,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_EDIT_HEIGHT,46,44,35,14,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_EDIT_PATH,7,18,250,14,ES_AUTOHSCROLL + LTEXT "Path:",IDC_STATIC,7,7,18,8 + PUSHBUTTON "...",IDC_BUTTON_BROWSE,261,18,22,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_DEVICE_VIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 390 + VERTGUIDE, 15 + VERTGUIDE, 57 + VERTGUIDE, 158 + VERTGUIDE, 169 + TOPMARGIN, 7 + BOTTOMMARGIN, 275 + END + + IDD_ICON_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 283 + VERTGUIDE, 42 + VERTGUIDE, 46 + TOPMARGIN, 7 + BOTTOMMARGIN, 72 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_DEVICE_VIEW DLGINIT +BEGIN + IDC_COMBO_ATTACHED, 0x403, 4, 0 +0x6559, 0x0073, + IDC_COMBO_ATTACHED, 0x403, 3, 0 +0x6f4e, "\000" + IDC_COMBO_VISIBLE, 0x403, 4, 0 +0x6559, 0x0073, + IDC_COMBO_VISIBLE, 0x403, 3, 0 +0x6f4e, "\000" + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_PLUGIN_NAME "Nullsoft Test Device Provider v%d.%d" +END + +STRINGTABLE +BEGIN + 65535 "{51B54A05-B711-4509-80AE-5A0AAA502FA5}" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/stringBuilder.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/stringBuilder.cpp new file mode 100644 index 00000000..85596feb --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/stringBuilder.cpp @@ -0,0 +1,85 @@ +#include "main.h" +#include "./stringBuilder.h" +#include <strsafe.h> + +StringBuilder::StringBuilder() + : buffer(NULL), cursor(NULL), allocated(0), remaining(0) +{ +} + +StringBuilder::~StringBuilder() +{ + String_Free(buffer); +} + +HRESULT StringBuilder::Allocate(size_t newSize) +{ + if (newSize <= allocated) + return S_FALSE; + + LPWSTR t = String_ReAlloc(buffer, newSize); + if (NULL == t) return E_OUTOFMEMORY; + + cursor = t + (cursor - buffer); + buffer = t; + + remaining += newSize - allocated; + allocated = newSize; + + return S_OK; +} + +void StringBuilder::Clear(void) +{ + if (NULL != buffer) + { + buffer[0] = L'\0'; + } + cursor = buffer; + remaining = allocated; +} + +LPCWSTR StringBuilder::Get(void) +{ + return buffer; +} + +HRESULT StringBuilder::Set(size_t index, WCHAR value) +{ + if (NULL == buffer) + return E_POINTER; + + if (index >= allocated) + return E_INVALIDARG; + + buffer[index] = value; + return S_OK; +} + +HRESULT StringBuilder::Append(LPCWSTR pszString) +{ + HRESULT hr; + if (NULL == buffer) + { + hr = Allocate(1024); + if (FAILED(hr)) return hr; + } + + size_t cchCursor = remaining; + hr = StringCchCopyEx(cursor, cchCursor, pszString, &cursor, &remaining, STRSAFE_IGNORE_NULLS); + if (STRSAFE_E_INSUFFICIENT_BUFFER == hr) + { + size_t offset = cchCursor - remaining; + size_t requested = lstrlen(pszString) + (allocated - remaining) + 1; + size_t newsize = allocated * 2; + while (newsize < requested) newsize = newsize * 2; + + hr = Allocate(newsize); + if (FAILED(hr)) return hr; + + hr = StringCchCopyEx(cursor, remaining, pszString + offset, &cursor, &remaining, STRSAFE_IGNORE_NULLS); + } + + return hr; +} +
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/stringBuilder.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/stringBuilder.h new file mode 100644 index 00000000..cc2170b7 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/stringBuilder.h @@ -0,0 +1,31 @@ +#ifndef NULLSOFT_WINAMP_STRING_BUILDER_HEADER +#define NULLSOFT_WINAMP_STRING_BUILDER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +class StringBuilder +{ +public: + StringBuilder(); + ~StringBuilder(); + +public: + HRESULT Allocate(size_t newSize); + void Clear(void); + LPCWSTR Get(void); + HRESULT Set(size_t index, WCHAR value); + HRESULT Append(LPCWSTR pszString); + + +protected: + LPWSTR buffer; + LPWSTR cursor; + size_t allocated; + size_t remaining; +}; + +#endif //NULLSOFT_WINAMP_STRING_BUILDER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/strings.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/strings.cpp new file mode 100644 index 00000000..aae4c53f --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/strings.cpp @@ -0,0 +1,243 @@ +#include "main.h" +#include "./strings.h" +#include <strsafe.h> + + +wchar_t * +String_Malloc(size_t size) +{ + return (wchar_t *)malloc(sizeof(wchar_t) * size); +} + +wchar_t * +String_ReAlloc(wchar_t *string, size_t size) +{ + return (wchar_t *)realloc(string, sizeof(wchar_t) * size); +} + +void +String_Free(wchar_t *string) +{ + if (NULL != string) + free(string); +} + +wchar_t * +String_Duplicate(const wchar_t *string) +{ + int length; + wchar_t *copy; + + if (NULL == string) + return NULL; + + length = lstrlenW(string) + 1; + + copy = String_Malloc(length); + if (NULL != copy) + CopyMemory(copy, string, sizeof(wchar_t) * length); + + return copy; +} + + +char * +String_ToAnsi(unsigned int codePage, unsigned long flags, const wchar_t *string, + int stringLength, const char *defaultChar, BOOL *usedDefaultChar) +{ + char *buffer; + int bufferSize; + + if (stringLength < 0) + stringLength = lstrlen(string); + + bufferSize = WideCharToMultiByte(codePage, flags, string, stringLength, + NULL, 0, defaultChar, usedDefaultChar); + if (0 == bufferSize) + return NULL; + + buffer = AnsiString_Malloc(bufferSize + 1); + if (NULL == buffer) + return NULL; + + bufferSize = WideCharToMultiByte(codePage, flags, string, stringLength, + buffer, bufferSize, defaultChar, usedDefaultChar); + if (0 == bufferSize) + { + AnsiString_Free(buffer); + return NULL; + } + buffer[bufferSize] = '\0'; + return buffer; +} + +size_t +String_CopyTo(wchar_t *destination, const wchar_t *source, size_t size) +{ + size_t remaining; + if (FAILED(StringCchCopyExW(destination, size, source, NULL, &remaining, STRSAFE_IGNORE_NULLS))) + return 0; + + return (size - remaining); +} + +wchar_t * +String_FromWindowEx(HWND hwnd, size_t *lengthOut, BOOL *errorOut) +{ + BOOL error; + size_t length; + wchar_t *string; + + error = TRUE; + string = NULL; + length = 0; + + if (NULL != hwnd) + { + length = GetWindowTextLength(hwnd); + if (0 != length || + ERROR_SUCCESS == GetLastError()) + { + string = String_Malloc(length + 1); + if(NULL != string) + { + if (0 == length) + { + string[0] = L'\0'; + error = FALSE; + } + else + { + length = GetWindowText(hwnd, string, (int)length + 1); + if (0 == length && ERROR_SUCCESS != GetLastError()) + { + String_Free(string); + string = NULL; + } + else + error = FALSE; + } + } + } + } + + if (NULL != lengthOut) + *lengthOut = length; + + if (NULL != errorOut) + *errorOut = error; + + return string; +} + +char * +AnsiString_Malloc(size_t size) +{ + return (char*)malloc(sizeof(char) * size); +} + +char * +AnsiString_ReAlloc(char *string, size_t size) +{ + return (char*)realloc(string, sizeof(char) * size); +} + +void +AnsiString_Free(char *string) +{ + if (NULL != string) + free(string); +} + +char * +AnsiString_Duplicate(const char *string) +{ + char *copy; + INT length; + + if (NULL == string) + return NULL; + + length = lstrlenA(string) + 1; + + copy = AnsiString_Malloc(length); + if (NULL != copy) + CopyMemory(copy, string, sizeof(char) * length); + + return copy; +} + + +wchar_t * +AnsiString_ToUnicode(unsigned int codePage, unsigned long flags, const char* string, INT stringLength) +{ + wchar_t *buffer; + int buffferSize; + + if (NULL == string) + return NULL; + + buffferSize = MultiByteToWideChar(codePage, flags, string, stringLength, NULL, 0); + if (0 == buffferSize) + return NULL; + + if (stringLength > 0) + buffferSize++; + + buffer = String_Malloc(buffferSize); + if (NULL == buffer) + return NULL; + + if (0 == MultiByteToWideChar(codePage, flags, string, stringLength, buffer, buffferSize)) + { + String_Free(buffer); + return NULL; + } + + if (stringLength > 0) + buffer[buffferSize - 1] = L'\0'; + + return buffer; +} + +wchar_t* +ResourceString_Duplicate(const wchar_t *source) +{ + return (FALSE != IS_INTRESOURCE(source)) ? + (LPWSTR)source : + String_Duplicate(source); +} + +void +ResourceString_Free(wchar_t *string) +{ + if (FALSE == IS_INTRESOURCE(string)) + String_Free(string); +} + +size_t +ResourceString_CopyTo(wchar_t *destination, const wchar_t *source, size_t size) +{ + if (NULL == destination) + return 0; + + if (NULL == source) + { + destination[0] = L'\0'; + return 0; + } + + if (FALSE != IS_INTRESOURCE(source)) + { + if (NULL == WASABI_API_LNG) + { + destination[0] = L'\0'; + return 0; + } + + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)source, destination, size); + return lstrlenW(destination); + } + + return String_CopyTo(destination, source, size); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/strings.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/strings.h new file mode 100644 index 00000000..c2c11baf --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/strings.h @@ -0,0 +1,82 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_STRINGS_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_STRINGS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#define IS_STRING_EMPTY(_string) (NULL == (_string) || L'\0' == *(_string)) + +wchar_t * +String_Malloc(size_t size); + +wchar_t * +String_ReAlloc(wchar_t *string, + size_t size); + +void +String_Free(wchar_t *string); + +wchar_t * +String_Duplicate(const wchar_t *string); + +char * +String_ToAnsi(unsigned int codePage, + unsigned long flags, + const wchar_t *string, + int stringLength, + const char *defaultChar, + BOOL *usedDefaultChar); + +size_t +String_CopyTo(wchar_t *destination, + const wchar_t *source, + size_t size); + +wchar_t * +String_FromWindowEx(HWND hwnd, + size_t *lengthOut, + BOOL *errorOut); + +#define String_FromWindow(/*HWND*/ _hwnd)\ + String_FromWindowEx((_hwnd), NULL, NULL) + +/* + Ansi String +*/ + +char * +AnsiString_Malloc(size_t size); + +char * +AnsiString_ReAlloc(char *string, + size_t size); + +void +AnsiString_Free(char *string); + +char * +AnsiString_Duplicate(const char *string); + +wchar_t * +AnsiString_ToUnicode(unsigned int codePage, + unsigned long flags, + const char *string, + int stringLength); + +/* + Resource String +*/ + +wchar_t* +ResourceString_Duplicate(const wchar_t *source); + +void +ResourceString_Free(wchar_t *string); + +size_t +ResourceString_CopyTo(wchar_t *destination, + const wchar_t *source, + size_t size); + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_STRINGS_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/supportedCommand.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/supportedCommand.cpp new file mode 100644 index 00000000..24966cf0 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/supportedCommand.cpp @@ -0,0 +1,108 @@ +#include "main.h" +#include "./supportedCommand.h" + +#include <strsafe.h> + +DeviceSupportedCommand::DeviceSupportedCommand() + : ref(1), name(NULL), flags(DeviceCommandFlag_None) +{ +} + +DeviceSupportedCommand::~DeviceSupportedCommand() +{ + AnsiString_Free(name); +} + +HRESULT DeviceSupportedCommand::CreateInstance(const char *name, DeviceSupportedCommand **instance) +{ + DeviceSupportedCommand *self; + + if (NULL == instance) + return E_POINTER; + + *instance = NULL; + + self = new DeviceSupportedCommand(); + if (NULL == self) + return E_OUTOFMEMORY; + + self->name = AnsiString_Duplicate(name); + + *instance = self; + return S_OK; +} + +size_t DeviceSupportedCommand::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t DeviceSupportedCommand::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int DeviceSupportedCommand::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) + return E_POINTER; + + if (IsEqualIID(interface_guid, IFC_DeviceSupportedCommand)) + *object = static_cast<ifc_devicesupportedcommand*>(this); + else + { + *object = NULL; + return E_NOINTERFACE; + } + + if (NULL == *object) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +const char *DeviceSupportedCommand::GetName() +{ + return name; +} + +HRESULT DeviceSupportedCommand::GetFlags(DeviceCommandFlags *flagsOut) +{ + if (NULL == flagsOut) + return E_POINTER; + + *flagsOut = flags; + + return S_OK; +} + +HRESULT DeviceSupportedCommand::SetFlags(DeviceCommandFlags mask, DeviceCommandFlags value) +{ + DeviceCommandFlags temp; + temp = (flags & mask) | (mask & value); + + if (temp == flags) + return S_FALSE; + + flags = temp; + + return S_OK; +} + +#define CBCLASS DeviceSupportedCommand +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +CB(API_GETNAME, GetName) +CB(API_GETFLAGS, GetFlags) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/supportedCommand.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/supportedCommand.h new file mode 100644 index 00000000..6cdc2700 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/supportedCommand.h @@ -0,0 +1,46 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_SUPPORTED_COMMAND_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_SUPPORTED_COMMAND_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include <ifc_devicesupportedcommand.h> + + +class DeviceSupportedCommand : public ifc_devicesupportedcommand +{ +protected: + DeviceSupportedCommand(); + ~DeviceSupportedCommand(); + +public: + static HRESULT CreateInstance(const char *name, + DeviceSupportedCommand **instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /* ifc_devicesupportedcommand */ + const char *GetName(); + HRESULT GetFlags(DeviceCommandFlags *flags); + +public: + HRESULT SetFlags(DeviceCommandFlags mask, DeviceCommandFlags value); + +protected: + size_t ref; + char *name; + DeviceCommandFlags flags; + + +protected: + RECVS_DISPATCH; +}; + + +#endif //_NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_SUPPORTED_COMMAND_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuite.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuite.cpp new file mode 100644 index 00000000..c5821925 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuite.cpp @@ -0,0 +1,555 @@ +#include "main.h" +#include "./testSuite.h" + +#include <strsafe.h> + +TestSuite::TestSuite() +{ +} + +TestSuite::~TestSuite() +{ + size_t index; + + index = deviceList.size(); + while(index--) + { + deviceList[index]->Release(); + } + + index = typeList.size(); + while(index--) + { + typeList[index]->Release(); + } + + index = connectionList.size(); + while(index--) + { + connectionList[index]->Release(); + } + + index = commandList.size(); + while(index--) + { + commandList[index]->Release(); + } + + index = insertList.size(); + while(index--) + { + AnsiString_Free(insertList[index]); + } +} + +BOOL TestSuite::AddDevice(Device *device) +{ + if (NULL == device) + return FALSE; + + deviceList.push_back(device); + device->AddRef(); + + return TRUE; +} + +size_t TestSuite::GetDeviceCount() +{ + return deviceList.size(); +} + +Device *TestSuite::GetDevice(size_t index) +{ + return deviceList[index]; +} + +static int +String_RemoveCounter(wchar_t *string) +{ + int len; + + len = lstrlenW(string); + if (len > 3) + { + int cutoff = 0; + if (string[len-1] == L')') + { + WORD charType; + cutoff = 1; + while(len > cutoff && + FALSE != GetStringTypeW(CT_CTYPE1, string + (len - 1) - cutoff, 1, &charType) && + 0 != (C1_DIGIT & charType)) + { + cutoff++; + } + + if (len > cutoff && + cutoff > 1 && + L'(' == string[len - 1 - cutoff]) + { + cutoff++; + if (len > cutoff && + L' ' == string[len - 1 - cutoff]) + { + string[len - 1 - cutoff] = L'\0'; + len -= (cutoff + 1); + } + } + } + } + + return len; +} + + +static int +AnsiString_RemoveCounter(char *string) +{ + int len; + + len = lstrlenA(string); + if (len > 3) + { + int cutoff = 0; + if (string[len-1] == ')') + { + WORD charType; + cutoff = 1; + while(len > cutoff && + FALSE != GetStringTypeA(LOCALE_SYSTEM_DEFAULT, CT_CTYPE1, string + (len - 1) - cutoff, 1, &charType) && + 0 != (C1_DIGIT & charType)) + { + cutoff++; + } + + if (len > cutoff && + cutoff > 1 && + '(' == string[len - 1 - cutoff]) + { + cutoff++; + if (len > cutoff && + ' ' == string[len - 1 - cutoff]) + { + string[len - 1 - cutoff] = '\0'; + len -= (cutoff + 1); + } + } + } + } + + return len; +} + +Device *TestSuite::CreateDeviceCopy(Device *source) +{ + const char *name; + size_t index, counter; + char buffer[1024]; + wchar_t *displayName; + BOOL found; + Device *destination; + int length; + + if (NULL == source) + return NULL; + + found = FALSE; + counter = 0; + + name = source->GetName(); + StringCbCopyA(buffer, sizeof(buffer), name); + length = AnsiString_RemoveCounter(buffer); + + for (;;) + { + + if (0 != counter) + StringCbPrintfA(buffer + length, sizeof(buffer) - length, " (%d)", counter); + + found = TRUE; + + index = deviceList.size(); + while(index--) + { + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, deviceList[index]->GetName(), -1, buffer, -1)) + { + found = FALSE; + break; + } + } + + if (FALSE != found) + break; + else + counter++; + } + + if (FAILED(Device::CreateInstance(buffer, source->GetType(), source->GetConnection(), &destination))) + return NULL; + + source->CopyTo(destination); + + source->GetDisplayName((wchar_t*)buffer, sizeof(buffer)/sizeof(wchar_t)); + displayName = (wchar_t*)buffer; + length = String_RemoveCounter(displayName); + + if (0 != counter) + StringCchPrintf(displayName + length, sizeof(buffer)/sizeof(wchar_t) - length, L" (%d)", counter); + + destination->SetDisplayName(displayName); + + + return destination; + +} +Device *TestSuite::GetRandomDevice() +{ + size_t index; + Device *device; + LARGE_INTEGER perfCounter; + + if (0 == deviceList.size()) + return NULL; + + if (FALSE != QueryPerformanceCounter(&perfCounter)) + srand(perfCounter.LowPart); + else + srand(GetTickCount()); + + index = (size_t)((double)rand()/(RAND_MAX + 1) * deviceList.size()); + device = deviceList[index]; + + if (S_OK == device->IsConnected()) + { + size_t search; + + for(search = index + 1; search < deviceList.size(); search++) + { + device = deviceList[search]; + if (S_FALSE == device->IsConnected()) + return device; + } + + search = index; + while(search--) + { + device = deviceList[search]; + if (S_FALSE == device->IsConnected()) + return device; + } + + device = CreateDeviceCopy(deviceList[index]); + if (NULL != device) + { + size_t totalSpace, usedSpace; + totalSpace = (size_t)((double)rand()/(RAND_MAX + 1) * 1000); + usedSpace = (size_t)((double)rand()/(RAND_MAX + 1) * totalSpace); + + device->SetTotalSpace(totalSpace); + device->SetUsedSpace(usedSpace); + + device->Disconnect(); + device->Detach(NULL); + AddDevice(device); + } + } + + return device; +} + +BOOL TestSuite::AddType(ifc_devicetype *type) +{ + if (NULL == type) + return FALSE; + + typeList.push_back(type); + type->AddRef(); + + return TRUE; +} + +size_t TestSuite::GetTypeCount() +{ + return typeList.size(); +} + +ifc_devicetype *TestSuite::GetType(size_t index) +{ + return typeList[index]; +} + + +BOOL TestSuite::RegisterTypes(api_devicemanager *manager) +{ + if (NULL == manager) + return FALSE; + + if (0 != typeList.size()) + manager->TypeRegister((ifc_devicetype**)typeList.begin(), typeList.size()); + + return TRUE; +} + +BOOL TestSuite::UnregisterTypes(api_devicemanager *manager) +{ + size_t index; + if (NULL == manager) + return FALSE; + + index = typeList.size(); + while(index--) + { + manager->TypeUnregister(typeList[index]->GetName()); + } + + return TRUE; +} + +BOOL TestSuite::SetIconBase(const wchar_t *path) +{ + size_t index; + Device *device; + ifc_devicetype *type; + ifc_devicetypeeditor *typeEditor; + ifc_deviceiconstore *iconStore; + ifc_deviceconnection *connection; + ifc_deviceconnectioneditor *connectionEditor; + ifc_devicecommand *command; + ifc_devicecommandeditor *commandEditor; + + index = deviceList.size(); + while(index--) + { + device = deviceList[index]; + device->SetIconBase(path); + } + + index = typeList.size(); + while(index--) + { + type = typeList[index]; + if (SUCCEEDED(type->QueryInterface(IFC_DeviceTypeEditor, (void**)&typeEditor))) + { + if(SUCCEEDED(typeEditor->GetIconStore(&iconStore))) + { + iconStore->SetBasePath(path); + iconStore->Release(); + } + typeEditor->Release(); + } + } + + index = commandList.size(); + while(index--) + { + command = commandList[index]; + if (SUCCEEDED(command->QueryInterface(IFC_DeviceCommandEditor, (void**)&commandEditor))) + { + if(SUCCEEDED(commandEditor->GetIconStore(&iconStore))) + { + iconStore->SetBasePath(path); + iconStore->Release(); + } + commandEditor->Release(); + } + } + + index = connectionList.size(); + while(index--) + { + connection = connectionList[index]; + if (SUCCEEDED(connection->QueryInterface(IFC_DeviceConnectionEditor, (void**)&connectionEditor))) + { + if(SUCCEEDED(connectionEditor->GetIconStore(&iconStore))) + { + iconStore->SetBasePath(path); + iconStore->Release(); + } + connectionEditor->Release(); + } + } + + + return S_OK; +} + +BOOL TestSuite::SetConnectList(char **devices, size_t count) +{ + size_t index; + char *name; + + index = insertList.size(); + if (index > 0) + { + while(index--) + { + name = insertList[index]; + AnsiString_Free(name); + } + insertList.clear(); + } + + for(index = 0; index < count; index++) + { + name = AnsiString_Duplicate(devices[index]); + if (NULL != name) + insertList.push_back(name); + } + + return TRUE; +} + +Device *TestSuite::GetDeviceByName(const char *name) +{ + size_t index; + Device *device; + + for (index = 0; index < deviceList.size(); index++) + { + device = deviceList[index]; + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, name, -1, device->GetName(), -1)) + return device; + } + + return NULL; +} + +BOOL TestSuite::RegisterDevices(api_devicemanager *manager) +{ + size_t index; + const char *name; + Device *device; + + if (NULL == manager) + return FALSE; + + for (index = 0; index < insertList.size(); index++) + { + name = insertList[index]; + device = GetDeviceByName(name); + if (NULL != device) + { + manager->DeviceRegister((ifc_device**)&device, 1); + device->Connect(); + } + } + + return TRUE; +} + +BOOL TestSuite::UnregisterDevices(api_devicemanager *manager) +{ + size_t index; + Device *device; + + if (NULL == manager) + return FALSE; + + index = deviceList.size(); + while(index--) + { + device = deviceList[index]; + if (S_OK == device->IsConnected()) + { + device->Disconnect(); + manager->DeviceUnregister(device->GetName()); + } + } + + return TRUE; +} + +BOOL TestSuite::AddConnection(ifc_deviceconnection *connection) +{ + if (NULL == connection) + return FALSE; + + connectionList.push_back(connection); + connection->AddRef(); + + return TRUE; +} + +size_t TestSuite::GetConnectionCount() +{ + return connectionList.size(); +} + +ifc_deviceconnection *TestSuite::GetConnection(size_t index) +{ + return connectionList[index]; +} + +BOOL TestSuite::RegisterConnections(api_devicemanager *manager) +{ + if (NULL == manager) + return FALSE; + + if (0 != connectionList.size()) + manager->ConnectionRegister((ifc_deviceconnection**)connectionList.begin(), connectionList.size()); + + return TRUE; +} + +BOOL TestSuite::UnregisterConnections(api_devicemanager *manager) +{ + size_t index; + if (NULL == manager) + return FALSE; + + index = connectionList.size(); + while(index--) + { + manager->ConnectionUnregister(connectionList[index]->GetName()); + } + + return TRUE; +} + + +BOOL TestSuite::AddCommand(ifc_devicecommand *command) +{ + if (NULL == command) + return FALSE; + + commandList.push_back(command); + command->AddRef(); + + return TRUE; +} + +size_t TestSuite::GetCommandCount() +{ + return commandList.size(); +} + +ifc_devicecommand *TestSuite::GetCommand(size_t index) +{ + return commandList[index]; +} + +BOOL TestSuite::RegisterCommands(api_devicemanager *manager) +{ + if (NULL == manager) + return FALSE; + + if (0 != commandList.size()) + manager->CommandRegister((ifc_devicecommand**)commandList.begin(), commandList.size()); + + return TRUE; +} + +BOOL TestSuite::UnregisterCommands(api_devicemanager *manager) +{ + size_t index; + if (NULL == manager) + return FALSE; + + index = commandList.size(); + while(index--) + { + manager->CommandUnregister(commandList[index]->GetName()); + } + + return TRUE; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuite.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuite.h new file mode 100644 index 00000000..596f9731 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuite.h @@ -0,0 +1,67 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_TEST_SUITE_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_TEST_SUITE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include <vector> +#include "./device.h" + +class TestSuite +{ +public: + TestSuite(); + ~TestSuite(); + +public: + BOOL AddDevice(Device *device); + size_t GetDeviceCount(); + Device *GetDevice(size_t index); + Device *GetRandomDevice(); + Device *CreateDeviceCopy(Device *source); + Device *GetDeviceByName(const char *name); + BOOL RegisterDevices(api_devicemanager *manager); + BOOL UnregisterDevices(api_devicemanager *manager); + + BOOL AddType(ifc_devicetype *type); + size_t GetTypeCount(); + ifc_devicetype *GetType(size_t index); + BOOL RegisterTypes(api_devicemanager *manager); + BOOL UnregisterTypes(api_devicemanager *manager); + + BOOL AddConnection(ifc_deviceconnection *connection); + size_t GetConnectionCount(); + ifc_deviceconnection *GetConnection(size_t index); + BOOL RegisterConnections(api_devicemanager *manager); + BOOL UnregisterConnections(api_devicemanager *manager); + + BOOL AddCommand(ifc_devicecommand *command); + size_t GetCommandCount(); + ifc_devicecommand *GetCommand(size_t index); + BOOL RegisterCommands(api_devicemanager *manager); + BOOL UnregisterCommands(api_devicemanager *manager); + + BOOL SetIconBase(const wchar_t *path); + BOOL SetConnectList(char **devices, size_t count); + +private: + typedef std::vector<Device*> DeviceList; + typedef std::vector<ifc_devicetype*> TypeList; + typedef std::vector<ifc_deviceconnection*> ConnectionList; + typedef std::vector<ifc_devicecommand*> CommandList; + + typedef std::vector<char*> NameList; + +private: + DeviceList deviceList; + TypeList typeList; + ConnectionList connectionList; + CommandList commandList; + NameList insertList; + + +}; + +#endif // _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_TEST_SUITE_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuiteLoader.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuiteLoader.cpp new file mode 100644 index 00000000..0496e909 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuiteLoader.cpp @@ -0,0 +1,226 @@ +#include "main.h" +#include "./testSuiteLoader.h" + +#include "../../xml/obj_xml.h" +#include <api/service/waservicefactory.h> + +#include <strsafe.h> + + +typedef void (CALLBACK *LOADERTAGCALLBACK)(TestSuiteLoader* /*loader*/, const wchar_t* /*value*/); + +typedef struct LOADERTAG +{ + const wchar_t *name; + LOADERTAGCALLBACK callback; +} LOADERTAG; + +static void CALLBACK +LoaderTag_ImageBase(TestSuiteLoader *loader, const wchar_t *value) +{ + String_Free(loader->imageBase); + loader->imageBase = String_Duplicate(value); +} + +static void CALLBACK +LoaderTag_Connect(TestSuiteLoader *loader, const wchar_t *value) +{ + if (IS_STRING_EMPTY(value)) + return; + + const wchar_t *block, *cursor; + char *name; + size_t length; + + block = value; + cursor = block; + for(;;) + { + if (L'\0' == *cursor || + L';' == *cursor || + L',' == *cursor) + { + + if (block < cursor) + { + length = cursor - block; + name = String_ToAnsi(CP_UTF8, 0, block, (int)length, NULL, NULL); + if (NULL != name) + loader->connectList.push_back(name); + } + + if (L'\0' == *cursor) + break; + + block = cursor + 1; + } + cursor++; + } + //loader->SetImageBase(value); +} + + +static const LOADERTAG knownTags[LOADER_TAG_MAX] = +{ + {L"imageBase", LoaderTag_ImageBase}, + {L"connect", LoaderTag_Connect}, +}; + +TestSuiteLoader::TestSuiteLoader() + : imageBase(NULL) +{ +} + +TestSuiteLoader::~TestSuiteLoader() +{ + size_t index; + + index = connectList.size(); + while(index--) + { + AnsiString_Free(connectList[index]); + } + + String_Free(imageBase); + +} + +BOOL TestSuiteLoader::Load(const wchar_t *path, TestSuite *testSuite) +{ + BOOL result; + HANDLE fileHandle; + obj_xml *reader; + + if (NULL == testSuite) + return FALSE; + + if (NULL == path || L'\0' == *path) + return FALSE; + + fileHandle = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == fileHandle) + return FALSE; + + result = FALSE; + if (NULL != WASABI_API_SVC) + { + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(obj_xmlGUID); + reader = (NULL != sf) ? (obj_xml*)sf->getInterface() : NULL; + if (NULL != reader) + { + if (OBJ_XML_SUCCESS == reader->xmlreader_open()) + { + reader->xmlreader_registerCallback(L"testprovider\fimageBase", this); + reader->xmlreader_registerCallback(L"testprovider\fconnect", this); + ZeroMemory(hitList, sizeof(hitList)); + + deviceParser.Begin(reader, testSuite); + typeParser.Begin(reader, testSuite); + connectionParser.Begin(reader, testSuite); + commandParser.Begin(reader, testSuite); + + result = FeedFile(reader, fileHandle, 8192); + + deviceParser.End(); + typeParser.End(); + connectionParser.End(); + commandParser.End(); + + reader->xmlreader_close(); + + testSuite->SetIconBase(imageBase); + testSuite->SetConnectList(connectList.begin(), connectList.size()); + + } + sf->releaseInterface(reader); + } + } + + CloseHandle(fileHandle); + + return result; +} + +BOOL TestSuiteLoader::FeedFile(obj_xml *reader, HANDLE fileHandle, DWORD bufferSize) +{ + BOOL result; + DWORD read; + BYTE *buffer; + int readerCode; + + if (NULL == reader || + INVALID_HANDLE_VALUE == fileHandle || + 0 == bufferSize) + { + return FALSE; + } + + buffer = (BYTE*)malloc(bufferSize); + if (NULL == buffer) + return FALSE; + + + readerCode = OBJ_XML_SUCCESS; + result = TRUE; + + for(;;) + { + if (FALSE == ReadFile(fileHandle, buffer, bufferSize, &read, NULL) || 0 == read) + { + result = FALSE; + + if (0 == read && OBJ_XML_SUCCESS == readerCode) + reader->xmlreader_feed(0, 0); + + break; + } + + readerCode = reader->xmlreader_feed(buffer, read); + if (OBJ_XML_SUCCESS != readerCode) + { + result = FALSE; + break; + } + } + + free(buffer); + return result; +} + +void TestSuiteLoader::Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + elementString.Clear(); +} + +void TestSuiteLoader::Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag) +{ + for (size_t i = 0; i < LOADER_TAG_MAX; i++) + { + if (FALSE == hitList[i] && + CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, knownTags[i].name, -1, xmltag, -1)) + { + knownTags[i].callback(this, elementString.Get()); + hitList[i] = TRUE; + break; + } + } +} + +void TestSuiteLoader::Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value) +{ + elementString.Append(value); +} + +void TestSuiteLoader::Event_XmlError(int linenum, int errcode, const wchar_t *errstr) +{ + elementString.Clear(); +} + +#define CBCLASS TestSuiteLoader +START_DISPATCH; +VCB(ONSTARTELEMENT, Event_XmlStartElement) +VCB(ONENDELEMENT, Event_XmlEndElement) +VCB(ONCHARDATA, Event_XmlCharData) +VCB(ONERROR, Event_XmlError) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuiteLoader.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuiteLoader.h new file mode 100644 index 00000000..1bfcb43e --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuiteLoader.h @@ -0,0 +1,60 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_TEST_SUITE_LOADER_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_TEST_SUITE_LOADER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../../xml/ifc_xmlreadercallback.h" +#include <vector> +#include "./DeviceNodeParser.h" +#include "./DeviceTypeNodeParser.h" +#include "./DeviceConnectionNodeParser.h" +#include "./DeviceCommandNodeParser.h" + +class obj_xml; + +#define LOADER_TAG_MAX 2 + +class TestSuiteLoader : public ifc_xmlreadercallback +{ + +public: + TestSuiteLoader(); + ~TestSuiteLoader(); + +public: + BOOL Load(const wchar_t *path, TestSuite *testSuite); + +private: + BOOL FeedFile(obj_xml *reader, HANDLE hFile, DWORD bufferSize); + + void Event_XmlStartElement(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void Event_XmlEndElement(const wchar_t *xmlpath, const wchar_t *xmltag); + void Event_XmlCharData(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *value); + void Event_XmlError(int linenum, int errcode, const wchar_t *errstr); + +protected: + friend static void CALLBACK LoaderTag_ImageBase(TestSuiteLoader *loader, const wchar_t *value); + friend static void CALLBACK LoaderTag_Connect(TestSuiteLoader *loader, const wchar_t *value); +protected: + typedef std::vector<char*> NameList; + +protected: + StringBuilder elementString; + DeviceNodeParser deviceParser; + DeviceTypeNodeParser typeParser; + DeviceConnectionNodeParser connectionParser; + DeviceCommandNodeParser commandParser; + BOOL hitList[LOADER_TAG_MAX]; + + wchar_t *imageBase; + + NameList connectList; + +protected: + RECVS_DISPATCH; +}; + +#endif // _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_TEST_SUITE_LOADER_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/testprovider.xml b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testprovider.xml new file mode 100644 index 00000000..f5213363 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/testprovider.xml @@ -0,0 +1,244 @@ +<?xml version="1.0" encoding="UTF-8"?> +<testprovider> + <imageBase>c:\winamp\ml_devices\resources\food</imageBase> + <connect>iconTest;apple;banana;grape;pear;pepper;eggplant;cucumber;carrot</connect> + <connections> + <connection name="usb"> + <displayName>USB</displayName> + <icon>..\connections\usb.png</icon> + </connection> + <connection name="wifi"> + <displayName>Wi-Fi</displayName> + <icon>..\connections\wifi.png</icon> + </connection> + <connection name="bluetooth"> + <displayName>Bluetooth</displayName> + <icon>..\connections\bluetooth.png</icon> + </connection> + </connections> + <types> + <type name="vegetables"> + <displayName>Vegetables</displayName> + <icon>vegetables.png</icon> + </type> + <type name="fruits"> + <displayName>Fruits</displayName> + <icon>fruits.png</icon> + </type> + <type name="grains"> + <displayName>Grains</displayName> + <icon>grains.png</icon> + </type> + </types> + <commands> + <command name="sync"> + <displayName>&Sync</displayName> + <description>Sync things</description> + <icon>..\commands\sync.png</icon> + </command> + <command name="settings"> + <displayName>Se&ttings</displayName> + <description>Open settings page</description> + <icon>..\commands\settings.png</icon> + </command> + <command name="detach"> + <displayName>&Detach</displayName> + <description>Detach device</description> + <icon>..\commands\detach.png</icon> + </command> + <command name="eject"> + <displayName>&Eject</displayName> + <description>Eject device</description> + <icon>..\commands\eject.png</icon> + </command> + </commands> + <devices> + <device name="iconTest" type="test"> + <connection></connection> + <displayName>Icon Test</displayName> + <icon width="24" height="32">..\zoom\24x32.png</icon> + <icon width="48" height="64">..\zoom\48x64.png</icon> + <icon width="96" height="128">..\zoom\96x128.png</icon> + <icon width="192" height="256">..\zoom\192x256.png</icon> + <command primary="1" disabled="0">sync</command> + <command primary="0" disabled="0" hidden="0" group="1">settings</command> + <command primary="0">detach</command> + <command disabled="1" hidden="0">eject</command> + <totalSpace>909242</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <model>Icon Test v1.0</model> + <status>Icon Test status string</status> + </device> + <device name="apple" type="fruits"> + <connection>wifi</connection> + <displayName>Dilicious Apple</displayName> + <icon>apple.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <model>iApple 2</model> + <command primary="1">sync</command> + <command group="1">eject</command> + </device> + <device name="banana" type="fruits"> + <connection>usb</connection> + <displayName>Banana</displayName> + <icon>banana.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <model>iBanana 2</model> + <command primary="1">sync</command> + <command group="1">eject</command> + </device> + <device name="peach" type="fruits"> + <connection>bluetooth</connection> + <displayName>Peach</displayName> + <icon>peach.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <model>iPeach 2</model> + <command primary="1">sync</command> + <command group="1">eject</command> + </device> + <device name="orange" type="fruits"> + <connection>usb</connection> + <displayName>Orange</displayName> + <icon>orange.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <model>iOrange 2</model> + <command primary="1">sync</command> + <command group="1">eject</command> + </device> + <device name="pear" type="fruits"> + <connection>wifi</connection> + <displayName>Pear</displayName> + <icon>pear.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <model>iPear 2</model> + <command primary="1">sync</command> + <command group="1">eject</command> + </device> + <device name="grape" type="fruits"> + <connection>bluetooth</connection> + <displayName>Grape</displayName> + <icon>grape.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <model>iGrape 2</model> + <command primary="1">sync</command> + <command group="1">eject</command> + </device> + <device name="lemon" type="fruits"> + <connection>usb</connection> + <displayName>Lemon</displayName> + <icon>lemon.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <model>iLemon 2</model> + <command primary="1">sync</command> + <command group="1">eject</command> + </device> + <device name="carrot" type="vegetables"> + <connection>wifi</connection> + <displayName>Carrot</displayName> + <icon>carrot.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <command primary="1">sync</command> + </device> + <device name="broccoli" type="vegetables"> + <connection>bluetooth</connection> + <displayName>Broccoli</displayName> + <icon>broccoli.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <command primary="1">sync</command> + </device> + <device name="eggplant" type="vegetables"> + <connection>usb</connection> + <displayName>Eggplant</displayName> + <icon>eggplant.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <command primary="1">sync</command> + </device> + <device name="cucumber" type="vegetables"> + <connection>wifi</connection> + <displayName>Cucumber</displayName> + <icon>cucumber.png</icon> + <totalSpace>0</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <command primary="1">sync</command> + </device> + <device name="pepper" type="vegetables"> + <connection>bluetooth</connection> + <displayName>Pepper</displayName> + <icon>pepper.png</icon> + <totalSpace>0</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + <command primary="1">sync</command> + </device> + <device name="bread" type="grains"> + <connection>usb</connection> + <displayName>Bread</displayName> + <icon>bread.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + </device> + <device name="pasta" type="grains"> + <connection>wifi</connection> + <displayName>Pasta</displayName> + <icon>pasta.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + </device> + <device name="rice" type="grains"> + <connection>bluetooth</connection> + <displayName>Rice</displayName> + <icon>rice.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + </device> + <device name="cereal" type="grains"> + <connection>usb</connection> + <displayName>Cereal</displayName> + <icon>cereal.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + </device> + <device name="muffin" type="grains"> + <connection>wifi</connection> + <displayName>Muffin</displayName> + <icon>muffin.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + </device> + <device name="pretzel" type="grains"> + <connection>bluetooth</connection> + <displayName>Pretzel</displayName> + <icon>pretzel.png</icon> + <totalSpace>100</totalSpace> + <usedSpace>0</usedSpace> + <hidden>0</hidden> + </device> + </devices> +</testprovider>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/wasabi.cpp b/Src/Plugins/Library/ml_devices/gen_deviceprovider/wasabi.cpp new file mode 100644 index 00000000..c3079681 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/wasabi.cpp @@ -0,0 +1,139 @@ +#include "main.h" +#include "./wasabi.h" + +#include <api/service/waservicefactory.h> + +api_service *WASABI_API_SVC = NULL; +api_application *WASABI_API_APP = NULL; +api_language *WASABI_API_LNG = NULL; +api_devicemanager *WASABI_API_DEVICES = NULL; + +HINSTANCE WASABI_API_LNG_HINST = NULL; +HINSTANCE WASABI_API_ORIG_HINST = NULL; + +static unsigned long wasabiReference = 0; +static BOOL defaultServicesLoaded = FALSE; + +static void +Wasabi_Uninitialize() +{ + if (NULL != WASABI_API_SVC) + { + Wasabi_ReleaseInterface(applicationApiServiceGuid, WASABI_API_APP); + Wasabi_ReleaseInterface(languageApiGUID, WASABI_API_LNG); + Wasabi_ReleaseInterface(DeviceManagerGUID, WASABI_API_DEVICES); + } + + WASABI_API_SVC = NULL; + WASABI_API_APP = NULL; + WASABI_API_LNG = NULL; + WASABI_API_DEVICES = NULL; + defaultServicesLoaded = FALSE; +} + +BOOL +Wasabi_Initialize(HINSTANCE instance, api_service *serviceMngr) +{ + if (NULL != WASABI_API_SVC) + return FALSE; + + defaultServicesLoaded = FALSE; + + WASABI_API_SVC = serviceMngr; + if ((api_service*)1 == WASABI_API_SVC) + WASABI_API_SVC = NULL; + + if (NULL == WASABI_API_SVC) + return FALSE; + + WASABI_API_APP = NULL; + WASABI_API_DEVICES = NULL; + + WASABI_API_LNG = NULL; + WASABI_API_ORIG_HINST = instance; + WASABI_API_LNG_HINST = WASABI_API_ORIG_HINST; + + Wasabi_AddRef(); + return TRUE; +} + +BOOL +Wasabi_InitializeFromWinamp(HINSTANCE instance, HWND winampWindow) +{ + api_service *serviceMngr; + serviceMngr = (api_service*)SENDWAIPC(winampWindow, IPC_GET_API_SERVICE, 0); + return Wasabi_Initialize(instance, serviceMngr); +} + +BOOL +Wasabi_LoadDefaultServices(void) +{ + if (NULL == WASABI_API_SVC) + return FALSE; + + if (FALSE != defaultServicesLoaded) + return FALSE; + + WASABI_API_APP = Wasabi_QueryInterface(api_application, applicationApiServiceGuid); + WASABI_API_DEVICES = Wasabi_QueryInterface(api_devicemanager, DeviceManagerGUID); + + WASABI_API_LNG = Wasabi_QueryInterface(api_language, languageApiGUID); + if (NULL != WASABI_API_LNG) + { + WASABI_API_LNG_HINST = WASABI_API_LNG->StartLanguageSupport(WASABI_API_ORIG_HINST, + PLUGIN_LANGUAGE_ID); + } + + defaultServicesLoaded = TRUE; + return TRUE; +} + +unsigned long +Wasabi_AddRef(void) +{ + return InterlockedIncrement((LONG*)&wasabiReference); +} + +unsigned long +Wasabi_Release(void) +{ + if (0 == wasabiReference) + return wasabiReference; + + LONG r = InterlockedDecrement((LONG*)&wasabiReference); + if (0 == r) + { + Wasabi_Uninitialize(); + } + return r; +} + +void * +Wasabi_QueryInterface0(const GUID &interfaceGuid) +{ + waServiceFactory *serviceFactory; + + if (NULL == WASABI_API_SVC) + return NULL; + + serviceFactory = WASABI_API_SVC->service_getServiceByGuid(interfaceGuid); + if (NULL == serviceFactory) + return NULL; + + return serviceFactory->getInterface(); +} + +void +Wasabi_ReleaseInterface0(const GUID &interfaceGuid, void *interfaceInstance) +{ + waServiceFactory *serviceFactory; + + if (NULL == WASABI_API_SVC) + return; + + serviceFactory = WASABI_API_SVC->service_getServiceByGuid(interfaceGuid); + if (NULL == serviceFactory) + return; + + serviceFactory->releaseInterface(interfaceInstance); +} diff --git a/Src/Plugins/Library/ml_devices/gen_deviceprovider/wasabi.h b/Src/Plugins/Library/ml_devices/gen_deviceprovider/wasabi.h new file mode 100644 index 00000000..02ff1d40 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/gen_deviceprovider/wasabi.h @@ -0,0 +1,57 @@ +#ifndef _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_WASABI_HEADER +#define _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_WASABI_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <api/service/api_service.h> +extern api_service *wasabiManager; +#define WASABI_API_SVC wasabiManager + +#include <api/application/api_application.h> +#define WASABI_API_APP applicationApi + +#include "../Agave/Language/api_language.h" + +#include "../../devices/api_devicemanager.h" +extern api_devicemanager *deviceManagerApi; +#define WASABI_API_DEVICES deviceManagerApi + +BOOL +Wasabi_Initialize(HINSTANCE instance, + api_service *serviceMngr); + +BOOL +Wasabi_InitializeFromWinamp(HINSTANCE instance, + HWND winampWindow); + +BOOL +Wasabi_LoadDefaultServices(void); + +unsigned long +Wasabi_AddRef(void); + +unsigned long +Wasabi_Release(void); + +void * +Wasabi_QueryInterface0(const GUID &interfaceGuid); + +#define Wasabi_QueryInterface(_interfaceType, _interfaceGuid)\ + ((##_interfaceType*)Wasabi_QueryInterface0(_interfaceGuid)) + + +void +Wasabi_ReleaseInterface0(const GUID &interfaceGuid, + void *interfaceInstance); + +#define Wasabi_ReleaseInterface(_interfaceGuid, _interfaceInstance)\ + (Wasabi_ReleaseInterface0((_interfaceGuid), (_interfaceInstance))) + + +#define Wasabi_ReleaseObject(_object)\ + {if (NULL != (_object)) {((Dispatchable*)(_object))->Release();}} + + +#endif // _NULLSOFT_WINAMP_GEN_DEVICE_PROVIDER_WASABI_HEADER diff --git a/Src/Plugins/Library/ml_devices/graphics.cpp b/Src/Plugins/Library/ml_devices/graphics.cpp new file mode 100644 index 00000000..7feb214b --- /dev/null +++ b/Src/Plugins/Library/ml_devices/graphics.cpp @@ -0,0 +1,313 @@ +#include "main.h" +#include "./graphics.h" + +BYTE +Graphics_GetSysFontQuality() +{ + BOOL smoothingEnabled; + if (FALSE == SystemParametersInfoW(SPI_GETFONTSMOOTHING, 0, &smoothingEnabled, 0) || + FALSE == smoothingEnabled) + { + return DEFAULT_QUALITY; + } + + OSVERSIONINFOW vi; + vi.dwOSVersionInfoSize = sizeof(vi); + if (FALSE == GetVersionExW(&vi)) + return DEFAULT_QUALITY; + + if (vi.dwMajorVersion > 5 || (vi.dwMajorVersion == 5 && vi.dwMinorVersion >= 1)) + { + UINT smootingType; + if (FALSE == SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &smootingType, 0)) + return DEFAULT_QUALITY; + + if (FE_FONTSMOOTHINGCLEARTYPE == smootingType) + return CLEARTYPE_NATURAL_QUALITY/*CLEARTYPE_QUALITY*/; + } + + return ANTIALIASED_QUALITY; +} + +HFONT +Graphics_CreateSysFont() +{ + LOGFONTW lf = {0}; + HFONT font = NULL; + + if (FALSE == SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0)) + return NULL; + + lf.lfQuality = Graphics_GetSysFontQuality(); + font = CreateFontIndirectW(&lf); + + return font; +} + +HFONT +Graphics_DuplicateFont(HFONT sourceFont, INT heightDeltaPt, BOOL forceBold, BOOL systemQuality) +{ + LOGFONTW lf = {0}; + + if (NULL == sourceFont) + return NULL; + + + if (sizeof(lf) != GetObjectW(sourceFont, sizeof(lf), &lf)) + return NULL; + + if (0 != heightDeltaPt) + { + HDC hdcTmp = NULL, hdc = GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE | DCX_NORESETATTRS); + + if (NULL != hdc) + { + hdcTmp = CreateCompatibleDC(hdc); + ReleaseDC(NULL, hdc); + } + + if (NULL == hdcTmp) + return NULL; + + LONG pixelsY = GetDeviceCaps(hdcTmp, LOGPIXELSY); + HFONT prevFont = SelectFont(hdcTmp, sourceFont); + + TEXTMETRICW tm = {0}; + if (FALSE != GetTextMetricsW(hdcTmp, &tm)) + { + INT basePt = MulDiv(tm.tmHeight - tm.tmInternalLeading, 96, pixelsY); + lf.lfHeight = -MulDiv((basePt + heightDeltaPt), pixelsY, 96); + + } + + SelectObject(hdcTmp, prevFont); + DeleteDC(hdcTmp); + } + + if (FALSE != systemQuality) + lf.lfQuality = Graphics_GetSysFontQuality(); + if (FALSE != forceBold && lf.lfWeight < FW_BOLD) + lf.lfWeight = FW_BOLD; + + return CreateFontIndirectW(&lf); +} + +long +Graphics_GetFontHeight(HDC hdc) +{ + TEXTMETRICW tm; + if (FALSE == GetTextMetricsW(hdc, &tm)) + return 0; + + return tm.tmHeight; +} + +long +Graphics_GetAveStrWidth(HDC hdc, UINT cchLen) +{ + const char szTest[] = + { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l', 'm','n','o','p','q','r','s','t','u','v','w','x','y','z' + }; + + SIZE textSize; + if (FALSE == GetTextExtentPointA(hdc, szTest, ARRAYSIZE(szTest) -1, &textSize)) + return 0; + + LONG result; + if (1 == cchLen) + { + result = (textSize.cx + ARRAYSIZE(szTest)/2)/ARRAYSIZE(szTest); + } + else + { + result = MulDiv(cchLen, textSize.cx + ARRAYSIZE(szTest)/2, ARRAYSIZE(szTest)); + if (0 != result) + { + TEXTMETRICW tm; + if (FALSE != GetTextMetricsW(hdc, &tm)) + result += tm.tmOverhang; + } + } + return result; +} + +BOOL +Graphics_GetWindowBaseUnits(HWND hwnd, LONG *baseUnitX, LONG *baseUnitY) +{ + BOOL result; + result = FALSE; + + if (NULL != hwnd) + { + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + TEXTMETRICW tm; + HFONT font, prevFont; + + font = (HFONT)SNDMSG(hwnd, WM_GETFONT, 0, 0L); + prevFont = SelectFont(hdc, font); + + if (FALSE != GetTextMetricsW(hdc, &tm)) + { + if (NULL != baseUnitX) + *baseUnitX = Graphics_GetAveStrWidth(hdc, 1); + + if (NULL != baseUnitY) + *baseUnitY = tm.tmHeight; + + result = TRUE; + } + + SelectFont(hdc, prevFont); + ReleaseDC(hwnd, hdc); + } + } + return result; +} + + +typedef int (*SkinColorFunc)(int idx); + +COLORREF Graphics_GetSkinColor(unsigned int colorIndex) +{ + static SkinColorFunc GetSkinColor = NULL; + + if (NULL == GetSkinColor) + { + GetSkinColor = (SkinColorFunc)SENDMLIPC(Plugin_GetLibraryWindow(), ML_IPC_SKIN_WADLG_GETFUNC, 1); + if (NULL == GetSkinColor) + return RGB(255, 0, 255); + } + + return GetSkinColor(colorIndex); +} + +COLORREF +Graphics_BlendColors(COLORREF rgbTop, COLORREF rgbBottom, INT alpha) +{ + if (alpha > 254) return rgbTop; + if (alpha < 0) return rgbBottom; + + WORD k = (WORD)(((255 - alpha)*255 + 127)/255); + + return RGB( (GetRValue(rgbTop)*alpha + k*GetRValue(rgbBottom) + 127)/255, + (GetGValue(rgbTop)*alpha + k*GetGValue(rgbBottom) + 127)/255, + (GetBValue(rgbTop)*alpha + k*GetBValue(rgbBottom) + 127)/255); +} + +INT +Graphics_GetColorDistance(COLORREF rgb1, COLORREF rgb2) +{ + return (1000 * ((GetRValue(rgb1) - GetRValue(rgb2)) + + (GetGValue(rgb1) - GetGValue(rgb2)) + + (GetBValue(rgb1) - GetBValue(rgb2))))/ (3 * 255); +} + +void +Graphics_ClampRect(RECT *rect, const RECT *boxRect) +{ + if (rect->left < boxRect->left) + rect->left = boxRect->left; + + if (rect->top < boxRect->top) + rect->top = boxRect->top; + + if (rect->right > boxRect->right) + rect->right = boxRect->right; + + if (rect->bottom > boxRect->bottom) + rect->bottom = boxRect->bottom; +} + +void +Graphics_NormalizeRect(RECT *rect) +{ + if (rect->top > rect->bottom) + rect->bottom = rect->top; + + if (rect->left > rect->right) + rect->right = rect->left; +} + +void +Graphics_GetRectSizeNormalized(const RECT *rect, SIZE *size) +{ + size->cx = rect->right - rect->left; + if (size->cx < 0) + size->cx = 0; + + size->cy = rect->bottom - rect->top; + if (size->cy < 0) + size->cy = 0; +} + +BOOL +Graphics_IsRectFit(const RECT *rect, const RECT *boxRect) +{ + if (rect->left < boxRect->left || + rect->top < boxRect->top || + rect->right > boxRect->right || + rect->bottom > boxRect->bottom) + { + return FALSE; + } + + return TRUE; +} + + +BOOL SetSizeEmpty(SIZE *size) +{ + if (NULL == size) + return FALSE; + + ZeroMemory(size, sizeof(SIZE)); + return TRUE; +} + +BOOL IsSizeEmpty(SIZE *size) +{ + return (NULL == size || 0 == size->cx || 0 == size->cy); +} + +BOOL SetSize(SIZE *size, long width, long height) +{ + if (NULL == size) + return FALSE; + + size->cx = width; + size->cy = height; + + return TRUE; +} + +BOOL SetPoint(POINT *pt, long x, long y) +{ + if (NULL == pt) + return FALSE; + + pt->x = x; + pt->y = y; + + return TRUE; +} + +BOOL MakeRectPolygon(POINT vertices[4], long left, long top, long right, long bottom) +{ + if (NULL == vertices) + return FALSE; + + vertices[0].x = left; + vertices[0].y = top; + vertices[1].x = right; + vertices[1].y = top; + vertices[2].x = right; + vertices[2].y = bottom; + vertices[3].x = left; + vertices[3].y = bottom; + + return TRUE; +} diff --git a/Src/Plugins/Library/ml_devices/graphics.h b/Src/Plugins/Library/ml_devices/graphics.h new file mode 100644 index 00000000..7aab1dce --- /dev/null +++ b/Src/Plugins/Library/ml_devices/graphics.h @@ -0,0 +1,72 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_GRAPHICS_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_GRAPHICS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#ifndef SelectBitmap + #define SelectBitmap(_hdc, _bitmap) ((HBITMAP)SelectObject(_hdc, _bitmap)) +#endif + +#ifndef SelectFont + #define SelectFont(_hdc, _font) ((HFONT)SelectObject(_hdc, _font)) +#endif + +#ifndef SelectBrush + #define SelectBrush(_hdc, _brush) ((HBRUSH)SelectObject(_hdc, _brush)) +#endif + +#ifndef GetCurrentBitmap + #define GetCurrentBitmap(_hdc) ((HBITMAP)GetCurrentObject(_hdc, OBJ_BITMAP)) +#endif + +#ifndef GetCurrentFont + #define GetCurrentFont(_hdc) ((HFONT)GetCurrentObject(_hdc, OBJ_FONT)) +#endif + +#ifndef GetCurrentBrush + #define GetCurrentBrush(_hdc) ((HBRUSH)GetCurrentObject(_hdc, OBJ_BRUSH)) +#endif + + + +#ifndef SET_GRADIENT_VERTEX + #define SET_GRADIENT_VERTEX(_vertex, _x, _y, _rgb)\ + {(_vertex).x = (_x); (_vertex).y = (_y);\ + (_vertex).Red = GetRValue(_rgb) << 8; (_vertex).Green = GetGValue(_rgb) << 8;\ + (_vertex).Blue = GetBValue(_rgb) << 8; (_vertex).Alpha = 0x0000;} +#endif + +#ifndef SET_GRADIENT_RECT_MESH + #define SET_GRADIENT_RECT_MESH(_mesh, _upperLeft, _bottomRight)\ + {(_mesh).UpperLeft = (_upperLeft); (_mesh).LowerRight = (_bottomRight);} +#endif + + +BYTE Graphics_GetSysFontQuality(); +HFONT Graphics_CreateSysFont(); +HFONT Graphics_DuplicateFont(HFONT sourceFont, INT heightDeltaPt, BOOL forceBold, BOOL systemQuality); + +long Graphics_GetFontHeight(HDC hdc); +long Graphics_GetAveStrWidth(HDC hdc, UINT cchLen); +BOOL Graphics_GetWindowBaseUnits(HWND hwnd, LONG *baseUnitX, LONG *baseUnitY); + +COLORREF Graphics_GetSkinColor(unsigned int colorIndex); +COLORREF Graphics_BlendColors(COLORREF rgbTop, COLORREF rgbBottom, INT alpha); +INT Graphics_GetColorDistance(COLORREF rgb1, COLORREF rgb2); + +void Graphics_ClampRect(RECT *rect, const RECT *boxRect); +void Graphics_NormalizeRect(RECT *rect); +void Graphics_GetRectSizeNormalized(const RECT *rect, SIZE *size); +BOOL Graphics_IsRectFit(const RECT *rect, const RECT *boxRect); + +BOOL SetSizeEmpty(SIZE *size); +BOOL IsSizeEmpty(SIZE *size); +BOOL SetSize(SIZE *size, long width, long height); + +BOOL SetPoint(POINT *pt, long x, long y); +BOOL MakeRectPolygon(POINT vertices[4], long left, long top, long right, long bottom); + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_GRAPHICS_HEADER diff --git a/Src/Plugins/Library/ml_devices/image.cpp b/Src/Plugins/Library/ml_devices/image.cpp new file mode 100644 index 00000000..5bcb0626 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/image.cpp @@ -0,0 +1,978 @@ +#include "main.h" +#include "./image.h" + + +HBITMAP +Image_Load(const wchar_t *path, unsigned int type, + unsigned int flags, int width, int height) +{ + MLIMAGESOURCE source; + HWND libraryWindow = Plugin_GetLibraryWindow(); + if (NULL == libraryWindow) + return NULL; + + source.cbSize = sizeof(source); + source.lpszName = path; + source.type = type; + source.flags = (flags & ~IMAGE_FILTER_MASK); + source.cxDst = width; + source.cyDst = height; + + if (0 == (ISF_LOADFROMFILE & source.flags)) + { + source.hInst = WASABI_API_LNG_HINST; + if (NULL != source.hInst) + { + HBITMAP bitmap = MLImageLoader_LoadDib(libraryWindow, &source); + if (NULL != bitmap) + return bitmap; + } + + if (WASABI_API_ORIG_HINST == source.hInst) + return NULL; + + source.hInst = WASABI_API_ORIG_HINST; + return (NULL != source.hInst) ? + MLImageLoader_LoadDib(libraryWindow, &source) : + NULL; + } + + return MLImageLoader_LoadDib(Plugin_GetLibraryWindow(), &source); +} + +HBITMAP +Image_LoadEx(HINSTANCE instance, const wchar_t *path, unsigned int type, + unsigned int flags, int width, int height) +{ + MLIMAGESOURCE source = {0}; + + source.cbSize = sizeof(source); + source.hInst = instance; + source.lpszName = path; + source.type = type; + source.flags = (flags & ~IMAGE_FILTER_MASK); + source.cxDst = width; + source.cyDst = height; + + return MLImageLoader_LoadDib(Plugin_GetLibraryWindow(), &source); +} + +BOOL +Image_FilterEx(void *pixelData, long width, long height, unsigned short bpp, + unsigned int flags, COLORREF backColor, COLORREF frontColor, COLORREF blendColor) +{ + MLIMAGEFILTERAPPLYEX filter; + HWND libraryWindow; + BOOL result; + + if (NULL == pixelData) + return FALSE; + + libraryWindow = Plugin_GetLibraryWindow(); + if (NULL == libraryWindow) + return FALSE; + + filter.cbSize = sizeof(filter); + filter.pData = (BYTE*)pixelData; + filter.cx = width; + filter.cy = height; + filter.bpp = bpp; + filter.imageTag = NULL; + + result = FALSE; + + if (0 != (IMAGE_FILTER_GRAYSCALE & flags)) + { + filter.filterUID = MLIF_GRAYSCALE_UID; + result = MLImageFilter_ApplyEx(libraryWindow, &filter); + } + + filter.rgbBk = backColor; + filter.rgbFg = frontColor; + + if (32 == bpp) + { + filter.filterUID = MLIF_FILTER1_PRESERVE_ALPHA_UID; + result = MLImageFilter_ApplyEx(libraryWindow, &filter); + + if (0 != (IMAGE_FILTER_BLEND & flags)) + { + filter.filterUID = MLIF_BLENDONBK_UID; + filter.rgbBk = blendColor; + result = MLImageFilter_ApplyEx(libraryWindow, &filter); + } + } + else + { + filter.filterUID = MLIF_FILTER1_UID; + result = MLImageFilter_ApplyEx(libraryWindow, &filter); + } + + return result; +} + +BOOL +Image_Filter(HBITMAP bitmap, unsigned int flags, + COLORREF backColor, COLORREF frontColor, COLORREF blendColor) +{ + DIBSECTION bitmapData; + BITMAP *bi; + + if (NULL == bitmap) + return NULL; + + if (sizeof(bitmapData) != GetObjectW(bitmap, sizeof(bitmapData), &bitmapData)) + return FALSE; + + bi = &bitmapData.dsBm; + + return Image_FilterEx(bi->bmBits, bi->bmWidth, bi->bmHeight, bi->bmBitsPixel, + flags, backColor, frontColor, blendColor); +} + +BOOL +Image_BlendEx(void *pixelData, long width, long height, unsigned short bpp, COLORREF blendColor) +{ + MLIMAGEFILTERAPPLYEX filter; + HWND libraryWindow; + + if (NULL == pixelData || 32 != bpp) + return FALSE; + + libraryWindow = Plugin_GetLibraryWindow(); + if (NULL == libraryWindow) + return FALSE; + + filter.cbSize = sizeof(filter); + filter.pData = (BYTE*)pixelData; + filter.cx = width; + filter.cy = height; + filter.bpp = bpp; + filter.imageTag = NULL; + + + filter.filterUID = MLIF_BLENDONBK_UID; + filter.rgbBk = blendColor; + return MLImageFilter_ApplyEx(libraryWindow, &filter); +} + +BOOL +Image_Blend(HBITMAP bitmap, COLORREF blendColor) +{ + DIBSECTION bitmapData; + BITMAP *bi; + + if (NULL == bitmap) + return NULL; + + if (sizeof(bitmapData) != GetObjectW(bitmap, sizeof(bitmapData), &bitmapData)) + return FALSE; + + bi = &bitmapData.dsBm; + + return Image_BlendEx(bi->bmBits, bi->bmWidth, bi->bmHeight, bi->bmBitsPixel, blendColor); +} + +HBITMAP +Image_LoadSkinnedEx(HINSTANCE instance, const wchar_t *path, unsigned int type, + unsigned int flags, int width, int height, + COLORREF backColor, COLORREF frontColor, COLORREF blendColor) +{ + HBITMAP bitmap; + + bitmap = Image_LoadEx(instance, path, type, flags, width, height); + if (NULL == bitmap) + return NULL; + + Image_Filter(bitmap, flags, backColor, frontColor, blendColor); + + return bitmap; +} + +HBITMAP +Image_LoadSkinned(const wchar_t *path, unsigned int type, + unsigned int flags, int width, int height, + COLORREF backColor, COLORREF frontColor, COLORREF blendColor) +{ + HBITMAP bitmap; + + bitmap = Image_Load(path, type, flags, width, height); + if (NULL == bitmap) + return NULL; + + Image_Filter(bitmap, flags, backColor, frontColor, blendColor); + + return bitmap; +} + +HBITMAP +Image_DuplicateDib(HBITMAP source) +{ + HBITMAP bitmap; + DIBSECTION sourceDib; + HDC windowDC; + void *pixelData; + + if (NULL == source) + return NULL; + + if (sizeof(sourceDib) != GetObjectW(source, sizeof(sourceDib), &sourceDib)) + return FALSE; + + + sourceDib.dsBmih.biSize = sizeof(BITMAPINFOHEADER); + if (sourceDib.dsBmih.biHeight > 0) + sourceDib.dsBmih.biHeight = -sourceDib.dsBmih.biHeight; + + windowDC = GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE); + + bitmap = CreateDIBSection(windowDC, (BITMAPINFO*)&sourceDib.dsBmih, DIB_RGB_COLORS, &pixelData, NULL, 0); + + if (NULL != windowDC) + ReleaseDC(NULL, windowDC); + + if (NULL == bitmap) + return NULL; + + CopyMemory(pixelData, sourceDib.dsBm.bmBits, sourceDib.dsBm.bmWidthBytes * sourceDib.dsBm.bmHeight); + + return bitmap; + +} +BOOL +Image_ColorOver(HBITMAP bitmap, const RECT *prcPart, BOOL premult, COLORREF rgb) +{ + DIBSECTION bitmapData; + BITMAP *bi; + + if (NULL == bitmap) + return NULL; + + if (sizeof(bitmapData) != GetObjectW(bitmap, sizeof(bitmapData), &bitmapData)) + return FALSE; + + if (BI_RGB != bitmapData.dsBmih.biCompression || + 1 != bitmapData.dsBmih.biPlanes || + 32 != bitmapData.dsBm.bmBitsPixel) + { + return FALSE; + } + + bi = &bitmapData.dsBm; + + return (NULL == prcPart) ? + Image_ColorOverEx((BYTE*)bi->bmBits, bi->bmWidth, bi->bmHeight, + 0, 0, bi->bmWidth, bi->bmHeight, + bi->bmBitsPixel, premult, rgb) : + Image_ColorOverEx((BYTE*)bi->bmBits, bi->bmWidth, bi->bmHeight, + prcPart->left, prcPart->top, + prcPart->right - prcPart->left, prcPart->bottom - prcPart->top, + bi->bmBitsPixel, premult, rgb); + +} + +BOOL +Image_ColorOverEx(unsigned char *pPixels, int bitmapCX, int bitmapCY, + long x, long y, long cx, long cy, unsigned short bpp, + BOOL premult, COLORREF rgb) +{ + LONG pitch; + UINT a, r, g, b, ma, mr, mg, mb; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (step < 3) + return TRUE; + + if (cy < 0) cy -= cy; + + a = (LOBYTE((rgb)>>24)); r = GetRValue(rgb); g = GetGValue(rgb); b = GetBValue(rgb); + ma = 255 - a; mr = r * 255; mg = g * 255; mb = b * 255; + + if (0 == a) + return TRUE; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + if (0xFF == a) + { + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + cursor[0] = (BYTE)b; + cursor[1] = (BYTE)g; + cursor[2] = (BYTE)r; + // cursor[3] = 0xFF; + } + } + return TRUE; + } + + + if (premult) + { + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + int t = (mb + ma * cursor[0] + 127) / 255; + cursor[0] = (t > 0xFF) ? 0xFF : t; + t = (mg + ma * cursor[1] + 127) / 255; + cursor[1] = (t > 0xFF) ? 0xFF : t; + t = (mr+ ma * cursor[2] + 127) / 255; + cursor[2] = (t > 0xFF) ? 0xFF : t; + } + } + } + else + { + WORD k = (((255 - a)*255 + 127)/255); + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + cursor[0] = (b*a + k*cursor[0] + 127)/255; + cursor[1] = (g*a + k*cursor[1] + 127)/255; + cursor[2] = (r*a + k*cursor[2] + 127)/255; + // cursor[3] = (a*a + k*cursor[3] + 127)/255; + } + } + + } + return TRUE; +} + +BOOL +Image_Premultiply(HBITMAP hbmp, const RECT *prcPart) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return (NULL == prcPart) ? + Image_PremultiplyEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + 0, 0, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + dibsec.dsBm.bmBitsPixel) : + Image_PremultiplyEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, RECTWIDTH(*prcPart), RECTHEIGHT(*prcPart), + dibsec.dsBm.bmBitsPixel); +} + +BOOL +Image_PremultiplyEx(unsigned char *pPixels, int bitmapCX, int bitmapCY, + long x, long y, long cx, long cy, unsigned short bpp) +{ + if (32 != bpp) + return FALSE; + + LONG pitch; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (cy < 0) + cy = -cy; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + UINT a; + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + a = cursor[3]; + if (0 == a) + { + cursor[0] = 0; + cursor[1] = 0; + cursor[2] = 0; + } + else if (255 != a) + { + cursor[0] = (BYTE)MulDiv(cursor[0], a, 255); + cursor[1] = (BYTE)MulDiv(cursor[1], a, 255); + cursor[2] = (BYTE)MulDiv(cursor[2], a, 255); + } + } + } + + return TRUE; +} + +BOOL +Image_Demultiply(HBITMAP hbmp, const RECT *prcPart) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return (NULL == prcPart) ? + Image_DemultiplyEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + 0, 0, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + dibsec.dsBm.bmBitsPixel) : + Image_DemultiplyEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, RECTWIDTH(*prcPart), RECTHEIGHT(*prcPart), + dibsec.dsBm.bmBitsPixel); +} + +BOOL +Image_DemultiplyEx(unsigned char *pPixels, int bitmapCX, int bitmapCY, + long x, long y, long cx, long cy, unsigned short bpp) +{ + if (32 != bpp) + return FALSE; + + LONG pitch; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (cy < 0) + cy = -cy; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + UINT a; + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + a = cursor[3]; + if (0 == a) + { + cursor[0] = 0; + cursor[1] = 0; + cursor[2] = 0; + } + else if (255 != a) + { + cursor[0] = (BYTE)MulDiv(cursor[0], 255, a); + cursor[1] = (BYTE)MulDiv(cursor[1], 255, a); + cursor[2] = (BYTE)MulDiv(cursor[2], 255, a); + } + } + } + + return TRUE; +} +BOOL +Image_Saturate(HBITMAP hbmp, const RECT *prcPart, int n, BOOL fScale) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return Image_SaturateEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, + prcPart->right - prcPart->left, prcPart->bottom - prcPart->top, + dibsec.dsBm.bmBitsPixel, n, fScale); +} + +BOOL +Image_SaturateEx(unsigned char *pPixels, int bitmapCX, int bitmapCY, + long x, long y, long cx, long cy, unsigned short bpp, + int n, BOOL fScale) +{ + if (32 != bpp) + return FALSE; + + LONG pitch; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (FALSE == fScale) + { + if (n < 0) n = 0; + else if (n > 1000) n = 1000; + } + else + { + if (n < -1000) n = -1000; + else if (n > 1000) n = 1000; + } + + if (cy < 0) + cy = -cy; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + COLORREF rgb; + INT k; + WORD h, l, s; + + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + rgb = RGB(cursor[2], cursor[1], cursor[0]); + ColorRGBToHLS(rgb, &h, &l, &s); + if(FALSE == fScale) + s = ((WORD)((240 * n)/1000)); + else + { + k = s; + s = (WORD)(k + (k * n) /1000); + } + + rgb = ColorHLSToRGB(h, l, s); + + cursor[0] = GetBValue(rgb); + cursor[1] = GetGValue(rgb); + cursor[2] = GetRValue(rgb); + } + } + + return TRUE; +} + +BOOL +Image_AdjustAlpha(HBITMAP hbmp, const RECT *prcPart, int n, BOOL fScale) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return Image_AdjustAlphaEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, + prcPart->right - prcPart->left, prcPart->bottom - prcPart->top, + dibsec.dsBm.bmBitsPixel, n, fScale); +} + +BOOL +Image_AdjustAlphaEx(unsigned char *pPixels, int bitmapCX, int bitmapCY, + long x, long y, long cx, long cy, unsigned short bpp, + int n, BOOL fScale) +{ + if (32 != bpp) + return FALSE; + + LONG pitch; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (FALSE == fScale) + { + if (n < 0) n = 0; + else if (n > 1000) n = 1000; + } + else + { + if (n < -1000) n = -1000; + else if (n > 1000) n = 1000; + } + + if (cy < 0) + cy = -cy; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + INT k; + + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + if(FALSE == fScale) + cursor[3] = ((BYTE)((255 * n)/1000)); + else + { + k = cursor[3]; + k = k + MulDiv(k, n, 1000); + if (k > 255) k = 255; + cursor[3] = (BYTE)k; + } + } + } + + return TRUE; +} + +BOOL +Image_AdjustSaturationAlpha(HBITMAP hbmp, const RECT *prcPart, int nSaturation, int nAlpha) +{ + DIBSECTION dibsec; + if (!hbmp || sizeof(DIBSECTION) != GetObject(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || 1 != dibsec.dsBmih.biPlanes || dibsec.dsBm.bmBitsPixel != 32) + return FALSE; + + return Image_AdjustSaturationAlphaEx((BYTE*)dibsec.dsBm.bmBits, dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, + prcPart->left, prcPart->top, + prcPart->right - prcPart->left, prcPart->bottom - prcPart->top, + dibsec.dsBm.bmBitsPixel, nSaturation, nAlpha); +} + +BOOL +Image_AdjustSaturationAlphaEx(unsigned char *pPixels, int bitmapCX, int bitmapCY, + long x, long y, long cx, long cy, + unsigned short bpp, int nSaturation, int nAlpha) +{ + if (32 != bpp) + return FALSE; + + LONG pitch; + INT step = (bpp>>3); + LPBYTE line, cursor; + pitch = bitmapCX * step; + while (pitch%4) pitch++; + + if (nSaturation < -1000) nSaturation = -1000; + else if (nSaturation > 1000) nSaturation = 1000; + + if (nAlpha < -1000) nAlpha = -1000; + else if (nAlpha > 1000) nAlpha = 1000; + + if (cy < 0) + cy = -cy; + + INT ofs = (bitmapCY > 0) ? (bitmapCY - (y + cy)) : y; + line = pPixels + pitch * ofs + x*step; + + INT k; + COLORREF rgb; + WORD h, l, s; + + for (; cy-- != 0; line += pitch) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + k = cursor[3]; + k = k + MulDiv(k, nAlpha, 1000); + if (k > 255) k = 255; + cursor[3] = (BYTE)k; + + rgb = RGB(cursor[2], cursor[1], cursor[0]); + ColorRGBToHLS(rgb, &h, &l, &s); + + k = s; + k = k + MulDiv(k, nSaturation, 1000); + if (k > 240) k = 240; + s = (WORD)k; + + rgb = ColorHLSToRGB(h, l, s); + cursor[0] = GetBValue(rgb); + cursor[1] = GetGValue(rgb); + cursor[2] = GetRValue(rgb); + } + } + + return TRUE; +} + +BOOL +Image_FillBorder(HDC targetDC, const RECT *targetRect, + HDC sourceDC, const RECT *sourceRect, + BOOL fillCenter, BYTE alphaConstant) +{ + INT prevStretchMode; + long centerWidth, centerHeight; + long sliceWidth, sliceHeight; + + const BLENDFUNCTION blendFunction = { AC_SRC_OVER, 0, alphaConstant, AC_SRC_ALPHA }; + const long targetWidth = RECTWIDTH(*targetRect); + const long targetHeight = RECTHEIGHT(*targetRect); + const long sourceWidth = RECTWIDTH(*sourceRect); + const long sourceHeight = RECTHEIGHT(*sourceRect); + + if (NULL == targetDC || NULL == sourceDC) + return FALSE; + + sliceWidth = sourceWidth/2; + sliceHeight = sourceHeight/2; + + if (sliceWidth*2 > targetWidth) + sliceWidth = targetWidth/2; + + if (sliceHeight*2 > targetHeight) + sliceHeight = targetHeight/2; + + if (0 == sliceWidth || 0 == sliceHeight) + return FALSE; + + prevStretchMode = SetStretchBltMode(targetDC, COLORONCOLOR); + SetViewportOrgEx(sourceDC, 0, 0, NULL); + + GdiAlphaBlend(targetDC, targetRect->left, targetRect->top, sliceWidth, sliceHeight, + sourceDC, sourceRect->left, sourceRect->top, sliceWidth, sliceHeight, blendFunction); + GdiAlphaBlend(targetDC, targetRect->right - sliceWidth, targetRect->top, sliceWidth, sliceHeight, + sourceDC, sourceRect->right - sliceWidth, sourceRect->top, sliceWidth, sliceHeight, blendFunction); + GdiAlphaBlend(targetDC, targetRect->left, targetRect->bottom - sliceHeight, sliceWidth, sliceHeight, + sourceDC, sourceRect->left, sourceRect->bottom - sliceHeight, sliceWidth, sliceHeight, blendFunction); + GdiAlphaBlend(targetDC, targetRect->right - sliceWidth, targetRect->bottom - sliceHeight, sliceWidth, sliceHeight, + sourceDC, sourceRect->right - sliceWidth, sourceRect->bottom - sliceHeight, sliceWidth, sliceHeight, blendFunction); + + if (targetWidth > 2*sliceWidth) + { + centerWidth = sourceWidth - 2*sliceWidth; + if(centerWidth < 1) + centerWidth = 1; + + GdiAlphaBlend(targetDC, targetRect->left + sliceWidth, targetRect->top, targetWidth - (sliceWidth * 2), sliceHeight, + sourceDC, sourceRect->left + sliceWidth, sourceRect->top, centerWidth, sliceHeight, blendFunction); + + GdiAlphaBlend(targetDC, targetRect->left + sliceWidth, targetRect->bottom - sliceHeight, targetWidth - (sliceWidth * 2), sliceHeight, + sourceDC, sourceRect->left + sliceWidth, sourceRect->bottom - sliceHeight, centerWidth, sliceHeight, blendFunction); + } + else + centerWidth = 0; + + if (targetHeight > 2*sliceHeight) + { + centerHeight = sourceHeight - 2*sliceHeight; + if(centerHeight < 1) + centerHeight = 1; + + GdiAlphaBlend(targetDC, targetRect->left, targetRect->top + sliceHeight, sliceWidth, targetHeight - (sliceHeight* 2), + sourceDC, sourceRect->left, sourceRect->top + sliceHeight, sliceWidth, centerHeight, blendFunction); + + GdiAlphaBlend(targetDC, targetRect->right - sliceWidth, targetRect->top + sliceHeight, sliceWidth, targetHeight - (sliceHeight* 2), + sourceDC, sourceRect->right - sliceWidth, sourceRect->top + sliceHeight, sliceWidth, centerHeight, blendFunction); + } + else + centerHeight = 0; + + if (FALSE != fillCenter && + 0 != centerWidth && 0 != centerHeight) + { + GdiAlphaBlend(targetDC, targetRect->left + sliceWidth, targetRect->top + sliceHeight, targetWidth - (sliceWidth * 2), targetHeight - (sliceHeight* 2), + sourceDC, sourceRect->left + sliceWidth, sourceRect->top + sliceHeight, centerWidth, centerHeight, blendFunction); + + } + + SetStretchBltMode(targetDC, prevStretchMode); + return TRUE; +} + +const ImageInfo * +Image_GetBestFit(const ImageInfo *images, size_t count, unsigned int width, unsigned int height) +{ + const ImageInfo *image, *bestFit; + double widthDbl, heightDbl; + double scaleMin, scaleHorz, scaleVert; + + if (NULL == images || count < 1) + return NULL; + + if (width < 1) + width = 1; + + if (height < 1) + height = 1; + + widthDbl = width; + heightDbl = height; + + image = &images[--count]; + scaleHorz = widthDbl/image->width; + scaleVert = heightDbl/image->height; + scaleMin = (scaleHorz < scaleVert) ? scaleHorz : scaleVert; + bestFit = image; + + if (1.0 != scaleMin) + { + scaleMin = fabs(1.0 - scaleMin); + while(count--) + { + image = &images[count]; + scaleHorz = widthDbl/image->width; + scaleVert = heightDbl/image->height; + if (scaleHorz > scaleVert) + scaleHorz = scaleVert; + + if (1.0 == scaleHorz) + { + bestFit = image; + break; + } + + scaleHorz = fabs(1.0 - scaleHorz); + if (scaleHorz < scaleMin) + { + scaleMin = scaleHorz; + bestFit = image; + } + } + } + + return bestFit; +} + +BOOL +Image_AlphaBlend(HDC targetDC, const RECT *targetRect, + HDC sourceDC, const RECT *sourceRect, BYTE sourceAlpha, + HBITMAP sourceBitmap, const RECT *paintRect, AlphaBlendFlags flags, + RECT *rectOut) +{ + BOOL result, clipSource; + RECT fillRect; + int sourceX, sourceY, sourceWidth, sourceHeight; + const BLENDFUNCTION blendFunction = + { + AC_SRC_OVER, + 0, + sourceAlpha, + AC_SRC_ALPHA + }; + + if (NULL != paintRect) + { + if (FALSE == IntersectRect(&fillRect, targetRect, paintRect)) + return TRUE; + + clipSource = TRUE; + } + else + { + CopyRect(&fillRect, targetRect); + clipSource = FALSE; + } + + if (NULL != sourceRect) + { + sourceX = sourceRect->left; + sourceY = sourceRect->top; + sourceWidth = RECTWIDTH(*sourceRect); + sourceHeight = RECTHEIGHT(*sourceRect); + } + else + { + BITMAP bitmapInfo; + if (sizeof(bitmapInfo) != GetObject(sourceBitmap, sizeof(bitmapInfo), &bitmapInfo)) + return FALSE; + + sourceX = 0; + sourceY = 0; + sourceWidth = bitmapInfo.bmWidth; + sourceHeight = bitmapInfo.bmHeight; + if (sourceHeight < 0) + sourceHeight = -sourceHeight; + } + + if (0 != (AlphaBlend_ScaleSource & flags)) + { + RECT rect; + double scaleHorz, scaleVert; + + scaleHorz = (double)RECTWIDTH(*targetRect) / sourceWidth; + scaleVert = (double)RECTHEIGHT(*targetRect) / sourceHeight; + if (scaleHorz > scaleVert) + scaleHorz = scaleVert; + + SetRect(&rect, 0, 0, (int)(sourceWidth * scaleHorz), (int)(sourceHeight * scaleHorz)); + + if (0 != (AlphaBlend_AlignLeft & flags)) + rect.left = targetRect->left; + else if (0 != (AlphaBlend_AlignRight & flags)) + rect.left = targetRect->right - rect.right; + else + rect.left = targetRect->left + (RECTWIDTH(*targetRect) - rect.right)/2; + + if (0 != (AlphaBlend_AlignTop & flags)) + rect.top = targetRect->top; + else if (0 != (AlphaBlend_AlignBottom & flags)) + rect.top = targetRect->bottom - rect.bottom; + else + rect.top = targetRect->top + (RECTHEIGHT(*targetRect) - rect.bottom)/2; + + rect.right += rect.left; + rect.bottom += rect.top; + + if (NULL != rectOut) + CopyRect(rectOut, &rect); + + if (NULL != paintRect) + { + if (FALSE == IntersectRect(&fillRect, &rect, paintRect)) + return TRUE; + } + else + CopyRect(&fillRect, &rect); + + sourceX += (int)((fillRect.left - rect.left)/scaleHorz); + sourceY += (int)((fillRect.top - rect.top)/ scaleHorz); + sourceWidth -= (int)((RECTWIDTH(rect) - RECTWIDTH(fillRect))/scaleHorz); + sourceHeight -= (int)((RECTHEIGHT(rect) - RECTHEIGHT(fillRect))/scaleHorz); + clipSource = FALSE; + } + else + { + if (sourceWidth < RECTWIDTH(*targetRect) || + sourceHeight < RECTHEIGHT(*targetRect)) + { + RECT rect; + + if (0 != (AlphaBlend_AlignLeft & flags)) + rect.left = targetRect->left; + else if (0 != (AlphaBlend_AlignRight & flags)) + rect.left = targetRect->right - sourceWidth; + else + rect.left = targetRect->left + (RECTWIDTH(*targetRect) - sourceWidth)/2; + + if (0 != (AlphaBlend_AlignTop & flags)) + rect.top = targetRect->top; + else if (0 != (AlphaBlend_AlignBottom & flags)) + rect.top = targetRect->bottom - sourceHeight; + else + rect.top = targetRect->top + (RECTHEIGHT(*targetRect) - sourceHeight)/2; + + rect.right = rect.left + sourceWidth; + rect.bottom = rect.top + sourceHeight; + + if (NULL != paintRect) + { + if (FALSE == IntersectRect(&fillRect, &rect, paintRect)) + return TRUE; + + sourceX += (fillRect.left - rect.left); + sourceY += (fillRect.top - rect.top); + sourceWidth -= (RECTWIDTH(rect) - RECTWIDTH(fillRect)); + sourceHeight -= (RECTHEIGHT(rect) - RECTHEIGHT(fillRect)); + } + else + CopyRect(&fillRect, &rect); + + if (NULL != rectOut) + CopyRect(rectOut, &rect); + + clipSource = FALSE; + } + else if (NULL != rectOut) + CopyRect(rectOut, targetRect); + } + + + + if (FALSE != clipSource) + { + sourceX += (fillRect.left - targetRect->left); + sourceY += (fillRect.top - targetRect->top); + sourceWidth -= (RECTWIDTH(*targetRect) - RECTWIDTH(fillRect)); + sourceHeight -= (RECTHEIGHT(*targetRect) - RECTHEIGHT(fillRect)); + } + + if (NULL != sourceBitmap) + SelectBitmap(sourceDC, sourceBitmap); + + result = GdiAlphaBlend(targetDC, fillRect.left, fillRect.top, RECTWIDTH(fillRect), RECTHEIGHT(fillRect), + sourceDC, sourceX, sourceY, sourceWidth, sourceHeight, blendFunction); + + return result; + +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/image.h b/Src/Plugins/Library/ml_devices/image.h new file mode 100644 index 00000000..47871324 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/image.h @@ -0,0 +1,227 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_IMAGE_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_IMAGE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#define IMAGE_FILTER_NORMAL 0x00000000 +#define IMAGE_FILTER_GRAYSCALE 0x00010000 +#define IMAGE_FILTER_BLEND 0x00020000 +#define IMAGE_FILTER_MASK 0xFFFF0000 + + +typedef struct ImageInfo +{ + unsigned int width; + unsigned int height; + const wchar_t *path; +} ImageInfo; + + +HBITMAP +Image_Load(const wchar_t *path, + unsigned int type, + unsigned int flags, + int width, + int height); + +HBITMAP +Image_LoadEx(HINSTANCE instance, + const wchar_t *path, + unsigned int type, + unsigned int flags, + int width, + int height); + +HBITMAP +Image_LoadSkinned(const wchar_t *path, + unsigned int type, + unsigned int flags, //ISF_XXX + IMAGE_FILTER_XXX + int width, + int height, + COLORREF backColor, + COLORREF frontColor, + COLORREF blendColor); // only valid if IMAGE_FILTER_BLEND set + +HBITMAP +Image_LoadSkinnedEx(HINSTANCE instance, + const wchar_t *path, + unsigned int type, + unsigned int flags, //ISF_XXX + IMAGE_FILTER_XXX + int width, + int height, + COLORREF backColor, + COLORREF frontColor, + COLORREF blendColor); // only valid if IMAGE_FILTER_BLEND set + +BOOL +Image_FilterEx(void *pixelData, + long width, + long height, + unsigned short bpp, + unsigned int flags, + COLORREF backColor, + COLORREF frontColor, + COLORREF blendColor); + +BOOL +Image_Filter(HBITMAP bitmap, + unsigned int flags, + COLORREF backColor, + COLORREF frontColor, + COLORREF blendColor); + +BOOL +Image_BlendEx(void *pixelData, + long width, + long height, + unsigned short bpp, + COLORREF blendColor); +BOOL +Image_Blend(HBITMAP bitmap, + COLORREF blendColor); + +HBITMAP +Image_DuplicateDib(HBITMAP source); + +BOOL +Image_ColorOver(HBITMAP hbmp, + const RECT *prcPart, + BOOL premult, + COLORREF rgb); + +BOOL +Image_ColorOverEx(unsigned char *pPixels, + int bitmapCX, + int bitmapCY, + long x, + long y, + long cx, + long cy, + unsigned short bpp, + BOOL premult, + COLORREF rgb); + +BOOL +Image_Premultiply(HBITMAP hbmp, + const RECT *prcPart); + +BOOL +Image_PremultiplyEx(unsigned char *pPixels, + int bitmapCX, + int bitmapCY, + long x, + long y, + long cx, + long cy, + unsigned short bpp); + +BOOL +Image_Demultiply(HBITMAP hbmp, + const RECT *prcPart); + +BOOL +Image_DemultiplyEx(unsigned char *pPixels, + int bitmapCX, + int bitmapCY, + long x, + long y, + long cx, + long cy, + unsigned short bpp); + +BOOL +Image_Saturate(HBITMAP hbmp, + const RECT *prcPart, + int n, + BOOL fScale); + +BOOL +Image_SaturateEx(unsigned char *pPixels, + int bitmapCX, + int bitmapCY, + long x, + long y, + long cx, + long cy, + unsigned short bpp, + int n, + BOOL fScale); + +BOOL +Image_AdjustAlpha(HBITMAP hbmp, + const RECT *prcPart, + int n, + BOOL fScale); + +BOOL +Image_AdjustAlphaEx(unsigned char *pPixels, + int bitmapCX, + int bitmapCY, + long x, + long y, + long cx, + long cy, + unsigned short bpp, + int n, + BOOL fScale); + +BOOL +Image_AdjustSaturationAlpha(HBITMAP hbmp, + const RECT *prcPart, + int nSaturation, + int nAlpha); + +BOOL +Image_AdjustSaturationAlphaEx(unsigned char *pPixels, + int bitmapCX, + int bitmapCY, + long x, + long y, + long cx, + long cy, + unsigned short bpp, + int nSaturation, + int nAlpha); + +BOOL +Image_FillBorder(HDC targetDC, + const RECT *targetRect, + HDC sourceDC, + const RECT *sourceRect, + BOOL fillCenter, + BYTE alphaConstant); + + +const ImageInfo * +Image_GetBestFit(const ImageInfo *images, + size_t count, + unsigned int width, + unsigned int height); + +typedef enum AlphaBlendFlags +{ + AlphaBlend_Normal = 0, + AlphaBlend_ScaleSource = (1 << 0), + AlphaBlend_AlignLeft = (1 << 1), + AlphaBlend_AlignRight = (1 << 2), + AlphaBlend_AlignCenter = 0, + AlphaBlend_AlignTop = (1 << 3), + AlphaBlend_AlignBottom = (1 << 4), + AlphaBlend_AlignVCenter = 0, +}AlphaBlendFlags; +DEFINE_ENUM_FLAG_OPERATORS(AlphaBlendFlags); + +BOOL +Image_AlphaBlend(HDC targetDC, + const RECT *targetRect, + HDC sourceDC, + const RECT *sourceRect, + BYTE sourceAlpha, + HBITMAP sourceBitmap, + const RECT *paintRect, + AlphaBlendFlags flags, + RECT *rectOut); + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_IMAGE_HEADER diff --git a/Src/Plugins/Library/ml_devices/imageCache.cpp b/Src/Plugins/Library/ml_devices/imageCache.cpp new file mode 100644 index 00000000..52e745b7 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/imageCache.cpp @@ -0,0 +1,902 @@ +#include "main.h" +#include "./imageCache.h" +#include <wincodec.h> +#include <vector> +#include <algorithm> + + +struct DeviceColoredImage +{ + size_t ref; + DeviceImage *base; + HBITMAP bitmap; + COLORREF color1; + COLORREF color2; + DeviceImageFilter filter; + void *filterParam; +}; + +typedef std::vector<DeviceColoredImage*> DeviceColoredImageList; + +struct DeviceImage +{ + size_t ref; + DeviceImageCache *cache; + wchar_t *source; + HBITMAP bitmap; + HBITMAP exactBitmap; + int width; + int height; + DeviceImageLoader loader; + void *loaderParam; + DeviceColoredImageList list; +}; +typedef std::vector<DeviceImage*> DeviceImageList; + +struct DeviceImageCache +{ + DeviceImageList list; + IWICImagingFactory *wicFactory; +}; + +typedef struct DeviceImageSeachParam +{ + const wchar_t *path; + int width; + int height; +} DeviceImageSeachParam; + +typedef struct DeviceColoredImageSeachParam +{ + COLORREF color1; + COLORREF color2; +} DeviceColoredImageSeachParam; + + +DeviceImageCache * +DeviceImageCache_Create() +{ + DeviceImageCache *self; + + self = new DeviceImageCache(); + if (NULL == self) + return NULL; + + self->wicFactory = NULL; + + return self; +} + + +void +DeviceImageCache_Free(DeviceImageCache *self) +{ + if (NULL == self) + return; + + size_t index = self->list.size(); + while(index--) + { + DeviceImage *image = self->list[index]; + image->cache = NULL; + } + + if (NULL != self->wicFactory) + self->wicFactory->Release(); + + delete(self); +} + + +static DeviceImage * +DeviceImage_Create(DeviceImageCache *cache, const wchar_t *path, int width, int height, + DeviceImageLoader loader, void *loaderParam) +{ + DeviceImage *self; + + if (NULL == loader) + return NULL; + + self = new DeviceImage(); + if (NULL == self) + return NULL; + + self->ref = 1; + self->cache = cache; + self->source = ResourceString_Duplicate(path); + self->loader = loader; + self->loaderParam = loaderParam; + self->bitmap = NULL; + self->exactBitmap = NULL; + self->width = width; + self->height = height; + + return self; +} + +static void +DeviceImage_Free(DeviceImage *self) +{ + if (NULL == self) + return; + + if (NULL != self->source) + ResourceString_Free(self->source); + + if (NULL != self->bitmap) + DeleteObject(self->bitmap); + + if (NULL != self->exactBitmap && + self->exactBitmap != self->bitmap) + { + DeleteObject(self->exactBitmap); + } + + delete(self); +} + +static int +DeviceImageCache_SearchCb(const void *key, const void *element) +{ + DeviceImageSeachParam *search; + DeviceImage *image; + int result; + + search = (DeviceImageSeachParam*)key; + image = (DeviceImage*)element; + + result = search->height - image->height; + if (0 != result) + return result; + + result = search->width - image->width; + if (0 != result) + return result; + + if (FALSE != IS_INTRESOURCE(search->path) || + FALSE != IS_INTRESOURCE(image->source)) + { + return (int)(INT_PTR)(search->path - image->source); + } + + return CompareString(CSTR_INVARIANT, 0, search->path, -1, image->source, -1) - 2; +} + +static int +DeviceImageCache_SortCb(const void *element1, const void *element2) +{ + DeviceImage *image1; + DeviceImage *image2; + int result; + + image1 = (DeviceImage*)element1; + image2 = (DeviceImage*)element2; + + result = image1->height - image2->height; + if (0 != result) + return result; + + result = image1->width - image2->width; + if (0 != result) + return result; + + if (FALSE != IS_INTRESOURCE(image1->source) || + FALSE != IS_INTRESOURCE(image2->source)) + { + return (int)(INT_PTR)(image1->source - image2->source); + } + + return CompareString(CSTR_INVARIANT, 0, image1->source, -1, image2->source, -1) - 2; +} +static int +DeviceImageCache_SortCb_V2(const void* element1, const void* element2) +{ + return DeviceImageCache_SortCb(element1, element2) < 0; +} + + +static HBITMAP +DeviceImage_DefaultImageLoader(const wchar_t *path, int width, int height, void *param) +{ + HBITMAP bitmap; + unsigned int flags; + + flags = ISF_PREMULTIPLY; + if (FALSE == IS_INTRESOURCE(path)) + flags |= ISF_LOADFROMFILE; + + bitmap = Image_Load(path, SRC_TYPE_PNG, flags, 0, 0); + + return bitmap; +} + +DeviceImage * +DeviceImageCache_GetImage(DeviceImageCache *self, const wchar_t *path, int width, int height, DeviceImageLoader loader, void *user) +{ + DeviceImage *image, *image_ptr = 0; + DeviceImageSeachParam searchParam; + + if (width < 1) + width = 0; + + if (height < 1) + height = 0; + + if (NULL == self) + return NULL; + + if (NULL == path || + (FALSE == IS_INTRESOURCE(path) && L'\0' == *path)) + { + return NULL; + } + + searchParam.height = height; + searchParam.width = width; + searchParam.path = path; + + //image_ptr = (DeviceImage**)bsearch(&searchParam, &self->list[0], self->list.size(), + // sizeof(DeviceImage**), + // DeviceImageCache_SearchCb); + auto it = std::find_if(self->list.begin(), self->list.end(), + [&](DeviceImage* upT) -> bool + { + return DeviceImageCache_SearchCb(&searchParam, upT) == 0; + } + ); + if (it != self->list.end()) + { + image_ptr = *it; + } + + + if (NULL != image_ptr) + { + image = image_ptr; + DeviceImage_AddRef(image); + return image; + } + + if (NULL == loader) + loader = DeviceImage_DefaultImageLoader; + + image = DeviceImage_Create(self, path, width, height, loader, user); + if (NULL != image) + { + self->list.push_back(image); + //qsort(&self->list[0], self->list.size(), sizeof(DeviceImage**), DeviceImageCache_SortCb); + std::sort(self->list.begin(), self->list.end(), DeviceImageCache_SortCb_V2); + } + + return image; +} + +static BOOL +DeviceImageCache_RemoveImage(DeviceImageCache *self, DeviceImage *image) +{ + size_t index; + + if (NULL == self || NULL == image) + return FALSE; + + index = self->list.size(); + while(index--) + { + if (self->list[index] == image) + { + self->list.erase(self->list.begin() + index); + return TRUE; + } + } + + return FALSE; +} +size_t +DeviceImage_AddRef(DeviceImage *self) +{ + if (NULL == self) + return 0; + return InterlockedIncrement((LONG*)&self->ref); +} + +size_t +DeviceImage_Release(DeviceImage *self) +{ + size_t r; + if (NULL == self || 0 == self->ref) + return 0; + + r = InterlockedDecrement((LONG*)&self->ref); + if (0 == r) + { + if (NULL != self->cache) + DeviceImageCache_RemoveImage(self->cache, self); + DeviceImage_Free(self); + return 0; + } + + return r; +} + +BOOL +DeviceImage_GetSize(DeviceImage *self, int *width, int *height) +{ + if (NULL == self) + return FALSE; + + if (NULL != width) + *width = self->width; + + if (NULL != height) + *height = self->height; + + return TRUE; +} + +static IWICImagingFactory* +DeviceImage_GetWicFactory(DeviceImageCache *cache) +{ + IWICImagingFactory *wicFactory; + HRESULT hr; + + if (NULL != cache && + NULL != cache->wicFactory) + { + cache->wicFactory->AddRef(); + return cache->wicFactory; + } + + hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&wicFactory)); + + if (FAILED(hr)) + return NULL; + + if (NULL != cache) + { + wicFactory->AddRef(); + cache->wicFactory = wicFactory; + } + + return wicFactory; +} + + +static HBITMAP +DeviceImage_HBitmapFromWicSource(IWICBitmapSource *wicSource, unsigned int targetWidth, + unsigned int targetHeight, DeviceImageFlags flags) +{ + HRESULT hr; + HBITMAP bitmap; + BITMAPINFO bitmapInfo; + unsigned int width, height, bitmapWidth, bitmapHeight; + void *pixelData = NULL; + WICPixelFormatGUID pixelFormat; + HDC windowDC; + unsigned int strideSize, imageSize; + + if (NULL == wicSource) + return NULL; + + hr = wicSource->GetPixelFormat(&pixelFormat); + if (FAILED(hr) || + (GUID_WICPixelFormat32bppPBGRA != pixelFormat && + GUID_WICPixelFormat32bppBGR != pixelFormat && + GUID_WICPixelFormat32bppBGRA != pixelFormat && + GUID_WICPixelFormat32bppRGBA != pixelFormat && + GUID_WICPixelFormat32bppPRGBA != pixelFormat)) + { + return NULL; + } + + hr = wicSource->GetSize(&width, &height); + if (FAILED(hr)) + return NULL; + + if (0 != (DeviceImage_ExactSize & flags)) + { + bitmapWidth = (targetWidth > width) ? targetWidth : width; + bitmapHeight = (targetHeight > height) ? targetHeight : height; + } + else + { + bitmapWidth = width; + bitmapHeight = height; + } + + ZeroMemory(&bitmapInfo, sizeof(bitmapInfo)); + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = bitmapWidth; + bitmapInfo.bmiHeader.biHeight = -(LONG)bitmapHeight; + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + + windowDC = GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE); + + bitmap = CreateDIBSection(windowDC, &bitmapInfo, DIB_RGB_COLORS, &pixelData, NULL, 0); + + if (NULL != windowDC) + ReleaseDC(NULL, windowDC); + + if (NULL == bitmap) + return NULL; + + hr = UIntMult(bitmapWidth, sizeof(DWORD), &strideSize); + if (SUCCEEDED(hr)) + { + if (0 != (DeviceImage_ExactSize & flags) && + (bitmapWidth > width || bitmapHeight > height)) + { + if (SUCCEEDED(UIntMult(strideSize, bitmapHeight, &imageSize))) + ZeroMemory(pixelData, imageSize); + } + + hr = UIntMult(strideSize, height, &imageSize); + if (SUCCEEDED(hr)) + { + unsigned int offset, delta; + + offset = 0; + + if (0 != (DeviceImage_AlignVCenter & flags)) + { + delta = bitmapHeight - height; + delta = delta/2 + delta%2; + } + else if (0 != (DeviceImage_AlignBottom & flags)) + delta = bitmapHeight - height; + else + delta = 0; + + if (0 != delta && SUCCEEDED(UIntMult(delta, strideSize, &delta))) + offset += delta; + + if (0 != (DeviceImage_AlignHCenter & flags)) + { + delta = bitmapWidth - width; + delta = delta/2; + } + else if (0 != (DeviceImage_AlignRight & flags)) + delta = bitmapWidth - width; + else + delta = 0; + + if (0 != delta && SUCCEEDED(UIntMult(delta, sizeof(DWORD), &delta))) + offset += delta; + + hr = wicSource->CopyPixels(NULL, strideSize, imageSize, ((BYTE*)pixelData) + offset); + } + } + + if (FAILED(hr)) + { + DeleteObject(bitmap); + bitmap = NULL; + } + + return bitmap; +} + +static BOOL +DeviceImage_ScaleBitmap(HBITMAP sourceBitmap, int width, int height, DeviceImageCache *cache, + DeviceImageFlags flags, HBITMAP *scalledBitmap) +{ + HBITMAP resultBitmap; + BITMAP sourceInfo; + IWICImagingFactory *wicFactory; + unsigned int requestedWidth, requestedHeight; + double scale, scaleH; + int t; + + if (NULL == sourceBitmap || NULL == scalledBitmap) + return FALSE; + + if (sizeof(sourceInfo) != GetObject(sourceBitmap, sizeof(sourceInfo), &sourceInfo)) + return FALSE; + + if (sourceInfo.bmHeight < 0) + sourceInfo.bmHeight = -sourceInfo.bmHeight; + + scale = (double)width / sourceInfo.bmWidth; + scaleH = (double)height / sourceInfo.bmHeight; + + if (scale > scaleH) + scale = scaleH; + + if (1.0 == scale) + { + *scalledBitmap = NULL; + return TRUE; + } + + requestedWidth = width; + requestedHeight = height; + + t = (int)((sourceInfo.bmWidth * scale) + 0.5); + if (t < width) + width = t; + + t = (int)((sourceInfo.bmHeight * scale) + 0.5); + if (t < height) + height = t; + + + resultBitmap = NULL; + + wicFactory = DeviceImage_GetWicFactory(cache); + if (NULL != wicFactory) + { + HRESULT hr; + IWICBitmap *wicBitmap; + + hr = wicFactory->CreateBitmapFromHBITMAP(sourceBitmap, NULL, + WICBitmapUsePremultipliedAlpha, &wicBitmap); + if (SUCCEEDED(hr)) + { + IWICBitmapScaler *wicScaler; + hr = wicFactory->CreateBitmapScaler(&wicScaler); + if (SUCCEEDED(hr)) + { + hr = wicScaler->Initialize(wicBitmap, width, height, WICBitmapInterpolationModeFant); + if (SUCCEEDED(hr)) + { + resultBitmap = DeviceImage_HBitmapFromWicSource(wicScaler, + requestedWidth, requestedHeight, flags); + } + wicScaler->Release(); + + } + wicBitmap->Release(); + } + + wicFactory->Release(); + } + + *scalledBitmap = resultBitmap; + return (NULL != resultBitmap); +} + +static HBITMAP +DeviceImage_GetExactBitmap(DeviceImage *self, DeviceImageFlags flags) +{ + if (NULL == self) + return NULL; + + if (NULL == self->exactBitmap) + { + if (NULL != self->bitmap) + { + BITMAP bi; + if (sizeof(bi) == GetObject(self->bitmap, sizeof(bi), &bi) && + bi.bmWidth == self->width && bi.bmHeight == self->height) + { + self->exactBitmap = self->bitmap; + return self->exactBitmap; + } + } + + if (NULL != self->loader) + { + HBITMAP bitmap; + bitmap = self->loader(self->source, self->width, self->height, self->loaderParam); + if (NULL != bitmap) + { + if (FALSE != DeviceImage_ScaleBitmap(bitmap, self->width, self->height, + self->cache, flags, &self->exactBitmap)) + { + + if (NULL == self->exactBitmap) + { + self->exactBitmap = bitmap; + bitmap = NULL; + } + } + + if (NULL != bitmap) + DeleteObject(bitmap); + } + } + } + + return self->exactBitmap; +} + + +HBITMAP +DeviceImage_GetBitmap(DeviceImage *self, DeviceImageFlags flags) +{ + if (NULL == self) + return NULL; + + if (0 != (DeviceImage_ExactSize & flags)) + return DeviceImage_GetExactBitmap(self, flags); + + if (NULL == self->bitmap) + { + if (NULL != self->loader) + { + HBITMAP bitmap; + bitmap = self->loader(self->source, self->width, self->height, self->loaderParam); + if (NULL != bitmap) + { + if (FALSE == DeviceImage_ScaleBitmap(bitmap, self->width, self->height, + self->cache, flags, &self->bitmap)) + { + self->bitmap = NULL; + } + + if (NULL != self->bitmap) + DeleteObject(bitmap); + else + self->bitmap = bitmap; + } + } + } + + return self->bitmap; +} + + +static DeviceColoredImage * +DeveiceColoredImage_Create(DeviceImage *base, COLORREF color1, COLORREF color2, + DeviceImageFilter filter, void *user) +{ + DeviceColoredImage *coloredImage; + + if (NULL == base) + return NULL; + + coloredImage = (DeviceColoredImage*)malloc(sizeof(DeviceColoredImage)); + if (NULL == coloredImage) + return NULL; + + ZeroMemory(coloredImage, sizeof(DeviceColoredImage)); + + coloredImage->ref = 1; + coloredImage->base = base; + coloredImage->color1 = color1; + coloredImage->color2 = color2; + coloredImage->filter = filter; + coloredImage->filterParam = user; + + DeviceImage_AddRef(base); + + return coloredImage; +} + +static void +DeviceColoredImage_Free(DeviceColoredImage *self) +{ + if (NULL == self) + return; + + if (NULL != self->bitmap) + DeleteObject(self->bitmap); + + free(self); +} + +static int +DeviceColoredImage_SearchCb(const void *key, const void *element) +{ + DeviceColoredImageSeachParam *search; + DeviceColoredImage *image; + int result; + + search = (DeviceColoredImageSeachParam*)key; + image = (DeviceColoredImage*)element; + + result = search->color1 - image->color1; + if (0 != result) + return result; + + return search->color2- image->color2; + +} + +static int +DeviceColoredImage_SortCb(const void *element1, const void *element2) +{ + DeviceColoredImage *image1; + DeviceColoredImage *image2; + int result; + + image1 = (DeviceColoredImage*)element1; + image2 = (DeviceColoredImage*)element2; + + result = image1->color1 - image2->color1; + if (0 != result) + return result; + + return image1->color2- image2->color2; +} + +static int +DeviceColoredImage_SortCb_V2(const void* element1, const void* element2) +{ + return DeviceColoredImage_SortCb(element1, element2) < 0; +} + +DeviceColoredImage * +DeviceImage_GetColoredImage(DeviceImage *self, COLORREF color1, COLORREF color2, + DeviceImageFilter filter, void *user) +{ + size_t listSize; + DeviceColoredImage *image; + DeviceColoredImageSeachParam searchParam; + + searchParam.color1 = color1; + searchParam.color2 = color2; + + listSize = self->list.size(); + if (listSize > 0) + { + DeviceColoredImage* image_ptr = NULL; + //DeviceColoredImage **image_ptr = (DeviceColoredImage**)bsearch(&searchParam, &self->list[0], listSize, + // sizeof(DeviceColoredImage**), + // DeviceColoredImage_SearchCb); + + auto it = std::find_if(self->list.begin(), self->list.end(), + [&](DeviceColoredImage* upT) -> bool + { + return DeviceColoredImage_SearchCb(&searchParam, upT) == 0; + } + ); + if (it != self->list.end()) + { + image_ptr = *it; + } + + if (NULL != image_ptr) + { + image = image_ptr; + DeviceColoredImage_AddRef(image); + return image; + } + } + + image = DeveiceColoredImage_Create(self, color1, color2, filter, user); + if (NULL == image) + return NULL; + + self->list.push_back(image); + listSize++; + + if (listSize > 1) + { + //qsort(&self->list[0], self->list.size(), sizeof(DeviceColoredImage**), + // DeviceColoredImage_SortCb); + std::sort(self->list.begin(), self->list.end(), DeviceColoredImage_SortCb_V2); + } + + return image; +} + +static void +DeviceImage_RemoveColored(DeviceImage *self, DeviceColoredImage *coloredImage) +{ + size_t index; + + if (NULL == self || NULL == coloredImage) + return; + + index = self->list.size(); + while(index--) + { + if (coloredImage == self->list[index]) + { + self->list.erase(self->list.begin() + index); + } + } +} + +size_t +DeviceColoredImage_AddRef(DeviceColoredImage *self) +{ + if (NULL == self) + return 0; + return InterlockedIncrement((LONG*)&self->ref); +} + +size_t +DeviceColoredImage_Release(DeviceColoredImage *self) +{ + size_t r; + if (NULL == self || 0 == self->ref) + return 0; + + r = InterlockedDecrement((LONG*)&self->ref); + if (0 == r) + { + if (NULL != self->base) + { + DeviceImage_RemoveColored(self->base, self); + DeviceImage_Release(self->base); + } + DeviceColoredImage_Free(self); + return 0; + } + + return r; +} + +static BOOL +DeviceColoredImage_DefaultFilter(HBITMAP bitmap, COLORREF color1, COLORREF color2, void *user) +{ + DIBSECTION bitmapDib; + BITMAP *bitmapInfo; + MLIMAGEFILTERAPPLYEX filter; + + if (sizeof(bitmapDib) != GetObjectW(bitmap, sizeof(bitmapDib), &bitmapDib)) + return FALSE; + + bitmapInfo = &bitmapDib.dsBm; + + filter.cbSize = sizeof(filter); + filter.pData = (BYTE*)bitmapInfo->bmBits; + filter.cx = bitmapInfo->bmWidth; + filter.cy = bitmapInfo->bmHeight; + filter.bpp = bitmapInfo->bmBitsPixel; + filter.imageTag = NULL; + + filter.filterUID = MLIF_GRAYSCALE_UID; + MLImageFilter_ApplyEx(Plugin_GetLibraryWindow(), &filter); + + filter.rgbBk = color1; + filter.rgbFg = color2; + + if (32 == bitmapInfo->bmBitsPixel) + { + filter.filterUID = MLIF_FILTER1_PRESERVE_ALPHA_UID; + MLImageFilter_ApplyEx(Plugin_GetLibraryWindow(), &filter); + } + else + { + filter.filterUID = MLIF_FILTER1_UID; + MLImageFilter_ApplyEx(Plugin_GetLibraryWindow(), &filter); + } + + return TRUE; +} + +HBITMAP +DeviceColoredImage_GetBitmap(DeviceColoredImage *self, DeviceImageFlags flags) +{ + if (NULL == self) + return NULL; + + if (NULL == self->bitmap) + { + HBITMAP bitmap; + bitmap = DeviceImage_GetBitmap(self->base, flags); + if (NULL != bitmap) + { + self->bitmap = Image_DuplicateDib(bitmap); + if (NULL != self->bitmap) + { + DeviceImageFilter filter; + if (NULL == self->filter) + filter = DeviceColoredImage_DefaultFilter; + else + filter = self->filter; + + Image_Demultiply(self->bitmap, NULL); + filter(self->bitmap, self->color1, self->color2, self->filterParam); + Image_Premultiply(self->bitmap, NULL); + } + } + } + + return self->bitmap; +} + +DeviceImage* +DeviceColoredImage_GetBaseImage(DeviceColoredImage *self) +{ + if (NULL == self || NULL == self->base) + return NULL; + + DeviceImage_AddRef(self->base); + return self->base; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/imageCache.h b/Src/Plugins/Library/ml_devices/imageCache.h new file mode 100644 index 00000000..1c67f98b --- /dev/null +++ b/Src/Plugins/Library/ml_devices/imageCache.h @@ -0,0 +1,76 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_IMAGE_CACHE_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_IMAGE_CACHE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +typedef struct DeviceColoredImage DeviceColoredImage; +typedef struct DeviceImage DeviceImage; +typedef struct DeviceImageCache DeviceImageCache; + +typedef HBITMAP (*DeviceImageLoader)(const wchar_t* /*path*/, int /*width*/, int /*height*/, void* /*user*/); +typedef BOOL (*DeviceImageFilter)(HBITMAP /*bitmap*/, COLORREF /*color1*/, COLORREF /*color2*/, void* /*user*/); + +DeviceImageCache * +DeviceImageCache_Create(); + +void +DeviceImageCache_Free(DeviceImageCache *self); + +DeviceImage * +DeviceImageCache_GetImage(DeviceImageCache *self, + const wchar_t *path, + int width, + int height, + DeviceImageLoader loader, + void *user); + +size_t +DeviceImage_AddRef(DeviceImage *self); + +size_t +DeviceImage_Release(DeviceImage *self); + +BOOL +DeviceImage_GetSize(DeviceImage *self, + int *width, + int *height); + +typedef enum DeviceImageFlags +{ + + DeviceImage_ExactSize = (1 << 0), + DeviceImage_AlignLeft = 0, + DeviceImage_AlignRight = (1 << 1), + DeviceImage_AlignHCenter = (1 << 2), + DeviceImage_AlignTop = 0, + DeviceImage_AlignBottom = (1 << 3), + DeviceImage_AlignVCenter = (1 << 4), + DeviceImage_Normal = (DeviceImage_AlignLeft | DeviceImage_AlignTop), +} DeviceImageFlags; +DEFINE_ENUM_FLAG_OPERATORS(DeviceImageFlags); + +HBITMAP +DeviceImage_GetBitmap(DeviceImage *self, DeviceImageFlags flags); + +DeviceColoredImage * +DeviceImage_GetColoredImage(DeviceImage *self, + COLORREF color1, + COLORREF color2, + DeviceImageFilter filter, + void *user); + +size_t +DeviceColoredImage_AddRef(DeviceColoredImage *self); + +size_t +DeviceColoredImage_Release(DeviceColoredImage *self); + +HBITMAP +DeviceColoredImage_GetBitmap(DeviceColoredImage *self, DeviceImageFlags flags); + +DeviceImage* +DeviceColoredImage_GetBaseImage(DeviceColoredImage *self); + +#endif // _NULLSOFT_WINAMP_ML_DEVICES_IMAGE_CACHE_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/infoWidget.cpp b/Src/Plugins/Library/ml_devices/infoWidget.cpp new file mode 100644 index 00000000..7a85e9bc --- /dev/null +++ b/Src/Plugins/Library/ml_devices/infoWidget.cpp @@ -0,0 +1,399 @@ +#include "main.h" +#include "./infoWidget.h" + +#define INFOWIDGET_OFFSET_LEFT_DLU 4 +#define INFOWIDGET_OFFSET_TOP_DLU 2 +#define INFOWIDGET_OFFSET_RIGHT_DLU 4 +#define INFOWIDGET_OFFSET_BOTTOM_DLU 2 + +#define INFOWIDGET_MIN_WIDTH_DLU (8*4) +#define INFOWIDGET_MAX_WIDTH_DLU (48*4) + +#define INFOWIDGET_TITLE_OFFSET_BOTTOM_DLU 24 +#define INFOWIDGET_IMAGE_OFFSET_BOTTOM_DLU 24 + + +typedef struct InfoWidget +{ + wchar_t *title; + wchar_t *text; + wchar_t *imagePath; + HBITMAP image; + RECT titleRect; + RECT textRect; + RECT imageRect; + BackBuffer backBuffer; +} InfoWidget; + +typedef struct InfoWidgetParam +{ + const wchar_t *title; + const wchar_t *text; + const wchar_t *imagePath; +} InfoWidgetParam; + +static BOOL +InfoWidget_InitCb(HWND hwnd, void **object, void *param) +{ + InfoWidget *self; + const InfoWidgetParam *createParam; + + self = (InfoWidget*)malloc(sizeof(InfoWidget)); + if (NULL == self) + return FALSE; + + ZeroMemory(self, sizeof(InfoWidget)); + + + createParam = (InfoWidgetParam*)param; + + if (NULL != createParam) + { + wchar_t buffer[4096] = {0}; + + if (FALSE != IS_INTRESOURCE(createParam->title)) + { + if (NULL != WASABI_API_LNG) + { + WASABI_API_LNGSTRINGW_BUF((int)(INT_PTR)createParam->title, buffer, ARRAYSIZE(buffer)); + self->title = String_Duplicate(buffer); + } + else + self->title = NULL; + } + else + self->title = String_Duplicate(createParam->title); + + if (FALSE != IS_INTRESOURCE(createParam->text)) + { + if (NULL != WASABI_API_LNG) + { + WASABI_API_LNGSTRINGW_BUF((int)(INT_PTR)createParam->text, buffer, ARRAYSIZE(buffer)); + self->text = String_Duplicate(buffer); + } + else + self->text = NULL; + } + else + self->text = String_Duplicate(createParam->text); + + self->imagePath = ResourceString_Duplicate(createParam->imagePath); + } + + BackBuffer_Initialize(&self->backBuffer, hwnd); + + *object = self; + + return TRUE; +} + +static void +InfoWidget_DestroyCb(InfoWidget *self, HWND hwnd) +{ + if (NULL == self) + return; + + String_Free(self->title); + String_Free(self->text); + ResourceString_Free(self->imagePath); + + if (NULL != self->image) + DeleteObject(self->image); + + BackBuffer_Uninitialize(&self->backBuffer); + free(self); +} + + +static HBITMAP +InfoWidget_GetImage(InfoWidget *self, WidgetStyle *style) +{ + if (NULL == self->image) + { + unsigned int flags; + + flags = IMAGE_FILTER_BLEND; + + if (FALSE == IS_INTRESOURCE(self->imagePath)) + flags |= ISF_LOADFROMFILE; + + self->image = Image_LoadSkinned(self->imagePath, SRC_TYPE_PNG, flags, + 0, 0, + WIDGETSTYLE_IMAGE_BACK_COLOR(style), + WIDGETSTYLE_IMAGE_FRONT_COLOR(style), + WIDGETSTYLE_BACK_COLOR(style)); + + } + + return self->image; +} + +static BOOL +InfoWidget_GetImageSize(InfoWidget *self, WidgetStyle *style, SIZE *size) +{ + HBITMAP image; + BITMAP imageInfo; + + image = InfoWidget_GetImage(self, style); + if (NULL == image) + return FALSE; + + if (sizeof(imageInfo) != GetObject(image, sizeof(imageInfo), &imageInfo)) + return FALSE; + + size->cx = imageInfo.bmWidth; + size->cy = imageInfo.bmHeight; + if (size->cy < 0) + size->cy = -size->cy; + + return TRUE; +} + +static BOOL +InfoWidget_GetTextSize(HDC hdc, HFONT font, const wchar_t *text, long width, + unsigned int format, SIZE *size) +{ + RECT rect; + BOOL result; + HFONT prevFont; + + if (FALSE != IS_STRING_EMPTY(text)) + { + size->cx = 0; + size->cy = 0; + return TRUE; + } + + prevFont = SelectFont(hdc, font); + + SetRect(&rect, 0, 0, width, 0); + result = DrawText(hdc, text, -1, &rect, DT_CALCRECT | format); + if (FALSE != result) + { + size->cx = RECTWIDTH(rect); + size->cy = RECTHEIGHT(rect); + } + + SelectFont(hdc, prevFont); + + return result; +} + +static long +InfoWidget_GetClientWidth(WidgetStyle *style, long viewWidth) +{ + long test; + + viewWidth -= (WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_OFFSET_LEFT_DLU) + + WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_OFFSET_RIGHT_DLU)); + + test = WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_MIN_WIDTH_DLU); + if (viewWidth < test) + return test; + + test = WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_MAX_WIDTH_DLU); + if (viewWidth > test) + return test; + + return viewWidth; +} + +static void +InfoWidget_LayoutCb(InfoWidget *self, HWND hwnd, WidgetStyle *style, + const RECT *clientRect, SIZE *viewSize, BOOL redraw) +{ + HDC windowDC; + LONG offsetX, offsetY; + SIZE widgetSize; + RECT offsetRect; + + if (NULL == self || NULL == style) + return; + + windowDC = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL == windowDC) + return; + + offsetRect.left = WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_OFFSET_LEFT_DLU); + offsetRect.top = WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_OFFSET_TOP_DLU); + offsetRect.right = WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_OFFSET_RIGHT_DLU); + offsetRect.bottom = WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_OFFSET_BOTTOM_DLU); + + widgetSize.cx = InfoWidget_GetClientWidth(style, RECTWIDTH(*clientRect)); + widgetSize.cy = 0; + + SetRectEmpty(&self->imageRect); + if (FALSE != InfoWidget_GetImageSize(self, style, ((SIZE*)&self->imageRect) + 1)) + { + if (widgetSize.cx < self->imageRect.right) + widgetSize.cx = self->imageRect.right; + + widgetSize.cy += self->imageRect.bottom; + if (0 != self->imageRect.bottom) + widgetSize.cy += WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_IMAGE_OFFSET_BOTTOM_DLU); + } + + SetRectEmpty(&self->titleRect); + if (FALSE != InfoWidget_GetTextSize(windowDC, style->titleFont, self->title, widgetSize.cx, + DT_LEFT | DT_TOP | DT_NOPREFIX | DT_WORDBREAK, ((SIZE*)&self->titleRect) + 1)) + { + widgetSize.cy += self->titleRect.bottom; + if (0 != self->titleRect.bottom) + widgetSize.cy += WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_TITLE_OFFSET_BOTTOM_DLU); + } + + SetRectEmpty(&self->textRect); + if (FALSE != InfoWidget_GetTextSize(windowDC, style->textFont, self->text, widgetSize.cx, + DT_LEFT | DT_TOP | DT_NOPREFIX | DT_WORDBREAK, ((SIZE*)&self->textRect) + 1)) + { + widgetSize.cy += self->textRect.bottom; + } + + + if ((widgetSize.cx + (offsetRect.left + offsetRect.right)) < RECTWIDTH(*clientRect)) + offsetX = clientRect->left + (RECTWIDTH(*clientRect) - widgetSize.cx)/2; + else + offsetX = clientRect->left + offsetRect.left; + + if ((widgetSize.cy + (offsetRect.top + offsetRect.bottom)) < RECTHEIGHT(*clientRect)) + offsetY = clientRect->top + (RECTHEIGHT(*clientRect) - widgetSize.cy)/2; + else + offsetY = clientRect->top + offsetRect.top; + + + if (FALSE == IsRectEmpty(&self->titleRect)) + { + OffsetRect(&self->titleRect, offsetX + (widgetSize.cx - self->titleRect.right)/2, offsetY); + offsetY = self->titleRect.bottom; + offsetY += WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_TITLE_OFFSET_BOTTOM_DLU); + } + + if (FALSE == IsRectEmpty(&self->imageRect)) + { + OffsetRect(&self->imageRect, offsetX + (widgetSize.cx - self->imageRect.right)/2, offsetY); + offsetY = self->imageRect.bottom; + offsetY += WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_IMAGE_OFFSET_BOTTOM_DLU); + } + + if (FALSE == IsRectEmpty(&self->textRect)) + { + OffsetRect(&self->textRect, offsetX + (widgetSize.cx - self->textRect.right)/2, offsetY); + } + + ReleaseDC(hwnd, windowDC); + + viewSize->cx = widgetSize.cx + offsetRect.left + offsetRect.right; + viewSize->cy = widgetSize.cy + offsetRect.top + offsetRect.bottom; +} + + +static BOOL +InfoWidget_PaintCb(InfoWidget *self, HWND hwnd, WidgetStyle *style, HDC hdc, const RECT *paintRect, BOOL erase) +{ + RECT intersectRect; + FillRegion fillRegion; + + FillRegion_Init(&fillRegion, paintRect); + + if (FALSE == IS_STRING_EMPTY(self->title) && + FALSE != IntersectRect(&intersectRect, &self->titleRect, paintRect)) + { + if (FALSE != BackBuffer_DrawTextEx(&self->backBuffer, hdc, self->title, -1, &self->titleRect, + DT_CENTER | DT_NOPREFIX | DT_WORDBREAK, + style->titleFont, style->backColor, style->titleColor, OPAQUE)) + { + FillRegion_ExcludeRect(&fillRegion, &intersectRect); + } + } + + if (NULL != self->image && + FALSE != IntersectRect(&intersectRect, &self->imageRect, paintRect)) + { + HDC windowDC = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != windowDC) + { + HDC sourceDC = CreateCompatibleDC(windowDC); + if (NULL != sourceDC) + { + HBITMAP prevBitmap = SelectBitmap(sourceDC, self->image); + if (FALSE != BitBlt(hdc, intersectRect.left, intersectRect.top, + RECTWIDTH(intersectRect), RECTHEIGHT(intersectRect), + sourceDC, + intersectRect.left - self->imageRect.left, + intersectRect.top - self->imageRect.top, + SRCCOPY)) + { + FillRegion_ExcludeRect(&fillRegion, &intersectRect); + } + + SelectBitmap(sourceDC, prevBitmap); + DeleteDC(sourceDC); + } + ReleaseDC(hwnd, windowDC); + } + } + + if (FALSE == IS_STRING_EMPTY(self->text) && + FALSE != IntersectRect(&intersectRect, &self->textRect, paintRect)) + { + if (FALSE != BackBuffer_DrawTextEx(&self->backBuffer, hdc, self->text, -1, &self->textRect, + DT_CENTER | DT_NOPREFIX | DT_WORDBREAK, + style->textFont, style->backColor, style->textColor, OPAQUE)) + { + FillRegion_ExcludeRect(&fillRegion, &intersectRect); + } + } + + + if (FALSE != erase) + FillRegion_BrushFill(&fillRegion, hdc, style->backBrush); + + FillRegion_Uninit(&fillRegion); + + return TRUE; +} + +static void +InfoWidget_StyleColorChangedCb(InfoWidget *self, HWND hwnd, WidgetStyle *style) +{ + if (NULL == self) + return; + + if (NULL != self->image) + { + if (NULL != self->image) + DeleteObject(self->image); + + self->image = NULL; + InfoWidget_GetImage(self, style); + } +} + +HWND InfoWidget_CreateWindow(unsigned int type, const wchar_t *title, const wchar_t *text, + const wchar_t *imagePath, HWND parentWindow, + int x, int y, int width, int height, BOOL border, unsigned int controlId) +{ + const static WidgetInterface infoWidgetInterface = + { + (WidgetInitCallback)InfoWidget_InitCb, + (WidgetDestroyCallback)InfoWidget_DestroyCb, + (WidgetLayoutCallback)InfoWidget_LayoutCb, + (WidgetPaintCallback)InfoWidget_PaintCb, + (WidgetStyleCallback)InfoWidget_StyleColorChangedCb, + }; + + InfoWidgetParam param; + + param.title = title; + param.text = text; + param.imagePath = imagePath; + + return Widget_CreateWindow(type, + &infoWidgetInterface, + NULL, + (FALSE != border) ? WS_EX_CLIENTEDGE : 0, + 0, + x, y, width, height, + parentWindow, + controlId, ¶m); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/infoWidget.h b/Src/Plugins/Library/ml_devices/infoWidget.h new file mode 100644 index 00000000..deb0035c --- /dev/null +++ b/Src/Plugins/Library/ml_devices/infoWidget.h @@ -0,0 +1,27 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_INFO_WIDGET_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_INFO_WIDGET_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#define WIDGET_TYPE_UNKNOWN 0 +#define WIDGET_TYPE_WELCOME 1 +#define WIDGET_TYPE_SERVICE_ERROR 2 +#define WIDGET_TYPE_VIEW_ERROR 3 + +HWND InfoWidget_CreateWindow(unsigned int type, + const wchar_t *title, + const wchar_t *text, + const wchar_t *imagePath, + HWND parentWindow, + int x, + int y, + int width, + int height, + BOOL border, + unsigned int controlId); + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_INFO_WIDGET_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/listWidget.cpp b/Src/Plugins/Library/ml_devices/listWidget.cpp new file mode 100644 index 00000000..7b57142d --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidget.cpp @@ -0,0 +1,4096 @@ +#include "main.h" +#include "./listWidget.h" +#include "./listWidgetInternal.h" +#include <vector> + +#include <strsafe.h> + +#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<ifc_device*> 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); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/listWidget.h b/Src/Plugins/Library/ml_devices/listWidget.h new file mode 100644 index 00000000..01713f58 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidget.h @@ -0,0 +1,23 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_LIST_WIDGET_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_LIST_WIDGET_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +#define WIDGET_TYPE_LIST 3 + +HWND +ListWidget_CreateWindow(HWND parentWindow, + int x, + int y, + int width, + int height, + BOOL border, + unsigned int controlId); + + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_LIST_WIDGET_HEADER diff --git a/Src/Plugins/Library/ml_devices/listWidgetCategory.cpp b/Src/Plugins/Library/ml_devices/listWidgetCategory.cpp new file mode 100644 index 00000000..0bb9cd18 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidgetCategory.cpp @@ -0,0 +1,202 @@ +#include "main.h" +#include "./listWidgetInternal.h" +#include <algorithm> + +#define LISTWIDGETCATEGORY_OFFSET_LEFT_DLU 3 +#define LISTWIDGETCATEGORY_OFFSET_TOP_DLU 0 +#define LISTWIDGETCATEGORY_OFFSET_RIGHT_DLU 4 +#define LISTWIDGETCATEGORY_OFFSET_BOTTOM_DLU 0 +#define LISTWIDGETCATEGORY_LINE_OFFSET_TOP_DLU 0 +#define LISTWIDGETCATEGORY_LINE_HEIGHT_DLU 1 +#define LISTWIDGETCATEGORY_TITLE_OFFSET_LEFT_DLU 4 +#define LISTWIDGETCATEGORY_MIN_HEIGHT_DLU 9 +#define LISTWIDGETCATEGORY_ARROW_WIDTH_PX 8 +#define LISTWIDGETCATEGORY_ARROW_HEIGHT_PX 8 + + +ListWidgetCategory * +ListWidget_CreateCategory(const char *name, const wchar_t *title, BOOL collapsed) +{ + ListWidgetCategory *category; + + if (NULL == name) + return NULL; + + category = new ListWidgetCategory(); + if(NULL == category) + return NULL; + + category->name = AnsiString_Duplicate(name); + category->title = String_Duplicate(title); + category->collapsed = collapsed; + category->countString = NULL; + category->countWidth = -1; + category->titleWidth = -1; + category->emptyText = NULL; + SetRect(&category->emptyTextRect, -1, -1, -1, -1); + + return category; +} + +void +ListWidget_DestroyCategory(ListWidgetCategory *category) +{ + size_t index; + if (NULL == category) + return; + + index = category->groups.size(); + while(index--) + { + ListWidget_DestroyGroup(category->groups[index]); + } + + AnsiString_Free(category->name); + String_Free(category->title); + String_Free(category->countString); + String_Free(category->emptyText); + + delete category; +} + +BOOL +ListWidget_GetCategoryMetrics(WidgetStyle *style, ListWidgetCategoryMetric *metrics) +{ + long test; + + if (NULL == metrics || NULL == style) + return FALSE; + + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(metrics->offsetLeft, style, LISTWIDGETCATEGORY_OFFSET_LEFT_DLU, 1); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->offsetTop, style, LISTWIDGETCATEGORY_OFFSET_TOP_DLU, 1); + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(metrics->offsetRight, style, LISTWIDGETCATEGORY_OFFSET_RIGHT_DLU, 1); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->offsetBottom, style, LISTWIDGETCATEGORY_OFFSET_BOTTOM_DLU, 1); + + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(metrics->titleOffsetLeft, style, LISTWIDGETCATEGORY_TITLE_OFFSET_LEFT_DLU, 2); + + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->lineOffsetTop, style, LISTWIDGETCATEGORY_LINE_OFFSET_TOP_DLU, 1); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->minHeight, style, LISTWIDGETCATEGORY_MIN_HEIGHT_DLU, 1); + + #if (0 != LISTWIDGETCATEGORY_LINE_HEIGHT_DLU) + { + metrics->lineHeight = WIDGETSTYLE_DLU_TO_VERT_PX(style, LISTWIDGETCATEGORY_LINE_HEIGHT_DLU); + metrics->lineHeight = metrics->lineHeight /2; + if (0 == metrics->lineHeight) + metrics->lineHeight = 1; + } + #endif + + + metrics->iconWidth = LISTWIDGETCATEGORY_ARROW_WIDTH_PX; + metrics->iconHeight = LISTWIDGETCATEGORY_ARROW_HEIGHT_PX; + + test = metrics->iconHeight + + metrics->offsetTop + metrics->offsetBottom + + metrics->lineHeight + metrics->lineOffsetTop; + + if (metrics->minHeight < test) + metrics->minHeight = test; + + return TRUE; +} + +ListWidgetCategory * +ListWidget_FindCategory(ListWidget *self, const char *name) +{ + if (NULL == self || NULL == name) + return NULL; + + for (size_t iCategory = 0; iCategory < self->categories.size(); iCategory++) + { + ListWidgetCategory *category = self->categories[iCategory]; + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, name, -1, category->name, -1)) + return category; + } + return NULL; +} + +ListWidgetCategory * +ListWidget_GetCategoryFromPoint(ListWidget *self, POINT point) +{ + size_t iCategory; + ListWidgetCategory *category; + + if (NULL == self) + return NULL; + + for (iCategory = 0; iCategory < self->categories.size(); iCategory++) + { + category = self->categories[iCategory]; + if (FALSE != PtInRect(&category->rect, point)) + return category; + } + return NULL; +} + +BOOL +ListWidget_ToggleCategory(ListWidgetCategory *category, HWND hwnd) +{ + if (NULL == category) + return FALSE; + + category->collapsed = !category->collapsed; + + if (NULL != hwnd) + ListWidget_UpdateLayout(hwnd, ListWidgetLayout_UpdateNow); + + return TRUE; +} + +void +ListWidget_ResetCategoryCounter(ListWidgetCategory *category) +{ + if (NULL != category) + { + String_Free(category->countString); + category->countString = NULL; + category->countWidth = -1; + } +} + +static bool +ListWidget_GroupSortCb(const void *element1, const void *element2) +{ + ListWidgetGroup *group1; + ListWidgetGroup *group2; + int result; + + group1 = *(ListWidgetGroup**)element1; + group2 = *(ListWidgetGroup**)element2; + + result = CompareString(LOCALE_USER_DEFAULT, 0, group1->title, -1, group2->title, -1); + if (CSTR_EQUAL == result || 0 == result) + result = CompareStringA(CSTR_INVARIANT, 0, group1->name, -1, group2->name, -1); + + return (result == CSTR_LESS_THAN); + +} + + +void +ListWidget_SortCategory(ListWidgetCategory *category) +{ + if (category->groups.size()) + { + //qsort(category->groups.first(), category->groups.size(), sizeof(ListWidgetGroup**), ListWidget_GroupSortCb); + std::sort(category->groups.begin(), category->groups.end(), ListWidget_GroupSortCb); + } +} + +BOOL +ListWidget_SetCategoryEmptyText(ListWidgetCategory *category, const wchar_t *text) +{ + if (NULL == category) + return FALSE; + + String_Free(category->emptyText); + category->emptyText = String_Duplicate(text); + + SetRect(&category->emptyTextRect, -1, -1, -1, -1); + + return TRUE; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/listWidgetCommand.cpp b/Src/Plugins/Library/ml_devices/listWidgetCommand.cpp new file mode 100644 index 00000000..6a8707bd --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidgetCommand.cpp @@ -0,0 +1,392 @@ +#include "main.h" +#include "./listWidgetInternal.h" + +#define REQUEST_STRING ((wchar_t*)1) +#define REQUEST_IMAGE ((DeviceImage*)1) + +typedef struct ListWidgetCommand +{ + char *name; + ListWidgetCommandState state; + wchar_t *title; + wchar_t *description; + DeviceImage *imageLarge; + DeviceImage *imageSmall; + RECT rect; +} ListWidgetCommand; + +ListWidgetCommand * +ListWidget_CreateCommand(const char *name, BOOL primary, BOOL disabled) +{ + ListWidgetCommand *self; + + if (NULL == name) + return NULL; + + self = (ListWidgetCommand*)malloc(sizeof(ListWidgetCommand)); + if (NULL == self) + return NULL; + + ZeroMemory(self, sizeof(ListWidgetCommand)); + + self->name = AnsiString_Duplicate(name); + self->state = ListWidgetCommandState_Normal; + + if (NULL != self->name) + { + if (FALSE != primary) + self->state |= ListWidgetCommandState_Primary; + + if (FALSE != disabled) + self->state |= ListWidgetCommandState_Disabled; + + } + else + { + ListWidget_DestroyCommand(self); + return NULL; + } + + self->title = REQUEST_STRING; + self->description = REQUEST_STRING; + self->imageLarge = REQUEST_IMAGE; + self->imageSmall = REQUEST_IMAGE; + + return self; +} + +void +ListWidget_DestroyCommand(ListWidgetCommand *command) +{ + if (NULL == command) + return; + + AnsiString_Free(command->name); + + if (NULL != command->imageLarge && REQUEST_IMAGE != command->imageLarge) + DeviceImage_Release(command->imageLarge); + + if (NULL != command->imageSmall && REQUEST_IMAGE != command->imageSmall) + DeviceImage_Release(command->imageSmall); + + if (REQUEST_STRING != command->title) + String_Free(command->title); + + if (REQUEST_STRING != command->description) + String_Free(command->description); + + free(command); +} + +size_t +ListWigdet_GetDeviceCommands(ListWidgetCommand **buffer, size_t bufferMax, ifc_device *device) +{ + size_t count; + ifc_devicesupportedcommandenum *enumerator; + ifc_devicesupportedcommand *command; + DeviceCommandFlags flags; + ListWidgetCommand *widgetCommand; + BOOL primaryFound; + + if (NULL == buffer || bufferMax < 1 || NULL == device) + return 0; + + if (FAILED(device->EnumerateCommands(&enumerator, DeviceCommandContext_View))) + return 0; + + count = 0; + primaryFound = FALSE; + + while(S_OK == enumerator->Next(&command, 1, NULL)) + { + if(FAILED(command->GetFlags(&flags))) + flags = DeviceCommandFlag_None; + + if (0 == (DeviceCommandFlag_Hidden & flags)) + { + widgetCommand = ListWidget_CreateCommand(command->GetName(), + (0 != (DeviceCommandFlag_Primary & flags)), + (0 != (DeviceCommandFlag_Disabled & flags))); + if (NULL != widgetCommand) + { + if (0 != (DeviceCommandFlag_Primary & flags)) + { + if (count == bufferMax) + { + ListWidget_DestroyCommand(buffer[count-1]); + count--; + } + + if (count > 0) + MoveMemory(&buffer[1], buffer, sizeof(ListWidgetCommand*) * count); + + buffer[0] = widgetCommand; + primaryFound = TRUE; + count++; + } + else + { + if (count < bufferMax) + { + buffer[count] = widgetCommand; + count++; + } + } + } + } + command->Release(); + + if (count == bufferMax && FALSE != primaryFound) + break; + } + + enumerator->Release(); + + return count; +} + +void +ListWidget_DestroyAllCommands(ListWidgetCommand **buffer, size_t bufferMax) +{ + if (NULL == buffer) + return; + + while(bufferMax--) + ListWidget_DestroyCommand(buffer[bufferMax]); +} + + + +const char * +ListWidget_GetCommandName(ListWidgetCommand *command) +{ + return (NULL != command) ? command->name : NULL; +} + +const wchar_t * +ListWidget_GetCommandTitle(ListWidgetCommand *command) +{ + if (NULL == command || NULL == command->title) + return NULL; + + if (REQUEST_STRING == command->title) + { + ifc_devicecommand *info; + + command->title = NULL; + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->CommandFind(command->name, &info)) + { + wchar_t buffer[512] = {0}; + + if (SUCCEEDED(info->GetDisplayName(buffer, ARRAYSIZE(buffer)))) + command->title = String_Duplicate(buffer); + + info->Release(); + } + } + + return command->title; +} + +const wchar_t * +ListWidget_GetCommandDescription(ListWidgetCommand *command) +{ + if (NULL == command || NULL == command->description) + return NULL; + + if (REQUEST_STRING == command->description) + { + ifc_devicecommand *info; + + command->description = NULL; + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->CommandFind(command->name, &info)) + { + wchar_t buffer[1024] = {0}; + + if (SUCCEEDED(info->GetDescription(buffer, ARRAYSIZE(buffer)))) + command->description = String_Duplicate(buffer); + + info->Release(); + } + } + return command->description; +} + +HBITMAP +ListWidget_GetCommandLargeBitmap(WidgetStyle *style, ListWidgetCommand *command, int width, int height) +{ + if (NULL == style || NULL == command || NULL == command->imageLarge) + return NULL; + + if (REQUEST_IMAGE == command->imageLarge) + { + ifc_devicecommand *info; + + command->imageLarge = NULL; + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->CommandFind(command->name, &info)) + { + wchar_t buffer[MAX_PATH * 2] = {0}; + + if (FAILED(info->GetIcon(buffer, ARRAYSIZE(buffer), width, height))) + buffer[0] = L'\0'; + + info->Release(); + + if (L'\0' != buffer[0]) + { + command->imageLarge = DeviceImageCache_GetImage(Plugin_GetImageCache(), buffer, + width, height, NULL, NULL); + + } + } + } + + return DeviceImage_GetBitmap(command->imageLarge, DeviceImage_Normal); +} + +HBITMAP +ListWidget_GetCommandSmallBitmap(WidgetStyle *style, ListWidgetCommand *command, int width, int height) +{ + if (NULL == style || NULL == command || NULL == command->imageSmall) + return NULL; + + if (REQUEST_IMAGE == command->imageSmall) + { + ifc_devicecommand *info; + + command->imageSmall = NULL; + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->CommandFind(command->name, &info)) + { + wchar_t buffer[MAX_PATH * 2] = {0}; + + if (FAILED(info->GetIcon(buffer, ARRAYSIZE(buffer), width, height))) + buffer[0] = L'\0'; + + info->Release(); + + if (L'\0' != buffer[0]) + { + command->imageSmall = DeviceImageCache_GetImage(Plugin_GetImageCache(), buffer, + width, height, NULL, NULL); + + } + } + } + + return DeviceImage_GetBitmap(command->imageSmall, DeviceImage_Normal); +} + +BOOL +ListWidget_ResetCommandImages(ListWidgetCommand *command) +{ + if (NULL == command) + return FALSE; + + if (REQUEST_IMAGE != command->imageLarge) + { + if (NULL != command->imageLarge) + DeviceImage_Release(command->imageLarge); + command->imageLarge = REQUEST_IMAGE; + } + + if (REQUEST_IMAGE != command->imageSmall) + { + if (NULL != command->imageSmall) + DeviceImage_Release(command->imageSmall); + command->imageSmall = REQUEST_IMAGE; + } + + return TRUE; +} + +BOOL +ListWidget_GetCommandRect(ListWidgetCommand *command, RECT *rect) +{ + if (NULL == command || NULL == rect) + return FALSE; + + return CopyRect(rect, &command->rect); +} + +BOOL +ListWidget_SetCommandRect(ListWidgetCommand *command, const RECT *rect) +{ + if (NULL == command || NULL == rect) + return FALSE; + + return CopyRect(&command->rect, rect); +} + +BOOL +ListWidget_GetCommandRectEqual(ListWidgetCommand *command, const RECT *rect) +{ + if (NULL == command || NULL == rect) + return FALSE; + + return EqualRect(&command->rect, rect); +} + +BOOL +ListWidget_GetCommandPrimary(ListWidgetCommand *command) +{ + return (NULL == command || + (0 != (ListWidgetCommandState_Primary & command->state))); +} + + +BOOL +ListWidget_GetCommandDisabled(ListWidgetCommand *command) +{ + return (NULL == command || + (0 != (ListWidgetCommandState_Disabled & command->state))); +} + + +BOOL +ListWidget_GetCommandPressed(ListWidgetCommand *command) +{ + return (NULL == command || + (0 != (ListWidgetCommandState_Pressed & command->state))); +} + +BOOL +ListWidget_EnableCommand(ListWidgetCommand *command, BOOL enable) +{ + if (NULL == command) + return FALSE; + + if ((FALSE == enable) == (0 != (ListWidgetCommandState_Disabled & command->state))) + return FALSE; + + if (FALSE == enable) + command->state |= ListWidgetCommandState_Disabled; + else + command->state &= ~ListWidgetCommandState_Disabled; + + return TRUE; +} + +BOOL +ListWidget_SetCommandPressed(ListWidgetCommand *command, BOOL pressed) +{ + if (NULL == command) + return FALSE; + + if ((FALSE == pressed) == (0 == (ListWidgetCommandState_Pressed & command->state))) + return FALSE; + + if (FALSE == pressed) + command->state &= ~ListWidgetCommandState_Pressed; + else + command->state |= ListWidgetCommandState_Pressed; + + return TRUE; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/listWidgetConnection.cpp b/Src/Plugins/Library/ml_devices/listWidgetConnection.cpp new file mode 100644 index 00000000..f5073181 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidgetConnection.cpp @@ -0,0 +1,262 @@ +#include "main.h" +#include "./listWidgetInternal.h" + +#define LISTWIDGETCONNECTION_IMAGE_WIDTH 16 +#define LISTWIDGETCONNECTION_IMAGE_HEIGHT 16 + +#define REQUEST_STRING ((wchar_t*)1) +#define REQUEST_IMAGE ((DeviceImage*)1) + + +typedef struct ListWidgetConnection +{ + char *name; + wchar_t *title; + DeviceImage *image; +} ListWidgetConnection; + + +ListWidgetConnection * +ListWidget_CreateConnection(const char *name) +{ + ListWidgetConnection *self; + + if (NULL == name) + return NULL; + + self = (ListWidgetConnection*)malloc(sizeof(ListWidgetConnection)); + if (NULL == self) + return NULL; + + ZeroMemory(self, sizeof(ListWidgetConnection)); + + self->name = AnsiString_Duplicate(name); + + if (NULL == self->name) + { + ListWidget_DestroyConnection(self); + return NULL; + } + + self->title = REQUEST_STRING; + self->image = REQUEST_IMAGE; + + return self; +} + +void +ListWidget_DestroyConnection(ListWidgetConnection *connection) +{ + if (NULL == connection) + return; + + AnsiString_Free(connection->name); + + if (NULL != connection->image && REQUEST_IMAGE != connection->image) + DeviceImage_Release(connection->image); + + if (REQUEST_STRING != connection->title) + String_Free(connection->title); + + free(connection); +} + +const wchar_t * +ListWidget_GetConnectionTitle(ListWidgetConnection *connection) +{ + if (NULL == connection || NULL == connection->title) + return NULL; + + if (REQUEST_STRING == connection->title) + { + ifc_deviceconnection *info; + + connection->title = NULL; + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->ConnectionFind(connection->name, &info)) + { + wchar_t buffer[512] = {0}; + + if (SUCCEEDED(info->GetDisplayName(buffer, ARRAYSIZE(buffer)))) + connection->title = String_Duplicate(buffer); + + info->Release(); + } + } + + return connection->title; +} + +HBITMAP +ListWidget_GetConnectionImage(WidgetStyle *style, ListWidgetConnection *connection, int width, int height) +{ + if (NULL == style || NULL == connection || NULL == connection->image) + return NULL; + + if (REQUEST_IMAGE == connection->image) + { + ifc_deviceconnection *info; + + connection->image = NULL; + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->ConnectionFind(connection->name, &info)) + { + wchar_t buffer[MAX_PATH * 2] = {0}; + + if (FAILED(info->GetIcon(buffer, ARRAYSIZE(buffer), width, height))) + buffer[0] = L'\0'; + + info->Release(); + + if (L'\0' != buffer[0]) + { + connection->image = DeviceImageCache_GetImage(Plugin_GetImageCache(), buffer, + width, height, NULL, NULL); + } + } + } + + if (NULL == connection->image) + return NULL; + + return DeviceImage_GetBitmap(connection->image, DeviceImage_Normal); +} + +BOOL +ListWidget_ConnectionResetColors(WidgetStyle *style, ListWidgetConnection *connection) +{ + return FALSE; +} + +void +ListWidget_ResetConnnectionsColors(ListWidget *self, WidgetStyle *style) +{ + return; +} + + +BOOL +ListWidget_UpdateConnectionImageSize(ListWidgetConnection *connection, int width, int height) +{ + if (NULL == connection) + return FALSE; + + if (NULL == connection->image || REQUEST_IMAGE == connection->image) + return TRUE; + + DeviceImage_Release(connection->image); + connection->image = REQUEST_IMAGE; + + return TRUE; +} + +ListWidgetConnection * +ListWidget_FindConnection(ListWidget *self, const char *name) +{ + if (NULL == self || FALSE != IS_STRING_EMPTY(name)) + return NULL; + + size_t index = self->connections.size(); + while(index--) + { + ListWidgetConnection *connection = self->connections[index]; + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, name, -1, connection->name, -1)) + return connection; + } + + return NULL; +} + +BOOL +ListWidget_AddConnection(ListWidget *self, ListWidgetConnection *connection) +{ + if (NULL == self || NULL == connection) + return FALSE; + + self->connections.push_back(connection); + return TRUE; +} + +void +ListWidget_RemoveConnection(ListWidget *self, const char *name) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + + + if (NULL == self || FALSE != IS_STRING_EMPTY(name)) + return; + + size_t index = self->connections.size(); + while(index--) + { + ListWidgetConnection *connection = self->connections[index]; + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, name, -1, connection->name, -1)) + { + 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(item->connection == connection) + item->connection = NULL; + } + } + } + + self->connections.erase(self->connections.begin() + index); + ListWidget_DestroyConnection(connection); + } + } +} + +void +ListWidget_RemoveAllConnections(ListWidget *self) +{ + if (NULL == self) + return; + + size_t index = self->connections.size(); + if (index > 0) + { + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + + 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->connection) + { + item->connection = NULL; + } + } + } + } + + while(index--) + { + ListWidgetConnection *connection = self->connections[index]; + ListWidget_DestroyConnection(connection); + } + + self->connections.clear(); + } + + +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/listWidgetGroup.cpp b/Src/Plugins/Library/ml_devices/listWidgetGroup.cpp new file mode 100644 index 00000000..7ac29e92 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidgetGroup.cpp @@ -0,0 +1,135 @@ +#include "main.h" +#include "./listWidgetInternal.h" +#include <algorithm> + +ListWidgetGroup * +ListWidget_CreateGroup(const char *name) +{ + ifc_devicetype *deviceType; + wchar_t buffer[1024] = {0}; + + if (NULL == name) + return NULL; + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->TypeFind(name, &deviceType)) + { + if (FAILED(deviceType->GetDisplayName(buffer, ARRAYSIZE(buffer)))) + buffer[0] = L'\0'; + + deviceType->Release(); + } + else + buffer[0] = L'\0'; + + return ListWidget_CreateGroupEx(name, buffer); +} + +ListWidgetGroup * +ListWidget_CreateGroupEx(const char *name, const wchar_t *title) +{ + ListWidgetGroup *group; + + if (NULL == name) + return NULL; + + group = new ListWidgetGroup(); + if (NULL == group) + return NULL; + + group->name = AnsiString_Duplicate(name); + group->title = String_Duplicate(title); + + return group; +} + + +void +ListWidget_DestroyGroup(ListWidgetGroup *group) +{ + size_t index; + if (NULL == group) + return; + + index = group->items.size(); + while(index--) + { + ListWidget_DestroyItem(group->items[index]); + } + + AnsiString_Free(group->name); + String_Free(group->title); + + delete group; +} + +BOOL +ListWidget_AddGroup( ListWidgetCategory *category, ListWidgetGroup *group) +{ + if (NULL == category || NULL == group) + return FALSE; + + category->groups.push_back(group); + return TRUE; +} + +ListWidgetGroup * +ListWidget_FindGroupEx(ListWidgetCategory *category, const char *name, size_t max) +{ + size_t index, count; + + if (NULL == category || NULL == name) + return NULL; + + count = category->groups.size(); + if (max < count) + count = max; + + for(index = 0; index < count; index++) + { + ListWidgetGroup *group = category->groups[index]; + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, name, -1, group->name, -1)) + return group; + } + + return NULL; +} + +ListWidgetGroup * +ListWidget_FindGroup(ListWidgetCategory *category, const char *name) +{ + return ListWidget_FindGroupEx(category, name, -1); +} + +static int +ListWidget_ItemSortCb(const void *element1, const void *element2) +{ + ListWidgetItem *item1; + ListWidgetItem *item2; + int result; + + item1 = (ListWidgetItem*)element1; + item2 = (ListWidgetItem*)element2; + + result = CompareString(LOCALE_USER_DEFAULT, 0, item1->title, -1, item2->title, -1); + if (CSTR_EQUAL == result || 0 == result) + result = CompareStringA(CSTR_INVARIANT, 0, item1->name, -1, item2->name, -1); + + return (result - 2); + +} +static bool +ListWidget_ItemSortCb_V2(const void* element1, const void* element2) +{ + return ListWidget_ItemSortCb(element1, element2) < 0; +} + +void +ListWidget_SortGroup(ListWidgetGroup *group) +{ + if (group->items.size()) + { + //qsort(group->items.first(), group->items.size(), sizeof(ListWidgetItem**), ListWidget_ItemSortCb); + std::sort(group->items.begin(), group->items.end(), ListWidget_ItemSortCb_V2); + } +} diff --git a/Src/Plugins/Library/ml_devices/listWidgetInternal.h b/Src/Plugins/Library/ml_devices/listWidgetInternal.h new file mode 100644 index 00000000..8c53a729 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidgetInternal.h @@ -0,0 +1,1020 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_LIST_WIDGET_INTERNAL_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_LIST_WIDGET_INTERNAL_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include <vector> +#include "./imageCache.h" + +typedef enum ListWidgetItemState +{ + ListWidgetItemState_Default = (0), + ListWidgetItemState_Hovered = (1 << 0), + ListWidgetItemState_Selected = (1 << 1), + ListWidgetItemState_Interactive = (1 << 2), + ListWidgetItemState_TextTruncated = (1 << 3), + ListWidgetItemState_TextEdited = (1 << 4), + +} ListWidgetItemSate; +DEFINE_ENUM_FLAG_OPERATORS(ListWidgetItemSate); + +#define ListWidgetItem_State(_item) (((ListWidgetItem*)(_item))->state) +#define ListWidgetItem_SetState(_item, _state) (ListWidgetItem_State(_item) |= (_state)) +#define ListWidgetItem_UnsetState(_item, _state) (ListWidgetItem_State(_item) &= ~(_state)) + +#define ListWidgetItem_IsHovered(_item) (0 != (ListWidgetItemState_Hovered & ListWidgetItem_State(_item))) +#define ListWidgetItem_SetHovered(_item) ListWidgetItem_SetState(_item, ListWidgetItemState_Hovered) +#define ListWidgetItem_UnsetHovered(_item) ListWidgetItem_UnsetState(_item, ListWidgetItemState_Hovered) + +#define ListWidgetItem_IsSelected(_item) (0 != (ListWidgetItemState_Selected & ListWidgetItem_State(_item))) +#define ListWidgetItem_SetSelected(_item) ListWidgetItem_SetState(_item, ListWidgetItemState_Selected) +#define ListWidgetItem_UnsetSelected(_item) ListWidgetItem_UnsetState(_item, ListWidgetItemState_Selected) + +#define ListWidgetItem_IsInteractive(_item) (0 != (ListWidgetItemState_Interactive & ListWidgetItem_State(_item))) +#define ListWidgetItem_SetInteractive(_item) ListWidgetItem_SetState(_item, ListWidgetItemState_Interactive) +#define ListWidgetItem_UnsetInteractive(_item) ListWidgetItem_UnsetState(_item, ListWidgetItemState_Interactive) + + +#define ListWidgetItem_IsTextTruncated(_item) (0 != (ListWidgetItemState_TextTruncated & ListWidgetItem_State(_item))) +#define ListWidgetItem_SetTextTruncated(_item) ListWidgetItem_SetState(_item, ListWidgetItemState_TextTruncated) +#define ListWidgetItem_UnsetTextTruncated(_item) ListWidgetItem_UnsetState(_item, ListWidgetItemState_TextTruncated) + +#define ListWidgetItem_IsTextEdited(_item) (0 != (ListWidgetItemState_TextEdited & ListWidgetItem_State(_item))) +#define ListWidgetItem_SetTextEdited(_item) ListWidgetItem_SetState(_item, ListWidgetItemState_TextEdited) +#define ListWidgetItem_UnsetTextEdited(_item) ListWidgetItem_UnsetState(_item, ListWidgetItemState_TextEdited) + + +typedef struct ListWidgetConnection ListWidgetConnection; +typedef std::vector<ListWidgetConnection*> ListWidgetConnectionList; + +typedef struct ListWidgetTooltip ListWidgetTooltip; + +typedef enum ListWidgetCommandState +{ + ListWidgetCommandState_Normal = (0), + ListWidgetCommandState_Disabled = (1 << 0), + ListWidgetCommandState_Primary = (1 << 1), + ListWidgetCommandState_Pressed = (1 << 2), +} ListWidgetCommandState; +DEFINE_ENUM_FLAG_OPERATORS(ListWidgetCommandState); + +typedef struct ListWidgetCommand ListWidgetCommand; + +typedef struct ListWidgetActivity +{ + unsigned int step; + unsigned int percent; + wchar_t *title; + SIZE titleSize; + BOOL cancelable; +} +ListWidgetActivity; + +typedef struct ListWidgetActivityMetric +{ + long height; + long width; + long progressWidth; + long progressHeight; + long percentWidth; + long percentHeight; + long titleWidth; + long titleHeight; + long fontHeight; + long offsetLeft; + long offsetRight; + long offsetTop; + long offsetBottom; + long spacing; +} ListWidgetActivityMetric; + +typedef enum ListWidgetActivityChange +{ + ListWidgetActivityChanged_Nothing = 0, + ListWidgetActivityChanged_Percent = (1 << 0), + ListWidgetActivityChanged_Title = (1 << 1), + ListWidgetActivityChanged_Cancelable = (1 << 2), + ListWidgetActivityChanged_All = (ListWidgetActivityChanged_Percent | ListWidgetActivityChanged_Title | ListWidgetActivityChanged_Cancelable ), +} +ListWidtetActivityChange; +DEFINE_ENUM_FLAG_OPERATORS(ListWidtetActivityChange); + + +typedef enum ListWidgetItemPart +{ + ListWidgetItemPart_None = 0, + ListWidgetItemPart_Frame = (1 << 0), + ListWidgetItemPart_Image = (1 << 1), + ListWidgetItemPart_Title = (1 << 2), + ListWidgetItemPart_Activity = (1 << 3), + ListWidgetItemPart_Command = (1 << 4), + ListWidgetItemPart_Spacebar = (1 << 5), + ListWidgetItemPart_Connection = (1 << 6), +}ListWidgetItemPart; +DEFINE_ENUM_FLAG_OPERATORS(ListWidgetItemPart); + +typedef struct ListWidgetItem +{ + char *name; + wchar_t *title; + RECT rect; + SIZE titleSize; + DeviceImage *image; + uint64_t spaceTotal; + uint64_t spaceUsed; + ListWidgetItemState state; + ListWidgetConnection *connection; + ListWidgetActivity *activity; +}ListWidgetItem; +typedef std::vector<ListWidgetItem*> ListWidgetItemList; + +typedef struct ListWidgetGroup +{ + char *name; + wchar_t *title; + ListWidgetItemList items; +} ListWidgetGroup; +typedef std::vector<ListWidgetGroup*> ListWidgetGroupList; + +typedef struct ListWidgetCategory +{ + char *name; + wchar_t *title; + BOOL collapsed; + ListWidgetGroupList groups; + RECT rect; + long titleWidth; + long countWidth; + wchar_t *countString; + wchar_t *emptyText; + RECT emptyTextRect; +}ListWidgetCategory; + +typedef std::vector<ListWidgetCategory*> ListWidgetCategoryList; + +typedef enum ListWidgetFlags +{ + ListWidgetFlag_NoFocusSelect = (1 << 0), + ListWidgetFlag_LButtonDownOnCommand = (1 << 1), +} ListWidgetFlags; +DEFINE_ENUM_FLAG_OPERATORS(ListWidgetFlags); + +typedef struct ListWidget +{ + ListWidgetFlags flags; + ListWidgetCategoryList categories; + ListWidgetConnectionList connections; + BackBuffer backBuffer; + ListWidgetItem *hoveredItem; + ListWidgetItem *selectedItem; + ListWidgetItem *titleEditItem; + ListWidgetCategory *pressedCategory; + SIZE imageSize; + long itemWidth; + size_t itemsPerLine; + size_t deviceHandler; + ListWidgetCommand **commands; + size_t commandsCount; + size_t commandsMax; + ListWidgetItemList activeItems; + POINT previousMouse; + + HBITMAP spacebarBitmap; + HBITMAP arrowsBitmap; + + HBITMAP hoverBitmap; + HBITMAP selectBitmap; + HBITMAP inactiveSelectBitmap; + + HBITMAP largeBadgeBitmap; + HBITMAP smallBadgeBitmap; + + SIZE connectionSize; + SIZE primaryCommandSize; + SIZE secondaryCommandSize; + DeviceImage *unknownCommandLargeImage; + DeviceImage *unknownCommandSmallImage; + + ListWidgetActivityMetric activityMetrics; + HFONT activityFont; + HBITMAP activityBadgeBitmap; + DeviceImage *activityProgressImage; + BOOL activityTimerEnabled; + + HMENU activeMenu; + ListWidgetTooltip *tooltip; + + unsigned int selectionStatus; + HWND titleEditor; + +} ListWidget; + +typedef struct +ListWidgetItemMetric +{ + long titleMinWidth; + long offsetLeft; + long offsetTop; + long offsetRight; + long offsetBottom; + long imageOffsetLeft; + long imageOffsetTop; + long imageOffsetRight; + long imageOffsetBottom; + long titleOffsetTop; + long spacebarOffsetTop; + long spacebarHeight; +} ListWidgetItemMetric; + +typedef struct +ListWidgetCategoryMetric +{ + long offsetLeft; + long offsetTop; + long offsetRight; + long offsetBottom; + long lineHeight; + long lineOffsetTop; + long titleOffsetLeft; + long minHeight; + long iconWidth; + long iconHeight; +} ListWidgetCategoryMetric; + +HBITMAP +ListWidget_GetSpacebarBitmap(ListWidget *self, + WidgetStyle *style, + HWND hwnd, + long width, + long height); + +HBITMAP +ListWidget_GetHoverBitmap(ListWidget *self, + WidgetStyle *style, + HWND hwnd, + long width, + long height); + +HBITMAP +ListWidget_GetSelectBitmap(ListWidget *self, + WidgetStyle *style, + HWND hwnd, + long width, + long height); + +HBITMAP +ListWidget_GetInactiveSelectBitmap(ListWidget *self, + WidgetStyle *style, + HWND hwnd, + long width, + long height); + +HBITMAP +ListWidget_GetLargeBadgeBitmap(ListWidget *self, + WidgetStyle *style, + HWND hwnd, + long width, + long height); + + +HBITMAP +ListWidget_GetSmallBadgeBitmap(ListWidget *self, + WidgetStyle *style, + HWND hwnd, + long width, + long height); + +HBITMAP +ListWidget_GetUnknownCommandSmallBitmap(ListWidget *self, + WidgetStyle *style, + long width, + long height); + +HBITMAP +ListWidget_GetUnknownCommandLargeBitmap(ListWidget *self, + WidgetStyle *style, + long width, + long height); + +HBITMAP +ListWidget_GetArrowsBitmap(ListWidget *self, + WidgetStyle *style, + HWND hwnd); + +HBITMAP +ListWidget_GetActivityProgressBitmap(ListWidget *self, + WidgetStyle *style); + + +HBITMAP +ListWidget_GetActivityBadgeBitmap(ListWidget *self, + WidgetStyle *style, + HWND hwnd, + long width, + long height); + +BOOL +ListWidget_GetViewOrigin(HWND hwnd, + POINT *pt); + +BOOL +ListWidget_UpdateHoverEx(ListWidget *self, + HWND hwnd, + const POINT *cursor); + +BOOL +ListWidget_UpdateHover(ListWidget *self, + HWND hwnd); + +BOOL +ListWidget_RemoveHover(ListWidget *self, + HWND hwnd, + BOOL invalidate); + +BOOL +ListWidget_SelectItem(ListWidget *self, + HWND hwnd, + ListWidgetItem *item, + BOOL ensureVisible); + +BOOL +ListWidget_SetImageSize(ListWidget *self, + HWND hwnd, + int imageWidth, + int imageHeight, + BOOL redraw); + +typedef enum ListWidgetLayoutFlags +{ + ListWidgetLayout_Normal = 0, + ListWidgetLayout_NoRedraw = (1 << 0), + ListWidgetLayout_UpdateNow = (1 << 1), + ListWidgetLayout_KeepStable = (1 << 2), +}ListWidgetLayoutFlags; +DEFINE_ENUM_FLAG_OPERATORS(ListWidgetLayoutFlags); + +BOOL +ListWidget_UpdateLayout(HWND hwnd, + ListWidgetLayoutFlags flags); + +BOOL +ListWidget_DisplayContextMenu(ListWidget *self, + HWND hostWindow, + POINT pt); + +BOOL +ListWidget_RegisterActiveItem(ListWidget *self, + HWND hwnd, + ListWidgetItem *item); + +BOOL +ListWidget_UnregisterActiveItem(ListWidget *self, + HWND hwnd, + ListWidgetItem *item); + +double +ListWidget_GetZoomRatio(ListWidget *self); + +void +ListWidget_UpdateSelectionStatus(ListWidget *self, + HWND hwnd, + BOOL ensureVisible); + +void +ListWidget_UpdateSelectionSpaceStatus(ListWidget *self, + HWND hwnd, + BOOL ensureVisible); + +void +ListWidget_UpdateTitleEditorColors(HWND editor, + WidgetStyle *style); +/* +<<<<<<<<<<<<<<<<<<<<<<<<< Category >>>>>>>>>>>>>>>>>>>>>>>>> +*/ + +ListWidgetCategory * +ListWidget_CreateCategory(const char *name, + const wchar_t *title, + BOOL collapsed); + +void +ListWidget_DestroyCategory(ListWidgetCategory *category); + +ListWidgetCategory * +ListWidget_GetCategoryFromPoint(ListWidget *self, + POINT point); + +ListWidgetCategory * +ListWidget_FindCategory(ListWidget *self, + const char *name); + +BOOL +ListWidget_GetCategoryMetrics(WidgetStyle *style, + ListWidgetCategoryMetric *metrics); + +BOOL +ListWidget_ToggleCategory(ListWidgetCategory *category, + HWND hwnd); + +void +ListWidget_ResetCategoryCounter(ListWidgetCategory *category); + +void +ListWidget_SortCategory(ListWidgetCategory *category); + +BOOL +ListWidget_SetCategoryEmptyText(ListWidgetCategory *category, const wchar_t *text); + +/* +<<<<<<<<<<<<<<<<<<<<<<<<< Group >>>>>>>>>>>>>>>>>>>>>>>>> +*/ + +ListWidgetGroup * +ListWidget_CreateGroup(const char *name); + +ListWidgetGroup * +ListWidget_CreateGroupEx(const char *name, + const wchar_t *title); + +void +ListWidget_DestroyGroup(ListWidgetGroup *group); + +BOOL +ListWidget_AddGroup(ListWidgetCategory *category, + ListWidgetGroup *group); + + +ListWidgetGroup * +ListWidget_FindGroup(ListWidgetCategory *category, + const char *name); + +ListWidgetGroup * +ListWidget_FindGroupEx(ListWidgetCategory *category, + const char *name, + size_t max); + +void +ListWidget_SortGroup(ListWidgetGroup *group); + +/* +<<<<<<<<<<<<<<<<<<<<<<<<< Item >>>>>>>>>>>>>>>>>>>>>>>>> +*/ + +typedef enum ListWidgetVisibleFlags +{ + VISIBLE_NORMAL = 0, + VISIBLE_PARTIAL_OK = (1 << 0), + VISIBLE_ALIGN_BOTTOM =(1 << 1), + VISIBLE_ALIGN_TOP = (1 << 2), + VISIBLE_ALIGN_ALWAYS = (1 << 3), +} ListWidgetVisibleFlags; +DEFINE_ENUM_FLAG_OPERATORS(ListWidgetVisibleFlags); + +ListWidgetItem* +ListWidget_CreateItemFromDevice(ListWidget *self, + ifc_device* device); + +void +ListWidget_DestroyItem(ListWidgetItem *item); + +BOOL +ListWidget_CalculateItemBaseSize(ListWidget *self, + WidgetStyle *style, + SIZE *baseSize, + long *itemTextWidth); + +size_t // number of removed items +ListWidget_RemoveItem(ListWidget *self, + HWND hwnd, + const char *name); + +ListWidgetItem * +ListWidget_GetFirstItem(ListWidget *self); + +ListWidgetItem * +ListWidget_GetLastItem(ListWidget *self); + +ListWidgetItem * +ListWidget_GetNextItem(ListWidget *self, + ListWidgetItem *baseItem); + +ListWidgetItem * +ListWidget_GetPreviousItem(ListWidget *self, + ListWidgetItem *baseItem); + +ListWidgetItem * +ListWidget_GetNextCategoryItem(ListWidget *self, + ListWidgetCategory *category, + ListWidgetItem *baseItem); + +ListWidgetItem * +ListWidget_GetPreviousCategoryItem(ListWidget *self, + ListWidgetCategory *category, + ListWidgetItem *baseItem); + +ListWidgetItem * +ListWidget_GetNextGroupItem(ListWidget *self, + ListWidgetGroup *group, + ListWidgetItem *baseItem); + +ListWidgetItem * +ListWidget_GetPreviousGroupItem(ListWidget *self, + ListWidgetGroup *group, + ListWidgetItem *baseItem); + +ListWidgetItem * +ListWidget_GetNextLineItem(ListWidget *self, + ListWidgetItem *baseItem); + +ListWidgetItem * +ListWidget_GetPreviousLineItem(ListWidget *self, + ListWidgetItem *baseItem); + +ListWidgetItem * +ListWidget_GetNextPageItem(ListWidget *self, + HWND hwnd, + ListWidgetItem *baseItem); + +ListWidgetItem * +ListWidget_GetPreviousPageItem(ListWidget *self, + HWND hwnd, + ListWidgetItem *baseItem); + +BOOL +ListWidget_EnsureItemVisisble(ListWidget *self, + HWND hwnd, + ListWidgetItem *item, + ListWidgetVisibleFlags flags); + +HBITMAP +ListWidget_GetItemImage(ListWidget *self, + WidgetStyle *style, + ListWidgetItem *item); + +BOOL +ListWidget_GetItemMetrics(WidgetStyle *style, + ListWidgetItemMetric *metrics); + +ListWidgetItem * +ListWidget_GetItemFromPointEx(ListWidget *self, + POINT point, + ListWidgetCategory **categoryOut, // optional + ListWidgetGroup **groupOut); // optional + +ListWidgetItem * +ListWidget_GetItemFromPoint(ListWidget *self, + POINT point); + + + +BOOL +ListWidget_AddItem(ListWidgetGroup *group, + ListWidgetItem *item); + +ListWidgetItem * +ListWidget_FindGroupItem(ListWidgetGroup *group, + const char *name); + +ListWidgetItem * +ListWidget_FindGroupItemEx(ListWidgetGroup *group, + const char *name, + size_t max); + +ListWidgetGroup * +ListWidget_GetItemOwner(ListWidget *self, + ListWidgetItem *baseItem, + ListWidgetCategory **categoryOut); + + +ListWidgetItem * +ListWidget_FindItem(ListWidget *self, + const char *name, + ListWidgetCategory **categoryOut, + ListWidgetGroup **groupOut); + +BOOL +ListWidget_FindItemPos(ListWidget *self, + ListWidgetItem *item, + size_t *categoryOut, + size_t *groupOut, + size_t *itemOut); + + +BOOL +ListWidget_SetItemTitle(ListWidgetItem *item, + const wchar_t *title); + +BOOL +ListWidget_DisplayItemContextMenu(ListWidget *self, + HWND hostWindow, + ListWidgetItem *item, + POINT pt); + +size_t +ListWidget_GetItemCommands(ListWidgetItem *item, + ListWidgetCommand **buffer, + size_t bufferMax); + +BOOL +ListWidget_SendItemCommand(const char *name, + const char *command, + HWND hostWindow, + ULONG_PTR param, + BOOL enableIntercept); + +BOOL +ListWidget_CreateItemActivity(ListWidgetItem *item); + +BOOL +ListWidget_DeleteItemActivity(ListWidgetItem *item); + + + +ListWidtetActivityChange +ListWidget_UpdateItemActivity(ListWidgetItem *item, + ifc_deviceactivity *activity); + +BOOL +ListWidget_InvalidateItemImage(ListWidget *self, + HWND hwnd, + ListWidgetItem *item); + +BOOL +ListWidget_InvalidateItemActivity(ListWidget *self, + HWND hwnd, + ListWidgetItem *item, + ListWidgetActivityChange changes); + + +BOOL +ListWidget_GetItemFrameRect(ListWidget *self, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + RECT *rect); + +BOOL +ListWidget_GetItemImageRect(ListWidget *self, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + RECT *rect); + +BOOL +ListWidget_GetItemActivityRect(ListWidget *self, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + RECT *rect); + +BOOL +ListWidget_GetItemActivityProgressRect(ListWidget *self, + HDC hdc, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + RECT *rect); + +BOOL +ListWidget_GetItemActivityPercentRect(ListWidget *self, + HDC hdc, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + RECT *rect); + +BOOL +ListWidget_GetItemActivityTitleRect(ListWidget *self, + HDC hdc, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + RECT *rect); + +BOOL +ListWidget_GetItemSpacebarRect(ListWidget *self, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + RECT *rect); + +BOOL +ListWidget_GetItemTitleRect(ListWidget *self, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + BOOL exactSize, + RECT *rect); + +BOOL +ListWidget_GetItemConnectionRect(ListWidget *self, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + RECT *rect); + +ListWidgetItemPart +ListWidget_GetItemPartFromPoint(ListWidget *self, + ListWidgetItem *item, + ListWidgetItemMetric *metrics, + POINT pt, + ListWidgetItemPart mask, + RECT *partRect); + +BOOL +ListWidget_FormatItemTip(ListWidget *self, + ListWidgetItem *item, + wchar_t *buffer, + size_t bufferMax); + +BOOL +ListWidget_FormatItemTitleTip(ListWidget *self, + ListWidgetItem *item, + wchar_t *buffer, + size_t bufferMax); + +BOOL +ListWidget_FormatItemCommandTip(ListWidget *self, + ListWidgetItem *item, + const RECT *commandRect, + wchar_t *buffer, + size_t bufferMax); + +BOOL +ListWidget_FormatItemSpaceTip(ListWidget *self, + ListWidgetItem *item, + wchar_t *buffer, + size_t bufferMax); + +BOOL +ListWidget_FormatItemStatus(ListWidget *self, + ListWidgetItem *item, + wchar_t *buffer, + size_t bufferMax); + +BOOL +ListWidget_FormatItemSpaceStatus(ListWidget *self, + ListWidgetItem *item, + wchar_t *buffer, + size_t bufferMax); +HWND +ListWidget_BeginItemTitleEdit(ListWidget *self, + HWND hwnd, + ListWidgetItem *item); + +int +ListWidget_CompareItemPos(ListWidget *self, + ListWidgetItem *item1, + ListWidgetItem *item2); + +BOOL +ListWidget_GetViewItemPos(HWND hwnd, + ListWidgetItem *item, + POINT *pt); + +/* +<<<<<<<<<<<<<<<<<<<<<<<<< Connection >>>>>>>>>>>>>>>>>>>>>>>>> +*/ + +ListWidgetConnection * +ListWidget_CreateConnection(const char *name); + +void +ListWidget_DestroyConnection(ListWidgetConnection *connection); + + +HBITMAP +ListWidget_GetConnectionImage(WidgetStyle *style, + ListWidgetConnection *connection, + int width, + int height); + +BOOL +ListWidget_ConnectionResetColors(WidgetStyle *style, + ListWidgetConnection *connection); + +void +ListWidget_ResetConnnectionsColors(ListWidget *self, + WidgetStyle *style); + +ListWidgetConnection * +ListWidget_FindConnection(ListWidget *self, + const char *name); + +BOOL +ListWidget_AddConnection(ListWidget *self, + ListWidgetConnection *connection); + +void +ListWidget_RemoveConnection(ListWidget *self, + const char *name); + +void +ListWidget_RemoveAllConnections(ListWidget *self); + + +BOOL +ListWidget_UpdateConnectionImageSize(ListWidgetConnection *connection, + int width, + int height); + +/* +<<<<<<<<<<<<<<<<<<<<<<<<< Command >>>>>>>>>>>>>>>>>>>>>>>>> +*/ + +ListWidgetCommand * +ListWidget_CreateCommand(const char *name, + BOOL primary, + BOOL disabled); + +void +ListWidget_DestroyCommand(ListWidgetCommand *command); + +size_t +ListWigdet_GetDeviceCommands(ListWidgetCommand **buffer, + size_t bufferMax, + ifc_device *device); + +void +ListWidget_DestroyAllCommands(ListWidgetCommand** buffer, + size_t bufferMax); + +const wchar_t * +ListWidget_GetCommandTitle(ListWidgetCommand *command); + +const wchar_t * +ListWidget_GetCommandDescription(ListWidgetCommand *command); + +HBITMAP +ListWidget_GetCommandLargeBitmap(WidgetStyle *style, + ListWidgetCommand *command, + int width, + int height); + +HBITMAP +ListWidget_GetCommandSmallBitmap(WidgetStyle *style, + ListWidgetCommand *command, + int width, + int height); + +BOOL +ListWidget_ResetCommandImages(ListWidgetCommand *command); + +BOOL +ListWidget_GetCommandRect(ListWidgetCommand *command, + RECT *rect); + +BOOL +ListWidget_SetCommandRect(ListWidgetCommand *command, + const RECT *rect); + +BOOL +ListWidget_GetCommandRectEqual(ListWidgetCommand *command, + const RECT *rect); + +BOOL +ListWidget_GetCommandPrimary(ListWidgetCommand *command); + +BOOL +ListWidget_GetCommandDisabled(ListWidgetCommand *command); + + +BOOL +ListWidget_EnableCommand(ListWidgetCommand *command, + BOOL enable); + +BOOL +ListWidget_GetCommandPressed(ListWidgetCommand *command); + + +BOOL +ListWidget_SetCommandPressed(ListWidgetCommand *command, + BOOL pressed); + +const char * +ListWidget_GetCommandName(ListWidgetCommand *command); + +/* +<<<<<<<<<<<<<<<<<<<<<<<<< Paint >>>>>>>>>>>>>>>>>>>>>>>>> +*/ + +typedef struct ListWidgetPaintSpacebar +{ + HBITMAP bitmap; + long width; + long height; + long emptyBarOffset; + long filledBarOffset; +} ListWidgetPaintSpacebar; + +typedef struct ListWidgetPaintArrow +{ + HBITMAP bitmap; + long width; + long height; + long collapsedOffset; + long expandedOffset; +} ListWidgetPaintArrow; + + +typedef struct ListWidgetPaint +{ + ListWidget *widget; + WidgetStyle *style; + HWND hwnd; + HDC hdc; + BOOL erase; + const RECT *paintRect; + HDC sourceDC; + ListWidgetPaintSpacebar spacebar; + ListWidgetItemMetric itemMetrics; + ListWidgetCategoryMetric categoryMetrics; + ListWidgetPaintArrow arrow; + RECT partRect; + BOOL focused; +} ListWidgetPaint; + +BOOL +ListWidgetPaint_Initialize(ListWidgetPaint *self, + ListWidget *widget, + WidgetStyle *style, + HWND hwnd, + HDC hdc, + const RECT *paintRect, + BOOL erase); + +void +ListWidgetPaint_Uninitialize(ListWidgetPaint *self); + +BOOL +ListWidgetPaint_DrawItem(ListWidgetPaint *self, + ListWidgetItem *item); + +BOOL +ListWidgetPaint_DrawCategory(ListWidgetPaint *self, + ListWidgetCategory *category); + +BOOL +ListWidgetPaint_DrawEmptyCategoryText(ListWidgetPaint *self, + ListWidgetCategory *category); + + +/* +<<<<<<<<<<<<<<<<<<<<<<<<< Tooltip >>>>>>>>>>>>>>>>>>>>>>>>> +*/ + +ListWidgetTooltip* +ListWidget_TooltipCreate(HWND hwnd); + +void +ListWidget_TooltipDestroy(ListWidgetTooltip *tooltip); + +void +ListWidget_TooltipFontChanged(ListWidgetTooltip *tooltip); + +BOOL +ListWidget_TooltipActivate(ListWidgetTooltip *tooltip, + const RECT *rect); + +BOOL +ListWidget_TooltipUpdate(ListWidgetTooltip *tooltip, + ListWidgetItem *item, + ListWidgetItemPart part, + const RECT *partRect); + +void +ListWidget_TooltipHide(ListWidgetTooltip *tooltip); + +void +ListWidget_TooltipRelayMouseMessage(ListWidgetTooltip *tooltip, + unsigned int message, + unsigned int vKeys, + const POINT *cursor); +BOOL +ListWidget_TooltipProcessNotification(ListWidget *self, + ListWidgetTooltip *tooltip, + NMHDR *pnmh, + LRESULT *result); + +ListWidgetItem * +ListWidget_TooltipGetCurrent(ListWidgetTooltip *tooltip, + ListWidgetItemPart *part, + RECT *partRect); + +BOOL +ListWidget_TooltipGetChanged(ListWidgetTooltip *tooltip, + ListWidgetItem *item, + ListWidgetItemPart part, + const RECT *partRect); + +typedef enum TooltipUpdateReason +{ + Tooltip_DeviceTitleChanged = 1, + Tooltip_DeviceSpaceChanged = 2, + Tooltip_DeviceActivityChanged = 3, + Tooltip_DeviceModelChanged = 4, + Tooltip_DeviceStatusChanged = 5, +} TooltipUpdateReason; + +BOOL +ListWidget_TooltipUpdateText(ListWidget *self, + ListWidgetTooltip *tooltip, + ListWidgetItem *item, + TooltipUpdateReason reason); + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_LIST_WIDGET_INTERNAL_HEADER + diff --git a/Src/Plugins/Library/ml_devices/listWidgetItem.cpp b/Src/Plugins/Library/ml_devices/listWidgetItem.cpp new file mode 100644 index 00000000..d10ad9c6 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidgetItem.cpp @@ -0,0 +1,2854 @@ +#include "main.h" +#include "./listWidgetInternal.h" +#include "../nu/AutoWide.h" +#include <strsafe.h> + +#define LISTWIDGETITEM_OFFSET_LEFT_DLU 0 +#define LISTWIDGETITEM_OFFSET_TOP_DLU 0 +#define LISTWIDGETITEM_OFFSET_RIGHT_DLU 0 +#define LISTWIDGETITEM_OFFSET_BOTTOM_DLU 2 +#define LISTWIDGETITEM_IMAGE_OFFSET_LEFT_DLU 2 +#define LISTWIDGETITEM_IMAGE_OFFSET_TOP_DLU 2 +#define LISTWIDGETITEM_IMAGE_OFFSET_RIGHT_DLU 2 +#define LISTWIDGETITEM_IMAGE_OFFSET_BOTTOM_DLU 2 +#define LISTWIDGETITEM_SPACEBAR_OFFSET_DLU 1 +#define LISTWIDGETITEM_SPACEBAR_HEIGHT_DLU 8 +#define LISTWIDGETITEM_TITLE_OFFSET_DLU 1 +#define LISTWIDGETITEM_TITLE_MIN_WIDTH_DLU (8 * 4) +#define LISTWIDGETITEM_TITLE_EDITOR_MARGIN_HORZ_DLU 2 + +ListWidgetItem* +ListWidget_CreateItemFromDevice(ListWidget *self, ifc_device* device) +{ + ListWidgetItem *item; + ifc_deviceactivity *activity; + wchar_t buffer[1024] = {0}; + + if (NULL == device || NULL == device->GetName()) + return NULL; + + item = new ListWidgetItem(); + if (NULL == item) + return NULL; + + item->name = AnsiString_Duplicate(device->GetName()); + if (NULL == item->name) + { + delete item; + return NULL; + } + + if (SUCCEEDED(device->GetDisplayName(buffer, ARRAYSIZE(buffer)))) + item->title = String_Duplicate(buffer); + else + item->title = NULL; + + ListWidgetItem_UnsetTextTruncated(item); + SetSize(&item->titleSize, -1, -1); + + item->image = NULL; + + if (FAILED(device->GetTotalSpace(&item->spaceTotal))) + item->spaceTotal = 0; + + if (FAILED(device->GetUsedSpace(&item->spaceUsed))) + item->spaceUsed = 0; + + item->connection = NULL; + if (NULL != self) + { + item->connection = ListWidget_FindConnection(self, device->GetConnection()); + if (NULL == item->connection) + { + item->connection = ListWidget_CreateConnection(device->GetConnection()); + if (NULL != item->connection) + ListWidget_AddConnection(self, item->connection); + } + } + + + + item->activity = NULL; + if (S_OK == device->GetActivity(&activity) && NULL != activity) + { + if (FALSE != activity->GetActive()) + { + ListWidget_CreateItemActivity(item); + ListWidget_UpdateItemActivity(item, activity); + } + + activity->Release(); + } + return item; +} + +void +ListWidget_DestroyItem(ListWidgetItem *item) +{ + if (NULL == item) + return; + + if (NULL != item->image) + DeviceImage_Release(item->image); + + ListWidget_DeleteItemActivity(item); + + AnsiString_Free(item->name); + String_Free(item->title); + + + + delete item; +} + +BOOL +ListWidget_SetItemTitle(ListWidgetItem *item, const wchar_t *title) +{ + if (NULL == item) + return FALSE; + + String_Free(item->title); + SetSize(&item->titleSize, -1, -1); + ListWidgetItem_UnsetTextTruncated(item); + + item->title = String_Duplicate(title); + + if (NULL != title && NULL == item->title) + return FALSE; + + return TRUE; + +} + +size_t +ListWidget_RemoveItem(ListWidget *self, HWND hwnd, const char *name) +{ + size_t iCategory, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + size_t removed; + RECT rect; + POINT origin; + ListWidgetItem *selectItem; + + removed = 0; + selectItem = NULL; + + if (NULL == self || NULL == name) + return 0; + + if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) + ZeroMemory(&origin, sizeof(POINT)); + + for (iCategory = 0; iCategory < self->categories.size(); iCategory++) + { + category = self->categories[iCategory]; + BOOL categoryModified = FALSE; + + size_t iGroup = category->groups.size(); + while(iGroup--) + { + group = category->groups[iGroup]; + iItem = group->items.size(); + while(iItem--) + { + item = group->items[iItem]; + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, item->name, -1, name, -1)) + { + removed++; + categoryModified = TRUE; + + if (NULL != item->activity) + ListWidget_UnregisterActiveItem(self, hwnd, item); + + if (self->selectedItem == item) + { + self->selectedItem = NULL; + + if (NULL != self->activeMenu) + EndMenu(); + + ListWidget_UpdateSelectionStatus(self, hwnd, FALSE); + + selectItem = ListWidget_GetNextGroupItem(self, group, item); + if (NULL == selectItem) + { + selectItem = ListWidget_GetPreviousGroupItem(self, group, item); + if (NULL == selectItem) + { + selectItem = ListWidget_GetNextCategoryItem(self, category, item); + if (NULL == selectItem) + selectItem = ListWidget_GetPreviousCategoryItem(self, category, item); + } + } + } + + if (self->hoveredItem == item) + self->hoveredItem = NULL; + + group->items.erase(group->items.begin() + iItem); + ListWidget_DestroyItem(item); + + if (0 == group->items.size()) + { + category->groups.erase(category->groups.begin() + iGroup); + ListWidget_DestroyGroup(group); + break; + } + } + } + } + + if (FALSE != categoryModified) + { + ListWidget_ResetCategoryCounter(category); + + if (FALSE == category->collapsed) + { + ListWidget_UpdateLayout(hwnd, ListWidgetLayout_UpdateNow | ListWidgetLayout_KeepStable); + } + else + { + CopyRect(&rect, &category->rect); + OffsetRect(&rect, origin.x, origin.y); + InvalidateRect(hwnd, &rect, FALSE); + } + } + } + + if (0 != removed && NULL != selectItem) + ListWidget_SelectItem(self, hwnd, selectItem, FALSE); + + return removed; +} + +BOOL +ListWidget_GetItemMetrics(WidgetStyle *style, ListWidgetItemMetric *metrics) +{ + if (NULL == metrics || NULL == style) + return FALSE; + + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(metrics->offsetLeft, style, LISTWIDGETITEM_OFFSET_LEFT_DLU, 1); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->offsetTop, style, LISTWIDGETITEM_OFFSET_TOP_DLU, 1); + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(metrics->offsetRight, style, LISTWIDGETITEM_OFFSET_RIGHT_DLU, 1); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->offsetBottom, style, LISTWIDGETITEM_OFFSET_BOTTOM_DLU, 1); + + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(metrics->imageOffsetLeft, style, LISTWIDGETITEM_IMAGE_OFFSET_LEFT_DLU, 2); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->imageOffsetTop, style, LISTWIDGETITEM_IMAGE_OFFSET_TOP_DLU, 2); + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(metrics->imageOffsetRight, style, LISTWIDGETITEM_IMAGE_OFFSET_RIGHT_DLU, 2); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->imageOffsetBottom, style, LISTWIDGETITEM_IMAGE_OFFSET_BOTTOM_DLU, 2); + + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->spacebarOffsetTop, style, LISTWIDGETITEM_SPACEBAR_OFFSET_DLU, 1); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->spacebarHeight, style, LISTWIDGETITEM_SPACEBAR_HEIGHT_DLU, 2); + metrics->spacebarHeight = 14; + + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(metrics->titleOffsetTop, style, LISTWIDGETITEM_TITLE_OFFSET_DLU, 1); + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(metrics->titleMinWidth, style, LISTWIDGETITEM_TITLE_MIN_WIDTH_DLU, 32); + + return TRUE; +} + +static HBITMAP +ListWidget_GetDeviceBitmap(ifc_device *device, int width, int height, + DeviceImageFlags flags, DeviceImage **imageOut) +{ + HBITMAP bitmap; + wchar_t path[MAX_PATH*2] = {0}; + const wchar_t *defaultImage; + + DeviceImage *image; + DeviceImageCache *imageCache; + ifc_devicetype *type; + + if (NULL == device) + return NULL; + + imageCache = Plugin_GetImageCache(); + if (NULL == imageCache) + return NULL; + + if (SUCCEEDED(device->GetIcon(path, ARRAYSIZE(path), width, height))) + { + image = DeviceImageCache_GetImage(imageCache, path, width, height, NULL, NULL); + if (NULL != image) + { + bitmap = DeviceImage_GetBitmap(image, flags); + if (NULL != bitmap) + { + if (NULL != imageOut) + *imageOut = image; + else + DeviceImage_Release(image); + + return bitmap; + } + } + } + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->TypeFind(device->GetType(), &type)) + { + if (SUCCEEDED(type->GetIcon(path, ARRAYSIZE(path), width, height))) + { + image = DeviceImageCache_GetImage(imageCache, path, width, height, NULL, NULL); + if (NULL != image) + { + bitmap = DeviceImage_GetBitmap(image, flags); + if (NULL != bitmap) + { + if (NULL != imageOut) + *imageOut = image; + else + DeviceImage_Release(image); + + type->Release(); + return bitmap; + } + } + } + type->Release(); + } + + defaultImage = Plugin_GetDefaultDeviceImage(width, height); + if (NULL != defaultImage) + { + image = DeviceImageCache_GetImage(imageCache, defaultImage, width, height, NULL, NULL); + if (NULL != image) + { + bitmap = DeviceImage_GetBitmap(image, flags); + if (NULL != bitmap) + { + if (NULL != imageOut) + *imageOut = image; + else + DeviceImage_Release(image); + return bitmap; + } + } + } + + return NULL; +} + + +HBITMAP +ListWidget_GetItemImage(ListWidget *self, WidgetStyle *style, ListWidgetItem *item) +{ + HBITMAP bitmap; + + if (NULL == item) + return NULL; + + if (NULL != item->image) + return DeviceImage_GetBitmap(item->image, DeviceImage_Normal); + + if (NULL == self || NULL == style) + return NULL; + + ifc_device *device; + if (NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(item->name, &device)) + { + return NULL; + } + + + bitmap = ListWidget_GetDeviceBitmap(device, self->imageSize.cx, self->imageSize.cy, + DeviceImage_Normal, &item->image); + + device->Release(); + + return bitmap; + +} + +BOOL +ListWidget_CalculateItemBaseSize(ListWidget *self, WidgetStyle *style, SIZE *baseSize, long *itemTextWidth) +{ + ListWidgetItemMetric metrics; + + if (NULL == baseSize) + return FALSE; + + if (FALSE == ListWidget_GetItemMetrics(style, &metrics)) + ZeroMemory(&metrics, sizeof(metrics)); + + baseSize->cx = self->imageSize.cx; + baseSize->cy = self->imageSize.cy; + + baseSize->cx += metrics.imageOffsetLeft + metrics.imageOffsetRight; + if (baseSize->cx < metrics.titleMinWidth) + baseSize->cx = metrics.titleMinWidth; + + if (FALSE != itemTextWidth) + *itemTextWidth = baseSize->cx; + + baseSize->cx += metrics.offsetLeft + metrics.offsetRight; + + baseSize->cy += metrics.offsetTop + metrics.offsetBottom + + metrics.imageOffsetTop + metrics.imageOffsetBottom + + metrics.spacebarHeight + metrics.spacebarOffsetTop + + metrics.titleOffsetTop; + + if (FALSE != itemTextWidth) + *itemTextWidth = baseSize->cx - (metrics.offsetLeft + metrics.offsetRight); + + return TRUE; +} + + + +ListWidgetItem * +ListWidget_GetItemFromPointEx(ListWidget *self, POINT point, + ListWidgetCategory **categoryOut, ListWidgetGroup **groupOut) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + + if (NULL == self) + return NULL; + + 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]; + for(iItem = 0; iItem < group->items.size(); iItem++) + { + item = group->items[iItem]; + + if (FALSE != PtInRect(&item->rect, point)) + { + if (NULL != categoryOut) + *categoryOut = category; + + if (NULL != groupOut) + *groupOut = group; + + return item; + } + } + } + } + } + + if (NULL != categoryOut) + *categoryOut = NULL; + + if (NULL != groupOut) + *groupOut = NULL; + + return NULL; +} + +ListWidgetItem * +ListWidget_GetItemFromPoint(ListWidget *self, POINT point) +{ + return ListWidget_GetItemFromPointEx(self, point, NULL, NULL); +} + +ListWidgetItem * +ListWidget_GetFirstItem(ListWidget *self) +{ + size_t iCategory, iGroup; + ListWidgetCategory *category; + ListWidgetGroup *group; + + if (NULL == self) + return NULL; + + 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 (group->items.size() > 0) + return group->items[0]; + } + } + } + return NULL; +} + +ListWidgetItem * +ListWidget_GetLastItem(ListWidget *self) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + + if (NULL == self) + return NULL; + + iCategory = self->categories.size(); + while(iCategory--) + { + category = self->categories[iCategory]; + if (FALSE == category->collapsed) + { + iGroup = category->groups.size(); + while(iGroup--) + { + group = category->groups[iGroup]; + iItem = group->items.size(); + if (iItem > 0) + return group->items[iItem - 1]; + } + } + } + return NULL; +} + +ListWidgetItem * +ListWidget_GetNextItem(ListWidget *self, ListWidgetItem *baseItem) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + BOOL returnNext; + + if (NULL == self || NULL == baseItem) + return NULL; + + returnNext = FALSE; + 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]; + for(iItem = 0; iItem < group->items.size(); iItem++) + { + item = group->items[iItem]; + if (item == baseItem) + returnNext = TRUE; + else if (FALSE != returnNext) + return item; + } + } + } + } + return NULL; +} + +ListWidgetItem * +ListWidget_GetPreviousItem(ListWidget *self, ListWidgetItem *baseItem) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + BOOL returnPrevious; + + if (NULL == self || NULL == baseItem) + return NULL; + + returnPrevious = FALSE; + iCategory = self->categories.size(); + while(iCategory--) + { + category = self->categories[iCategory]; + if (FALSE == category->collapsed) + { + iGroup = category->groups.size(); + while(iGroup--) + { + group = category->groups[iGroup]; + iItem = group->items.size(); + while(iItem--) + { + item = group->items[iItem]; + if (item == baseItem) + returnPrevious = TRUE; + else if (FALSE != returnPrevious) + return item; + } + } + } + } + return NULL; +} + +ListWidgetItem * +ListWidget_GetNextCategoryItem(ListWidget *self, ListWidgetCategory *category, ListWidgetItem *baseItem) +{ + size_t iGroup, iItem; + ListWidgetGroup *group; + ListWidgetItem *item; + BOOL returnNext; + + if (NULL == self || NULL == baseItem || + NULL == category || FALSE != category->collapsed) + { + return NULL; + } + + returnNext = FALSE; + + 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 (item == baseItem) + returnNext = TRUE; + else if (FALSE != returnNext) + return item; + } + } + + return NULL; +} + +ListWidgetItem * +ListWidget_GetPreviousCategoryItem(ListWidget *self, ListWidgetCategory *category, ListWidgetItem *baseItem) +{ + if (NULL == self || NULL == baseItem || + NULL == category || FALSE != category->collapsed) + { + return NULL; + } + + BOOL returnPrevious = FALSE; + size_t iGroup = category->groups.size(); + while(iGroup--) + { + ListWidgetGroup *group = category->groups[iGroup]; + size_t iItem = group->items.size(); + while(iItem--) + { + ListWidgetItem *item = group->items[iItem]; + if (item == baseItem) + returnPrevious = TRUE; + else if (FALSE != returnPrevious) + return item; + } + } + + return NULL; +} + +ListWidgetItem * +ListWidget_GetNextGroupItem(ListWidget *self, ListWidgetGroup *group, ListWidgetItem *baseItem) +{ + if (NULL == self || NULL == baseItem || NULL == group) + return NULL; + + BOOL returnNext = FALSE; + for(size_t iItem = 0; iItem < group->items.size(); iItem++) + { + ListWidgetItem *item = group->items[iItem]; + if (item == baseItem) + returnNext = TRUE; + else if (FALSE != returnNext) + return item; + } + + return NULL; +} + +ListWidgetItem * +ListWidget_GetPreviousGroupItem(ListWidget *self, ListWidgetGroup *group, ListWidgetItem *baseItem) +{ + if (NULL == self || NULL == baseItem || NULL == group) + return NULL; + + BOOL returnPrevious = FALSE; + size_t iItem = group->items.size(); + while(iItem--) + { + ListWidgetItem *item = group->items[iItem]; + if (item == baseItem) + returnPrevious = TRUE; + else if (FALSE != returnPrevious) + return item; + } + + return NULL; +} + +ListWidgetItem * +ListWidget_GetNextLineItem(ListWidget *self, ListWidgetItem *baseItem) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + size_t itemLinePos, itemsPerLine, targetLinePos; + + if (NULL == self || NULL == baseItem) + return NULL; + + itemsPerLine = MAX(self->itemsPerLine, 1); + targetLinePos = -1; + + 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 (-1 == targetLinePos) + { + itemLinePos = 0; + for(iItem = 0; iItem < group->items.size(); iItem++, itemLinePos++) + { + if (itemLinePos == itemsPerLine) + itemLinePos = 0; + + item = group->items[iItem]; + if (item == baseItem) + { + size_t test; + targetLinePos = itemLinePos; + test = iItem + (itemsPerLine - itemLinePos); + if (test < group->items.size()) + { + test += targetLinePos; + if (test >= group->items.size()) + test = group->items.size() - 1; + return group->items[test]; + } + break; + } + } + } + else if (group->items.size() > 0) + { + size_t test; + + if (targetLinePos < group->items.size()) + test = targetLinePos; + else + test = group->items.size() - 1; + return group->items[test]; + } + + } + } + } + + return NULL; +} + +ListWidgetItem * +ListWidget_GetPreviousLineItem(ListWidget *self, ListWidgetItem *baseItem) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + size_t itemLinePos, itemsPerLine, targetLinePos; + + if (NULL == self || NULL == baseItem) + return NULL; + + itemsPerLine = MAX(self->itemsPerLine, 1); + targetLinePos = -1; + + iCategory = self->categories.size(); + while(iCategory--) + { + category = self->categories[iCategory]; + + if (FALSE == category->collapsed) + { + iGroup = category->groups.size(); + while(iGroup--) + { + group = category->groups[iGroup]; + if (-1 == targetLinePos) + { + itemLinePos = 0; + for(iItem = 0; iItem < group->items.size(); iItem++, itemLinePos++) + { + if (itemLinePos == itemsPerLine) + itemLinePos = 0; + + item = group->items[iItem]; + if (item == baseItem) + { + targetLinePos = itemLinePos; + if (iItem >= (itemLinePos + 1)) + { + size_t test = iItem - (itemLinePos + 1); + if (test >= (itemsPerLine - (itemLinePos + 1))) + test -= itemsPerLine - (itemLinePos + 1); + return group->items[test]; + } + break; + } + } + } + else if (group->items.size() > 0) + { + size_t test = group->items.size(); + test = test/itemsPerLine + ((0 != test%itemsPerLine) ? 0 : - 1); + test = test * itemsPerLine; + test += targetLinePos; + if (test >= group->items.size()) + test = group->items.size() - 1; + return group->items[test]; + } + + } + } + } + + return NULL; +} + +static ListWidgetItem * +ListWidget_FindLastVisibleLine(ListWidget *self, long viewBottom) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + ListWidgetItem *lineItem; + + lineItem = NULL; + + 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]; + for(iItem = 0; iItem < group->items.size(); iItem++) + { + item = group->items[iItem]; + if (item->rect.top < viewBottom) + { + if (NULL == lineItem || + item->rect.top != lineItem->rect.top) + { + if (item->rect.bottom <= viewBottom) + lineItem = item; + else + return (NULL != lineItem) ? lineItem : item; + } + } + else + return lineItem; + } + } + } + } + + return lineItem; +} + +static ListWidgetItem * +ListWidget_FindFirstVisibleLine(ListWidget *self, long viewTop) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + ListWidgetItem *lineItem; + + lineItem = NULL; + + 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]; + for(iItem = 0; iItem < group->items.size(); iItem++) + { + item = group->items[iItem]; + if (NULL == lineItem || + item->rect.top != lineItem->rect.top) + { + lineItem = item; + if (item->rect.top >= viewTop) + return lineItem; + } + } + } + } + } + + return lineItem; +} + +static ListWidgetItem * +ListWidget_FindNextLine(ListWidget *self, ListWidgetItem *baseItem) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + BOOL foundItem; + + foundItem = FALSE; + + 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]; + for(iItem = 0; iItem < group->items.size(); iItem++) + { + item = group->items[iItem]; + if (item == baseItem) + foundItem = TRUE; + if (FALSE != foundItem && + item->rect.top != baseItem->rect.top) + { + return item; + } + } + } + } + } + return NULL; +} + +static ListWidgetItem * +ListWidget_FindPreviousLine(ListWidget *self, ListWidgetItem *baseItem) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + BOOL foundItem; + + foundItem = FALSE; + + iCategory = self->categories.size(); + while(iCategory--) + { + category = self->categories[iCategory]; + + if (FALSE == category->collapsed) + { + iGroup = category->groups.size(); + while(iGroup--) + { + group = category->groups[iGroup]; + iItem = group->items.size(); + while(iItem--) + { + item = group->items[iItem]; + if (item == baseItem) + foundItem = TRUE; + if (FALSE != foundItem && + item->rect.top != baseItem->rect.top) + { + return item; + } + } + } + } + } + + return NULL; +} + +static ListWidgetItem * +ListWidget_FindLineItemAtPos(ListWidget *self, ListWidgetItem *beginLine, ListWidgetItem *linePosition) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + BOOL foundLine; + + foundLine = FALSE; + + 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]; + for(iItem = 0; iItem < group->items.size(); iItem++) + { + item = group->items[iItem]; + if (item == beginLine) + foundLine = TRUE; + + if (FALSE != foundLine) + { + if (beginLine->rect.top == linePosition->rect.top) + { + if (item->rect.top != beginLine->rect.top) + { + return (iItem > 0) ? group->items[iItem - 1] : beginLine; + } + } + else + { + if (item->rect.left == linePosition->rect.left) + return item; + } + } + } + if (FALSE != foundLine) + { + if (group->items.size() > 0) + return group->items[group->items.size() - 1]; + return NULL; + } + } + } + } + + return NULL; +} + +ListWidgetItem * +ListWidget_GetNextPageItem(ListWidget *self, HWND hwnd, ListWidgetItem *baseItem) +{ + ListWidgetItem *lineItem; + RECT rect; + POINT origin; + long viewBottom; + + if (NULL == self || NULL == baseItem) + return NULL; + + if (FALSE == GetClientRect(hwnd, &rect)) + return NULL; + + if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) + OffsetRect(&rect, -origin.x, -origin.y); + + if (baseItem->rect.bottom < rect.top) + viewBottom = baseItem->rect.top + RECTHEIGHT(rect); + else + viewBottom = rect.bottom; + + lineItem = ListWidget_FindLastVisibleLine(self, viewBottom); + if (NULL == lineItem) + return NULL; + + if (lineItem->rect.top <= baseItem->rect.top) + { + viewBottom = baseItem->rect.top + RECTHEIGHT(rect); + lineItem = ListWidget_FindLastVisibleLine(self, viewBottom); + if (NULL == lineItem) + return NULL; + if (lineItem->rect.top <= baseItem->rect.top) + { + lineItem = ListWidget_FindNextLine(self, baseItem); + if (NULL == lineItem) + return NULL; + } + } + + return ListWidget_FindLineItemAtPos(self, lineItem, baseItem); +} + +ListWidgetItem * +ListWidget_GetPreviousPageItem(ListWidget *self, HWND hwnd, ListWidgetItem *baseItem) +{ + ListWidgetItem *lineItem; + RECT rect; + POINT origin; + long viewTop; + + if (NULL == self || NULL == baseItem) + return NULL; + + if (FALSE == GetClientRect(hwnd, &rect)) + return NULL; + + if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) + OffsetRect(&rect, -origin.x, -origin.y); + + if (baseItem->rect.top > rect.bottom) + viewTop = baseItem->rect.bottom - RECTHEIGHT(rect); + else + viewTop = rect.top; + + lineItem = ListWidget_FindFirstVisibleLine(self, viewTop); + if (NULL == lineItem) + return NULL; + + if (lineItem->rect.top >= baseItem->rect.top) + { + viewTop = baseItem->rect.bottom - RECTHEIGHT(rect); + lineItem = ListWidget_FindFirstVisibleLine(self, viewTop); + if (NULL == lineItem) + return NULL; + if (lineItem->rect.top >= baseItem->rect.top) + { + lineItem = ListWidget_FindPreviousLine(self, baseItem); + if (NULL == lineItem) + return NULL; + } + } + + return ListWidget_FindLineItemAtPos(self, lineItem, baseItem); +} + +BOOL +ListWidget_EnsureItemVisisble(ListWidget *self, HWND hwnd, ListWidgetItem *item, ListWidgetVisibleFlags flags) +{ + RECT rect; + POINT pt; + int dx, dy; + + if (NULL == self || NULL == item || NULL == hwnd) + return FALSE; + + if (FALSE == GetClientRect(hwnd, &rect)) + return FALSE; + + if (FALSE != ListWidget_GetViewOrigin(hwnd, &pt)) + OffsetRect(&rect, -pt.x, -pt.y); + + if (0 == (VISIBLE_ALIGN_ALWAYS & flags)) + { + if (item->rect.left >= rect.left && + item->rect.right <= rect.right && + item->rect.top >= rect.top && + item->rect.bottom <= rect.bottom) + { + return FALSE; + } + } + + if (0 != (VISIBLE_PARTIAL_OK & flags)) + { + if (item->rect.left < rect.right && + item->rect.right > rect.left && + item->rect.top < rect.bottom && + item->rect.bottom > rect.top) + { + return FALSE; + } + } + + if (item->rect.right > rect.right) + dx = item->rect.right - rect.right; + else + dx = 0; + + if ((item->rect.left - dx) < rect.left) + dx = item->rect.left - rect.left; + + dy = 0; + if (0 != (VISIBLE_ALIGN_TOP & flags)) + { + dy = item->rect.bottom - rect.bottom; + } + else if (0 != (VISIBLE_ALIGN_BOTTOM & flags)) + { + SCROLLINFO scrollInfo; + scrollInfo.cbSize = sizeof(scrollInfo); + scrollInfo.fMask = SIF_RANGE | SIF_PAGE; + if (FALSE != GetScrollInfo(hwnd, SB_VERT, &scrollInfo)) + { + dy = scrollInfo.nMax - rect.bottom; + } + } + + if ((item->rect.bottom - dy) > rect.bottom) + dy = item->rect.bottom - rect.bottom; + + if ((item->rect.top - dy) < rect.top) + dy = item->rect.top - rect.top; + + if (0 == dx && 0 == dy) + return FALSE; + + if (FALSE == WIDGET_SCROLL(hwnd, dx, dy, TRUE)) + return FALSE; + + ListWidget_UpdateHover(self, hwnd); + + return TRUE; +} + +BOOL +ListWidget_AddItem(ListWidgetGroup *group, ListWidgetItem *item) +{ + if (NULL == group || NULL == item) + return FALSE; + + group->items.push_back(item); + return TRUE; + +} + + +ListWidgetItem * +ListWidget_FindGroupItemEx(ListWidgetGroup *group, const char *name, size_t max) +{ + size_t index, count; + + if (NULL == group || NULL == name) + return NULL; + + count = group->items.size(); + if (max < count) + count = max; + + for(index = 0; index < count; index++) + { + ListWidgetItem *item = group->items[index]; + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, name, -1, item->name, -1)) + return item; + } + + return NULL; +} + +ListWidgetItem * +ListWidget_FindGroupItem(ListWidgetGroup *group, const char *name) +{ + return ListWidget_FindGroupItemEx(group, name, -1); +} + + +ListWidgetGroup * +ListWidget_GetItemOwner(ListWidget *self, ListWidgetItem *baseItem, ListWidgetCategory **categoryOut) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + + if (NULL == self || NULL == baseItem) + return NULL; + + 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 (item == baseItem) + { + if (NULL != categoryOut) + *categoryOut = category; + + return group; + } + } + } + } + + if (NULL != categoryOut) + *categoryOut = NULL; + + return NULL; +} + +ListWidgetItem * +ListWidget_FindItem(ListWidget *self, const char *name, + ListWidgetCategory **categoryOut, + ListWidgetGroup **groupOut) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + ListWidgetItem *item; + + if (NULL == self || NULL == name) + return NULL; + + 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 (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, name, -1, item->name, -1)) + { + if (NULL != categoryOut) + *categoryOut = category; + + if (NULL != groupOut) + *groupOut = group; + + return item; + } + } + } + } + + return NULL; +} + +BOOL +ListWidget_FindItemPos(ListWidget *self, ListWidgetItem *item, + size_t *categoryOut, size_t *groupOut, size_t *itemOut) +{ + size_t iCategory, iGroup, iItem; + ListWidgetCategory *category; + ListWidgetGroup *group; + + if (NULL == self || NULL == item) + return FALSE; + + 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++) + { + if (item == group->items[iItem]) + { + if (NULL != categoryOut) + *categoryOut = iCategory; + + if (NULL != groupOut) + *groupOut = iGroup; + + if (NULL != itemOut) + *itemOut = iItem; + + return TRUE; + } + } + } + } + + return FALSE; +} + +BOOL +ListWidget_DisplayItemContextMenu(ListWidget *self, HWND hwnd, ListWidgetItem *item, POINT pt) +{ + HMENU menu; + ifc_device *device; + unsigned int commandId; + BOOL succeeded; + char *itemName; + + + if (NULL == self || NULL == item) + return FALSE; + + if (NULL != self->activeMenu) + return FALSE; + + if (NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(item->name, &device)) + { + return FALSE; + } + + menu = CreatePopupMenu(); + if (NULL != menu) + { + if (0 == Menu_InsertDeviceItems(menu, 0, 100, device, DeviceCommandContext_ViewMenu)) + { + DestroyMenu(menu); + menu = NULL; + } + } + + device->Release(); + + if (NULL == menu) + return FALSE; + + succeeded = FALSE; + + self->activeMenu = menu; + itemName = AnsiString_Duplicate(item->name); + + if (FALSE != ListWidget_RemoveHover(self, hwnd, TRUE)) + UpdateWindow(hwnd); + + commandId = Menu_TrackPopup(Plugin_GetLibraryWindow(), menu, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION | TPM_VERTICAL | TPM_RETURNCMD, + pt.x, pt.y, hwnd, NULL); + + + self->activeMenu = NULL; + + if (0 != commandId) + { + const char *command; + + command = (const char*)Menu_GetItemData(menu, commandId, FALSE); + succeeded = ListWidget_SendItemCommand(itemName, command, hwnd, 0, TRUE); + } + else + { + if (ERROR_SUCCESS == GetLastError()) + succeeded = TRUE; + } + + Menu_FreeItemData(menu, 0, -1); + + AnsiString_Free(itemName); + + if (FALSE != ListWidget_UpdateHover(self, hwnd)) + UpdateWindow(hwnd); + + return succeeded; +} + +size_t +ListWidget_GetItemCommands(ListWidgetItem *item, ListWidgetCommand **buffer, size_t bufferMax) +{ + size_t count; + ifc_device *device; + + if (NULL == item) + return 0; + + count = 0; + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->DeviceFind(item->name, &device)) + { + count = ListWigdet_GetDeviceCommands(buffer, bufferMax, device); + device->Release(); + } + + return count; +} + +BOOL +ListWidget_SendItemCommand(const char *name, const char *command, HWND hostWindow, ULONG_PTR param, BOOL enableIntercept) +{ + BOOL succeeded; + ifc_device *device; + BOOL commandProcessed; + + if (NULL == name || + NULL == command || + NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(name, &device)) + { + return FALSE; + } + + commandProcessed = FALSE; + succeeded = FALSE; + + if (FALSE != enableIntercept) + { + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, command, -1, "view_open", -1)) + { + succeeded = Navigation_SelectDevice(device->GetName()); + commandProcessed = succeeded; + } + else if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, command, -1, "rename", -1)) + { + succeeded = Navigation_EditDeviceTitle(device->GetName()); + commandProcessed = succeeded; + } + } + + if (FALSE == commandProcessed && + SUCCEEDED(device->SendCommand(command, hostWindow, param))) + { + succeeded = TRUE; + } + + device->Release(); + + return succeeded; +} + +BOOL +ListWidget_CreateItemActivity(ListWidgetItem *item) +{ + if (NULL == item) + return FALSE; + + if (NULL == item->activity) + { + item->activity = (ListWidgetActivity*)malloc(sizeof(ListWidgetActivity)); + if (NULL == item->activity) + return FALSE; + } + + item->activity->step = 0; + item->activity->cancelable = FALSE; + item->activity->percent = (unsigned int)-1; + item->activity->title = NULL; + SetSizeEmpty(&item->activity->titleSize); + + return TRUE; +} + +BOOL +ListWidget_DeleteItemActivity(ListWidgetItem *item) +{ + if (NULL == item || + NULL == item->activity) + { + return FALSE; + } + + String_Free(item->activity->title); + + free(item->activity); + item->activity = NULL; + + return TRUE; +} + + +ListWidtetActivityChange +ListWidget_UpdateItemActivity(ListWidgetItem *item, ifc_deviceactivity *activity) +{ + ListWidgetActivityChange changed; + BOOL cancelable; + unsigned int percent; + wchar_t buffer[512] = {0}; + + if (NULL == item || NULL == item->activity || NULL == activity) + return ListWidgetActivityChanged_Nothing; + + changed = ListWidgetActivityChanged_Nothing; + + cancelable = activity->GetCancelable(); + if (item->activity->cancelable != cancelable) + { + changed |= ListWidgetActivityChanged_Cancelable; + item->activity->cancelable = cancelable; + } + + if(FAILED(activity->GetProgress(&percent))) + percent = (unsigned int)-1; + + if (item->activity->percent != percent) + { + changed |= ListWidgetActivityChanged_Percent; + item->activity->percent = percent; + + } + + if (FAILED(activity->GetDisplayName(buffer, ARRAYSIZE(buffer)))) + buffer[0] = L'\0'; + + if (NULL == item->activity->title || + CSTR_EQUAL != CompareString(LOCALE_SYSTEM_DEFAULT, 0, item->activity->title, -1, buffer, -1)) + { + changed |= ListWidgetActivityChanged_Title; + + String_Free(item->activity->title); + item->activity->title = String_Duplicate(buffer); + SetSizeEmpty(&item->activity->titleSize); + } + + return changed; +} + +BOOL +ListWidget_GetItemImageRect(ListWidget *self, ListWidgetItem *item, ListWidgetItemMetric *metrics, RECT *rect) +{ + if (NULL == item || NULL == rect) + return FALSE; + + if (FALSE == CopyRect(rect, &item->rect)) + return FALSE; + + if (NULL != metrics) + { + rect->left += metrics->offsetLeft + metrics->imageOffsetLeft; + rect->top += metrics->offsetTop + metrics->imageOffsetTop; + rect->right -= metrics->offsetRight - metrics->imageOffsetRight; + rect->bottom = rect->top + self->imageSize.cy; + } + + return TRUE; +} + +BOOL +ListWidget_GetItemFrameRect(ListWidget *self, ListWidgetItem *item, ListWidgetItemMetric *metrics, RECT *rect) +{ + if (NULL == item || NULL == rect) + return FALSE; + + if (FALSE == CopyRect(rect, &item->rect)) + return FALSE; + + if (NULL != metrics) + { + rect->bottom = rect->top + metrics->offsetTop + + metrics->imageOffsetTop + metrics->imageOffsetBottom + + self->imageSize.cy; + } + + return TRUE; +} + +static BOOL +ListWidget_CalcItemActivityTitleSize(ListWidget *self, HDC hdc, ListWidgetActivity *activity) +{ + BOOL result; + HDC windowDC; + HFONT prevFont; + RECT rect; + + + if (NULL == hdc) + { + windowDC = GetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE | DCX_NORESETATTRS); + if (NULL == windowDC) + { + SetSizeEmpty(&activity->titleSize); + return FALSE; + } + hdc = windowDC; + } + else + windowDC = NULL; + + prevFont = SelectFont(hdc, self->activityFont); + + SetRect(&rect, 0, 0, self->activityMetrics.titleWidth, self->activityMetrics.titleHeight); + result = DrawText(hdc, activity->title, -1, &rect, + DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK | DT_EDITCONTROL | DT_WORD_ELLIPSIS); + + if (FALSE == result) + SetSizeEmpty(&activity->titleSize); + else + { + TEXTMETRIC textMetrics; + if (FALSE == GetTextMetrics(hdc, &textMetrics)) + ZeroMemory(&textMetrics, sizeof(textMetrics)); + + if (rect.right > self->activityMetrics.titleWidth) + rect.right = self->activityMetrics.titleWidth; + if (rect.bottom > self->activityMetrics.titleHeight) + { + textMetrics.tmHeight = self->activityMetrics.fontHeight; + rect.bottom = (self->activityMetrics.titleHeight/textMetrics.tmHeight)*textMetrics.tmHeight; + } + + activity->titleSize.cx = rect.right + textMetrics.tmAveCharWidth/2; + activity->titleSize.cy = rect.bottom; + } + + SelectFont(hdc, prevFont); + if (NULL != windowDC) + ReleaseDC(NULL, windowDC); + + return result; +} + +static BOOL +ListWidget_GetItemActivityWorkRect(ListWidget *self, HDC hdc, + ListWidgetItem *item, ListWidgetItemMetric *metrics, RECT *rect) +{ + ListWidgetActivityMetric *activityMetrics; + long length; + + if (FALSE == ListWidget_GetItemActivityRect(self, item, metrics, rect)) + return FALSE; + + if (0 == item->activity->titleSize.cy && + FALSE == ListWidget_CalcItemActivityTitleSize(self, hdc, item->activity)) + { + return FALSE; + } + + activityMetrics = &self->activityMetrics; + + length = 0; + if (0 != activityMetrics->progressWidth) + { + if (0 != length) + length += activityMetrics->spacing; + length += activityMetrics->progressWidth; + } + + if (0 != item->activity->titleSize.cx) + { + if (0 != length) + length += activityMetrics->spacing; + length += item->activity->titleSize.cx; + } + + if (0 != activityMetrics->percentWidth) + { + if (0 != length) + length += activityMetrics->spacing; + length += activityMetrics->percentWidth; + } + + rect->top += activityMetrics->offsetTop; + rect->bottom -= activityMetrics->offsetBottom; + + rect->left += activityMetrics->offsetLeft; + rect->right -= activityMetrics->offsetRight; + + rect->left += ((rect->right - rect->left) - length)/2; + rect->right = rect->left + length; + + return TRUE; +} + + +BOOL +ListWidget_GetItemActivityRect(ListWidget *self, ListWidgetItem *item, ListWidgetItemMetric *metrics, RECT *rect) +{ + if (NULL == self || + NULL == item || + NULL == metrics || + NULL == rect) + { + return FALSE; + } + + rect->bottom = item->rect.top + self->imageSize.cy /*+ metrics.imageOffsetBottom*/; + rect->bottom += metrics->offsetTop + metrics->imageOffsetTop; + rect->top = rect->bottom - self->activityMetrics.height; + + rect->left = item->rect.left + (self->itemWidth - self->activityMetrics.width)/2; + rect->right = rect->left + self->activityMetrics.width; + + return TRUE; +} + +BOOL +ListWidget_GetItemActivityProgressRect(ListWidget *self, HDC hdc, + ListWidgetItem *item, ListWidgetItemMetric *metrics, RECT *rect) +{ + if (0 == self->activityMetrics.progressWidth || + FALSE == ListWidget_GetItemActivityWorkRect(self, hdc, item, metrics, rect)) + { + return FALSE; + } + + rect->right = rect->left + self->activityMetrics.progressWidth; + rect->top +=((rect->bottom - rect->top) - self->activityMetrics.progressHeight)/2; + rect->bottom = rect->top + self->activityMetrics.progressHeight; + return TRUE; +} + +BOOL +ListWidget_GetItemActivityPercentRect(ListWidget *self, HDC hdc, + ListWidgetItem *item, ListWidgetItemMetric *metrics, RECT *rect) +{ + if (0 == self->activityMetrics.percentWidth || + FALSE == ListWidget_GetItemActivityWorkRect(self, hdc, item, metrics, rect)) + { + return FALSE; + } + + rect->left = rect->right - self->activityMetrics.percentWidth; + rect->top += ((rect->bottom - rect->top) - self->activityMetrics.percentHeight)/2; + rect->bottom = rect->top + self->activityMetrics.percentHeight; + + return TRUE; +} + +BOOL +ListWidget_GetItemActivityTitleRect(ListWidget *self, HDC hdc, + ListWidgetItem *item, ListWidgetItemMetric *metrics, RECT *rect) +{ + if (0 == self->activityMetrics.titleWidth || + FALSE == ListWidget_GetItemActivityWorkRect(self, hdc, item, metrics, rect)) + { + return FALSE; + } + + if (0 != self->activityMetrics.progressWidth) + rect->left += self->activityMetrics.progressWidth + self->activityMetrics.spacing; + + rect->right = rect->left + item->activity->titleSize.cx; + + rect->top += ((rect->bottom - rect->top) - item->activity->titleSize.cy)/2; + rect->bottom = rect->top + item->activity->titleSize.cy; + + return TRUE; +} + +BOOL +ListWidget_InvalidateItemActivity(ListWidget *self, HWND hwnd, ListWidgetItem *item, ListWidgetActivityChange changes) +{ + ListWidgetItemMetric metrics; + WidgetStyle *style; + POINT origin; + RECT rect; + BOOL invalidated; + + + if (ListWidgetActivityChanged_Nothing == changes) + return FALSE; + + style = WIDGET_GET_STYLE(hwnd); + + + if (NULL == style || + FALSE == ListWidget_GetItemMetrics(style, &metrics)) + { + return FALSE; + } + + if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) + { + origin.x = 0; + origin.y = 0; + } + + invalidated = FALSE; + + if (0 != (ListWidgetActivityChanged_Percent & changes)) + { + if (FALSE != ListWidget_GetItemActivityPercentRect(self, NULL, item, &metrics, &rect)) + { + OffsetRect(&rect, origin.x, origin.y); + if (FALSE != InvalidateRect(hwnd, &rect, FALSE)) + invalidated = TRUE; + } + } + + if (0 != (ListWidgetActivityChanged_Title & changes)) + { + if (FALSE != ListWidget_GetItemActivityTitleRect(self, NULL, item, &metrics, &rect)) + { + OffsetRect(&rect, origin.x, origin.y); + if (FALSE != InvalidateRect(hwnd, &rect, FALSE)) + invalidated = TRUE; + } + } + + return invalidated; +} + +BOOL +ListWidget_InvalidateItemImage(ListWidget *self, HWND hwnd, ListWidgetItem *item) +{ + ListWidgetItemMetric metrics; + WidgetStyle *style; + POINT origin; + RECT rect; + + style = WIDGET_GET_STYLE(hwnd); + + if (NULL == style || + FALSE == ListWidget_GetItemMetrics(style, &metrics)) + { + return FALSE; + } + + if (FALSE == ListWidget_GetItemImageRect(self, item, &metrics, &rect)) + return FALSE; + + if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) + OffsetRect(&rect, origin.x, origin.y); + + return InvalidateRect(hwnd, &rect, FALSE); +} + + +BOOL +ListWidget_GetItemSpacebarRect(ListWidget *self, ListWidgetItem *item, ListWidgetItemMetric *metrics, RECT *rect) +{ + if (NULL == item || NULL == rect) + return FALSE; + + if (0 == item->spaceTotal || + FALSE == CopyRect(rect, &item->rect)) + { + return FALSE; + } + + if (NULL != metrics) + { + rect->left += metrics->offsetLeft; + rect->top += metrics->offsetTop + metrics->imageOffsetTop + + self->imageSize.cy + metrics->imageOffsetBottom + + metrics->spacebarOffsetTop; + rect->right -= metrics->offsetRight; + rect->bottom = rect->top + metrics->spacebarHeight; + } + + return TRUE; +} + +BOOL +ListWidget_GetItemTitleRect(ListWidget *self, ListWidgetItem *item, ListWidgetItemMetric *metrics, BOOL exactSize, RECT *rect) +{ + if (NULL == item || NULL == rect) + return FALSE; + + if (FALSE == CopyRect(rect, &item->rect)) + { + return FALSE; + } + + if (NULL != metrics) + { + rect->left += metrics->offsetLeft; + rect->top += metrics->offsetTop + metrics->imageOffsetTop + + self->imageSize.cy + metrics->imageOffsetBottom; + + if (0 != item->spaceTotal) + rect->top += metrics->spacebarOffsetTop + metrics->spacebarHeight; + + rect->top += metrics->titleOffsetTop; + rect->right -= metrics->offsetRight; + rect->bottom -= metrics->offsetBottom; + } + + if (-1 != item->titleSize.cy) + { + long max; + + if (FALSE != exactSize) + { + max = rect->right - rect->left; + if (max > item->titleSize.cx) + { + rect->left += (max - item->titleSize.cx)/2; + rect->right = rect->left + item->titleSize.cx; + } + } + + max = rect->top + item->titleSize.cy; + if (rect->bottom > max) + rect->bottom = max; + } + + return TRUE; +} + +BOOL +ListWidget_GetItemConnectionRect(ListWidget *self, ListWidgetItem *item, ListWidgetItemMetric *metrics, RECT *rect) +{ + if (NULL == item || NULL == rect) + return FALSE; + + if (NULL == metrics) + return FALSE; + + SetRect(rect, 0, 0, self->connectionSize.cx, self->connectionSize.cy); + OffsetRect(rect, + item->rect.right - metrics->offsetRight - metrics->imageOffsetRight - rect->right - 2, + item->rect.top + metrics->offsetTop + metrics->imageOffsetTop + self->imageSize.cy - rect->bottom - 2); + + if (rect->left < (item->rect.left + metrics->offsetLeft) || + rect->top < (item->rect.top + metrics->offsetTop)) + { + return FALSE; + } + + return TRUE; +} + +ListWidgetItemPart +ListWidget_GetItemPartFromPoint(ListWidget *self, ListWidgetItem *item, ListWidgetItemMetric *metrics, + POINT pt, ListWidgetItemPart mask, RECT *partRect) +{ + RECT rect; + + if (NULL == self || + NULL == item || + NULL == metrics) + { + if (NULL != partRect) + SetRectEmpty(partRect); + + return ListWidgetItemPart_None; + } + + + + if (NULL != item->activity && + FALSE != ListWidget_GetItemActivityRect(self, item, metrics, &rect) && + FALSE != PtInRect(&rect, pt)) + { + if (0 != (ListWidgetItemPart_Activity & mask)) + { + if (NULL != partRect) + CopyRect(partRect, &rect); + + return ListWidgetItemPart_Activity; + } + + mask &= ~(ListWidgetItemPart_Command | ListWidgetItemPart_Connection); + } + + if (0 != (ListWidgetItemPart_Command & mask) && + FALSE != ListWidgetItem_IsInteractive(item)) + { + + size_t index = self->commandsCount; + while(index--) + { + if (FALSE != ListWidget_GetCommandRect(self->commands[index], &rect)) + { + OffsetRect(&rect, item->rect.left, item->rect.top); + if (FALSE != PtInRect(&rect, pt)) + { + if (FALSE == ListWidget_GetCommandDisabled(self->commands[index])) + { + if (NULL != partRect) + CopyRect(partRect, &rect); + + return ListWidgetItemPart_Command; + } + break; + } + } + } + } + + if (0 != (ListWidgetItemPart_Connection & mask) && + FALSE != ListWidget_GetItemConnectionRect(self, item, metrics, &rect) && + FALSE != PtInRect(&rect, pt)) + { + if (NULL != partRect) + CopyRect(partRect, &rect); + return ListWidgetItemPart_Connection; + } + + if (0 != (ListWidgetItemPart_Image & mask) && + FALSE != ListWidget_GetItemImageRect(self, item, metrics, &rect) && + FALSE != PtInRect(&rect, pt)) + { + if (NULL != partRect) + CopyRect(partRect, &rect); + return ListWidgetItemPart_Image; + } + + if (0 != (ListWidgetItemPart_Frame & mask) && + FALSE != ListWidget_GetItemFrameRect(self, item, metrics, &rect) && + FALSE != PtInRect(&rect, pt)) + { + if (NULL != partRect) + CopyRect(partRect, &rect); + return ListWidgetItemPart_Frame; + } + + + if (0 != (ListWidgetItemPart_Spacebar & mask) && + FALSE != ListWidget_GetItemSpacebarRect(self, item, metrics, &rect) && + FALSE != PtInRect(&rect, pt)) + { + if (NULL != partRect) + CopyRect(partRect, &rect); + return ListWidgetItemPart_Spacebar; + } + + if (0 != (ListWidgetItemPart_Title & mask) && + FALSE != ListWidget_GetItemTitleRect(self, item, metrics, FALSE, &rect) && + FALSE != PtInRect(&rect, pt)) + { + if (NULL != partRect) + CopyRect(partRect, &rect); + return ListWidgetItemPart_Title; + } + + return ListWidgetItemPart_None; +} + + + +static BOOL +ListWidget_FilterItemTitle(wchar_t *buffer, size_t bufferMax) +{ + size_t read, write; + + for(read = 0, write = 0;; read++) + { + if (read == bufferMax) + return FALSE; + + if (L'\r' == buffer[read]) + continue; + + if (L'\n' == buffer[read] || + L'\t' == buffer[read] || + L'\b' == buffer[read]) + { + buffer[write] = L' '; + } + + buffer[write] = buffer[read]; + if (L'\0' == buffer[read]) + break; + + write++; + } + + return TRUE; +} + +static HRESULT +ListWidget_GetDeviceStatus(ifc_device *device, wchar_t *buffer, size_t bufferMax) +{ + HRESULT hr; + ifc_deviceactivity *activity; + + if(NULL == buffer) + return E_POINTER; + + buffer[0] = L'\0'; + if (NULL == device) + return S_OK; + + hr = device->GetActivity(&activity); + if (S_OK == hr && NULL != activity) + { + hr = activity->GetStatus(buffer, bufferMax); + if (FAILED(hr) || L'\0' == buffer[0]) + hr = activity->GetDisplayName(buffer, bufferMax); + + activity->Release(); + } + + if (FAILED(hr) || L'\0' == buffer[0]) + hr = device->GetStatus(buffer, bufferMax); + + if (E_NOTIMPL == hr) + { + hr = S_OK; + buffer[0] = L'\0'; + } + + return hr; + +} +BOOL +ListWidget_FormatItemCommandTip(ListWidget *self, ListWidgetItem *item, const RECT *commandRect, wchar_t *buffer, size_t bufferMax) +{ + size_t index; + RECT rect; + + if (NULL == self) + return FALSE; + + for(index = 0; index < self->commandsCount; index++) + { + ListWidgetCommand *command = self->commands[index]; + if (FALSE != ListWidget_GetCommandRect(command, &rect) && + FALSE != EqualRect(&rect, commandRect)) + { + const wchar_t *value; + wchar_t *cursor; + size_t remaining; + + cursor = buffer; + remaining = bufferMax; + + value = ListWidget_GetCommandTitle(command); + if (FALSE == IS_STRING_EMPTY(value)) + StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + value = ListWidget_GetCommandDescription(command); + if (FALSE == IS_STRING_EMPTY(value)) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + + return (cursor != buffer); + } + } + + return FALSE; +} + +BOOL +ListWidget_FormatItemTip(ListWidget *self, ListWidgetItem *item, wchar_t *buffer, size_t bufferMax) +{ + ifc_device *device; + ifc_devicetype *type; + ifc_deviceconnection *connection; + wchar_t value[1024], valueName[512], *cursor; + size_t remaining; + uint64_t totalSpace, usedSpace; + + if (NULL == item || + NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(item->name, &device)) + { + return FALSE; + } + + cursor = buffer; + remaining = bufferMax; + + if (FALSE != ListWidgetItem_IsTextTruncated(item)) + { + if (SUCCEEDED(device->GetDisplayName(value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + ListWidget_FilterItemTitle(value, ARRAYSIZE(value)); + StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + } + + if (SUCCEEDED(device->GetModel(value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_MODEL_SHORT, valueName, ARRAYSIZE(valueName)); + HRESULT hr; + if (L'\0' != valueName[0]) + hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: ", valueName); + else + hr = S_OK; + + if (SUCCEEDED(hr)) + StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->TypeFind(device->GetType(), &type)) + { + const char* typeStr = device->GetDisplayType(); + if (typeStr && *typeStr) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_TYPE_SHORT, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: ", valueName); + + StringCchCopyEx(cursor, remaining, AutoWide(typeStr), &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + else + { + if (SUCCEEDED(type->GetDisplayName(value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_TYPE_SHORT, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: ", valueName); + + StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + } + type->Release(); + } + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->ConnectionFind(device->GetConnection(), &connection)) + { + if (SUCCEEDED(connection->GetDisplayName(value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_CONNECTION_SHORT, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: ", valueName); + + StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + connection->Release(); + } + + if (FAILED(device->GetTotalSpace(&totalSpace)) || + 0 == totalSpace) + { + totalSpace = ((uint64_t)-1); + } + + if (FAILED(device->GetUsedSpace(&usedSpace))) + usedSpace = ((uint64_t)-1); + else if (((uint64_t)-1) != totalSpace && usedSpace > totalSpace) + usedSpace = totalSpace; + + if (((uint64_t)-1) != totalSpace && ((uint64_t)-1) != usedSpace) + { + if (NULL != WASABI_API_LNG->FormattedSizeString(value, ARRAYSIZE(value), totalSpace - usedSpace)) + { + + WASABI_API_LNGSTRINGW_BUF(IDS_FREE_SPACE, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: %s", + valueName, value); + } + } + } + + if (((uint64_t)-1) != totalSpace) + { + if (NULL != WASABI_API_LNG->FormattedSizeString(value, ARRAYSIZE(value), totalSpace)) + { + + WASABI_API_LNGSTRINGW_BUF(IDS_TOTAL_SPACE, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: %s", + valueName, value); + } + } + } + + // status + /*if (SUCCEEDED(ListWidget_GetDeviceStatus(device, value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_STATUS_SHORT, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: ", valueName); + else + hr = S_OK; + + if (SUCCEEDED(hr)) + hr = StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + */ + + device->Release(); + return (cursor != buffer); +} + +BOOL +ListWidget_FormatItemTitleTip(ListWidget *self, ListWidgetItem *item, wchar_t *buffer, size_t bufferMax) +{ + BOOL result; + ifc_device *device; + + if (NULL == item || + FALSE == ListWidgetItem_IsTextTruncated(item) || + NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(item->name, &device)) + { + return FALSE; + } + + if (SUCCEEDED(device->GetDisplayName(buffer, bufferMax))) + { + ListWidget_FilterItemTitle(buffer, bufferMax); + result = TRUE; + } + else + result = FALSE; + + device->Release(); + + return result; +} + +BOOL +ListWidget_FormatItemSpaceTip(ListWidget *self, ListWidgetItem *item, wchar_t *buffer, size_t bufferMax) +{ + ifc_device *device; + + wchar_t value[1024], valueName[512], *cursor; + size_t remaining; + uint64_t totalSpace, usedSpace; + + if (NULL == item || + NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(item->name, &device)) + { + return FALSE; + } + + cursor = buffer; + remaining = bufferMax; + + if (FAILED(device->GetTotalSpace(&totalSpace)) || + 0 == totalSpace) + { + totalSpace = ((uint64_t)-1); + } + + if (FAILED(device->GetUsedSpace(&usedSpace))) + usedSpace = ((uint64_t)-1); + else if (((uint64_t)-1) != totalSpace && usedSpace > totalSpace) + usedSpace = totalSpace; + + if (((uint64_t)-1) != usedSpace) + { + if (NULL != WASABI_API_LNG->FormattedSizeString(value, ARRAYSIZE(value), usedSpace)) + { + WASABI_API_LNGSTRINGW_BUF(IDS_USED_SPACE, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: %s", + valueName, value); + } + } + } + + if (((uint64_t)-1) != totalSpace && ((uint64_t)-1) != usedSpace) + { + if (NULL != WASABI_API_LNG->FormattedSizeString(value, ARRAYSIZE(value), totalSpace - usedSpace)) + { + WASABI_API_LNGSTRINGW_BUF(IDS_FREE_SPACE, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: %s", + valueName, value); + } + } + } + + if (((uint64_t)-1) != totalSpace) + { + if (NULL != WASABI_API_LNG->FormattedSizeString(value, ARRAYSIZE(value), totalSpace)) + { + WASABI_API_LNGSTRINGW_BUF(IDS_TOTAL_SPACE, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: %s", + valueName, value); + } + } + } + + device->Release(); + return (cursor != buffer); +} + +BOOL +ListWidget_FormatItemStatus(ListWidget *self, ListWidgetItem *item, wchar_t *buffer, size_t bufferMax) +{ + ifc_device *device; + ifc_devicetype *type; + ifc_deviceconnection *connection; + + HRESULT hr; + wchar_t value[512], valueName[128], *cursor; + size_t remaining; + + if (NULL == item || + NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(item->name, &device)) + { + return FALSE; + } + + hr = S_OK; + cursor = buffer; + remaining = bufferMax; + + if (FALSE != ListWidgetItem_IsTextTruncated(item)) + { + if (SUCCEEDED(device->GetDisplayName(value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + ListWidget_FilterItemTitle(value, ARRAYSIZE(value)); + hr = StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + } + + if (cursor == buffer && + SUCCEEDED(device->GetModel(value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_MODEL_SHORT, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: ", valueName); + + if (SUCCEEDED(hr)) + hr = StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + + if (cursor == buffer && + NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->TypeFind(device->GetType(), &type)) + { + if (SUCCEEDED(type->GetDisplayName(value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_TYPE_SHORT, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: ", valueName); + + if (SUCCEEDED(hr)) + hr = StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + + type->Release(); + } + else + { + const char* typeStr = device->GetDisplayType(); + if (typeStr && *typeStr) + { + if (cursor != buffer) + hr = StringCchCopyEx(cursor, remaining, L", ", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_TYPE_SHORT, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: ", valueName); + + if (SUCCEEDED(hr)) + StringCchCopyEx(cursor, remaining, AutoWide(typeStr), &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + } + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->ConnectionFind(device->GetConnection(), &connection)) + { + if (SUCCEEDED(connection->GetDisplayName(value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L", ", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICE_CONNECTION_SHORT, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, L"%s: ", valueName); + + StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + connection->Release(); + } + + if (cursor == buffer && + SUCCEEDED(device->GetDisplayName(value, ARRAYSIZE(value)))) + { + ListWidget_FilterItemTitle(value, ARRAYSIZE(value)); + StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + + if (SUCCEEDED(ListWidget_GetDeviceStatus(device, value, ARRAYSIZE(value))) && + L'\0' != value[0]) + { + if (cursor != buffer) + StringCchCopyEx(cursor, remaining, L", ", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + + StringCchCopyEx(cursor, remaining, value, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE); + } + + device->Release(); + + return (cursor != buffer); +} + +BOOL +ListWidget_FormatItemSpaceStatus(ListWidget *self, ListWidgetItem *item, wchar_t *buffer, size_t bufferMax) +{ + ifc_device *device; + + wchar_t *cursor; + size_t remaining; + uint64_t totalSpace, usedSpace; + + if (NULL == item || + NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(item->name, &device)) + { + return FALSE; + } + + cursor = buffer; + remaining = bufferMax; + + if (FAILED(device->GetTotalSpace(&totalSpace)) || + 0 == totalSpace) + { + totalSpace = ((uint64_t)-1); + } + + if (FAILED(device->GetUsedSpace(&usedSpace))) + usedSpace = ((uint64_t)-1); + else if (((uint64_t)-1) != totalSpace && usedSpace > totalSpace) + usedSpace = totalSpace; + + if (((uint64_t)-1) != totalSpace) + { + if (((uint64_t)-1) != usedSpace) + { + wchar_t value1[64] = {0}, value2[64] = {0}; + if (NULL != WASABI_API_LNG->FormattedSizeString(value1, ARRAYSIZE(value1), totalSpace - usedSpace) && + NULL != WASABI_API_LNG->FormattedSizeString(value2, ARRAYSIZE(value2), totalSpace)) + { + wchar_t format[128] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_STATUS_SPACE_TEMPLATE, format, ARRAYSIZE(format)); + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, + format, value1, value2); + } + } + else + { + wchar_t value[64] = {0}; + if (NULL != WASABI_API_LNG->FormattedSizeString(value, ARRAYSIZE(value), totalSpace)) + { + wchar_t valueName[128] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_TOTAL_SPACE, valueName, ARRAYSIZE(valueName)); + if (L'\0' != valueName[0]) + { + StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, + L"%s %s", value, valueName); + + } + } + } + } + + device->Release(); + + return (cursor != buffer); +} + +static void CALLBACK +ListWidget_EndTitleEditCb(HWND editorWindow, BOOL canceled, const wchar_t *text, void *user) +{ + HWND hwnd; + ListWidget *self; + char *itemName; + + hwnd = GetAncestor(editorWindow, GA_PARENT); + self = WIDGET_GET_SELF(hwnd, ListWidget); + + itemName = (char*)user; + + if (NULL != self) + { + ListWidgetItem *item; + + if (self->titleEditor == editorWindow) + self->titleEditor = NULL; + + item = ListWidget_FindItem(self, itemName, NULL, NULL); + if (NULL != item) + { + ListWidgetItemMetric metrics; + WidgetStyle *style; + POINT origin; + RECT rect; + + ListWidgetItem_UnsetTextEdited(item); + + style = WIDGET_GET_STYLE(hwnd); + + if (NULL != style && + FALSE != ListWidget_GetItemMetrics(style, &metrics) && + FALSE != ListWidget_GetItemTitleRect(self, item, &metrics, FALSE, &rect)) + { + if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) + OffsetRect(&rect, origin.x, origin.y); + + InvalidateRect(hwnd, &rect, FALSE); + } + } + + if (FALSE == canceled) + { + ifc_device *device; + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->DeviceFind(itemName, &device)) + { + wchar_t buffer[1024] = {0}; + + if (FAILED(device->GetDisplayName(buffer, ARRAYSIZE(buffer))) || + CSTR_EQUAL != CompareString(LOCALE_USER_DEFAULT, 0, buffer, -1, text, -1)) + { + HRESULT hr; + + hr = device->SetDisplayName(text); + + if (FAILED(hr)) + { + wchar_t title[256] = {0}, message[1024] = {0}; + + WASABI_API_LNGSTRINGW_BUF(IDS_MESSAGEBOX_TITLE, title, ARRAYSIZE(title)); + WASABI_API_LNGSTRINGW_BUF(IDS_MESSAGE_UNABLE_TO_RENAME, message, ARRAYSIZE(message)); + + MessageBox(hwnd, message, title, MB_OK | MB_ICONERROR); + } + } + + device->Release(); + } + } + } + + EMBEDDEDEDITOR_SET_USER_DATA(editorWindow, NULL); + AnsiString_Free(itemName); +} + +HWND +ListWidget_BeginItemTitleEdit(ListWidget *self, HWND hwnd, ListWidgetItem *item) +{ + RECT rect; + WidgetStyle *style; + ListWidgetItemMetric metrics; + HWND editor; + POINT origin; + unsigned long editorStyleEx, editorStyle; + char *itemName; + ifc_device * device; + BOOL blockEditor; + + + if (NULL == self || NULL == item) + return NULL; + + style = WIDGET_GET_STYLE(hwnd); + if (NULL == style) + return NULL; + + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->DeviceFind(item->name, &device)) + { + blockEditor = (FALSE == DeviceCommand_GetEnabled(device, "rename", + DeviceCommandContext_ViewMenu)); + device->Release(); + } + else + blockEditor = TRUE; + + if (FALSE != blockEditor) + return NULL; + + if (FALSE == ListWidget_GetItemMetrics(style, &metrics)) + return NULL; + + if (FALSE == ListWidget_GetItemTitleRect(self, item, &metrics, FALSE, &rect)) + return NULL; + + if (FALSE != ListWidget_GetViewOrigin(hwnd, &origin)) + OffsetRect(&rect, origin.x, origin.y); + + editorStyleEx = WS_EX_CLIENTEDGE; + editorStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | + ES_CENTER | ES_NOHIDESEL | ES_MULTILINE | ES_AUTOVSCROLL; + + EmbeddedEditor_AdjustWindowRectEx(&rect, editorStyleEx, editorStyle); + + editor = CreateWindowEx(editorStyleEx, WC_EDIT, item->title, editorStyle, + rect.left, rect.top, 0, 0, + hwnd, NULL, NULL, 0L); + if (NULL == editor) + return NULL; + + + itemName = AnsiString_Duplicate(item->name); + if (FALSE == EmbeddedEditor_Attach(editor, ListWidget_EndTitleEditCb, itemName)) + { + AnsiString_Free(itemName); + DestroyWindow(editor); + return NULL; + } + + ListWidgetItem_SetTextEdited(item); + + EMBEDDEDEDITOR_SET_ANCHOR_POINT(editor, rect.left, rect.top); + EMBEDDEDEDITOR_SET_MAX_SIZE(editor, RECTWIDTH(rect), 0); + + + SendMessage(editor, WM_SETFONT, (WPARAM)WIDGETSTYLE_TEXT_FONT(style), 0L); + + ListWidget_UpdateTitleEditorColors(editor, style); + + SendMessage(editor, EM_SETSEL, 0, -1); + ShowWindow(editor, SW_SHOW); + SetFocus(editor); + + return editor; +} + + +int +ListWidget_CompareItemPos(ListWidget *self, ListWidgetItem *item1, ListWidgetItem *item2) +{ + size_t iCategory1, iGroup1, iItem1; + size_t iCategory2, iGroup2, iItem2; + + if (FALSE == ListWidget_FindItemPos(self, item1, &iCategory1, &iGroup1, &iItem1) || + FALSE == ListWidget_FindItemPos(self, item2, &iCategory2, &iGroup2, &iItem2)) + { + return _NLSCMPERROR; + } + + if (iCategory1 != iCategory2) + return (int)(iCategory1 - iCategory2); + + if (iGroup1 != iGroup2) + return (int)(iGroup1 - iGroup2); + + return (int)(iItem1 - iItem2); +} + +BOOL +ListWidget_GetViewItemPos(HWND hwnd, ListWidgetItem *item, POINT *pt) +{ + if (NULL == hwnd || + NULL == item || + NULL == pt) + { + return FALSE; + } + + if (FALSE == ListWidget_GetViewOrigin(hwnd, pt)) + { + pt->x = 0; + pt->y = 0; + } + + pt->x = item->rect.left - pt->x; + pt->y = item->rect.top - pt->y; + + return TRUE; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/listWidgetPaint.cpp b/Src/Plugins/Library/ml_devices/listWidgetPaint.cpp new file mode 100644 index 00000000..9f281d43 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidgetPaint.cpp @@ -0,0 +1,725 @@ +#include "main.h" +#include "./listWidgetInternal.h" + +#include <strsafe.h> + +static BOOL +ListWidgetPaintSpacebar_Initialize(ListWidgetPaintSpacebar *self, ListWidget *widget, + WidgetStyle *style, HWND hwnd, long width, long height) +{ + if (NULL == self) + return FALSE; + + self->bitmap = ListWidget_GetSpacebarBitmap(widget, style, hwnd, width, height); + if (NULL == self->bitmap) + { + self->width = 0; + self->height = 0; + self->emptyBarOffset = 0; + self->filledBarOffset = 0; + return FALSE; + } + + self->width = width; + self->height = height; + self->emptyBarOffset = 0; + self->filledBarOffset = height; + return TRUE; +} + +static void +ListWidgetPaintSpacebar_Uninitialize(ListWidgetPaintSpacebar *self) +{ + if (NULL == self) + return; +} + +static BOOL +ListWidgetPaintArrow_Initialize(ListWidgetPaintArrow *self, ListWidget *widget, + WidgetStyle *style, HWND hwnd) +{ + BITMAP bitmapInfo; + + if (NULL == self) + return FALSE; + + self->bitmap = ListWidget_GetArrowsBitmap(widget, style, hwnd); + if (NULL == self->bitmap || + sizeof(bitmapInfo) != GetObject(self->bitmap, sizeof(bitmapInfo), &bitmapInfo)) + { + ZeroMemory(self, sizeof(ListWidgetPaintArrow)); + return FALSE; + } + + self->width = bitmapInfo.bmWidth; + self->height = bitmapInfo.bmHeight/2; + if (self->height < 0) + self->height = -self->height; + + self->collapsedOffset = 0; + self->expandedOffset = self->height; + + return TRUE; +} + +static void +ListWidgetPaintArrow_Uninitialize(ListWidgetPaintArrow *self) +{ + if (NULL == self) + return; +} + +BOOL +ListWidgetPaint_Initialize(ListWidgetPaint *self, ListWidget *widget, WidgetStyle *style, + HWND hwnd, HDC hdc, const RECT *paintRect, BOOL erase) +{ + HDC windowDC; + + if (NULL == self) + return FALSE; + + if (NULL == widget || NULL == style) + return FALSE; + + self->widget = widget; + self->style = style; + self->hwnd = hwnd; + self->hdc = hdc; + self->erase = erase; + self->paintRect = paintRect; + + windowDC = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != windowDC) + { + self->sourceDC = CreateCompatibleDC(windowDC); + ReleaseDC(hwnd, windowDC); + } + + if (NULL == self->sourceDC) + return FALSE; + + if (FALSE == ListWidget_GetItemMetrics(style, &self->itemMetrics)) + return FALSE; + + if (FALSE == ListWidget_GetCategoryMetrics(style, &self->categoryMetrics)) + return FALSE; + + if (FALSE == ListWidgetPaintSpacebar_Initialize(&self->spacebar, widget, style, hwnd, + widget->itemWidth - (self->itemMetrics.offsetLeft + self->itemMetrics.offsetRight), + self->itemMetrics.spacebarHeight)) + { + // ListWidgetPaint_Uninitialize(self); + // return FALSE; + } + + + if (FALSE == ListWidgetPaintArrow_Initialize(&self->arrow, widget, style, hwnd)) + { + // ListWidgetPaint_Uninitialize(self); + // return FALSE; + } + + return TRUE; +} + +void +ListWidgetPaint_Uninitialize(ListWidgetPaint *self) +{ + if (NULL == self) + return; + + if (NULL != self->sourceDC) + DeleteDC(self->sourceDC); + + ListWidgetPaintSpacebar_Uninitialize(&self->spacebar); + ListWidgetPaintArrow_Uninitialize(&self->arrow); +} + + +static BOOL +ListWidgetPaint_DrawSpacebar(ListWidgetPaint *self, HDC hdc, int x, int y, + uint64_t totalSpace, uint64_t usedSpace) +{ + RECT *partRect, sourceRect; + BOOL succeeded; + long usedWidth; + ListWidgetPaintSpacebar *spacebar; + + if (NULL == self) + return FALSE; + + partRect = &self->partRect; + spacebar = &self->spacebar; + if (NULL == spacebar->bitmap) + return FALSE; + + succeeded = TRUE; + + if (usedSpace > totalSpace) + usedSpace = totalSpace; + + if (0 == totalSpace) + usedWidth = 0; + else + usedWidth = (long)(spacebar->width * (double)usedSpace/totalSpace); + + + SetRect(partRect, x, y, x + spacebar->width, y + spacebar->height); + SetRect(&sourceRect, 0, 0, spacebar->width, spacebar->height); + OffsetRect(&sourceRect, 0, spacebar->emptyBarOffset); + + if (FALSE == Image_AlphaBlend(hdc, partRect, self->sourceDC, &sourceRect, 255, + spacebar->bitmap, self->paintRect, AlphaBlend_Normal, NULL)) + { + succeeded = FALSE; + } + + if (0 != usedWidth) + { + SetRect(partRect, x, y, x + usedWidth, y + spacebar->height); + SetRect(&sourceRect, 0, 0, usedWidth, spacebar->height); + OffsetRect(&sourceRect, 0, spacebar->filledBarOffset); + + if (FALSE == Image_AlphaBlend(hdc, partRect, self->sourceDC, &sourceRect, 255, + spacebar->bitmap, self->paintRect, AlphaBlend_Normal, NULL)) + { + succeeded = FALSE; + } + } + + return succeeded; +} + +void +ListWidgetPaint_DrawItemAction(ListWidgetPaint *self, HDC hdc, ListWidgetItem *item, ListWidgetActivity *activity) +{ + HDC sourceDC; + WidgetStyle *style; + ListWidget *widget; + ListWidgetItemMetric *metrics; + RECT *partRect, sourceRect; + HBITMAP bitmap, prevSourceBitmap; + COLORREF prevTextColor; + HFONT prevFont; + int stringLength; + + if (NULL == activity || + FALSE == ListWidget_GetItemActivityRect(self->widget, item, &self->itemMetrics, &self->partRect) || + FALSE == IntersectRect(&sourceRect, &self->partRect, self->paintRect)) + { + return; + } + + + style = self->style; + widget = self->widget; + metrics = &self->itemMetrics; + sourceDC = self->sourceDC; + partRect = &self->partRect; + prevSourceBitmap = GetCurrentBitmap(sourceDC); + prevTextColor = GetTextColor(hdc); + prevFont = GetCurrentFont(hdc); + + if (NULL != widget->activityFont) + SelectFont(hdc, widget->activityFont); + + bitmap = ListWidget_GetActivityBadgeBitmap(widget, style, self->hwnd, + RECTWIDTH(*partRect), RECTHEIGHT(*partRect)); + if (NULL != bitmap) + { + Image_AlphaBlend(hdc, partRect, sourceDC, NULL, 255, bitmap, self->paintRect, + AlphaBlend_Normal, NULL); + } + + + if (FALSE == IS_STRING_EMPTY(activity->title) && + FALSE != ListWidget_GetItemActivityTitleRect(widget, hdc, item, metrics, partRect) && + FALSE != IntersectRect(&sourceRect, partRect, self->paintRect)) + { + stringLength = lstrlen(activity->title); + + OffsetRect(partRect, 0, 1); + SetTextColor(hdc, 0x000000); + DrawText(hdc, activity->title, stringLength, partRect, + DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_EDITCONTROL | DT_WORD_ELLIPSIS); + + OffsetRect(partRect, 0, -1); + SetTextColor(hdc, 0xFFFFFF); + DrawText(hdc, activity->title, stringLength, partRect, + DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_EDITCONTROL | DT_WORD_ELLIPSIS); + + } + + if (FALSE != ListWidget_GetItemActivityProgressRect(widget, hdc, item, metrics, partRect) && + FALSE != IntersectRect(&sourceRect, partRect, self->paintRect)) + { + bitmap = ListWidget_GetActivityProgressBitmap(widget, style); + if (NULL != bitmap) + { + SetRect(&sourceRect, 0, 0, + widget->activityMetrics.progressWidth, widget->activityMetrics.progressHeight); + OffsetRect(&sourceRect, 0, widget->activityMetrics.progressHeight * activity->step); + + Image_AlphaBlend(hdc, partRect, sourceDC, &sourceRect, 255, bitmap, self->paintRect, + AlphaBlend_Normal, NULL); + } + } + + if ((unsigned int)-1 != activity->percent && + FALSE != ListWidget_GetItemActivityPercentRect(widget, hdc, item, metrics, partRect) && + FALSE != IntersectRect(&sourceRect, partRect, self->paintRect)) + { + wchar_t buffer[6] = {0}; + + if (FAILED(StringCchPrintf(buffer, ARRAYSIZE(buffer), L"%d%%", activity->percent))) + stringLength = 0; + else + stringLength = lstrlen(buffer); + + OffsetRect(partRect, 0, 1); + SetTextColor(hdc, 0x000000); + DrawText(hdc, buffer, stringLength, partRect, + DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE); + + OffsetRect(partRect, 0, -1); + SetTextColor(hdc, 0xFFFFFF); + DrawText(hdc, buffer, stringLength, partRect, + DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE); + } + + SetTextColor(hdc, prevTextColor); + SelectFont(hdc, prevFont); +} + +BOOL +ListWidgetPaint_DrawItem(ListWidgetPaint *self, ListWidgetItem *item) +{ + RECT frameRect; + long frameHeight, frameWidth; + HDC hdc, sourceDC, bufferDC, targetDC; + WidgetStyle *style; + ListWidget *widget; + ListWidgetItemMetric *metrics; + RECT *partRect, paintRect; + HBITMAP bitmap, prevSourceBitmap; + + if (NULL == self || NULL == item) + return FALSE; + + hdc = self->hdc; + style = self->style; + widget = self->widget; + metrics = &self->itemMetrics; + sourceDC = self->sourceDC; + partRect = &self->partRect; + prevSourceBitmap = GetCurrentBitmap(sourceDC); + + + + if (FALSE == ListWidget_GetItemFrameRect(widget, item, metrics, &frameRect)) + return FALSE; + + frameWidth = RECTWIDTH(frameRect); + frameHeight = RECTHEIGHT(frameRect); + + if (FALSE == IntersectRect(&paintRect, &item->rect, self->paintRect)) + return TRUE; + + CopyRect(partRect, &paintRect); + + if (FALSE != BackBuffer_EnsureSizeEx(&widget->backBuffer, + RECTWIDTH(paintRect), RECTHEIGHT(paintRect), + RECTWIDTH(item->rect), RECTHEIGHT(item->rect))) + { + + bufferDC = BackBuffer_GetDC(&widget->backBuffer); + if (NULL != bufferDC) + { + SetViewportOrgEx(bufferDC, -paintRect.left, -paintRect.top, NULL); + SetTextColor(bufferDC, GetTextColor(hdc)); + SetBkColor(bufferDC, GetBkColor(hdc)); + SetBkMode(bufferDC, GetBkMode(hdc)); + SelectFont(bufferDC, GetCurrentFont(hdc)); + + targetDC = hdc; + hdc = bufferDC; + } + } + else + bufferDC = NULL; + + + if (FALSE != self->erase) + { + FillRect(hdc, partRect, style->backBrush); + } + + + if (FALSE != ListWidgetItem_IsHovered(item)) + { + bitmap = ListWidget_GetHoverBitmap(widget, style, self->hwnd, + frameWidth, frameHeight); + + Image_AlphaBlend(hdc, &frameRect, sourceDC, NULL, 255, bitmap, &paintRect, + AlphaBlend_Normal, NULL); + } + + if (FALSE != ListWidgetItem_IsSelected(item)) + { + bitmap = (GetFocus() == self->hwnd) ? + ListWidget_GetSelectBitmap(widget, style, self->hwnd, frameWidth, frameHeight) : + ListWidget_GetInactiveSelectBitmap(widget, style, self->hwnd, frameWidth, frameHeight); + + Image_AlphaBlend(hdc, &frameRect, sourceDC, NULL, 255, bitmap, &paintRect, + AlphaBlend_Normal, NULL); + } + + + bitmap = ListWidget_GetItemImage(widget, style, item); + if (NULL != bitmap) + { + partRect->left = frameRect.left + metrics->offsetLeft + metrics->imageOffsetLeft; + partRect->top = frameRect.top + metrics->offsetTop + metrics->imageOffsetTop; + partRect->right = frameRect.right - (metrics->offsetRight + metrics->imageOffsetRight); + partRect->bottom = frameRect.bottom - metrics->imageOffsetBottom; + + Image_AlphaBlend(hdc, partRect, sourceDC, NULL, 255, bitmap, &paintRect, + AlphaBlend_ScaleSource | AlphaBlend_AlignBottom, NULL); + } + + bitmap = ListWidget_GetConnectionImage(style, item->connection, + widget->connectionSize.cx, widget->connectionSize.cy); + if (NULL != bitmap && + FALSE != ListWidget_GetItemConnectionRect(widget, item, metrics, partRect)) + { + + Image_AlphaBlend(hdc, partRect, sourceDC, NULL, 255, bitmap, &paintRect, + AlphaBlend_AlignCenter | AlphaBlend_AlignBottom, NULL); + } + + ListWidgetPaint_DrawItemAction(self, hdc, item, item->activity); + + if (FALSE != ListWidgetItem_IsInteractive(item) && 0 != widget->commandsCount) + { + + HBITMAP commandBitmap; + size_t index; + RECT commandPaintRect; + + bitmap = NULL; + for(index = 0; index < widget->commandsCount; index++) + { + long offset; + BYTE sourceAlpha; + ListWidget_GetCommandRect(widget->commands[index], partRect); + OffsetRect(partRect, frameRect.left, frameRect.top); + + if (FALSE == IntersectRect(&commandPaintRect, partRect, &paintRect)) + continue; + + if (FALSE != ListWidget_GetCommandPressed(widget->commands[index])) + sourceAlpha = 200; + else if (FALSE != ListWidget_GetCommandDisabled(widget->commands[index])) + sourceAlpha = 32; + else + sourceAlpha = 255; + + if (NULL == item->activity && + FALSE != ListWidget_GetCommandPrimary(widget->commands[index])) + { + bitmap = ListWidget_GetLargeBadgeBitmap(widget, style, self->hwnd, + RECTWIDTH(*partRect), RECTHEIGHT(*partRect)); + + Image_AlphaBlend(hdc, partRect, sourceDC, NULL, 255, bitmap, &commandPaintRect, + AlphaBlend_AlignCenter, NULL); + + bitmap = NULL; + + offset = widget->primaryCommandSize.cy/6; + if (offset < 3) + offset = 3; + + InflateRect(partRect, -offset, -offset); + commandBitmap = ListWidget_GetCommandLargeBitmap(style, widget->commands[index], + RECTWIDTH(*partRect), RECTHEIGHT(*partRect)); + if (NULL == commandBitmap) + commandBitmap = ListWidget_GetUnknownCommandLargeBitmap(widget, style, + RECTWIDTH(*partRect), RECTHEIGHT(*partRect)); + } + else + { + if (NULL == bitmap) + { + bitmap = ListWidget_GetSmallBadgeBitmap(widget, style, self->hwnd, + RECTWIDTH(*partRect), RECTHEIGHT(*partRect)); + } + + Image_AlphaBlend(hdc, partRect, sourceDC, NULL, 255, bitmap, &commandPaintRect, + AlphaBlend_AlignCenter, NULL); + + offset = widget->secondaryCommandSize.cy/6; + if (offset < 3) + offset = 3; + + InflateRect(partRect, -offset, -offset); + commandBitmap = ListWidget_GetCommandSmallBitmap(style, widget->commands[index], + RECTWIDTH(*partRect), RECTHEIGHT(*partRect)); + + if (NULL == commandBitmap) + commandBitmap = ListWidget_GetUnknownCommandSmallBitmap(widget, style, + RECTWIDTH(*partRect), RECTHEIGHT(*partRect)); + } + + if (NULL != commandBitmap) + { + Image_AlphaBlend(hdc, partRect, sourceDC, NULL, sourceAlpha, commandBitmap, &commandPaintRect, + AlphaBlend_Normal, NULL); + } + + } + + } + + if (FALSE != ListWidget_GetItemSpacebarRect(widget, item, metrics, partRect)) + { + ListWidgetPaint_DrawSpacebar(self, hdc, + partRect->left, + partRect->top, + item->spaceTotal, item->spaceUsed); + } + + + if (FALSE == ListWidgetItem_IsTextEdited(item) && + FALSE == IS_STRING_EMPTY(item->title) && + FALSE != ListWidget_GetItemTitleRect(widget, item, metrics, TRUE, partRect)) + { + DrawText(hdc, item->title, -1, partRect, + DT_CENTER | DT_NOPREFIX | DT_WORDBREAK | DT_EDITCONTROL | DT_END_ELLIPSIS); + } + + SelectBitmap(sourceDC, prevSourceBitmap); + + if (NULL != bufferDC) + { + hdc = targetDC; + + SetViewportOrgEx(bufferDC, 0, 0, NULL); + + BackBuffer_Copy(&widget->backBuffer, hdc, + paintRect.left, paintRect.top, RECTWIDTH(paintRect), RECTHEIGHT(paintRect)); + } + + return TRUE; +} + +BOOL +ListWidgetPaint_DrawCategory(ListWidgetPaint *self, ListWidgetCategory *category) +{ + HDC hdc; + RECT elementRect, *partRect; + WidgetStyle *style; + ListWidgetCategoryMetric *metrics; + + if (NULL == self || NULL == category) + return FALSE; + + hdc = self->hdc; + style = self->style; + partRect = &self->partRect; + metrics = &self->categoryMetrics; + + if (FALSE == IntersectRect(partRect, &category->rect, self->paintRect)) + return TRUE; + + CopyRect(partRect, &category->rect); + partRect->right -= metrics->offsetRight; + if (FALSE != IntersectRect(partRect, partRect, self->paintRect)) + FillRect(hdc, partRect, WIDGETSTYLE_CATEGORY_BRUSH(style)); + + partRect->left = category->rect.right - metrics->offsetRight; + partRect->right = category->rect.right; + if (FALSE != IntersectRect(partRect, partRect, self->paintRect)) + FillRect(hdc, partRect, WIDGETSTYLE_BACK_BRUSH(style)); + + if (NULL != self->arrow.bitmap) + { + long limit; + SetRect(&elementRect, 0, 0, self->arrow.width, self->arrow.height); + OffsetRect(&elementRect, + 0, + (FALSE == category->collapsed) ? + self->arrow.expandedOffset : + self->arrow.collapsedOffset); + + + CopyRect(partRect, &category->rect); + partRect->left += metrics->offsetLeft; + partRect->right = partRect->left + self->arrow.width; + + limit = (RECTHEIGHT(category->rect) - self->arrow.height - metrics->offsetTop); + partRect->top += metrics->offsetTop + limit/2 + limit%2; + partRect->bottom = partRect->top + self->arrow.height; + + limit = category->rect.bottom - metrics->offsetBottom - metrics->lineHeight; + if (partRect->bottom > limit) + OffsetRect(partRect, 0, (limit - partRect->bottom)); + + limit = category->rect.top + metrics->offsetTop; + if (partRect->top < limit) + OffsetRect(partRect, 0, (limit - partRect->top)); + + Image_AlphaBlend(hdc, partRect, self->sourceDC, &elementRect, + 255, self->arrow.bitmap, self->paintRect, + AlphaBlend_Normal, NULL); + } + + CopyRect(&elementRect, &category->rect); + elementRect.left += metrics->offsetLeft + metrics->iconWidth + metrics->titleOffsetLeft; + elementRect.top += metrics->offsetTop; + elementRect.right -= metrics->offsetRight; + elementRect.bottom -= (metrics->offsetBottom + metrics->lineHeight + metrics->lineOffsetTop); + + if (FALSE != IntersectRect(partRect, &elementRect, self->paintRect)) + { + COLORREF prevTextColor = SetTextColor(hdc, WIDGETSTYLE_CATEGORY_TEXT_COLOR(style)); + HFONT prevFont = SelectFont(hdc, WIDGETSTYLE_CATEGORY_FONT(style)); + + if (NULL == category->countString) + { + size_t count, index; + wchar_t buffer[64] = {0}; + + count = 0; + index = category->groups.size(); + while(index--) + { + count += category->groups[index]->items.size(); + } + + if (SUCCEEDED(StringCchPrintf(buffer, ARRAYSIZE(buffer), L" (%u)", count))) + category->countString = String_Duplicate(buffer); + } + + if (-1 == category->titleWidth) + { + category->titleWidth = 0; + + if (FALSE == IS_STRING_EMPTY(category->title)) + { + SetRect(partRect, 0, 0, 0, RECTHEIGHT(elementRect)); + if (FALSE != DrawText(hdc, category->title, -1, partRect, + DT_CALCRECT | DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE)) + { + category->titleWidth = RECTWIDTH(*partRect); + } + } + } + + if (-1 == category->countWidth) + { + category->countWidth = 0; + if (FALSE == IS_STRING_EMPTY(category->countString)) + { + SetRect(partRect, 0, 0, 0, RECTHEIGHT(elementRect)); + if (FALSE != DrawText(hdc, category->countString, -1, partRect, + DT_CALCRECT | DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE)) + { + category->countWidth = RECTWIDTH(*partRect); + } + } + } + + if (0 != category->titleWidth) + { + CopyRect(partRect, &elementRect); + + if (partRect->right < (partRect->left + category->titleWidth + category->countWidth)) + partRect->right = partRect->right - category->countWidth; + + if (partRect->right > partRect->left) + { + DrawText(hdc, category->title, -1, partRect, + DT_LEFT | DT_BOTTOM | DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS); + } + } + + if (0 != category->countWidth) + { + CopyRect(partRect, &elementRect); + partRect->left += category->titleWidth; + + if (partRect->left > (partRect->right - category->countWidth)) + { + partRect->left = partRect->right - category->countWidth; + if (partRect->left < elementRect.left) + partRect->left = elementRect.left; + } + + if (partRect->right > partRect->left) + { + DrawText(hdc, category->countString, -1, partRect, + DT_LEFT | DT_BOTTOM | DT_NOPREFIX | DT_SINGLELINE); + } + } + + SetTextColor(hdc, prevTextColor); + SelectFont(hdc, prevFont); + } + + if (0 != metrics->lineHeight) + { + CopyRect(partRect, &category->rect); + partRect->right -= metrics->offsetRight; + partRect->bottom -= metrics->offsetBottom; + partRect->top = partRect->bottom - metrics->lineHeight; + + if (FALSE != IntersectRect(partRect, partRect, self->paintRect)) + { + COLORREF prevBackColor; + + prevBackColor = SetBkColor(hdc, WIDGETSTYLE_CATEGORY_LINE_COLOR(style)); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, partRect, NULL, 0, NULL); + + SetBkColor(hdc, prevBackColor); + } + } + + return TRUE; +} + +BOOL +ListWidgetPaint_DrawEmptyCategoryText(ListWidgetPaint *self, ListWidgetCategory *category) +{ + HDC hdc; + WidgetStyle *style; + RECT *partRect; + BOOL result; + COLORREF prevTextColor; + + if (NULL == self || NULL == category) + return FALSE; + + hdc = self->hdc; + style = self->style; + partRect = &self->partRect; + + + if (FALSE == IntersectRect(partRect, self->paintRect, &category->emptyTextRect)) + return TRUE; + + if (FALSE != self->erase) + { + FillRect(hdc, partRect, style->backBrush); + } + + if (FALSE != IS_STRING_EMPTY(category->emptyText)) + return TRUE; + + prevTextColor = SetTextColor(hdc, WIDGETSTYLE_CATEGORY_EMPTY_TEXT_COLOR(style)); + + result = DrawText(hdc, category->emptyText, -1, &category->emptyTextRect, + DT_CENTER | DT_TOP | DT_NOPREFIX | DT_WORDBREAK | DT_EDITCONTROL); + + SetTextColor(hdc, prevTextColor); + return result; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/listWidgetTooltip.cpp b/Src/Plugins/Library/ml_devices/listWidgetTooltip.cpp new file mode 100644 index 00000000..0bdb8dc4 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/listWidgetTooltip.cpp @@ -0,0 +1,592 @@ +#include "main.h" +#include "./listWidgetInternal.h" + +#include <strsafe.h> + +#define TOOLTIP_MARGIN_LEFT_DLU 3 +#define TOOLTIP_MARGIN_TOP_DLU 1 +#define TOOLTIP_MARGIN_RIGHT_DLU 3 +#define TOOLTIP_MARGIN_BOTTOM_DLU 1 + +#define TOOLTIP_DELAY_INITIAL 1000 +#define TOOLTIP_DELAY_RESHOW 400 + +static ATOM LISTWIDGETTOOLTIP_PROP = 0; + +static LRESULT WINAPI +ListWidgetTooltip_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +typedef struct ListWidgetTooltip +{ + HWND window; + HWND owner; + BOOL active; + POINT position; + wchar_t *buffer; + ListWidgetItem *item; + ListWidgetItemPart part; + RECT partRect; + BOOL blockLocationChange; + WNDPROC originalProc; + DWORD showTime; +} ListWidgetTooltip; + + +static HWND +ListWidget_TooltipCreateWindow(HWND owner, WNDPROC *originalProc, HANDLE windowProperty) +{ + HWND hwnd; + TOOLINFO ti; + + hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT | WS_EX_LAYERED, + TOOLTIPS_CLASS, NULL, WS_CLIPSIBLINGS | WS_POPUP | TTS_NOANIMATE | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + owner, NULL, NULL, NULL); + + if ( hwnd == NULL ) + return NULL; + + MLSkinWindow2(Plugin_GetLibraryWindow(), hwnd, SKINNEDWND_TYPE_TOOLTIP, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + + if (NULL != originalProc) + { + if (0 == LISTWIDGETTOOLTIP_PROP) + LISTWIDGETTOOLTIP_PROP = GlobalAddAtom(TEXT("ListWidgetTooltipProp")); + + if (0 != LISTWIDGETTOOLTIP_PROP) + { + *originalProc = (WNDPROC)(LONG_PTR)SetWindowLongPtr(hwnd, GWLP_WNDPROC, + (LONGX86)(LONG_PTR)ListWidgetTooltip_WindowProc); + if (NULL != *originalProc && + FALSE == SetProp(hwnd, MAKEINTATOM(LISTWIDGETTOOLTIP_PROP), windowProperty)) + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)*originalProc); + *originalProc = NULL; + } + } + else + *originalProc = NULL; + } + + SendMessage(hwnd, CCM_SETVERSION, 6, 0L); + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); + + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.hwnd = owner; + ti.lpszText = LPSTR_TEXTCALLBACK; + ti.uFlags = 0/*TTF_TRACK | TTF_ABSOLUTE*/; + + SendMessage(hwnd, TTM_ADDTOOL, 0, (LPARAM)&ti); + + return hwnd; +} + +ListWidgetTooltip* +ListWidget_TooltipCreate(HWND hwnd) +{ + ListWidgetTooltip *tooltip; + + tooltip = (ListWidgetTooltip*)malloc(sizeof(ListWidgetTooltip)); + if (NULL == tooltip) + return FALSE; + + ZeroMemory(tooltip, sizeof(ListWidgetTooltip)); + + tooltip->window = ListWidget_TooltipCreateWindow(hwnd, &tooltip->originalProc, tooltip); + if (NULL == tooltip->window) + { + ListWidget_TooltipDestroy(tooltip); + return NULL; + } + + tooltip->owner = hwnd; + ListWidget_TooltipFontChanged(tooltip); + + return tooltip; +} + +void +ListWidget_TooltipDestroy(ListWidgetTooltip *tooltip) +{ + if (NULL != tooltip) + { + if (NULL != tooltip->window) + DestroyWindow(tooltip->window); + + String_Free(tooltip->buffer); + + free(tooltip); + } +} + +void +ListWidget_TooltipFontChanged(ListWidgetTooltip *tooltip) +{ + WidgetStyle *style; + RECT marginsRect; + HDC hdc; + long maxWidth; + + if (NULL == tooltip) + return; + + if (NULL == tooltip->window) + { + // attempt to recover... + tooltip->window = ListWidget_TooltipCreateWindow(tooltip->owner, &tooltip->originalProc, tooltip); + if (NULL == tooltip->window) + return; + + tooltip->active = FALSE; + tooltip->part = ListWidgetItemPart_None; + SetRectEmpty(&tooltip->partRect); + } + + + style = WIDGET_GET_STYLE(tooltip->owner); + if (NULL == style) + return; + + hdc = GetDCEx(tooltip->window, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT font = (HFONT)SendMessage(tooltip->window, WM_GETFONT, 0, 0L); + HFONT prevFont = SelectFont(hdc, font); + + maxWidth = Graphics_GetAveStrWidth(hdc, 36); + + SelectFont(hdc, prevFont); + ReleaseDC(tooltip->window, hdc); + } + else + maxWidth = 100; + + + SendMessage(tooltip->window, TTM_SETMAXTIPWIDTH, 0, (LPARAM)maxWidth); + + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(marginsRect.left, style, TOOLTIP_MARGIN_LEFT_DLU, 3); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(marginsRect.top, style, TOOLTIP_MARGIN_TOP_DLU, 2); + WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(marginsRect.right, style, TOOLTIP_MARGIN_RIGHT_DLU, 3); + WIDGETSTYLE_DLU_TO_VERT_PX_MIN(marginsRect.bottom, style, TOOLTIP_MARGIN_BOTTOM_DLU, 2); + + SendMessage(tooltip->window, TTM_SETMARGIN, 0, (LPARAM)&marginsRect); +} + +void +ListWidget_TooltipRelayMouseMessage(ListWidgetTooltip *tooltip, unsigned int message, unsigned int vKeys, const POINT *cursor) +{ + if (NULL != tooltip && + NULL != tooltip->window && + FALSE != tooltip->active) + { + MSG msg; + + msg.hwnd = tooltip->owner; + msg.message = message; + msg.wParam = (WPARAM)vKeys; + msg.lParam = MAKELPARAM(cursor->x, cursor->y); + + SendMessage(tooltip->window, TTM_RELAYEVENT, 0, (LPARAM)&msg); + } +} + +void +ListWidget_TooltipHide(ListWidgetTooltip *tooltip) +{ + if (NULL == tooltip || + NULL == tooltip->window || + FALSE == tooltip->active) + { + return; + } + + tooltip->active = FALSE; + tooltip->part = ListWidgetItemPart_None; + SetRectEmpty(&tooltip->partRect); + + SendMessage(tooltip->window, TTM_ACTIVATE, FALSE, 0); +} + +BOOL +ListWidget_TooltipActivate(ListWidgetTooltip *tooltip, const RECT *rect) +{ + TOOLINFO ti; + POINT origin; + + if (NULL == tooltip || + NULL == tooltip->window) + { + return FALSE; + } + + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.hwnd = tooltip->owner; + ti.uId = 0; + + + if (FALSE == SendMessage(tooltip->window, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) + return FALSE; + + if (FALSE != tooltip->active) + { + tooltip->active = FALSE; + SendMessage(tooltip->window, TTM_ACTIVATE, FALSE, 0); + } + else + SendMessage(tooltip->window, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELPARAM(TOOLTIP_DELAY_INITIAL, 0)); + + + CopyRect(&ti.rect, rect); + if (FALSE != ListWidget_GetViewOrigin(tooltip->owner, &origin)) + OffsetRect(&ti.rect, origin.x, origin.y); + + ti.lParam = NULL; + ti.lpszText = LPSTR_TEXTCALLBACK; + + SendMessage(tooltip->window, TTM_SETTOOLINFO, 0, (LPARAM)&ti); + + + if (FALSE == tooltip->active) + { + KillTimer(tooltip->window, 4); + SendMessage(tooltip->window, TTM_ACTIVATE, TRUE, 0); + tooltip->active = TRUE; + } + + return TRUE; +} + + +BOOL +ListWidget_TooltipUpdate(ListWidgetTooltip *tooltip, ListWidgetItem *item, + ListWidgetItemPart part, const RECT *partRect) +{ + BOOL tooltipValid; + + if (NULL == tooltip) + return FALSE; + + if(FALSE == tooltip->active && NULL == item) + return FALSE; + + if (0 != (ListWidgetItemPart_Activity & part)) + { + part &= ~ListWidgetItemPart_Activity; + part |= ListWidgetItemPart_Frame; + } + + tooltipValid = (NULL != item && ListWidgetItemPart_None != part); + if (tooltip->item == item && + tooltip->part == part && + (FALSE != (FALSE != tooltipValid) ? + EqualRect(&tooltip->partRect, partRect) : + IsRectEmpty(&tooltip->partRect))) + { + + return FALSE; + } + + tooltip->item = item; + tooltip->part = part; + + if (FALSE != tooltipValid) + CopyRect(&tooltip->partRect, partRect); + else + SetRectEmpty(&tooltip->partRect); + + if (FALSE == tooltipValid) + { + ListWidget_TooltipHide(tooltip); + return FALSE; + } + + return ListWidget_TooltipActivate(tooltip, &tooltip->partRect); +} + +static BOOL +ListWidget_TooltipFormatTip(ListWidget *self, HWND hwnd, + const RECT *rect, wchar_t *buffer, size_t bufferMax) +{ + WidgetStyle *style; + ListWidgetItem *item; + ListWidgetItemPart part; + ListWidgetItemMetric metrics; + RECT partRect; + POINT pt, origin; + + if (NULL == self || NULL == rect) + return FALSE; + + style = WIDGET_GET_STYLE(hwnd); + if (NULL == style) + return FALSE; + + if (FALSE == ListWidget_GetItemMetrics(style, &metrics)) + return FALSE; + + if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin)) + { + origin.x = 0; + origin.y = 0; + } + + pt.x = (rect->left + (rect->right - rect->left)/2) - origin.x; + pt.y = (rect->top + (rect->bottom - rect->top)/2) - origin.y; + + + item = ListWidget_GetItemFromPoint(self, pt); + if (NULL == item) + return FALSE; + + + part = ListWidgetItemPart_Frame | + ListWidgetItemPart_Command | + ListWidgetItemPart_Spacebar | + ListWidgetItemPart_Title; + + part = ListWidget_GetItemPartFromPoint(self, item, &metrics, pt, part, &partRect); + switch(part) + { + case ListWidgetItemPart_Command: + CopyRect(&partRect, rect); + OffsetRect(&partRect, -origin.x - item->rect.left, -origin.y - item->rect.top); + return ListWidget_FormatItemCommandTip(self, item, &partRect, buffer, bufferMax); + + case ListWidgetItemPart_Activity: + case ListWidgetItemPart_Frame: + return ListWidget_FormatItemTip(self, item, buffer, bufferMax); + + case ListWidgetItemPart_Spacebar: + return ListWidget_FormatItemSpaceTip(self, item, buffer, bufferMax); + + case ListWidgetItemPart_Title: + return ListWidget_FormatItemTitleTip(self, item, buffer, bufferMax); + } + + return FALSE; +} + +static void +ListWidget_TooltipGetDispInfo(ListWidget *self, ListWidgetTooltip *tooltip, NMTTDISPINFO *dispInfo) +{ + TOOLINFO ti; + + if (NULL == dispInfo) + return; + + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.hwnd = tooltip->owner; + ti.uId = dispInfo->hdr.idFrom; + + String_Free(tooltip->buffer); + tooltip->buffer = NULL; + + if (FALSE != SendMessage(dispInfo->hdr.hwndFrom, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) + { + wchar_t buffer[4096] = {0}; + if (FALSE != ListWidget_TooltipFormatTip(self, tooltip->owner, &ti.rect, buffer, ARRAYSIZE(buffer))) + tooltip->buffer = String_Duplicate(buffer); + } + + dispInfo->lpszText = tooltip->buffer; + dispInfo->szText[0] = L'\0'; + dispInfo->hinst = NULL; + dispInfo->uFlags = TTF_DI_SETITEM; +} + +BOOL +ListWidget_TooltipProcessNotification(ListWidget *self, ListWidgetTooltip *tooltip, NMHDR *pnmh, LRESULT *result) +{ + if (NULL == tooltip || + NULL == pnmh || + pnmh->hwndFrom != tooltip->window) + { + return FALSE; + } + + switch(pnmh->code) + { + case TTN_GETDISPINFO: + ListWidget_TooltipGetDispInfo(self, tooltip, (NMTTDISPINFO*)pnmh); + break; + + case TTN_SHOW: + if (0 == (WS_VISIBLE & GetWindowStyle(tooltip->window))) + tooltip->showTime = GetTickCount(); + + if (FALSE != tooltip->active) + { + SendMessage(tooltip->window, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELPARAM(TOOLTIP_DELAY_RESHOW, 0)); + } + break; + } + + return TRUE; +} + +ListWidgetItem * +ListWidget_TooltipGetCurrent(ListWidgetTooltip *tooltip, ListWidgetItemPart *part, RECT *partRect) +{ + if (NULL == tooltip || + FALSE == tooltip->active || + NULL == tooltip->item) + { + return NULL; + } + + if (NULL != part) + *part = tooltip->part; + + if (NULL != partRect) + CopyRect(partRect, &tooltip->partRect); + + return tooltip->item; +} + +BOOL +ListWidget_TooltipGetChanged(ListWidgetTooltip *tooltip, ListWidgetItem *item, + ListWidgetItemPart part, const RECT *partRect) +{ + if (NULL == tooltip) + return FALSE; + + if (tooltip->item != item) + return TRUE; + + if (NULL == item) + return FALSE; + + if (tooltip->part != part) + return TRUE; + + if (FALSE == EqualRect(&tooltip->partRect, partRect)) + return TRUE; + + return FALSE; +} + +BOOL +ListWidget_TooltipUpdateText(ListWidget *self, ListWidgetTooltip *tooltip, ListWidgetItem *item, TooltipUpdateReason reason) +{ + TOOLINFO ti; + ListWidgetItemPart mask; + unsigned int windowStyle; + unsigned long showTimeout, currentTime; + + if (NULL == self || + NULL == tooltip || + NULL == tooltip->active || + NULL == tooltip->window) + { + return FALSE; + } + + if (NULL != item && tooltip->item != item) + return FALSE; + + windowStyle = GetWindowStyle(tooltip->window); + if (0 == (WS_VISIBLE & windowStyle)) + return FALSE; + + showTimeout = (unsigned long)SendMessage(tooltip->window, TTM_GETDELAYTIME, (WPARAM)TTDT_AUTOPOP, 0L); + currentTime = GetTickCount(); + if (currentTime < tooltip->showTime) + return FALSE; + + currentTime -= tooltip->showTime; + if (showTimeout > (currentTime + 10)) + showTimeout -= currentTime; + else + return FALSE; + + KillTimer(tooltip->window, 4); + + switch(reason) + { + case Tooltip_DeviceTitleChanged: + case Tooltip_DeviceModelChanged: + case Tooltip_DeviceStatusChanged: + mask = ListWidgetItemPart_Frame | + ListWidgetItemPart_Activity | + ListWidgetItemPart_Title; + + if (0 == (mask & tooltip->part)) + return FALSE; + + break; + + case Tooltip_DeviceSpaceChanged: + mask = ListWidgetItemPart_Frame | + ListWidgetItemPart_Activity | + ListWidgetItemPart_Spacebar; + + if (0 == (mask & tooltip->part)) + return FALSE; + + break; + + /*case Tooltip_DeviceActivityChanged: + mask = ListWidgetItemPart_Frame | + ListWidgetItemPart_Activity; + + if (0 == (mask & tooltip->part)) + return FALSE; + + break;*/ + } + + + ZeroMemory(&ti, sizeof(ti)); + ti.cbSize = sizeof(ti); + ti.hwnd = tooltip->owner; + ti.uId = 0; + ti.lpszText = LPSTR_TEXTCALLBACK; + + tooltip->blockLocationChange = TRUE; + SendMessage(tooltip->window, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti); + tooltip->blockLocationChange = FALSE; + + SetTimer(tooltip->window, 4, showTimeout, 0); + return TRUE; +} + +static LRESULT WINAPI +ListWidgetTooltip_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + ListWidgetTooltip *tooltip; + + tooltip = (ListWidgetTooltip*)GetProp(hwnd, MAKEINTATOM(LISTWIDGETTOOLTIP_PROP)); + if (NULL == tooltip || + NULL == tooltip->originalProc) + { + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + switch(uMsg) + { + case WM_DESTROY: + RemoveProp(hwnd, MAKEINTATOM(LISTWIDGETTOOLTIP_PROP)); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)tooltip->originalProc); + CallWindowProc(tooltip->originalProc, hwnd, uMsg, wParam, lParam); + tooltip->originalProc = NULL; + tooltip->window = NULL; + break; + case WM_WINDOWPOSCHANGING: + if (FALSE != tooltip->blockLocationChange) + { + WINDOWPOS *pwp; + pwp = (WINDOWPOS*)lParam; + if (NULL != pwp) + { + pwp->flags |= SWP_NOMOVE; + } + } + break; + } + + return CallWindowProc(tooltip->originalProc, hwnd, uMsg, wParam, lParam); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/local_menu.cpp b/Src/Plugins/Library/ml_devices/local_menu.cpp new file mode 100644 index 00000000..446552f1 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/local_menu.cpp @@ -0,0 +1,183 @@ +#include "main.h" +#include "./local_menu.h" + +unsigned int +Menu_InsertDeviceItems(HMENU menu, int position, unsigned int baseId, + ifc_device *device, DeviceCommandContext context) +{ + unsigned int count, separator; + MENUITEMINFO itemInfo = {0}; + wchar_t itemName[512] = {0}; + + ifc_devicecommand *commandInfo; + ifc_devicesupportedcommandenum *enumerator; + ifc_devicesupportedcommand *command; + DeviceCommandFlags commandFlags; + + if (NULL == device || NULL == menu) + return 0; + + if (FAILED(device->EnumerateCommands(&enumerator, context))) + return 0; + + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.fMask = MIIM_DATA | MIIM_ID | MIIM_FTYPE | MIIM_STATE | MIIM_STRING; + + count = 0; + separator = 0; + + while(S_OK == enumerator->Next(&command, 1, NULL)) + { + if(SUCCEEDED(command->GetFlags(&commandFlags))) + { + if (0 != (DeviceCommandFlag_Group & commandFlags) && + separator != count) + { + itemInfo.fType = MFT_SEPARATOR; + itemInfo.fState = MFS_ENABLED; + itemInfo.wID = 0xFFFE; + itemInfo.dwItemData = NULL; + if (0 != InsertMenuItem(menu, position + count, TRUE, &itemInfo)) + { + count++; + separator = count; + } + } + if (0 == (DeviceCommandFlag_Hidden & commandFlags)) + { + if (S_OK == WASABI_API_DEVICES->CommandFind(command->GetName(), &commandInfo)) + { + if (SUCCEEDED(commandInfo->GetDisplayName(itemName, ARRAYSIZE(itemName)))) + { + itemInfo.dwItemData = (ULONG_PTR)AnsiString_Duplicate(command->GetName()); + if (NULL != itemInfo.dwItemData) + { + itemInfo.fType = MFT_STRING; + itemInfo.dwTypeData = itemName; + itemInfo.fState = 0; + itemInfo.wID = baseId + count; + + if (0 == (DeviceCommandFlag_Disabled & commandFlags)) + itemInfo.fState |= MFS_ENABLED; + else + itemInfo.fState |= (MFS_DISABLED | MFS_GRAYED); + + if (0 != (DeviceCommandFlag_Primary & commandFlags)) + itemInfo.fState |= MFS_DEFAULT; + + if (0 != InsertMenuItem(menu, position + count, TRUE, &itemInfo)) + count++; + else + AnsiString_Free((char*)itemInfo.dwItemData); + } + + } + commandInfo->Release(); + } + } + } + command->Release(); + } + + + enumerator->Release(); + + return count; +} + +unsigned int +Menu_FreeItemData(HMENU menu, unsigned int start, int count) +{ + unsigned int processed; + MENUITEMINFO itemInfo; + + if (NULL == menu) + return 0; + + if (count < 0 ) + count = GetMenuItemCount(menu); + + if (start > (unsigned int)count) + return 0; + + count -= start; + processed = 0; + + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.fMask = MIIM_DATA; + + while(count-- && + FALSE != GetMenuItemInfo(menu, start + processed, TRUE, &itemInfo)) + { + AnsiString_Free((char*)itemInfo.dwItemData); + processed++; + } + + return processed; +} + +ULONG_PTR +Menu_GetItemData(HMENU menu, unsigned int item, BOOL byPosition) +{ + MENUITEMINFO itemInfo; + + if (NULL == menu) + return 0; + + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.fMask = MIIM_DATA; + + if (FALSE != GetMenuItemInfo(menu, item, byPosition, &itemInfo)) + return itemInfo.dwItemData; + + return 0; +} + +unsigned int +Menu_FindItemByData(HMENU menu, Menu_FindItemByDataCb callback, void *user) +{ + int index, count; + MENUITEMINFO itemInfo; + + if (NULL == menu || NULL == callback) + return -1; + + count = GetMenuItemCount(menu); + if (0 == count) + return -1; + + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.fMask = MIIM_DATA | MIIM_ID; + + for (index = 0; index < count; index++) + { + if (FALSE != GetMenuItemInfo(menu, index, TRUE, &itemInfo) && + FALSE != callback(itemInfo.dwItemData, user)) + { + return itemInfo.wID; + } + } + + return -1; + +} + +static BOOL +Menu_FindItemByAnsiStringDataCb(ULONG_PTR param, void *user) +{ + const char *string1, *string2; + + string1 = (const char*)param; + string2 = (const char*)user; + + if (NULL == string1 || NULL == string2) + return (string1 == string2); + + return (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, string1, -1, string2, -1)); +} + +unsigned int +Menu_FindItemByAnsiStringData(HMENU menu, const char *string) +{ + return Menu_FindItemByData(menu, Menu_FindItemByAnsiStringDataCb, (void*)string); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/local_menu.h b/Src/Plugins/Library/ml_devices/local_menu.h new file mode 100644 index 00000000..1a94c209 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/local_menu.h @@ -0,0 +1,48 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_MENU_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_MENU_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +int +Menu_TrackSkinnedPopup(HMENU menu, + unsigned int flags, + int x, + int y, + HWND hwnd, + LPTPMPARAMS lptpm); + +unsigned int +Menu_InsertDeviceItems(HMENU menu, + int position, + unsigned int baseId, + ifc_device *device, + DeviceCommandContext context); + +unsigned int +Menu_FreeItemData(HMENU menu, + unsigned int start, + int count); + +ULONG_PTR +Menu_GetItemData(HMENU menu, + unsigned int item, + BOOL byPosition); + + +typedef BOOL (*Menu_FindItemByDataCb)(ULONG_PTR param, void *user); + +unsigned int +Menu_FindItemByData(HMENU menu, + Menu_FindItemByDataCb callback, + void *user); + +unsigned int +Menu_FindItemByAnsiStringData(HMENU menu, + const char *string); + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_MENU_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/main.cpp b/Src/Plugins/Library/ml_devices/main.cpp new file mode 100644 index 00000000..da4d49da --- /dev/null +++ b/Src/Plugins/Library/ml_devices/main.cpp @@ -0,0 +1,5 @@ +#include <initguid.h> +#include "main.h" +#define _ML_HEADER_IMPMLEMENT +#include "../../General/gen_ml/ml_ipc_0313.h" +#undef _ML_HEADER_IMPMLEMENT diff --git a/Src/Plugins/Library/ml_devices/main.h b/Src/Plugins/Library/ml_devices/main.h new file mode 100644 index 00000000..5fb70520 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/main.h @@ -0,0 +1,50 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_MAIN_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_MAIN_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +// #define _CRTDBG_MAP_ALLOC + +#include <wtypes.h> + +#include "./common.h" +#include "./strings.h" +#include "./plugin.h" +#include "./local_menu.h" +#include "./graphics.h" +#include "./image.h" +#include "./imageCache.h" +#include "./fillRegion.h" +#include "./backBuffer.h" +#include "./wasabi.h" +#include "./config.h" +#include "./navigation.h" +#include "./navigationIcons.h" +#include "./managerView.h" +#include "./statusBar.h" +#include "./widgetHost.h" +#include "./widgetStyle.h" +#include "./widget.h" +#include "./infoWidget.h" +#include "./welcomeWidget.h" +#include "./listWidget.h" +#include "./deviceManagerHandler.h" +#include "./deviceHandler.h" +#include "./eventRelay.h" +#include "./deviceCommands.h" +#include "./resource.h" +#include "./embeddedEditor.h" + +#include "../../General/gen_ml/ml.h" +#include "../../General/gen_ml/menu.h" +#include "../../General/gen_ml/ml_ipc_0313.h" +#include "../winamp/wa_ipc.h" +#include "../winamp/wa_dlg.h" +#include "../nu/trace.h" + +#include <shlwapi.h> +#include <math.h> + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_MAIN_HEADER diff --git a/Src/Plugins/Library/ml_devices/managerView.cpp b/Src/Plugins/Library/ml_devices/managerView.cpp new file mode 100644 index 00000000..bba9679b --- /dev/null +++ b/Src/Plugins/Library/ml_devices/managerView.cpp @@ -0,0 +1,994 @@ +#include "main.h" +#include "./managerView.h" + +#define MANAGERVIEW_PROP L"NullsoftDevicesManagerViewProp" + +#define VIEW_OFFSET_LEFT_PX 0 +#define VIEW_OFFSET_TOP_PX 0 +#define VIEW_OFFSET_RIGHT_PX 2 +#define VIEW_OFFSET_BOTTOM_PX -1 + +#define DISCOVER_BUTTON_MIN_HEIGHT_PX 18 +#define DISCOVER_BUTTON_MIN_WIDTH_PX 72 +#define DISCOVER_BUTTON_EXTRA_SPACE_DLU 8 +#define DISCOVER_BUTTON_SPACING_RIGHT_DLU 6 + +#define ZOOM_SLIDER_SPACING_LEFT_DLU 6 + +#define BOTTOM_BAR_OFFSET_TOP_DLU 2 + + +#define MANAGERVIEW_WIDGET_ID 10000 +#define MANAGERVIEW_STATUSBAR_ID 10001 + +static ATOM MANAGERVIEW_ATOM = 0; + + +typedef +enum ManagerViewState +{ + MANAGERVIEW_STATE_FROZEN_UI = (1 << 0), +} WelcomeViewState; +DEFINE_ENUM_FLAG_OPERATORS(ManagerViewState); + +#define MANAGERVIEW_IS_FROZEN(_view) (0 != (MANAGERVIEW_STATE_FROZEN_UI & (_view)->state)) +#define MANAGERVIEW_FREEZE(_view) (((_view)->state) |= MANAGERVIEW_STATE_FROZEN_UI) +#define MANAGERVIEW_THAW(_view) (((_view)->state) &= ~MANAGERVIEW_STATE_FROZEN_UI) + +#define MANAGERVIEW_DLU_TO_HORZ_PX(_view, _dlu) MulDiv((_dlu), (_view)->unitSize.cx, 4) +#define MANAGERVIEW_DLU_TO_VERT_PX(_view, _dlu) MulDiv((_dlu), (_view)->unitSize.cy, 8) + +#define MANAGERVIEW_REGISTER_WIDGET(_widgetWindow) (SetWindowLongPtrW((_widgetWindow), GWLP_ID, MANAGERVIEW_WIDGET_ID)) +#define MANAGERVIEW_WIDGET(_viewWindow) (GetDlgItem((_viewWindow), MANAGERVIEW_WIDGET_ID)) +#define MANAGERVIEW_STATUS_BAR(_viewWindow) (GetDlgItem((_viewWindow), MANAGERVIEW_STATUSBAR_ID)) +#define MANAGERVIEW_DISCOVER_BUTTON(_viewWindow) (GetDlgItem((_viewWindow), IDC_BUTTON_DISCOVER)) +#define MANAGERVIEW_ZOOM_SLIDER(_viewWindow) (GetDlgItem((_viewWindow), IDC_SLIDER_ZOOM)) + +typedef struct ManagerView +{ + ManagerViewState state; + WidgetStyle widgetStyle; + HFONT font; + HFONT systemFont; + COLORREF backColor; + COLORREF textColor; + COLORREF borderColor; + HBRUSH backBrush; + SIZE unitSize; + BOOL devicesPresent; + size_t deviceHandler; + HRGN updateRegion; + POINT updateOffset; + unsigned int discoveryStatus; +} ManagerView; + +#define MANAGERVIEW(_hwnd) ((ManagerView*)GetPropW((_hwnd), MAKEINTATOM(MANAGERVIEW_ATOM))) +#define MANAGERVIEW_RET_VOID(_view, _hwnd) { (_view) = MANAGERVIEW((_hwnd)); if (NULL == (_view)) return; } +#define MANAGERVIEW_RET_VAL(_view, _hwnd, _error) { (_view) = MANAGERVIEW((_hwnd)); if (NULL == (_view)) return (_error); } + +static INT_PTR CALLBACK +ManagerView_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +static void CALLBACK +ManagerView_PluginUnloadCb() +{ + if (0 != MANAGERVIEW_ATOM) + { + GlobalDeleteAtom(MANAGERVIEW_ATOM); + MANAGERVIEW_ATOM = 0; + } +} + +HWND ManagerView_CreateWindow(HWND parentWindow) +{ + if (0 == MANAGERVIEW_ATOM) + { + MANAGERVIEW_ATOM = GlobalAddAtom(MANAGERVIEW_PROP); + if (0 == MANAGERVIEW_ATOM) + return NULL; + + Plugin_RegisterUnloadCallback(ManagerView_PluginUnloadCb); + } + + HWND hwnd = WASABI_API_CREATEDIALOGPARAMW((INT_PTR)IDD_MANAGER_VIEW, parentWindow, + ManagerView_DialogProc, (LPARAM)0L); + + return hwnd; +} + +static void +ManagerView_Layout(HWND hwnd, BOOL redraw) +{ + ManagerView *self = NULL; + RECT clientRect, elementRect; + LONG bottomBarHeight = 0, buttonWidth = 0, sliderWidth = 0; + + MANAGERVIEW_RET_VOID(self, hwnd); + + GetClientRect(hwnd, &clientRect); + clientRect.left += VIEW_OFFSET_LEFT_PX; + clientRect.top += VIEW_OFFSET_TOP_PX; + clientRect.right -= VIEW_OFFSET_RIGHT_PX; + clientRect.bottom -= VIEW_OFFSET_BOTTOM_PX; + + HDWP hdwp = BeginDeferWindowPos(4); + if (NULL == hdwp) + return; + + UINT swpFlags = SWP_NOACTIVATE | SWP_NOZORDER; + if (FALSE == redraw) + swpFlags |= SWP_NOREDRAW; + + HWND buttonWindow = MANAGERVIEW_DISCOVER_BUTTON(hwnd); + if (NULL != buttonWindow) + { + if (FALSE != GetWindowRect(buttonWindow, &elementRect)) + { + wchar_t buffer[128] = {0}; + GetWindowText(buttonWindow, buffer, ARRAYSIZE(buffer)); + LRESULT idealSize = MLSkinnedButton_GetIdealSize(buttonWindow, buffer); + + hdwp = DeferWindowPos(hdwp, buttonWindow, NULL, clientRect.left + 1, + clientRect.bottom - WASABI_API_APP->getScaleY(HIWORD(idealSize)) - WASABI_API_APP->getScaleY(1), + RECTWIDTH(elementRect), WASABI_API_APP->getScaleY(HIWORD(idealSize)), swpFlags); + + bottomBarHeight = WASABI_API_APP->getScaleY(HIWORD(idealSize)); + buttonWidth = RECTWIDTH(elementRect); + } + } + + HWND sliderWindow = MANAGERVIEW_ZOOM_SLIDER(hwnd); + if (NULL != sliderWindow) + { + if (0 != (WS_VISIBLE & GetWindowStyle(sliderWindow)) && + FALSE != GetWindowRect(sliderWindow, &elementRect)) + { + hdwp = DeferWindowPos(hdwp, sliderWindow, NULL, clientRect.right - RECTWIDTH(elementRect), + clientRect.bottom - RECTHEIGHT(elementRect), + RECTWIDTH(elementRect), RECTHEIGHT(elementRect), swpFlags); + sliderWidth = RECTWIDTH(elementRect); + } + } + + HWND statusBar = MANAGERVIEW_STATUS_BAR(hwnd); + if (NULL != statusBar) + { + long statusBarHeight = 0; + CopyRect(&elementRect, &clientRect); + + if (0 != buttonWidth) + { + elementRect.left += buttonWidth; + elementRect.left += MANAGERVIEW_DLU_TO_HORZ_PX(self, DISCOVER_BUTTON_SPACING_RIGHT_DLU); + if (elementRect.left > clientRect.right) + elementRect.left = clientRect.right; + } + + if (0 != sliderWidth) + { + elementRect.right -= sliderWidth; + elementRect.right -= MANAGERVIEW_DLU_TO_HORZ_PX(self, ZOOM_SLIDER_SPACING_LEFT_DLU); + if (elementRect.left > elementRect.right) + elementRect.right = elementRect.left; + } + + statusBarHeight = STATUSBAR_GET_IDEAL_HEIGHT(statusBar); + if (statusBarHeight > bottomBarHeight) + statusBarHeight = 0; + + elementRect.top = elementRect.bottom - statusBarHeight - WASABI_API_APP->getScaleY(3); + elementRect.bottom = elementRect.top + statusBarHeight; + + hdwp = DeferWindowPos(hdwp, statusBar, NULL, elementRect.left, elementRect.top, + RECTWIDTH(elementRect), RECTHEIGHT(elementRect), swpFlags); + } + + HWND widgetWindow = MANAGERVIEW_WIDGET(hwnd); + if (NULL != widgetWindow) + { + CopyRect(&elementRect, &clientRect); + if (0 != bottomBarHeight) + { + elementRect.bottom -= (bottomBarHeight + WASABI_API_APP->getScaleY(4)); + } + + Graphics_ClampRect(&elementRect, &clientRect); + + if (NULL != hdwp) + { + hdwp = DeferWindowPos(hdwp, widgetWindow, NULL, elementRect.left, elementRect.top, + RECTWIDTH(elementRect), RECTHEIGHT(elementRect), swpFlags); + } + } + + if (NULL != hdwp) + EndDeferWindowPos(hdwp); +} + +static BOOL +ManagerView_Paint(HWND hwnd, HDC hdc, const RECT *paintRect, BOOL erase) +{ + ManagerView *self; + MANAGERVIEW_RET_VAL(self, hwnd, FALSE); + + if (FALSE != erase) + { + HRGN fillRegion; + fillRegion = CreateRectRgnIndirect(paintRect); + FillRgn(hdc, fillRegion, self->backBrush); + DeleteObject(fillRegion); + } + + return TRUE; +} + +static void +ManagerView_UpdateSkin(HWND hwnd) +{ + ManagerView *self; + MANAGERVIEW_RET_VOID(self, hwnd); + + BOOL styleChanged = FALSE; + COLORREF color = Graphics_GetSkinColor(WADLG_WNDBG); + if (color != self->backColor || NULL == self->backBrush) + { + self->backColor = color; + self->backBrush = CreateSolidBrush(self->backColor); + styleChanged = TRUE; + } + + color = Graphics_GetSkinColor(WADLG_WNDFG); + if (self->textColor != color) + { + self->textColor = color; + styleChanged = TRUE; + } + + color = Graphics_GetSkinColor(WADLG_HILITE); + if (self->borderColor != color) + { + self->borderColor = color; + styleChanged = TRUE; + } + + if (FALSE != WidgetStyle_UpdateDefaultColors(&self->widgetStyle)) + styleChanged = TRUE; + + if (FALSE != styleChanged) + { + HWND controlWindow = MANAGERVIEW_WIDGET(hwnd); + if (NULL != controlWindow) + { + WIDGET_STYLE_COLOR_CHANGED(controlWindow); + InvalidateRect(controlWindow, NULL, TRUE); + } + + controlWindow = MANAGERVIEW_STATUS_BAR(hwnd); + if (NULL != controlWindow) + { + STATUSBAR_SET_TEXT_COLOR(controlWindow, self->textColor, FALSE); + STATUSBAR_SET_BACK_COLOR(controlWindow, self->backColor, FALSE); + STATUSBAR_SET_BACK_BRUSH(controlWindow, self->backBrush, FALSE); + } + } +} + +static BOOL +ManagerView_GetIdealButtonSize(HWND buttonWindow, SIZE *buttonSize) +{ + if (NULL == buttonWindow || NULL == buttonSize) + return FALSE; + + LRESULT skinSize = MLSkinnedButton_GetIdealSize(buttonWindow, NULL); + if (0 != skinSize) + { + buttonSize->cx = LOWORD(skinSize); + buttonSize->cy = HIWORD(skinSize); + return TRUE; + } + + buttonSize->cx = 0; + buttonSize->cy = 0; + if (FALSE != SendMessageW(buttonWindow, BCM_GETIDEALSIZE, 0, (LPARAM)buttonSize)) + return TRUE; + + return FALSE; +} + +static BOOL +ManagerView_UpdateDiscoverButtonFont(HWND hwnd, HFONT font, SIZE *size) +{ + ManagerView *self; + SIZE buttonSize; + + MANAGERVIEW_RET_VAL(self, hwnd, FALSE); + + HWND buttonWindow = MANAGERVIEW_DISCOVER_BUTTON(hwnd); + if (NULL == buttonWindow) + return FALSE; + + SendMessage(buttonWindow, WM_SETFONT, (WPARAM)font, MAKELPARAM(0,0)); + + if (FALSE == ManagerView_GetIdealButtonSize(buttonWindow, &buttonSize)) + { + RECT buttonRect; + if (FALSE == GetWindowRect(buttonWindow, &buttonRect)) + return FALSE; + + buttonSize.cx = RECTWIDTH(buttonRect); + buttonSize.cy = RECTHEIGHT(buttonRect); + } + + buttonSize.cx += MANAGERVIEW_DLU_TO_HORZ_PX(self, DISCOVER_BUTTON_EXTRA_SPACE_DLU); + + if (buttonSize.cx < DISCOVER_BUTTON_MIN_WIDTH_PX) + buttonSize.cx = DISCOVER_BUTTON_MIN_WIDTH_PX; + + if (buttonSize.cy < DISCOVER_BUTTON_MIN_HEIGHT_PX) + buttonSize.cy = DISCOVER_BUTTON_MIN_HEIGHT_PX; + + BOOL result = SetWindowPos(buttonWindow, NULL, 0, 0, buttonSize.cx, buttonSize.cy, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW); + if (FALSE == result) + return FALSE; + + if (NULL != size) + *size = buttonSize; + + return TRUE; +} + +static void +ManagerView_UpdateFont(HWND hwnd, BOOL redraw) +{ + HWND controlWindow = NULL; + ManagerView *self = NULL; + MANAGERVIEW_RET_VOID(self, hwnd); + + if (FALSE == Graphics_GetWindowBaseUnits(hwnd, &self->unitSize.cx, &self->unitSize.cy)) + { + self->unitSize.cx = 6; + self->unitSize.cy = 13; + } + + if (FALSE != WidgetStyle_UpdateDefaultFonts(&self->widgetStyle, self->font, self->unitSize.cx, self->unitSize.cy)) + { + controlWindow = MANAGERVIEW_WIDGET(hwnd); + if (NULL != controlWindow) + WIDGET_STYLE_FONT_CHANGED(controlWindow); + } + + ManagerView_UpdateDiscoverButtonFont(hwnd, self->font, NULL); + + controlWindow = MANAGERVIEW_STATUS_BAR(hwnd); + if (NULL != controlWindow) + SendMessage(controlWindow, WM_SETFONT, (WPARAM)self->font, 0L); + + if (NULL != redraw) + { + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE |SWP_NOMOVE |SWP_FRAMECHANGED | SWP_NOREDRAW); + + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE); + } +} + +static void +ManagerView_UpdateDiscoveryStatus(HWND hwnd, BOOL discoveryActive) +{ + ManagerView *self; + MANAGERVIEW_RET_VOID(self, hwnd); + + HWND statusBar = MANAGERVIEW_STATUS_BAR(hwnd); + if (NULL == statusBar) + return; + + if (FALSE != discoveryActive) + { + wchar_t buffer[512] = {0}; + + WASABI_API_LNGSTRINGW_BUF(IDS_STATUS_DISCOVERY_ACTIVE, buffer, ARRAYSIZE(buffer)); + + if (STATUS_ERROR == self->discoveryStatus) + self->discoveryStatus = STATUSBAR_ADD_STATUS(statusBar, buffer); + else + { + STATUSBAR_SET_STATUS_TEXT(statusBar, self->discoveryStatus, buffer); + STATUSBAR_MOVE_STATUS(statusBar, self->discoveryStatus, STATUS_MOVE_TOP); + } + } + else + { + if (STATUS_ERROR != self->discoveryStatus) + { + STATUSBAR_REMOVE_STATUS(statusBar, self->discoveryStatus); + self->discoveryStatus = STATUS_ERROR; + } + } +} + +static HWND +ManagerView_CreateServiceErrorWidget(HWND hwnd) +{ + return InfoWidget_CreateWindow(WIDGET_TYPE_SERVICE_ERROR, + MAKEINTRESOURCE(IDS_INFOWIDGET_TITLE), + MAKEINTRESOURCE(IDS_DEVICE_SERVICE_NOT_FOUND), + NULL, + hwnd, 0, 0, 0, 0, TRUE, 0); +} + +static HWND +ManagerView_CreateViewErrorWidget(HWND hwnd) +{ + return InfoWidget_CreateWindow(WIDGET_TYPE_VIEW_ERROR, + MAKEINTRESOURCE(IDS_INFOWIDGET_TITLE), + MAKEINTRESOURCE(IDS_CREATE_MANAGER_VIEW_FAILED), + NULL, + hwnd, 0, 0, 0, 0, TRUE, 0); +} + +static HWND +ManagerView_UpdateWidget(HWND hwnd) +{ + ManagerView *self; + unsigned int widgetType, requiredType; + unsigned int windowStyle; + + MANAGERVIEW_RET_VAL(self, hwnd, NULL); + + HWND widgetWindow = MANAGERVIEW_WIDGET(hwnd); + if (NULL == widgetWindow) + widgetType = WIDGET_TYPE_UNKNOWN; + else + widgetType = WIDGET_GET_TYPE(widgetWindow); + + if (FALSE != self->devicesPresent) + requiredType = WIDGET_TYPE_LIST; + else + { + requiredType = (NULL != WASABI_API_DEVICES) ? + WIDGET_TYPE_WELCOME : + WIDGET_TYPE_SERVICE_ERROR; + } + + if (widgetType == requiredType) + return widgetWindow; + + windowStyle = GetWindowStyle(hwnd); + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowStyle(hwnd, windowStyle & ~WS_VISIBLE); + + if (NULL != widgetWindow) + { + SetWindowLongPtr(widgetWindow, GWLP_ID, 0); + WIDGET_FREEZE(widgetWindow); + DestroyWindow(widgetWindow); + } + + switch(requiredType) + { + case WIDGET_TYPE_SERVICE_ERROR: + widgetWindow = ManagerView_CreateServiceErrorWidget(hwnd); + break; + case WIDGET_TYPE_WELCOME: + widgetWindow = WelcomeWidget_CreateWindow(hwnd, 0, 0, 0, 0, TRUE, 0); + break; + case WIDGET_TYPE_LIST: + widgetWindow = ListWidget_CreateWindow(hwnd, 0, 0, 0, 0, TRUE, 0); + break; + default: + widgetWindow = NULL; + break; + } + + if (NULL == widgetWindow) + widgetWindow = ManagerView_CreateViewErrorWidget(hwnd); + + if (NULL != widgetWindow) + { + if (FALSE != MANAGERVIEW_IS_FROZEN(self)) + WIDGET_FREEZE(widgetWindow); + + MANAGERVIEW_REGISTER_WIDGET(widgetWindow); + + WIDGET_SET_STYLE(widgetWindow, &self->widgetStyle); + } + + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW | SWP_FRAMECHANGED); + + if (NULL != widgetWindow) + { + ShowWindow(widgetWindow, SW_SHOWNA); + + SetWindowPos(widgetWindow, HWND_TOP, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + } + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + { + windowStyle |= WS_VISIBLE; + SetWindowStyle(hwnd, windowStyle); + } + } + + if (0 != (WS_VISIBLE & windowStyle)) + { + RECT rect; + GetClientRect(hwnd, &rect); + + RedrawWindow(hwnd, &rect, NULL, + RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN); + } + + return widgetWindow; +} + +static void +ManagerView_StartDiscovery(HWND hwnd, BOOL silent) +{ + Plugin_BeginDiscovery(); +} + +static BOOL +ManagerView_CheckDevicesPresent() +{ + ifc_deviceobjectenum *enumerator = 0; + ifc_deviceobject *object = 0; + ifc_device *device = 0; + + BOOL devicesPresent = FALSE; + + if (NULL == WASABI_API_DEVICES || + FAILED(WASABI_API_DEVICES->DeviceEnumerate(&enumerator))) + { + return FALSE; + } + + 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")) + devicesPresent = TRUE; + + device->Release(); + } + object->Release(); + + if (FALSE != devicesPresent) + break; + } + enumerator->Release(); + + return devicesPresent; +} + +static void +ManagerView_AddDevice(HWND hwnd, ifc_device *device) +{ + ManagerView *self; + + MANAGERVIEW_RET_VOID(self, hwnd); + + if (FALSE != self->devicesPresent) + return; + + self->devicesPresent = ManagerView_CheckDevicesPresent(); + if (FALSE != self->devicesPresent) + ManagerView_UpdateWidget(hwnd); +} + +static void +ManagerView_RemoveDevice(HWND hwnd, ifc_device *device) +{ + ManagerView *self; + MANAGERVIEW_RET_VOID(self, hwnd); + + if (FALSE == self->devicesPresent) + return; + + self->devicesPresent = ManagerView_CheckDevicesPresent(); + if (FALSE == self->devicesPresent) + ManagerView_UpdateWidget(hwnd); +} + +static void +ManagerView_DeviceCb(ifc_device *device, DeviceEvent eventId, void *user) +{ + HWND hwnd = (HWND)user; + + switch(eventId) + { + case Event_DeviceAdded: + ManagerView_AddDevice(hwnd, device); + break; + + case Event_DeviceRemoved: + ManagerView_RemoveDevice(hwnd, device); + break; + } +} + +static void +ManagerView_DiscoveryCb(api_devicemanager *manager, DeviceDiscoveryEvent eventId, void *user) +{ + HWND hwnd = (HWND)user; + + switch(eventId) + { + case Event_DiscoveryStarted: + ManagerView_UpdateDiscoveryStatus(hwnd, TRUE); + break; + + case Event_DiscoveryFinished: + ManagerView_UpdateDiscoveryStatus(hwnd, FALSE); + break; + } +} + +static BOOL +ManagerView_RegisterDeviceHandler(HWND hwnd) +{ + ManagerView *self; + DeviceEventCallbacks callbacks; + + MANAGERVIEW_RET_VAL(self, hwnd, FALSE); + + if (0 != self->deviceHandler) + return FALSE; + + HWND eventRelay = Plugin_GetEventRelayWindow(); + if (NULL == eventRelay) + return FALSE; + + ZeroMemory(&callbacks, sizeof(callbacks)); + callbacks.deviceCb = ManagerView_DeviceCb; + callbacks.discoveryCb = ManagerView_DiscoveryCb; + + self->deviceHandler = EVENTRELAY_REGISTER_HANDLER(eventRelay, &callbacks, hwnd); + return (0 != self->deviceHandler); +} + +static void +ManagerView_OnDisplayChanged(HWND hwnd, INT bpp, INT dpi_x, INT dpi_y) +{ + ManagerView *self = NULL; + MANAGERVIEW_RET_VOID(self, hwnd); + + if (FALSE != MANAGERVIEW_IS_FROZEN(self)) + return; + + ManagerView_UpdateSkin(hwnd); + + RECT rc; + GetClientRect(hwnd, &rc); + RedrawWindow(hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW | RDW_UPDATENOW); + + ManagerView_Layout(hwnd, TRUE); +} + +static INT_PTR +ManagerView_OnInitDialog(HWND hwnd, HWND focusWindow, LPARAM param) +{ + ManagerView *self = (ManagerView*)malloc(sizeof(ManagerView)); + if (NULL != self) + { + ZeroMemory(self, sizeof(ManagerView)); + if (FALSE == SetProp(hwnd, MAKEINTATOM(MANAGERVIEW_ATOM), self)) + { + free(self); + self = NULL; + } + } + + if (NULL == self) + { + DestroyWindow(hwnd); + return 0; + } + + MANAGERVIEW_FREEZE(self); + + ManagerView_RegisterDeviceHandler(hwnd); + + self->devicesPresent = ManagerView_CheckDevicesPresent(); + + MLSkinWindow2(Plugin_GetLibraryWindow(), hwnd, SKINNEDWND_TYPE_DIALOG, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + + HWND discoverButton = MANAGERVIEW_DISCOVER_BUTTON(hwnd); + if (NULL != discoverButton) + { + MLSkinWindow2(Plugin_GetLibraryWindow(), discoverButton, SKINNEDWND_TYPE_BUTTON, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + + ShowWindow(discoverButton, SW_SHOW); + EnableWindow(discoverButton, TRUE); + } + + HWND statusBar = StatusBar_CreateWindow(0, NULL, WS_VISIBLE, 0, 0, 0, 0, hwnd, MANAGERVIEW_STATUSBAR_ID); + if (NULL != statusBar) + { + MLSkinWindow2(Plugin_GetLibraryWindow(), statusBar, SKINNEDWND_TYPE_AUTO, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + } + + self->discoveryStatus = STATUS_ERROR; + + if (S_OK == WASABI_API_DEVICES->IsDiscoveryActive()) + ManagerView_UpdateDiscoveryStatus(hwnd, TRUE); + + ManagerView_UpdateFont(hwnd, FALSE); + ManagerView_UpdateSkin(hwnd); + + HWND widgetWindow = ManagerView_UpdateWidget(hwnd); + MANAGERVIEW_THAW(self); + + if (NULL != widgetWindow) + WIDGET_THAW(widgetWindow); + + PostMessage(hwnd, WM_DISPLAYCHANGE, 0, 0); + + return 0; +} + +static void +ManagerView_OnDestroy(HWND hwnd) +{ + ManagerView *self = MANAGERVIEW(hwnd); + RemoveProp(hwnd, MAKEINTATOM(MANAGERVIEW_ATOM)); + + if (NULL == self) + return; + + MANAGERVIEW_FREEZE(self); + + if (0 != self->deviceHandler) + { + HWND eventRelay = Plugin_GetEventRelayWindow(); + if (NULL != eventRelay) + { + EVENTRELAY_UNREGISTER_HANDLER(eventRelay, self->deviceHandler); + } + } + + if (NULL != self->systemFont) + DeleteObject(self->systemFont); + + if (NULL != self->backBrush) + DeleteObject(self->backBrush); + + WidgetStyle_Free(&self->widgetStyle); + free(self); +} + +static LRESULT +ManagerView_OnColorDialog(HWND hwnd, HDC hdc) +{ + ManagerView *self; + self = MANAGERVIEW(hwnd); + + if (NULL == self) + return DefWindowProcW(hwnd, WM_CTLCOLORDLG, (WPARAM)hdc, (LPARAM)hwnd); + + if (NULL != hdc) + { + SetTextColor(hdc, self->textColor); + SetBkColor(hdc, self->backColor); + } + + return (LRESULT)self->backBrush; +} + +static void +ManagerView_OnWindowPosChanged(HWND hwnd, WINDOWPOS *windowPos) +{ + if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE) & windowPos->flags) || + (SWP_FRAMECHANGED & windowPos->flags)) + { + ManagerView *self; + MANAGERVIEW_RET_VOID(self, hwnd); + + if (FALSE != MANAGERVIEW_IS_FROZEN(self)) + return; + + ManagerView_Layout(hwnd, !(SWP_NOREDRAW & windowPos->flags)); + } +} + +static void +ManagerView_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + + if (NULL != BeginPaint(hwnd, &ps)) + { + ManagerView_Paint(hwnd, ps.hdc, &ps.rcPaint, ps.fErase); + EndPaint(hwnd, &ps); + } +} + +static void +ManagerView_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + RECT clientRect; + if (GetClientRect(hwnd, &clientRect)) + { + ManagerView_Paint(hwnd, hdc, &clientRect, TRUE); + } +} + +static void +ManagerView_OnSetFont(HWND hwnd, HFONT font, BOOL redraw) +{ + ManagerView *self = NULL; + LOGFONTW prevFont = {0}, newFont = {0}; + + MANAGERVIEW_RET_VOID(self, hwnd); + + if (NULL == self->font || + sizeof(LOGFONTW) != GetObjectW(self->font, sizeof(prevFont), &prevFont)) + { + ZeroMemory(&prevFont, sizeof(prevFont)); + } + + self->font = font; + if (NULL == self->font) + { + if (NULL == self->systemFont) + self->systemFont = Graphics_CreateSysFont(); + } + + if (NULL == self->font || + sizeof(newFont) != GetObjectW(self->font, sizeof(newFont), &newFont)) + { + ZeroMemory(&newFont, sizeof(newFont)); + } + + if (0 == memcmp(&prevFont, &newFont, sizeof(prevFont)) || + FALSE != MANAGERVIEW_IS_FROZEN(self)) + { + redraw = FALSE; + } + + ManagerView_UpdateFont(hwnd, redraw); +} + +static HFONT +ManagerView_OnGetFont(HWND hwnd) +{ + ManagerView *self; + MANAGERVIEW_RET_VAL(self, hwnd, NULL); + + return self->font; +} + +static void +ManagerView_OnCommand(HWND hwnd, INT commandId, INT eventId, HWND controlWindow) +{ + switch(commandId) + { + case IDC_BUTTON_DISCOVER: + switch(eventId) + { + case BN_CLICKED: + ManagerView_StartDiscovery(hwnd, FALSE); + break; + } + break; + } +} + +static void +ManagerView_OnZoomSliderPosChanging(HWND hwnd, NMTRBTHUMBPOSCHANGING *sliderInfo) +{ + HWND widgetWindow = MANAGERVIEW_WIDGET(hwnd); + if (NULL != widgetWindow) + WIDGET_ZOOM_SLIDER_POS_CHANGING(widgetWindow, sliderInfo); +} + +static LRESULT +ManagerView_OnNotify(HWND hwnd, NMHDR *pnmh) +{ + return 0; +} + +static void +ManagerView_OnHorzScroll(HWND hwnd, INT action, INT trackPosition, HWND senderWindow) +{ + HWND sliderWindow = MANAGERVIEW_ZOOM_SLIDER(hwnd); + if (NULL != sliderWindow && senderWindow == sliderWindow) + { + NMTRBTHUMBPOSCHANGING zoomInfo; + zoomInfo.hdr.code = TRBN_THUMBPOSCHANGING; + zoomInfo.hdr.hwndFrom = senderWindow; + zoomInfo.hdr.idFrom = IDC_SLIDER_ZOOM; + zoomInfo.nReason = action; + if (TB_THUMBPOSITION == action || + TB_THUMBTRACK == action) + { + zoomInfo.dwPos = trackPosition; + } + else + zoomInfo.dwPos = (DWORD)SendMessage(sliderWindow, TBM_GETPOS, 0, 0L); + + ManagerView_OnZoomSliderPosChanging(hwnd, &zoomInfo); + } +} + +static BOOL +ManagerView_OnHelp(HWND hwnd, HELPINFO *helpInfo) +{ + HWND widgetWindow = MANAGERVIEW_WIDGET(hwnd); + if (NULL != widgetWindow) + { + wchar_t buffer[4096] = {0}; + + if (FALSE != WIDGET_GET_HELP_URL(widgetWindow, buffer, ARRAYSIZE(buffer)) && + MediaLibrary_ShowHelp(Plugin_GetLibraryWindow(), buffer)) + { + return TRUE; + } + } + + return Plugin_ShowHelp(); +} + +static void +ManagerView_OnSetUpdateRegion(HWND hwnd, HRGN updateRegion, POINTS regionOffset) +{ + ManagerView *self; + MANAGERVIEW_RET_VOID(self, hwnd); + + self->updateRegion = updateRegion; + self->updateOffset.x = regionOffset.x; + self->updateOffset.y = regionOffset.y; +} + +static HWND +ManagerView_OnGetZoomSlider(HWND hwnd) +{ + return MANAGERVIEW_ZOOM_SLIDER(hwnd); +} + +static HWND +ManagerView_OnGetStatusBar(HWND hwnd) +{ + return MANAGERVIEW_STATUS_BAR(hwnd); +} + +static INT_PTR CALLBACK +ManagerView_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: return ManagerView_OnInitDialog(hwnd, (HWND)wParam, lParam); + case WM_DESTROY: ManagerView_OnDestroy(hwnd); return TRUE; + case WM_CTLCOLORDLG: return ManagerView_OnColorDialog(hwnd, (HDC)wParam); + case WM_PAINT: ManagerView_OnPaint(hwnd); return TRUE; + case WM_PRINTCLIENT: ManagerView_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return TRUE; + case WM_ERASEBKGND: DIALOG_RESULT(hwnd, 0); + case WM_WINDOWPOSCHANGED: ManagerView_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return TRUE; + case WM_DISPLAYCHANGE: ManagerView_OnDisplayChanged(hwnd, (INT)wParam, LOWORD(lParam), HIWORD(lParam)); return TRUE; + case WM_SETFONT: ManagerView_OnSetFont(hwnd, (HFONT)wParam, LOWORD(lParam)); return TRUE; + case WM_GETFONT: DIALOG_RESULT(hwnd, ManagerView_OnGetFont(hwnd)); + case WM_COMMAND: ManagerView_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); return TRUE; + case WM_NOTIFY: DIALOG_RESULT(hwnd, ManagerView_OnNotify(hwnd, (NMHDR*)lParam)); + case WM_HSCROLL: ManagerView_OnHorzScroll(hwnd, LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); return TRUE; + case WM_HELP: DIALOG_RESULT(hwnd, ManagerView_OnHelp(hwnd, (HELPINFO*)lParam)); + + // gen_ml flickerless drawing + case WM_USER + 0x200: DIALOG_RESULT(hwnd, 1); + case WM_USER + 0x201: ManagerView_OnSetUpdateRegion(hwnd, (HRGN)lParam, MAKEPOINTS(wParam)); return TRUE; + + case MANAGERVIEW_WM_ZOOMSLIDER: DIALOG_RESULT(hwnd, ManagerView_OnGetZoomSlider(hwnd)); + case MANAGERVIEW_WM_STATUSBAR: DIALOG_RESULT(hwnd, ManagerView_OnGetStatusBar(hwnd)); + } + return 0; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/managerView.h b/Src/Plugins/Library/ml_devices/managerView.h new file mode 100644 index 00000000..af32d790 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/managerView.h @@ -0,0 +1,22 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_MANAGER_VIEW_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_MANAGER_VIEW_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +HWND ManagerView_CreateWindow(HWND parentWindow); + +#define MANAGERVIEW_WM_FIRST (WM_USER + 10) + +#define MANAGERVIEW_WM_ZOOMSLIDER (MANAGERVIEW_WM_FIRST + 0) +#define MANAGERVIEW_GET_ZOOM_SLIDER(/*HWND*/ _hwnd)\ + ((HWND)SendMessageW((_hwnd), MANAGERVIEW_WM_ZOOMSLIDER, 0, 0L)) + +#define MANAGERVIEW_WM_STATUSBAR (MANAGERVIEW_WM_FIRST + 1) +#define MANAGERVIEW_GET_STATUS_BAR(/*HWND*/ _hwnd)\ + ((HWND)SendMessageW((_hwnd), MANAGERVIEW_WM_STATUSBAR, 0, 0L)) + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_MANAGER_VIEW_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/ml_devices.rc b/Src/Plugins/Library/ml_devices/ml_devices.rc new file mode 100644 index 00000000..436157f5 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/ml_devices.rc @@ -0,0 +1,172 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MANAGER_VIEW DIALOGEX 0, 0, 317, 186 +STYLE DS_SETFONT | DS_NOIDLEMSG | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT | WS_EX_NOACTIVATE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Discover",IDC_BUTTON_DISCOVER,0,172,50,14 + CONTROL "",IDC_SLIDER_ZOOM,"msctls_trackbar32",TBS_NOTICKS | TBS_ENABLESELRANGE | NOT WS_VISIBLE | WS_TABSTOP,244,174,73,12 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_PLUGIN_MENU MENU +BEGIN + POPUP "NavigationRootContext" + BEGIN + MENUITEM "&Open", ID_VIEW_OPEN + MENUITEM SEPARATOR + MENUITEM "Start &Discovery", ID_DISCOVERY_BEGIN + MENUITEM SEPARATOR + MENUITEM "&Help", ID_PLUGIN_HELP + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_WELCOMEWIDGET_TITLE "WINAMP DEVICE MANAGER" + IDS_WELCOMEWIDGET_TEXT "Manage the media on your devices by connecting them to Winamp. To get started, connect a device or click Discover to find already connected devices. To manage Android devices over Wi-Fi, ensure wireless sync is enabled in the Winamp App settings." + IDS_DEVICES_NAVIGATION_NODE "Devices" + IDS_CATEGORY_ATTACHED "Attached" + IDS_CATEGORY_DISCOVERED "Discovered" + IDS_INFOWIDGET_TITLE "Winamp Device Manager" + IDS_DEVICE_SERVICE_NOT_FOUND + "Winamp Device Manager unable to communicate with device service. Try reinstalling application." + IDS_CREATE_DEVICE_VIEW_FAILED + "Error creating device view. Verify that you have latest version of portable media plug-in." + IDS_SYNC_COMMAND_TITLE "&Sync" + IDS_SYNC_COMMAND_DESC "Transfer media to device" +END + +STRINGTABLE +BEGIN + IDS_PLUGIN_NAME "Nullsoft Device Manager v%d.%02d" + 65535 "{CA4D071B-4E9B-44fd-862A-783FC763B63D}" +END + +STRINGTABLE +BEGIN + IDS_CANCEL_SYNC_COMMAND_TITLE "C&ancel Sync" + IDS_CANCEL_SYNC_COMMAND_DESC "Cancel synchronization" + IDS_CREATE_MANAGER_VIEW_FAILED "Unable to create device manager view." + IDS_ATTACH_COMMAND_TITLE "&Attach" + IDS_ATTACH_COMMAND_DESC "Connect device to Winamp" + IDS_DETACH_COMMAND_TITLE "&Detach" + IDS_DETACH_COMMAND_DESC "Detach device from Winamp" + IDS_EJECT_COMMAND_TITLE "&Eject" + IDS_EJECT_COMMAND_DESC "Eject device" + IDS_DEVICE_TYPE_SHORT "Type" + IDS_DEVICE_CONNECTION_SHORT "Connection" + IDS_TOTAL_SPACE "Total" + IDS_FREE_SPACE "Free" + IDS_USED_SPACE "Used" + IDS_FRACTION_SEPARATOR "." +END + +STRINGTABLE +BEGIN + IDS_DEVICE_ACTIVITY_SHORT "Status" + IDS_PLUGIN_HELP_URL "https://help.winamp.com/hc/articles/8106455294612-Winamp-Portables-Guide" + IDS_RENAME_COMMAND_TITLE "&Rename\tF2" + IDS_RENAME_COMMAND_DESC "Change the name of this device." + IDS_VIEW_OPEN_COMMAND_TITLE "&Open" + IDS_VIEW_OPEN_COMMAND_DESC "Show contents of this device." + IDS_PREFERENCES_COMMAND_TITLE "&Preferences" + IDS_PREFERENCES_COMMAND_DESC "Change settings for this device." + IDS_PLAYLIST_CREATE_COMMAND_TITLE "&New Playlist" + IDS_PLAYLIST_CREATE_COMMAND_DESC "Make a new playlist on this device." + IDS_MESSAGEBOX_TITLE "Winamp Device Manager" + IDS_MESSAGE_UNABLE_TO_RENAME "Unable to change device name." +END + +STRINGTABLE +BEGIN + IDS_DEVICE_MODEL_SHORT "Model" + IDS_DEVICE_STATUS_SHORT "Status" + IDS_STATUS_DISCOVERY_ACTIVE "Discovering devices..." + IDS_STATUS_SPACE_TEMPLATE "%s free of %s" + IDS_CATEGORY_ATTACHED_EMPTY_TEXT + "No devices are attached, select a discovered device to attach." + IDS_WELCOMEWIDGET_HELP_URL + "https://help.winamp.com/hc/articles/8106455294612-Winamp-Portables-Guide" + IDS_HELP_LINK "Help" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Library/ml_devices/ml_devices.sln b/Src/Plugins/Library/ml_devices/ml_devices.sln new file mode 100644 index 00000000..368e87ff --- /dev/null +++ b/Src/Plugins/Library/ml_devices/ml_devices.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_devices", "ml_devices.vcxproj", "{07D24820-5624-4EC3-AAC6-288D20E039DE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "devices", "..\devices\devices.vcxproj", "{06F6E796-653F-48A9-BA2F-46B679D35F9E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_deviceprovider", "gen_deviceprovider\gen_deviceprovider.vcxproj", "{A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {07D24820-5624-4EC3-AAC6-288D20E039DE}.Debug|Win32.ActiveCfg = Debug|Win32 + {07D24820-5624-4EC3-AAC6-288D20E039DE}.Debug|Win32.Build.0 = Debug|Win32 + {07D24820-5624-4EC3-AAC6-288D20E039DE}.Debug|x64.ActiveCfg = Debug|x64 + {07D24820-5624-4EC3-AAC6-288D20E039DE}.Debug|x64.Build.0 = Debug|x64 + {07D24820-5624-4EC3-AAC6-288D20E039DE}.Release|Win32.ActiveCfg = Release|Win32 + {07D24820-5624-4EC3-AAC6-288D20E039DE}.Release|Win32.Build.0 = Release|Win32 + {07D24820-5624-4EC3-AAC6-288D20E039DE}.Release|x64.ActiveCfg = Release|x64 + {07D24820-5624-4EC3-AAC6-288D20E039DE}.Release|x64.Build.0 = Release|x64 + {06F6E796-653F-48A9-BA2F-46B679D35F9E}.Debug|Win32.ActiveCfg = Debug|Win32 + {06F6E796-653F-48A9-BA2F-46B679D35F9E}.Debug|Win32.Build.0 = Debug|Win32 + {06F6E796-653F-48A9-BA2F-46B679D35F9E}.Debug|x64.ActiveCfg = Debug|x64 + {06F6E796-653F-48A9-BA2F-46B679D35F9E}.Debug|x64.Build.0 = Debug|x64 + {06F6E796-653F-48A9-BA2F-46B679D35F9E}.Release|Win32.ActiveCfg = Release|Win32 + {06F6E796-653F-48A9-BA2F-46B679D35F9E}.Release|Win32.Build.0 = Release|Win32 + {06F6E796-653F-48A9-BA2F-46B679D35F9E}.Release|x64.ActiveCfg = Release|x64 + {06F6E796-653F-48A9-BA2F-46B679D35F9E}.Release|x64.Build.0 = Release|x64 + {A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}.Debug|Win32.ActiveCfg = Debug|Win32 + {A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}.Debug|Win32.ABuild = Debug|Win32 + {A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}.Debug|x64.ActiveCfg = Debug|x64 + {A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}.Debug|x64.ABuild = Debug|x64 + {A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}.Release|Win32.ActiveCfg = Release|Win32 + {A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}.Release|Win32.Build = Release|Win32 + {A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}.Release|x64.ActiveCfg = Release|x64 + {A3C7D830-CD01-4C2B-9E1A-FB62B5236BA9}.Release|x64.Build = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6DCE600E-002D-459F-9DDB-A21A22F89EF7} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Library/ml_devices/ml_devices.vcxproj b/Src/Plugins/Library/ml_devices/ml_devices.vcxproj new file mode 100644 index 00000000..baabc009 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/ml_devices.vcxproj @@ -0,0 +1,412 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{07D24820-5624-4EC3-AAC6-288D20E039DE}</ProjectGuid> + <RootNamespace>ml_devices</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>false</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;ML_DEVICES_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <Profile>false</Profile> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + </Link> + <Xdcmake> + <OutputFile>$(IntDir)$(TargetName).xml</OutputFile> + </Xdcmake> + <Bscmake> + <OutputFile>$(IntDir)$(TargetName).bsc</OutputFile> + </Bscmake> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..;../wasabi;../agave;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;ML_DEVICES_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <DisableSpecificWarnings>4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <Profile>false</Profile> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + </Link> + <Xdcmake> + <OutputFile>$(IntDir)$(TargetName).xml</OutputFile> + </Xdcmake> + <Bscmake> + <OutputFile>$(IntDir)$(TargetName).bsc</OutputFile> + </Bscmake> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;ML_DEVICES_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <Xdcmake> + <OutputFile>$(IntDir)$(ProjectName).xml</OutputFile> + </Xdcmake> + <Bscmake> + <OutputFile>$(IntDir)$(TargetName).bsc</OutputFile> + </Bscmake> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>..;..\..\..\;..\..\..\wasabi;..\..\..\agave;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;ML_DEVICES_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4312;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ModuleDefinitionFile> + </ModuleDefinitionFile> + </Link> + <Xdcmake> + <OutputFile>$(IntDir)$(TargetName).xml</OutputFile> + </Xdcmake> + <Bscmake> + <OutputFile>$(IntDir)$(TargetName).bsc</OutputFile> + </Bscmake> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\General\gen_ml\menu.cpp" /> + <ClCompile Include="..\..\..\nu\trace.cpp" /> + <ClCompile Include="backBuffer.cpp" /> + <ClCompile Include="config.cpp" /> + <ClCompile Include="deviceCommands.cpp" /> + <ClCompile Include="deviceHandler.cpp" /> + <ClCompile Include="deviceManagerHandler.cpp" /> + <ClCompile Include="embeddedEditor.cpp" /> + <ClCompile Include="eventRelay.cpp" /> + <ClCompile Include="fillRegion.cpp" /> + <ClCompile Include="graphics.cpp" /> + <ClCompile Include="image.cpp" /> + <ClCompile Include="imageCache.cpp" /> + <ClCompile Include="infoWidget.cpp" /> + <ClCompile Include="listWidget.cpp" /> + <ClCompile Include="listWidgetCategory.cpp" /> + <ClCompile Include="listWidgetCommand.cpp" /> + <ClCompile Include="listWidgetConnection.cpp" /> + <ClCompile Include="listWidgetGroup.cpp" /> + <ClCompile Include="listWidgetItem.cpp" /> + <ClCompile Include="listWidgetPaint.cpp" /> + <ClCompile Include="listWidgetTooltip.cpp" /> + <ClCompile Include="local_menu.cpp" /> + <ClCompile Include="main.cpp" /> + <ClCompile Include="managerView.cpp" /> + <ClCompile Include="navigation.cpp" /> + <ClCompile Include="navigationIcons.cpp" /> + <ClCompile Include="plugin.cpp" /> + <ClCompile Include="statusBar.cpp" /> + <ClCompile Include="strings.cpp" /> + <ClCompile Include="wasabi.cpp" /> + <ClCompile Include="welcomeWidget.cpp" /> + <ClCompile Include="widget.cpp" /> + <ClCompile Include="widgetHost.cpp" /> + <ClCompile Include="widgetStyle.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\General\gen_ml\menu.h" /> + <ClInclude Include="..\..\..\nu\trace.h" /> + <ClInclude Include="backBuffer.h" /> + <ClInclude Include="common.h" /> + <ClInclude Include="config.h" /> + <ClInclude Include="deviceCommands.h" /> + <ClInclude Include="deviceHandler.h" /> + <ClInclude Include="deviceManagerHandler.h" /> + <ClInclude Include="embeddedEditor.h" /> + <ClInclude Include="eventRelay.h" /> + <ClInclude Include="fillRegion.h" /> + <ClInclude Include="graphics.h" /> + <ClInclude Include="image.h" /> + <ClInclude Include="imageCache.h" /> + <ClInclude Include="infoWidget.h" /> + <ClInclude Include="listWidget.h" /> + <ClInclude Include="listWidgetInternal.h" /> + <ClInclude Include="local_menu.h" /> + <ClInclude Include="main.h" /> + <ClInclude Include="managerView.h" /> + <ClInclude Include="navigation.h" /> + <ClInclude Include="navigationIcons.h" /> + <ClInclude Include="plugin.h" /> + <ClInclude Include="resource.h" /> + <ClInclude Include="statusBar.h" /> + <ClInclude Include="strings.h" /> + <ClInclude Include="wasabi.h" /> + <ClInclude Include="welcomeWidget.h" /> + <ClInclude Include="widget.h" /> + <ClInclude Include="widgetHost.h" /> + <ClInclude Include="widgetStyle.h" /> + </ItemGroup> + <ItemGroup> + <Image Include="resources\action-bg.png" /> + <Image Include="resources\arrows.png" /> + <Image Include="resources\attach-command-large.png" /> + <Image Include="resources\attach-command-small.png" /> + <Image Include="resources\cancel-sync-command-small.png" /> + <Image Include="resources\command-bg.png" /> + <Image Include="resources\command-secondary-bg.png" /> + <Image Include="resources\detach-command-large.png" /> + <Image Include="resources\detach-command-small.png" /> + <Image Include="resources\devices-title-en.png" /> + <Image Include="resources\devices.png" /> + <Image Include="resources\eject-command-small.png" /> + <Image Include="resources\generic-device-160x160.png" /> + <Image Include="resources\generic-device-16x16.png" /> + <Image Include="resources\item-hover.png" /> + <Image Include="resources\item-select.png" /> + <Image Include="resources\progress-large.png" /> + <Image Include="resources\progress-small.png" /> + <Image Include="resources\spacebar.png" /> + <Image Include="resources\sync-command-large.png" /> + <Image Include="resources\sync-command-small.png" /> + <Image Include="resources\unknown-command-large.png" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_devices.rc" /> + <ResourceCompile Include="png.rc" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/ml_devices.vcxproj.filters b/Src/Plugins/Library/ml_devices/ml_devices.vcxproj.filters new file mode 100644 index 00000000..20529cc7 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/ml_devices.vcxproj.filters @@ -0,0 +1,310 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="backBuffer.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="config.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceCommands.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceHandler.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="deviceManagerHandler.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="embeddedEditor.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="eventRelay.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="fillRegion.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="graphics.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="image.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="imageCache.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="infoWidget.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="listWidget.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="listWidgetCategory.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="listWidgetCommand.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="listWidgetConnection.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="listWidgetGroup.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="listWidgetItem.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="listWidgetPaint.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="listWidgetTooltip.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="local_menu.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="managerView.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="navigation.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="navigationIcons.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="plugin.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="statusBar.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="strings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="wasabi.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="welcomeWidget.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="widgetStyle.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="widget.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="widgetHost.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\General\gen_ml\menu.cpp"> + <Filter>Source Files\gen_ml</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\PtrList.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\trace.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="backBuffer.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="common.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="config.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceCommands.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceHandler.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="deviceManagerHandler.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="embeddedEditor.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="eventRelay.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="fillRegion.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="graphics.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="image.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="imageCache.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="infoWidget.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="listWidget.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="listWidgetInternal.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="local_menu.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="main.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="managerView.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="navigation.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="navigationIcons.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="plugin.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="statusBar.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="strings.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="wasabi.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="welcomeWidget.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="widget.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="widgetHost.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="widgetStyle.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\General\gen_ml\menu.h"> + <Filter>Header Files\gen_ml</Filter> + </ClInclude> + <ClInclude Include="..\..\..\nu\trace.h"> + <Filter>Header Files\nu</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_devices.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + <ResourceCompile Include="png.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> + <ItemGroup> + <Image Include="resources\attach-command-small.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\attach-command-large.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\cancel-sync-command-small.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\command-secondary-bg.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\detach-command-large.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\detach-command-small.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\generic-device-16x16.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\devices-title-en.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\devices.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\eject-command-small.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\command-bg.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\generic-device-160x160.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\action-bg.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\arrows.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\item-select.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\item-hover.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\progress-small.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\progress-large.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\sync-command-large.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\sync-command-small.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\spacebar.png"> + <Filter>Image Files</Filter> + </Image> + <Image Include="resources\unknown-command-large.png"> + <Filter>Image Files</Filter> + </Image> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{e0dd5a27-571d-4f70-a2d4-d5752e61b4c9}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{517e7a4b-9342-4d4c-8b09-cc9cd316f8d2}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{b0bc738e-6b68-4e58-9192-30fd269eb5b3}</UniqueIdentifier> + </Filter> + <Filter Include="Image Files"> + <UniqueIdentifier>{9918fa42-7a6e-4012-93e5-82c60aac295f}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\gen_ml"> + <UniqueIdentifier>{f48e1848-fef5-4f67-a582-5621c5ca6e47}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\nu"> + <UniqueIdentifier>{4b166e51-c75c-4c33-81ff-81af63bdcbfe}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\gen_ml"> + <UniqueIdentifier>{1a9a3d9f-f9f0-4c01-b873-2f961be3f40f}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\nu"> + <UniqueIdentifier>{5960b4c7-3d94-4269-9755-8cabb8309b36}</UniqueIdentifier> + </Filter> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/navigation.cpp b/Src/Plugins/Library/ml_devices/navigation.cpp new file mode 100644 index 00000000..25fade94 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/navigation.cpp @@ -0,0 +1,1073 @@ +#include "main.h" +#include "./navigation.h" + +#include <strsafe.h> + +#define DEVICES_NAVITEM_PREFIX L"ml_devices_" + +static HNAVITEM navigationRoot = NULL; +static size_t deviceHandler = 0; + +static HNAVITEM +NavigationItem_Insert(HNAVITEM parent, + HNAVITEM insertAfter, + unsigned int mask, + int itemId, + const wchar_t *name, + const wchar_t *displayName, + unsigned int style, + int image, + LPARAM param) +{ + NAVITEM *item; + NAVINSERTSTRUCT nis = {0}; + + nis.hInsertAfter = insertAfter; + nis.hParent = parent; + item = &nis.item; + + item->cbSize = sizeof(NAVITEM); + item->id = itemId; + + if (0 != (NIMF_IMAGE & mask)) + mask |= NIMF_IMAGESEL; + + item->mask = mask; + item->pszText = (wchar_t*)displayName; + item->pszInvariant = (wchar_t*)name; + item->style = style; + item->styleMask = style; + item->iImage = image; + item->iSelectedImage = image; + item->lParam = param; + + if (!wcsnicmp(L"ml_devices_all_sources", item->pszInvariant, 22)) + { + // and this will allow us to make the cloud library + // root do a web page or a sources view as needed... + return NULL; + } + + return MLNavCtrl_InsertItem(Plugin_GetLibraryWindow(), &nis); +} + +static BOOL +NavigationItem_Delete(HNAVITEM item) +{ + return MLNavCtrl_DeleteItem(Plugin_GetLibraryWindow(), item); +} + +static HNAVITEM +NavigationItem_GetFromMessage(INT msg, INT_PTR param) +{ + return (msg < ML_MSG_NAVIGATION_FIRST) ? + MLNavCtrl_FindItemById(Plugin_GetLibraryWindow(), param) : + (HNAVITEM)param; +} + +static HNAVITEM +NavigationItem_Find(HNAVITEM root, const wchar_t *name, BOOL allow_root = 0) +{ + NAVCTRLFINDPARAMS find = {0}; + HNAVITEM item; + HWND libraryWindow; + + if (NULL == name) + return NULL; + + libraryWindow = Plugin_GetLibraryWindow(); + if (NULL == libraryWindow) + return NULL; + + find.pszName = (wchar_t*)name; + find.cchLength = -1; + find.compFlags = NICF_INVARIANT; + find.fFullNameSearch = FALSE; + + item = MLNavCtrl_FindItemByName(libraryWindow, &find); + if (NULL == item) + return NULL; + + if (!allow_root) + { + // if allowed then we can look for root level items which + // is really for getting 'cloud' devices to another group + if (NULL != root && + root != MLNavItem_GetParent(libraryWindow, item)) + { + item = NULL; + } + } + + return item; +} + +static wchar_t * +NavigationItem_GetNameFromDeviceName(wchar_t *buffer, size_t bufferMax, const char *name) +{ + wchar_t *cursor; + size_t length; + BOOL allocated; + + if (NULL == name || '\0' == *name) + return NULL; + + length = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); + if (ARRAYSIZE(DEVICES_NAVITEM_PREFIX) > bufferMax || + length > (bufferMax - (ARRAYSIZE(DEVICES_NAVITEM_PREFIX) - 1))) + { + bufferMax = length + ARRAYSIZE(DEVICES_NAVITEM_PREFIX) - 1; + buffer = String_Malloc(bufferMax); + if (NULL == buffer) + return NULL; + + allocated = TRUE; + } + else + allocated = FALSE; + + if (FAILED(StringCchCopyEx(buffer, bufferMax, DEVICES_NAVITEM_PREFIX, &cursor, &length, 0)) || + 0 == MultiByteToWideChar(CP_UTF8, 0, name, -1, cursor, (int)length)) + { + if (FALSE != allocated) + String_Free(buffer); + + return NULL; + } + + return buffer; +} + +static HNAVITEM +NavigationItem_FindFromDeviceName(HNAVITEM root, const char *name) +{ + wchar_t buffer[1], *itemName; + HNAVITEM item; + + if (NULL == root) + return NULL; + + itemName = NavigationItem_GetNameFromDeviceName(buffer, ARRAYSIZE(buffer), name); + if (NULL == itemName) + return NULL; + + item = NavigationItem_Find(root, itemName); + + if (itemName != buffer) + String_Free(itemName); + + return item; +} + +static HNAVITEM +NavigationItem_FindFromDevice(HNAVITEM root, ifc_device *device) +{ + if (NULL == device) + return NULL; + + return NavigationItem_FindFromDeviceName(root, device->GetName()); +} + +static BOOL +NavigationItem_DisplayDeviceMenu(HNAVITEM item, const char *deviceName, HWND hostWindow, POINT pt) +{ + HMENU menu; + ifc_device *device; + unsigned int commandId; + BOOL succeeded; + HWND libraryWindow; + + libraryWindow = Plugin_GetLibraryWindow(); + + if (NULL == item|| NULL == deviceName) + return FALSE; + + if (NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(deviceName, &device)) + { + return FALSE; + } + + menu = CreatePopupMenu(); + if (NULL != menu) + { + if (0 == Menu_InsertDeviceItems(menu, 0, 100, device, DeviceCommandContext_NavigationMenu)) + { + DestroyMenu(menu); + menu = NULL; + } + } + + device->Release(); + + if (NULL == menu) + return FALSE; + + succeeded = FALSE; + + if (item == MLNavCtrl_GetSelection(libraryWindow)) + { + commandId = Menu_FindItemByAnsiStringData(menu, "view_open"); + if ((unsigned int)-1 != commandId) + EnableMenuItem(menu, commandId, MF_BYCOMMAND | MF_DISABLED); + } + + commandId = Menu_TrackPopup(Plugin_GetLibraryWindow(), menu, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION | TPM_VERTICAL | TPM_RETURNCMD, + pt.x, pt.y, hostWindow, NULL); + + if (0 != commandId) + { + const char *command; + + command = (const char*)Menu_GetItemData(menu, commandId, FALSE); + if (NULL != command) + { + if (NULL != WASABI_API_DEVICES && + S_OK == WASABI_API_DEVICES->DeviceFind(deviceName, &device)) + { + BOOL commandProcessed; + + commandProcessed = FALSE; + + if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, command, -1, "view_open", -1)) + { + succeeded = MLNavItem_Select(libraryWindow, item); + commandProcessed = succeeded; + } + else if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, command, -1, "rename", -1)) + { + succeeded = MLNavItem_EditTitle(libraryWindow, item); + commandProcessed = succeeded; + } + + if (FALSE == commandProcessed && + SUCCEEDED(device->SendCommand(command, hostWindow, 0))) + { + succeeded = TRUE; + } + + device->Release(); + } + } + } + else + { + if (ERROR_SUCCESS == GetLastError()) + succeeded = TRUE; + } + + Menu_FreeItemData(menu, 0, -1); + + return succeeded; +} + +static BOOL +NavigationItem_DisplayRootMenu(HNAVITEM item, HWND hostWindow, POINT pt) +{ + HMENU pluginMenu, menu; + BOOL succeeded; + + if (NULL == item) + return FALSE; + + pluginMenu = Plugin_LoadMenu(); + if (NULL == pluginMenu) + return FALSE; + + succeeded = FALSE; + + menu = GetSubMenu(pluginMenu, 0); + if (NULL != menu) + { + HWND libraryWindow; + HNAVITEM selectedItem; + + libraryWindow = Plugin_GetLibraryWindow(); + + selectedItem = MLNavCtrl_GetSelection(libraryWindow); + + EnableMenuItem(menu, ID_VIEW_OPEN, MF_BYCOMMAND | ((item != selectedItem) ? MF_ENABLED : MF_DISABLED)); + SetMenuDefaultItem(menu, ID_VIEW_OPEN, FALSE); + + unsigned int commandId = Menu_TrackPopup(Plugin_GetLibraryWindow(), menu, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION | TPM_VERTICAL | TPM_RETURNCMD, + pt.x, pt.y, hostWindow, NULL); + + if (0 != commandId) + { + switch(commandId) + { + case ID_VIEW_OPEN: + MLNavItem_Select(libraryWindow, item); + break; + case ID_DISCOVERY_BEGIN: + Plugin_BeginDiscovery(); + break; + case ID_PLUGIN_HELP: + Plugin_ShowHelp(); + break; + } + } + else + { + if (ERROR_SUCCESS == GetLastError()) + succeeded = TRUE; + } + } + + DestroyMenu(pluginMenu); + + return succeeded; +} + +static BOOL +Navigation_GetDeviceTitle(ifc_device *device, wchar_t *buffer, size_t bufferMax) +{ + size_t read, write; + + if (NULL == device || + FAILED(device->GetDisplayName(buffer, bufferMax))) + { + return FALSE; + } + + for(read = 0, write = 0;; read++) + { + if (read == bufferMax) + return FALSE; + + if (L'\r' == buffer[read]) + continue; + + if (L'\n' == buffer[read] || + L'\t' == buffer[read] || + L'\b' == buffer[read]) + { + buffer[write] = L' '; + } + + buffer[write] = buffer[read]; + if (L'\0' == buffer[read]) + break; + + write++; + } + + return TRUE; +} + +static BOOL +Navigation_DeviceAdd(HNAVITEM root, ifc_device *device) +{ + HNAVITEM item, insertAfter = NCI_LAST; + wchar_t nameBuffer[256] = {0}, *itemName; + wchar_t title[512] = {0}; + int iconIndex; + + if (NULL == device) + return FALSE; + + itemName = NavigationItem_GetNameFromDeviceName(nameBuffer, ARRAYSIZE(nameBuffer), device->GetName()); + if (NULL == itemName) + return FALSE; + + if (NULL != NavigationItem_Find(root, itemName)) + { + if (itemName != nameBuffer) + String_Free(itemName); + + return FALSE; + } + + if (FALSE == Navigation_GetDeviceTitle(device, title, ARRAYSIZE(title))) + title[0] = L'\0'; + + iconIndex = NavigationIcons_GetDeviceIconIndex(device); + + // filter the cloud devices to their own group + if (!lstrcmpiA(device->GetConnection(), "cloud")) + { + HNAVITEM cloud = NavigationItem_Find(navigationRoot, L"cloud_sources", TRUE); + if (cloud != NULL) + { + root = cloud; + HNAVITEM transfers = NavigationItem_Find(0, L"cloud_transfers", TRUE); + + // to maintain some specific orders, we need to alter the insert position + if (!wcsnicmp(L"ml_devices_hss", itemName, 14)) + { + insertAfter = transfers; + if (!insertAfter) insertAfter = NCI_LAST; + } + else if (!wcsnicmp(L"ml_devices_local_desktop", itemName, 24)) + { + insertAfter = NavigationItem_Find(0, L"ml_devices_hss", TRUE); + if (!insertAfter) insertAfter = NCI_LAST; + } + + // when adding children, change from the cloud source to the open/closed arrow + HWND libraryWindow = Plugin_GetLibraryWindow(); + if (NULL != libraryWindow && transfers) + { + NAVITEM itemInfo = {0}; + itemInfo.hItem = cloud; + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.mask = NIMF_IMAGE | NIMF_IMAGESEL; + itemInfo.iImage = itemInfo.iSelectedImage = -1; + MLNavItem_SetInfo(libraryWindow, &itemInfo); + } + } + } + + item = NavigationItem_Insert(root, insertAfter, + NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL | NIMF_STYLE, + 0, itemName, title, NIS_ALLOWEDIT, iconIndex, 0L); + + if (NULL == item) + NavigationIcons_ReleaseIconIndex(iconIndex); + + if (itemName != nameBuffer) + String_Free(itemName); + + if (NULL == item) + return FALSE; + + device->SetNavigationItem(item); + + return TRUE; +} + +static BOOL +Navigation_DeviceRemove(HNAVITEM root, ifc_device *device) +{ + HNAVITEM item; + + item = NavigationItem_FindFromDevice(root, device); + if (NULL == item) + return FALSE; + + return NavigationItem_Delete(item); +} + +static BOOL +Navigation_DeviceTitleChanged(HNAVITEM root, ifc_device *device) +{ + NAVITEM itemInfo; + wchar_t buffer[1024] = {0}; + + itemInfo.hItem = NavigationItem_FindFromDevice(root, device); + if (NULL == itemInfo.hItem) + return FALSE; + + if (FALSE == Navigation_GetDeviceTitle(device, buffer, ARRAYSIZE(buffer))) + return FALSE; + + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.pszText = buffer; + itemInfo.cchTextMax = -1; + itemInfo.mask = NIMF_TEXT; + + return MLNavItem_SetInfo(Plugin_GetLibraryWindow(), &itemInfo); +} + +static BOOL +Navigation_DeviceIconChanged(HNAVITEM root, ifc_device *device) +{ + NAVITEM itemInfo; + int iconIndex; + HWND libraryWindow; + + if (NULL == root || NULL == device) + return FALSE; + + libraryWindow = Plugin_GetLibraryWindow(); + + itemInfo.hItem = NavigationItem_FindFromDevice(root, device); + if (NULL == itemInfo.hItem) + return FALSE; + + iconIndex = NavigationIcons_GetDeviceIconIndex(device); + + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.mask = NIMF_IMAGE; + + if (FALSE == MLNavItem_GetInfo(libraryWindow, &itemInfo)) + { + NavigationIcons_ReleaseIconIndex(iconIndex); + return FALSE; + } + + if (itemInfo.iImage == iconIndex) + { + NavigationIcons_ReleaseIconIndex(iconIndex); + return TRUE; + } + + NavigationIcons_ReleaseIconIndex(itemInfo.iImage); + itemInfo.mask = NIMF_IMAGE | NIMF_IMAGESEL; + itemInfo.iImage = iconIndex; + itemInfo.iSelectedImage = iconIndex; + + if (FALSE == MLNavItem_SetInfo(libraryWindow, &itemInfo)) + { + NavigationIcons_ReleaseIconIndex(iconIndex); + return FALSE; + } + + return TRUE; +} + +static void +Navigation_AddExistingDevices(HNAVITEM root) +{ + ifc_device *device; + ifc_deviceobject *object; + ifc_deviceobjectenum *enumerator; + + if (NULL == WASABI_API_DEVICES) + return; + + if (FAILED(WASABI_API_DEVICES->DeviceEnumerate(&enumerator))) + return; + + while(S_OK == enumerator->Next(&object, 1, NULL)) + { + if (SUCCEEDED(object->QueryInterface(IFC_Device, (void**)&device))) + { + if (FALSE == device->GetHidden() && + FALSE != device->GetAttached()) + { + Navigation_DeviceAdd(root, device); + } + device->Release(); + } + object->Release(); + } + enumerator->Release(); +} + +static void +Navigation_DeviceCb(ifc_device *device, DeviceEvent eventId, void *user) +{ + HNAVITEM rootItem; + rootItem = (HNAVITEM)user; + + switch(eventId) + { + case Event_DeviceAdded: + if (FALSE != device->GetAttached()) + Navigation_DeviceAdd(rootItem, device); + break; + case Event_DeviceRemoved: + if (FALSE != device->GetAttached()) + Navigation_DeviceRemove(rootItem, device); + break; + case Event_DeviceHidden: + if (FALSE != device->GetAttached()) + Navigation_DeviceRemove(rootItem, device); + break; + case Event_DeviceShown: + if (FALSE != device->GetAttached()) + Navigation_DeviceAdd(rootItem, device); + break; + case Event_DeviceAttached: + Navigation_DeviceAdd(rootItem, device); + break; + case Event_DeviceDetached: + Navigation_DeviceRemove(rootItem, device); + break; + case Event_DeviceDisplayNameChanged: + Navigation_DeviceTitleChanged(rootItem, device); + break; + case Event_DeviceIconChanged: + Navigation_DeviceIconChanged(rootItem, device); + break; + } +} + +static BOOL +Navigation_RegisterDeviceHandler(HNAVITEM root) +{ + HWND eventRelay; + DeviceEventCallbacks callbacks; + + if (0 != deviceHandler) + return FALSE; + + eventRelay = Plugin_GetEventRelayWindow(); + if (NULL == eventRelay) + return FALSE; + + ZeroMemory(&callbacks, sizeof(callbacks)); + callbacks.deviceCb = Navigation_DeviceCb; + + deviceHandler = EVENTRELAY_REGISTER_HANDLER(eventRelay, &callbacks, root); + return (0 != deviceHandler); +} + +BOOL +Navigation_Initialize(void) +{ + HWND libraryWindow; + wchar_t buffer[128] = {0}; + + if (NULL != navigationRoot) + return FALSE; + + libraryWindow = Plugin_GetLibraryWindow(); + + MLNavCtrl_BeginUpdate(libraryWindow, NUF_LOCK_TOP); + + WASABI_API_LNGSTRINGW_BUF(IDS_DEVICES_NAVIGATION_NODE, buffer, ARRAYSIZE(buffer)); + + navigationRoot = NavigationItem_Insert(NULL, NULL, + NIMF_ITEMID | NIMF_TEXT | NIMF_STYLE | NIMF_TEXTINVARIANT | + NIMF_PARAM | NIMF_IMAGE | NIMF_IMAGESEL, + ML_TREEVIEW_ID_DEVICES, + DEVICES_NAVITEM_PREFIX L"root", + buffer, + NIS_HASCHILDREN | NIS_ALLOWCHILDMOVE | NIS_DEFAULTIMAGE, + -1, + -1L); + + if (NULL == navigationRoot) + return FALSE; + + Navigation_AddExistingDevices(navigationRoot); + Navigation_RegisterDeviceHandler(navigationRoot); + + MLNavCtrl_EndUpdate(libraryWindow); + + return TRUE; +} + +void +Navigation_Uninitialize(void) +{ + if (0 != deviceHandler) + { + HWND eventRelay; + eventRelay = Plugin_GetEventRelayWindow(); + if (NULL != eventRelay) + { + EVENTRELAY_UNREGISTER_HANDLER(eventRelay, deviceHandler); + } + deviceHandler = NULL; + } + + if (NULL != navigationRoot) + { + NavigationItem_Delete(navigationRoot); + navigationRoot = FALSE; + } + + NavigationIcons_ClearCache(); +} + +BOOL +Navigation_SelectDevice(const char *name) +{ + HNAVITEM item; + + item = NavigationItem_FindFromDeviceName(navigationRoot, name); + if (NULL == item) + return FALSE; + + return MLNavItem_Select(Plugin_GetLibraryWindow(), item); +} + +BOOL +Navigation_EditDeviceTitle(const char *name) +{ + HNAVITEM item; + + item = NavigationItem_FindFromDeviceName(navigationRoot, name); + if (NULL == item) + return FALSE; + + return MLNavItem_EditTitle(Plugin_GetLibraryWindow(), item); +} + +static void +Navigation_DestroyCb() +{ +} + +static BOOL +NavigationItem_IsMine(HNAVITEM item, ifc_device **device) +{ + NAVITEM info; + wchar_t buffer[128] = {0}; + INT nameLength; + INT prefixLength; + + if (NULL == item) + return FALSE; + + if (item == navigationRoot) + { + if (NULL != device) + *device = NULL; + return TRUE; + } + + info.cbSize = sizeof(NAVITEM); + info.hItem = item; + info.mask = NIMF_TEXTINVARIANT; + info.cchInvariantMax = ARRAYSIZE(buffer); + info.pszInvariant = buffer; + + if (FALSE == MLNavItem_GetInfo(Plugin_GetLibraryWindow(), &info)) + return FALSE; + + // to maintain some specific orders, we need to alter the insert position + BOOL swappped = FALSE; + if (!wcsnicmp(L"cloud_sources", info.pszInvariant, 13)) + { + // and this will allow us to make the cloud library + // root do a web page or a sources view as needed... + lstrcpynW(info.pszInvariant, L"ml_devices_all_sources", ARRAYSIZE(buffer)); + swappped = TRUE; + } + + nameLength = (NULL != info.pszInvariant) ? lstrlen(info.pszInvariant) : 0; + prefixLength = ARRAYSIZE(DEVICES_NAVITEM_PREFIX) - 1; + + if (nameLength <= prefixLength) + return FALSE; + + if (CSTR_EQUAL != CompareString(CSTR_INVARIANT, 0, + DEVICES_NAVITEM_PREFIX, prefixLength, info.pszInvariant, prefixLength)) + { + return FALSE; + } + + if (NULL != device) + { + char name[ARRAYSIZE(buffer)] = {0}; + nameLength = WideCharToMultiByte(CP_UTF8, 0, + info.pszInvariant + prefixLength, nameLength - prefixLength, + name, ARRAYSIZE(name), NULL, NULL); + name[nameLength] = '\0'; + + if (0 == nameLength || + NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->DeviceFind(name, device)) + { + *device = NULL; + if (swappped) return FALSE; + } + } + return TRUE; +} + +static HWND +Navigation_CreateViewErrorWidget(HWND hostWindow, void *user) +{ + return InfoWidget_CreateWindow(WIDGET_TYPE_VIEW_ERROR, + MAKEINTRESOURCE(IDS_INFOWIDGET_TITLE), + MAKEINTRESOURCE(IDS_CREATE_DEVICE_VIEW_FAILED), + NULL, + hostWindow, 0, 0, 0, 0, FALSE, 0); +} + +static HWND +NavigationItem_CreateViewCb(HNAVITEM item, HWND parentWindow) +{ + HWND hwnd; + ifc_device *device; + + if (NULL == item || + FALSE == NavigationItem_IsMine(item, &device)) + return NULL; + + if (NULL != device) + { + hwnd = device->CreateView(parentWindow); + device->Release(); + + if (NULL == hwnd) + { + hwnd = WidgetHost_Create(0, 0, 0, 0, 0, parentWindow, Navigation_CreateViewErrorWidget, NULL); + } + } + else + { + hwnd = ManagerView_CreateWindow(parentWindow); + } + return hwnd; +} + +static BOOL +NavigationItem_ShowContextMenuCb(HNAVITEM item, HWND hostWindow, POINTS pts) +{ + POINT pt; + ifc_device *device; + + if (NULL == item || + FALSE == NavigationItem_IsMine(item, &device)) + return FALSE; + + POINTSTOPOINT(pt, pts); + + if (item != navigationRoot) + { + if (NULL != device) + { + char *deviceName = AnsiString_Duplicate(device->GetName()); + device->Release(); + device = NULL; + + if (NULL != deviceName) + { + NavigationItem_DisplayDeviceMenu(item, deviceName, hostWindow, pt); + AnsiString_Free(deviceName); + } + } + } + else + { + NavigationItem_DisplayRootMenu(item, hostWindow, pt); + } + + if (NULL != device) + device->Release(); + + return TRUE; +} + +static BOOL +NavigationItem_ShowHelpCb(HNAVITEM item, HWND hostWindow, POINTS pts) +{ + if (NULL == item || + FALSE == NavigationItem_IsMine(item, NULL)) + { + return FALSE; + } + + Plugin_ShowHelp(); + return TRUE; +} + +static BOOL +NavigationItem_DeleteCb(HNAVITEM item) +{ + ifc_device *device; + + if (NULL == item || + FALSE == NavigationItem_IsMine(item, &device)) + return FALSE; + + if (NULL != device) + { + device->SetNavigationItem(NULL); + device->Release(); + } + + if (item != navigationRoot) + { + NAVITEM itemInfo; + HWND libraryWindow; + + libraryWindow = Plugin_GetLibraryWindow(); + + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.mask = NIMF_IMAGE; + itemInfo.hItem = item; + + if (FALSE != MLNavItem_GetInfo(libraryWindow, &itemInfo) && + -1 != itemInfo.iImage) + { + NavigationIcons_ReleaseIconIndex(itemInfo.iImage); + } + } + + return TRUE; +} + +static BOOL +NavigationItem_KeyDownCb(HNAVITEM item, NMTVKEYDOWN *keyData) +{ + ifc_device *device; + + if (NULL == keyData || + NULL == item || + FALSE == NavigationItem_IsMine(item, &device)) + { + return FALSE; + } + + if (NULL == device) + return TRUE; + + switch(keyData->wVKey) + { + case VK_F2: + MLNavItem_EditTitle(Plugin_GetLibraryWindow(), item); + break; + } + + device->Release(); + return TRUE; +} + +static int +NavigationItem_DropTargetCb(HNAVITEM item, unsigned int dataType, void *data) +{ + ifc_device *device; + int result; + + if (NULL == item || + FALSE == NavigationItem_IsMine(item, &device)) + { + return 0; + } + + if (NULL == device) + return -1; + + result = -1; + + if (NULL == data) + { + if (S_OK == device->GetDropSupported(dataType)) + result = 1; + } + else + { + if (SUCCEEDED(device->Drop(data, dataType))) + result = 1; + } + + device->Release(); + + return result; +} + +static BOOL +NavigationItem_TitleEditBeginCb(HNAVITEM item) +{ // return TRUE to cancel ediging (only on own items!!!); + + ifc_device *device; + + BOOL blockEditor; + + if (NULL == item || + FALSE == NavigationItem_IsMine(item, &device)) + { + return FALSE; + } + + if (NULL == device) + return TRUE; + + blockEditor = (FALSE == DeviceCommand_GetEnabled(device, "rename", + DeviceCommandContext_NavigationMenu)); + + device->Release(); + + return blockEditor; +} + + +static BOOL +NavigationItem_TitleEditEndCb(HNAVITEM item, const wchar_t *title) +{ + HRESULT hr; + ifc_device *device; + + if (NULL == title) + return FALSE; + + if (NULL == item || + FALSE == NavigationItem_IsMine(item, &device) || + NULL == device) + { + return FALSE; + } + + hr = device->SetDisplayName(title); + device->Release(); + + if (FAILED(hr)) + { + HWND libraryWindow; + wchar_t title[256] = {0}, message[1024] = {0}; + + libraryWindow = Plugin_GetLibraryWindow(); + + WASABI_API_LNGSTRINGW_BUF(IDS_MESSAGEBOX_TITLE, title, ARRAYSIZE(title)); + WASABI_API_LNGSTRINGW_BUF(IDS_MESSAGE_UNABLE_TO_RENAME, message, ARRAYSIZE(message)); + + MessageBox(libraryWindow, message, title, MB_OK | MB_ICONERROR); + return FALSE; + } + + return FALSE; +} + + +BOOL +Navigation_ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result) +{ + if (msg == ML_MSG_NO_CONFIG) + { + *result = TRUE; + return TRUE; + } + + if (msg < ML_MSG_TREE_BEGIN || msg > ML_MSG_TREE_END) + return FALSE; + + HNAVITEM item; + switch(msg) + { + case ML_MSG_TREE_ONCREATEVIEW: + item = NavigationItem_GetFromMessage(msg, param1); + *result = (INT_PTR)NavigationItem_CreateViewCb(item, (HWND)param2); + return TRUE; + + case ML_MSG_NAVIGATION_CONTEXTMENU: + item = NavigationItem_GetFromMessage(msg, param1); + *result = (INT_PTR)NavigationItem_ShowContextMenuCb(item, (HWND)param2, MAKEPOINTS(param3)); + return TRUE; + + case ML_MSG_NAVIGATION_HELP: + item = NavigationItem_GetFromMessage(msg, param1); + *result = (INT_PTR)NavigationItem_ShowHelpCb(item, (HWND)param2, MAKEPOINTS(param3)); + return TRUE; + + case ML_MSG_NAVIGATION_ONDELETE: + item = NavigationItem_GetFromMessage(msg, param1); + *result = (INT_PTR)NavigationItem_DeleteCb(item); + return TRUE; + + case ML_MSG_TREE_ONKEYDOWN: + item = NavigationItem_GetFromMessage(msg, param1); + *result = (INT_PTR)NavigationItem_KeyDownCb(item, (NMTVKEYDOWN*)param2); + return TRUE; + + case ML_MSG_TREE_ONDROPTARGET: + item = NavigationItem_GetFromMessage(msg, param1); + *result = (INT_PTR)NavigationItem_DropTargetCb(item, (unsigned int)param2, (void*)param3); + return TRUE; + + case ML_MSG_NAVIGATION_ONBEGINTITLEEDIT: + item = NavigationItem_GetFromMessage(msg, param1); + *result = (INT_PTR)NavigationItem_TitleEditBeginCb(item); + return TRUE; + + case ML_MSG_NAVIGATION_ONENDTITLEEDIT: + item = NavigationItem_GetFromMessage(msg, param1); + *result = (INT_PTR)NavigationItem_TitleEditEndCb(item, (const wchar_t*)param2); + return TRUE; + + case ML_MSG_NAVIGATION_ONDESTROY: + Navigation_DestroyCb(); + *result = 0L; + return TRUE; + + } + return FALSE; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/navigation.h b/Src/Plugins/Library/ml_devices/navigation.h new file mode 100644 index 00000000..c7849ba7 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/navigation.h @@ -0,0 +1,29 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_NAVIGATION_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_NAVIGATION_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +BOOL +Navigation_Initialize(void); + +void +Navigation_Uninitialize(void); + +BOOL +Navigation_SelectDevice(const char *name); + +BOOL +Navigation_EditDeviceTitle(const char *name); + +BOOL +Navigation_ProcessMessage(INT msg, + INT_PTR param1, + INT_PTR param2, + INT_PTR param3, + INT_PTR *result); + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_NAVIGATION_HEADER diff --git a/Src/Plugins/Library/ml_devices/navigationIcons.cpp b/Src/Plugins/Library/ml_devices/navigationIcons.cpp new file mode 100644 index 00000000..038c682d --- /dev/null +++ b/Src/Plugins/Library/ml_devices/navigationIcons.cpp @@ -0,0 +1,343 @@ +#include "main.h" +#include "navigationIcons.h" + +#include <vector> + +typedef struct IconCacheRecord +{ + size_t ref; + int index; + DeviceImage *image; +} IconCacheRecord; + +typedef std::vector<IconCacheRecord> IconCache; + +static IconCache iconCache; + +static BOOL +NavigationIcons_GetSize(unsigned int *width, unsigned int *height) +{ + HWND libraryWindow; + MLIMAGELISTIMAGESIZE imageSize; + + libraryWindow = Plugin_GetLibraryWindow(); + + imageSize.hmlil = MLNavCtrl_GetImageList(libraryWindow); + if (NULL == imageSize.hmlil) + return FALSE; + + if (FALSE == MLImageList_GetImageSize(libraryWindow, &imageSize)) + return FALSE; + + *width = imageSize.cx; + *height = imageSize.cy; + + return TRUE; +} + +static DeviceImage* +NavigationIcons_GetDeviceImage(ifc_device *device, unsigned int width, unsigned int height) +{ + wchar_t path[MAX_PATH * 2] = {0}; + + if (NULL == device) + return NULL; + + if (FAILED(device->GetIcon(path, ARRAYSIZE(path), width, height))) + return NULL; + + return DeviceImageCache_GetImage(Plugin_GetImageCache(), + path, width, height, NULL, NULL); +} + +static DeviceImage* +NavigationIcons_GetDeviceTypeImage(ifc_device *device, unsigned int width, unsigned int height) +{ + ifc_devicetype *type; + DeviceImage *image; + wchar_t path[MAX_PATH * 2] = {0}; + + if (NULL == device || + NULL == WASABI_API_DEVICES || + S_OK != WASABI_API_DEVICES->TypeFind(device->GetName(), &type)) + { + return NULL; + } + + if (SUCCEEDED(type->GetIcon(path, ARRAYSIZE(path), width, height))) + { + + image = DeviceImageCache_GetImage(Plugin_GetImageCache(), + path, width, height, NULL, NULL); + } + else + image = NULL; + + type->Release(); + + return image; +} + +static DeviceImage* +NavigationIcons_GetDefaultImage(ifc_device *device, unsigned int width, unsigned int height) +{ + const wchar_t *path; + + path = Plugin_GetDefaultDeviceImage(width, height); + if (NULL == path) + return NULL; + + return DeviceImageCache_GetImage(Plugin_GetImageCache(), path, width, height, NULL, NULL); +} + +static int +NavigationIcons_RegisterDeviceIcon(DeviceImage *image, int iconIndex) +{ + MLIMAGESOURCE imageSource; + MLIMAGELISTITEM listItem; + HWND libraryWindow; + HBITMAP bitmap; + + if (NULL == image) + return -1; + + libraryWindow = Plugin_GetLibraryWindow(); + if (NULL == libraryWindow) + return -1; + + + bitmap = DeviceImage_GetBitmap(image, + DeviceImage_ExactSize | DeviceImage_AlignHCenter | DeviceImage_AlignVCenter); + + if (NULL == bitmap) + return -1; + + + imageSource.cbSize = sizeof(imageSource); + imageSource.lpszName = (LPCWSTR)bitmap; + imageSource.type = SRC_TYPE_HBITMAP; + imageSource.bpp = 32; + imageSource.flags = 0; + imageSource.hInst = NULL; + + listItem.cbSize = sizeof(listItem); + listItem.hmlil = MLNavCtrl_GetImageList(libraryWindow); + listItem.filterUID = MLIF_FILTER3_UID; + listItem.pmlImgSource = &imageSource; + listItem.mlilIndex = iconIndex; + + if (NULL == listItem.hmlil) + return -1; + + if (listItem.mlilIndex >= 0) + { + if (FALSE == MLImageList_Replace(libraryWindow, &listItem)) + return -1; + + return listItem.mlilIndex; + } + + return MLImageList_Add(libraryWindow, &listItem); +} + + +static IconCacheRecord * +NavigationIcons_FindCacheRecord(DeviceImage *image) +{ + size_t index; + IconCacheRecord *record; + + if (NULL == image) + return NULL; + + index = iconCache.size(); + while(index--) + { + record = &iconCache[index]; + if (record->image == image) + return record; + } + + return NULL; +} + +static IconCacheRecord * +NavigationIcons_FindAvailableCacheRecord() +{ + size_t index; + IconCacheRecord *record; + + index = iconCache.size(); + while(index--) + { + record = &iconCache[index]; + if (0 == record->ref) + return record; + } + + return NULL; +} + +static IconCacheRecord * +NavigationIcons_FindCacheRecordByIndex(int iconIndex) +{ + size_t index; + IconCacheRecord *record; + + if (iconIndex < 0) + return NULL; + + index = iconCache.size(); + while(index--) + { + record = &iconCache[index]; + if (record->index == iconIndex) + return record; + } + + return NULL; +} + +static IconCacheRecord * +NavigationIcons_CreateCacheRecord(DeviceImage *image, int iconIndex) +{ + IconCacheRecord record; + + if (NULL == image || -1 == iconIndex) + return NULL; + + record.ref = 1; + record.index = iconIndex; + record.image = image; + + DeviceImage_AddRef(image); + + iconCache.push_back(record); + return &iconCache.back(); +} + +static HBITMAP +NavigationIcons_GetDeviceImageBitmap(DeviceImage *image) +{ + return DeviceImage_GetBitmap(image, + DeviceImage_ExactSize | + DeviceImage_AlignHCenter | + DeviceImage_AlignVCenter); +} + +int +NavigationIcons_GetDeviceIconIndex(ifc_device *device) +{ + DeviceImage *image; + unsigned int width, height; + IconCacheRecord *record; + int iconIndex; + size_t attempt; + + if (FALSE == NavigationIcons_GetSize(&width, &height)) + return -1; + + for(attempt = 0; attempt < 3; attempt++) + { + switch(attempt) + { + case 0: + image = NavigationIcons_GetDeviceImage(device, width, height); + break; + case 1: + image = NavigationIcons_GetDeviceTypeImage(device, width, height); + break; + case 2: + image = NavigationIcons_GetDefaultImage(device, width, height); + break; + } + + record = (NULL != image) ? + NavigationIcons_FindCacheRecord(image) : + NULL; + + if (NULL == record && + NULL == NavigationIcons_GetDeviceImageBitmap(image)) + { + continue; + } + + break; + } + + if (NULL != record) + { + record->ref++; + iconIndex = record->index; + } + else + { + record = NavigationIcons_FindAvailableCacheRecord(); + if (NULL != record) + { + iconIndex = NavigationIcons_RegisterDeviceIcon(image, record->index); + if (-1 != iconIndex) + { + record->ref++; + record->image = image; + } + } + else + { + iconIndex = NavigationIcons_RegisterDeviceIcon(image, -1); + if (-1 != iconIndex) + { + IconCacheRecord newRecord; + newRecord.ref = 1; + newRecord.image = image; + newRecord.index = iconIndex; + iconCache.push_back(newRecord); + } + } + } + + if (-1 == iconIndex) + DeviceImage_Release(image); + + return iconIndex; +} + +BOOL +NavigationIcons_ReleaseIconIndex(int iconIndex) +{ + IconCacheRecord *record; + + record = NavigationIcons_FindCacheRecordByIndex(iconIndex); + if (NULL == record) + return FALSE; + + if (0 == record->ref) + return FALSE; + + record->ref--; + DeviceImage_Release(record->image); + if (0 == record->ref) + record->image = NULL; + + return TRUE; +} + +void +NavigationIcons_ClearCache() +{ + size_t index; + IconCacheRecord *record; + + index = iconCache.size(); + while(index--) + { + record = &iconCache[index]; + if (NULL != record->image) + { + while(record->ref--) + DeviceImage_Release(record->image); + record->image = NULL; + } + record->ref = 0; + } +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/navigationIcons.h b/Src/Plugins/Library/ml_devices/navigationIcons.h new file mode 100644 index 00000000..1b53fc75 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/navigationIcons.h @@ -0,0 +1,21 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_NAVIGATION_ICONS_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_NAVIGATION_ICONS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +int +NavigationIcons_GetDeviceIconIndex(ifc_device *device); + +BOOL +NavigationIcons_ReleaseIconIndex(int iconIndex); + +void +NavigationIcons_ClearCache(); + + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_NAVIGATION_ICONS_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/plugin.cpp b/Src/Plugins/Library/ml_devices/plugin.cpp new file mode 100644 index 00000000..e8396373 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/plugin.cpp @@ -0,0 +1,357 @@ +#include "main.h" +#include <vector> + +//#include <crtdbg.h> +#include <strsafe.h> + +typedef std::vector<PluginUnloadCallback> UnloadCallbackList; + +static int Plugin_Init(); +static void Plugin_Quit(); + +static INT_PTR +Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3); + +extern "C" winampMediaLibraryPlugin plugin = +{ + MLHDR_VER, + "nullsoft(ml_devices.dll)", + Plugin_Init, + Plugin_Quit, + Plugin_MessageProc, + 0, + 0, + 0, +}; + +static UnloadCallbackList *unloadCallbacks = NULL; +static DeviceImageCache *imageCache = NULL; +static HWND eventRelayWindow = NULL; + +HINSTANCE +Plugin_GetInstance(void) +{ + return plugin.hDllInstance; +} + +HWND +Plugin_GetWinampWindow(void) +{ + return plugin.hwndWinampParent; +} + +HWND +Plugin_GetLibraryWindow(void) +{ + return plugin.hwndLibraryParent; +} + +static void +Plugin_SetDescription() +{ + static wchar_t szDescription[256]; + StringCchPrintf(szDescription, ARRAYSIZE(szDescription), + WASABI_API_LNGSTRINGW(IDS_PLUGIN_NAME), + PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR); + plugin.description = (char*)szDescription; +} + +static int Plugin_Init() +{ + unloadCallbacks = NULL; + +// _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | +// _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_CHECK_CRT_DF); + + if (FALSE == Wasabi_InitializeFromWinamp(plugin.hDllInstance, plugin.hwndWinampParent)) + return ML_INIT_FAILURE; + + Wasabi_LoadDefaultServices(); + + Plugin_SetDescription(); + + imageCache = DeviceImageCache_Create(); + + if (NULL == eventRelayWindow) + { + eventRelayWindow = EventRelay_CreateWindow(); + if (NULL == eventRelayWindow) + return 2; + } + + if (FALSE == Navigation_Initialize()) + { + if (NULL != eventRelayWindow) + { + DestroyWindow(eventRelayWindow); + eventRelayWindow = NULL; + } + + Wasabi_Release(); + return 3; + } + + DeviceCommands_Register(); + + return ML_INIT_SUCCESS; +} + +static void Plugin_Quit() +{ + if (NULL != unloadCallbacks) + { + size_t index = unloadCallbacks->size(); + while(index--) + unloadCallbacks->at(index)(); + delete(unloadCallbacks); + } + + if (NULL != eventRelayWindow) + { + DestroyWindow(eventRelayWindow); + eventRelayWindow = NULL; + } + + Navigation_Uninitialize(); + + if (NULL != imageCache) + { + DeviceImageCache_Free(imageCache); + imageCache = NULL; + } + + Wasabi_Release(); +} + +static INT_PTR +Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3) +{ + INT_PTR result = 0; + + if (FALSE != Navigation_ProcessMessage(msg, param1, param2, param3, &result)) + return result; + + return FALSE; +} + +BOOL +Plugin_RegisterUnloadCallback(PluginUnloadCallback callback) +{ + if (NULL == callback) + return FALSE; + + if (NULL == unloadCallbacks) + { + unloadCallbacks = new UnloadCallbackList(); + if (NULL == unloadCallbacks) + return FALSE; + } + + unloadCallbacks->push_back(callback); + return TRUE; +} + +DeviceImageCache * +Plugin_GetImageCache() +{ + return imageCache; +} + +HWND +Plugin_GetEventRelayWindow() +{ + return eventRelayWindow; +} + +const wchar_t * +Plugin_GetDefaultDeviceImage(unsigned int width, unsigned int height) +{ + const ImageInfo *image; + const ImageInfo deviceImages[] = + { + {16, 16, MAKEINTRESOURCE(IDR_GENERIC_DEVICE_16x16_IMAGE)}, + {160, 160, MAKEINTRESOURCE(IDR_GENERIC_DEVICE_160x160_IMAGE)}, + }; + + image = Image_GetBestFit(deviceImages, ARRAYSIZE(deviceImages), width, height); + if (NULL == image) + return NULL; + + return image->path; +} + +HRESULT +Plugin_EnsurePathExist(const wchar_t *path) +{ + unsigned long errorCode; + unsigned int errorMode; + + errorCode = ERROR_SUCCESS; + errorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + + if (0 == CreateDirectory(path, NULL)) + { + errorCode = GetLastError(); + if (ERROR_PATH_NOT_FOUND == errorCode) + { + const wchar_t *block, *cursor; + wchar_t buffer[MAX_PATH] = {0}; + + block = path; + cursor = PathFindNextComponent(block); + + errorCode = (cursor == block || + S_OK != StringCchCopyN(buffer, ARRAYSIZE(buffer), block, (cursor - block))) ? + ERROR_INVALID_NAME : ERROR_SUCCESS; + + block = cursor; + + while(ERROR_SUCCESS == errorCode && + NULL != (cursor = PathFindNextComponent(block))) + { + if (cursor == block || + S_OK != StringCchCatN(buffer, ARRAYSIZE(buffer), block, (cursor - block))) + { + errorCode = ERROR_INVALID_NAME; + } + + if (ERROR_SUCCESS == errorCode && + FALSE == CreateDirectory(buffer, NULL)) + { + errorCode = GetLastError(); + if (ERROR_ALREADY_EXISTS == errorCode) + errorCode = ERROR_SUCCESS; + } + block = cursor; + } + } + + if (ERROR_ALREADY_EXISTS == errorCode) + errorCode = ERROR_SUCCESS; + } + + SetErrorMode(errorMode); + SetLastError(errorCode); + return HRESULT_FROM_WIN32(errorCode); +} + +BOOL +Plugin_GetResourceString(const wchar_t *resourceName, const wchar_t *resourceType, wchar_t *buffer, size_t bufferMax) +{ + unsigned long filenameLength; + + if (NULL == resourceName) + return FALSE; + + if (FAILED(StringCchCopyEx(buffer, bufferMax, L"res://", &buffer, &bufferMax, 0))) + return FALSE; + + filenameLength = GetModuleFileName(Plugin_GetInstance(), buffer, (DWORD)bufferMax); + if (0 == filenameLength || bufferMax == filenameLength) + return FALSE; + + buffer += filenameLength; + bufferMax -= filenameLength; + + if (NULL != resourceType) + { + if (FALSE != IS_INTRESOURCE(resourceType)) + { + if (FAILED(StringCchPrintfEx(buffer, bufferMax, &buffer, &bufferMax, 0, L"/#%d", (int)(INT_PTR)resourceType))) + return FALSE; + } + else + { + if (FAILED(StringCchPrintfEx(buffer, bufferMax, &buffer, &bufferMax, 0, L"/%s", resourceType))) + return FALSE; + } + } + + if (FALSE != IS_INTRESOURCE(resourceName)) + { + if (FAILED(StringCchPrintfEx(buffer, bufferMax, &buffer, &bufferMax, 0, L"/#%d", (int)(INT_PTR)resourceName))) + return FALSE; + } + else + { + if (FAILED(StringCchPrintfEx(buffer, bufferMax, &buffer, &bufferMax, 0, L"/%s", resourceName))) + return FALSE; + } + + return TRUE; +} + +HMENU +Plugin_LoadMenu() +{ + return WASABI_API_LOADMENUW(IDR_PLUGIN_MENU); +} + +BOOL +Plugin_ShowHelp() +{ + BOOL result; + wchar_t buffer[8192] = {0}; + + WASABI_API_LNGSTRINGW_BUF(IDS_PLUGIN_HELP_URL, buffer, ARRAYSIZE(buffer)); + if (L'\0' == buffer[0]) + { + if (FAILED(StringCchCopy(buffer, ARRAYSIZE(buffer), + L"https://help.winamp.com/hc/articles/8106455294612-Winamp-Portables-Guide"))) + { + return FALSE; + } + } + + result = MediaLibrary_ShowHelp(plugin.hwndLibraryParent, buffer); + + return result; +} + +BOOL +Plugin_BeginDiscovery() +{ + if (NULL == WASABI_API_DEVICES || + FAILED(WASABI_API_DEVICES->BeginDiscovery())) + { + return FALSE; + } + + return TRUE; +} + +BOOL +Plugin_OpenUrl(HWND ownerWindow, const wchar_t *url, BOOL forceExternal) +{ + BOOL result; + HCURSOR cursor; + + if (NULL == WASABI_API_WINAMP) + return FALSE; + + cursor = LoadCursor(NULL, IDC_APPSTARTING); + if (NULL != cursor) + cursor = SetCursor(cursor); + + if (FALSE != forceExternal) + { + HINSTANCE instance = ShellExecute(ownerWindow, L"open", url, NULL, NULL, SW_SHOWNORMAL); + result = ((INT_PTR)instance > 32) ? TRUE: FALSE; + } + else + { + HRESULT hr = WASABI_API_WINAMP->OpenUrl(ownerWindow, url); + result = SUCCEEDED(hr); + } + + if (NULL != cursor) + SetCursor(cursor); + + return result; +} + +EXTERN_C __declspec(dllexport) winampMediaLibraryPlugin * +winampGetMediaLibraryPlugin() +{ + return &plugin; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/plugin.h b/Src/Plugins/Library/ml_devices/plugin.h new file mode 100644 index 00000000..b09f5499 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/plugin.h @@ -0,0 +1,33 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_PLUGIN_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_PLUGIN_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "../Plugins/General/gen_ml/ml.h" +#include "./imageCache.h" +#include "./deviceManagerHandler.h" +#include "./deviceHandler.h" + +#define PLUGIN_VERSION_MAJOR 1 +#define PLUGIN_VERSION_MINOR 35 + +typedef void (CALLBACK *PluginUnloadCallback)(void); + +HINSTANCE Plugin_GetInstance(void); +HWND Plugin_GetWinampWindow(void); +HWND Plugin_GetLibraryWindow(void); +BOOL Plugin_RegisterUnloadCallback(PluginUnloadCallback callback); +DeviceImageCache *Plugin_GetImageCache(); +HWND Plugin_GetEventRelayWindow(); +const wchar_t *Plugin_GetDefaultDeviceImage(unsigned int width, unsigned int height); +HRESULT Plugin_EnsurePathExist(const wchar_t *path); +BOOL Plugin_GetResourceString(const wchar_t *resourceName, const wchar_t *resourceType, wchar_t *buffer, size_t bufferMax); +HMENU Plugin_LoadMenu(); +BOOL Plugin_ShowHelp(); +BOOL Plugin_BeginDiscovery(); +BOOL Plugin_OpenUrl(HWND ownerWindow, const wchar_t *url, BOOL forceExternal); + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_PLUGIN_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/png.rc b/Src/Plugins/Library/ml_devices/png.rc new file mode 100644 index 00000000..38fdea1e --- /dev/null +++ b/Src/Plugins/Library/ml_devices/png.rc @@ -0,0 +1,50 @@ +#include "resource.h" +///////////////////////////////////////////////////////////////////////////// +// +// Data +// +IDR_WELCOME_WIDGET_IMAGE RCDATA +".\\resources\\devices.png" +IDR_SPACEBAR_IMAGE RCDATA +".\\resources\\spacebar.png" +IDR_CATEGORY_ARROWS_IMAGE RCDATA +".\\resources\\arrows.png" +IDR_ITEM_HOVER_IMAGE RCDATA +".\\resources\\item-hover.png" +IDR_ITEM_SELECT_IMAGE RCDATA +".\\resources\\item-select.png" +IDR_COMMAND_BACKGROUND_IMAGE RCDATA +".\\resources\\command-bg.png" +IDR_COMMAND_SECONDARY_BACKGROUND_IMAGE RCDATA +".\\resources\\command-secondary-bg.png" +IDR_PROGRESS_LARGE_IMAGE RCDATA +".\\resources\\progress-large.png" +IDR_PROGRESS_SMALL_IMAGE RCDATA +".\\resources\\progress-small.png" +IDR_ACTION_BACKGROUND_IMAGE RCDATA +".\\resources\\action-bg.png" +IDR_GENERIC_DEVICE_160x160_IMAGE RCDATA +".\\resources\\generic-device-160x160.png" +IDR_GENERIC_DEVICE_16x16_IMAGE RCDATA +".\\resources\\generic-device-16x16.png" +IDR_UNKNOWN_COMMAND_LARGE_IMAGE RCDATA +".\\resources\\unknown-command-large.png" +IDR_SYNC_COMMAND_LARGE_IMAGE RCDATA +".\\resources\\sync-command-large.png" +IDR_SYNC_COMMAND_SMALL_IMAGE RCDATA +".\\resources\\sync-command-small.png" +IDR_CANCEL_SYNC_COMMAND_SMALL_IMAGE RCDATA +".\\resources\\cancel-sync-command-small.png" +IDR_ATTACH_COMMAND_LARGE_IMAGE RCDATA +".\\resources\\attach-command-large.png" +IDR_ATTACH_COMMAND_SMALL_IMAGE RCDATA +".\\resources\\attach-command-small.png" +IDR_DETACH_COMMAND_LARGE_IMAGE RCDATA +".\\resources\\detach-command-large.png" +IDR_DETACH_COMMAND_SMALL_IMAGE RCDATA +".\\resources\\detach-command-small.png" +IDR_EJECT_COMMAND_SMALL_IMAGE RCDATA +".\\resources\\eject-command-small.png" +IDR_WELCOME_WIDGET_TITLE_IMAGE RCDATA +".\\resources\\devices-title-en.png" + diff --git a/Src/Plugins/Library/ml_devices/resource.h b/Src/Plugins/Library/ml_devices/resource.h new file mode 100644 index 00000000..8d75063f --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resource.h @@ -0,0 +1,105 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ml_devices.rc +// +#define IDR_CANCEL_SYNC_COMMAND_LARGE_IMAGE 0 +#define IDR_EJECT_COMMAND_LARGE_IMAGE 0 +#define IDR_RENAME_COMMAND_LARGE_IMAGE 0 +#define IDR_RENAME_COMMAND_SMALL_IMAGE 0 +#define IDR_VIEW_OPEN_COMMAND_LARGE_IMAGE 0 +#define IDR_VIEW_OPEN_COMMAND_SMALL_IMAGE 0 +#define IDR_PREFERENCES_COMMAND_LARGE_IMAGE 0 +#define IDR_PREFERENCES_COMMAND_SMALL_IMAGE 0 +#define IDR_PLAYLIST_CREATE_COMMAND_LARGE_IMAGE 0 +#define IDR_PLAYLIST_CREATE_COMMAND_SMALL_IMAGE 0 +#define IDD_MANAGER_VIEW 102 +#define IDS_WELCOMEWIDGET_TITLE 102 +#define IDS_WELCOMEWIDGET_TEXT 103 +#define IDR_PLUGIN_MENU 103 +#define IDS_DEVICES_NAVIGATION_NODE 104 +#define IDS_CATEGORY_ATTACHED 105 +#define IDS_CATEGORY_DISCOVERED 106 +#define IDS_INFOWIDGET_TITLE 107 +#define IDS_DEVICE_SERVICE_NOT_FOUND 108 +#define IDS_CREATE_DEVICE_VIEW_FAILED 109 +#define IDS_SYNC_COMMAND_TITLE 110 +#define IDS_SYNC_COMMAND_DESC 111 +#define IDS_CANCEL_SYNC_COMMAND_TITLE 112 +#define IDS_CANCEL_SYNC_COMMAND_DESC 113 +#define IDS_CREATE_MANAGER_VIEW_FAILED 114 +#define IDS_ATTACH_COMMAND_TITLE 115 +#define IDS_ATTACH_COMMAND_DESC 116 +#define IDS_DETACH_COMMAND_TITLE 117 +#define IDS_DETACH_COMMAND_DESC 118 +#define IDS_EJECT_COMMAND_TITLE 119 +#define IDS_EJECT_COMMAND_DESC 120 +#define IDS_DEVICE_TYPE_SHORT 121 +#define IDS_DEVICE_CONNECTION_SHORT 122 +#define IDS_TOTAL_SPACE 123 +#define IDS_FREE_SPACE 124 +#define IDS_USED_SPACE 125 +#define IDS_FRACTION_SEPARATOR 126 +#define IDS_DEVICE_ACTIVITY_SHORT 132 +#define IDS_PLUGIN_HELP_URL 133 +#define IDS_RENAME_COMMAND_TITLE 134 +#define IDS_RENAME_COMMAND_DESC 135 +#define IDS_VIEW_OPEN_COMMAND_TITLE 136 +#define IDS_VIEW_OPEN_COMMAND_DESC 137 +#define IDS_PREFERENCES_COMMAND_TITLE 138 +#define IDS_PREFERENCES_COMMAND_DESC 139 +#define IDS_PLAYLIST_CREATE_COMMAND_TITLE 140 +#define IDS_PLAYLIST_CREATE_COMMAND_DESC 141 +#define IDS_MESSAGEBOX_TITLE 142 +#define IDS_MESSAGE_UNABLE_TO_RENAME 143 +#define IDS_DEVICE_MODEL_SHORT 144 +#define IDS_DEVICE_STATUS_SHORT 145 +#define IDS_STATUS_DISCOVERY_ACTIVE 146 +#define IDS_STATUS_SPACE_TEMPLATE 147 +#define IDS_CATEGORY_ATTACHED_EMPTY_TEXT 148 +#define IDS_WELCOMEWIDGET_HELP_URL 149 +#define IDS_HELP_LINK 150 +#define IDC_BUTTON_DISCOVER 1001 +#define IDC_SLIDER_ZOOM 1002 +#define IDR_WELCOME_WIDGET_TITLE_IMAGE 19999 +#define IDR_WELCOME_WIDGET_IMAGE 20000 +#define IDR_SPACEBAR_IMAGE 20001 +#define IDR_CATEGORY_ARROWS_IMAGE 20002 +#define IDR_ITEM_HOVER_IMAGE 20003 +#define IDR_ITEM_SELECT_IMAGE 20004 +#define IDR_COMMAND_BACKGROUND_IMAGE 20005 +#define IDR_COMMAND_SECONDARY_BACKGROUND_IMAGE 20006 +#define IDR_ACTION_BACKGROUND_IMAGE 20007 +#define IDR_PROGRESS_LARGE_IMAGE 20008 +#define IDR_PROGRESS_SMALL_IMAGE 20009 +#define IDR_GENERIC_DEVICE_160x160_IMAGE 20010 +#define IDR_GENERIC_DEVICE_16x16_IMAGE 20011 +#define IDR_UNKNOWN_COMMAND_LARGE_IMAGE 20012 +#define IDR_UNKNOWN_COMMAND_SMALL_IMAGE 20013 +#define IDR_SYNC_COMMAND_LARGE_IMAGE 20014 +#define IDR_SYNC_COMMAND_SMALL_IMAGE 20015 +#define IDR_CANCEL_SYNC_COMMAND_SMALL_IMAGE 20017 +#define IDR_ATTACH_COMMAND_LARGE_IMAGE 20018 +#define IDR_ATTACH_COMMAND_SMALL_IMAGE 20019 +#define IDR_DETACH_COMMAND_LARGE_IMAGE 20020 +#define IDR_DETACH_COMMAND_SMALL_IMAGE 20021 +#define IDR_EJECT_COMMAND_SMALL_IMAGE 20023 +#define ID_NAVIGATION_OPEN_VIEW 40005 +#define ID_DISCOVERY_BEGIN 40006 +#define ID_SHOW_PREFERENCES 40007 +#define ID_SHOW_HELP 40008 +#define ID_OPEN_VIEW 40009 +#define ID_VIEW_OPEN 40010 +#define ID_PLUGIN_PREFERENCES 40012 +#define ID_PLUGIN_HELP 40013 +#define IDS_PLUGIN_NAME 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40014 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Library/ml_devices/resources/action-bg.png b/Src/Plugins/Library/ml_devices/resources/action-bg.png Binary files differnew file mode 100644 index 00000000..3831d9dc --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/action-bg.png diff --git a/Src/Plugins/Library/ml_devices/resources/arrows.png b/Src/Plugins/Library/ml_devices/resources/arrows.png Binary files differnew file mode 100644 index 00000000..c5229d40 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/arrows.png diff --git a/Src/Plugins/Library/ml_devices/resources/attach-command-large.png b/Src/Plugins/Library/ml_devices/resources/attach-command-large.png Binary files differnew file mode 100644 index 00000000..bb645908 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/attach-command-large.png diff --git a/Src/Plugins/Library/ml_devices/resources/attach-command-small.png b/Src/Plugins/Library/ml_devices/resources/attach-command-small.png Binary files differnew file mode 100644 index 00000000..64f2e081 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/attach-command-small.png diff --git a/Src/Plugins/Library/ml_devices/resources/cancel-sync-command-small.png b/Src/Plugins/Library/ml_devices/resources/cancel-sync-command-small.png Binary files differnew file mode 100644 index 00000000..a467a588 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/cancel-sync-command-small.png diff --git a/Src/Plugins/Library/ml_devices/resources/command-bg.png b/Src/Plugins/Library/ml_devices/resources/command-bg.png Binary files differnew file mode 100644 index 00000000..ac50f5b0 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/command-bg.png diff --git a/Src/Plugins/Library/ml_devices/resources/command-secondary-bg.png b/Src/Plugins/Library/ml_devices/resources/command-secondary-bg.png Binary files differnew file mode 100644 index 00000000..3831d9dc --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/command-secondary-bg.png diff --git a/Src/Plugins/Library/ml_devices/resources/commands/detach.png b/Src/Plugins/Library/ml_devices/resources/commands/detach.png Binary files differnew file mode 100644 index 00000000..074cebfd --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/commands/detach.png diff --git a/Src/Plugins/Library/ml_devices/resources/commands/eject.png b/Src/Plugins/Library/ml_devices/resources/commands/eject.png Binary files differnew file mode 100644 index 00000000..afa2e21d --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/commands/eject.png diff --git a/Src/Plugins/Library/ml_devices/resources/commands/settings.png b/Src/Plugins/Library/ml_devices/resources/commands/settings.png Binary files differnew file mode 100644 index 00000000..0e3ebacb --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/commands/settings.png diff --git a/Src/Plugins/Library/ml_devices/resources/commands/sync.png b/Src/Plugins/Library/ml_devices/resources/commands/sync.png Binary files differnew file mode 100644 index 00000000..86cb780a --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/commands/sync.png diff --git a/Src/Plugins/Library/ml_devices/resources/connections/bluetooth.png b/Src/Plugins/Library/ml_devices/resources/connections/bluetooth.png Binary files differnew file mode 100644 index 00000000..57d7062b --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/connections/bluetooth.png diff --git a/Src/Plugins/Library/ml_devices/resources/connections/cloud.png b/Src/Plugins/Library/ml_devices/resources/connections/cloud.png Binary files differnew file mode 100644 index 00000000..57c95556 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/connections/cloud.png diff --git a/Src/Plugins/Library/ml_devices/resources/connections/usb.png b/Src/Plugins/Library/ml_devices/resources/connections/usb.png Binary files differnew file mode 100644 index 00000000..961dbcee --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/connections/usb.png diff --git a/Src/Plugins/Library/ml_devices/resources/connections/wifi.png b/Src/Plugins/Library/ml_devices/resources/connections/wifi.png Binary files differnew file mode 100644 index 00000000..b0de726a --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/connections/wifi.png diff --git a/Src/Plugins/Library/ml_devices/resources/detach-command-large.png b/Src/Plugins/Library/ml_devices/resources/detach-command-large.png Binary files differnew file mode 100644 index 00000000..cf38b271 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/detach-command-large.png diff --git a/Src/Plugins/Library/ml_devices/resources/detach-command-small.png b/Src/Plugins/Library/ml_devices/resources/detach-command-small.png Binary files differnew file mode 100644 index 00000000..b34a7515 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/detach-command-small.png diff --git a/Src/Plugins/Library/ml_devices/resources/devices-title-en.png b/Src/Plugins/Library/ml_devices/resources/devices-title-en.png Binary files differnew file mode 100644 index 00000000..fd6bf09b --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/devices-title-en.png diff --git a/Src/Plugins/Library/ml_devices/resources/devices.png b/Src/Plugins/Library/ml_devices/resources/devices.png Binary files differnew file mode 100644 index 00000000..c2f3079f --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/devices.png diff --git a/Src/Plugins/Library/ml_devices/resources/eject-command-small.png b/Src/Plugins/Library/ml_devices/resources/eject-command-small.png Binary files differnew file mode 100644 index 00000000..503aebbe --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/eject-command-small.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/apple.png b/Src/Plugins/Library/ml_devices/resources/food/apple.png Binary files differnew file mode 100644 index 00000000..c4c75712 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/apple.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/banana.png b/Src/Plugins/Library/ml_devices/resources/food/banana.png Binary files differnew file mode 100644 index 00000000..7bb201fd --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/banana.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/bread.png b/Src/Plugins/Library/ml_devices/resources/food/bread.png Binary files differnew file mode 100644 index 00000000..f41e0bd7 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/bread.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/broccoli.png b/Src/Plugins/Library/ml_devices/resources/food/broccoli.png Binary files differnew file mode 100644 index 00000000..0e27da6f --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/broccoli.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/carrot.png b/Src/Plugins/Library/ml_devices/resources/food/carrot.png Binary files differnew file mode 100644 index 00000000..b435271d --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/carrot.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/cereal.png b/Src/Plugins/Library/ml_devices/resources/food/cereal.png Binary files differnew file mode 100644 index 00000000..2cf401f5 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/cereal.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/cucumber.png b/Src/Plugins/Library/ml_devices/resources/food/cucumber.png Binary files differnew file mode 100644 index 00000000..38878a34 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/cucumber.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/eggplant.png b/Src/Plugins/Library/ml_devices/resources/food/eggplant.png Binary files differnew file mode 100644 index 00000000..a64fb0df --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/eggplant.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/grape.png b/Src/Plugins/Library/ml_devices/resources/food/grape.png Binary files differnew file mode 100644 index 00000000..03094de9 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/grape.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/lemon.png b/Src/Plugins/Library/ml_devices/resources/food/lemon.png Binary files differnew file mode 100644 index 00000000..ffabe0ba --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/lemon.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/muffin.png b/Src/Plugins/Library/ml_devices/resources/food/muffin.png Binary files differnew file mode 100644 index 00000000..f47db34d --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/muffin.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/orange.png b/Src/Plugins/Library/ml_devices/resources/food/orange.png Binary files differnew file mode 100644 index 00000000..431f1cb3 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/orange.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/pasta.png b/Src/Plugins/Library/ml_devices/resources/food/pasta.png Binary files differnew file mode 100644 index 00000000..6edf4326 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/pasta.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/peach.png b/Src/Plugins/Library/ml_devices/resources/food/peach.png Binary files differnew file mode 100644 index 00000000..232fe5a4 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/peach.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/pear.png b/Src/Plugins/Library/ml_devices/resources/food/pear.png Binary files differnew file mode 100644 index 00000000..f25c4823 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/pear.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/pepper.png b/Src/Plugins/Library/ml_devices/resources/food/pepper.png Binary files differnew file mode 100644 index 00000000..1cf1ac68 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/pepper.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/pretzel.png b/Src/Plugins/Library/ml_devices/resources/food/pretzel.png Binary files differnew file mode 100644 index 00000000..f9ebb77d --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/pretzel.png diff --git a/Src/Plugins/Library/ml_devices/resources/food/rice.png b/Src/Plugins/Library/ml_devices/resources/food/rice.png Binary files differnew file mode 100644 index 00000000..f5bc2993 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/food/rice.png diff --git a/Src/Plugins/Library/ml_devices/resources/generic-device-160x160.png b/Src/Plugins/Library/ml_devices/resources/generic-device-160x160.png Binary files differnew file mode 100644 index 00000000..2c934863 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/generic-device-160x160.png diff --git a/Src/Plugins/Library/ml_devices/resources/generic-device-16x16.png b/Src/Plugins/Library/ml_devices/resources/generic-device-16x16.png Binary files differnew file mode 100644 index 00000000..537d4ef2 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/generic-device-16x16.png diff --git a/Src/Plugins/Library/ml_devices/resources/item-hover.png b/Src/Plugins/Library/ml_devices/resources/item-hover.png Binary files differnew file mode 100644 index 00000000..20190ae8 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/item-hover.png diff --git a/Src/Plugins/Library/ml_devices/resources/item-select.png b/Src/Plugins/Library/ml_devices/resources/item-select.png Binary files differnew file mode 100644 index 00000000..9bee9f57 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/item-select.png diff --git a/Src/Plugins/Library/ml_devices/resources/progress-large.png b/Src/Plugins/Library/ml_devices/resources/progress-large.png Binary files differnew file mode 100644 index 00000000..5eecb405 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/progress-large.png diff --git a/Src/Plugins/Library/ml_devices/resources/progress-small.png b/Src/Plugins/Library/ml_devices/resources/progress-small.png Binary files differnew file mode 100644 index 00000000..63eadd23 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/progress-small.png diff --git a/Src/Plugins/Library/ml_devices/resources/spacebar.png b/Src/Plugins/Library/ml_devices/resources/spacebar.png Binary files differnew file mode 100644 index 00000000..29114b2b --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/spacebar.png diff --git a/Src/Plugins/Library/ml_devices/resources/sync-command-large.png b/Src/Plugins/Library/ml_devices/resources/sync-command-large.png Binary files differnew file mode 100644 index 00000000..86cb780a --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/sync-command-large.png diff --git a/Src/Plugins/Library/ml_devices/resources/sync-command-small.png b/Src/Plugins/Library/ml_devices/resources/sync-command-small.png Binary files differnew file mode 100644 index 00000000..3eff1403 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/sync-command-small.png diff --git a/Src/Plugins/Library/ml_devices/resources/unknown-command-large.png b/Src/Plugins/Library/ml_devices/resources/unknown-command-large.png Binary files differnew file mode 100644 index 00000000..24e53214 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/unknown-command-large.png diff --git a/Src/Plugins/Library/ml_devices/resources/zoom/192x256.png b/Src/Plugins/Library/ml_devices/resources/zoom/192x256.png Binary files differnew file mode 100644 index 00000000..890461ca --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/zoom/192x256.png diff --git a/Src/Plugins/Library/ml_devices/resources/zoom/24x32.png b/Src/Plugins/Library/ml_devices/resources/zoom/24x32.png Binary files differnew file mode 100644 index 00000000..b93b7ce9 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/zoom/24x32.png diff --git a/Src/Plugins/Library/ml_devices/resources/zoom/48x64.png b/Src/Plugins/Library/ml_devices/resources/zoom/48x64.png Binary files differnew file mode 100644 index 00000000..c6d159f5 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/zoom/48x64.png diff --git a/Src/Plugins/Library/ml_devices/resources/zoom/96x128.png b/Src/Plugins/Library/ml_devices/resources/zoom/96x128.png Binary files differnew file mode 100644 index 00000000..a7838541 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/resources/zoom/96x128.png diff --git a/Src/Plugins/Library/ml_devices/statusBar.cpp b/Src/Plugins/Library/ml_devices/statusBar.cpp new file mode 100644 index 00000000..5f1d3fe2 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/statusBar.cpp @@ -0,0 +1,1001 @@ +#include "main.h" +#include "./statusBar.h" +#include <vector> + +#include <strsafe.h> + +typedef struct StatusTextBlock +{ + wchar_t *text; + size_t length; + long width; +} StatusTextBlock; + +typedef struct StaturRecord +{ + unsigned int id; + StatusTextBlock mainBlock; + StatusTextBlock rightBlock; +} StatusRecord; +typedef std::vector<StatusRecord*> StatusRecordList; + +typedef struct StatusBar +{ + HFONT font; + HBRUSH backBrush; + COLORREF textColor; + COLORREF backColor; + StatusRecordList list; + int idealHeight; + long spacing; + long leftBlockMinWidth; + BackBuffer backBuffer; +} StatusBar; + +#define STATUSBAR(_hwnd) ((StatusBar*)(LONGX86)GetWindowLongPtrW((_hwnd), 0)) +#define STATUSBAR_RET_VOID(_self, _hwnd) {(_self) = STATUSBAR((_hwnd)); if (NULL == (_self)) return;} +#define STATUSBAR_RET_VAL(_self, _hwnd, _error) {(_self) = STATUSBAR((_hwnd)); if (NULL == (_self)) return (_error);} + +static LRESULT CALLBACK +StatusBar_WindowProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam); + + +static ATOM +StatusBar_GetClassAtom(HINSTANCE instance) +{ + WNDCLASSEXW klass; + ATOM klassAtom; + + klassAtom = (ATOM)GetClassInfoExW(instance, STATUSBAR_WINDOW_CLASS, &klass); + if (0 != klassAtom) + return klassAtom; + + memset(&klass, 0, sizeof(klass)); + klass.cbSize = sizeof(klass); + klass.style = 0; + klass.lpfnWndProc = StatusBar_WindowProc; + klass.cbClsExtra = 0; + klass.cbWndExtra = sizeof(StatusBar*); + klass.hInstance = instance; + klass.hIcon = NULL; + klass.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + klass.hbrBackground = NULL; + klass.lpszMenuName = NULL; + klass.lpszClassName = STATUSBAR_WINDOW_CLASS; + klass.hIconSm = NULL; + klassAtom = RegisterClassExW(&klass); + + return klassAtom; +} + +HWND +StatusBar_CreateWindow(unsigned long windowExStyle, const wchar_t *text, unsigned long windowStyle, + int x, int y, int width, int height, + HWND parentWindow, unsigned int controlId) +{ + HINSTANCE instance = GetModuleHandleW(NULL); + ATOM klassAtom = StatusBar_GetClassAtom(instance); + if (0 == klassAtom) + return NULL; + + return CreateWindowExW(WS_EX_NOPARENTNOTIFY | windowExStyle, (LPCWSTR)MAKEINTATOM(klassAtom), text, + WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | windowStyle, + x, y, width, height, parentWindow, (HMENU)controlId, instance, NULL); +} + +static void +StatusBar_InitTextBlock(StatusTextBlock *block) +{ + if (NULL != block) + { + block->text = NULL; + block->length = ((size_t)-1); + block->width = -1; + } +} + +static void +StatusBar_FreeTextBlock(StatusTextBlock *block) +{ + if (NULL != block) + { + ResourceString_Free(block->text); + StatusBar_InitTextBlock(block); + } +} + +static const wchar_t * +StatusBar_RetriveText(StatusTextBlock *block) +{ + const wchar_t *source; + if (NULL == block) + return NULL; + + source = block->text; + if (FALSE != IS_INTRESOURCE(source) && + NULL != source) + { + wchar_t buffer[256] = {0}; + if (NULL == WASABI_API_LNG) + return NULL; + + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)source, buffer, ARRAYSIZE(buffer)); + block->text = String_Duplicate(buffer); + } + return block->text; +} + +static size_t +StatusBar_GetTextBlockLength(StatusTextBlock *block) +{ + const wchar_t *text; + + if (NULL == block) + return NULL; + + if (((size_t)-1) != block->length) + { + text = StatusBar_RetriveText(block); + block->length = (NULL != text) ? lstrlenW(text) : 0; + } + + return block->length; +} + +static const wchar_t * +StatusBar_GetTextBlockInfo(StatusTextBlock *block, HDC hdc) +{ + const wchar_t *text; + + text = StatusBar_RetriveText(block); + + if (((size_t)-1) == block->length) + { + + if (FALSE == IS_STRING_EMPTY(text)) + block->length = lstrlenW(text); + else + block->length = 0; + } + + if (-1 == block->width) + { + block->width = 0; + + if (0 != block->length) + { + RECT rect; + DRAWTEXTPARAMS textParams; + + textParams.cbSize = sizeof(textParams); + textParams.iTabLength = 4; + textParams.iLeftMargin = 0; + textParams.iRightMargin = 0; + + SetRect(&rect, 0, 0, 0x7FFFFFFF, 0); + + if (FALSE != DrawTextEx(hdc, (wchar_t*)text, -1, &rect, + DT_LEFT | DT_TOP | DT_TABSTOP | DT_SINGLELINE | + DT_NOPREFIX | DT_EXPANDTABS | DT_NOCLIP | DT_CALCRECT | DT_EDITCONTROL, + &textParams)) + { + block->width = RECTWIDTH(rect); + } + } + + TEXTMETRIC metrics; + if (FALSE != GetTextMetrics(hdc, &metrics)) + { + block->width += metrics.tmAveCharWidth/2; + } + + } + + return text; +} + +static unsigned int +StatusBar_GetNextFreeId(StatusBar *self) +{ + unsigned int id; + size_t index, count; + StatusRecord *record; + BOOL checkId; + + count = self->list.size(); + id = (unsigned int)count; + do + { + if (0xFFFFFFFF == id) + return STATUS_ERROR; + + checkId = FALSE; + index = count; + while(index--) + { + record = self->list[index]; + if (record->id == id) + { + id++; + checkId = TRUE; + break; + } + } + + } while(FALSE != checkId); + + return id; +} + +static StatusRecord * +StatusBar_AddRecord(StatusBar *self, const wchar_t *text) +{ + StatusRecord *record; + unsigned int id; + + id = StatusBar_GetNextFreeId(self); + if (STATUS_ERROR == id) + return NULL; + + record = (StatusRecord*)malloc(sizeof(StatusRecord)); + if(NULL == record) + return NULL; + + record->id = 0; + StatusBar_InitTextBlock(&record->mainBlock); + StatusBar_InitTextBlock(&record->rightBlock); + + record->mainBlock.text = ResourceString_Duplicate(text); + + self->list.push_back(record); + + return record; +} + +static StatusRecord * +StatusBar_FindRecord(StatusBar *self, unsigned int id, size_t *indexOut) +{ + StatusRecord *record; + size_t index; + + index = self->list.size(); + while(index--) + { + record = self->list[index]; + if (record->id == id) + { + if (NULL != indexOut) + *indexOut = index; + return record; + } + } + + return NULL; +} + +static StatusRecord * +StatusBar_FindVisibleRecord(StatusBar *self, size_t *indexOut) +{ + StatusRecord *record; + size_t index; + const wchar_t *text; + + index = self->list.size(); + while(index--) + { + record = self->list[index]; + + text = StatusBar_RetriveText(&record->mainBlock); + if (FALSE != IS_STRING_EMPTY(text)) + text = StatusBar_RetriveText(&record->rightBlock); + + if (FALSE == IS_STRING_EMPTY(text)) + { + if (NULL != indexOut) + *indexOut = index; + return record; + } + } + + return NULL; +} + +static void +StatusBar_Paint(HWND hwnd, HDC hdc, const RECT *paintRect, BOOL erase) +{ + StatusBar *self; + StatusRecord *record; + const wchar_t *text; + + STATUSBAR_RET_VOID(self, hwnd); + + if (FALSE != erase) + { + FillRect(hdc, paintRect, self->backBrush); + } + + record = StatusBar_FindVisibleRecord(self, NULL); + if (NULL != record) + { + COLORREF prevBackColor, prevTextColor; + HFONT prevFont; + RECT clientRect, textRect; + DRAWTEXTPARAMS textParams; + long limit; + + prevBackColor = SetBkColor(hdc, self->backColor); + prevTextColor = SetTextColor(hdc, self->textColor); + prevFont = SelectFont(hdc, self->font); + + textParams.cbSize = sizeof(textParams); + textParams.iTabLength = 4; + textParams.iLeftMargin = 0; + textParams.iRightMargin = 0; + + GetClientRect(hwnd, &clientRect); + + if (-1 == self->spacing) + self->spacing = Graphics_GetAveStrWidth(hdc, 2); + if (-1 == self->leftBlockMinWidth) + self->leftBlockMinWidth = Graphics_GetAveStrWidth(hdc, 16); + + text = StatusBar_GetTextBlockInfo(&record->rightBlock, hdc); + if (FALSE == IS_STRING_EMPTY(text)) + { + CopyRect(&textRect, &clientRect); + textRect.left = textRect.right - record->rightBlock.width; + limit = clientRect.left + self->spacing + self->leftBlockMinWidth; + if (textRect.left < limit) + textRect.left = limit; + + + if (textRect.left < textRect.right) + { + DrawTextEx(hdc, (wchar_t*)text, -1, &textRect, + DT_LEFT | DT_BOTTOM | DT_TABSTOP | DT_SINGLELINE | + DT_NOPREFIX | DT_EXPANDTABS, &textParams); + } + } + + text = StatusBar_GetTextBlockInfo(&record->mainBlock, hdc); + if (FALSE == IS_STRING_EMPTY(text)) + { + CopyRect(&textRect, &clientRect); + textRect.right = textRect.left + record->mainBlock.width; + limit = clientRect.right - record->rightBlock.width - 8; + if (limit < self->leftBlockMinWidth) + limit = self->leftBlockMinWidth; + if (textRect.right > limit) + textRect.right = limit; + + + if (textRect.left < textRect.right) + { + DrawTextEx(hdc, (wchar_t*)text, -1, &textRect, + DT_LEFT | DT_BOTTOM | DT_TABSTOP | DT_SINGLELINE | + DT_NOPREFIX | DT_EXPANDTABS | DT_END_ELLIPSIS, &textParams); + } + } + + + + SetBkColor(hdc, prevBackColor); + SetTextColor(hdc, prevTextColor); + SelectFont(hdc, prevFont); + } + + return; +} + +static void +StatusBar_PaintBuffered(HWND hwnd, HDC hdc, const RECT *paintRect, BOOL erase) +{ + StatusBar *self; + HDC bufferDC, targetDC; + RECT clientRect; + + STATUSBAR_RET_VOID(self, hwnd); + + GetClientRect(hwnd, &clientRect); + + if (FALSE != BackBuffer_EnsureSizeEx(&self->backBuffer, + RECTWIDTH(*paintRect), RECTHEIGHT(*paintRect), + RECTWIDTH(clientRect), RECTHEIGHT(clientRect))) + { + + bufferDC = BackBuffer_GetDC(&self->backBuffer); + if (NULL != bufferDC) + { + SetViewportOrgEx(bufferDC, -paintRect->left, -paintRect->top, NULL); + targetDC = hdc; + hdc = bufferDC; + } + } + else + { + bufferDC = NULL; + targetDC = NULL; + } + + StatusBar_Paint(hwnd, hdc, paintRect, erase); + + if (NULL != bufferDC) + { + hdc = targetDC; + + SetViewportOrgEx(bufferDC, 0, 0, NULL); + + BackBuffer_Copy(&self->backBuffer, hdc, + paintRect->left, paintRect->top, RECTWIDTH(*paintRect), RECTHEIGHT(*paintRect)); + } + return; +} + +static BOOL +StatusBar_InvalidateByIndex(StatusBar *self, HWND hwnd, size_t recordIndex) +{ + unsigned int windowStyle; + size_t visibleIndex; + + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + return FALSE; + + if(NULL != StatusBar_FindVisibleRecord(self, &visibleIndex)) + { + if (recordIndex == visibleIndex) + return InvalidateRect(hwnd, NULL, FALSE); + } + + return FALSE; + +} + +static BOOL +StatusBar_InvalidateByRecord(StatusBar *self, HWND hwnd, StatusRecord *record) +{ + unsigned int windowStyle; + StatusRecord *visibleRecord; + + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + return FALSE; + + visibleRecord = StatusBar_FindVisibleRecord(self, NULL); + if (record == visibleRecord && + NULL != visibleRecord) + { + return InvalidateRect(hwnd, NULL, FALSE); + } + return FALSE; +} + + +static LRESULT +StatusBar_OnCreate(HWND hwnd, CREATESTRUCT *createStruct) +{ + StatusBar *self; + + self = new StatusBar(); + if (NULL == self) + return -1; + + SetLastError(ERROR_SUCCESS); + if (!SetWindowLongPtr(hwnd, 0, (LONGX86)self) && ERROR_SUCCESS != GetLastError()) + { + delete self; + return -1; + } + + if (NULL != createStruct->lpszName) + StatusBar_AddRecord(self, createStruct->lpszName); + + self->backBrush = GetSysColorBrush(COLOR_WINDOW); + self->backColor = GetSysColor(COLOR_WINDOW); + self->textColor = GetSysColor(COLOR_WINDOWTEXT); + self->idealHeight = -1; + self->spacing = -1; + self->leftBlockMinWidth = -1; + + + BackBuffer_Initialize(&self->backBuffer, hwnd); + + return 0; +} + +static void +StatusBar_OnDestroy(HWND hwnd) +{ + StatusBar *self; + size_t index; + StatusRecord *record; + + self = STATUSBAR(hwnd); + SetWindowLongPtr(hwnd, 0, 0); + + if (NULL == self) + return; + + index = self->list.size(); + while(index--) + { + record = self->list[index]; + StatusBar_FreeTextBlock(&record->mainBlock); + StatusBar_FreeTextBlock(&record->rightBlock); + } + + BackBuffer_Uninitialize(&self->backBuffer); + + delete self; +} + +static void +StatusBar_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + + if (NULL != BeginPaint(hwnd, &ps)) + { + StatusBar_PaintBuffered(hwnd, ps.hdc, &ps.rcPaint, ps.fErase); + EndPaint(hwnd, &ps); + } +} + +static void +StatusBar_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + RECT clientRect; + if (GetClientRect(hwnd, &clientRect)) + { + StatusBar_Paint(hwnd, hdc, &clientRect, TRUE); + } +} + +static void +StatusBar_OnWindowPosChanged(HWND hwnd, WINDOWPOS *windowPos) +{ + if (SWP_NOSIZE != ((SWP_NOSIZE | SWP_FRAMECHANGED) & windowPos->flags) && + 0 == (SWP_NOREDRAW & windowPos->flags)) + { + InvalidateRect(hwnd, NULL, FALSE); + } +} + +static LRESULT +StatusBar_OnSetText(HWND hwnd, LPCWSTR text) +{ + StatusBar *self; + StatusRecord *record; + size_t count; + + STATUSBAR_RET_VAL(self, hwnd, FALSE); + + count = self->list.size(); + if (0 == count) + { + if (NULL == text) + return TRUE; + + record = StatusBar_AddRecord(self, text); + if (NULL == record) + return FALSE; + } + else + { + record = self->list[count - 1]; + StatusBar_FreeTextBlock(&record->mainBlock); + StatusBar_FreeTextBlock(&record->rightBlock); + + record->mainBlock.text = ResourceString_Duplicate(text); + } + + StatusBar_InvalidateByRecord(self, hwnd, record); + + return TRUE; +} + +static LRESULT +StatusBar_OnGetText(HWND hwnd, LPWSTR buffer, size_t bufferMax) +{ + StatusBar *self; + StatusRecord *record; + const wchar_t *lText, *rText; + size_t count, remaining; + wchar_t *cursor; + HRESULT hr; + + STATUSBAR_RET_VAL(self, hwnd, 0); + + if (NULL == buffer) + return 0; + + count = self->list.size(); + if (0 == count) + { + lText = NULL; + rText = NULL; + } + else + { + record = self->list[count - 1]; + lText = StatusBar_RetriveText(&record->mainBlock); + rText = StatusBar_RetriveText(&record->rightBlock); + } + + cursor = buffer; + remaining = bufferMax; + + if (NULL != lText) + hr = StringCchCopyEx(cursor, bufferMax, lText, &cursor, &remaining, 0); + else + hr = S_OK; + + if (NULL != rText && SUCCEEDED(hr)) + { + hr = StringCchCopyEx(cursor, bufferMax, L"\f", &cursor, &remaining, 0); + if (SUCCEEDED(hr)) + hr = StringCchCopyEx(cursor, bufferMax, rText, &cursor, &remaining, 0); + } + + return (bufferMax - remaining); +} + +static LRESULT +StatusBar_OnGetTextLength(HWND hwnd) +{ + StatusBar *self; + StatusRecord *record; + size_t length, r; + + STATUSBAR_RET_VAL(self, hwnd, 0); + + length = self->list.size(); + if (0 == length) + return 0; + + record = self->list[length - 1]; + length = StatusBar_GetTextBlockLength(&record->mainBlock); + r = StatusBar_GetTextBlockLength(&record->rightBlock); + if (0 != r) + { + length += (r + 1); + } + + return length; +} + + +static void +StatusBar_OnSetFont(HWND hwnd, HFONT font, BOOL redraw) +{ + StatusBar *self; + StatusRecord *record; + size_t index; + + STATUSBAR_RET_VOID(self, hwnd); + + self->font = font; + self->idealHeight = -1; + self->spacing = -1; + self->leftBlockMinWidth = -1; + + index = self->list.size(); + while(index--) + { + record = self->list[index]; + record->mainBlock.width = -1; + record->rightBlock.width = -1; + } + + if (NULL != redraw) + InvalidateRect(hwnd, NULL, TRUE); +} + +static LRESULT +StatusBar_OnGetFont(HWND hwnd) +{ + StatusBar *self; + STATUSBAR_RET_VAL(self, hwnd, NULL); + + return (LRESULT)self->font; +} + +static LRESULT +StatusBar_OnSetBackBrush(HWND hwnd, HBRUSH brush, BOOL redraw) +{ + StatusBar *self; + HBRUSH prevBrush; + + STATUSBAR_RET_VAL(self, hwnd, (LRESULT)GetSysColorBrush(COLOR_WINDOW)); + + prevBrush = self->backBrush; + self->backBrush = brush; + + if (NULL != redraw) + InvalidateRect(hwnd, NULL, TRUE); + + return (LRESULT)prevBrush; +} + +static LRESULT +StatusBar_OnGetBackBrush(HWND hwnd) +{ + StatusBar *self; + + STATUSBAR_RET_VAL(self, hwnd, (LRESULT)GetSysColorBrush(COLOR_WINDOW)); + + return (LRESULT)self->backBrush; +} + +static LRESULT +StatusBar_OnSetBackColor(HWND hwnd, COLORREF color, BOOL redraw) +{ + StatusBar *self; + COLORREF prevColor; + + STATUSBAR_RET_VAL(self, hwnd, GetSysColor(COLOR_WINDOW)); + + prevColor = self->backColor; + self->backColor = color; + + if (NULL != redraw) + InvalidateRect(hwnd, NULL, FALSE); + + return (LRESULT)prevColor; +} + +static LRESULT +StatusBar_OnGetBackColor(HWND hwnd) +{ + StatusBar *self; + STATUSBAR_RET_VAL(self, hwnd, GetSysColor(COLOR_WINDOW)); + + return (LRESULT)self->backColor; + +} + +static LRESULT +StatusBar_OnSetTextColor(HWND hwnd, COLORREF color, BOOL redraw) +{ + StatusBar *self; + COLORREF prevColor; + + STATUSBAR_RET_VAL(self, hwnd, GetSysColor(COLOR_WINDOWTEXT)); + + prevColor = self->textColor; + self->textColor = color; + + if (NULL != redraw) + InvalidateRect(hwnd, NULL, FALSE); + + return (LRESULT)prevColor; +} + +static LRESULT +StatusBar_OnGetTextColor(HWND hwnd) +{ + StatusBar *self; + STATUSBAR_RET_VAL(self, hwnd, GetSysColor(COLOR_WINDOWTEXT)); + + return (LRESULT)self->textColor; +} + +static LRESULT +StatusBar_OnGetIdealHeight(HWND hwnd) +{ + StatusBar *self; + STATUSBAR_RET_VAL(self, hwnd, 0); + + if (self->idealHeight < 0) + { + HDC hdc; + + self->idealHeight = 0; + + hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + TEXTMETRIC metrics; + HFONT font, prevFont; + + font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L); + prevFont = SelectFont(hdc, font); + + if (FALSE != GetTextMetrics(hdc, &metrics)) + self->idealHeight = metrics.tmHeight; + + SelectFont(hdc, prevFont); + ReleaseDC(hwnd, hdc); + } + } + + return (LRESULT)self->idealHeight; + +} +static LRESULT +StatusBar_OnAddStatus(HWND hwnd, const wchar_t *text) +{ + StatusBar *self; + StatusRecord *record; + + STATUSBAR_RET_VAL(self, hwnd, STATUS_ERROR); + + record = StatusBar_AddRecord(self, text); + if (NULL == record) + return STATUS_ERROR; + + StatusBar_InvalidateByRecord(self, hwnd, record); + + return record->id; +} + +static LRESULT +StatusBar_OnRemoveStatus(HWND hwnd, unsigned int statusId) +{ + StatusBar *self; + StatusRecord *record; + size_t index; + STATUSBAR_RET_VAL(self, hwnd, FALSE); + + record = StatusBar_FindRecord(self, statusId, &index); + if (NULL == record) + return FALSE; + + StatusBar_InvalidateByRecord(self, hwnd, record); + + self->list.erase(self->list.begin() + index); + + StatusBar_FreeTextBlock(&record->mainBlock); + StatusBar_FreeTextBlock(&record->rightBlock); + + free(record); + + return (LRESULT)TRUE; +} + +static LRESULT +StatusBar_OnSetStatusText(HWND hwnd, unsigned int statusId, const wchar_t *text) +{ + StatusBar *self; + StatusRecord *record; + size_t index; + + STATUSBAR_RET_VAL(self, hwnd, FALSE); + + record = StatusBar_FindRecord(self, statusId, &index); + if (NULL == record) + return FALSE; + + StatusBar_InvalidateByRecord(self, hwnd, record); + + StatusBar_FreeTextBlock(&record->mainBlock); + record->mainBlock.text = ResourceString_Duplicate(text); + + StatusBar_InvalidateByRecord(self, hwnd, record); + + return (LRESULT)TRUE; +} + +static LRESULT +StatusBar_OnSetStatusRightText(HWND hwnd, unsigned int statusId, const wchar_t *text) +{ + StatusBar *self; + StatusRecord *record; + size_t index; + + STATUSBAR_RET_VAL(self, hwnd, FALSE); + + record = StatusBar_FindRecord(self, statusId, &index); + if (NULL == record) + return FALSE; + + StatusBar_InvalidateByRecord(self, hwnd, record); + + StatusBar_FreeTextBlock(&record->rightBlock); + record->rightBlock.text = ResourceString_Duplicate(text); + + StatusBar_InvalidateByRecord(self, hwnd, record); + + return (LRESULT)TRUE; +} + + +static LRESULT +StatusBar_OnMoveStatus(HWND hwnd, unsigned int statusId, unsigned int moveCommand) +{ + StatusBar *self; + StatusRecord *record; + size_t index, count, newIndex; + + STATUSBAR_RET_VAL(self, hwnd, FALSE); + + record = StatusBar_FindRecord(self, statusId, &index); + if (NULL == record) + return FALSE; + + count = self->list.size(); + + switch(moveCommand) + { + + case STATUS_MOVE_BOTTOM: + if (0 == index) + return TRUE; + self->list.erase(self->list.begin() + index); + self->list.insert(self->list.begin(), record); + newIndex = 0; + break; + + case STATUS_MOVE_TOP: + if (index == (count - 1)) + return TRUE; + + self->list.erase(self->list.begin() + index); + self->list.push_back(record); + newIndex = count - 1; + break; + + case STATUS_MOVE_UP: + if (index == (count - 1)) + return TRUE; + + self->list.erase(self->list.begin() + index); + newIndex = index + 1; + self->list.insert(self->list.begin() + newIndex, record); + break; + + case STATUS_MOVE_DOWN: + if (index == 0) + return TRUE; + + self->list.erase(self->list.begin() + index); + newIndex = index - 1; + self->list.insert(self->list.begin() + newIndex, record); + break; + } + + if (index != newIndex) + { + StatusBar_InvalidateByIndex(self, hwnd, index); + StatusBar_InvalidateByIndex(self, hwnd, newIndex); + } + + return (LRESULT)TRUE; +} + +static LRESULT CALLBACK +StatusBar_WindowProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return StatusBar_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: StatusBar_OnDestroy(hwnd); return 0; + case WM_PAINT: StatusBar_OnPaint(hwnd); return 0; + case WM_PRINTCLIENT: StatusBar_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return 0; + case WM_PRINT: return 0; + case WM_ERASEBKGND: return 0; + case WM_WINDOWPOSCHANGED: StatusBar_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_SIZE: return 0; + case WM_MOVE: return 0; + case WM_SETTEXT: return StatusBar_OnSetText(hwnd, (LPCWSTR)lParam); + case WM_GETTEXT: return StatusBar_OnGetText(hwnd, (LPWSTR)lParam, (INT)wParam); + case WM_GETTEXTLENGTH: return StatusBar_OnGetTextLength(hwnd); + case WM_SETFONT: StatusBar_OnSetFont(hwnd, (HFONT)wParam, (BOOL)LOWORD(lParam)); return 0; + case WM_GETFONT: return StatusBar_OnGetFont(hwnd); + + case STATUSBAR_WM_SET_BACK_BRUSH: return StatusBar_OnSetBackBrush(hwnd, (HBRUSH)lParam, (BOOL)wParam); + case STATUSBAR_WM_GET_BACK_BRUSH: return StatusBar_OnGetBackBrush(hwnd); + case STATUSBAR_WM_SET_BACK_COLOR: return StatusBar_OnSetBackColor(hwnd, (COLORREF)lParam, (BOOL)wParam); + case STATUSBAR_WM_GET_BACK_COLOR: return StatusBar_OnGetBackColor(hwnd); + case STATUSBAR_WM_SET_TEXT_COLOR: return StatusBar_OnSetTextColor(hwnd, (COLORREF)lParam, (BOOL)wParam); + case STATUSBAR_WM_GET_TEXT_COLOR: return StatusBar_OnGetTextColor(hwnd); + case STATUSBAR_WM_GET_IDEAL_HEIGHT: return StatusBar_OnGetIdealHeight(hwnd); + case STATUSBAR_WM_ADD_STATUS: return StatusBar_OnAddStatus(hwnd, (const wchar_t*)lParam); + case STATUSBAR_WM_REMOVE_STATUS: return StatusBar_OnRemoveStatus(hwnd, (unsigned int)(wParam)); + case STATUSBAR_WM_SET_STATUS_TEXT: return StatusBar_OnSetStatusText(hwnd, (unsigned int)(wParam), (const wchar_t*)lParam); + case STATUSBAR_WM_SET_STATUS_RTEXT: return StatusBar_OnSetStatusRightText(hwnd, (unsigned int)(wParam), (const wchar_t*)lParam); + case STATUSBAR_WM_MOVE_STATUS: return StatusBar_OnMoveStatus(hwnd, (unsigned int)(wParam), (unsigned int)lParam); + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/statusBar.h b/Src/Plugins/Library/ml_devices/statusBar.h new file mode 100644 index 00000000..692d6d46 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/statusBar.h @@ -0,0 +1,81 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_STATUS_BAR_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_STATUS_BAR_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + + +#define STATUSBAR_WINDOW_CLASS L"NullsoftDevicesStatusBar" + +#define STATUS_ERROR ((unsigned int)-1) + +HWND +StatusBar_CreateWindow(unsigned long windowExStyle, + const wchar_t *text, + unsigned long windowStyle, + int x, + int y, + int width, + int height, + HWND parentWindow, + unsigned int controlId); + +#define STATUSBAR_WM_FIRST (WM_USER + 10) + +#define STATUSBAR_WM_SET_BACK_BRUSH (STATUSBAR_WM_FIRST + 0) +#define STATUSBAR_SET_BACK_BRUSH(/*HWND*/ _hwnd, /*HBRSUSH*/ _brush, /*BOOL*/_redraw)\ + ((HBRUSH)SendMessageW((_hwnd), STATUSBAR_WM_SET_BACK_BRUSH, (WPARAM)(_redraw), (LPARAM)(_brush))) + +#define STATUSBAR_WM_GET_BACK_BRUSH (STATUSBAR_WM_FIRST + 1) +#define STATUSBAR_GET_BRUSH(/*HWND*/ _hwnd)\ + ((HBRUSH)SendMessageW((_hwnd), STATUSBAR_WM_GET_BRUSH, 0, 0L)) + +#define STATUSBAR_WM_SET_BACK_COLOR (STATUSBAR_WM_FIRST + 2) +#define STATUSBAR_SET_BACK_COLOR(/*HWND*/ _hwnd, /*COLORREF*/ _color, /*BOOL*/_redraw)\ + ((COLORREF)SendMessageW((_hwnd), STATUSBAR_WM_SET_BACK_COLOR, (WPARAM)(_redraw), (LPARAM)(_color))) + +#define STATUSBAR_WM_GET_BACK_COLOR (STATUSBAR_WM_FIRST + 3) +#define STATUSBAR_GET_BACK_COLOR(/*HWND*/ _hwnd)\ + ((COLORREF)SendMessageW((_hwnd), STATUSBAR_WM_GET_BACK_COLOR, 0, 0L)) + +#define STATUSBAR_WM_SET_TEXT_COLOR (STATUSBAR_WM_FIRST + 4) +#define STATUSBAR_SET_TEXT_COLOR(/*HWND*/ _hwnd, /*COLORREF*/ _color, /*BOOL*/_redraw)\ + ((COLORREF)SendMessageW((_hwnd), STATUSBAR_WM_SET_TEXT_COLOR, (WPARAM)(_redraw), (LPARAM)(_color))) + +#define STATUSBAR_WM_GET_TEXT_COLOR (STATUSBAR_WM_FIRST + 5) +#define STATUSBAR_GET_TEXT_COLOR(/*HWND*/ _hwnd)\ + ((COLORREF)SendMessageW((_hwnd), STATUSBAR_WM_GET_TEXT_COLOR, 0, 0L)) + +#define STATUSBAR_WM_GET_IDEAL_HEIGHT (STATUSBAR_WM_FIRST + 6) +#define STATUSBAR_GET_IDEAL_HEIGHT(/*HWND*/ _hwnd)\ + ((int)SendMessageW((_hwnd), STATUSBAR_WM_GET_IDEAL_HEIGHT, 0, 0L)) + +#define STATUSBAR_WM_ADD_STATUS (STATUSBAR_WM_FIRST + 7) +#define STATUSBAR_ADD_STATUS(/*HWND*/ _hwnd, /*const wchar_t* */ _statusText)\ + ((unsigned int)SendMessageW((_hwnd), STATUSBAR_WM_ADD_STATUS, 0, (LPARAM)(_statusText))) + +#define STATUSBAR_WM_REMOVE_STATUS (STATUSBAR_WM_FIRST + 8) +#define STATUSBAR_REMOVE_STATUS(/*HWND*/ _hwnd, /*unsigned int*/ _statusId)\ + ((BOOL)SendMessageW((_hwnd), STATUSBAR_WM_REMOVE_STATUS, (WPARAM)(_statusId), 0L)) + +#define STATUSBAR_WM_SET_STATUS_TEXT (STATUSBAR_WM_FIRST + 9) +#define STATUSBAR_SET_STATUS_TEXT(/*HWND*/ _hwnd, /*unsigned int*/ _statusId, /*const wchar_t* */ _statusText)\ + ((BOOL)SendMessageW((_hwnd), STATUSBAR_WM_SET_STATUS_TEXT, (WPARAM)(_statusId), (LPARAM)(_statusText))) + +#define STATUSBAR_WM_SET_STATUS_RTEXT (STATUSBAR_WM_FIRST + 10) +#define STATUSBAR_SET_STATUS_RTEXT(/*HWND*/ _hwnd, /*unsigned int*/ _statusId, /*const wchar_t* */ _statusText)\ + ((BOOL)SendMessageW((_hwnd), STATUSBAR_WM_SET_STATUS_RTEXT, (WPARAM)(_statusId), (LPARAM)(_statusText))) + +#define STATUS_MOVE_TOP 0 +#define STATUS_MOVE_BOTTOM 1 +#define STATUS_MOVE_UP 2 +#define STATUS_MOVE_DOWN 3 + +#define STATUSBAR_WM_MOVE_STATUS (STATUSBAR_WM_FIRST + 11) +#define STATUSBAR_MOVE_STATUS(/*HWND*/ _hwnd, /*unsigned int*/ _statusId, /*unsigned int */ _statusMove)\ + ((BOOL)SendMessageW((_hwnd), STATUSBAR_WM_MOVE_STATUS, (WPARAM)(_statusId), (LPARAM)(_statusMove))) + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_STATUS_BAR_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/strings.cpp b/Src/Plugins/Library/ml_devices/strings.cpp new file mode 100644 index 00000000..bfbef052 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/strings.cpp @@ -0,0 +1,195 @@ +#include "main.h" +#include "./strings.h" +#include <strsafe.h> + + +wchar_t * +String_Malloc(size_t size) +{ + return (wchar_t *)malloc(sizeof(wchar_t) * size); +} + +wchar_t * +String_ReAlloc(wchar_t *string, size_t size) +{ + return (wchar_t *)realloc(string, sizeof(wchar_t) * size); +} + +void +String_Free(wchar_t *string) +{ + if (NULL != string) + free(string); +} + +wchar_t * +String_Duplicate(const wchar_t *string) +{ + int length; + wchar_t *copy; + + if (NULL == string) + return NULL; + + length = lstrlenW(string) + 1; + + copy = String_Malloc(length); + if (NULL != copy) + CopyMemory(copy, string, sizeof(wchar_t) * length); + + return copy; +} + + +char * +String_ToAnsi(unsigned int codePage, unsigned long flags, const wchar_t *string, + int stringLength, const char *defaultChar, BOOL *usedDefaultChar) +{ + char *buffer; + int bufferSize; + + if (stringLength < 0) + stringLength = lstrlen(string); + + bufferSize = WideCharToMultiByte(codePage, flags, string, stringLength, + NULL, 0, defaultChar, usedDefaultChar); + if (0 == bufferSize) + return NULL; + + buffer = AnsiString_Malloc(bufferSize + 1); + if (NULL == buffer) + return NULL; + + bufferSize = WideCharToMultiByte(codePage, flags, string, stringLength, + buffer, bufferSize, defaultChar, usedDefaultChar); + if (0 == bufferSize) + { + AnsiString_Free(buffer); + return NULL; + } + buffer[bufferSize] = '\0'; + return buffer; +} + +size_t +String_CopyTo(wchar_t *destination, const wchar_t *source, size_t size) +{ + size_t remaining; + if (FAILED(StringCchCopyExW(destination, size, source, NULL, &remaining, STRSAFE_IGNORE_NULLS))) + return 0; + + return (size - remaining); +} + + +char * +AnsiString_Malloc(size_t size) +{ + return (char*)malloc(sizeof(char) * size); +} + +char * +AnsiString_ReAlloc(char *string, size_t size) +{ + return (char*)realloc(string, sizeof(char) * size); +} + +void +AnsiString_Free(char *string) +{ + if (NULL != string) + free(string); +} + +char * +AnsiString_Duplicate(const char *string) +{ + char *copy; + INT length; + + if (NULL == string) + return NULL; + + length = lstrlenA(string) + 1; + + copy = AnsiString_Malloc(length); + if (NULL != copy) + CopyMemory(copy, string, sizeof(char) * length); + + return copy; +} + + +wchar_t * +AnsiString_ToUnicode(unsigned int codePage, unsigned long flags, const char* string, INT stringLength) +{ + wchar_t *buffer; + int buffferSize; + + if (NULL == string) + return NULL; + + buffferSize = MultiByteToWideChar(codePage, flags, string, stringLength, NULL, 0); + if (0 == buffferSize) + return NULL; + + if (stringLength > 0) + buffferSize++; + + buffer = String_Malloc(buffferSize); + if (NULL == buffer) + return NULL; + + if (0 == MultiByteToWideChar(codePage, flags, string, stringLength, buffer, buffferSize)) + { + String_Free(buffer); + return NULL; + } + + if (stringLength > 0) + buffer[buffferSize - 1] = L'\0'; + + return buffer; +} + +wchar_t* +ResourceString_Duplicate(const wchar_t *source) +{ + return (FALSE != IS_INTRESOURCE(source)) ? + (LPWSTR)source : + String_Duplicate(source); +} + +void +ResourceString_Free(wchar_t *string) +{ + if (FALSE == IS_INTRESOURCE(string)) + String_Free(string); +} + +size_t +ResourceString_CopyTo(wchar_t *destination, const wchar_t *source, size_t size) +{ + if (NULL == destination) + return 0; + + if (NULL == source) + { + destination[0] = L'\0'; + return 0; + } + + if (FALSE != IS_INTRESOURCE(source)) + { + if (NULL == WASABI_API_LNG) + { + destination[0] = L'\0'; + return 0; + } + + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)source, destination, size); + return lstrlenW(destination); + } + + return String_CopyTo(destination, source, size); +} diff --git a/Src/Plugins/Library/ml_devices/strings.h b/Src/Plugins/Library/ml_devices/strings.h new file mode 100644 index 00000000..52ddfd21 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/strings.h @@ -0,0 +1,74 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_STRINGS_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_STRINGS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#define IS_STRING_EMPTY(_string) (NULL == (_string) || L'\0' == *(_string)) + +wchar_t * +String_Malloc(size_t size); + +wchar_t * +String_ReAlloc(wchar_t *string, + size_t size); + +void +String_Free(wchar_t *string); + +wchar_t * +String_Duplicate(const wchar_t *string); + +char * +String_ToAnsi(unsigned int codePage, + unsigned long flags, + const wchar_t *string, + int stringLength, + const char *defaultChar, + BOOL *usedDefaultChar); + +size_t +String_CopyTo(wchar_t *destination, + const wchar_t *source, + size_t size); +/* + Ansi String +*/ + +char * +AnsiString_Malloc(size_t size); + +char * +AnsiString_ReAlloc(char *string, + size_t size); + +void +AnsiString_Free(char *string); + +char * +AnsiString_Duplicate(const char *string); + +wchar_t * +AnsiString_ToUnicode(unsigned int codePage, + unsigned long flags, + const char *string, + int stringLength); + +/* + Resource String +*/ + +wchar_t* +ResourceString_Duplicate(const wchar_t *source); + +void +ResourceString_Free(wchar_t *string); + +size_t +ResourceString_CopyTo(wchar_t *destination, + const wchar_t *source, + size_t size); + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_PLUGIN_STRINGS_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/version.rc2 b/Src/Plugins/Library/ml_devices/version.rc2 new file mode 100644 index 00000000..a410e817 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,35,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Media Library Plug-in" + VALUE "FileVersion", "1,35,0,0" + VALUE "InternalName", "Nullsoft Device Manager" + VALUE "LegalCopyright", "Copyright © 2010-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "ml_devices.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Library/ml_devices/wasabi.cpp b/Src/Plugins/Library/ml_devices/wasabi.cpp new file mode 100644 index 00000000..f71f4bed --- /dev/null +++ b/Src/Plugins/Library/ml_devices/wasabi.cpp @@ -0,0 +1,107 @@ +#include "main.h" +#include "./wasabi.h" + +#include <api/service/waservicefactory.h> + +api_application *WASABI_API_APP = NULL; +api_language *WASABI_API_LNG = NULL; +api_devicemanager *WASABI_API_DEVICES = NULL; +api_winamp *WASABI_API_WINAMP = NULL; + +HINSTANCE WASABI_API_LNG_HINST = NULL; +HINSTANCE WASABI_API_ORIG_HINST = NULL; + +static unsigned long wasabiReference = 0; +static BOOL defaultServicesLoaded = FALSE; +EXTERN_C winampMediaLibraryPlugin plugin; + +static void Wasabi_Uninitialize() +{ + Wasabi_ReleaseInterface(applicationApiServiceGuid, WASABI_API_APP); + Wasabi_ReleaseInterface(languageApiGUID, WASABI_API_LNG); + Wasabi_ReleaseInterface(DeviceManagerGUID, WASABI_API_DEVICES); + Wasabi_ReleaseInterface(winampApiGuid, WASABI_API_WINAMP); + + WASABI_API_APP = NULL; + WASABI_API_LNG = NULL; + WASABI_API_DEVICES = NULL; + WASABI_API_WINAMP = NULL; + defaultServicesLoaded = FALSE; +} + +BOOL Wasabi_Initialize(HINSTANCE instance) +{ + defaultServicesLoaded = FALSE; + + WASABI_API_APP = NULL; + WASABI_API_DEVICES = NULL; + + WASABI_API_LNG = NULL; + WASABI_API_ORIG_HINST = instance; + WASABI_API_LNG_HINST = WASABI_API_ORIG_HINST; + + Wasabi_AddRef(); + return TRUE; +} + +BOOL Wasabi_InitializeFromWinamp(HINSTANCE instance, HWND winampWindow) +{ + return Wasabi_Initialize(instance); +} + +BOOL Wasabi_LoadDefaultServices(void) +{ + if (FALSE != defaultServicesLoaded) + return FALSE; + + WASABI_API_APP = Wasabi_QueryInterface(api_application, applicationApiServiceGuid); + WASABI_API_DEVICES = Wasabi_QueryInterface(api_devicemanager, DeviceManagerGUID); + WASABI_API_WINAMP = Wasabi_QueryInterface(api_winamp, winampApiGuid); + + WASABI_API_LNG = Wasabi_QueryInterface(api_language, languageApiGUID); + if (NULL != WASABI_API_LNG) + { + WASABI_API_LNG_HINST = WASABI_API_LNG->StartLanguageSupport(WASABI_API_ORIG_HINST, MlDevicesLangGUID); + } + + defaultServicesLoaded = TRUE; + return TRUE; +} + +unsigned long +Wasabi_AddRef(void) +{ + return InterlockedIncrement((LONG*)&wasabiReference); +} + +unsigned long +Wasabi_Release(void) +{ + if (0 == wasabiReference) + return wasabiReference; + + LONG r = InterlockedDecrement((LONG*)&wasabiReference); + if (0 == r) + { + Wasabi_Uninitialize(); + } + return r; +} + +void * Wasabi_QueryInterface0(const GUID &interfaceGuid) +{ + waServiceFactory *serviceFactory = plugin.service->service_getServiceByGuid(interfaceGuid); + if (NULL == serviceFactory) + return NULL; + + return serviceFactory->getInterface(); +} + +void Wasabi_ReleaseInterface0(const GUID &interfaceGuid, void *interfaceInstance) +{ + waServiceFactory *serviceFactory = plugin.service->service_getServiceByGuid(interfaceGuid); + if (NULL == serviceFactory) + return; + + serviceFactory->releaseInterface(interfaceInstance); +} diff --git a/Src/Plugins/Library/ml_devices/wasabi.h b/Src/Plugins/Library/ml_devices/wasabi.h new file mode 100644 index 00000000..071dc8fd --- /dev/null +++ b/Src/Plugins/Library/ml_devices/wasabi.h @@ -0,0 +1,43 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_WASABI_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_WASABI_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <api/application/api_application.h> +#define WASABI_API_APP applicationApi + +#include "../Agave/Language/api_language.h" + +#include "../devices/api_devicemanager.h" +extern api_devicemanager *deviceManagerApi; +#define WASABI_API_DEVICES deviceManagerApi + +#include "../winamp/api_winamp.h" +extern api_winamp *winampApi; +#define WASABI_API_WINAMP winampApi + +BOOL Wasabi_Initialize(HINSTANCE instance); +BOOL Wasabi_InitializeFromWinamp(HINSTANCE instance, HWND winampWindow); +BOOL Wasabi_LoadDefaultServices(void); + +unsigned long Wasabi_AddRef(void); +unsigned long Wasabi_Release(void); + +void * Wasabi_QueryInterface0(const GUID &interfaceGuid); + +#define Wasabi_QueryInterface(_interfaceType, _interfaceGuid)\ + ((##_interfaceType*)Wasabi_QueryInterface0(_interfaceGuid)) + + +void Wasabi_ReleaseInterface0(const GUID &interfaceGuid, void *interfaceInstance); + +#define Wasabi_ReleaseInterface(_interfaceGuid, _interfaceInstance)\ + (Wasabi_ReleaseInterface0((_interfaceGuid), (_interfaceInstance))) + + +#define Wasabi_ReleaseObject(_object)\ + {if (NULL != (_object)) {((Dispatchable*)(_object))->Release();}} + +#endif // _NULLSOFT_WINAMP_ML_DEVICES_WASABI_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/welcomeWidget.cpp b/Src/Plugins/Library/ml_devices/welcomeWidget.cpp new file mode 100644 index 00000000..a4ea327b --- /dev/null +++ b/Src/Plugins/Library/ml_devices/welcomeWidget.cpp @@ -0,0 +1,760 @@ +#include "main.h" +#include "./welcomeWidget.h" +#include "../winamp/commandLink.h" + +#include <strsafe.h> + +#define WELCOMEWIDGET_OFFSET_LEFT_DLU 4 +#define WELCOMEWIDGET_OFFSET_TOP_DLU 2 +#define WELCOMEWIDGET_OFFSET_RIGHT_DLU 4 +#define WELCOMEWIDGET_OFFSET_BOTTOM_DLU 2 + +#define WELCOMEWIDGET_IMAGE_MIN_WIDTH 280 +#define WELCOMEWIDGET_IMAGE_MIN_HEIGHT (424 - 200) + +#define WELCOMEWIDGET_TITLE_MAX_HEIGHT 168 +#define WELCOMEWIDGET_TITLE_MIN_WIDTH WELCOMEWIDGET_IMAGE_MIN_WIDTH + +#define WELCOMEWIDGET_TEXT_MAX_HEIGHT 66 +#define WELCOMEWIDGET_TEXT_MIN_WIDTH WELCOMEWIDGET_IMAGE_MIN_WIDTH + +#define WELCOMEWIDGET_HELP_LINK 1000 + +typedef struct WelcomeWidget +{ + HDC context; + long contextWidth; + long contextHeight; + HBITMAP previousBitmap; + HFONT helpFont; +} WelcomeWidget; + +static BOOL +WelcomeWidget_GetBestTextRect(HDC hdc, const wchar_t *text, unsigned int length, + RECT *rect, long maxWidth, unsigned int format) +{ + RECT testRect; + long right; + + if (NULL == rect) + return FALSE; + + format |= DT_CALCRECT; + + if (length < 0) + length = (NULL != text) ? lstrlen(text) : 0; + + if (0 == length || NULL == text) + { + rect->right = rect->left; + rect->bottom = rect->top; + return TRUE; + } + + + SetRect(&testRect, rect->left, rect->top, rect->right, rect->top); + maxWidth += rect->left; + right = rect->right; + + for(;;) + { + if (FALSE == DrawText(hdc, text, length, &testRect, format)) + return FALSE; + + if (testRect.bottom <= rect->bottom || right >= maxWidth) + { + CopyRect(rect, &testRect); + break; + } + + long increment = maxWidth - right; + if (increment > 60) + increment = increment / 4; + else if (increment > 4) + increment = increment / 2; + + right += increment; + testRect.right = right; + testRect.bottom = rect->top; + } + + return TRUE; +} + +static COLORREF +WelcomeWidget_GetTextColor(WidgetStyle *style, COLORREF *shadowColor, BOOL titleMode) +{ + COLORREF frontColor, backColor; + WORD backHue, backLuma, backSat, frontHue, frontLuma, frontSat; + + backColor = WIDGETSTYLE_BACK_COLOR(style); + frontColor = WIDGETSTYLE_TEXT_COLOR(style); + + ColorRGBToHLS(backColor, &backHue, &backLuma, &backSat); + ColorRGBToHLS(frontColor, &frontHue, &frontLuma, &frontSat); + + if(frontLuma > backLuma) + { + if (NULL != shadowColor) + { + if (FALSE != titleMode || backLuma > 180) + *shadowColor = Graphics_BlendColors(0x000000, backColor, 102); + else + *shadowColor = backColor; + } + + return Graphics_BlendColors((FALSE != titleMode) ? 0xBBBBBB : 0xEEEEEE, frontColor, 230); + } + + if (NULL != shadowColor) + { + if (FALSE != titleMode || backLuma < 60) + *shadowColor = Graphics_BlendColors(0xFFFFFF, backColor, 102); + else + *shadowColor = backColor; + } + + return Graphics_BlendColors((FALSE != titleMode) ? 0x333333 : 0x111111, frontColor, 230); +} + +static HFONT +WelcomeWidget_CreateTitleFont(WidgetStyle *style, HDC hdc) +{ + LOGFONT lf; + HFONT textFont; + long height, pixelsY; + + pixelsY = GetDeviceCaps(hdc, LOGPIXELSY); + height = MulDiv(16, pixelsY, 96); + + textFont = WIDGETSTYLE_TITLE_FONT(style); + if (NULL != textFont) + { + HFONT prevFont; + TEXTMETRIC tm; + + prevFont = SelectFont(hdc, textFont); + if (FALSE != GetTextMetricsW(hdc, &tm)) + { + long test = MulDiv(tm.tmHeight - tm.tmInternalLeading, 96, pixelsY); + test = MulDiv((test + 2), pixelsY, 72); + if (test > height) + height = test; + } + SelectFont(hdc, prevFont); + } + + lf.lfHeight = -height; + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + lf.lfWeight = FW_DONTCARE; + lf.lfItalic = FALSE; + lf.lfUnderline = FALSE; + lf.lfStrikeOut = FALSE; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfOutPrecision = OUT_DEFAULT_PRECIS; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfQuality = Graphics_GetSysFontQuality(); + lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Arial"); + + return CreateFontIndirect(&lf); +} + +static BOOL +WelcomeWidget_FillContext(HDC targetDC, long width, long height, HDC sourceDC, + HBITMAP backBitmap, long backWidth, long backHeight, WidgetStyle *style) +{ + RECT rect, partRect; + wchar_t buffer[4096] = {0}; + int bufferLength; + COLORREF textColor, shadowColor; + HFONT prevFont; + + if (NULL == targetDC || + NULL == sourceDC || + NULL == style) + { + return FALSE; + } + + SetRect(&rect, 0, 0, width, height); + + FillRect(targetDC, &rect, WIDGETSTYLE_BACK_BRUSH(style)); + + + if (NULL != backBitmap) + { + Image_AlphaBlend(targetDC, &rect, sourceDC, NULL, 255, backBitmap, NULL, + AlphaBlend_AlignCenter | AlphaBlend_AlignVCenter, NULL); + } + + SetBkMode(targetDC, TRANSPARENT); + + + WASABI_API_LNGSTRINGW_BUF(IDS_WELCOMEWIDGET_TITLE, buffer, ARRAYSIZE(buffer)); + if (L'\0' != buffer[0]) + { + BOOL textMode; + + textMode = TRUE; + + if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, buffer, -1, L"WINAMP DEVICE MANAGER", -1)) + { + HBITMAP titleBitmap; + titleBitmap = Image_LoadEx(Plugin_GetInstance(), MAKEINTRESOURCE(IDR_WELCOME_WIDGET_TITLE_IMAGE), + SRC_TYPE_PNG, ISF_PREMULTIPLY, 0, 0); + if (NULL != titleBitmap) + { + SetRect(&partRect, rect.left, rect.top, rect.right, rect.top + WELCOMEWIDGET_TITLE_MAX_HEIGHT); + + if (FALSE != Image_AlphaBlend(targetDC, &partRect, sourceDC, NULL, 255, titleBitmap, NULL, + AlphaBlend_AlignCenter | AlphaBlend_AlignVCenter, NULL)) + { + textMode = FALSE; + } + + DeleteObject(titleBitmap); + } + } + + if (FALSE != textMode) + { + HFONT titleFont; + + bufferLength = lstrlen(buffer); + + titleFont = WelcomeWidget_CreateTitleFont(style, targetDC); + + textColor = WelcomeWidget_GetTextColor(style, &shadowColor, TRUE); + prevFont = SelectFont(targetDC, (NULL != titleFont) ? titleFont : WIDGETSTYLE_TITLE_FONT(style)); + + SetRect(&partRect, 0, 0, width, 0); + + if (FALSE != DrawText(targetDC, buffer, bufferLength, &partRect, + DT_CALCRECT | DT_LEFT | DT_TOP | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL | DT_END_ELLIPSIS)) + { + if (partRect.bottom > WELCOMEWIDGET_TITLE_MAX_HEIGHT) + { + TEXTMETRIC textMetrics; + if (FALSE != GetTextMetrics(targetDC, &textMetrics)) + partRect.bottom = (WELCOMEWIDGET_TITLE_MAX_HEIGHT/textMetrics.tmHeight)*textMetrics.tmHeight; + } + + OffsetRect(&partRect, + rect.left + (RECTWIDTH(rect) - RECTWIDTH(partRect))/2, + rect.top + (WELCOMEWIDGET_TITLE_MAX_HEIGHT - RECTHEIGHT(partRect))/2); + + OffsetRect(&partRect, 0, 2); + SetTextColor(targetDC, shadowColor); + DrawText(targetDC, buffer, bufferLength, &partRect, + DT_CENTER | DT_TOP | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL | DT_END_ELLIPSIS); + + OffsetRect(&partRect, 0, -2); + + SetTextColor(targetDC, textColor); + DrawText(targetDC, buffer, bufferLength, &partRect, + DT_CENTER | DT_TOP | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL | DT_END_ELLIPSIS); + } + + SelectFont(targetDC, prevFont); + if (NULL != titleFont) + DeleteObject(titleFont); + } + } + + WASABI_API_LNGSTRINGW_BUF(IDS_WELCOMEWIDGET_TEXT, buffer, ARRAYSIZE(buffer)); + if (L'\0' != buffer[0]) + { + bufferLength = lstrlen(buffer); + + textColor = WelcomeWidget_GetTextColor(style, &shadowColor, FALSE); + prevFont = SelectFont(targetDC, WIDGETSTYLE_TEXT_FONT(style)); + + SetRect(&partRect, 0, 0, WELCOMEWIDGET_TEXT_MIN_WIDTH, WELCOMEWIDGET_TEXT_MAX_HEIGHT); + + if (FALSE != WelcomeWidget_GetBestTextRect(targetDC, buffer, bufferLength, &partRect, width, + DT_LEFT | DT_TOP | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL | DT_END_ELLIPSIS)) + { + if (partRect.bottom > WELCOMEWIDGET_TEXT_MAX_HEIGHT) + { + TEXTMETRIC textMetrics; + if (FALSE != GetTextMetrics(targetDC, &textMetrics)) + partRect.bottom = (WELCOMEWIDGET_TEXT_MAX_HEIGHT/textMetrics.tmHeight)*textMetrics.tmHeight; + } + + OffsetRect(&partRect, + rect.left + (RECTWIDTH(rect) - RECTWIDTH(partRect))/2, + rect.bottom - WELCOMEWIDGET_TEXT_MAX_HEIGHT); + + OffsetRect(&partRect, 0, 1); + SetTextColor(targetDC, shadowColor); + DrawText(targetDC, buffer, bufferLength, &partRect, + DT_LEFT | DT_TOP | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL | DT_END_ELLIPSIS); + + OffsetRect(&partRect, 0, -1); + + SetTextColor(targetDC, textColor); + DrawText(targetDC, buffer, bufferLength, &partRect, + DT_LEFT | DT_TOP | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL | DT_END_ELLIPSIS); + } + + SelectFont(targetDC, prevFont); + } + + return TRUE; +} + +static HDC +WelcomeWidget_CreateContext(HWND hwnd, WidgetStyle *style, HBITMAP *prevBitmapOut, long *widthOut, long *heightOut) +{ + + HDC windowDC, targetDC, sourceDC; + HBITMAP backBitmap, targetBitmap; + HBITMAP prevTargetBitmap; + long width, height, backWidth, backHeight; + + if (NULL == prevBitmapOut || NULL == style) + return NULL; + + windowDC = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL == windowDC) + return NULL; + + backBitmap = Image_LoadEx(Plugin_GetInstance(), MAKEINTRESOURCE(IDR_WELCOME_WIDGET_IMAGE), + SRC_TYPE_PNG, ISF_PREMULTIPLY, 0, 0); + + width = WELCOMEWIDGET_IMAGE_MIN_WIDTH; + height = WELCOMEWIDGET_IMAGE_MIN_HEIGHT; + backWidth = 0; + backHeight = 0; + + if (NULL != backBitmap) + { + BITMAP bi; + if (sizeof(bi) == GetObject(backBitmap, sizeof(bi), &bi)) + { + backWidth = bi.bmWidth; + if (backWidth > width) + width = backWidth; + + backHeight = ABS(bi.bmHeight); + if (backHeight > height) + height = backHeight; + } + } + + targetDC = CreateCompatibleDC(windowDC); + sourceDC = CreateCompatibleDC(windowDC); + targetBitmap = CreateCompatibleBitmap(windowDC, width, height); + prevTargetBitmap = NULL; + + ReleaseDC(hwnd, windowDC); + + if (NULL != targetDC && + NULL != sourceDC && + NULL != targetBitmap) + { + + HFONT prevTargetFont; + prevTargetBitmap = SelectBitmap(targetDC, targetBitmap); + prevTargetFont = GetCurrentFont(targetDC); + HBITMAP prevSourceBitmap = GetCurrentBitmap(sourceDC); + + if (FALSE == WelcomeWidget_FillContext(targetDC, width, height, sourceDC, + backBitmap, backWidth, backHeight, style)) + { + SelectBitmap(targetDC, prevTargetBitmap); + prevTargetBitmap = NULL; + DeleteObject(targetBitmap); + targetBitmap = NULL; + } + + SelectFont(targetDC, prevTargetFont); + SelectBitmap(sourceDC, prevSourceBitmap); + } + + if (NULL != sourceDC) + DeleteDC(sourceDC); + + if (NULL != targetDC && NULL == targetBitmap) + { + DeleteDC(targetDC); + targetDC = NULL; + prevTargetBitmap = NULL; + } + + if (NULL != backBitmap) + DeleteObject(backBitmap); + + *prevBitmapOut = prevTargetBitmap; + if (NULL != widthOut) + *widthOut = width; + if (NULL != heightOut) + *heightOut = height; + + return targetDC; +} + +static void +WelcomeWidget_ResetContext(WelcomeWidget * self) +{ + if (NULL == self || + NULL == self->context) + { + return; + } + + if (NULL != self->previousBitmap) + { + HBITMAP bitmap = SelectBitmap(self->context, self->previousBitmap); + if (NULL != bitmap) + DeleteObject(bitmap); + } + + DeleteDC(self->context); + self->context = NULL; + self->previousBitmap = NULL; + self->contextWidth = 0; + self->contextHeight = 0; +} + +static BOOL +WelcomeWidget_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 BOOL +WelcomeWidget_UpdateHelpPosition(WelcomeWidget *self, HWND hwnd, const RECT *clientRect, BOOL redraw) +{ + HWND elementWindow; + RECT elementRect; + unsigned int positionFlags; + long left, top; + POINT origin; + + if (NULL == self || NULL == hwnd || NULL == clientRect) + return FALSE; + + elementWindow = GetDlgItem(hwnd, WELCOMEWIDGET_HELP_LINK); + if (NULL == elementWindow) + return FALSE; + + + if (FALSE == WelcomeWidget_GetViewOrigin(hwnd, &origin)) + { + origin.x = 0; + origin.y = 0; + } + + if (FALSE == GetWindowRect(elementWindow, &elementRect)) + return FALSE; + + positionFlags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE; + if (FALSE == redraw) + positionFlags |= SWP_NOREDRAW; + + left = clientRect->right; + left -= (RECTWIDTH(elementRect) + 2); + if (left < clientRect->left) + left = clientRect->left; + + top = clientRect->top; + top += 2; + + return SetWindowPos(elementWindow, NULL, left, origin.y + top, 0, 0, positionFlags); +} + +static BOOL +WelcomeWidget_EnsureHelpVisible(WelcomeWidget *self, HWND hwnd) +{ + HWND elementWindow; + POINT origin; + + if (NULL == self || NULL == hwnd) + return FALSE; + + elementWindow = GetDlgItem(hwnd, WELCOMEWIDGET_HELP_LINK); + if (NULL == elementWindow) + return FALSE; + + if (FALSE != WelcomeWidget_GetViewOrigin(hwnd, &origin) && + 0 != origin.y) + { + return WIDGET_SCROLL(hwnd, 0, origin.y, TRUE); + } + + return FALSE; +} + +static BOOL +WelcomeWidget_GetHelpUrl(wchar_t *buffer, size_t bufferMax) +{ + if (NULL == buffer) + return FALSE; + + WASABI_API_LNGSTRINGW_BUF(IDS_WELCOMEWIDGET_HELP_URL, buffer, bufferMax); + return (L'\0' != buffer[0]); +} + +static BOOL +WelcomeWidget_InitCb(HWND hwnd, void **object, void *param) +{ + wchar_t buffer[64] = {0}; + WelcomeWidget *self = (WelcomeWidget*)malloc(sizeof(WelcomeWidget)); + + if (NULL == self) + return FALSE; + + ZeroMemory(self, sizeof(WelcomeWidget)); + WIDGET_ENABLE_CHILDREN_SCROLL(hwnd, FALSE); + + + WASABI_API_LNGSTRINGW_BUF(IDS_HELP_LINK, buffer, ARRAYSIZE(buffer)); + CommandLink_CreateWindow(WS_EX_NOPARENTNOTIFY, + buffer, + WS_CHILD | WS_VISIBLE | WS_TABSTOP | + /*CLS_ALWAYSUNDERLINE |*/ CLS_HOTTRACK | CLS_HIGHLIGHTCOLOR, + 0, 0, 0, 0, hwnd, WELCOMEWIDGET_HELP_LINK); + + *object = self; + + return TRUE; +} + +static void +WelcomeWidget_DestroyCb(WelcomeWidget *self, HWND hwnd) +{ + if (NULL == self) + return; + + WelcomeWidget_ResetContext(self); + if (NULL != self->helpFont) + DeleteObject(self->helpFont); + + free(self); +} + + +static void +WelcomeWidget_LayoutCb(WelcomeWidget *self, HWND hwnd, WidgetStyle *style, + const RECT *clientRect, SIZE *viewSize, BOOL redraw) +{ + if (NULL == self || NULL == style) + return; + + if (NULL == self->context) + { + self->context = WelcomeWidget_CreateContext(hwnd, style, &self->previousBitmap, &self->contextWidth, &self->contextHeight); + if (NULL == self->context) + return; + } + + viewSize->cx = self->contextWidth + + WIDGETSTYLE_DLU_TO_HORZ_PX(style, WELCOMEWIDGET_OFFSET_LEFT_DLU); + + viewSize->cy = self->contextHeight + + WIDGETSTYLE_DLU_TO_VERT_PX(style, WELCOMEWIDGET_OFFSET_TOP_DLU); + + + WelcomeWidget_UpdateHelpPosition(self, hwnd, clientRect, redraw); +} + + +static BOOL +WelcomeWidget_PaintCb(WelcomeWidget *self, HWND hwnd, WidgetStyle *style, HDC hdc, const RECT *paintRect, BOOL erase) +{ + FillRegion fillRegion; + + FillRegion_Init(&fillRegion, paintRect); + + if (NULL != self->context) + { + RECT partRect, rect; + long offsetX, offsetY; + + GetClientRect(hwnd, &rect); + + SetRect(&partRect, 0, 0, self->contextWidth, self->contextHeight); + + offsetX = rect.left + WIDGETSTYLE_DLU_TO_HORZ_PX(style, WELCOMEWIDGET_OFFSET_LEFT_DLU); + offsetY = WIDGETSTYLE_DLU_TO_HORZ_PX(style, WELCOMEWIDGET_OFFSET_RIGHT_DLU); + if ((offsetX + partRect.right + offsetY) < rect.right) + offsetX += (rect.right - (offsetX + partRect.right + offsetY))/2; + + offsetY = rect.top + WIDGETSTYLE_DLU_TO_VERT_PX(style, WELCOMEWIDGET_OFFSET_TOP_DLU); + + OffsetRect(&partRect, offsetX, offsetY); + + if (FALSE != IntersectRect(&rect, &partRect, paintRect) && + FALSE != BitBlt(hdc, rect.left, rect.top, RECTWIDTH(rect), RECTHEIGHT(rect), + self->context, rect.left - partRect.left, rect.top - partRect.top, SRCCOPY)) + { + FillRegion_ExcludeRect(&fillRegion, &rect); + } + + } + + + if (FALSE != erase) + FillRegion_BrushFill(&fillRegion, hdc, WIDGETSTYLE_BACK_BRUSH(style)); + + FillRegion_Uninit(&fillRegion); + + return TRUE; +} + +static void +WelcomeWidget_StyleColorChangedCb(WelcomeWidget *self, HWND hwnd, WidgetStyle *style) +{ + HWND elementWindow; + + if (NULL == self) + return; + + WelcomeWidget_ResetContext(self); + self->context = WelcomeWidget_CreateContext(hwnd, style, &self->previousBitmap, &self->contextWidth, &self->contextHeight); + + elementWindow = GetDlgItem(hwnd, WELCOMEWIDGET_HELP_LINK); + if (NULL != elementWindow) + { + CommandLink_SetBackColor(elementWindow, WIDGETSTYLE_BACK_COLOR(style)); + CommandLink_SetTextColor(elementWindow, WelcomeWidget_GetTextColor(style, NULL, TRUE)); + CommandLink_SetHighlightColor(elementWindow, WelcomeWidget_GetTextColor(style, NULL, FALSE)); + } +} + +static void +WelcomeWidget_StyleFontChangedCb(WelcomeWidget *self, HWND hwnd, WidgetStyle *style) +{ + HWND elementWindow; + + if (NULL == self) + return; + + WelcomeWidget_ResetContext(self); + self->context = WelcomeWidget_CreateContext(hwnd, style, &self->previousBitmap, &self->contextWidth, &self->contextHeight); + + elementWindow = GetDlgItem(hwnd, WELCOMEWIDGET_HELP_LINK); + if (NULL != elementWindow) + { + SIZE elementSize; + + if (NULL != self->helpFont) + DeleteObject(self->helpFont); + + self->helpFont = Graphics_DuplicateFont(WIDGETSTYLE_TEXT_FONT(style), 0, TRUE, TRUE); + + SendMessage(elementWindow, WM_SETFONT, (WPARAM)self->helpFont, 0L); + if (FALSE != CommandLink_GetIdealSize(elementWindow, &elementSize)) + { + SetWindowPos(elementWindow, NULL, 0, 0, elementSize.cx, elementSize.cy, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); + } + } +} + +static void +WelcomeWidget_ScrollCb(WelcomeWidget *self, HWND hwnd, int *dx, int *dy) +{ + RECT clientRect; + if (FALSE != GetClientRect(hwnd, &clientRect)) + WelcomeWidget_UpdateHelpPosition(self, hwnd, &clientRect, TRUE); + +} + +static void +WelcomeWidget_HelpLinkCb(WelcomeWidget *self, HWND hwnd, NMHDR *pnmh, LRESULT *result) +{ + switch(pnmh->code) + { + case NM_CLICK: + { + wchar_t buffer[4096] = {0}; + if (FALSE != WelcomeWidget_GetHelpUrl(buffer, ARRAYSIZE(buffer))) + MediaLibrary_ShowHelp(Plugin_GetLibraryWindow(), buffer); + } + break; + case NM_SETFOCUS: + WelcomeWidget_EnsureHelpVisible(self, hwnd); + break; + + } +} + +static BOOL +WelcomeWidget_NotifyCb(WelcomeWidget *self, HWND hwnd, NMHDR *pnmh, LRESULT *result) +{ + switch(pnmh->idFrom) + { + case WELCOMEWIDGET_HELP_LINK: + WelcomeWidget_HelpLinkCb(self, hwnd, pnmh, result); + return TRUE; + } + + return FALSE; +} + +static BOOL +WelcomeWidget_HelpCb(WelcomeWidget *self, HWND hwnd, wchar_t *buffer, size_t bufferMax) +{ + return WelcomeWidget_GetHelpUrl(buffer, bufferMax); +} + +HWND WelcomeWidget_CreateWindow(HWND parentWindow, int x, int y, int width, int height, BOOL border, unsigned int controlId) +{ + const static WidgetInterface welcomeWidgetInterface = + { + (WidgetInitCallback)WelcomeWidget_InitCb, + (WidgetDestroyCallback)WelcomeWidget_DestroyCb, + (WidgetLayoutCallback)WelcomeWidget_LayoutCb, + (WidgetPaintCallback)WelcomeWidget_PaintCb, + (WidgetStyleCallback)WelcomeWidget_StyleColorChangedCb, + (WidgetStyleCallback)WelcomeWidget_StyleFontChangedCb, + NULL /*mouseMove*/, + NULL /*leftButtonDown*/, + NULL /*leftButtonUp*/, + NULL /*leftButtonDblClk*/, + NULL /*rightButtonDown*/, + NULL /*rightButtonUp*/, + NULL /*keyDown*/, + NULL /*keyUp*/, + NULL /*character*/, + NULL /*inputRequest*/, + NULL /*focusChanged*/, + NULL /*contextMenu*/, + NULL /*zoomChanging*/, + NULL /*scrollBefore*/, + (WidgetScrollCallback)WelcomeWidget_ScrollCb, + (WidgetNotifyCallback)WelcomeWidget_NotifyCb, + (WidgetHelpCallback)WelcomeWidget_HelpCb, + + }; + + return Widget_CreateWindow(WIDGET_TYPE_WELCOME, + &welcomeWidgetInterface, + NULL, + ((FALSE != border) ? WS_EX_CLIENTEDGE : 0) | WS_EX_CONTROLPARENT, + WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + x, y, width, height, + parentWindow, + controlId, 0L); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/welcomeWidget.h b/Src/Plugins/Library/ml_devices/welcomeWidget.h new file mode 100644 index 00000000..1a5e7819 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/welcomeWidget.h @@ -0,0 +1,20 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_WELCOME_WIDGET_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_WELCOME_WIDGET_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + + + +HWND WelcomeWidget_CreateWindow(HWND parentWindow, + int x, + int y, + int width, + int height, + BOOL border, + unsigned int controlId); + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_WELCOME_WIDGET_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/widget.cpp b/Src/Plugins/Library/ml_devices/widget.cpp new file mode 100644 index 00000000..c5e99c6e --- /dev/null +++ b/Src/Plugins/Library/ml_devices/widget.cpp @@ -0,0 +1,1284 @@ +#include "main.h" +#include "./widget.h" + +typedef +enum WidgetState +{ + WIDGET_STATE_MOUSE_MOVE_TRACKED = (1 << 0), + WIDGET_STATE_DISABLE_CHILDREN_SCROLL = (1 << 1), +} WidgetState; + +DEFINE_ENUM_FLAG_OPERATORS(WidgetState); + +#define WIDGET_IS_FROZEN(_widget) (0 != (_widget)->freezer) + +#define WIDGET_IS_MOUSE_MOVE_TRACKED(_widget) (0 != (WIDGET_STATE_MOUSE_MOVE_TRACKED & (_widget)->state)) +#define WIDGET_SET_MOUSE_MOVE_TRACK(_widget) (((_widget)->state) |= WIDGET_STATE_MOUSE_MOVE_TRACKED) +#define WIDGET_UNSET_MOUSE_MOVE_TRACK(_widget) (((_widget)->state) &= ~WIDGET_STATE_MOUSE_MOVE_TRACKED) + + +#define WIDGET_IS_CHILDREN_SCROLL_DISABLED(_widget) (0 != (WIDGET_STATE_DISABLE_CHILDREN_SCROLL & (_widget)->state)) +#define WIDGET_SET_DISABLE_CHILDREN_SCROLL(_widget) (((_widget)->state) |= WIDGET_STATE_DISABLE_CHILDREN_SCROLL) +#define WIDGET_UNSET_DISABLE_CHILDREN_SCROLL(_widget) (((_widget)->state) &= ~WIDGET_STATE_DISABLE_CHILDREN_SCROLL) + +typedef struct Widget +{ + unsigned int type; + WidgetState state; + const WidgetInterface *callbacks; + void *object; + WidgetStyle *style; + wchar_t *text; + HFONT font; + SIZE viewSize; + POINT viewOrigin; + int wheelCarryover; + size_t freezer; +} Widget; + +typedef struct WidgetCreateParam +{ + unsigned int type; + const WidgetInterface *callbacks; + void *param; + const wchar_t *text; +} WidgetCreateParam; + +#define WIDGET(_hwnd) ((Widget*)(LONGX86)GetWindowLongPtrW((_hwnd), 0)) +#define WIDGET_RET_VOID(_view, _hwnd) {(_view) = WIDGET((_hwnd)); if (NULL == (_view)) return;} +#define WIDGET_RET_VAL(_view, _hwnd, _error) {(_view) = WIDGET((_hwnd)); if (NULL == (_view)) return (_error);} + +#define WIDGETSTYLE(_widget) (((Widget*)(_widget))->style) +#define WIDGETOBJECT(_widget) (((Widget*)(_widget))->object) +#define WIDGETCALLBACKS(_widget) (((Widget*)(_widget))->callbacks) + +static UINT WINAMP_WM_DIRECT_MOUSE_WHEEL = WM_NULL; + +static LRESULT CALLBACK +Widget_WindowProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam); + + +static ATOM +Widget_GetClassAtom(HINSTANCE instance) +{ + WNDCLASSEXW klass; + ATOM klassAtom; + + klassAtom = (ATOM)GetClassInfoExW(instance, WIDGET_WINDOW_CLASS, &klass); + if (0 != klassAtom) + return klassAtom; + + memset(&klass, 0, sizeof(klass)); + klass.cbSize = sizeof(klass); + klass.style = CS_DBLCLKS; + klass.lpfnWndProc = Widget_WindowProc; + klass.cbClsExtra = 0; + klass.cbWndExtra = sizeof(Widget*); + klass.hInstance = instance; + klass.hIcon = NULL; + klass.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + klass.hbrBackground = NULL; + klass.lpszMenuName = NULL; + klass.lpszClassName = WIDGET_WINDOW_CLASS; + klass.hIconSm = NULL; + klassAtom = RegisterClassExW(&klass); + + return klassAtom; +} + +HWND +Widget_CreateWindow(unsigned int type, const WidgetInterface *callbacks, + const wchar_t *text, unsigned long windowExStyle, unsigned long windowStyle, + int x, int y, int width, int height, + HWND parentWindow, unsigned int controlId, void *param) +{ + HINSTANCE instance; + ATOM klassAtom; + HWND hwnd; + WidgetCreateParam createParam; + + if (NULL == parentWindow || FALSE == IsWindow(parentWindow)) + return NULL; + + instance = GetModuleHandleW(NULL); + klassAtom = Widget_GetClassAtom(instance); + if (0 == klassAtom) + return NULL; + + createParam.type = type; + createParam.param = param; + createParam.callbacks = callbacks; + createParam.text = text; + + hwnd = CreateWindowExW(WS_EX_NOPARENTNOTIFY | windowExStyle, (LPCWSTR)MAKEINTATOM(klassAtom), NULL, + WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | windowStyle, + x, y, width, height, + parentWindow, (HMENU)controlId, instance, &createParam); + + return hwnd; +} + + +static LRESULT +Widget_DefWindowProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) +{ + Widget *self; + + self = WIDGET(hwnd); + + if (NULL != self && NULL != self->callbacks->messageProc) + { + LRESULT result; + result = 0; + if (FALSE != self->callbacks->messageProc(self->object, + hwnd, uMsg, wParam, lParam, &result)) + { + return result; + } + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +static void +Widget_Freeze(Widget *self) +{ + if (NULL != self) + self->freezer++; +} + +static void +Widget_Thaw(Widget *self) +{ + if (NULL != self && 0 != self->freezer) + self->freezer--; +} + +static INT +Widget_ScrollBarOffsetPos(HWND hwnd, INT barType, INT delta, BOOL redraw) +{ + Widget *self; + INT position; + SCROLLINFO scrollInfo; + + self = WIDGET(hwnd); + + scrollInfo.cbSize = sizeof(scrollInfo); + scrollInfo.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; + + if (FALSE == GetScrollInfo(hwnd, barType, &scrollInfo)) + return 0; + + position = scrollInfo.nPos + delta; + + if (position < scrollInfo.nMin) + position = scrollInfo.nMin; + else if (position > (scrollInfo.nMax - (INT)scrollInfo.nPage)) + position = scrollInfo.nMax - (INT)scrollInfo.nPage + 1; + + delta = position - scrollInfo.nPos; + + scrollInfo.fMask = SIF_POS; + scrollInfo.nPos = position; + SetScrollInfo(hwnd, barType, &scrollInfo, redraw); + + if (NULL != self) + { + if (SB_HORZ == barType) + self->viewOrigin.x = -position; + else + self->viewOrigin.y = -position; + } + + return delta; +} + +static BOOL +Widget_ScrollContent(HWND hwnd, int dx, int dy, BOOL redraw) +{ + Widget *self; + UINT scrollFlags; + HRGN invalidRgn; + INT scrollError; + + if (0 == dx && 0 == dy) + return FALSE; + + self = WIDGET(hwnd); + if (NULL != self && + NULL != self->callbacks && + NULL != self->callbacks->scrollBefore) + { + self->callbacks->scrollBefore(WIDGETOBJECT(self), hwnd, &dx, &dy); + if (0 == dx && 0 == dy) + return FALSE; + } + + + scrollFlags = (FALSE == WIDGET_IS_CHILDREN_SCROLL_DISABLED(self)) ? SW_SCROLLCHILDREN : 0; + if (FALSE != redraw) + { + invalidRgn = CreateRectRgn(0, 0, 0, 0); + scrollFlags |= SW_INVALIDATE | SW_ERASE; + } + else + { + invalidRgn = NULL; + } + + scrollError = ScrollWindowEx(hwnd, -dx, -dy, NULL, NULL, invalidRgn, NULL, scrollFlags); + if (ERROR != scrollError) + { + if (NULL != self && + NULL != self->callbacks && + NULL != self->callbacks->scroll) + { + self->callbacks->scroll(WIDGETOBJECT(self), hwnd, &dx, &dy); + } + + if (FALSE != redraw && NULLREGION != scrollError) + { + RedrawWindow(hwnd, NULL, invalidRgn, + RDW_ERASENOW | RDW_UPDATENOW | RDW_ALLCHILDREN); + } + } + + if (NULL != invalidRgn) + DeleteObject(invalidRgn); + + return (ERROR != scrollError); +} + +static BOOL +Widget_SyncContentOrigin(HWND hwnd, BOOL redraw) +{ + Widget *self; + RECT clientRect; + SCROLLINFO scrollInfo; + INT dx, dy; + + WIDGET_RET_VAL(self, hwnd, FALSE); + + + scrollInfo.cbSize = sizeof(scrollInfo); + scrollInfo.fMask = SIF_POS; + + if (FALSE == GetClientRect(hwnd, &clientRect)) + SetRectEmpty(&clientRect); + + if (self->viewSize.cx < RECTWIDTH(clientRect)) + { + scrollInfo.nPos = 0; + dx = scrollInfo.nPos + self->viewOrigin.x; + self->viewOrigin.x = 0; + } + else if (FALSE != GetScrollInfo(hwnd, SB_HORZ, &scrollInfo)) + { + dx = scrollInfo.nPos + self->viewOrigin.x; + self->viewOrigin.x = -scrollInfo.nPos; + } + else + dx = 0; + + if (FALSE != GetScrollInfo(hwnd, SB_VERT, &scrollInfo)) + { + dy = scrollInfo.nPos + self->viewOrigin.y; + self->viewOrigin.y = -scrollInfo.nPos; + } + else + dy = 0; + + if (0 == dx && 0 == dy) + return FALSE; + + return Widget_ScrollContent(hwnd, dx, dy, redraw); +} + + +static BOOL +Widget_ScrollWindow(HWND hwnd, INT dx, INT dy, BOOL redraw) +{ + if (0 != dx) + dx = Widget_ScrollBarOffsetPos(hwnd, SB_HORZ, dx, redraw); + + if (0 != dy) + dy = Widget_ScrollBarOffsetPos(hwnd, SB_VERT, dy, redraw); + + return Widget_ScrollContent(hwnd, dx, dy, redraw); +} + +static BOOL +Widget_ScrollBarAction(HWND hwnd, INT barType, INT actionLayout, INT line, BOOL redraw) +{ + INT delta; + SCROLLINFO scrollInfo; + + scrollInfo.cbSize = sizeof(scrollInfo); + scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS; + + if (FALSE == GetScrollInfo(hwnd, barType, &scrollInfo)) + return FALSE; + + switch(actionLayout) + { + case SB_BOTTOM: + delta = scrollInfo.nMax - scrollInfo.nPos; + break; + case SB_TOP: + delta = scrollInfo.nMin - scrollInfo.nPos; + break; + case SB_LINEDOWN: + delta = line; + break; + case SB_LINEUP: + delta = -line; + break; + case SB_PAGEDOWN: + delta = (INT)scrollInfo.nPage; + break; + case SB_PAGEUP: + delta = -(INT)scrollInfo.nPage; + break; + case SB_THUMBTRACK: + delta = scrollInfo.nTrackPos - scrollInfo.nPos; + break; + case SB_THUMBPOSITION: + case SB_ENDSCROLL: + delta = 0; + break; + default: + return FALSE; + } + + if(0 != delta) + { + Widget_ScrollWindow(hwnd, + (SB_HORZ == barType) ? delta : 0, + (SB_VERT == barType) ? delta : 0, + redraw); + } + else + Widget_SyncContentOrigin(hwnd, redraw); + + return TRUE; +} + +static BOOL +Widget_ScrollBarUpdate(HWND hwnd, INT barType, UINT page, INT max, BOOL redraw) +{ + Widget *self; + SCROLLINFO scrollInfo; + UINT windowStyle, styleFilter; + + WIDGET_RET_VAL(self, hwnd, FALSE); + + windowStyle = GetWindowStyle(hwnd); + + switch(barType) + { + case SB_HORZ: styleFilter = WS_HSCROLL; break; + case SB_VERT: styleFilter = WS_VSCROLL; break; + default: return FALSE; + } + + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_PAGE | SIF_RANGE; + + if (page >= (UINT)max) + { + if (0 == (styleFilter & windowStyle)) + return FALSE; + + scrollInfo.nPage = page + 1; + scrollInfo.nMin = 0; + scrollInfo.nMax = max; + scrollInfo.nPos = scrollInfo.nMin; + scrollInfo.nTrackPos = scrollInfo.nPos; + scrollInfo.fMask |= (SIF_POS | SIF_TRACKPOS); + + Widget_Freeze(self); + SetScrollInfo(hwnd, barType, &scrollInfo, redraw); + Widget_Thaw(self); + + windowStyle = GetWindowStyle(hwnd); + if (0 != (styleFilter & windowStyle)) + SetWindowStyle(hwnd, windowStyle & ~styleFilter); + + return TRUE; + } + + + + if (FALSE == GetScrollInfo(hwnd, barType, &scrollInfo)) + { + if (ERROR_NO_SCROLLBARS == GetLastError()) + { + scrollInfo.nPage = 0; + scrollInfo.nMax = 0; + scrollInfo.nMin = 0; + scrollInfo.nPos = scrollInfo.nMin; + scrollInfo.nTrackPos = scrollInfo.nPos; + } + else + return FALSE; + } + + scrollInfo.fMask = 0; + + if (scrollInfo.nPage != page) + { + scrollInfo.nPage = page; + scrollInfo.fMask |= SIF_PAGE; + } + + if (scrollInfo.nMax != max) + { + scrollInfo.nMax = max; + scrollInfo.fMask |= SIF_RANGE; + } + + if (0 == (styleFilter & windowStyle)) + { + scrollInfo.fMask |= (SIF_POS | SIF_TRACKPOS); + scrollInfo.nPos = scrollInfo.nMin; + scrollInfo.nTrackPos = scrollInfo.nMin; + } + + if (0 == scrollInfo.fMask) + return FALSE; + + Widget_Freeze(self); + SetScrollInfo(hwnd, barType, &scrollInfo, redraw); + Widget_Thaw(self); + + + if (0 == (styleFilter & windowStyle)) + { + windowStyle = GetWindowStyle(hwnd); + if (0 == (styleFilter & windowStyle)) + SetWindowStyle(hwnd, windowStyle | styleFilter); + + return TRUE; + } + + return FALSE; +} + +static void +Widget_Layout(HWND hwnd, BOOL redraw) +{ + Widget *self; + RECT rect; + size_t iteration; + + WIDGET_RET_VOID(self, hwnd); + + iteration = 0; + do + { + if (iteration++ > 2) + break; + + if (FALSE == GetClientRect(hwnd, &rect)) + break; + + SetSize(&self->viewSize, 0, 0); + + if (NULL != self->callbacks->layout) + { + self->callbacks->layout(self->object, hwnd, self->style, &rect, &self->viewSize, redraw); + } + + if (FALSE != IsSizeEmpty(&self->viewSize)) + SetSize(&self->viewSize, RECTWIDTH(rect), RECTHEIGHT(rect)); + + } + while(FALSE != Widget_ScrollBarUpdate(hwnd, SB_HORZ, RECTWIDTH(rect), self->viewSize.cx, redraw) || + FALSE != Widget_ScrollBarUpdate(hwnd, SB_VERT, RECTHEIGHT(rect), self->viewSize.cy, redraw)); + + Widget_SyncContentOrigin(hwnd, redraw); +} + +static BOOL +Widget_Paint(HWND hwnd, HDC hdc, const RECT *paintRect, BOOL erase) +{ + Widget *self; + BOOL result; + + self = WIDGET(hwnd); + if (NULL == self || NULL == self->style) + return FALSE; + + + if (NULL != self->callbacks->paint) + { + POINT prevOrigin; + RECT viewRect; + + CopyRect(&viewRect, paintRect); + OffsetRect(&viewRect, -self->viewOrigin.x, -self->viewOrigin.y); + + OffsetViewportOrgEx(hdc, self->viewOrigin.x, self->viewOrigin.y, &prevOrigin); + + result = self->callbacks->paint(self->object, hwnd, + self->style, hdc, &viewRect, erase); + + SetViewportOrgEx(hdc, prevOrigin.x, prevOrigin.y, NULL); + } + else + result = FALSE; + + if (FALSE == result) + { + if (FALSE != erase) + result = FillRect(hdc, paintRect, WIDGETSTYLE_BACK_BRUSH(self->style)); + else + result = TRUE; + } + + return result; +} + +static void +Widget_FocusChanged(HWND hwnd, HWND focusWindow, BOOL focusReceived) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != self->callbacks && + NULL != self->callbacks->focusChanged) + { + self->callbacks->focusChanged(self->object, hwnd, focusWindow, focusReceived); + } +} + +static LRESULT +Widget_OnCreate(HWND hwnd, CREATESTRUCT *createStruct) +{ + Widget *self; + WidgetCreateParam *createParam; + + if (NULL == createStruct) + return -1; + + createParam = (WidgetCreateParam*)createStruct->lpCreateParams; + if (NULL == createParam) + return -1; + + self = (Widget*)malloc(sizeof(Widget)); + if (NULL == self) + return -1; + + SetLastError(ERROR_SUCCESS); + if (!SetWindowLongPtr(hwnd, 0, (LONGX86)self) && ERROR_SUCCESS != GetLastError()) + return -1; + + memset(self, 0, sizeof(Widget)); + + if (WM_NULL == WINAMP_WM_DIRECT_MOUSE_WHEEL) + WINAMP_WM_DIRECT_MOUSE_WHEEL = RegisterWindowMessageW(L"WINAMP_WM_DIRECT_MOUSE_WHEEL"); + + self->type = createParam->type; + self->callbacks = createParam->callbacks; + + Widget_Freeze(self); + + if (NULL != createParam->text) + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)createParam->text); + + MLSkinWindow2(Plugin_GetLibraryWindow(), hwnd, SKINNEDWND_TYPE_SCROLLWND, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + + if (NULL != self->callbacks->init && + FALSE == self->callbacks->init(hwnd, &self->object, createParam->param)) + { + return -1; + } + + Widget_Thaw(self); + + return 0; +} + +static void +Widget_OnDestroy(HWND hwnd) +{ + Widget *self; + + self = WIDGET(hwnd); + SetWindowLongPtr(hwnd, 0, 0); + + if (NULL == self) + return; + + Widget_Freeze(self); + + if (NULL != self->callbacks->destroy) + self->callbacks->destroy(self->object, hwnd); + + String_Free(self->text); + + free(self); +} + +static void +Widget_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + + if (NULL != BeginPaint(hwnd, &ps)) + { + if (FALSE == Widget_Paint(hwnd, ps.hdc, &ps.rcPaint, ps.fErase)) + { + COLORREF backColor, prevBackColor; + + backColor = Graphics_GetSkinColor(WADLG_WNDBG); + prevBackColor = SetBkColor(ps.hdc, backColor); + + ExtTextOut(ps.hdc, 0, 0, ETO_OPAQUE, &ps.rcPaint, NULL, 0, NULL); + SetBkColor(ps.hdc, prevBackColor); + } + EndPaint(hwnd, &ps); + } +} + +static void +Widget_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + RECT clientRect; + if (GetClientRect(hwnd, &clientRect)) + { + Widget_Paint(hwnd, hdc, &clientRect, TRUE); + } +} + +static void +Widget_OnWindowPosChanged(HWND hwnd, WINDOWPOS *windowPos) +{ + if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED) & windowPos->flags)) + { + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (FALSE != WIDGET_IS_FROZEN(self)) + return; + + Widget_Layout(hwnd, 0 == (SWP_NOREDRAW & windowPos->flags)); + } +} + +static LRESULT +Widget_OnSetText(HWND hwnd, LPCWSTR text) +{ + Widget *self; + WIDGET_RET_VAL(self, hwnd, FALSE); + + String_Free(self->text); + + if (NULL == text) + self->text = NULL; + else if (FALSE != IS_INTRESOURCE(text)) + { + WCHAR buffer[4096] = {0}; + ResourceString_CopyTo(buffer, text, ARRAYSIZE(buffer)); + self->text = String_Duplicate(buffer); + } + else + self->text = String_Duplicate(text); + + return TRUE; +} + +static LRESULT +Widget_OnGetText(HWND hwnd, LPWSTR buffer, size_t bufferMax) +{ + Widget *self; + + WIDGET_RET_VAL(self, hwnd, 0); + + return String_CopyTo(buffer, self->text, bufferMax); +} + +static LRESULT +Widget_OnGetTextLength(HWND hwnd) +{ + Widget *self; + WIDGET_RET_VAL(self, hwnd, 0); + + return ( NULL != self->text) ? lstrlenW(self->text) : 0; +} + + +static void +Widget_OnSetFont(HWND hwnd, HFONT font, BOOL redraw) +{ + Widget *self; + + WIDGET_RET_VOID(self, hwnd); + + self->font = font; + + if (NULL != redraw) + InvalidateRect(hwnd, NULL, TRUE); +} + +static HFONT +Widget_OnGetFont(HWND hwnd) +{ + Widget *self; + WIDGET_RET_VAL(self, hwnd, NULL); + + return self->font; +} + +static void +Widget_OnVertScroll(HWND hwnd, INT actionLayout, INT trackPosition, HWND scrollBar) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + Widget_ScrollBarAction(hwnd, SB_VERT, actionLayout, self->style->unitSize.cy, TRUE); +} + +static void +Widget_OnHorzScroll(HWND hwnd, INT actionLayout, INT trackPosition, HWND scrollBar) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + Widget_ScrollBarAction(hwnd, SB_HORZ, actionLayout, self->style->unitSize.cx, TRUE); +} + +static void +Widget_OnMouseWheel(HWND hwnd, INT virtualKeys, INT distance, LONG pointer_s) +{ + Widget *self; + UINT wheelScroll; + INT scrollLines; + UINT windowStyle; + INT barType; + + WIDGET_RET_VOID(self, hwnd); + + windowStyle = GetWindowStyle(hwnd); + + if (0 != (WS_VSCROLL & windowStyle)) + barType = SB_VERT; + else if (0 != (WS_HSCROLL & windowStyle)) + barType = SB_HORZ; + else + return; + + if (FALSE == SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheelScroll, 0)) + wheelScroll = 3; + + if (0 == wheelScroll) + return; + + if (WHEEL_PAGESCROLL == wheelScroll) + { + RECT clientRect; + GetClientRect(hwnd, &clientRect); + if (SB_VERT == barType) + wheelScroll = RECTHEIGHT(clientRect)/self->style->unitSize.cy; + else + wheelScroll = RECTWIDTH(clientRect)/self->style->unitSize.cx; + } + + distance += self->wheelCarryover; + scrollLines = distance * (INT)wheelScroll / WHEEL_DELTA; + + self->wheelCarryover = distance - scrollLines * WHEEL_DELTA / (INT)wheelScroll; + + if (FALSE != Widget_ScrollWindow(hwnd, + (SB_HORZ == barType) ? -(scrollLines * self->style->unitSize.cx) : 0, + (SB_VERT == barType) ? -(scrollLines * self->style->unitSize.cy) : 0, + TRUE)) + { + + } + +} + + +static void +Widget_OnMouseMove(HWND hwnd, unsigned int vKeys, long cursor_s) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != self->callbacks && NULL != self->callbacks->mouseMove) + { + BOOL processed; + POINT cursor; + POINTSTOPOINT(cursor, cursor_s); + + + processed = self->callbacks->mouseMove(self->object, hwnd, vKeys, &cursor); + + if (FALSE == WIDGET_IS_MOUSE_MOVE_TRACKED(self)) + { + TRACKMOUSEEVENT trackMouse; + trackMouse.cbSize = sizeof(trackMouse); + trackMouse.dwFlags = TME_LEAVE; + trackMouse.hwndTrack = hwnd; + if (FALSE != TrackMouseEvent(&trackMouse)) + WIDGET_SET_MOUSE_MOVE_TRACK(self); + } + + if (FALSE != processed) + return; + } + + DefWindowProc(hwnd, WM_MOUSEMOVE, (WPARAM)vKeys, (LPARAM)cursor_s); +} + +static void +Widget_OnMouseLeave(HWND hwnd) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + WIDGET_UNSET_MOUSE_MOVE_TRACK(self); + + if (NULL != self->callbacks && NULL != self->callbacks->mouseMove) + { + POINT cursor; + cursor.x = 0xEFFFFFFF; + cursor.y = 0xEFFFFFFF; + + if (FALSE != self->callbacks->mouseMove(self->object, hwnd, 0, &cursor)) + return; + } + + DefWindowProc(hwnd, WM_MOUSELEAVE, 0, 0L); + +} + +static void +Widget_OnLeftButtonDown(HWND hwnd, unsigned int vKeys, long cursor_s) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != self->callbacks && NULL != self->callbacks->leftButtonDown) + { + POINT cursor; + POINTSTOPOINT(cursor, cursor_s); + if (FALSE != self->callbacks->leftButtonDown(self->object, hwnd, vKeys, &cursor)) + return; + } + + DefWindowProc(hwnd, WM_LBUTTONDOWN, (WPARAM)vKeys, (LPARAM)cursor_s); + +} + +static void +Widget_OnLeftButtonUp(HWND hwnd, unsigned int vKeys, long cursor_s) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != self->callbacks && NULL != self->callbacks->leftButtonUp) + { + POINT cursor; + POINTSTOPOINT(cursor, cursor_s); + if (FALSE != self->callbacks->leftButtonUp(self->object, hwnd, vKeys, &cursor)) + return; + } + + DefWindowProc(hwnd, WM_LBUTTONUP, (WPARAM)vKeys, (LPARAM)cursor_s); +} + +static void +Widget_OnLeftButtonDblClk(HWND hwnd, unsigned int vKeys, long cursor_s) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != self->callbacks && NULL != self->callbacks->leftButtonDblClk) + { + POINT cursor; + POINTSTOPOINT(cursor, cursor_s); + if (FALSE != self->callbacks->leftButtonDblClk(self->object, hwnd, vKeys, &cursor)) + return; + } + DefWindowProc(hwnd, WM_LBUTTONDBLCLK, (WPARAM)vKeys, (LPARAM)cursor_s); +} + +static void +Widget_OnRightButtonDown(HWND hwnd, unsigned int vKeys, long cursor_s) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != self->callbacks && NULL != self->callbacks->rightButtonDown) + { + POINT cursor; + POINTSTOPOINT(cursor, cursor_s); + if (FALSE != self->callbacks->rightButtonDown(self->object, hwnd, vKeys, &cursor)) + return; + } + + DefWindowProc(hwnd, WM_RBUTTONDOWN, (WPARAM)vKeys, (LPARAM)cursor_s); + +} + +static void +Widget_OnRightButtonUp(HWND hwnd, unsigned int vKeys, long cursor_s) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != self->callbacks && NULL != self->callbacks->rightButtonUp) + { + POINT cursor; + POINTSTOPOINT(cursor, cursor_s); + if (FALSE != self->callbacks->rightButtonUp(self->object, hwnd, vKeys, &cursor)) + return; + } + + DefWindowProc(hwnd, WM_RBUTTONUP, (WPARAM)vKeys, (LPARAM)cursor_s); +} + +static void +Widget_OnKeyDown(HWND hwnd, unsigned int vKey, unsigned int flags) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != self->callbacks && + NULL != self->callbacks->keyDown && + FALSE != self->callbacks->keyDown(self->object, hwnd, vKey, flags)) + { + return; + } + + DefWindowProc(hwnd, WM_KEYDOWN, (WPARAM)vKey, (LPARAM)flags); + +} + +static void +Widget_OnKeyUp(HWND hwnd, unsigned int vKey, unsigned int flags) +{ + Widget *self; + self = WIDGET(hwnd); + + if (NULL != self && + NULL != self->callbacks && + NULL != self->callbacks->keyUp && + FALSE != self->callbacks->keyUp(self->object, hwnd, vKey, flags)) + { + return; + } + + DefWindowProc(hwnd, WM_KEYUP, (WPARAM)vKey, (LPARAM)flags); +} + +static void +Widget_OnChar(HWND hwnd, unsigned int vKey, unsigned int flags) +{ + Widget *self; + self = WIDGET(hwnd); + + if (NULL != self && + NULL != self->callbacks && + NULL != self->callbacks->character && + FALSE != self->callbacks->character(self->object, hwnd, vKey, flags)) + { + return; + } + + DefWindowProc(hwnd, WM_CHAR, (WPARAM)vKey, (LPARAM)flags); +} + +static unsigned int +Widget_OnGetDlgCode(HWND hwnd, unsigned int vKey, MSG *message) +{ + Widget *self; + self = WIDGET(hwnd); + + if (NULL != self && + NULL != self->callbacks && + NULL != self->callbacks->inputRequest) + { + return self->callbacks->inputRequest(self->object, hwnd, vKey, message); + } + + return (unsigned int)DefWindowProc(hwnd, WM_GETDLGCODE, (WPARAM)vKey, (LPARAM)message); +} + + +static void +Widget_OnSetFocus(HWND hwnd, HWND focusWindow) +{ + Widget_FocusChanged(hwnd, focusWindow, TRUE); +} + +static void +Widget_OnKillFocus(HWND hwnd, HWND focusWindow) +{ + Widget_FocusChanged(hwnd, focusWindow, FALSE); +} + +static void +Widget_OnContextMenu(HWND hwnd, HWND targetWindow, long cursor_s) +{ + BOOL processed; + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != self->callbacks && NULL != self->callbacks->contextMenu) + { + POINT cursor; + POINTSTOPOINT(cursor, cursor_s); + processed = self->callbacks->contextMenu(self->object, hwnd, targetWindow, &cursor); + } + else + processed = FALSE; + + if (FALSE == processed) + Widget_DefWindowProc(hwnd, WM_CONTEXTMENU, (WPARAM)targetWindow, (LPARAM)cursor_s); +} + +static unsigned int +Widget_OnGetType(HWND hwnd) +{ + Widget *self; + WIDGET_RET_VAL(self, hwnd, WIDGET_TYPE_UNKNOWN); + + return self->type; +} + +static void* +Widget_OnGetSelf(HWND hwnd) +{ + Widget *self; + WIDGET_RET_VAL(self, hwnd, NULL); + + return self->object; +} + +static BOOL +Widget_OnSetStyle(HWND hwnd, WidgetStyle *style) +{ + Widget *self; + BOOL styleChanged; + + WIDGET_RET_VAL(self, hwnd, FALSE); + + styleChanged = (self->style != style); + + self->style = style; + + if (FALSE != styleChanged) + { + if (NULL != WIDGETCALLBACKS(self)) + { + if (NULL != WIDGETCALLBACKS(self)->styleColorChanged) + WIDGETCALLBACKS(self)->styleColorChanged(WIDGETOBJECT(self), hwnd, WIDGETSTYLE(self)); + + if (NULL != WIDGETCALLBACKS(self)->styleFontChanged) + WIDGETCALLBACKS(self)->styleFontChanged(WIDGETOBJECT(self), hwnd, WIDGETSTYLE(self)); + } + } + + return TRUE; +} + +static WidgetStyle * +Widget_OnGetStyle(HWND hwnd) +{ + Widget *self; + WIDGET_RET_VAL(self, hwnd, NULL); + + return self->style; +} + +static void +Widget_OnStyleColorChanged(HWND hwnd) +{ + Widget *self; + + WIDGET_RET_VOID(self, hwnd); + + if (FALSE != WIDGET_IS_FROZEN(self)) + return; + + if (NULL != WIDGETCALLBACKS(self) && + NULL != WIDGETCALLBACKS(self)->styleColorChanged) + { + WIDGETCALLBACKS(self)->styleColorChanged(WIDGETOBJECT(self), hwnd, WIDGETSTYLE(self)); + } +} + +static void +Widget_OnStyleFontChanged(HWND hwnd) +{ + Widget *self; + + WIDGET_RET_VOID(self, hwnd); + + if (FALSE != WIDGET_IS_FROZEN(self)) + return; + + if (NULL != WIDGETCALLBACKS(self) && + NULL != WIDGETCALLBACKS(self)->styleFontChanged) + { + WIDGETCALLBACKS(self)->styleFontChanged(WIDGETOBJECT(self), hwnd, WIDGETSTYLE(self)); + } +} + +static void +Widget_OnFreeze(HWND hwnd, BOOL freeze) +{ + Widget *self; + + WIDGET_RET_VOID(self, hwnd); + + if (FALSE == freeze) + Widget_Thaw(self); + else + Widget_Freeze(self); +} + +static BOOL +Widget_OnScroll(HWND hwnd, int dx, int dy, BOOL redraw) +{ + return Widget_ScrollWindow(hwnd, dx, dy, redraw); +} + +static LRESULT +Widget_OnSetScrollPos(HWND hwnd, int dx, int dy, BOOL redraw) +{ + if (0 != dx) + dx = Widget_ScrollBarOffsetPos(hwnd, SB_HORZ, dx, redraw); + + if (0 != dy) + dy = Widget_ScrollBarOffsetPos(hwnd, SB_VERT, dy, redraw); + + return (LRESULT)MAKELONG(dx, dy); + +} + +static void +Widget_OnZoomSliderPosChanging(HWND hwnd, NMTRBTHUMBPOSCHANGING *sliderInfo) +{ + Widget *self; + WIDGET_RET_VOID(self, hwnd); + + if (NULL != WIDGETCALLBACKS(self) && + NULL != WIDGETCALLBACKS(self)->zoomChanging) + { + WIDGETCALLBACKS(self)->zoomChanging(WIDGETOBJECT(self), hwnd, sliderInfo); + } +} + +static LRESULT +Widget_OnNotify(HWND hwnd, NMHDR *notification) +{ + Widget *self; + + self = WIDGET(hwnd); + if (NULL != self && + NULL != self->callbacks && + NULL != self->callbacks->notify) + { + LRESULT result; + if (FALSE != self->callbacks->notify(WIDGETOBJECT(self), hwnd, notification, &result)) + return result; + } + + return Widget_DefWindowProc(hwnd, WM_NOTIFY, + (WPARAM)notification->idFrom, (LPARAM)notification); +} + +static BOOL +Widget_OnEnableChildrenScroll(HWND hwnd, BOOL enable) +{ + Widget *self; + BOOL previous; + + WIDGET_RET_VAL(self, hwnd, FALSE); + + previous = (FALSE == WIDGET_IS_CHILDREN_SCROLL_DISABLED(self)); + + if (FALSE != enable) + WIDGET_UNSET_DISABLE_CHILDREN_SCROLL(self); + else + WIDGET_SET_DISABLE_CHILDREN_SCROLL(self); + + return previous; +} + +static BOOL +Widget_OnGetChildrenScrollEnabled(HWND hwnd) +{ + Widget *self; + WIDGET_RET_VAL(self, hwnd, FALSE); + + return (FALSE == WIDGET_IS_CHILDREN_SCROLL_DISABLED(self)); + +} +static LRESULT CALLBACK +Widget_WindowProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return Widget_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: Widget_OnDestroy(hwnd); return 0; + case WM_PAINT: Widget_OnPaint(hwnd); return 0; + case WM_PRINTCLIENT: Widget_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return 0; + case WM_PRINT: return 0; + case WM_ERASEBKGND: return 0; + case WM_WINDOWPOSCHANGED: Widget_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_SIZE: return 0; + case WM_MOVE: return 0; + case WM_SETTEXT: return Widget_OnSetText(hwnd, (LPCWSTR)lParam); + case WM_GETTEXT: return Widget_OnGetText(hwnd, (LPWSTR)lParam, (INT)wParam); + case WM_GETTEXTLENGTH: return Widget_OnGetTextLength(hwnd); + case WM_SETFONT: Widget_OnSetFont(hwnd, (HFONT)wParam, (BOOL)LOWORD(lParam)); return 0; + case WM_GETFONT: return (LRESULT)Widget_OnGetFont(hwnd); + case WM_VSCROLL: Widget_OnVertScroll(hwnd, LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); return 0; + case WM_HSCROLL: Widget_OnHorzScroll(hwnd, LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); return 0; + case WM_MOUSEWHEEL: Widget_OnMouseWheel(hwnd, LOWORD(wParam), (short)HIWORD(wParam), (LONG)lParam); return 0; + case WM_MOUSEMOVE: Widget_OnMouseMove(hwnd, (unsigned int)wParam, (long)lParam); return 0; + case WM_MOUSELEAVE: Widget_OnMouseLeave(hwnd); return 0; + case WM_LBUTTONDOWN: Widget_OnLeftButtonDown(hwnd, (unsigned int)wParam, (long)lParam); return 0; + case WM_LBUTTONUP: Widget_OnLeftButtonUp(hwnd, (unsigned int)wParam, (long)lParam); return 0; + case WM_LBUTTONDBLCLK: Widget_OnLeftButtonDblClk(hwnd, (unsigned int)wParam, (long)lParam); return 0; + case WM_RBUTTONDOWN: Widget_OnRightButtonDown(hwnd, (unsigned int)wParam, (long)lParam); return 0; + case WM_RBUTTONUP: Widget_OnRightButtonUp(hwnd, (unsigned int)wParam, (long)lParam); return 0; + case WM_KEYDOWN: Widget_OnKeyDown(hwnd, (unsigned int)wParam, (unsigned int)lParam); return 0; + case WM_KEYUP: Widget_OnKeyUp(hwnd, (unsigned int)wParam, (unsigned int)lParam); return 0; + case WM_CHAR: Widget_OnChar(hwnd, (unsigned int)wParam, (unsigned int)lParam); return 0; + case WM_GETDLGCODE: return Widget_OnGetDlgCode(hwnd, (unsigned int)wParam, (MSG*)lParam); + case WM_SETFOCUS: Widget_OnSetFocus(hwnd, (HWND)wParam); return 0; + case WM_KILLFOCUS: Widget_OnKillFocus(hwnd, (HWND)wParam); return 0; + case WM_CONTEXTMENU: Widget_OnContextMenu(hwnd, (HWND)wParam, (long)lParam); return 0; + case WM_NOTIFY: return Widget_OnNotify(hwnd, (NMHDR*)lParam); + + case WIDGET_WM_GET_TYPE: return (LRESULT)Widget_OnGetType(hwnd); + case WIDGET_WM_GET_SELF: return (LRESULT)Widget_OnGetSelf(hwnd); + case WIDGET_WM_SET_STYLE: return Widget_OnSetStyle(hwnd, (WidgetStyle*)lParam); + case WIDGET_WM_GET_STYLE: return (LRESULT)Widget_OnGetStyle(hwnd); + case WIDGET_WM_STYLE_COLOR_CHANGED: Widget_OnStyleColorChanged(hwnd); return 0; + case WIDGET_WM_STYLE_FONT_CHANGED: Widget_OnStyleFontChanged(hwnd); return 0; + case WIDGET_WM_FREEZE: Widget_OnFreeze(hwnd, (BOOL)wParam); return 0; + case WIDGET_WM_SET_SCROLL_POS: return Widget_OnSetScrollPos(hwnd, (short)LOWORD(lParam), (short)HIWORD(lParam), (BOOL)wParam); + case WIDGET_WM_SCROLL: return Widget_OnScroll(hwnd, (short)LOWORD(lParam), (short)HIWORD(lParam), (BOOL)wParam); + case WIDGET_WM_ZOOM_SLIDER_POS_CHANGING: Widget_OnZoomSliderPosChanging(hwnd, (NMTRBTHUMBPOSCHANGING*)lParam); return 0; + case WIDGET_WM_ENABLE_CHILDREN_SCROLL: return Widget_OnEnableChildrenScroll(hwnd, (BOOL)lParam); + case WIDGET_WM_GET_CHILDREN_SCROLL_ENABLED: return Widget_OnGetChildrenScrollEnabled(hwnd); + } + + if (WINAMP_WM_DIRECT_MOUSE_WHEEL == uMsg && + WM_NULL != WINAMP_WM_DIRECT_MOUSE_WHEEL) + { + Widget_OnMouseWheel(hwnd, LOWORD(wParam), (SHORT)HIWORD(wParam), (LONG)lParam); + return TRUE; + } + + return Widget_DefWindowProc(hwnd, uMsg, wParam, lParam); +} + diff --git a/Src/Plugins/Library/ml_devices/widget.h b/Src/Plugins/Library/ml_devices/widget.h new file mode 100644 index 00000000..673dcd5f --- /dev/null +++ b/Src/Plugins/Library/ml_devices/widget.h @@ -0,0 +1,129 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_WIDGET_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_WIDGET_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> +#include "./widgetStyle.h" + +#define WIDGET_WINDOW_CLASS L"NullsoftDevicesWidget" + + +typedef BOOL (*WidgetInitCallback)(HWND /*hwnd*/, void** /*self_out*/, void* /*param*/); +typedef void (*WidgetDestroyCallback)(void* /*self*/, HWND /*hwnd*/); +typedef void (*WidgetLayoutCallback)(void* /*self*/, HWND /*hwnd*/, WidgetStyle* /*style*/, const RECT* /*clientRect*/, SIZE* /*viewSize*/, BOOL /*redraw*/); +typedef BOOL (*WidgetPaintCallback)(void* /*self*/, HWND /*hwnd*/, WidgetStyle* /*style*/, HDC /*hdc*/, const RECT* /*paintRect*/, BOOL /*erase*/); +typedef BOOL (*WidgetMessageCallback)(void* /*self*/, HWND /*hwnd*/, unsigned int /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, LRESULT* /*result*/); +typedef void (*WidgetStyleCallback)(void* /*self*/, HWND /*hwnd*/, WidgetStyle* /*style*/); +typedef BOOL (*WidgetMouseCallback)(void* /*self*/, HWND /*hwnd*/, unsigned int /*vKeys*/, const POINT* /*cursor*/); +typedef INT (*WidgetInputCallback)(void* /*self*/, HWND /*hwnd*/, unsigned int /*vKey*/, MSG* /*message*/); +typedef BOOL (*WidgetKeyCallback)(void* /*self*/, HWND /*hwnd*/, unsigned int /*vKey*/, unsigned int /*flags*/); +typedef void (*WidgetFocusCallback)(void* /*self*/, HWND /*hwnd*/, HWND /*focusHwnd*/, BOOL /*focusReceived*/); +typedef BOOL (*WidgetMenuCallback)(void* /*self*/, HWND /*hwnd*/, HWND /*targetWindow*/, const POINT* /*cursor*/); +typedef void (*WidgetZoomCallback)(void* /*self*/, HWND /*hwnd*/, NMTRBTHUMBPOSCHANGING* /*sliderInfo*/); +typedef void (*WidgetScrollCallback)(void* /*self*/, HWND /*hwnd*/, int* /*dx*/, int* /*dy*/); +typedef BOOL (*WidgetNotifyCallback)(void* /*self*/, HWND /*hwnd*/, NMHDR* /*notification*/, LRESULT* /*result*/); +typedef BOOL (*WidgetHelpCallback)(void* /*self*/, HWND /*hwnd*/, wchar_t* /*buffer*/, size_t /*bufferMax*/); + +typedef struct WidgetInterface +{ + WidgetInitCallback init; + WidgetDestroyCallback destroy; + WidgetLayoutCallback layout; + WidgetPaintCallback paint; + WidgetStyleCallback styleColorChanged; + WidgetStyleCallback styleFontChanged; + WidgetMouseCallback mouseMove; + WidgetMouseCallback leftButtonDown; + WidgetMouseCallback leftButtonUp; + WidgetMouseCallback leftButtonDblClk; + WidgetMouseCallback rightButtonDown; + WidgetMouseCallback rightButtonUp; + WidgetKeyCallback keyDown; + WidgetKeyCallback keyUp; + WidgetKeyCallback character; + WidgetInputCallback inputRequest; + WidgetFocusCallback focusChanged; + WidgetMenuCallback contextMenu; + WidgetZoomCallback zoomChanging; + WidgetScrollCallback scrollBefore; + WidgetScrollCallback scroll; + WidgetNotifyCallback notify; + WidgetHelpCallback help; + WidgetMessageCallback messageProc; +} WidgetInterface; + +HWND +Widget_CreateWindow(unsigned int type, + const WidgetInterface *callbacks, + const wchar_t *text, + unsigned long windowExStyle, + unsigned long windowStyle, + int x, + int y, + int width, + int height, + HWND parentWindow, + unsigned int controlId, + void *param); + + +#define WIDGET_WM_FIRST (WM_USER + 10) + +#define WIDGET_WM_GET_TYPE (WIDGET_WM_FIRST + 0) +#define WIDGET_GET_TYPE(/*HWND*/ _hwnd)\ + ((unsigned int)SendMessageW((_hwnd), WIDGET_WM_GET_TYPE, 0, 0L)) + +#define WIDGET_WM_GET_SELF (WIDGET_WM_FIRST + 1) +#define WIDGET_GET_SELF(/*HWND*/ _hwnd, _type)\ + ((_type*)SendMessageW((_hwnd), WIDGET_WM_GET_SELF, 0, 0L)) + +#define WIDGET_WM_SET_STYLE (WIDGET_WM_FIRST + 2) +#define WIDGET_SET_STYLE(/*HWND*/ _hwnd, /*WidgetStyle* */ _style)\ + ((BOOL)SendMessageW((_hwnd), WIDGET_WM_SET_STYLE, 0, (LPARAM)(_style))) + +#define WIDGET_WM_GET_STYLE (WIDGET_WM_FIRST + 3) +#define WIDGET_GET_STYLE(/*HWND*/ _hwnd)\ + ((WidgetStyle*)SendMessageW((_hwnd), WIDGET_WM_GET_STYLE, 0, 0L)) + +#define WIDGET_WM_STYLE_COLOR_CHANGED (WIDGET_WM_FIRST + 4) +#define WIDGET_STYLE_COLOR_CHANGED(/*HWND*/ _hwnd)\ + (SendMessageW((_hwnd), WIDGET_WM_STYLE_COLOR_CHANGED, 0, 0L)) + +#define WIDGET_WM_STYLE_FONT_CHANGED (WIDGET_WM_FIRST + 5) +#define WIDGET_STYLE_FONT_CHANGED(/*HWND*/ _hwnd)\ + (SendMessageW((_hwnd), WIDGET_WM_STYLE_FONT_CHANGED, 0, 0L)) + +#define WIDGET_WM_FREEZE (WIDGET_WM_FIRST + 6) +#define WIDGET_FREEZE(/*HWND*/ _hwnd)\ + (SendMessageW((_hwnd), WIDGET_WM_FREEZE, TRUE, 0L)) +#define WIDGET_THAW(/*HWND*/ _hwnd)\ + (SendMessageW((_hwnd), WIDGET_WM_FREEZE, FALSE, 0L)) + +#define WIDGET_WM_SET_SCROLL_POS (WIDGET_WM_FIRST + 7) // just offsets scroll positions wihtout scrolling view. result = (LRESULT)MAKELPARAM(actualDx,actualDy). +#define WIDGET_SET_SCROLL_POS(/*HWND*/ _hwnd, /*int*/ _dx, /*int*/ _dy, /*BOOL*/ _redraw)\ + (SendMessageW((_hwnd), WIDGET_WM_SET_SCROLL_POS, (WPARAM)(_redraw), MAKELPARAM((_dx), (_dy)))) + +#define WIDGET_WM_SCROLL (WIDGET_WM_FIRST + 8) +#define WIDGET_SCROLL(/*HWND*/ _hwnd, /*int*/ _dx, /*int*/ _dy, /*BOOL*/ _redraw)\ + ((BOOL)SendMessageW((_hwnd), WIDGET_WM_SCROLL, (WPARAM)(_redraw), MAKELPARAM((_dx), (_dy)))) + +#define WIDGET_WM_ZOOM_SLIDER_POS_CHANGING (WIDGET_WM_FIRST + 9) +#define WIDGET_ZOOM_SLIDER_POS_CHANGING(/*HWND*/ _hwnd, /*NMTRBTHUMBPOSCHANGING* */ _sliderInfo)\ + ((BOOL)SendMessageW((_hwnd), WIDGET_WM_ZOOM_SLIDER_POS_CHANGING, 0, (LPARAM)(_sliderInfo))) + +#define WIDGET_WM_ENABLE_CHILDREN_SCROLL (WIDGET_WM_FIRST + 10) +#define WIDGET_ENABLE_CHILDREN_SCROLL(/*HWND*/ _hwnd, /*BOOL*/ _enable)\ + ((BOOL)SendMessageW((_hwnd), WIDGET_WM_ENABLE_CHILDREN_SCROLL, 0, (LPARAM)(_enable))) + +#define WIDGET_WM_GET_CHILDREN_SCROLL_ENABLED (WIDGET_WM_FIRST + 11) +#define WIDGET_GET_CHILDREN_SCROLL_ENABLED(/*HWND*/ _hwnd)\ + ((BOOL)SendMessageW((_hwnd), WIDGET_WM_GET_CHILDREN_SCROLL_ENABLED, 0, 0L)) + +#define WIDGET_WM_GET_HELP_URL (WIDGET_WM_FIRST + 12) +#define WIDGET_GET_HELP_URL(/*HWND*/ _hwnd, /* wchar_t* */ _buffer, /*size_t*/ _bufferMax)\ + ((BOOL)SendMessageW((_hwnd), WIDGET_WM_GET_HELP_URL, (WPARAM)(_bufferMax), (LPARAM)(_buffer))) + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_WIDGET_HEADER
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/widgetHost.cpp b/Src/Plugins/Library/ml_devices/widgetHost.cpp new file mode 100644 index 00000000..0b8e2ae9 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/widgetHost.cpp @@ -0,0 +1,391 @@ +#include "main.h" +#include "./widgetHost.h" + + +#define WIDGETHOST_WINDOW_CLASS L"NullsoftDevicesWidgetHost" +#define WIDGETHOST_WIDGET_ID 1000 + +typedef +enum WidgetHostState +{ + WIDGETHOST_STATE_FROZEN_UI = (1 << 0), +} WidgetHostState; +DEFINE_ENUM_FLAG_OPERATORS(WidgetHostState); + +typedef struct WidgetHost +{ + WidgetHostState state; + WidgetStyle widgetStyle; + HFONT font; + HRGN updateRegion; + POINT updateOffset; +} WidgetHost; + +typedef struct WidgetHostCreateParam +{ + WidgetCreateProc widgetCreate; + void *widgetCreateParam; +} WidgetHostCreateParam; + +#define WIDGETHOST(_hwnd) ((WidgetHost*)(LONGX86)GetWindowLongPtrW((_hwnd), 0)) +#define WIDGETHOST_RET_VOID(_self, _hwnd) {(_self) = WIDGETHOST((_hwnd)); if (NULL == (_self)) return;} +#define WIDGETHOST_RET_VAL(_self, _hwnd, _error) {(_self) = WIDGETHOST((_hwnd)); if (NULL == (_self)) return (_error);} + +#define WIDGETHOST_WIDGET(_hostWindow) (GetDlgItem((_hostWindow), WIDGETHOST_WIDGET_ID)) + +#define WIDGETHOST_IS_FROZEN(_self) (0 != (WIDGETHOST_STATE_FROZEN_UI & (_self)->state)) +#define WIDGETHOST_FREEZE(_self) (((_self)->state) |= WIDGETHOST_STATE_FROZEN_UI) +#define WIDGETHOST_THAW(_self) (((_self)->state) &= ~WIDGETHOST_STATE_FROZEN_UI) + + +static LRESULT CALLBACK +WidgetHost_WindowProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam); + +static ATOM +WidgetHost_GetClassAtom(HINSTANCE instance) +{ + WNDCLASSEXW klass; + ATOM klassAtom; + + klassAtom = (ATOM)GetClassInfoExW(instance, WIDGETHOST_WINDOW_CLASS, &klass); + if (0 != klassAtom) + return klassAtom; + + memset(&klass, 0, sizeof(klass)); + klass.cbSize = sizeof(klass); + klass.style = CS_DBLCLKS; + klass.lpfnWndProc = WidgetHost_WindowProc; + klass.cbClsExtra = 0; + klass.cbWndExtra = sizeof(WidgetHost*); + klass.hInstance = instance; + klass.hIcon = NULL; + klass.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + klass.hbrBackground = NULL; + klass.lpszMenuName = NULL; + klass.lpszClassName = WIDGETHOST_WINDOW_CLASS; + klass.hIconSm = NULL; + klassAtom = RegisterClassExW(&klass); + + return klassAtom; +} + + +HWND +WidgetHost_Create(unsigned int windowStyle, int x, int y, int width, int height, + HWND parentWindow, WidgetCreateProc createProc, void *createParam) +{ + HINSTANCE instance; + ATOM klassAtom; + HWND hwnd; + WidgetHostCreateParam hostParam; + + + if (NULL == createProc) + return NULL; + + instance = GetModuleHandleW(NULL); + klassAtom = WidgetHost_GetClassAtom(instance); + if (0 == klassAtom) + return NULL; + + hostParam.widgetCreate = createProc; + hostParam.widgetCreateParam = createParam; + + + hwnd = CreateWindowExW(WS_EX_NOPARENTNOTIFY |WS_EX_CONTROLPARENT, + (LPCWSTR)MAKEINTATOM(klassAtom), NULL, + WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | windowStyle, + x, y, width, height, + parentWindow, NULL, instance, &hostParam); + + return hwnd; +} + +static void +WidgetHost_Layout(HWND hwnd, BOOL redraw) +{ + WidgetHost *self; + RECT rect; + WIDGETHOST_RET_VOID(self, hwnd); + + if (FALSE == GetClientRect(hwnd, &rect)) + return; + + HWND widgetWindow = WIDGETHOST_WIDGET(hwnd); + if (NULL != widgetWindow) + { + unsigned int flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER; + if (FALSE == redraw) + flags |= SWP_NOREDRAW; + + SetWindowPos(widgetWindow, NULL, 0, 0, RECTWIDTH(rect), RECTHEIGHT(rect), flags); + } +} + +static void +WidgetHost_Paint(HWND hwnd, HDC hdc, const RECT *paintRect, BOOL erase) +{ + if (FALSE != erase) + { + COLORREF backColor, prevBackColor; + backColor = Graphics_GetSkinColor(WADLG_WNDBG); + prevBackColor = SetBkColor(hdc, backColor); + + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, paintRect, NULL, 0, NULL); + SetBkColor(hdc, prevBackColor); + } +} + +static void +WidgetHost_UpdateSkin(HWND hwnd) +{ + WidgetHost *self; + WIDGETHOST_RET_VOID(self, hwnd); + BOOL styleChanged = FALSE; + + if (FALSE != WidgetStyle_UpdateDefaultColors(&self->widgetStyle)) + styleChanged = TRUE; + + if (FALSE != styleChanged) + { + HWND widgetWindow = WIDGETHOST_WIDGET(hwnd); + if (NULL != widgetWindow) + { + WIDGET_STYLE_COLOR_CHANGED(widgetWindow); + InvalidateRect(widgetWindow, NULL, TRUE); + } + } + +} + +static void +WidgetHost_UpdateFont(HWND hwnd, BOOL redraw) +{ + WidgetHost *self; + BOOL styleChanged = FALSE; + long unitWidth, unitHeight; + + WIDGETHOST_RET_VOID(self, hwnd); + + if (FALSE == Graphics_GetWindowBaseUnits(hwnd, &unitWidth, &unitHeight)) + { + unitWidth = 6; + unitHeight = 13; + } + + if (FALSE != WidgetStyle_UpdateDefaultFonts(&self->widgetStyle, self->font, unitWidth, unitHeight)) + styleChanged = TRUE; + + if (FALSE != styleChanged) + { + HWND widgetWindow = WIDGETHOST_WIDGET(hwnd); + if (NULL != widgetWindow) + { + WIDGET_STYLE_COLOR_CHANGED(widgetWindow); + InvalidateRect(widgetWindow, NULL, TRUE); + } + } + +} + +static LRESULT +WidgetHost_OnCreate(HWND hwnd, CREATESTRUCT *createStruct) +{ + WidgetHost *self; + HWND widgetWindow; + WidgetHostCreateParam *createParam; + + if (NULL == createStruct) + return -1; + + createParam = (WidgetHostCreateParam*)createStruct->lpCreateParams; + if (NULL == createParam) + return -1; + + self = (WidgetHost*)malloc(sizeof(WidgetHost)); + if (NULL == self) + return -1; + + SetLastError(ERROR_SUCCESS); + if (!SetWindowLongPtr(hwnd, 0, (LONGX86)self) && ERROR_SUCCESS != GetLastError()) + return -1; + + memset(self, 0, sizeof(WidgetHost)); + + WIDGETHOST_FREEZE(self); + + MLSkinWindow2(Plugin_GetLibraryWindow(), hwnd, SKINNEDWND_TYPE_WINDOW, + SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + + WidgetHost_UpdateFont(hwnd, FALSE); + WidgetHost_UpdateSkin(hwnd); + + widgetWindow = NULL; + if (NULL != createParam->widgetCreate) + widgetWindow = createParam->widgetCreate(hwnd, createParam->widgetCreateParam); + + if (NULL == widgetWindow) + return -1; + + SetWindowLongPtrW(widgetWindow, GWLP_ID, WIDGETHOST_WIDGET_ID); + + WIDGET_SET_STYLE(widgetWindow, &self->widgetStyle); + + SetWindowPos(widgetWindow, NULL, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW | SWP_FRAMECHANGED); + + ShowWindow(widgetWindow, SW_SHOWNA); + + SetWindowPos(widgetWindow, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + + WIDGETHOST_THAW(self); + + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); + + return 0; +} + +static void +WidgetHost_OnDestroy(HWND hwnd) +{ + WidgetHost *self; + + self = WIDGETHOST(hwnd); + SetWindowLongPtr(hwnd, 0, 0); + + if (NULL == self) + return; + + WIDGETHOST_FREEZE(self); + + WidgetStyle_Free(&self->widgetStyle); + free(self); +} + +static void +WidgetHost_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + + if (NULL != BeginPaint(hwnd, &ps)) + { + WidgetHost_Paint(hwnd, ps.hdc, &ps.rcPaint, ps.fErase); + EndPaint(hwnd, &ps); + } +} + +static void +WidgetHost_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + RECT clientRect; + if (GetClientRect(hwnd, &clientRect)) + { + WidgetHost_Paint(hwnd, hdc, &clientRect, TRUE); + } +} + +static void +WidgetHost_OnWindowPosChanged(HWND hwnd, WINDOWPOS *windowPos) +{ + if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED) & windowPos->flags)) + { + WidgetHost *self; + WIDGETHOST_RET_VOID(self, hwnd); + + if (FALSE != WIDGETHOST_IS_FROZEN(self)) + return; + + WidgetHost_Layout(hwnd, 0 == (SWP_NOREDRAW & windowPos->flags)); + } +} + +static void +WidgetHost_OnDisplayChanged(HWND hwnd, INT bpp, INT dpi_x, INT dpi_y) +{ + WidgetHost *self; + WIDGETHOST_RET_VOID(self, hwnd); + + if (FALSE != WIDGETHOST_IS_FROZEN(self)) + return; + + WidgetHost_UpdateSkin(hwnd); + InvalidateRect(hwnd, NULL, TRUE); +} + +static void +WidgetHost_OnSetFont(HWND hwnd, HFONT font, BOOL redraw) +{ + WidgetHost *self; + LOGFONTW prevFont, newFont; + + WIDGETHOST_RET_VOID(self, hwnd); + + if (NULL == self->font || + sizeof(LOGFONTW) != GetObjectW(self->font, sizeof(prevFont), &prevFont)) + { + ZeroMemory(&prevFont, sizeof(prevFont)); + } + + self->font = font; + + + if (NULL == self->font || + sizeof(newFont) != GetObjectW(self->font, sizeof(newFont), &newFont)) + { + ZeroMemory(&newFont, sizeof(newFont)); + } + + if (0 != memcmp(&prevFont, &newFont, sizeof(prevFont)) && + FALSE == WIDGETHOST_IS_FROZEN(self)) + { + WidgetHost_UpdateFont(hwnd, redraw); + } +} + +static HFONT +WidgetHost_OnGetFont(HWND hwnd) +{ + WidgetHost *self; + WIDGETHOST_RET_VAL(self, hwnd, NULL); + + return self->font; +} + +static void +WidgetHost_OnSetUpdateRegion(HWND hwnd, HRGN updateRegion, POINTS regionOffset) +{ + WidgetHost *self; + WIDGETHOST_RET_VOID(self, hwnd); + + self->updateRegion = updateRegion; + self->updateOffset.x = regionOffset.x; + self->updateOffset.y = regionOffset.y; +} + + +static LRESULT CALLBACK +WidgetHost_WindowProc(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return WidgetHost_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: WidgetHost_OnDestroy(hwnd); return 0; + case WM_PAINT: WidgetHost_OnPaint(hwnd); return 0; + case WM_PRINTCLIENT: WidgetHost_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return 0; + case WM_PRINT: return 0; + case WM_ERASEBKGND: return 0; + case WM_WINDOWPOSCHANGED: WidgetHost_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_SIZE: return 0; + case WM_MOVE: return 0; + case WM_DISPLAYCHANGE: WidgetHost_OnDisplayChanged(hwnd, (INT)wParam, LOWORD(lParam), HIWORD(lParam)); return 0; + case WM_SETFONT: WidgetHost_OnSetFont(hwnd, (HFONT)wParam, LOWORD(lParam)); return 0; + case WM_GETFONT: return (LRESULT)WidgetHost_OnGetFont(hwnd); + + // gen_ml flickerless drawing + case WM_USER + 0x200: return 1; + case WM_USER + 0x201: WidgetHost_OnSetUpdateRegion(hwnd, (HRGN)lParam, MAKEPOINTS(wParam)); return 0; + + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/widgetHost.h b/Src/Plugins/Library/ml_devices/widgetHost.h new file mode 100644 index 00000000..b27b3ea3 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/widgetHost.h @@ -0,0 +1,23 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_WDGET_HOST_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_WIDGET_HOST_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +typedef HWND (*WidgetCreateProc)(HWND hostWindow, void *user); + +HWND +WidgetHost_Create(unsigned int windowStyle, + int x, + int y, + int width, + int height, + HWND parentWindow, + WidgetCreateProc createProc, + void *createParam); + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_WIDGET_HOST_HEADER diff --git a/Src/Plugins/Library/ml_devices/widgetStyle.cpp b/Src/Plugins/Library/ml_devices/widgetStyle.cpp new file mode 100644 index 00000000..1e72ec06 --- /dev/null +++ b/Src/Plugins/Library/ml_devices/widgetStyle.cpp @@ -0,0 +1,261 @@ +#include "main.h" +#include "./widgetStyle.h" + +static void +WidgetStyle_DeleteGdiObject(UINT flags, UINT flagsMask, HGDIOBJ object) +{ + if (NULL != object && 0 != (flagsMask & flags)) + DeleteObject(object); +} + + +static BOOL +WidgetStyle_SetBrushInt(HBRUSH *brush, WidgetStyleFlags *flags, WidgetStyleFlags flagsMask, + HBRUSH sourceBrush, WidgetStyleAssignFlags assignFlags) +{ + LOGBRUSH lb; + + WidgetStyle_DeleteGdiObject(*flags, flagsMask, *brush); + + if (0 == (WIDGETSTYLE_COPY_OBJECT & assignFlags)) + { + if (0 == (WIDGETSTYLE_OWN_OBJECT & assignFlags)) + *flags &= ~flagsMask; + else + *flags |= flagsMask; + + *brush = sourceBrush; + return TRUE; + } + + + *brush = (sizeof(lb) == GetObjectW(sourceBrush, sizeof(lb), &lb)) ? + CreateBrushIndirect(&lb) : NULL; + + if (NULL == *brush) + { + *flags &= ~flagsMask; + return FALSE; + } + + *flags |= flagsMask; + return TRUE; +} + +static BOOL +WidgetStyle_SetFontInt(HFONT *font, WidgetStyleFlags *flags, WidgetStyleFlags flagsMask, + HFONT sourceFont, WidgetStyleAssignFlags assignFlags) +{ + LOGFONTW lf = {0}; + WidgetStyle_DeleteGdiObject(*flags, flagsMask, *font); + + if (0 == (WIDGETSTYLE_COPY_OBJECT & assignFlags)) + { + if (0 == (WIDGETSTYLE_OWN_OBJECT & assignFlags)) + *flags &= ~flagsMask; + else + *flags |= flagsMask; + + *font = sourceFont; + return TRUE; + } + + *font = (sizeof(lf) == GetObjectW(sourceFont, sizeof(lf), &lf)) ? CreateFontIndirectW(&lf) : NULL; + + if (NULL == *font) + { + *flags &= ~flagsMask; + return FALSE; + } + + *flags |= flagsMask; + return TRUE; +} + +void +WidgetStyle_Free(WidgetStyle *self) +{ + if (NULL == self) + return; + + WidgetStyle_DeleteGdiObject(self->flags, WIDGETSTYLE_OWN_TEXT_FONT, self->textFont); + WidgetStyle_DeleteGdiObject(self->flags, WIDGETSTYLE_OWN_TITLE_FONT, self->titleFont); + WidgetStyle_DeleteGdiObject(self->flags, WIDGETSTYLE_OWN_CATEGORY_FONT, self->categoryFont); + WidgetStyle_DeleteGdiObject(self->flags, WIDGETSTYLE_OWN_BACK_BRUSH, self->backBrush); + WidgetStyle_DeleteGdiObject(self->flags, WIDGETSTYLE_OWN_CATEGORY_BRUSH, self->categoryBrush); +} + +BOOL +WidgetStyle_SetBackBrush(WidgetStyle *self, HBRUSH brush, WidgetStyleAssignFlags flags) +{ + return WidgetStyle_SetBrushInt(&self->backBrush, &self->flags, + WIDGETSTYLE_OWN_BACK_BRUSH, brush, flags); +} + +BOOL +WidgetStyle_SetCategoryBrush(WidgetStyle *self, HBRUSH brush, WidgetStyleAssignFlags flags) +{ + return WidgetStyle_SetBrushInt(&self->categoryBrush, &self->flags, + WIDGETSTYLE_OWN_CATEGORY_BRUSH, brush, flags); +} + +BOOL +WidgetStyle_SetTextFont(WidgetStyle *self, HFONT font, WidgetStyleAssignFlags flags) +{ + return WidgetStyle_SetFontInt(&self->textFont, &self->flags, + WIDGETSTYLE_OWN_TEXT_FONT, font, flags); + +} + +BOOL +WidgetStyle_SetTitleFont(WidgetStyle *self, HFONT font, WidgetStyleAssignFlags flags) +{ + return WidgetStyle_SetFontInt(&self->titleFont, &self->flags, + WIDGETSTYLE_OWN_TITLE_FONT, font, flags); + +} + +BOOL +WidgetStyle_SetCategoryFont(WidgetStyle *self, HFONT font, WidgetStyleAssignFlags flags) +{ + return WidgetStyle_SetFontInt(&self->categoryFont, &self->flags, + WIDGETSTYLE_OWN_CATEGORY_FONT, font, flags); + +} + +static COLORREF +WidgetStyle_GetCategoryLineColor(COLORREF categoryBackColor) +{ + COLORREF categoryLineColor; + size_t index; + + const int categoryLineColors[] = + { + WADLG_LISTHEADER_FRAME_MIDDLECOLOR, + WADLG_LISTHEADER_FRAME_BOTTOMCOLOR, + WADLG_LISTHEADER_FRAME_TOPCOLOR, + WADLG_HILITE, + }; + + for (index = 0; index < ARRAYSIZE(categoryLineColors); index++) + { + categoryLineColor = Graphics_GetSkinColor(categoryLineColors[index]); + int distance = Graphics_GetColorDistance(categoryLineColor, categoryBackColor); + if (distance < 0) distance = -distance; + if (distance >= 40) break; + } + + return categoryLineColor; +} + +static COLORREF +WidgetStyle_GetBorderColor(COLORREF backColor, COLORREF textColor) +{ + COLORREF borderColor; + + for(int step = 0;; step++) + { + switch(step) + { + case 0: + borderColor = Graphics_GetSkinColor(WADLG_HILITE); + break; + case 1: + borderColor = Graphics_BlendColors(Graphics_GetSkinColor(WADLG_SELBAR_FGCOLOR), Graphics_GetSkinColor(WADLG_SELBAR_BGCOLOR), 17); + borderColor = Graphics_BlendColors(borderColor, backColor, 229); + break; + default: + return textColor; + } + + int distance = Graphics_GetColorDistance(borderColor, backColor); + if (distance < 0) distance = -distance; + if (distance >= 40) + break; + } + + return borderColor; +} + +BOOL +WidgetStyle_UpdateDefaultColors(WidgetStyle *style) +{ + #define WIDGETSTYLE_SET_COLOR(_colorName, _color)\ + {COLORREF _colorCopy = (_color);\ + if (WIDGETSTYLE_##_colorName##_COLOR(style) != _colorCopy)\ + {WIDGETSTYLE_SET_##_colorName##_COLOR(style, _colorCopy);\ + styleChanged = TRUE;}} + + #define WIDGETSTYLE_SET_COLOR_BLEND(_colorName, _colorTop, _colorBottom, _alpha)\ + WIDGETSTYLE_SET_COLOR(_colorName, Graphics_BlendColors((_colorTop), (_colorBottom), (_alpha))) + + + COLORREF widgetBackColor, widgetTextColor, categoryBackColor, categoryTextColor; + BOOL styleChanged; + HBRUSH brush; + + if (NULL == style) + return FALSE; + + styleChanged = FALSE; + + widgetBackColor = Graphics_GetSkinColor(WADLG_ITEMBG); + widgetTextColor = Graphics_GetSkinColor(WADLG_ITEMFG); + categoryBackColor = Graphics_GetSkinColor(WADLG_LISTHEADER_BGCOLOR); + categoryTextColor = Graphics_GetSkinColor(WADLG_LISTHEADER_FONTCOLOR); + + if (WIDGETSTYLE_BACK_COLOR(style) != widgetBackColor || + NULL == WIDGETSTYLE_BACK_BRUSH(style)) + { + brush = CreateSolidBrush(widgetBackColor); + WIDGETSTYLE_SET_BACK_BRUSH(style, brush, WIDGETSTYLE_OWN_OBJECT); + styleChanged = TRUE; + } + + if (WIDGETSTYLE_CATEGORY_BACK_COLOR(style) != categoryBackColor || + NULL == WIDGETSTYLE_CATEGORY_BRUSH(style)) + { + brush = CreateSolidBrush(categoryBackColor); + WIDGETSTYLE_SET_CATEGORY_BRUSH(style, brush, WIDGETSTYLE_OWN_OBJECT); + styleChanged = TRUE; + } + + WIDGETSTYLE_SET_COLOR(BACK, widgetBackColor); + WIDGETSTYLE_SET_COLOR(TEXT, widgetTextColor); + WIDGETSTYLE_SET_COLOR_BLEND(TITLE, widgetTextColor, widgetBackColor, 210); + WIDGETSTYLE_SET_COLOR(BORDER, WidgetStyle_GetBorderColor(widgetBackColor, widgetTextColor)); + WIDGETSTYLE_SET_COLOR(IMAGE_BACK, widgetBackColor); + WIDGETSTYLE_SET_COLOR(IMAGE_FRONT, widgetTextColor); + WIDGETSTYLE_SET_COLOR(SELECT_BACK, Graphics_GetSkinColor(WADLG_SELBAR_BGCOLOR)); + WIDGETSTYLE_SET_COLOR(SELECT_FRONT, Graphics_GetSkinColor(WADLG_SELBAR_FGCOLOR)); + WIDGETSTYLE_SET_COLOR_BLEND(INACTIVE_SELECT_BACK, WIDGETSTYLE_SELECT_BACK_COLOR(style), widgetBackColor, 192); + WIDGETSTYLE_SET_COLOR_BLEND(INACTIVE_SELECT_FRONT, WIDGETSTYLE_SELECT_FRONT_COLOR(style), widgetBackColor, 192); + WIDGETSTYLE_SET_COLOR(CATEGORY_BACK, categoryBackColor); + WIDGETSTYLE_SET_COLOR(CATEGORY_TEXT, categoryTextColor); + WIDGETSTYLE_SET_COLOR(CATEGORY_LINE, WidgetStyle_GetCategoryLineColor(categoryBackColor)); + WIDGETSTYLE_SET_COLOR_BLEND(CATEGORY_EMPTY_TEXT, widgetTextColor, widgetBackColor, 210); + WIDGETSTYLE_SET_COLOR_BLEND(TEXT_EDITOR_BORDER, widgetTextColor, widgetBackColor, 140); + + return styleChanged; +} + +BOOL +WidgetStyle_UpdateDefaultFonts(WidgetStyle *style, HFONT baseFont, long unitWidth, long unitHeight) +{ + HFONT tempFont; + + if (NULL == style) + return FALSE; + + tempFont = Graphics_DuplicateFont(baseFont, 3, FALSE, TRUE); + WIDGETSTYLE_SET_TITLE_FONT(style, tempFont, WIDGETSTYLE_OWN_OBJECT); + + tempFont = Graphics_DuplicateFont(baseFont, 0, FALSE, TRUE); + WIDGETSTYLE_SET_CATEGORY_FONT(style, tempFont, WIDGETSTYLE_OWN_OBJECT); + + WIDGETSTYLE_SET_TEXT_FONT(style, baseFont, WIDGETSTYLE_LINK_OBJECT); + + WIDGETSTYLE_SET_UNIT_SIZE(style, unitWidth, unitHeight); + + return TRUE; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_devices/widgetStyle.h b/Src/Plugins/Library/ml_devices/widgetStyle.h new file mode 100644 index 00000000..bb2a010a --- /dev/null +++ b/Src/Plugins/Library/ml_devices/widgetStyle.h @@ -0,0 +1,159 @@ +#ifndef _NULLSOFT_WINAMP_ML_DEVICES_WIDGETSTYLE_HEADER +#define _NULLSOFT_WINAMP_ML_DEVICES_WIDGETSTYLE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include <wtypes.h> + +typedef struct WidgetStyle WidgetStyle; +typedef enum WidgetStyleFlags WidgetStyleFlags; +typedef enum WidgetStyleAssignFlags WidgetStyleAssignFlags; + +enum WidgetStyleFlags +{ + WIDGETSTYLE_OWN_TEXT_FONT = (1 << 0), + WIDGETSTYLE_OWN_TITLE_FONT = (1 << 1), + WIDGETSTYLE_OWN_CATEGORY_FONT = (1 << 2), + WIDGETSTYLE_OWN_BACK_BRUSH = (1 << 3), + WIDGETSTYLE_OWN_CATEGORY_BRUSH = (1 << 4), +}; +DEFINE_ENUM_FLAG_OPERATORS(WidgetStyleFlags); + +enum WidgetStyleAssignFlags +{ + WIDGETSTYLE_LINK_OBJECT = 0, + WIDGETSTYLE_COPY_OBJECT = (1 << 0), + WIDGETSTYLE_OWN_OBJECT = (1 << 1), +}; +DEFINE_ENUM_FLAG_OPERATORS(WidgetStyleAssignFlags); + + +struct WidgetStyle +{ + WidgetStyleFlags flags; + HFONT textFont; + HFONT titleFont; + HFONT categoryFont; + HBRUSH backBrush; + HBRUSH categoryBrush; + COLORREF titleColor; + COLORREF textColor; + COLORREF backColor; + COLORREF borderColor; + COLORREF imageBackColor; + COLORREF imageFrontColor; + COLORREF selectBackColor; + COLORREF selectFrontColor; + COLORREF inactiveSelectBackColor; + COLORREF inactiveSelectFrontColor; + COLORREF categoryTextColor; + COLORREF categoryLineColor; + COLORREF categoryBackColor; + COLORREF categoryEmptyTextColor; + COLORREF textEditorBorderColor; + SIZE unitSize; +}; + +#define DLU_TO_PX_VALIDATE_MIN(_value, _dlu, _min)\ + {if (0 != (_dlu) && ((_value) < (_min))) (_value) = (_min);} + +#define WIDGETSTYLE_DLU_TO_HORZ_PX(_style, _dlu) MulDiv((_dlu), ((WidgetStyle*)(_style))->unitSize.cx, 4) +#define WIDGETSTYLE_DLU_TO_VERT_PX(_style, _dlu) MulDiv((_dlu), ((WidgetStyle*)(_style))->unitSize.cy, 8) + +#define WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(_value, _style, _dlu, _min)\ + {_value = WIDGETSTYLE_DLU_TO_HORZ_PX(_style, _dlu); DLU_TO_PX_VALIDATE_MIN(_value, _dlu, _min);} + +#define WIDGETSTYLE_DLU_TO_VERT_PX_MIN(_value, _style, _dlu, _min)\ + {_value = WIDGETSTYLE_DLU_TO_VERT_PX(_style, _dlu); DLU_TO_PX_VALIDATE_MIN(_value, _dlu, _min);} + +#define WIDGETSTYLE_TITLE_FONT(_style) (((WidgetStyle*)(_style))->titleFont) +#define WIDGETSTYLE_TEXT_FONT(_style) (((WidgetStyle*)(_style))->textFont) +#define WIDGETSTYLE_CATEGORY_FONT(_style) (((WidgetStyle*)(_style))->categoryFont) +#define WIDGETSTYLE_BACK_BRUSH(_style) (((WidgetStyle*)(_style))->backBrush) +#define WIDGETSTYLE_CATEGORY_BRUSH(_style) (((WidgetStyle*)(_style))->categoryBrush) +#define WIDGETSTYLE_TITLE_COLOR(_style) (((WidgetStyle*)(_style))->titleColor) +#define WIDGETSTYLE_TEXT_COLOR(_style) (((WidgetStyle*)(_style))->textColor) +#define WIDGETSTYLE_BACK_COLOR(_style) (((WidgetStyle*)(_style))->backColor) +#define WIDGETSTYLE_BORDER_COLOR(_style) (((WidgetStyle*)(_style))->borderColor) +#define WIDGETSTYLE_IMAGE_BACK_COLOR(_style) (((WidgetStyle*)(_style))->imageBackColor) +#define WIDGETSTYLE_IMAGE_FRONT_COLOR(_style) (((WidgetStyle*)(_style))->imageFrontColor) +#define WIDGETSTYLE_SELECT_BACK_COLOR(_style) (((WidgetStyle*)(_style))->selectBackColor) +#define WIDGETSTYLE_SELECT_FRONT_COLOR(_style) (((WidgetStyle*)(_style))->selectFrontColor) +#define WIDGETSTYLE_INACTIVE_SELECT_BACK_COLOR(_style) (((WidgetStyle*)(_style))->inactiveSelectBackColor) +#define WIDGETSTYLE_INACTIVE_SELECT_FRONT_COLOR(_style) (((WidgetStyle*)(_style))->inactiveSelectFrontColor) +#define WIDGETSTYLE_CATEGORY_TEXT_COLOR(_style) (((WidgetStyle*)(_style))->categoryTextColor) +#define WIDGETSTYLE_CATEGORY_BACK_COLOR(_style) (((WidgetStyle*)(_style))->categoryBackColor) +#define WIDGETSTYLE_CATEGORY_LINE_COLOR(_style) (((WidgetStyle*)(_style))->categoryLineColor) +#define WIDGETSTYLE_CATEGORY_EMPTY_TEXT_COLOR(_style) (((WidgetStyle*)(_style))->categoryEmptyTextColor) +#define WIDGETSTYLE_TEXT_EDITOR_BORDER_COLOR(_style) (((WidgetStyle*)(_style))->textEditorBorderColor) +#define WIDGETSTYLE_SET_UNIT_SIZE(_style, _width, _height)\ + {(((WidgetStyle*)(_style))->unitSize).cx = _width;\ + (((WidgetStyle*)(_style))->unitSize).cy = _height;} + +#define WIDGETSTYLE_SET_TITLE_FONT(_style, _font, _flags)\ + WidgetStyle_SetTitleFont(((WidgetStyle*)(_style)), (_font), (_flags)) +#define WIDGETSTYLE_SET_TEXT_FONT(_style, _font, _flags)\ + WidgetStyle_SetTextFont(((WidgetStyle*)(_style)), (_font), (_flags)) +#define WIDGETSTYLE_SET_CATEGORY_FONT(_style, _font, _flags)\ + WidgetStyle_SetCategoryFont(((WidgetStyle*)(_style)), (_font), (_flags)) +#define WIDGETSTYLE_SET_BACK_BRUSH(_style, _brush, _flags)\ + WidgetStyle_SetBackBrush(((WidgetStyle*)(_style)), (_brush), (_flags)) +#define WIDGETSTYLE_SET_CATEGORY_BRUSH(_style, _brush, _flags)\ + WidgetStyle_SetCategoryBrush(((WidgetStyle*)(_style)), (_brush), (_flags)) + +#define WIDGETSTYLE_SET_TITLE_COLOR(_style, _color) (((WidgetStyle*)(_style))->titleColor = (_color)) +#define WIDGETSTYLE_SET_TEXT_COLOR(_style, _color) (((WidgetStyle*)(_style))->textColor = (_color)) +#define WIDGETSTYLE_SET_BACK_COLOR(_style, _color) (((WidgetStyle*)(_style))->backColor = (_color)) +#define WIDGETSTYLE_SET_BORDER_COLOR(_style, _color) (((WidgetStyle*)(_style))->borderColor = (_color)) +#define WIDGETSTYLE_SET_IMAGE_BACK_COLOR(_style, _color) (((WidgetStyle*)(_style))->imageBackColor = (_color)) +#define WIDGETSTYLE_SET_IMAGE_FRONT_COLOR(_style, _color) (((WidgetStyle*)(_style))->imageFrontColor = (_color)) +#define WIDGETSTYLE_SET_SELECT_BACK_COLOR(_style, _color) (((WidgetStyle*)(_style))->selectBackColor = (_color)) +#define WIDGETSTYLE_SET_SELECT_FRONT_COLOR(_style, _color) (((WidgetStyle*)(_style))->selectFrontColor = (_color)) +#define WIDGETSTYLE_SET_INACTIVE_SELECT_BACK_COLOR(_style, _color) (((WidgetStyle*)(_style))->inactiveSelectBackColor = (_color)) +#define WIDGETSTYLE_SET_INACTIVE_SELECT_FRONT_COLOR(_style, _color) (((WidgetStyle*)(_style))->inactiveSelectFrontColor = (_color)) +#define WIDGETSTYLE_SET_CATEGORY_TEXT_COLOR(_style, _color) (((WidgetStyle*)(_style))->categoryTextColor = (_color)) +#define WIDGETSTYLE_SET_CATEGORY_BACK_COLOR(_style, _color) (((WidgetStyle*)(_style))->categoryBackColor = (_color)) +#define WIDGETSTYLE_SET_CATEGORY_LINE_COLOR(_style, _color) (((WidgetStyle*)(_style))->categoryLineColor = (_color)) +#define WIDGETSTYLE_SET_CATEGORY_EMPTY_TEXT_COLOR(_style, _color) (((WidgetStyle*)(_style))->categoryEmptyTextColor = (_color)) +#define WIDGETSTYLE_SET_TEXT_EDITOR_BORDER_COLOR(_style, _color) (((WidgetStyle*)(_style))->textEditorBorderColor = (_color)) +void +WidgetStyle_Free(WidgetStyle *self); + +BOOL +WidgetStyle_SetBackBrush(WidgetStyle *self, + HBRUSH brush, + WidgetStyleAssignFlags flags); + +BOOL +WidgetStyle_SetCategoryBrush(WidgetStyle *self, + HBRUSH brush, + WidgetStyleAssignFlags flags); + +BOOL +WidgetStyle_SetTextFont(WidgetStyle *self, + HFONT font, + WidgetStyleAssignFlags flags); + +BOOL +WidgetStyle_SetTitleFont(WidgetStyle *self, + HFONT font, + WidgetStyleAssignFlags flags); + +BOOL +WidgetStyle_SetCategoryFont(WidgetStyle *self, + HFONT font, + WidgetStyleAssignFlags flags); + +BOOL +WidgetStyle_UpdateDefaultColors(WidgetStyle *style); + +BOOL +WidgetStyle_UpdateDefaultFonts(WidgetStyle *style, + HFONT baseFont, + long unitWidth, + long unitHeight); + + +#endif //_NULLSOFT_WINAMP_ML_DEVICES_WIDGETSTYLE_HEADER |