winamp/Src/Plugins/Library/ml_local/view_media.cpp
2024-09-24 14:54:57 +02:00

4051 lines
120 KiB
C++

#include "main.h"
#include "ml_local.h"
#include <windowsx.h>
#include "../nu/listview.h"
#include "..\..\General\gen_ml/config.h"
#include "resource.h"
#include <time.h>
#include "..\..\General\gen_ml/ml_ipc.h"
#include "../ml_pmp/pmp.h"
#include "..\..\General\gen_ml/gaystring.h"
#include "../nde/nde.h"
#include "../replicant/nu/AutoWide.h"
#include "../replicant/nu/AutoChar.h"
#include "..\..\General\gen_ml/ml_ipc_0313.h"
#include <math.h>
#include <shlwapi.h>
#include <strsafe.h>
#include "..\..\General\gen_ml/menufucker.h"
#include "api_mldb.h"
#include "../replicant/foundation/error.h"
static wchar_t oldText[4096];
static int IPC_LIBRARY_SENDTOMENU;
static HINSTANCE cloud_hinst;
const int ML_MSG_PDXS_STATUS = 0x1001;
const int ML_MSG_PDXS_MIX = 0x1002;
void RefreshMetadata(HWND parent);
static HRGN g_rgnUpdate = NULL;
static int offsetX, offsetY, customAllowed;
int groupBtn = 1, enqueuedef = 0;
static viewButtons view;
HWND hwndSearchGlobal = 0;
//timers
#define TIMER_RATINGAUTOUNHOVER_ID 65520
#define TIMER_RATINGAUTOUNHOVER_DELAY 200
void FixAmps(wchar_t *str, size_t len)
{
size_t realSize = 0;
size_t extra = 0;
wchar_t *itr = str;
while (itr && *itr)
{
if (itr && *itr == L'&')
extra++;
itr++;
realSize++;
}
extra = min(len - (realSize + 1), extra);
while (extra)
{
str[extra+realSize] = str[realSize];
if (str[realSize] == L'&')
{
extra--;
str[extra+realSize] = L'&';
}
realSize--;
}
}
void MakeDateString(__time64_t convertTime, wchar_t *dest, size_t destlen)
{
SYSTEMTIME sysTime;
tm *newtime = _localtime64(&convertTime);
if (newtime)
{
sysTime.wYear = (WORD)(newtime->tm_year + 1900);
sysTime.wMonth = (WORD)(newtime->tm_mon + 1);
sysTime.wDayOfWeek = (WORD)newtime->tm_wday;
sysTime.wDay = (WORD)newtime->tm_mday;
sysTime.wHour = (WORD)newtime->tm_hour;
sysTime.wMinute = (WORD)newtime->tm_min;
sysTime.wSecond = (WORD)newtime->tm_sec;
sysTime.wMilliseconds = 0;
GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &sysTime, NULL, dest, destlen);
size_t dateSize = lstrlenW(dest);
dest += dateSize;
destlen -= dateSize;
if (destlen)
{
*dest++ = L' ';
destlen--;
}
GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &sysTime, NULL, dest, destlen);
//wcsftime(expire_time, 63, L"%b %d, %Y", _localtime64(&convertTime));
}
else
dest[0] = 0;
}
#define MAINTABLE_ID_CLOUD (unsigned char)-1
const unsigned char extra_idsW[] =
{
MAINTABLE_ID_ISPODCAST,
MAINTABLE_ID_PODCASTCHANNEL,
MAINTABLE_ID_PODCASTPUBDATE,
MAINTABLE_ID_GRACENOTEFILEID,
MAINTABLE_ID_GRACENOTEEXTDATA,
MAINTABLE_ID_LOSSLESS,
MAINTABLE_ID_CODEC,
MAINTABLE_ID_DIRECTOR,
MAINTABLE_ID_PRODUCER,
MAINTABLE_ID_WIDTH,
MAINTABLE_ID_HEIGHT,
MAINTABLE_ID_MIMETYPE,
0,
MAINTABLE_ID_DATEADDED,
MAINTABLE_ID_CLOUD,
};
const ExtendedFields extended_fields =
{
L"ispodcast",
L"podcastchannel",
L"podcastpubdate",
L"GracenoteFileID",
L"GracenoteExtData",
L"lossless",
L"codec",
L"director",
L"producer",
L"width",
L"height",
L"mime",
L"realsize",
L"dateadded",
L"cloud",
};
const wchar_t *extra_strsW[] =
{
extended_fields.ispodcast,
extended_fields.podcastchannel,
extended_fields.podcastpubdate,
extended_fields.GracenoteFileID,
extended_fields.GracenoteExtData,
extended_fields.lossless,
extended_fields.codec,
extended_fields.director,
extended_fields.producer,
extended_fields.width,
extended_fields.height,
extended_fields.mimetype,
extended_fields.realsize,
extended_fields.dateadded,
extended_fields.cloud,
};
const int NUM_EXTRA_COLSW = sizeof(extra_idsW) / sizeof(*extra_idsW);
bool isMixable(itemRecordW &song);
static int predixisExist;
static BOOL g_displaysearch = TRUE;
static BOOL g_displaycontrols = TRUE;
nde_scanner_t m_media_scanner = 0;
W_ListView resultlist;
static int resultSkin;
void fileInfoDialogs(HWND hwndParent);
void editInfo(HWND hwndParent);
void customizeColumnsDialog(HWND hwndParent);
static HWND m_hwnd;
itemRecordListW itemCache;
static int bgThread_Kill = 0;
static HANDLE bgThread_Handle;
static bool isMixablePresent = true;
CloudFiles cloudFiles, cloudUploading;
typedef struct
{
UINT column_id;
char *config_name;
WORD defWidth;
WORD minWidth;
WORD maxWidth;
}
headerColumn;
#define UNLIMITED_WIDTH ((WORD)-1)
#define COLUMN_DEFMINWIDTH 12
#define COLUMN_DEFMAXWIDTH UNLIMITED_WIDTH
#define MAX_COLUMN_ORDER (MEDIAVIEW_COL_NUMS+1)
static headerColumn columnList[MAX_COLUMN_ORDER - 1] =
{
{IDS_ARTIST, "mv_col_artist", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_TITLE, "mv_col_title", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_ALBUM, "mv_col_album", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_LENGTH, "mv_col_length", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_TRACK_NUMBER, "mv_col_track", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_GENRE, "mv_col_genre", 75, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_YEAR, "mv_col_year", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_FILENAME, "mv_col_fn", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_RATING, "mv_col_rating", 65, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_PLAY_COUNT, "mv_col_playcount", 70, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_PLAYED_LAST, "mv_col_lastplay", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_LAST_UPDATED, "mv_col_lastupd", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_FILE_TIME, "mv_col_filetime", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_COMMENT, "mv_col_comment", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_FILE_SIZE, "mb_col_filesize", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_BITRATE, "mb_col_bitrate", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_TYPE, "mb_col_type", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_DISC, "mb_col_disc", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_ALBUM_ARTIST, "mb_col_albumartist", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_FILE_PATH, "mb_col_filepath", 140, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_ALBUM_GAIN, "mb_col_albumgain", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_TRACK_GAIN, "mb_col_trackgain", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_PUBLISHER, "mb_col_publisher", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_COMPOSER, "mb_col_composer", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_EXTENSION, "mb_col_extension", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_IS_PODCAST, "mb_col_ispodcast", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_PODCAST_CHANNEL, "mb_col_podcastchannel", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_PODCAST_PUBLISH_DATE, "mb_col_podcastpubdate", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_BPM, "mb_col_bpm", 55, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_CATEGORY, "mb_col_category", 75, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_DIRECTOR, "mb_col_director", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_PRODUCER, "mb_col_producer", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_DIMENSION, "mb_col_dimension", 100, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_DATE_ADDED, "mb_col_dateadded", 115, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
{IDS_CLOUD, "mv_col_cloud", 27, COLUMN_DEFMINWIDTH, COLUMN_DEFMAXWIDTH},
};
//default column order
static signed char defColumnOrderCloud[MAX_COLUMN_ORDER] =
{
MEDIAVIEW_COL_ARTIST,
MEDIAVIEW_COL_ALBUM,
MEDIAVIEW_COL_TRACK,
MEDIAVIEW_COL_CLOUD,
MEDIAVIEW_COL_TITLE,
MEDIAVIEW_COL_LENGTH,
MEDIAVIEW_COL_GENRE,
MEDIAVIEW_COL_RATING,
MEDIAVIEW_COL_PLAYCOUNT,
MEDIAVIEW_COL_LASTPLAY,
MEDIAVIEW_COL_YEAR,
-1
};
static signed char defColumnOrder[MAX_COLUMN_ORDER] =
{
MEDIAVIEW_COL_ARTIST,
MEDIAVIEW_COL_ALBUM,
MEDIAVIEW_COL_TRACK,
MEDIAVIEW_COL_TITLE,
MEDIAVIEW_COL_LENGTH,
MEDIAVIEW_COL_GENRE,
MEDIAVIEW_COL_RATING,
MEDIAVIEW_COL_PLAYCOUNT,
MEDIAVIEW_COL_LASTPLAY,
MEDIAVIEW_COL_YEAR,
-1
};
static signed char columnOrder[MAX_COLUMN_ORDER];
int WCSCMP_NULLOK(const wchar_t *pa, const wchar_t *pb)
{
if (!pa) pa = L"";
else SKIP_THE_AND_WHITESPACEW(pa)
if (!pb) pb = L"";
else SKIP_THE_AND_WHITESPACEW(pb)
return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE /*| NORM_IGNORENONSPACE*/, pa, -1, pb, -1) - 2;
// return lstrcmpi(pa, pb);
}
int FLOATWCMP_NULLOK(const wchar_t *pa, const wchar_t *pb)
{
if (pa) SKIP_THE_AND_WHITESPACEW(pa)
if (pb) SKIP_THE_AND_WHITESPACEW(pb)
if ((!pa || !*pa) && (!pb || !*pb))
return 0;
if (!pa || !*pa)
return 1;
if (!pb || !*pb)
return -1;
_locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
float a = (float)_wtof_l(pa,C_locale);
float b = (float)_wtof_l(pb,C_locale);
if (a > b)
return 1;
else if (a < b)
return -1;
else
return 0;
}
typedef struct
{
resultsniff_funcW cb;
int user32;
}
bgThreadParms;
static int bg_total_len_s;
static __int64 bg_total_len_bytes;
static LARGE_INTEGER querytime;
static HMENU rate_hmenu = NULL;
static HMENU cloud_hmenu = NULL;
static HMENU sendto_hmenu = NULL;
static librarySendToMenuStruct s;
static RATINGCOLUMN ratingColumn;
static WCHAR ratingBackText[128];
#define IDC_LIST2HEADER 2001 // WM_INITDIALOG assign this is to the IDC_LIST2 header
// internal messages
#define IM_SYNCHEADERORDER (WM_USER + 0xFFF0)
static DWORD WINAPI bgThreadQueryProc(void *tmp)
{
bgThreadParms *p = (bgThreadParms*)tmp;
bg_total_len_s = 0;
bg_total_len_bytes = 0;
LARGE_INTEGER starttime, endtime;
QueryPerformanceCounter(&starttime);
bg_total_len_s = saveQueryToListW(g_view_metaconf, m_media_scanner, &itemCache,
&cloudFiles, &cloudUploading, p->cb, p->user32,
&bgThread_Kill, &bg_total_len_bytes);
QueryPerformanceCounter(&endtime);
querytime.QuadPart = endtime.QuadPart - starttime.QuadPart;
if (!bgThread_Kill) PostMessage(m_hwnd, WM_APP + 3, 0x69, 0);
return 0;
}
void bgQuery_Stop() // exported for other people to call since it is useful (eventually
// we should have bgQuery pass the new query info along but I'll do that soon)
{
if (bgThread_Handle)
{
bgThread_Kill = 1;
WaitForSingleObject(bgThread_Handle, INFINITE);
CloseHandle(bgThread_Handle);
bgThread_Handle = 0;
}
KillTimer(m_hwnd, 123);
}
static void bgQuery(resultsniff_funcW cb = 0, int user32 = 0) // only internal used
{
bgQuery_Stop();
SetDlgItemTextW(m_hwnd, IDC_MEDIASTATUS, WASABI_API_LNGSTRINGW(IDS_SCANNING));
StringCchCopyW(oldText, 4096, WASABI_API_LNGSTRINGW(IDS_SCANNING));
SetTimer(m_hwnd, 123, 200, NULL);
DWORD id;
static bgThreadParms parms;
parms.cb = cb;
parms.user32 = user32;
bgThread_Kill = 0;
bgThread_Handle = CreateThread(NULL, 0, bgThreadQueryProc, (LPVOID) & parms, 0, &id);
}
// this thing does not produce a fully valid itemRecordList. be afraid.
static void copyFilesToItemCacheW(itemRecordListW *obj)
{
if (bgThread_Handle) return ;
int cnt = itemCache.Size;
int i, l = cnt;
cnt = 0;
for (i = 0;i < l;i++)
{
if (resultlist.GetSelected(i)) cnt++;
}
obj->Alloc = obj->Size = 0;
if (!cnt) return ;
allocRecordList(obj, cnt, 0);
if (!obj->Items)
{
obj->Size = obj->Alloc = 0;
return ;
}
for (i = 0; i < itemCache.Size; i ++)
{
if (resultlist.GetSelected(i))
{
obj->Items[obj->Size++] = itemCache.Items[i];
// makes sure that we are providing filesize in kb as
// per spec even if we store it as __int64 internally
obj->Items[obj->Size-1].filesize /= 1024;
}
}
}
void playFiles(int enqueue, int all)
{
if (bgThread_Handle) return ;
int cnt = 0;
int l = itemCache.Size;
int foo_all = 0; // all but play the only selected
int foo_selected = -1;
if (!enqueue && !all && g_config->ReadInt(L"viewplaymode", 1))
{
int selcnt = 0;
for (int i = 0;i < l;i++)
{
if (resultlist.GetSelected(i)) selcnt++;
}
if (selcnt == 1)
{
foo_all = -1;
}
}
for (int i = 0;i < l;i++)
{
if (foo_all || all || resultlist.GetSelected(i))
{
if (foo_all && foo_selected < 0 && resultlist.GetSelected(i)) foo_selected = i;
if (!cnt)
{
if (!enqueue) SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_DELETE);
cnt++;
}
wchar_t title[2048] = {0};
TAG_FMT_EXT(itemCache.Items[i].filename, itemrecordWTagFunc, ndeTagFuncFree, (void*)&itemCache.Items[i], title, 2048, 0);
enqueueFileWithMetaStructW s;
s.filename = itemCache.Items[i].filename;
s.title = title;
s.ext = NULL;
s.length = itemCache.Items[i].length;
#ifndef _DEBUG
ndestring_retain(itemCache.Items[i].filename);
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW_NDE);
#else
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW);
#endif
}
}
if (cnt)
{
if (foo_selected >= 0)
{
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, foo_selected, IPC_SETPLAYLISTPOS);
SendMessage(plugin.hwndWinampParent, WM_COMMAND, 40047, 0); // stop button, literally
SendMessage(plugin.hwndWinampParent, WM_COMMAND, 40045, 0); // play button, literally
}
else if (!enqueue) SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_STARTPLAY);
}
}
// out can never be bigger than in+1
static void parsequicksearch(wchar_t *out, wchar_t *in) // parses a list into a list of terms that we are searching for
{
int inquotes = 0, neednull = 0;
while (in && *in)
{
wchar_t c = *in++;
if (c != ' ' && c != '\t' && c != '\"')
{
neednull = 1;
*out++ = c;
}
else if (c == '\"')
{
inquotes = !inquotes;
if (!inquotes)
{
*out++ = 0;
neednull = 0;
}
}
else
{
if (inquotes) *out++ = c;
else if (neednull)
{
*out++ = 0;
neednull = 0;
}
}
}
*out++ = 0;
*out++ = 0;
}
void makeQueryStringFromText(GayStringW *query, wchar_t *text, int nf)
{
int ispar = 0;
if (query->Get()[0])
{
ispar = 1;
query->Append(L"&(");
}
if (!_wcsnicmp(text, L"query:", 6))
query->Append(text + 6); // copy the query as is
else if (text[0] == L'?')
query->Append(text + 1);
else
{
int isAny = 0;
if (*text == L'*' && text[1] == L' ')
{
isAny = 1;
text += 2;
}
wchar_t tmpbuf[2048 + 32] = {0};
parsequicksearch(tmpbuf, text);
int x;
wchar_t *fields[] =
{
L"filename",
L"title",
L"artist",
L"album",
L"genre",
L"albumartist",
L"publisher",
L"composer",
};
wchar_t *p = tmpbuf;
while (p && *p)
{
size_t lenp = wcslen(p);
if (p == tmpbuf) query->Append(L"(");
else if (isAny) query->Append(L")|(");
else query->Append(L")&(");
if (p[0] == L'<' && p[wcslen(p) - 1] == L'>' && wcslen(p) > 2)
{
wchar_t *op = p;
while (op && *op)
{
if (*op == L'\'') *op = L'\"';
op++;
}
p[lenp - 1] = 0; // remove >
query->Append(p + 1);
}
else
{
for (x = 0; x < (int)min(sizeof(fields) / sizeof(fields[0]), nf); x ++)
{
wchar_t *field = fields[x];
if (x) query->Append(L"|");
query->Append(field);
query->Append(L" HAS \"");
GayStringW escaped;
queryStrEscape(p, escaped);
query->Append(escaped.Get());
query->Append(L"\"");
}
}
p += lenp + 1;
}
query->Append(L")");
}
if (ispar) query->Append(L")");
}
static void doQuery(HWND hwndDlg, wchar_t *text, int dobg = 1)
{
bgQuery_Stop();
GayStringW query;
if (text[0]) makeQueryStringFromText(&query, text);
wchar_t *parent_query = NULL;
extern wchar_t* m_query;
parent_query = m_query;
SendMessage(GetParent(hwndDlg), WM_APP + 2, 0, (LPARAM)&parent_query);
GayStringW q;
if (parent_query && parent_query[0])
{
q.Set(L"(");
q.Append(parent_query);
q.Append(L")");
}
if (query.Get() && query.Get()[0])
{
if (q.Get()[0])
{
q.Append(L" & (");
q.Append(query.Get());
q.Append(L")");
}
else q.Set(query.Get());
}
EnterCriticalSection(&g_db_cs);
NDE_Scanner_Query(m_media_scanner, q.Get());
LeaveCriticalSection(&g_db_cs);
if (dobg) bgQuery();
}
static void RecycleSelectedItems()
{
int totalItems = resultlist.GetSelectedCount();
if (!totalItems)
return ;
SHFILEOPSTRUCTW fileOp;
fileOp.hwnd = m_hwnd;
fileOp.wFunc = FO_DELETE;
fileOp.pFrom = 0;
fileOp.pTo = 0;
fileOp.fFlags = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_USES_RECYCLEBIN) ? FOF_ALLOWUNDO : 0;
fileOp.fAnyOperationsAborted = 0;
fileOp.hNameMappings = 0;
fileOp.lpszProgressTitle = 0;
EnterCriticalSection(&g_db_cs);
nde_scanner_t s = NDE_Table_CreateScanner(g_table);
int cchLen = totalItems * (MAX_PATH + 1) + 1;
wchar_t *files = new wchar_t[cchLen]; // need room for each file name, null terminated. then have to null terminate the whole list
if (files) // if malloc succeeded
{
wchar_t *curFile = files;
for (int i = 0; i < itemCache.Size; i++)
{
if (resultlist.GetSelected(i))
{
if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[i].filename))
{
StringCchCopyW(curFile, cchLen, itemCache.Items[i].filename);
curFile += wcslen(itemCache.Items[i].filename) + 1;
}
}
}
if (curFile != files)
{
curFile[0] = 0; // null terminate
fileOp.pFrom = files;
fileOp.fAnyOperationsAborted = 0;
if (SHFileOperationW(&fileOp))
{
wchar_t title[64] = {0};
MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_ERROR_DELETING_FILES),
WASABI_API_LNGSTRINGW_BUF(IDS_ERROR,title,64), MB_OK);
}
else
{
// only remove items if deletion was allowed
if (!fileOp.fAnyOperationsAborted)
{
for (int j = 0; j < itemCache.Size; j++)
{
if (resultlist.GetSelected(j))
{
if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[j].filename))
{
// Wasabi callback event for pre remove
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)itemCache.Items[j].filename, 0);
NDE_Scanner_Delete(s);
NDE_Scanner_Post(s);
g_table_dirty++;
// Wasabi callback event for post remove
// ToDo: (BigG) Move outside of critical section somehow
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)itemCache.Items[j].filename, 0);
}
}
}
}
}
}
delete [] files;
}
else // if malloc failed ... maybe because there's too many items.
{
files = new wchar_t[MAX_PATH + 1 + 1]; // double null termination
if (!files) // if this malloc failed, just bail out
{
NDE_Table_DestroyScanner(g_table, s);
LeaveCriticalSection(&g_db_cs);
return ;
}
fileOp.pFrom = files;
for (int i = 0;i < itemCache.Size;i++)
{
if (resultlist.GetSelected(i))
{
if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[i].filename))
{
StringCchCopyW(files, MAX_PATH + 1 + 1, itemCache.Items[i].filename);
files[wcslen(itemCache.Items[i].filename) + 1] = 0; // double null terminate
fileOp.fAnyOperationsAborted = 0;
if (SHFileOperationW(&fileOp))
{
wchar_t mes[4096] = {0};
StringCchPrintfW(mes, 4096, WASABI_API_LNGSTRINGW(IDS_ERROR_DELETING_X), files);
MessageBoxW(m_hwnd, mes, WASABI_API_LNGSTRINGW(IDS_ERROR), MB_OK);
continue;
}
if (!fileOp.fAnyOperationsAborted)
{
// Wasabi callback event for pre remove
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)itemCache.Items[i].filename, 0);
NDE_Scanner_Delete(s);
NDE_Scanner_Post(s);
g_table_dirty++;
// Wasabi callback event for post remove
// ToDo: (BigG) Move outside of critical section somehow
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)itemCache.Items[i].filename, 0);
}
}
}
delete files;
}
}
NDE_Table_DestroyScanner(g_table, s);
if (g_table_dirty) NDE_Table_Sync(g_table);
g_table_dirty = 0;
LeaveCriticalSection(&g_db_cs);
resultlist.Clear();
emptyRecordList(&itemCache);
// this might be gay, refreshing it completely (i.e. the cursor pos gets put back to normal, etc),
// but really it is necessary for the view to be accurate.
SendMessage(m_hwnd, WM_APP + 1, 0, 0); //refresh current view
}
static void removeSelectedItems(int physical)
{
if (physical)
{
RecycleSelectedItems();
return ;
}
int hasdel = 0;
EnterCriticalSection(&g_db_cs);
nde_scanner_t s = NDE_Table_CreateScanner(g_table);
for (int i = 0;i < itemCache.Size;i++)
{
if (resultlist.GetSelected(i))
{
if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[i].filename))
{
wchar_t conf[32] = {0};
if (!hasdel && MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_SURE_YOU_WANT_TO_REMOVE_SELECTED_FROM_LIBRARY),
WASABI_API_LNGSTRINGW_BUF(IDS_CONFIRMATION,conf,32), MB_YESNO | MB_ICONQUESTION) != IDYES)
{
NDE_Table_DestroyScanner(g_table, s);
LeaveCriticalSection(&g_db_cs);
return ;
//FUCKO> need to eat the RETURN msg
//MSG msg;
//while(PeekMessage(&msg,m_hwnd,WM_COMMAND,WM_COMMAND,1));
}
if (!hasdel) // stop any background queries
{
bgQuery_Stop();
}
// Wasabi callback event for pre remove
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_PRE, (size_t)itemCache.Items[i].filename, 0);
hasdel = 1;
NDE_Scanner_Delete(s);
NDE_Scanner_Post(s);
g_table_dirty++;
// Wasabi callback event for post remove
// ToDo: (BigG) Move this outside of the critical section
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_REMOVED_POST, (size_t)itemCache.Items[i].filename, 0);
}
}
}
NDE_Table_DestroyScanner(g_table, s);
if (g_table_dirty) NDE_Table_Sync(g_table);
g_table_dirty = 0;
LeaveCriticalSection(&g_db_cs);
if (!hasdel) return ;
resultlist.Clear();
emptyRecordList(&itemCache);
// this might be gay, refreshing it completely (i.e. the cursor pos gets put back to normal, etc),
// but really it is necessary for the view to be accurate.
SendMessage(m_hwnd, WM_APP + 1, 0, 0); //refresh current view
}
static void exploreItemFolder(HWND hwndDlg)
{
if (resultlist.GetSelectionMark() >= 0)
{
int l=resultlist.GetCount();
for(int i=0;i<l;i++)
{
if (resultlist.GetSelected(i))
{
WASABI_API_EXPLORERFINDFILE->AddFile(itemCache.Items[i].filename);
}
}
WASABI_API_EXPLORERFINDFILE->ShowFiles();
}
}
static void removeDeadFiles(HWND hwndDlg)
{
Scan_RemoveFiles(hwndDlg);
// this might be gay, refreshing it completely (i.e. the cursor pos gets put back to normal, etc),
// but really it is necessary for the view to be accurate.
SendMessage(m_hwnd, WM_APP + 1, 0, 0); //refresh current view
}
static WNDPROC search_oldWndProc;
static DWORD WINAPI search_newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_KEYDOWN && wParam == VK_DOWN)
{
PostMessageW(GetParent(hwndDlg), WM_NEXTDLGCTL, (WPARAM)resultlist.getwnd(), (LPARAM)TRUE);
ListView_SetItemState(resultlist.getwnd(), 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
}
return CallWindowProcW(search_oldWndProc, hwndDlg, uMsg, wParam, lParam);
}
typedef struct _LAYOUT
{
INT id;
HWND hwnd;
INT x;
INT y;
INT cx;
INT cy;
DWORD flags;
HRGN rgn;
}
LAYOUT, PLAYOUT;
#define SETLAYOUTPOS(_layout, _x, _y, _cx, _cy) { _layout->x=_x; _layout->y=_y;_layout->cx=_cx;_layout->cy=_cy;_layout->rgn=NULL; }
#define SETLAYOUTFLAGS(_layout, _r) \
{ \
BOOL fVis; \
fVis = (WS_VISIBLE & (LONG)GetWindowLongPtr(_layout->hwnd, GWL_STYLE)); \
if (_layout->x == _r.left && _layout->y == _r.top) _layout->flags |= SWP_NOMOVE; \
if (_layout->cx == (_r.right - _r.left) && _layout->cy == (_r.bottom - _r.top)) _layout->flags |= SWP_NOSIZE; \
if ((SWP_HIDEWINDOW & _layout->flags) && !fVis) _layout->flags &= ~SWP_HIDEWINDOW; \
if ((SWP_SHOWWINDOW & _layout->flags) && fVis) _layout->flags &= ~SWP_SHOWWINDOW; \
}
#define LAYOUTNEEEDUPDATE(_layout) ((SWP_NOMOVE | SWP_NOSIZE) != ((SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_SHOWWINDOW) & _layout->flags))
#define GROUP_MIN 0x1
#define GROUP_MAX 0x3
#define GROUP_SEARCH 0x1
#define GROUP_STATUSBAR 0x2
#define GROUP_MAIN 0x3
static void LayoutWindows(HWND hwnd, BOOL fRedraw, BOOL fUpdateAll = FALSE)
{
static INT controls[] =
{
GROUP_SEARCH, IDC_SEARCHCAPTION, IDC_CLEAR, IDC_QUICKSEARCH,
GROUP_STATUSBAR, IDC_BUTTON_PLAY, IDC_BUTTON_ENQUEUE, IDC_BUTTON_MIX, IDC_BUTTON_CREATEPLAYLIST, IDC_BUTTON_INFOTOGGLE, IDC_MIXABLE, IDC_MEDIASTATUS,
GROUP_MAIN, IDC_LIST2
};
INT index;
RECT rc, rg, ri;
LAYOUT layout[sizeof(controls)/sizeof(controls[0])], *pl;
BOOL skipgroup;
HRGN rgn = NULL;
GetClientRect(hwnd, &rc);
if (rc.bottom == rc.top || rc.right == rc.left) return;
SetRect(&rg, rc.left, rc.top, rc.right, rc.bottom);
pl = layout;
skipgroup = FALSE;
InvalidateRect(hwnd, NULL, TRUE);
for (index = 0; index < sizeof(controls) / sizeof(*controls); index++)
{
if (controls[index] >= GROUP_MIN && controls[index] <= GROUP_MAX) // group id
{
skipgroup = FALSE;
switch (controls[index])
{
case GROUP_SEARCH:
if (g_displaysearch)
{
wchar_t buffer[128] = {0};
HWND ctrl = GetDlgItem(hwnd, IDC_CLEAR);
GetWindowTextW(ctrl, buffer, ARRAYSIZE(buffer));
LRESULT idealSize = MLSkinnedButton_GetIdealSize(ctrl, buffer);
SetRect(&rg, rc.left,
rc.top + WASABI_API_APP->getScaleY(2),
rc.right - WASABI_API_APP->getScaleX(2),
rc.top + WASABI_API_APP->getScaleY(HIWORD(idealSize)+1));
rc.top = rg.bottom + WASABI_API_APP->getScaleY(3);
}
skipgroup = !g_displaysearch;
break;
case GROUP_STATUSBAR:
if (g_displaycontrols)
{
wchar_t buffer[128] = {0};
HWND ctrl = GetDlgItem(hwnd, IDC_BUTTON_PLAY);
GetWindowTextW(ctrl, buffer, ARRAYSIZE(buffer));
LRESULT idealSize = MLSkinnedButton_GetIdealSize(ctrl, buffer);
SetRect(&rg, rc.left + WASABI_API_APP->getScaleX(1),
rc.bottom - WASABI_API_APP->getScaleY(HIWORD(idealSize)),
rc.right, rc.bottom);
rc.bottom = rg.top - WASABI_API_APP->getScaleY(3);
}
skipgroup = !g_displaycontrols;
break;
case GROUP_MAIN:
SetRect(&rg, rc.left + WASABI_API_APP->getScaleX(1), rc.top, rc.right, rc.bottom);
break;
}
continue;
}
if (skipgroup) continue;
pl->id = controls[index];
pl->hwnd = GetDlgItem(hwnd, pl->id);
if (!pl->hwnd) continue;
GetWindowRect(pl->hwnd, &ri);
MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&ri, 2);
pl->flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS;
switch (pl->id)
{
case IDC_SEARCHCAPTION:
{
wchar_t buffer[128] = {0};
GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer));
LRESULT idealSize = MLSkinnedStatic_GetIdealSize(pl->hwnd, buffer);
SETLAYOUTPOS(pl, rg.left + WASABI_API_APP->getScaleX(2),
rg.top + WASABI_API_APP->getScaleY(1),
WASABI_API_APP->getScaleX(LOWORD(idealSize)),
(rg.bottom - rg.top));
rg.left += (pl->cx + WASABI_API_APP->getScaleX(4));
break;
}
case IDC_CLEAR:
{
wchar_t buffer[128] = {0};
GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer));
LRESULT idealSize = MLSkinnedButton_GetIdealSize(pl->hwnd, buffer);
LONG width = LOWORD(idealSize) + WASABI_API_APP->getScaleX(6);
pl->flags |= (((rg.right - rg.left) - width) > WASABI_API_APP->getScaleX(40)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW ;
SETLAYOUTPOS(pl, rg.right - width, rg.top, width, rg.bottom - rg.top);
if (SWP_SHOWWINDOW & pl->flags) rg.right -= (pl->cx + WASABI_API_APP->getScaleX(4));
break;
}
case IDC_QUICKSEARCH:
pl->flags |= (rg.right > rg.left) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
SETLAYOUTPOS(pl, rg.left, rg.top, rg.right - rg.left - WASABI_API_APP->getScaleX(1),
(rg.bottom - rg.top) - WASABI_API_APP->getScaleY(1));
break;
case IDC_BUTTON_PLAY:
case IDC_BUTTON_ENQUEUE:
case IDC_BUTTON_MIX:
case IDC_BUTTON_CREATEPLAYLIST:
if (IDC_BUTTON_MIX != pl->id || customAllowed)
{
if (groupBtn && pl->id == IDC_BUTTON_PLAY && enqueuedef == 1)
{
pl->flags |= SWP_HIDEWINDOW;
break;
}
if (groupBtn && pl->id == IDC_BUTTON_ENQUEUE && enqueuedef != 1)
{
pl->flags |= SWP_HIDEWINDOW;
break;
}
if (groupBtn && (pl->id == IDC_BUTTON_PLAY || pl->id == IDC_BUTTON_ENQUEUE) && customAllowed)
{
pl->flags |= SWP_HIDEWINDOW;
break;
}
if (pl->id == IDC_BUTTON_CREATEPLAYLIST && !AGAVE_API_PLAYLIST_GENERATOR)
{
pl->flags |= SWP_HIDEWINDOW;
break;
}
wchar_t buffer[128] = {0};
GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer));
LRESULT idealSize = MLSkinnedButton_GetIdealSize(pl->hwnd, buffer);
LONG width = LOWORD(idealSize) + WASABI_API_APP->getScaleX(6);
SETLAYOUTPOS(pl, rg.left, rg.bottom - WASABI_API_APP->getScaleY(HIWORD(idealSize)),
width, WASABI_API_APP->getScaleY(HIWORD(idealSize)));
pl->flags |= ((rg.right - rg.left) > width) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
if (SWP_SHOWWINDOW & pl->flags) rg.left += (pl->cx + WASABI_API_APP->getScaleX(4));
}
else
pl->flags |= SWP_HIDEWINDOW;
break;
case IDC_BUTTON_INFOTOGGLE:
switch (SendMessage(GetParent(hwnd), WM_USER + 66, 0, 0))
{
case 0xFF:
case 0xF0:
{
wchar_t buffer[128] = {0};
GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer));
LRESULT idealSize = MLSkinnedButton_GetIdealSize(pl->hwnd, buffer);
LONG width = LOWORD(idealSize) + WASABI_API_APP->getScaleX(6);
pl->flags |= (((rg.right - rg.left) - (ri.right - ri.left)) > WASABI_API_APP->getScaleX(60)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW ;
SETLAYOUTPOS(pl, rg.right - width - WASABI_API_APP->getScaleX(2),
rg.top, width, (rg.bottom - rg.top));
if (SWP_SHOWWINDOW & pl->flags) rg.right -= (pl->cx + WASABI_API_APP->getScaleX(4));
break;
}
}
break;
case IDC_MIXABLE:
if (predixisExist & 1)
{
SETLAYOUTPOS(pl, rg.right - (ri.right - ri.left), rg.top, (ri.right - ri.left), (rg.bottom - rg.top));
pl->flags |= ((rg.right - rg.left) - (ri.right - ri.left) > WASABI_API_APP->getScaleX(60)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
if (SWP_SHOWWINDOW & pl->flags) rg.right -= (pl->cx + WASABI_API_APP->getScaleX(4));
}
break;
case IDC_MEDIASTATUS:
SETLAYOUTPOS(pl, rg.left, rg.top, rg.right - rg.left, (rg.bottom - rg.top));
pl->flags |= (pl->cx > WASABI_API_APP->getScaleX(16)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
break;
case IDC_LIST2:
SETLAYOUTPOS(pl, rg.left, rg.top + 1, (rg.right - rg.left) - WASABI_API_APP->getScaleX(3), (rg.bottom - rg.top) - WASABI_API_APP->getScaleY(2));
break;
}
SETLAYOUTFLAGS(pl, ri);
if (LAYOUTNEEEDUPDATE(pl))
{
if (SWP_NOSIZE == ((SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_NOSIZE) & pl->flags) &&
ri.left == (pl->x + offsetX) && ri.top == (pl->y + offsetY) && !fUpdateAll && IsWindowVisible(pl->hwnd))
{
SetRect(&ri, pl->x, pl->y, pl->cx + pl->x, pl->y + pl->cy);
ValidateRect(hwnd, &ri);
}
pl++;
}
else if (!fUpdateAll && (fRedraw || (!offsetX && !offsetY)) && IsWindowVisible(pl->hwnd))
{
ValidateRect(hwnd, &ri);
if (GetUpdateRect(pl->hwnd, NULL, FALSE))
{
if (!rgn) rgn = CreateRectRgn(0,0,0,0);
GetUpdateRgn(pl->hwnd, rgn, FALSE);
OffsetRgn(rgn, pl->x, pl->y);
InvalidateRgn(hwnd, rgn, FALSE);
}
}
}
if (pl != layout)
{
LAYOUT *pc;
HDWP hdwp = BeginDeferWindowPos((INT)(pl - layout));
for (pc = layout; pc < pl && hdwp; pc++)
{
hdwp = DeferWindowPos(hdwp, pc->hwnd, NULL, pc->x, pc->y, pc->cx, pc->cy, pc->flags);
}
if (hdwp) EndDeferWindowPos(hdwp);
if (!rgn) rgn = CreateRectRgn(0, 0, 0, 0);
if (fRedraw)
{
GetUpdateRgn(hwnd, rgn, FALSE);
for (pc = layout; pc < pl && hdwp; pc++)
{
if (pc->rgn)
{
OffsetRgn(pc->rgn, pc->x, pc->y);
CombineRgn(rgn, rgn, pc->rgn, RGN_OR);
}
}
RedrawWindow(hwnd, NULL, rgn, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN);
}
if (g_rgnUpdate)
{
GetUpdateRgn(hwnd, g_rgnUpdate, FALSE);
for (pc = layout; pc < pl && hdwp; pc++)
{
if (pc->rgn)
{
OffsetRgn(pc->rgn, pc->x, pc->y);
CombineRgn(g_rgnUpdate, g_rgnUpdate, pc->rgn, RGN_OR);
}
}
}
for (pc = layout; pc < pl && hdwp; pc++)
if (pc->rgn) DeleteObject(pc->rgn);
}
if (rgn) DeleteObject(rgn);
ValidateRgn(hwnd, NULL);
}
static void updateInfoText(HWND hwndDlg, bool x = false)
{
int a = SendMessage(GetParent(hwndDlg), WM_USER + 66, x ? -1 : 0, 0);
if (a == 0xff)
SetDlgItemTextW(hwndDlg, IDC_BUTTON_INFOTOGGLE, WASABI_API_LNGSTRINGW(IDS_HIDE_INFO));
else if (a == 0xf0)
SetDlgItemTextW(hwndDlg, IDC_BUTTON_INFOTOGGLE, WASABI_API_LNGSTRINGW(IDS_SHOW_INFO));
else ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFOTOGGLE), SW_HIDE);
}
static void initColumnsHeader(HWND hwndList)
{
INT index, sortby;
LVCOLUMNW lvc = {0, };
if (!hwndList || !IsWindow(hwndList)) return;
SendMessageW(hwndList, WM_SETREDRAW, FALSE, 0L);
while (SendMessageW(hwndList, LVM_DELETECOLUMN, 0, 0L));
sortby = g_view_metaconf->ReadInt(L"mv_sort_by", 1);
lvc.mask = LVCF_TEXT | LVCF_WIDTH;
lvc.pszText = L"";
lvc.cx = 30;
SendMessageW(hwndList, LVM_INSERTCOLUMNW, 0, (LPARAM)&lvc); // create dummy column
// TODO set to a zero width if not available
MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), -1); // reset the cloud status column so it'll be correctly removed
SetPropW(hwndList, L"pmp_list_info", (HANDLE)-1);
for (index = 0; columnOrder[index] != -1; index++)
{
headerColumn *cl = &columnList[columnOrder[index]];
lvc.pszText = WASABI_API_LNGSTRINGW(cl->column_id);
lvc.cx = g_view_metaconf->ReadInt(AutoWide(cl->config_name), cl->defWidth);
if (lvc.cx < cl->minWidth) lvc.cx = cl->minWidth;
// update position of the cloud column icon
if (columnOrder[index] == MEDIAVIEW_COL_CLOUD)
{
if (!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
!SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE))
{
MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), -1);
SetPropW(hwndList, L"pmp_list_info", (HANDLE)-1);
lvc.cx = 0;
}
else
{
MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), index);
SetPropW(hwndList, L"pmp_list_info", (HANDLE)index);
lvc.cx = 27;
MLCloudColumn_GetWidth(plugin.hwndLibraryParent, &lvc.cx);
}
}
SendMessageW(hwndList, LVM_INSERTCOLUMNW, 0xFFFF, (LPARAM)&lvc);
if (sortby == columnOrder[index]) MLSkinnedListView_DisplaySort(hwndList, index, !g_view_metaconf->ReadInt(L"mv_sort_dir", 0));
}
SendMessageW(hwndList, LVM_DELETECOLUMN, 0, 0L); // Delete dummy column
for (index = 0; -1 != columnOrder[index] && MEDIAVIEW_COL_RATING != columnOrder[index]/* && MEDIAVIEW_COL_CLOUD != columnOrder[index]*/; index++);
if (-1 != index) SendMessageW(hwndList, LVM_SETCOLUMNWIDTH, index, (LPARAM)SendMessageW(hwndList, LVM_GETCOLUMNWIDTH, index, 0L));
SendMessageW(hwndList, WM_SETREDRAW, TRUE, 0L);
}
static int m_last_selitem = -1;
static int m_bgupdinfoviewerflag;
extern void add_to_library(HWND wndparent);
static INT_PTR CALLBACK needAddFilesProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
if (AGAVE_API_ITUNES_IMPORTER && AGAVE_API_ITUNES_IMPORTER->iTunesExists())
EnableWindow(GetDlgItem(hwndDlg, IDC_IMPORT_ITUNES), TRUE);
else
EnableWindow(GetDlgItem(hwndDlg, IDC_IMPORT_ITUNES), FALSE);
SetTimer(hwndDlg, 1, 1000, NULL);
return 1;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
if (BN_CLICKED == HIWORD(wParam))
{
if (IsDlgButtonChecked(hwndDlg, IDC_CHECK1)) g_config->WriteInt(L"noshowadddlg", 1);
EndDialog(hwndDlg, 0);
}
break;
case ID_ADD_FILES:
if (BN_CLICKED == HIWORD(wParam))
{
add_to_library(hwndDlg);
PostMessage(hwndDlg, WM_TIMER, 1, 0);
}
break;
case IDC_IMPORT_ITUNES:
if (BN_CLICKED == HIWORD(wParam))
{
if (AGAVE_API_ITUNES_IMPORTER)
AGAVE_API_ITUNES_IMPORTER->ImportFromiTunes(hwndDlg);
PostMessage(hwndDlg, WM_TIMER, 1, 0);
if (m_curview_hwnd) PostMessage(m_curview_hwnd, WM_APP + 1, 0, 0); //update current view
}
break;
case IDC_BTN_LINK_PROMO:
if (BN_CLICKED == HIWORD(wParam)) ShellExecuteA(plugin.hwndWinampParent, "open", "https://help.winamp.com/hc/articles/8105244490772-Player-Overview", NULL, ".", 0);
break;
}
break;
case WM_TIMER:
if (g_table && NDE_Table_GetRecordsCount(g_table))
{
wchar_t buf[512] = {0};
StringCchPrintfW(buf, 512, WASABI_API_LNGSTRINGW(IDS_THERE_ARE_NOW_X_ITEMS_IN_THE_LIBRARY), NDE_Table_GetRecordsCount(g_table));
SetDlgItemTextW(hwndDlg, IDC_TEXT, buf);
SetDlgItemTextW(hwndDlg, ID_ADD_FILES, WASABI_API_LNGSTRINGW(IDS_ADD_MORE));
}
break;
case WM_DRAWITEM:
{
DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam;
if (di->CtlType == ODT_BUTTON)
{
wchar_t wt[123] = {0};
int y;
RECT r;
HPEN hPen, hOldPen;
DWORD style;
GetDlgItemText(hwndDlg, (INT)wParam, wt, ARRAYSIZE(wt));
style = (DWORD)GetWindowLongPtrW(di->hwndItem, GWL_STYLE);
// draw text
SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
memset(&r, 0, sizeof(r));
DrawText(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT);
if (BS_RIGHT & style) r.left = max(di->rcItem.left+ 2, di->rcItem.right - r.right - 2);
else if (BS_LEFT & style) r.left = di->rcItem.left+ 2;
else r.left = ((di->rcItem.right - di->rcItem.left - 4) - r.right) / 2;
if (r.left < di->rcItem.left + 2)
{
r.left = di->rcItem.left + 2;
r.right = di->rcItem.right - 2;
}
else r.right += r.left;
if (r.right > di->rcItem.right - 2) r.right = di->rcItem.right - 2;
if (BS_TOP & style) r.top = di->rcItem.top;
else if(BS_VCENTER & style) r.top = ((di->rcItem.bottom - di->rcItem.top - 2) - r.bottom) /2;
else r.top = di->rcItem.bottom - 2 - r.bottom;
if (r.top < di->rcItem.top)
{
r.top = di->rcItem.top;
r.bottom = di->rcItem.bottom - 2;
}
else r.bottom += r.top;
if (r.bottom > di->rcItem.bottom - 2) r.bottom = di->rcItem.bottom - 2;
DrawText(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_WORD_ELLIPSIS);
// draw underline
y = min(di->rcItem.bottom, r.bottom + 1);
hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
hOldPen = (HPEN) SelectObject(di->hDC, hPen);
MoveToEx(di->hDC, r.left, y, NULL);
LineTo(di->hDC, r.right, y);
SelectObject(di->hDC, hOldPen);
DeleteObject(hPen);
}
}
}
return 0;
};
static void SetStatusText(HWND hwndStatus, LPCWSTR *ppsz, INT count)
{
WCHAR buffer[4096] = {0};
if (0 == count || !ppsz)
{
SetWindowText(hwndStatus, L"");
return;
}
buffer[0] = 0x00;
for (int i = 0; i < count; i++)
{
StringCchCatW(buffer, 4096, ppsz[i]);
StringCchCatW(buffer, 4096, L" ");
}
SetWindowTextW(hwndStatus, buffer);
StringCchCopyW(oldText, 4096, buffer);
}
static void SetRating(UINT iItem, INT newRating, HWND hwndList)
{
if (0 == newRating) newRating = -1;
if (iItem < (UINT)itemCache.Size)
{
if (g_table && newRating != itemCache.Items[iItem].rating)
{
EnterCriticalSection(&g_db_cs);
nde_scanner_t s = NDE_Table_CreateScanner(g_table);
if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[iItem].filename))
{
NDE_Scanner_Edit(s);
db_setFieldInt(s, MAINTABLE_ID_RATING, newRating);
NDE_Scanner_Post(s);
itemCache.Items[iItem].rating = newRating;
if (g_config->ReadInt(L"writeratings", 0))
{
wchar_t buf[64] = {0};
if (newRating > 0)
{
wsprintfW(buf, L"%d", newRating);
}
else
buf[0] = 0;
updateFileInfo(itemCache.Items[iItem].filename, DB_FIELDNAME_rating, buf);
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_WRITE_EXTENDED_FILE_INFO);
}
}
NDE_Table_DestroyScanner(g_table, s);
if (g_table_dirty)
{
g_table_dirty = 0;
NDE_Table_Sync(g_table);
}
LeaveCriticalSection(&g_db_cs);
}
if (newRating == itemCache.Items[iItem].rating && hwndList)
{
ratingColumn.hwndList = hwndList;
ratingColumn.iItem = iItem;
ratingColumn.iSubItem = 600;
MLRatingColumn_Animate(plugin.hwndLibraryParent, &ratingColumn);
ListView_RedrawItems(resultlist.getwnd(), iItem, iItem);
// TODO: benski> update the top panes w/o refreshing, if possible
// CUT: PostMessage(GetParent(GetParent(hwndList)), WM_APP + 4, (WPARAM)newRating, (LPARAM)1);
}
}
}
/////////// Header Messages / Notifications
static BOOL Header_OnItemChanging(HWND hwndDlg, NMHEADERW *phdr, LRESULT *pResult, UINT uMsg)
{
if (phdr->pitem && (HDI_WIDTH & phdr->pitem->mask))
{
INT test;
test = columnList[columnOrder[phdr->iItem]].minWidth;
if (phdr->pitem->cxy < test) phdr->pitem->cxy = test;
test = columnList[columnOrder[phdr->iItem]].maxWidth;
if (test != UNLIMITED_WIDTH && phdr->pitem->cxy > test) phdr->pitem->cxy = test;
if (MEDIAVIEW_COL_RATING == columnOrder[phdr->iItem])
{
RATINGWIDTH rw;
rw.fStyle = RCS_DEFAULT;
rw.width = phdr->pitem->cxy;
if (MLRatingColumn_GetWidth(plugin.hwndLibraryParent, &rw)) phdr->pitem->cxy = rw.width;
if (0 == phdr->iItem)
{
RATINGBACKTEXT rbt;
rbt.pszText = ratingBackText;
rbt.cchTextMax = sizeof(ratingBackText)/sizeof(WCHAR);
rbt.nColumnWidth = phdr->pitem->cxy;
rbt.fStyle = RCS_DEFAULT;
MLRatingColumn_FillBackString(plugin.hwndLibraryParent, &rbt);
}
else ratingBackText[0] = 0x00;
}
else if (MEDIAVIEW_COL_CLOUD == columnOrder[phdr->iItem])
{
if (!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
!SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE))
phdr->pitem->cxy = 0;
else
{
INT width = phdr->pitem->cxy;
if (MLCloudColumn_GetWidth(plugin.hwndLibraryParent, &width))
{
phdr->pitem->cxy = width;
}
}
}
}
return FALSE;
}
static BOOL Header_OnEndDrag(HWND hwndDlg, NMHEADERW *phdr, LRESULT *pResult)
{
PostMessageW(hwndDlg, IM_SYNCHEADERORDER, 0, (LPARAM)phdr->hdr.hwndFrom);
return FALSE;
}
static BOOL Header_OnRightClick(HWND hwndDlg, NMHDR *pnmh, LRESULT *pResult)
{
HMENU menu = GetSubMenu(g_context_menus, 4);
POINT p;
GetCursorPos(&p);
int r = DoTrackPopup(menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, p.x, p.y, hwndDlg, NULL);
switch (r)
{
case ID_HEADERWND_CUSTOMIZECOLUMNS:
customizeColumnsDialog(hwndDlg);
break;
}
return FALSE;
}
/////////// ListView Messages / Notifications
static BOOL ListView_OnItemChanged(HWND hwndDlg, NMLISTVIEW *pnmv)
{
if (pnmv->uNewState & LVIS_SELECTED)
{
//if (GetFocus()==resultlist.getwnd())
{
m_last_selitem = pnmv->iItem;
KillTimer(hwndDlg, 6600);
SetTimer(hwndDlg, 6600, 250, NULL);
}
}
else
{
if (isMixablePresent)
{
SetDlgItemText(hwndDlg, IDC_MIXABLE, L"");
isMixablePresent = false;
}
}
return FALSE;
}
static BOOL ListView_OnDoubleClick(HWND hwndDlg, NMITEMACTIVATE *pnmitem)
{
playFiles((!!g_config->ReadInt(L"enqueuedef", 0)) ^(!!(GetAsyncKeyState(VK_SHIFT)&0x8000)), 0);
return FALSE;
}
void EatKeyboard()
{
Sleep(100);
MSG msg;
while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
}
static void Dialog_OnContextMenu(HWND hwndDlg, HWND hwndFrom, int x, int y)
{
if (hwndFrom != resultlist.getwnd())
return;
POINT pt = {x,y};
if (x == -1 || y == -1) // x and y are -1 if the user invoked a shift-f10 popup menu
{
RECT itemRect = {0};
int selected = resultlist.GetNextSelected();
if (selected != -1) // if something is selected we'll drop the menu from there
{
resultlist.GetItemRect(selected, &itemRect);
ClientToScreen(resultlist.getwnd(), (POINT *)&itemRect);
}
else // otherwise we'll drop it from the top-left corner of the listview, adjusting for the header location
{
GetWindowRect(resultlist.getwnd(), &itemRect);
HWND hHeader = (HWND)SNDMSG(resultlist.getwnd(), LVM_GETHEADER, 0, 0L);
RECT headerRect;
if ((WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) && GetWindowRect(hHeader, &headerRect))
{
itemRect.top += (headerRect.bottom - headerRect.top);
}
}
x = itemRect.left;
y = itemRect.top;
}
HWND hHeader = (HWND)SNDMSG(resultlist.getwnd(), LVM_GETHEADER, 0, 0L);
RECT headerRect;
if (0 == (WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) || FALSE == GetWindowRect(hHeader, &headerRect))
{
SetRectEmpty(&headerRect);
}
if (FALSE != PtInRect(&headerRect, pt))
{
return;
}
HMENU globmenu = WASABI_API_LOADMENU(IDR_CONTEXTMENUS);
HMENU menu = GetSubMenu(globmenu, 0);
int rate_idx = 9;
sendto_hmenu = GetSubMenu(menu, 2);
rate_hmenu = GetSubMenu(menu, rate_idx);
ConvertRatingMenuStar(rate_hmenu, ID_RATE_5);
ConvertRatingMenuStar(rate_hmenu, ID_RATE_4);
ConvertRatingMenuStar(rate_hmenu, ID_RATE_3);
ConvertRatingMenuStar(rate_hmenu, ID_RATE_2);
ConvertRatingMenuStar(rate_hmenu, ID_RATE_1);
s.mode = 0;
s.hwnd = 0;
s.build_hMenu = 0;
IPC_LIBRARY_SENDTOMENU = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE);
if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)0, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
{
s.mode = 1;
s.hwnd = hwndDlg;
s.data_type = ML_TYPE_ITEMRECORDLIST;
s.ctx[1] = 1;
s.build_hMenu = sendto_hmenu;
}
wchar_t *artist = NULL;
wchar_t *album = NULL;
int n = resultlist.GetSelectionMark();
if (n != -1)
{
artist = itemCache.Items[n].artist;
album = itemCache.Items[n].album;
wchar_t str[2048] = {0}, str2[128] = {0};
// (BigG): Check the ini settings for viewing vs playing and create the menu accordingly
// Keeping this here in case we want to use the ini setting:
//StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW( (g_viewnotplay != 0) ? IDS_VIEW_ALL_FILES_BY : IDS_PLAY_ALL_FILES_BY), artist ? artist : L"");
int len = lstrlenW(artist);
if (len > 39)
{
StringCchPrintfW(str2, 40, L"%.36s...", artist);//WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_BY), artist ? artist : L"");
StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_BY), str2);
}
else
{
StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_BY), artist ? artist : L"");
}
FixAmps(str, 2048);
MENUITEMINFOW mii =
{
sizeof(MENUITEMINFOW),
MIIM_TYPE | MIIM_ID,
MFT_STRING,
MFS_ENABLED,
0x1234,
NULL,
NULL,
NULL,
0,
str,
0,
};
if (!(!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
!SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE)))
{
MENUITEMINFOW m = {sizeof(m), MIIM_TYPE | MIIM_ID | MIIM_SUBMENU, MFT_SEPARATOR, 0};
m.wID = CLOUD_SOURCE_MENUS - 1;
InsertMenuItemW(menu, 0, FALSE, &m);
wchar_t a[100] = {0};
m.fType = MFT_STRING;
m.dwTypeData = WASABI_API_LNGSTRINGW_BUF(IDS_CLOUD_SOURCES, a, 100);
m.wID = CLOUD_SOURCE_MENUS;
m.hSubMenu = cloud_hmenu = CreatePopupMenu();
InsertMenuItemW(menu, 0, FALSE, &m);
}
if (artist && artist[0])
{
InsertMenuItemW(menu, ID_EDITITEMINFOS, FALSE, &mii);
}
// (BigG): Check the ini settings for viewing vs playing and create the menu accordingly
// Keeping this here in case we want to use the ini setting:
len = lstrlenW(album);
if (len > 39)
{
StringCchPrintfW(str2, 40, L"%.36s...", album);
StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_FROM), str2);
}
else
{
StringCchPrintfW(str, 2048, WASABI_API_LNGSTRINGW(IDS_VIEW_ALL_FILES_FROM), album ? album : L"");
}
FixAmps(str, 2048);
mii.cch = wcslen(str);
mii.wID = 0x1235;
if (album && album[0])
{
InsertMenuItemW(menu, ID_EDITITEMINFOS, FALSE, &mii);
}
{
mii.wID = 0xdeadbeef;
mii.fType = MFT_SEPARATOR;
InsertMenuItemW(menu, ID_EDITITEMINFOS, FALSE, &mii);
}
}
else
{
EnableMenuItem(menu, ID_MEDIAWND_PLAYSELECTEDFILES, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(menu, ID_MEDIAWND_ENQUEUESELECTEDFILES, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(menu, IDC_REFRESH_METADATA, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(menu, ID_MEDIAWND_REMOVEFROMLIBRARY, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(menu, ID_EDITITEMINFOS, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(menu, ID_MEDIAWND_EXPLOREFOLDER, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(menu, ID_PE_ID3, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(menu, 2, MF_BYPOSITION | MF_GRAYED); //grays the "Add to playlist..." menu
EnableMenuItem(menu, rate_idx, MF_BYPOSITION | MF_GRAYED); //grays the "Rate..." menu
EnableMenuItem(menu, 13, MF_BYPOSITION | MF_GRAYED); //grays the "Remove..." menu
}
menufucker_t mf = {sizeof(mf),MENU_MEDIAVIEW,menu,0x3000,0x4000,0};
mf.extinf.mediaview.list = resultlist.getwnd();
mf.extinf.mediaview.items = &itemCache;
pluginMessage message_build = {ML_IPC_MENUFUCKER_BUILD,(intptr_t)&mf,0};
SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&message_build, ML_IPC_SEND_PLUGIN_MESSAGE);
Menu_SetRatingValue(rate_hmenu, 0);
UpdateMenuItems(hwndDlg, menu, IDR_VIEW_ACCELERATORS);
int r = DoTrackPopup(menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, x, y, hwndDlg, NULL);
pluginMessage message_result = {ML_IPC_MENUFUCKER_RESULT,(intptr_t)&mf,r,0};
SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&message_result, ML_IPC_SEND_PLUGIN_MESSAGE);
switch (r)
{
case ID_MEDIAWND_PLAYSELECTEDFILES:
case ID_MEDIAWND_ENQUEUESELECTEDFILES:
playFiles((r == ID_MEDIAWND_ENQUEUESELECTEDFILES), 0);
break;
case ID_MEDIAWND_SELECTALL:
{
LVITEM item = {0};
item.state = LVIS_SELECTED;
item.stateMask = LVIS_SELECTED;
SendMessageW(hwndFrom, LVM_SETITEMSTATE, (WPARAM)-1, (LPARAM)&item);
}
break;
case ID_MEDIAWND_REMOVEFROMLIBRARY:
removeSelectedItems(0);
break;
case ID_EDITITEMINFOS:
if (resultlist.GetSelectedCount() > 0)
editInfo(hwndDlg);
break;
case ID_PE_ID3:
fileInfoDialogs(hwndDlg);
PostMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)hwndFrom, (LPARAM)TRUE);
break;
case IDC_REFRESH_METADATA:
RefreshMetadata(hwndDlg);
break;
case 0x1234: // all files from selected artist
{
wchar_t tmp[2048] = {0};
GayStringW escaped;
queryStrEscape(artist, escaped);
// Keeping this here in case we want to use the ini setting later:
//if ( g_viewnotplay ) // (BigG): Check to see if we should play or view the current tracks
//{
StringCchPrintfW(tmp, 2048, L"?artist = \"%s\"", escaped.Get());
SetWindowTextW(hwndSearchGlobal, tmp);
//}
//else // Otherwise do the old behavior and just play it back
//{
// StringCchPrintfW(tmp, 2048, L"artist = \"%s\"", escaped.Get());
// main_playQuery(g_view_metaconf, tmp, 0);
//}
SetWindowTextW(hwndSearchGlobal, tmp);
}
break;
case 0x1235: // all files from selected album
{
wchar_t tmp[2048] = {0};
GayStringW escaped;
queryStrEscape(album, escaped);
// Keeping this here in case we want to use the ini setting later:
//if ( g_viewnotplay ) // (BigG): Check to see if we should play or view the current tracks
//{
StringCchPrintfW(tmp, 2048, L"?album = \"%s\"", escaped.Get());
SetWindowTextW(hwndSearchGlobal, tmp);
//}
//else // Otherwise do the old behavior and just play it back
//{
// StringCchPrintfW(tmp, 2048, L"album = \"%s\"", escaped.Get());
// main_playQuery(g_view_metaconf, tmp, 0);
//}
}
break;
case ID_RATE_1:
case ID_RATE_2:
case ID_RATE_3:
case ID_RATE_4:
case ID_RATE_5:
case ID_RATE_0:
{
int rate = r - ID_RATE_1 + 1;
if (r == ID_RATE_0) rate = 0;
int x;
int has = 0;
EnterCriticalSection(&g_db_cs);
nde_scanner_t s = NDE_Table_CreateScanner(g_table);
for (x = 0; x < itemCache.Size; x ++)
{
if (resultlist.GetSelected(x))
{
if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, itemCache.Items[x].filename))
{
has++;
NDE_Scanner_Edit(s);
db_setFieldInt(s, MAINTABLE_ID_RATING, rate);
NDE_Scanner_Post(s);
itemCache.Items[x].rating = rate;
if (g_config->ReadInt(L"writeratings", 0))
{
wchar_t buf[64] = {0};
if (rate > 0)
{
wsprintfW(buf, L"%d", rate);
}
else
buf[0] = 0;
updateFileInfo(itemCache.Items[x].filename, DB_FIELDNAME_rating, buf);
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_WRITE_EXTENDED_FILE_INFO);
}
}
}
}
NDE_Table_DestroyScanner(g_table, s);
if (g_table_dirty)
{
g_table_dirty = 0;
NDE_Table_Sync(g_table);
}
LeaveCriticalSection(&g_db_cs);
if (has)
{
ListView_RedrawItems(resultlist.getwnd(), 0, itemCache.Size - 1);
PostMessage(GetParent(hwndDlg), WM_APP + 4, (WPARAM)rate, (LPARAM)0);
}
}
break;
case ID_MEDIAWND_EXPLOREFOLDER:
exploreItemFolder(hwndDlg);
break;
case ID_MEDIAWND_REMOVE_REMOVEALLDEADFILES:
removeDeadFiles(hwndDlg);
break;
case ID_MEDIAWND_REMOVE_PHYSICALLYREMOVESELECTEDITEMS:
RecycleSelectedItems();
break;
default:
{
if (cloud_hmenu && (r >= CLOUD_SOURCE_MENUS && r < CLOUD_SOURCE_MENUS_UPPER)) // deals with cloud specific menus
{
// 0 = no change
// 1 = add cloud
// 2 = add local
// 4 = removed
int mode = 0;
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_PROCESS_CLOUD_STATUS, (intptr_t)r, (intptr_t)&mode);
int n = resultlist.GetSelectionMark();
if (n != -1)
{
switch (mode)
{
case 1:
setCloudValue(&itemCache.Items[n], L"5");
break;
case 2:
setCloudValue(&itemCache.Items[n], L"0");
break;
case 4:
setCloudValue(&itemCache.Items[n], L"4");
break;
}
InvalidateRect(resultlist.getwnd(), NULL, TRUE);
}
break;
}
if (s.mode == 2)
{
s.menu_id = r;
if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
{
// build my data.
s.mode = 3;
s.data_type = ML_TYPE_ITEMRECORDLISTW;
itemRecordListW myObj = {0, };
copyFilesToItemCacheW(&myObj); // does not dupe strings
s.data = (void*) & myObj;
LRESULT result = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM) & s, IPC_LIBRARY_SENDTOMENU);
if (result != 1)
{
s.mode = 3;
s.data_type = ML_TYPE_ITEMRECORDLIST;
itemRecordList objA = {0, };
convertRecordList(&objA, &myObj);
s.data = (void*) & objA;
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU);
freeRecordList(&objA);
}
_aligned_free(myObj.Items);
}
}
break;
}
}
if (s.mode)
{
s.mode = 4;
SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU); // cleanup
}
sendto_hmenu = 0;
DestroyMenu(cloud_hmenu);
cloud_hmenu = 0;
DestroyMenu(globmenu);
UpdateWindow(hwndFrom);
EatKeyboard();
}
static BOOL ListView_OnFindItem(HWND hwndDlg, NMLVFINDITEMW *pfi, LRESULT *pResult, UINT uMsg)
{
if (bgThread_Handle) return FALSE;
int i = pfi->iStart;
if (i >= itemCache.Size) i = 0;
int cnt = itemCache.Size - i;
if (pfi->lvfi.flags & LVFI_WRAP) cnt += i;
int by = g_view_metaconf->ReadInt(L"mv_sort_by", MEDIAVIEW_COL_ARTIST);
while (cnt-- > 0)
{
itemRecordW *thisitem = itemCache.Items + i;
wchar_t tmp[128] = {0};
wchar_t *name = 0;
switch (by)
{
case MEDIAVIEW_COL_FILENAME:
name = thisitem->filename + wcslen(thisitem->filename);
while (name >= thisitem->filename && *name != L'/' && *name != L'\\') name--;
break;
case MEDIAVIEW_COL_FULLPATH:
name = thisitem->filename;
break;
case MEDIAVIEW_COL_EXTENSION:
name = PathFindExtensionW(thisitem->filename);
if (name && *name)
name++;
break;
case MEDIAVIEW_COL_TITLE: name = thisitem->title; break;
case MEDIAVIEW_COL_COMMENT: name = thisitem->comment; break;
case MEDIAVIEW_COL_ARTIST: name = thisitem->artist; break;
case MEDIAVIEW_COL_ALBUM: name = thisitem->album; break;
case MEDIAVIEW_COL_GENRE: name = thisitem->genre; break;
case MEDIAVIEW_COL_YEAR:
tmp[0] = 0;
if (thisitem->year >= 0) StringCchPrintfW(tmp, 128, L"%04d", thisitem->year);
name = tmp;
break;
case MEDIAVIEW_COL_TRACK:
tmp[0] = 0;
if (thisitem->track > 0)
{
if (thisitem->tracks > 0) StringCchPrintfW(tmp, 128, L"%d/%d", thisitem->track, thisitem->tracks);
else StringCchPrintfW(tmp, 128, L"%d", thisitem->track);
}
name = tmp;
break;
case MEDIAVIEW_COL_LENGTH:
tmp[0] = 0;
if (thisitem->length >= 0) StringCchPrintfW(tmp, 128, L"%d:%02d", thisitem->length / 60, thisitem->length % 60);
name = tmp;
break;
case MEDIAVIEW_COL_RATING:
tmp[0] = 0;
if (thisitem->rating > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->rating);
name = tmp;
break;
case MEDIAVIEW_COL_CLOUD:
{
tmp[0] = 0;
wchar_t *x = getRecordExtendedItem_fast(thisitem, extended_fields.cloud);
if (x && *x) StringCchPrintfW(tmp, 128, L"%d", x);
name = tmp;
break;
}
case MEDIAVIEW_COL_PLAYCOUNT:
tmp[0] = 0;
if (thisitem->playcount > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->playcount);
name = tmp;
break;
case MEDIAVIEW_COL_BITRATE:
if (thisitem->bitrate > 0)
StringCchPrintfW(name = tmp, 128, L"%d%s", thisitem->bitrate, WASABI_API_LNGSTRINGW(IDS_KBPS));
break;
case MEDIAVIEW_COL_BPM:
if (thisitem->bpm > 0)
StringCchPrintfW(name = tmp, 128, L"%d", thisitem->bpm);
break;
case MEDIAVIEW_COL_TYPE:
name = WASABI_API_LNGSTRINGW(thisitem->type ? IDS_VIDEO : IDS_AUDIO);
break;
case MEDIAVIEW_COL_DISC:
tmp[0] = 0;
if (thisitem->disc > 0)
{
if (thisitem->discs > 0)
StringCchPrintfW(tmp, 128, L"%d/%d", thisitem->disc, thisitem->discs);
else
StringCchPrintfW(tmp, 128, L"%d", thisitem->disc);
}
name = tmp;
break;
case MEDIAVIEW_COL_ALBUMARTIST:
name = thisitem->albumartist;
break;
case MEDIAVIEW_COL_PUBLISHER:
name = thisitem->publisher;
break;
case MEDIAVIEW_COL_COMPOSER:
name = thisitem->composer;
break;
case MEDIAVIEW_COL_ALBUMGAIN:
name = thisitem->replaygain_album_gain;
break;
case MEDIAVIEW_COL_TRACKGAIN:
name = thisitem->replaygain_track_gain;
break;
case MEDIAVIEW_COL_FILESIZE:
tmp[0] = 0;
if (thisitem->filesize > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->filesize);
name = tmp;
break;
case MEDIAVIEW_COL_FILETIME:
tmp[0] = 0;
if (thisitem->filetime > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->filetime);
name = tmp;
break;
case MEDIAVIEW_COL_LASTUPD:
tmp[0] = 0;
if (thisitem->lastupd > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->lastupd);
name = tmp;
break;
case MEDIAVIEW_COL_DATEADDED:
{
tmp[0] = 0;
wchar_t *x = getRecordExtendedItem_fast(thisitem,extended_fields.dateadded);
if (x && *x) StringCchPrintfW(tmp, 128, L"%d", x);
name = tmp;
break;
}
case MEDIAVIEW_COL_LASTPLAY:
tmp[0] = 0;
if (thisitem->lastplay > 0) StringCchPrintfW(tmp, 128, L"%d", thisitem->lastplay);
name = tmp;
break;
case MEDIAVIEW_COL_ISPODCAST:
{
wchar_t * t = getRecordExtendedItem_fast(thisitem, extended_fields.ispodcast);
name = WASABI_API_LNGSTRINGW(t && wcscmp(t,L"1") ? IDS_PODCAST : IDS_NON_PODCAST);
}
break;
case MEDIAVIEW_COL_PODCASTCHANNEL:
name = getRecordExtendedItem_fast(thisitem,extended_fields.podcastchannel);
break;
case MEDIAVIEW_COL_PODCASTPUBDATE:
{
tmp[0] = 0;
wchar_t *x = getRecordExtendedItem_fast(thisitem,extended_fields.podcastpubdate);
if (x && *x) StringCchPrintfW(tmp, 128, L"%d", x);
name = tmp;
}
break;
case MEDIAVIEW_COL_CATEGORY:
name = thisitem->category;
break;
case MEDIAVIEW_COL_DIRECTOR:
name = getRecordExtendedItem_fast(thisitem,extended_fields.director);
break;
case MEDIAVIEW_COL_PRODUCER:
name = getRecordExtendedItem_fast(thisitem,extended_fields.producer);
break;
case MEDIAVIEW_COL_DIMENSION:
{
tmp[0] = 0;
wchar_t *w = getRecordExtendedItem_fast(thisitem,extended_fields.width);
wchar_t *h = getRecordExtendedItem_fast(thisitem,extended_fields.height);
if (w && *w && h && *h) StringCchPrintfW(tmp, 128, L"%dx%d", _wtoi(w), _wtoi(h));
name = tmp;
break;
}
}
if (!name) name = L"";
else SKIP_THE_AND_WHITESPACEW(name)
if (pfi->lvfi.flags & (4 | LVFI_PARTIAL))
{
if (!StrCmpNIW(name, pfi->lvfi.psz, wcslen(pfi->lvfi.psz)))
{
*pResult = i;
return TRUE;
}
}
else if (pfi->lvfi.flags & LVFI_STRING)
{
if (!lstrcmpiW(name, pfi->lvfi.psz))
{
*pResult = i;
return TRUE;
}
}
else
{
*pResult = i;
return TRUE;
}
if (++i == itemCache.Size) i = 0;
}
*pResult = i;
return TRUE;
}
static BOOL ListView_OnGetDispInfo(HWND hwndDlg, NMLVDISPINFOW* pdi, UINT uMsg)
{
LVITEMW *pItem;
itemRecordW *pRec;
pItem = &pdi->item;
if (bgThread_Handle)
{
if (0 == pItem->iItem && 0 == pItem->iSubItem && (LVIF_TEXT & pItem->mask))
{
static char bufpos = 0;
static int buflen = 17;
static wchar_t buffer[64];//L"Scanning _\0/-\\|";
if (!buffer[0])
{
WASABI_API_LNGSTRINGW_BUF(IDS_SCANNING_PLAIN,buffer,54);
StringCchCatW(buffer,64,L" _");
StringCchCatW(buffer+(buflen=lstrlenW(buffer)+1),64,L"/-\\|");
buflen+=4;
}
int pos = buflen - 5;;
buffer[pos - 1] = buffer[pos + (bufpos++&3) + 1];
pItem->pszText = buffer;
}
return FALSE;
}
if (pItem->iItem < 0 || pItem->iItem >= itemCache.Size) return FALSE;
pRec = itemCache.Items + pItem->iItem;
if (LVIF_TEXT & pItem->mask)
{
switch (columnOrder[pItem->iSubItem])
{
case MEDIAVIEW_COL_FILENAME: // show filename (Tho we'll show without the path cause paths are gay)
pItem->pszText = PathFindFileNameW(pRec->filename);
break;
case MEDIAVIEW_COL_FULLPATH: // show filename (Tho we'll show without the path cause paths are gay)
pItem->pszText = pRec->filename;
break;
case MEDIAVIEW_COL_EXTENSION:
pItem->pszText = PathFindExtensionW(pRec->filename);
if (pItem->pszText && *pItem->pszText) pItem->pszText++;
break;
case MEDIAVIEW_COL_TITLE:
pItem->pszText = pRec->title;
break;
case MEDIAVIEW_COL_COMMENT:
pItem->pszText = pRec->comment;
break;
case MEDIAVIEW_COL_ALBUM:
pItem->pszText = pRec->album;
break;
case MEDIAVIEW_COL_ARTIST:
pItem->pszText = pRec->artist;
break;
case MEDIAVIEW_COL_TRACK:
if (pRec->track > 0)
{
if (pRec->tracks > 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d/%d", pRec->track, pRec->tracks);
else StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", pRec->track);
}
break;
case MEDIAVIEW_COL_GENRE:
pItem->pszText = pRec->genre;
break;
case MEDIAVIEW_COL_YEAR:
if (pRec->year >= 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%04d", pRec->year);
break;
case MEDIAVIEW_COL_LENGTH:
if (pRec->length >= 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d:%02d", pRec->length / 60, pRec->length % 60);
break;
case MEDIAVIEW_COL_RATING:
if (0 == pItem->iSubItem && ratingBackText[0]) pItem->pszText = ratingBackText;
else if (pRec->rating > 0 && pRec->rating <= 5) _itow(pRec->rating, pItem->pszText, 10);
break;
case MEDIAVIEW_COL_CLOUD:
{
wchar_t *t = getRecordExtendedItem_fast(pRec, extended_fields.cloud);
if (t && *t) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", _wtoi(t));
break;
}
case MEDIAVIEW_COL_PLAYCOUNT:
if (pRec->playcount >= 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", pRec->playcount);
else pItem->pszText = L"0";
break;
case MEDIAVIEW_COL_DISC:
if (pRec->disc > 0)
{
if (pRec->discs > 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d/%d", pRec->disc, pRec->discs);
else StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", pRec->disc);
}
break;
case MEDIAVIEW_COL_ALBUMARTIST:
pItem->pszText = pRec->albumartist;
break;
case MEDIAVIEW_COL_PUBLISHER:
pItem->pszText = pRec->publisher;
break;
case MEDIAVIEW_COL_COMPOSER:
pItem->pszText = pRec->composer;
break;
case MEDIAVIEW_COL_ALBUMGAIN:
pItem->pszText = pRec->replaygain_album_gain;
break;
case MEDIAVIEW_COL_TRACKGAIN:
pItem->pszText = pRec->replaygain_track_gain;
break;
case MEDIAVIEW_COL_TYPE:
pItem->pszText = WASABI_API_LNGSTRINGW((pRec->type) ? IDS_VIDEO : IDS_AUDIO);
break;
case MEDIAVIEW_COL_BITRATE:
if (pRec->bitrate > 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d%s", pRec->bitrate, WASABI_API_LNGSTRINGW(IDS_KBPS));
break;
case MEDIAVIEW_COL_BPM:
if (pRec->bpm > 0) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%d", pRec->bpm);
break;
case MEDIAVIEW_COL_FILESIZE:
if (pRec->filesize != -1) WASABI_API_LNG->FormattedSizeString(pItem->pszText, pItem->cchTextMax, pRec->filesize);
break;
case MEDIAVIEW_COL_FILETIME:
MakeDateString(pRec->filetime, pItem->pszText, pItem->cchTextMax);
break;
case MEDIAVIEW_COL_LASTUPD:
MakeDateString(pRec->lastupd, pItem->pszText, pItem->cchTextMax);
break;
case MEDIAVIEW_COL_DATEADDED:
{
wchar_t * t = getRecordExtendedItem_fast(pRec,extended_fields.dateadded);
if (t && *t) MakeDateString(_wtoi64(t), pItem->pszText, pItem->cchTextMax);
break;
}
case MEDIAVIEW_COL_LASTPLAY:
if (pRec->lastplay > 0) MakeDateString(pRec->lastplay, pItem->pszText, pItem->cchTextMax);
break;
case MEDIAVIEW_COL_ISPODCAST:
{
wchar_t *t = getRecordExtendedItem_fast(pRec, extended_fields.ispodcast);
pItem->pszText = WASABI_API_LNGSTRINGW((t && t[0] == L'1' && !t[1]) ? IDS_PODCAST : IDS_NON_PODCAST);
break;
}
case MEDIAVIEW_COL_PODCASTCHANNEL:
pItem->pszText = getRecordExtendedItem_fast(pRec, extended_fields.podcastchannel);
break;
case MEDIAVIEW_COL_PODCASTPUBDATE:
{
wchar_t * t = getRecordExtendedItem_fast(pRec,extended_fields.podcastpubdate);
if (t && *t) MakeDateString(_wtoi64(t), pItem->pszText, pItem->cchTextMax);
break;
}
case MEDIAVIEW_COL_CATEGORY:
pItem->pszText = pRec->category;
break;
case MEDIAVIEW_COL_DIRECTOR:
pItem->pszText = getRecordExtendedItem_fast(pRec, extended_fields.director);
break;
case MEDIAVIEW_COL_PRODUCER:
pItem->pszText = getRecordExtendedItem_fast(pRec, extended_fields.producer);
break;
case MEDIAVIEW_COL_DIMENSION:
{
wchar_t *w = getRecordExtendedItem_fast(pRec,extended_fields.width);
wchar_t *h = getRecordExtendedItem_fast(pRec,extended_fields.height);
if (w && *w && h && *h) StringCchPrintfW(pItem->pszText, pItem->cchTextMax, L"%dx%d", _wtoi(w), _wtoi(h));
break;
}
}
if (!pItem->pszText) pItem->pszText = L"";
}
return FALSE;
}
static BOOL ListView_OnColumnClick(HWND hwndDlg, NMLISTVIEW *pnmv)
{
int l_sc = g_view_metaconf->ReadInt(L"mv_sort_by", MEDIAVIEW_COL_ARTIST);
int l_sd = g_view_metaconf->ReadInt(L"mv_sort_dir", 0);
if (columnOrder[pnmv->iSubItem] == l_sc) l_sd = !l_sd;
else
{
/* JF> I like this better when the direction doesnt get reset every
time you choose a new column. To revert to old behavior, uncomment
this line:
l_sd=0;
*/
l_sc = columnOrder[pnmv->iSubItem];
}
g_view_metaconf->WriteInt(L"mv_sort_by", l_sc);
g_view_metaconf->WriteInt(L"mv_sort_dir", l_sd);
MLSkinnedListView_DisplaySort(pnmv->hdr.hwndFrom, pnmv->iSubItem, !l_sd);
sortResults(g_view_metaconf, &itemCache);
resultlist.SetVirtualCount(0);
resultlist.SetVirtualCount(itemCache.Size); // TODO: we could set a limit here
ListView_RedrawItems(pnmv->hdr.hwndFrom, 0, itemCache.Size - 1);
return FALSE;
}
static BOOL ListView_OnBeginDrag(HWND hwndDlg, NMLISTVIEW *pnmv)
{
switch (columnOrder[pnmv->iSubItem])
{
case MEDIAVIEW_COL_RATING:
ratingColumn.hwndList = pnmv->hdr.hwndFrom;
ratingColumn.fStyle = RCS_DEFAULT;
ratingColumn.iItem = pnmv->iItem;
ratingColumn.iSubItem = pnmv->iSubItem;
ratingColumn.value = ((UINT)pnmv->iItem < (UINT)itemCache.Size) ? itemCache.Items[pnmv->iItem].rating : 0;
MLRatingColumn_BeginDrag(plugin.hwndLibraryParent, &ratingColumn);
break;
}
SetCapture(hwndDlg);
return FALSE;
}
static BOOL ListView_OnReturn(HWND hwndDlg, NMHDR *pnmh)
{
SendMessage(hwndDlg, WM_COMMAND, ((!!(GetAsyncKeyState(VK_SHIFT)&0x8000)) ^(!!g_config->ReadInt(L"enqueuedef", 0)))
? IDC_BUTTON_ENQUEUE : IDC_BUTTON_PLAY, 0);
return FALSE;
}
static BOOL ListView_OnCustomDraw(HWND hwndDlg, NMLVCUSTOMDRAW *plvcd, LRESULT *pResult)
{
static BOOL bDrawFocus;
static RECT rcView;
static RATINGCOLUMNPAINT ratingColumnPaint;
static CLOUDCOLUMNPAINT cloudColumnPaint;
*pResult = CDRF_DODEFAULT;
switch (plvcd->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult |= CDRF_NOTIFYITEMDRAW;
CopyRect(&rcView, &plvcd->nmcd.rc);
ratingColumnPaint.fStyle = RCS_DEFAULT;
ratingColumnPaint.hwndList = plvcd->nmcd.hdr.hwndFrom;
ratingColumnPaint.hdc = plvcd->nmcd.hdc;
ratingColumnPaint.prcView = &rcView;
cloudColumnPaint.hwndList = plvcd->nmcd.hdr.hwndFrom;
cloudColumnPaint.hdc = plvcd->nmcd.hdc;
cloudColumnPaint.prcView = &rcView;
return TRUE;
case CDDS_ITEMPREPAINT:
*pResult |= CDRF_NOTIFYSUBITEMDRAW;
bDrawFocus = (CDIS_FOCUS & plvcd->nmcd.uItemState);
if (bDrawFocus)
{
plvcd->nmcd.uItemState &= ~CDIS_FOCUS;
*pResult |= CDRF_NOTIFYPOSTPAINT;
}
return TRUE;
case CDDS_ITEMPOSTPAINT:
if (bDrawFocus)
{
RECT rc;
rc.left = LVIR_BOUNDS;
SendMessageW(plvcd->nmcd.hdr.hwndFrom, LVM_GETITEMRECT, plvcd->nmcd.dwItemSpec, (LPARAM)&rc);
rc.left += 3;
DrawFocusRect(plvcd->nmcd.hdc, &rc);
plvcd->nmcd.uItemState |= CDIS_FOCUS;
bDrawFocus = FALSE;
}
*pResult = CDRF_SKIPDEFAULT;
return TRUE;
case(CDDS_SUBITEM | CDDS_ITEMPREPAINT):
switch (columnOrder[plvcd->iSubItem])
{
case MEDIAVIEW_COL_RATING:
if (bgThread_Handle || (0 == plvcd->iSubItem && 0 == plvcd->nmcd.rc.right)) break;
ratingColumnPaint.iItem = plvcd->nmcd.dwItemSpec;
ratingColumnPaint.iSubItem = plvcd->iSubItem;
ratingColumnPaint.value = (plvcd->nmcd.dwItemSpec >= 0 && plvcd->nmcd.dwItemSpec < (UINT)itemCache.Size) ? itemCache.Items[plvcd->nmcd.dwItemSpec].rating : 0;
ratingColumnPaint.prcItem = &plvcd->nmcd.rc;
ratingColumnPaint.rgbBk = plvcd->clrTextBk;
ratingColumnPaint.rgbFg = plvcd->clrText;
if (MLRatingColumn_Paint(plugin.hwndLibraryParent, &ratingColumnPaint))
{
*pResult = CDRF_SKIPDEFAULT;
return TRUE;
}
break;
case MEDIAVIEW_COL_CLOUD:
if (bgThread_Handle || (0 == plvcd->iSubItem && 0 == plvcd->nmcd.rc.right)) break;
int icon = 4;
wchar_t *t = getRecordExtendedItem_fast(&itemCache.Items[plvcd->nmcd.dwItemSpec], extended_fields.cloud);
if (t && *t) icon = _wtoi(t);
cloudColumnPaint.iItem = plvcd->nmcd.dwItemSpec;
cloudColumnPaint.iSubItem = plvcd->iSubItem;
cloudColumnPaint.value = icon;
cloudColumnPaint.prcItem = &plvcd->nmcd.rc;
cloudColumnPaint.rgbBk = plvcd->clrTextBk;
cloudColumnPaint.rgbFg = plvcd->clrText;
if (MLCloudColumn_Paint(plugin.hwndLibraryParent, &cloudColumnPaint))
{
*pResult = CDRF_SKIPDEFAULT;
return TRUE;
}
break;
}
break;
}
return FALSE;
}
static BOOL ListView_OnClick(HWND hwnDlg, NMITEMACTIVATE *pnmitem)
{
if (bgThread_Handle) return FALSE;
if (pnmitem->iItem != -1)
{
switch (columnOrder[pnmitem->iSubItem])
{
case MEDIAVIEW_COL_RATING:
ratingColumn.hwndList = pnmitem->hdr.hwndFrom;
ratingColumn.ptAction = pnmitem->ptAction;
ratingColumn.bRedrawNow = TRUE;
ratingColumn.fStyle = RCS_DEFAULT;
if (!MLRatingColumn_Click(plugin.hwndLibraryParent, &ratingColumn)) return FALSE;
SetRating(ratingColumn.iItem, ratingColumn.value, ratingColumn.hwndList);
break;
case MEDIAVIEW_COL_CLOUD:
{
RECT itemRect = {0};
if (pnmitem->iSubItem)
ListView_GetSubItemRect(pnmitem->hdr.hwndFrom, pnmitem->iItem, pnmitem->iSubItem, LVIR_BOUNDS, &itemRect);
else
{
ListView_GetItemRect(pnmitem->hdr.hwndFrom, pnmitem->iItem, &itemRect, LVIR_BOUNDS);
itemRect.right = itemRect.left + ListView_GetColumnWidth(pnmitem->hdr.hwndFrom, pnmitem->iSubItem);
}
MapWindowPoints(pnmitem->hdr.hwndFrom, HWND_DESKTOP, (POINT*)&itemRect, 2);
HMENU cloud_menu = CreatePopupMenu();
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_GET_CLOUD_STATUS, (intptr_t)itemCache.Items[pnmitem->iItem].filename, (intptr_t)&cloud_menu);
if (cloud_menu)
{
int r = DoTrackPopup(cloud_menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, itemRect.right, itemRect.top, pnmitem->hdr.hwndFrom, NULL);
if (r >= CLOUD_SOURCE_MENUS && r < CLOUD_SOURCE_MENUS_UPPER)
{
// 0 = no change
// 1 = adding to cloud
// 2 = added locally
// 4 = removed
int mode = 0; // deals with cloud specific menus
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_PROCESS_CLOUD_STATUS, (intptr_t)r, (intptr_t)&mode);
switch (mode)
{
case 1:
setCloudValue(&itemCache.Items[pnmitem->iItem], L"5");
break;
case 2:
setCloudValue(&itemCache.Items[pnmitem->iItem], L"4");
break;
case 4:
setCloudValue(&itemCache.Items[pnmitem->iItem], L"4");
break;
}
InvalidateRect(resultlist.getwnd(), NULL, TRUE);
}
DestroyMenu(cloud_menu);
}
}
break;
}
}
return FALSE;
}
static BOOL ListView_OnHotTrack(HWND hwndDlg, NMLISTVIEW *pnmlv, LRESULT *pResult)
{
UINT iItem;
if (bgThread_Handle)
{
pnmlv->iItem = -1;
*pResult = TRUE;
return TRUE;
}
if (-1 == pnmlv->iItem && 0 == pnmlv->iSubItem)
{
LVHITTESTINFO lvhit;
lvhit.pt = pnmlv->ptAction;
SendMessageW(pnmlv->hdr.hwndFrom, LVM_HITTEST, 0, (LPARAM)&lvhit);
iItem = lvhit.iItem;
}
else iItem = pnmlv->iItem;
switch (columnOrder[pnmlv->iSubItem])
{
case MEDIAVIEW_COL_RATING:
ratingColumn.hwndList = pnmlv->hdr.hwndFrom;
ratingColumn.fStyle = RCS_DEFAULT;
ratingColumn.iItem = iItem;
ratingColumn.iSubItem = pnmlv->iSubItem;
ratingColumn.value = (iItem < (UINT)itemCache.Size) ? itemCache.Items[iItem].rating : 0;
ratingColumn.ptAction = pnmlv->ptAction;
ratingColumn.bRedrawNow = TRUE;
MLRatingColumn_Track(plugin.hwndLibraryParent, &ratingColumn);
break;
}
// LVS_EX_ONECLICKACTIVATE enabled - make listview select nothing
pnmlv->iItem = -1;
*pResult = TRUE;
return TRUE;
}
/////////// Dialog Messages / Notifications
static void Dialog_OnDisplayChange(HWND hwndDlg)
{
INT i;
HWND hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
for (i = 0; -1 != columnOrder[i] && MEDIAVIEW_COL_RATING != columnOrder[i] && MEDIAVIEW_COL_CLOUD != columnOrder[i]; i++);
if (-1 != columnOrder[i])
{
if (hwndList)
{
INT w = (INT)SendMessageW(hwndList, LVM_GETCOLUMNWIDTH, i, 0L);
SendMessageW(hwndList, LVM_SETCOLUMNWIDTH, i, (LPARAM)w);
}
}
LayoutWindows(hwndDlg, TRUE);
}
static wchar_t tt_buf[256] = {L""};
static int last_item = -1, last_icon = -1;
LRESULT pmp_listview(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg == WM_NOTIFY)
{
LPNMHDR l=(LPNMHDR)lParam;
switch (l->code)
{
case TTN_SHOW:
{
LVHITTESTINFO lvh = {0};
GetCursorPos(&lvh.pt);
ScreenToClient(hwnd, &lvh.pt);
ListView_SubItemHitTest(hwnd, &lvh);
int cloudcol = (int)GetPropW(hwnd, L"pmp_list_info");
if (lvh.iItem != -1 && lvh.iSubItem == cloudcol)
{
RECT r = {0};
if (lvh.iSubItem)
ListView_GetSubItemRect(hwnd, lvh.iItem, lvh.iSubItem, LVIR_BOUNDS, &r);
else
{
ListView_GetItemRect(hwnd, lvh.iItem, &r, LVIR_BOUNDS);
r.right = r.left + ListView_GetColumnWidth(hwnd, cloudcol);
}
MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)&r, 2);
SetWindowPos(l->hwndFrom, HWND_TOPMOST, r.right, r.top + 2, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE);
return 1;
}
}
break;
case TTN_NEEDTEXTW:
{
LVHITTESTINFO lvh = {0};
GetCursorPos(&lvh.pt);
ScreenToClient(hwnd, &lvh.pt);
ListView_SubItemHitTest(hwnd, &lvh);
int cloudcol = (int)GetPropW(hwnd, L"pmp_list_info");
if (lvh.iItem != -1 && lvh.iSubItem == cloudcol)
{
LPNMTTDISPINFOW lpnmtdi = (LPNMTTDISPINFOW)lParam;
int icon = 4;
wchar_t *t = getRecordExtendedItem_fast(&itemCache.Items[lvh.iItem], extended_fields.cloud);
if (t && *t) icon = _wtoi(t);
if (last_item == lvh.iItem && last_icon == icon)
{
lpnmtdi->lpszText = tt_buf;
return 0;
}
if (icon == 4)
{
WASABI_API_LNGSTRINGW_BUF(IDS_UPLOAD_TO_SOURCE, tt_buf, ARRAYSIZE(tt_buf));
}
else if (icon == 5)
{
WASABI_API_LNGSTRINGW_BUF(IDS_UPLOADING_TO_SOURCE, tt_buf, ARRAYSIZE(tt_buf));
}
else
{
if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
if (cloud_hinst && cloud_hinst != (HINSTANCE)1)
{
winampMediaLibraryPlugin *(*gp)();
gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(cloud_hinst, "winampGetMediaLibraryPlugin");
if (gp)
{
winampMediaLibraryPlugin *mlplugin = gp();
if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
{
WASABI_API_LNGSTRINGW_BUF(IDS_TRACK_AVAILABLE, tt_buf, ARRAYSIZE(tt_buf));
nx_string_t *out_devicenames = 0;
size_t num_names = mlplugin->MessageProc(0x405, (INT_PTR)itemCache.Items[lvh.iItem].filename, (INT_PTR)&out_devicenames, 0);
if (num_names > 0)
{
for (size_t i = 0; i < num_names; i++)
{
if (i > 0) StringCchCatW(tt_buf, ARRAYSIZE(tt_buf), L", ");
StringCchCatW(tt_buf, ARRAYSIZE(tt_buf), out_devicenames[i]->string);
}
}
else
{
WASABI_API_LNGSTRINGW_BUF(IDS_UPLOAD_TO_SOURCE, tt_buf, ARRAYSIZE(tt_buf));
}
if (out_devicenames)
free(out_devicenames);
}
}
}
}
last_item = lvh.iItem;
last_icon = icon;
lpnmtdi->lpszText = tt_buf;
// bit of a fiddle but it allows for multi-line tooltips
//SendMessage(l->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 0);
}
else
return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"pmp_list_proc"), hwnd, uMsg, wParam, lParam);
}
return 0;
}
}
return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"pmp_list_proc"), hwnd, uMsg, wParam, lParam);
}
void Dialog_UpdateButtonText(HWND hwndDlg, int _enqueuedef)
{
if (groupBtn)
{
switch(_enqueuedef)
{
case 1:
SetDlgItemTextW(hwndDlg, IDC_BUTTON_PLAY, view.enqueue);
customAllowed = FALSE;
break;
default:
// v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay
// pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed
pluginMessage p = {ML_MSG_VIEW_BUTTON_HOOK_IN_USE, (INT_PTR)_enqueuedef, 0, 0};
wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p);
if (pszTextW && pszTextW[0] != 0)
{
// set this to be a bit different so we can just use one button and not the
// mixable one as well (leaving that to prevent messing with the resources)
SetDlgItemTextW(hwndDlg, IDC_BUTTON_PLAY, pszTextW);
customAllowed = TRUE;
}
else
{
SetDlgItemTextW(hwndDlg, IDC_BUTTON_PLAY, view.play);
customAllowed = FALSE;
}
break;
}
}
}
enum
{
BPM_ECHO_WM_COMMAND=0x1, // send WM_COMMAND and return value
BPM_WM_COMMAND = 0x2, // just send WM_COMMAND
};
BOOL Dialog_ButtonPopupMenu(HWND hwndDlg, int buttonId, HMENU menu, int flags=0)
{
RECT r;
HWND buttonHWND = GetDlgItem(hwndDlg, buttonId);
GetWindowRect(buttonHWND, &r);
UpdateMenuItems(hwndDlg, menu, IDR_VIEW_ACCELERATORS);
MLSkinnedButton_SetDropDownState(buttonHWND, TRUE);
UINT tpmFlags = TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_LEFTALIGN;
if (!(flags & BPM_WM_COMMAND)) tpmFlags |= TPM_RETURNCMD;
int x = DoTrackPopup(menu, tpmFlags, r.left, r.top, hwndDlg, NULL);
if ((flags & BPM_ECHO_WM_COMMAND) && x)
SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(x, 0), 0);
MLSkinnedButton_SetDropDownState(buttonHWND, FALSE);
return x;
}
static void Dialog_Play(HWND hwndDlg, HWND from, UINT idFrom)
{
HMENU listMenu = GetSubMenu(g_context_menus2, 0);
int count = GetMenuItemCount(listMenu);
if (count > 2)
{
for (int i = 2; i < count; i++)
{
DeleteMenu(listMenu, 2, MF_BYPOSITION);
}
}
Dialog_ButtonPopupMenu(hwndDlg, idFrom, listMenu, BPM_WM_COMMAND);
}
static INT_PTR Dialog_OnInit(HWND hwndDlg, HWND hwndFocus, LPARAM lParam)
{
// benski> this is just going here because it's very likely to get called. will only get compiled in debug mode.
assert(sizeof(extra_idsW) / sizeof(*extra_idsW) == sizeof(extra_strsW) / sizeof(*extra_strsW));
g_displaysearch = !(BOOL)lParam;
// Set the hwnd for the search to a global only if the multipane view hasnt set it yet, as it takes precedence to which box gets populated with the query
//if (!IsWindow(hwndSearchGlobal))
hwndSearchGlobal = GetDlgItem(hwndDlg, IDC_QUICKSEARCH);
HACCEL accel = WASABI_API_LOADACCELERATORSW(IDR_VIEW_ACCELERATORS);
if (accel)
WASABI_API_APP->app_addAccelerators(hwndDlg, &accel, 1, TRANSLATE_MODE_CHILD);
if (!view.play)
{
SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_GET_VIEW_BUTTON_TEXT, (WPARAM)&view);
}
HWND hwndList;
FLICKERFIX ff;
INT index;
INT ffcl[] = { IDC_CLEAR,
IDC_BUTTON_PLAY,
IDC_BUTTON_ENQUEUE,
IDC_BUTTON_MIX,
IDC_BUTTON_INFOTOGGLE,
IDC_BUTTON_CREATEPLAYLIST,
IDC_MIXABLE,
IDC_MEDIASTATUS,
};
m_hwnd = hwndDlg;
m_bgupdinfoviewerflag = 0;
columnOrder[0] = -1;
last_item = -1;
tt_buf[0] = 0;
if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
if (cloud_hinst && cloud_hinst != (HINSTANCE)1)
{
winampMediaLibraryPlugin *(*gp)();
gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(cloud_hinst, "winampGetMediaLibraryPlugin");
if (gp)
{
winampMediaLibraryPlugin *mlplugin = gp();
if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
{
int64_t *out_ids = 0;
nx_string_t *out_filenames = 0;
size_t num_files = mlplugin->MessageProc(0x404, (INT_PTR)&out_filenames, (INT_PTR)&out_ids, 0xDEADBEEF);
for (size_t i = 0; i < num_files; i++)
{
cloudFiles.push_back((wchar_t *)out_filenames[i]->string);
}
if (out_filenames)
{
free(out_filenames);
out_filenames = 0;
}
if (out_ids)
{
free(out_ids);
out_ids = 0;
}
HWND ml_pmp_window = FindWindowW(L"ml_pmp_window", NULL);
if (IsWindow(ml_pmp_window))
{
SendMessage(ml_pmp_window, WM_PMP_IPC, (WPARAM)&cloudUploading, PMP_IPC_GETCLOUDTRANSFERS);
wchar_t a[32] = {0};
StringCchPrintfW(a, 32, L"%d", cloudUploading.size());
}
}
}
}
EnterCriticalSection(&g_db_cs);
if (!m_media_scanner) m_media_scanner = NDE_Table_CreateScanner(g_table);
LeaveCriticalSection(&g_db_cs);
itemCache.Items = 0;
itemCache.Alloc = 0;
itemCache.Size = 0;
hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
if (IsWindow(hwndList))
{
resultlist.setwnd(hwndList);
resultlist.ForceUnicode();
DWORD styleEx = LVS_EX_DOUBLEBUFFER | LVS_EX_HEADERDRAGDROP | LVS_EX_ONECLICKACTIVATE; /*LVS_EX_ONECLICKACTIVATE - needed to hottracking work prior WinXp */
SendMessageW(hwndList, LVM_SETEXTENDEDLISTVIEWSTYLE, styleEx, styleEx);
HWND hwndHeader = (HWND)SendMessage(hwndList, LVM_GETHEADER, 0, 0L);
if (IsWindow(hwndHeader)) SetWindowLongPtrW(hwndHeader, GWLP_ID, IDC_LIST2HEADER);
MLSKINWINDOW skin = {0};
skin.hwndToSkin = hwndList;
skin.skinType = SKINNEDWND_TYPE_LISTVIEW;
skin.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS;
MLSkinWindow(plugin.hwndLibraryParent, &skin);
}
else resultlist.setwnd(NULL);
ff.mode = FFM_ERASEINPAINT;
for (index = 0; index < sizeof(ffcl) / sizeof(INT); index++)
{
ff.hwnd = GetDlgItem(hwndDlg, ffcl[index]);
SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_FLICKERFIX, (WPARAM)&ff);
}
if (!g_displaysearch) // disable search box
{
ShowWindow(GetDlgItem(hwndDlg, IDC_QUICKSEARCH), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_CLEAR), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, IDC_SEARCHCAPTION), SW_HIDE);
}
if (!cloud_hinst || cloud_hinst == (HINSTANCE)1)
memcpy(columnOrder, defColumnOrder, sizeof(defColumnOrder));
else
memcpy(columnOrder, defColumnOrderCloud, sizeof(defColumnOrderCloud));
//Read the column order
Dialog_OnDisplayChange(hwndDlg);
int cloudcol = -1;
int l = g_view_metaconf->ReadInt(L"nbcolumns", 0);
if (l)
{
if (l > MAX_COLUMN_ORDER - 1) l = MAX_COLUMN_ORDER - 1;
index = 0;
for (; index < l; index++)
{
wchar_t tmp[128] = {0};
StringCchPrintfW(tmp, 128, L"column%d", index);
int v = g_view_metaconf->ReadInt(tmp, 0);
if (v == MEDIAVIEW_COL_CLOUD) cloudcol = index;
if (v < 0 || v >= MEDIAVIEW_COL_NUMS) v = 0;
columnOrder[index] = (BYTE)v;
}
columnOrder[index] = -1;
}
if (cloudcol == -1 && !g_view_metaconf->ReadInt(L"cloud", 1))
{
g_view_metaconf->WriteInt(L"cloud", 1);
for(int i = l; i != 0; i--)
{
columnOrder[i+1] = columnOrder[i];
if (i == 3)
{
columnOrder[i] = MEDIAVIEW_COL_CLOUD;
break;
}
}
}
if (!GetPropW(hwndList, L"pmp_list_proc")) {
SetPropW(hwndList, L"pmp_list_proc", (HANDLE)SetWindowLongPtrW(hwndList, GWLP_WNDPROC, (LONG_PTR)pmp_listview));
}
initColumnsHeader(hwndList);
char *pszTextA = (g_config->ReadInt(L"remembersearch", 0)) ? g_view_metaconf->ReadString("lastquery_utf8", "") : "";
AutoWide queryUnicode(pszTextA, CP_UTF8);
SetDlgItemTextW(hwndDlg, IDC_QUICKSEARCH, queryUnicode);
KillTimer(hwndDlg, UPDATE_QUERY_TIMER_ID);
doQuery(hwndDlg, queryUnicode, 0);
updateInfoText(hwndDlg);
search_oldWndProc = (WNDPROC)SetWindowLongPtrW(GetDlgItem(hwndDlg, IDC_QUICKSEARCH), GWLP_WNDPROC, (LONG_PTR)search_newWndProc);
if (g_table && !NDE_Table_GetRecordsCount(g_table) && !g_config->ReadInt(L"noshowadddlg", 0))
{
SetTimer(hwndDlg, 5050, 1000, NULL);
}
groupBtn = g_config->ReadInt(L"groupbtn", 1);
enqueuedef = (g_config->ReadInt(L"enqueuedef", 0) == 1);
/// detect predixis
predixisExist = FALSE;
//predixis out - begin
//pluginMessage p = {ML_MSG_PDXS_STATUS, (INT_PTR)"test", 0, 0};
//pszTextA = (char *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p);
//predixisExist = (pszTextA != NULL && pszTextA[0] != 0);
//predixis out - end
// v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay
// pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed
pluginMessage p = {ML_MSG_VIEW_BUTTON_HOOK, (INT_PTR)hwndDlg, (INT_PTR)MAKELONG(IDC_BUTTON_MIX, IDC_BUTTON_ENQUEUE), (INT_PTR)L"ml_local"};
wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p);
if (pszTextW && pszTextW[0] != 0)
{
// set this to be a bit different so we can just use one button and not the
// mixable one as well (leaving that to prevent messing with the resources)
customAllowed = TRUE;
SetDlgItemTextW(hwndDlg, IDC_BUTTON_MIX, pszTextW);
}
else
customAllowed = FALSE;
ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON_MIX), (!customAllowed ? SW_HIDE : SW_SHOW));
ShowWindow(GetDlgItem(hwndDlg, IDC_MIXABLE), (!(predixisExist & 1) ? SW_HIDE : SW_SHOW));
MLSKINWINDOW m = {0};
m.hwndToSkin = hwndDlg;
m.skinType = SKINNEDWND_TYPE_AUTO;
m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS;
MLSkinWindow(plugin.hwndLibraryParent, &m);
m.skinType = SKINNEDWND_TYPE_BUTTON;
m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | (groupBtn ? SWBS_SPLITBUTTON : 0);
const int buttonids[] = {IDC_BUTTON_PLAY, IDC_BUTTON_ENQUEUE, IDC_BUTTON_MIX};
for (size_t i=0;i!=sizeof(buttonids)/sizeof(buttonids[0]);i++)
{
m.hwndToSkin = GetDlgItem(hwndDlg, buttonids[i]);
if (IsWindow(m.hwndToSkin)) MLSkinWindow(plugin.hwndLibraryParent, &m);
}
m.skinType = SKINNEDWND_TYPE_AUTO;
m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS;
const int buttonidz[] = {IDC_SEARCHCAPTION, IDC_QUICKSEARCH, IDC_MEDIASTATUS, IDC_CLEAR, IDC_BUTTON_INFOTOGGLE, IDC_BUTTON_CREATEPLAYLIST, IDC_MIXABLE};
for (size_t i=0;i!=sizeof(buttonidz)/sizeof(buttonidz[0]);i++)
{
m.hwndToSkin = GetDlgItem(hwndDlg, buttonidz[i]);
if (IsWindow(m.hwndToSkin)) MLSkinWindow(plugin.hwndLibraryParent, &m);
}
Dialog_UpdateButtonText(hwndDlg, enqueuedef);
return FALSE;
}
static BOOL Dialog_OnNotify(HWND hwndDlg, INT idCtrl, NMHDR* pnmh, LRESULT *pResult)
{
switch (pnmh->idFrom)
{
case IDC_LIST2:
switch (pnmh->code)
{
case LVN_ITEMCHANGED: return ListView_OnItemChanged(hwndDlg, (NMLISTVIEW*)pnmh);
case NM_DBLCLK: return ListView_OnDoubleClick(hwndDlg, (NMITEMACTIVATE*)pnmh);
case LVN_ODFINDITEMA:
case LVN_ODFINDITEMW: return ListView_OnFindItem(hwndDlg, (NMLVFINDITEMW*)pnmh, pResult, pnmh->code);
case LVN_GETDISPINFOA:
case LVN_GETDISPINFOW: return ListView_OnGetDispInfo(hwndDlg, (NMLVDISPINFOW*)pnmh, pnmh->code);
case LVN_COLUMNCLICK: return ListView_OnColumnClick(hwndDlg, (NMLISTVIEW*)pnmh);
case LVN_BEGINDRAG: return ListView_OnBeginDrag(hwndDlg, (NMLISTVIEW*)pnmh);
case NM_RETURN: return ListView_OnReturn(hwndDlg, pnmh);
case NM_CUSTOMDRAW: return ListView_OnCustomDraw(hwndDlg, (NMLVCUSTOMDRAW*)pnmh, pResult);
case LVN_HOTTRACK: return ListView_OnHotTrack(hwndDlg, (NMLISTVIEW*)pnmh, pResult);
case NM_CLICK: return ListView_OnClick(hwndDlg, (NMITEMACTIVATE*)pnmh);
}
break;
case IDC_LIST2HEADER:
switch (pnmh->code)
{
case NM_RCLICK: return Header_OnRightClick(hwndDlg, pnmh, pResult);
case HDN_ENDDRAG: return Header_OnEndDrag(hwndDlg, (NMHEADERW*)pnmh, pResult);
case HDN_ITEMCHANGINGA:
case HDN_ITEMCHANGINGW: return Header_OnItemChanging(hwndDlg, (NMHEADERW*)pnmh, pResult, pnmh->code);
}
break;
}
return FALSE;
}
static void Dialog_OnInitMenuPopup(HWND hwndDlg, HMENU hMenu, UINT nIndex, BOOL bSysMenu)
{
if (hMenu && hMenu == s.build_hMenu && s.mode == 1)
{
myMenu = TRUE;
if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
s.mode = 2;
myMenu = FALSE;
}
if (rate_hmenu && hMenu == rate_hmenu)
{
int x;
int sel = 0;
for (x = 0; x < itemCache.Size; x ++)
{
if (resultlist.GetSelected(x))
{
int s = itemCache.Items[x].rating;
if (s == sel || !sel) sel = s;
if (s != sel) break;
}
}
if (-1 == sel) sel = 0;
Menu_SetRatingValue(rate_hmenu, sel);
}
if (cloud_hmenu && hMenu == cloud_hmenu)
{
int n = resultlist.GetSelectionMark();
if (n != -1 && !GetMenuItemCount(hMenu))
{
WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_GET_CLOUD_STATUS, (intptr_t)itemCache.Items[n].filename, (intptr_t)&cloud_hmenu);
}
}
}
static void Dialog_OnMouseMove(HWND hwndDlg, UINT nFlags, POINTS pts)
{
if (GetCapture() == hwndDlg)
{
mlDropItemStruct m = {0};
POINTSTOPOINT(m.p, pts);
MapWindowPoints(hwndDlg, HWND_DESKTOP, (POINT*)&m.p, 1);
if (MLRatingColumn_Drag(plugin.hwndLibraryParent, &m.p)) return;
m.type = ML_TYPE_ITEMRECORDLIST;
pluginHandleIpcMessage(ML_IPC_HANDLEDRAG, (WPARAM)&m);
}
}
static void Dialog_OnLButtonUp(HWND hwndDlg, UINT nFlags, POINTS pts)
{
if (GetCapture() == hwndDlg)
{
mlDropItemStruct m = {0};
ReleaseCapture();
POINTSTOPOINT(m.p, pts);
MapWindowPoints(hwndDlg, HWND_DESKTOP, (POINT*)&m.p, 1);
ratingColumn.bCanceled = FALSE;
ratingColumn.ptAction = m.p;
ratingColumn.bRedrawNow = TRUE;
if (MLRatingColumn_EndDrag(plugin.hwndLibraryParent, &ratingColumn))
{
SetRating(ratingColumn.iItem, ratingColumn.value, ratingColumn.hwndList);
return;
}
m.type = ML_TYPE_ITEMRECORDLISTW;
m.flags = ML_HANDLEDRAG_FLAG_NOCURSOR;
pluginHandleIpcMessage(ML_IPC_HANDLEDRAG, (WPARAM)&m);
if (m.result > 0) // try itemRecordListW
{
itemRecordListW myObj = {0, };
copyFilesToItemCacheW(&myObj);
m.flags = 0;
m.result = 0;
m.data = (void*) & myObj;
pluginHandleIpcMessage(ML_IPC_HANDLEDROP, (WPARAM)&m);
_aligned_free(myObj.Items); // DO NOT empty this object, cause it doesnt own its data
}
else // if it didn't work, fall back to itemRecordList
{
m.type = ML_TYPE_ITEMRECORDLIST;
m.result = 0;
pluginHandleIpcMessage(ML_IPC_HANDLEDRAG, (WPARAM)&m);
if (m.result > 0)
{
itemRecordListW myObj = {0, };
copyFilesToItemCacheW(&myObj);
itemRecordList objA = {0, };
convertRecordList(&objA, &myObj);
m.flags = 0;
m.result = 0;
m.data = (void*) & objA;
pluginHandleIpcMessage(ML_IPC_HANDLEDROP, (WPARAM)&m);
emptyRecordList(&objA);
freeRecordList(&objA);
_aligned_free(myObj.Items); // DO NOT empty this object, cause it doesnt own its data
}
}
}
}
class ItemRecordPlaylist : public ifc_playlist
{
public:
ItemRecordPlaylist(const itemRecordListW *_list)
{
list = _list;
}
private:
size_t GetNumItems()
{
return list->Size;
}
size_t GetItem(size_t item, wchar_t *filename, size_t filenameCch)
{
if (item < (size_t)list->Size && list->Items[item].filename)
{
StringCchCopyW(filename, filenameCch, list->Items[item].filename);
return 1;
}
return 0;
}
size_t GetItemTitle(size_t item, wchar_t *title, size_t titleCch)
{
if (item < (size_t)list->Size && list->Items[item].filename)
{
TAG_FMT_EXT(list->Items[item].filename, itemrecordWTagFunc, ndeTagFuncFree, (void*)&list->Items[item], title, titleCch, 0);
return 1;
}
return 0;
}
int GetItemLengthMilliseconds(size_t item)
{
if (item < (size_t)list->Size && list->Items[item].length>=0)
{
return list->Items[item].length * 1000;
}
return -1000;
}
private:
const itemRecordListW *list;
protected:
RECVS_DISPATCH;
};
#define CBCLASS ItemRecordPlaylist
START_DISPATCH;
CB(IFC_PLAYLIST_GETNUMITEMS, GetNumItems)
CB(IFC_PLAYLIST_GETITEM, GetItem)
CB(IFC_PLAYLIST_GETITEMTITLE, GetItemTitle)
CB(IFC_PLAYLIST_GETITEMLENGTHMILLISECONDS, GetItemLengthMilliseconds)
END_DISPATCH;
#undef CBCLASS
static void Dialog_OnCommand(HWND hwndDlg, UINT idCtrl, INT nCode, HWND hwndCtrl)
{
if (GetFocus() != hwndSearchGlobal)
{
switch (idCtrl)
{
case IDC_CLEAR:
SetDlgItemText(hwndDlg, IDC_QUICKSEARCH, L"");
break;
case IDC_BUTTON_INFOTOGGLE:
updateInfoText(hwndDlg, TRUE);
UpdateWindow(hwndDlg);
LayoutWindows(hwndDlg, TRUE);
break;
case IDC_QUICKSEARCH:
if (nCode == EN_CHANGE)
{
KillTimer(hwndDlg, UPDATE_QUERY_TIMER_ID);
SetTimer(hwndDlg, UPDATE_QUERY_TIMER_ID, g_querydelay, NULL);
}
break;
case ID_AUDIOWND_PLAYSELECTION:
case ID_QUERYWND_PLAYQUERY:
case IDC_BUTTON_PLAY:
case ID_MEDIAWND_PLAYSELECTEDFILES:
case ID_AUDIOWND_ENQUEUESELECTION:
case IDC_BUTTON_ENQUEUE:
case ID_MEDIAWND_ENQUEUESELECTEDFILES:
case IDC_BUTTON_MIX:
{
if (nCode == MLBN_DROPDOWN)
{
Dialog_Play(hwndDlg, hwndCtrl, idCtrl);
}
else
{
int action;
if (idCtrl == IDC_BUTTON_PLAY || idCtrl == ID_MEDIAWND_PLAYSELECTEDFILES || idCtrl == ID_AUDIOWND_PLAYSELECTION)
{
action = (nCode == 1) ? g_config->ReadInt(L"enqueuedef", 0) == 1 : 0;
}
else if (idCtrl == IDC_BUTTON_ENQUEUE || idCtrl == ID_MEDIAWND_ENQUEUESELECTEDFILES || idCtrl == ID_AUDIOWND_ENQUEUESELECTION)
{
action = (nCode == 1) ? g_config->ReadInt(L"enqueuedef", 0) != 1 : 1;
}
else
break;
int i, l = itemCache.Size;
for (i = 0; i < l; i++) if (resultlist.GetSelected(i)) break;
playFiles(action/*idCtrl == IDC_BUTTON_ENQUEUE*/, i == l);
}
}
break;
case IDC_BUTTON_CREATEPLAYLIST:
#if 0
// TODO consider exposing this option somehow...
if (AGAVE_API_PLAYLISTMANAGER) // This is the old Create Playlist button code
{
wchar_t fn[MAX_PATH] = {0};
wchar_t dir[MAX_PATH] = {0};
GetTempPathW(MAX_PATH,dir);
GetTempFileNameW(dir,L"ml_playlist",0,fn);
wcscat(fn,L".m3u8");
ItemRecordPlaylist playlist(&itemCache);
if (AGAVE_API_PLAYLISTMANAGER->Save(fn, &playlist) == PLAYLISTMANAGER_SUCCESS)
{
mlAddPlaylist p={sizeof(p),NULL,fn,PL_FLAGS_IMPORT,-1,-1};
SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&p,ML_IPC_PLAYLIST_ADD);
DeleteFileW(fn);
}
}
#endif
if (AGAVE_API_PLAYLIST_GENERATOR)
{
const int number_selected = resultlist.GetSelectedCount(); // Total selected
if (number_selected > 0)
{
itemRecordListW recordList; // Record list
itemRecordW *records = new itemRecordW[number_selected]; // Array of records
int selectedRecordcounter = 0;
for (int i = 0; i < itemCache.Size; i++)
{
if (resultlist.GetSelected(i)) // See if the current item is selected or not
{
records[selectedRecordcounter] = itemCache.Items[i]; // If its selected then add it to our itemlist
selectedRecordcounter++;
}
}
recordList.Size = selectedRecordcounter; // Set the correct size of the record list
recordList.Items = records; // Set the array of records to the record list
AGAVE_API_PLAYLIST_GENERATOR->GeneratePlaylist(hwndDlg, &recordList); // Call the playlist API with the list of selected records
delete [] records; // Free up the array of records
}
else
{
wchar_t title[64] = {0};
MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_ERROR_PLG_SELECT_TRACKS), WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_PLAYLIST_GENERATOR,title,64), MB_OK | MB_ICONINFORMATION);
}
}
break;
#if 0
case IDC_BUTTON_MIX:
{
itemRecordList list;
int i;
int ct = 0;
for (i = 0; i < itemCache.Size; i++)
{
if (resultlist.GetSelected(i))
{
ct++;
}
}
ZeroMemory(&list, sizeof(itemRecordList));
allocRecordList(&list, ct, 1);
list.Size = ct;
ct = 0;
for (i = 0; i < itemCache.Size; i++)
{
if (resultlist.GetSelected(i))
{
convertRecord(&list.Items[ct], &itemCache.Items[i]);
ct++;
}
}
pluginMessage p = {ML_MSG_PDXS_MIX, (INT_PTR) &list, 0, 0};
SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM)&p, ML_IPC_SEND_PLUGIN_MESSAGE);
emptyRecordList(&list);
freeRecordList(&list);
}
break;
#endif
case ID_MEDIAWND_SELECTALL:
{
LVITEM item = {0};
item.state = LVIS_SELECTED;
item.stateMask = LVIS_SELECTED;
SendMessageW(resultlist.getwnd(), LVM_SETITEMSTATE, (WPARAM)-1, (LPARAM)&item);
}
break;
case ID_MEDIAWND_REMOVEFROMLIBRARY:
removeSelectedItems(0);
break;
case ID_EDITITEMINFOS:
if (resultlist.GetSelectedCount() > 0)
editInfo(hwndDlg);
break;
case ID_PE_ID3:
fileInfoDialogs(hwndDlg);
PostMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)resultlist.getwnd(), (LPARAM)TRUE);
break;
case IDC_REFRESH_METADATA:
RefreshMetadata(hwndDlg);
break;
case ID_MEDIAWND_EXPLOREFOLDER:
exploreItemFolder(hwndDlg);
break;
}
}
else
{
switch (idCtrl)
{
case IDC_QUICKSEARCH:
if (nCode == EN_CHANGE)
{
KillTimer(hwndDlg, UPDATE_QUERY_TIMER_ID);
SetTimer(hwndDlg, UPDATE_QUERY_TIMER_ID, g_querydelay, NULL);
}
break;
case ID_MEDIAWND_SELECTALL:
SendMessageW(hwndSearchGlobal, EM_SETSEL, 0, -1);
break;
case ID_MEDIAWND_REMOVEFROMLIBRARY:
{
DWORD start = -1, end = -1;
SendMessageW(hwndSearchGlobal, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
if (start != -1)
{
if (start == end)
{
SendMessageW(hwndSearchGlobal, EM_SETSEL, start, end + 1);
}
SendMessageW(hwndSearchGlobal, EM_REPLACESEL, TRUE, (LPARAM)"");
SendMessageW(hwndSearchGlobal, EM_SETSEL, start, start);
}
}
break;
}
}
}
static void Dialog_OnTimer(HWND hwndDlg, UINT_PTR idEvent, TIMERPROC fnTimer)
{
switch (idEvent)
{
case 5050:
KillTimer(hwndDlg, 5050);
WASABI_API_DIALOGBOXW(IDD_NEEDADDFILES, hwndDlg, needAddFilesProc);
PostMessage(GetParent(hwndDlg), WM_APP + 1, (WPARAM)0, (LPARAM)0);
break;
case 6600:
KillTimer(hwndDlg, idEvent);
if (m_last_selitem >= 0 && m_last_selitem < itemCache.Size)
{
if (predixisExist & 1)
{
// Only single seeds are supported currently
if (resultlist.GetSelectedCount() == 1 && isMixable(itemCache.Items[m_last_selitem]))
{
SetDlgItemTextW(hwndDlg, IDC_MIXABLE, WASABI_API_LNGSTRINGW(IDS_MIXABLE));
isMixablePresent = true;
}
else SetDlgItemText(hwndDlg, IDC_MIXABLE, L"");
}
SendMessageW(GetParent(hwndDlg), WM_SHOWFILEINFO, (WPARAM)FALSE, (LPARAM)itemCache.Items[m_last_selitem].filename);
m_last_selitem = -1;
}
break;
case 123:
if (bgThread_Handle)
{
HWND hwndList;
hwndList = resultlist.getwnd();
if (1 != ListView_GetItemCount(hwndList)) ListView_SetItemCountEx(hwndList, 1, LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL);
ListView_RedrawItems(hwndList, 0, 0);
//UpdateWindow(hwndList);
}
break;
case UPDATE_QUERY_TIMER_ID:
{
KillTimer(hwndDlg, UPDATE_QUERY_TIMER_ID);
wchar_t buf[2048] = {0};
GetWindowTextW(GetDlgItem(hwndDlg, IDC_QUICKSEARCH), buf, ARRAYSIZE(buf));
doQuery(hwndDlg, buf);
}
break;
case UPDATE_RESULT_LIST_TIMER_ID:
{
ListView_RedrawItems(resultlist.getwnd(), 0, resultlist.GetCount() - 1);
}
break;
}
}
static void Dialog_OnDestroy(HWND hwndDlg)
{
HWND hwndList;
INT i, j;
wchar_t buf[2048] = {0};
bgQuery_Stop();
GetDlgItemTextW(hwndDlg, IDC_QUICKSEARCH, buf, ARRAYSIZE(buf));
g_view_metaconf->WriteString("lastquery_utf8", AutoChar(buf, CP_UTF8));
hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
if (hwndList && IsWindow(hwndList))
{
for (i = 0; columnOrder[i] != -1; i++)
{
headerColumn *cl = &columnList[columnOrder[i]];
g_view_metaconf->WriteInt(AutoWide(cl->config_name), SendMessageW(hwndList, LVM_GETCOLUMNWIDTH, i, 0L));
}
}
//Save the column order
for (i = 0; columnOrder[i] != -1; i++);
g_view_metaconf->WriteInt(L"nbcolumns", i);
for (j = 0; j < i; j++)
{
wchar_t tmp[128] = {0};
StringCchPrintfW(tmp, 128, L"column%d", j);
g_view_metaconf->WriteInt(tmp, columnOrder[j]);
}
freeRecordList(&itemCache);
itemCache.Items = 0;
itemCache.Alloc = 0;
itemCache.Size = 0;
cloudFiles.clear();
cloudUploading.clear();
hwndSearchGlobal = 0; // Set the hwnd for the search to a global to null so we know we are not in the single pane view
}
static void Dialog_OnWindowPosChanged(HWND hwndDlg, WINDOWPOS *pwp)
{
if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE) & pwp->flags) || (SWP_FRAMECHANGED & pwp->flags))
{
LayoutWindows(hwndDlg, !(SWP_NOREDRAW & pwp->flags), 0 != (SWP_SHOWWINDOW & pwp->flags));
}
}
static void Dialog_OnSyncHeaderOrder(HWND hwndDlg, HWND hwndHeader)
{
LVCOLUMNW column = {0};
wchar_t buffer[128] = {0};
signed char tempOrder[MAX_COLUMN_ORDER] = {0};
HWND hwndList = GetDlgItem(hwndDlg, IDC_LIST2);
if (!hwndList) return;
CopyMemory(tempOrder, columnOrder, sizeof(tempOrder)/sizeof(signed char));
column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_IMAGE;
column.cchTextMax = sizeof(buffer) /sizeof(wchar_t);
SendMessageW(hwndList, WM_SETREDRAW, FALSE, 0L);
INT sort = MLSkinnedListView_GetSort(hwndList);
INT count = (INT)SendMessageW(hwndHeader, HDM_GETITEMCOUNT, 0, 0L);
if (count > 0)
{
INT index = count + 1, *pOrder = (INT*)calloc(1, sizeof(INT)*count);
if (pOrder && SendMessageW(hwndList, LVM_GETCOLUMNORDERARRAY, count, (LPARAM)pOrder))
{
INT order;
for (order = 0; order < count; order++)
{
column.pszText = buffer;
if (!SendMessageW(hwndList, LVM_GETCOLUMNW, pOrder[order], (LPARAM)&column)) continue;
column.iOrder = order;
// update position of the cloud column icon
if (tempOrder[pOrder[order]] == MEDIAVIEW_COL_CLOUD)
{
if (!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
!SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE))
{
MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), -1);
SetPropW(hwndList, L"pmp_list_info", (HANDLE)-1);
column.cx = 0;
}
else
{
MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(hwndList), order);
SetPropW(hwndList, L"pmp_list_info", (HANDLE)order);
column.cx = 27;
MLCloudColumn_GetWidth(plugin.hwndLibraryParent, &column.cx);
}
}
SendMessageW(hwndList, LVM_INSERTCOLUMNW, index++, (LPARAM)&column);
columnOrder[order] = tempOrder[pOrder[order]];
if (LOWORD(sort) == pOrder[order]) MLSkinnedListView_DisplaySort(hwndList, order, HIWORD(sort));
}
for (order = 0; order < count; order++) SendMessageW(hwndList, LVM_DELETECOLUMN, 0, 0L);
}
for (index = 0; -1 != columnOrder[index] && MEDIAVIEW_COL_RATING != columnOrder[index] && MEDIAVIEW_COL_CLOUD != columnOrder[index]; index++);
if (-1 != columnOrder[index])
{
INT w = (INT)SendMessageW(hwndList, LVM_GETCOLUMNWIDTH, index, 0L);
SendMessageW(hwndList, LVM_SETCOLUMNWIDTH, index, (LPARAM)w);
}
if (pOrder) free(pOrder);
}
SendMessageW(hwndList, WM_SETREDRAW, TRUE, 0L);
}
static void Window_OnQueryFileInfo(HWND hwnd)
{
INT index;
HWND hwndList = GetDlgItem(hwnd, IDC_LIST2);
index = (hwndList) ? (INT)SendMessage(hwndList, LVM_GETNEXTITEM, (WPARAM)-1, (LPARAM)LVNI_FOCUSED) : -1;
SendMessageW(GetParent(hwnd), WM_SHOWFILEINFO, (WPARAM)FALSE, (LPARAM)((-1 != index && index < itemCache.Size) ? itemCache.Items[index].filename : L""));
}
void Window_OnDropFiles(HWND hwndDlg, HDROP hdrop)
{
wchar_t temp[1024] = {0};
int y = DragQueryFileW(hdrop, 0xffffffff, temp, 1024);
if (y > 0)
{
wchar_t **paths = (wchar_t **)calloc(y, sizeof(wchar_t *));
int *guesses = (int *)calloc(y, sizeof(int));
int *metas = (int *)calloc(y, sizeof(int));
int *recs= (int *)calloc(y, sizeof(int));
if (paths && guesses && metas && recs)
{
size_t count=0;
for (int x = 0; x < y; x ++)
{
DragQueryFileW(hdrop, x, temp, 1024);
int guess = -1, meta = -1, rec = 1;
// do this for normal media drops
PLCallBackW plCB;
if (AGAVE_API_PLAYLISTMANAGER && PLAYLISTMANAGER_SUCCESS != AGAVE_API_PLAYLISTMANAGER->Load(temp, &plCB))
{
autoscan_add_directory(temp, &guess, &meta, &rec, 0);
if (guess == -1) guess = g_config->ReadInt(L"guessmode", 0);
if (meta == -1) meta = g_config->ReadInt(L"usemetadata", 1);
paths[count] = _wcsdup(temp);
guesses[count]=guess;
metas[count]=meta;
recs[count]=rec;
count++;
}
}
DragFinish(hdrop);
Scan_ScanFolders(hwndDlg, count, paths, guesses, metas, recs);
if (IsWindow(m_curview_hwnd)) SendMessage(m_curview_hwnd, WM_APP + 1, 0, 0); //update current view
}
else
{
free(paths);
free(guesses);
free(metas);
free(recs);
}
}
else DragFinish(hdrop);
}
INT_PTR CALLBACK view_mediaDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL a = dialogSkinner.Handle(hwndDlg, uMsg, wParam, lParam); if (a) return a;
switch (uMsg)
{
case WM_INITMENUPOPUP: Dialog_OnInitMenuPopup(hwndDlg, (HMENU)wParam, LOWORD(lParam), HIWORD(lParam)); break;
case WM_DISPLAYCHANGE: Dialog_OnDisplayChange(hwndDlg); break;
case WM_INITDIALOG: return Dialog_OnInit(hwndDlg, (HWND)wParam, lParam);
case WM_MOUSEMOVE: Dialog_OnMouseMove(hwndDlg, (UINT)wParam, MAKEPOINTS(lParam)); break;
case WM_LBUTTONUP: Dialog_OnLButtonUp(hwndDlg, (UINT)wParam, MAKEPOINTS(lParam)); break;
case WM_DROPFILES: Window_OnDropFiles(hwndDlg, (HDROP)wParam);
case WM_COMMAND: Dialog_OnCommand(hwndDlg, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break;
case WM_TIMER: Dialog_OnTimer(hwndDlg, (UINT_PTR) wParam, (TIMERPROC)lParam); break;
case WM_DESTROY: Dialog_OnDestroy(hwndDlg); break;
case WM_WINDOWPOSCHANGED: Dialog_OnWindowPosChanged(hwndDlg, (WINDOWPOS*)lParam); break;
case WM_ERASEBKGND: return 1; //handled by WADlg_DrawChildWindowBorders in WM_PAINT
case WM_CONTEXTMENU:
Dialog_OnContextMenu(hwndDlg, (HWND)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return 0;
case WM_NOTIFY:
{
LRESULT result;
result = 0L;
if (Dialog_OnNotify(hwndDlg, (INT)wParam, (NMHDR*)lParam, &result))
{
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)result);
return TRUE;
}
}
break;
case IM_SYNCHEADERORDER: Dialog_OnSyncHeaderOrder(hwndDlg, (HWND)lParam); break;
case WM_APP + 3: // send by bgthread
if (wParam == 0x666) m_bgupdinfoviewerflag = 1;
else if (wParam == 0x69)
{
bgQuery_Stop();
resultlist.SetVirtualCount(0);
resultlist.SetVirtualCount(itemCache.Size); // TODO: we could set a limit here
ListView_RedrawItems(resultlist.getwnd(), 0, itemCache.Size - 1);
UpdateWindow(resultlist.getwnd());
__int64 total_len_bytes = bg_total_len_bytes;
int total_length_s = (int)bg_total_len_s & 0x7FFFFFFF;
wchar_t buffer[4*64] = {0};
wchar_t *pb[4] = {0};
for (int i = 0; i < 4; i++) pb[i] = buffer + i * 64;
int index(0);
StringCchPrintfW(pb[index], 64, L"%d %s", itemCache.Size,
WASABI_API_LNGSTRINGW(itemCache.Size == 1 ? IDS_ITEM : IDS_ITEMS));
index++;
if (itemCache.Size)
{
int uncert = 0; //bg_total_len_s>>31;
if (total_length_s < 60*60) StringCchPrintfW(pb[index], 64, L"[%s%u:%02u]", uncert ? L"~" : L"", total_length_s / 60, total_length_s % 60);
else if (total_length_s < 60*60*24) StringCchPrintfW(pb[index], 64, L"[%s%u:%02u:%02u]", uncert ? L"~" : L"", total_length_s / 60 / 60, (total_length_s / 60) % 60, total_length_s % 60);
else
{
wchar_t days[16] = {0};
int total_days = total_length_s / (60 * 60 * 24);
total_length_s -= total_days * 60 * 60 * 24;
StringCchPrintfW(pb[index], 64,
WASABI_API_LNGSTRINGW(IDS_LENGTH_DURATION_STRING),
uncert ? L"~" : L"", total_days,
WASABI_API_LNGSTRINGW_BUF(total_days == 1 ? IDS_DAY : IDS_DAYS, days, 16),
total_length_s / 60 / 60, (total_length_s / 60) % 60, total_length_s % 60);
}
index++;
}
if (total_len_bytes)
{
StringCchCopyW(pb[index], 64, L"[");
WASABI_API_LNG->FormattedSizeString(pb[index] + 1, 64, total_len_bytes);
StringCchCatW(pb[index], 64, L"]");
index++;
}
unsigned int ms = (UINT)(querytime.QuadPart * 1000 / freq.QuadPart);
StringCchPrintfW(pb[index], 64, WASABI_API_LNGSTRINGW(IDS_IN_X_SEC), ms / 1000.0f);
index++;
SetStatusText(GetDlgItem(hwndDlg, IDC_MEDIASTATUS), (LPCWSTR*)pb, index);
if (m_bgupdinfoviewerflag)
{
m_bgupdinfoviewerflag = 0;
if (itemCache.Size > 0)
{
if (predixisExist)
{
if (resultlist.GetSelectedCount() == 1 && isMixable(itemCache.Items[0]))
{
SetDlgItemText(hwndDlg, IDC_MIXABLE, L"");
isMixablePresent = true;
}
else
{
SetDlgItemText(hwndDlg, IDC_MIXABLE, L"");
}
}
SendMessageW(GetParent(hwndDlg), WM_SHOWFILEINFO, (WPARAM)FALSE, (LPARAM)itemCache.Items[0].filename);
}
}
}
break;
case WM_APP + 1:
bgQuery((resultsniff_funcW)wParam, (int)lParam);
break;
case WM_APP + 5:
{
// TODO
int done = (HIWORD(wParam) == 1);
int code = (LOWORD(wParam));
for (int i = 0; i < itemCache.Size; i ++)
{
// 0 = no change
// 1 = add cloud
// 2 = add local
// 4 = removed
if (!lstrcmpiW(itemCache.Items[i].filename, (wchar_t *)lParam))
{
if (!done)
{
setCloudValue(&itemCache.Items[i], L"5");
}
else
{
if (code == NErr_Success)
{
// uploaded ok
// TODO if going to another device, will need to alter this
setCloudValue(&itemCache.Items[i], L"0");
}
else
{
// re-query state
setCloudValue(&itemCache.Items[i], L"0");
}
}
InvalidateRect(resultlist.getwnd(), NULL, TRUE);
break;
}
}
break;
}
case WM_APP + 6: // handles the ml_cloud 'first pull' announces so we
{ // can then show the cloud column & update the cache
initColumnsHeader(resultlist.getwnd());
bgQuery();//(resultsniff_funcW)wParam, (int)lParam);
break;
}
case WM_APP + 104:
{
Dialog_UpdateButtonText(hwndDlg, wParam);
LayoutWindows(hwndDlg, TRUE);
return 0;
}
case WM_PAINT:
{
int tab[] = { IDC_LIST2 | DCW_SUNKENBORDER, IDC_QUICKSEARCH | DCW_SUNKENBORDER};
dialogSkinner.Draw(hwndDlg, tab, 1 + !!IsWindowVisible(GetDlgItem(hwndDlg, IDC_QUICKSEARCH)));
}
return 0;
case WM_ML_CHILDIPC:
switch (lParam)
{
case ML_CHILDIPC_GO_TO_SEARCHBAR:
SendDlgItemMessage(hwndDlg, IDC_QUICKSEARCH, EM_SETSEL, 0, -1);
SetFocus(GetDlgItem(hwndDlg, IDC_QUICKSEARCH));
break;
case ML_CHILDIPC_REFRESH_SEARCH:
PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_QUICKSEARCH, EN_CHANGE), (LPARAM)GetDlgItem(hwndDlg, IDC_QUICKSEARCH));
break;
}
break;
case WM_USER + 0x201:
offsetX = (short)LOWORD(wParam);
offsetY = (short)HIWORD(wParam);
g_rgnUpdate = (HRGN)lParam;
return TRUE;
case WM_QUERYFILEINFO: Window_OnQueryFileInfo(hwndDlg); break;
}
return FALSE;
}
//////////////////////////////// Customize columns dialog
static signed char edit_columnOrder[MAX_COLUMN_ORDER];
static INT_PTR CALLBACK custColumns_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HWND m_curlistbox_hwnd, m_availlistbox_hwnd;
switch (uMsg)
{
case WM_INITDIALOG:
memcpy(edit_columnOrder, columnOrder, sizeof(edit_columnOrder));
m_curlistbox_hwnd = GetDlgItem(hwndDlg, IDC_LIST1);
m_availlistbox_hwnd = GetDlgItem(hwndDlg, IDC_LIST2);
if (NULL != WASABI_API_APP)
{
if (NULL != m_curlistbox_hwnd)
WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(m_curlistbox_hwnd, TRUE);
if (NULL != m_availlistbox_hwnd)
WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(m_availlistbox_hwnd, TRUE);
}
case WM_USER + 32:
{
for (int i = 0; edit_columnOrder[i] != -1; i++)
{
int c = edit_columnOrder[i];
headerColumn *cl = &columnList[c];
int column_id = cl->column_id;
if (column_id == IDS_CLOUD || column_id == IDS_CLOUD_HIDDEN)
{
// if no cloud support at all then we hide everything
if (!cloud_hinst || cloud_hinst == (HINSTANCE)1) continue;
column_id = ((!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
!SendMessageW(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE)) ? IDS_CLOUD_HIDDEN : IDS_CLOUD);
}
int r = SendMessageW(m_curlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(column_id));
SendMessageW(m_curlistbox_hwnd, LB_SETITEMDATA, r, c);
}
for (int i = 0; i < sizeof(columnList) / sizeof(headerColumn); i++)
{
headerColumn *cl = &columnList[i];
int j;
for (j = 0; edit_columnOrder[j] != -1 && edit_columnOrder[j] != i; j++);
if (edit_columnOrder[j] == -1)
{
int column_id = cl->column_id;
if (column_id == IDS_CLOUD || column_id == IDS_CLOUD_HIDDEN)
{
// if no cloud support at all then we hide everything
if (!cloud_hinst || cloud_hinst == (HINSTANCE)1) continue;
column_id = ((!cloud_hinst || cloud_hinst == (HINSTANCE)1 ||
!SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_ACTIVE)) ? IDS_CLOUD_HIDDEN : IDS_CLOUD);
}
int r = SendMessageW(m_availlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(column_id));
SendMessageW(m_availlistbox_hwnd, LB_SETITEMDATA, r, i);
}
}
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_DEFS:
if (!cloud_hinst || cloud_hinst == (HINSTANCE)1)
memcpy(edit_columnOrder, defColumnOrder, sizeof(defColumnOrder));
else
memcpy(edit_columnOrder, defColumnOrderCloud, sizeof(defColumnOrderCloud));
SendMessage(m_curlistbox_hwnd, LB_RESETCONTENT, 0, 0);
SendMessage(m_availlistbox_hwnd, LB_RESETCONTENT, 0, 0);
SendMessage(hwndDlg, WM_USER + 32, 0, 0);
break;
case IDC_LIST2:
if (HIWORD(wParam) != LBN_DBLCLK)
{
if (HIWORD(wParam) == LBN_SELCHANGE)
{
int r = SendMessage(m_availlistbox_hwnd, LB_GETSELCOUNT, 0, 0) > 0;
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), r);
}
return 0;
}
case IDC_BUTTON2:
//add column
{
for (int i = 0;i < SendMessage(m_availlistbox_hwnd, LB_GETCOUNT, 0, 0);i++)
{
if (SendMessage(m_availlistbox_hwnd, LB_GETSEL, i, 0))
{
int c = SendMessage(m_availlistbox_hwnd, LB_GETITEMDATA, i, 0);
int j;
for (j = 0;edit_columnOrder[j] != -1;j++);
edit_columnOrder[j] = (BYTE)c;
edit_columnOrder[j + 1] = -1;
SendMessage(m_availlistbox_hwnd, LB_DELETESTRING, i, 0);
i--;
int r = SendMessageW(m_curlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(columnList[c].column_id));
SendMessageW(m_curlistbox_hwnd, LB_SETITEMDATA, r, c);
}
}
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), 0);
}
break;
case IDC_LIST1:
if (HIWORD(wParam) != LBN_DBLCLK)
{
if (HIWORD(wParam) == LBN_SELCHANGE)
{
int r = SendMessage(m_curlistbox_hwnd, LB_GETSELCOUNT, 0, 0) > 0;
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON3), r);
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON4), r);
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON5), r);
}
return 0;
}
case IDC_BUTTON3:
//remove column
{
for (int i = 0;i < SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);i++)
{
if (SendMessage(m_curlistbox_hwnd, LB_GETSEL, i, 0))
{
int c = edit_columnOrder[i];
for (int j = i;edit_columnOrder[j] != -1;j++)
{
edit_columnOrder[j] = edit_columnOrder[j + 1];
}
SendMessage(m_curlistbox_hwnd, LB_DELETESTRING, i, 0);
i--;
int r = SendMessageW(m_availlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(columnList[c].column_id));
SendMessageW(m_availlistbox_hwnd, LB_SETITEMDATA, r, c);
}
}
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON3), 0);
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON4), 0);
EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON5), 0);
}
break;
case IDC_BUTTON4:
//move column up
{
for (int i = 0;i < (INT)SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);i++)
{
if (i != 0 && (INT)SendMessage(m_curlistbox_hwnd, LB_GETSEL, i, 0))
{
BYTE c = edit_columnOrder[i - 1];
edit_columnOrder[i - 1] = edit_columnOrder[i];
edit_columnOrder[i] = c;
SendMessage(m_curlistbox_hwnd, LB_DELETESTRING, i - 1, 0);
int r = (INT)SendMessageW(m_curlistbox_hwnd, LB_INSERTSTRING, i, (LPARAM)WASABI_API_LNGSTRINGW(columnList[c].column_id));
SendMessage(m_curlistbox_hwnd, LB_SETITEMDATA, r, c);
}
}
}
break;
case IDC_BUTTON5:
//move column down
{
int l = SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);
for (int i = l - 2;i >= 0;i--)
{
if (SendMessage(m_curlistbox_hwnd, LB_GETSEL, i, 0))
{
BYTE c = edit_columnOrder[i + 1];
edit_columnOrder[i + 1] = edit_columnOrder[i];
edit_columnOrder[i] = c;
SendMessage(m_curlistbox_hwnd, LB_DELETESTRING, i + 1, 0);
int r = (INT)SendMessageW(m_curlistbox_hwnd, LB_INSERTSTRING, i, (LPARAM)WASABI_API_LNGSTRINGW(columnList[c].column_id));
SendMessage(m_curlistbox_hwnd, LB_SETITEMDATA, r, c);
}
}
}
break;
case IDOK:
{
HWND hwndList = resultlist.getwnd();
memcpy(columnOrder, edit_columnOrder, sizeof(edit_columnOrder));
if (hwndList)
{
initColumnsHeader(hwndList);
InvalidateRect(hwndList, NULL, TRUE);
UpdateWindow(hwndList);
}
}
case IDCANCEL:
EndDialog(hwndDlg, 0);
break;
}
break;
case WM_DESTROY:
if (NULL != WASABI_API_APP)
{
if (NULL != m_curlistbox_hwnd)
WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(m_curlistbox_hwnd, FALSE);
if (NULL != m_availlistbox_hwnd)
WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(m_availlistbox_hwnd, FALSE);
WASABI_API_APP->app_removeAccelerators(hwndDlg);
}
break;
}
return FALSE;
}
void customizeColumnsDialog(HWND hwndParent)
{
WASABI_API_DIALOGBOXW(IDD_CUSTCOLUMNS, hwndParent, custColumns_dialogProc);
EatKeyboard();
}
bool isMixable(itemRecordW &song)
{
if (!song.filename) return false;
AutoChar charFn(song.filename);
pluginMessage p = {ML_MSG_PDXS_STATUS, (INT_PTR)(char *)charFn, 0, 0};
char *text = (char *)SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (WPARAM) & p, ML_IPC_SEND_PLUGIN_MESSAGE);
// Analyzed/Identified = mixable
return text && (text[0] == 'A' || text[0] == 'I');
}
void AccessingGracenoteHack(int p)
{
if (p == 0)
{
GetDlgItemTextW(m_hwnd, IDC_MEDIASTATUS, oldText, 4096);
SetDlgItemTextW(m_hwnd, IDC_MEDIASTATUS, L"Accessing Gracenote Database");
}
else
{
SetDlgItemTextW(m_hwnd, IDC_MEDIASTATUS, oldText);
}
}