1073 lines
23 KiB
C++
1073 lines
23 KiB
C++
|
#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;
|
||
|
}
|