888 lines
25 KiB
C++
888 lines
25 KiB
C++
|
/*
|
||
|
** Copyright (C) 2003 Nullsoft, Inc.
|
||
|
**
|
||
|
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
|
||
|
** liable for any damages arising from the use of this software.
|
||
|
**
|
||
|
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
|
||
|
** alter it and redistribute it freely, subject to the following restrictions:
|
||
|
**
|
||
|
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
|
||
|
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||
|
**
|
||
|
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||
|
**
|
||
|
** 3. This notice may not be removed or altered from any source distribution.
|
||
|
**
|
||
|
*/
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <windowsx.h>
|
||
|
#include <stdio.h>
|
||
|
#include "../ml.h"
|
||
|
#include "resource.h"
|
||
|
#include "../listview.h"
|
||
|
|
||
|
#include "../childwnd.h"
|
||
|
#include "../../winamp/wa_dlg.h"
|
||
|
|
||
|
#include "../itemlist.h"
|
||
|
|
||
|
|
||
|
// configuration section in winamp.ini
|
||
|
#define CONFIG_SEC "ml_ex"
|
||
|
|
||
|
|
||
|
// columns in our big treeview
|
||
|
#define COL_ARTIST 0
|
||
|
#define COL_TITLE 1
|
||
|
#define COL_ALBUM 2
|
||
|
#define COL_LENGTH 3
|
||
|
#define COL_TRACK 4
|
||
|
#define COL_GENRE 5
|
||
|
#define COL_YEAR 6
|
||
|
#define COL_FILENAME 7
|
||
|
|
||
|
// makes a NULL char * an empty string
|
||
|
#define MAKESAFE(x) ((x)?(x):"")
|
||
|
|
||
|
|
||
|
// yes, we could easily use an itemRecord / itemRecordList here instead of 'Song's, but the point of this example
|
||
|
// is to show how to integrate with some other native structure
|
||
|
typedef struct
|
||
|
{
|
||
|
char *artist;
|
||
|
char *title;
|
||
|
char *album;
|
||
|
int songlen; // seconds?
|
||
|
int track_nr;
|
||
|
char *genre;
|
||
|
int year;
|
||
|
char *filename;
|
||
|
} Song;
|
||
|
|
||
|
|
||
|
// our leading crap reduction agent for use with sorting/etc
|
||
|
#define SKIP_THE_AND_WHITESPACE(x) { while (!isalnum(*x) && *x) x++; if (!_strnicmp(x,"the ",4)) x+=4; while (*x == ' ') x++; }
|
||
|
|
||
|
|
||
|
extern winampMediaLibraryPlugin plugin;
|
||
|
static int myParam; // param of our tree item
|
||
|
static C_ItemList m_songs, *m_songs_sorted;
|
||
|
static W_ListView m_list;
|
||
|
static HWND m_hwnd;
|
||
|
static HMENU m_context_menus;
|
||
|
static int m_skinlistview_handle;
|
||
|
|
||
|
|
||
|
void config(HWND parent);
|
||
|
void sortResults();
|
||
|
|
||
|
static void deleteSongPtr(Song *song)
|
||
|
{
|
||
|
free(song->album);
|
||
|
free(song->artist);
|
||
|
free(song->title);
|
||
|
free(song->genre);
|
||
|
free(song->filename);
|
||
|
free(song);
|
||
|
}
|
||
|
|
||
|
static void clearSongList()
|
||
|
{
|
||
|
int i=m_songs.GetSize();
|
||
|
while (i>0)
|
||
|
{
|
||
|
Song *song=(Song *)m_songs.Get(--i);
|
||
|
deleteSongPtr(song);
|
||
|
m_songs.Del(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this doesnt actually alloc the memory for all the strings, just references them (so it is only temporarily valid at best)
|
||
|
void SongsToItemList(itemRecordList *p, int all)
|
||
|
{
|
||
|
if (!m_hwnd) all=1;
|
||
|
|
||
|
p->Alloc=p->Size=0;
|
||
|
p->Items=0;
|
||
|
|
||
|
C_ItemList *list=(C_ItemList *)m_songs_sorted;
|
||
|
if (!list) { list=&m_songs; all=1; }
|
||
|
|
||
|
int x,l=list->GetSize();
|
||
|
for (x = 0 ; x < l; x ++)
|
||
|
{
|
||
|
if (!all && !m_list.GetSelected(x)) continue;
|
||
|
|
||
|
allocRecordList(p,p->Size+1,256);
|
||
|
if (!p->Items) break;
|
||
|
|
||
|
Song *s=(Song *)list->Get(x);
|
||
|
|
||
|
memset(&p->Items[p->Size],0,sizeof(itemRecord));
|
||
|
p->Items[p->Size].album=s->album;
|
||
|
p->Items[p->Size].artist=s->artist;
|
||
|
p->Items[p->Size].title=s->title;
|
||
|
p->Items[p->Size].genre=s->genre;
|
||
|
p->Items[p->Size].filename=s->filename;
|
||
|
p->Items[p->Size].track=s->track_nr;
|
||
|
p->Items[p->Size].year=s->year;
|
||
|
p->Items[p->Size].length=s->songlen;
|
||
|
p->Size++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void playFiles(int enqueue, int all)
|
||
|
{
|
||
|
if (!m_songs_sorted) return;
|
||
|
if (!m_hwnd && !all) return;
|
||
|
|
||
|
itemRecordList obj={0,};
|
||
|
SongsToItemList(&obj,all);
|
||
|
if (obj.Size)
|
||
|
{
|
||
|
mlSendToWinampStruct s={ML_TYPE_ITEMRECORDLIST,(void*)&obj,!!enqueue};
|
||
|
SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&s,ML_IPC_SENDTOWINAMP);
|
||
|
}
|
||
|
free(obj.Items);
|
||
|
}
|
||
|
|
||
|
|
||
|
void addItemListToSongs(itemRecordList *p)
|
||
|
{
|
||
|
if (p) for (int x = 0 ; x < p->Size; x ++)
|
||
|
{
|
||
|
Song *s=(Song *)calloc(1,sizeof(Song));
|
||
|
if (p->Items[x].album) s->album=_strdup(p->Items[x].album);
|
||
|
if (p->Items[x].artist) s->artist=_strdup(p->Items[x].artist);
|
||
|
if (p->Items[x].title) s->title=_strdup(p->Items[x].title);
|
||
|
if (p->Items[x].genre) s->genre=_strdup(p->Items[x].genre);
|
||
|
if (p->Items[x].filename) s->filename=_strdup(p->Items[x].filename);
|
||
|
s->track_nr=p->Items[x].track;
|
||
|
s->year=p->Items[x].year;
|
||
|
s->songlen=p->Items[x].length;
|
||
|
m_songs.Add((void*)s);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
char *conf_file;
|
||
|
int init() {
|
||
|
mlAddTreeItemStruct mla={
|
||
|
0, // if you used 0, it would put it on top level, or ML_TREEVIEW_ID_DEVICES
|
||
|
"Item Cache Example",
|
||
|
1,
|
||
|
};
|
||
|
conf_file=(char*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GETINIFILE); // get winamp.ini name :)
|
||
|
|
||
|
SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&mla,ML_IPC_ADDTREEITEM);
|
||
|
myParam=mla.this_id;
|
||
|
|
||
|
m_context_menus=LoadMenu(plugin.hDllInstance,MAKEINTRESOURCE(IDR_CONTEXTMENUS));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void quit()
|
||
|
{
|
||
|
clearSongList();
|
||
|
}
|
||
|
|
||
|
void loadSongList()
|
||
|
{
|
||
|
clearSongList();
|
||
|
|
||
|
// populate m_songs from whatever source we have
|
||
|
Song *p=(Song *)calloc(sizeof(Song),1);
|
||
|
p->filename = _strdup("http://www.firehose.net/~deadbeef/media/Misc/music/030223%20-%20pervert-in-a-satellite.mp3");
|
||
|
p->album=_strdup("SEP");
|
||
|
p->artist=_strdup("Nullsoft Band");
|
||
|
p->genre=_strdup("Shit");
|
||
|
p->songlen = 666;
|
||
|
p->track_nr=1;
|
||
|
p->title=_strdup("Pervert In A Satellite");
|
||
|
p->year=2003;
|
||
|
|
||
|
m_songs.Add((void*)p);
|
||
|
}
|
||
|
|
||
|
|
||
|
// this is uberadvancedsearchtechnology[tm]
|
||
|
static void parsequicksearch(char *out, char *in) // parses a list into a list of terms that we are searching for
|
||
|
{
|
||
|
int inquotes=0, neednull=0;
|
||
|
while (*in)
|
||
|
{
|
||
|
char 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;
|
||
|
}
|
||
|
|
||
|
static int in_string(char *string, char *substring)
|
||
|
{
|
||
|
if (!string) return 0;
|
||
|
if (!*substring) return 1;
|
||
|
int l=strlen(substring);
|
||
|
while (string[0]) if (!_strnicmp(string++,substring,l)) return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void updateList()
|
||
|
{
|
||
|
if(!m_hwnd) return;
|
||
|
|
||
|
char filterstr[256],filteritems[300];
|
||
|
GetDlgItemText(m_hwnd,IDC_QUICKSEARCH,filterstr,sizeof(filterstr)-1);
|
||
|
parsequicksearch(filteritems,filterstr);
|
||
|
|
||
|
delete m_songs_sorted;
|
||
|
m_songs_sorted=new C_ItemList;
|
||
|
unsigned int totallen=0,filterlen=0,filterval=0;
|
||
|
for(int i=0;i<m_songs.GetSize();i++)
|
||
|
{
|
||
|
Song *s=(Song *)m_songs.Get(i);
|
||
|
totallen+=s->songlen;
|
||
|
char year[32]="";
|
||
|
if (s->year < 5000 && s->year > 0) sprintf(year,"%d",s->year);
|
||
|
char *p=filteritems;
|
||
|
if (*p)
|
||
|
{
|
||
|
while (*p)
|
||
|
{
|
||
|
// search for 'p' in the song
|
||
|
if (!in_string(s->album,p) && !in_string(s->artist,p) && !in_string(s->title,p) && !in_string(s->genre,p) && !in_string(year,p))
|
||
|
break;
|
||
|
|
||
|
p+=strlen(p)+1;
|
||
|
}
|
||
|
if (*p) continue;
|
||
|
}
|
||
|
filterval++;
|
||
|
filterlen+=s->songlen;
|
||
|
m_songs_sorted->Add((void *)s);
|
||
|
}
|
||
|
|
||
|
sortResults();
|
||
|
|
||
|
char tmp[512];
|
||
|
if (m_songs.GetSize() != m_songs_sorted->GetSize())
|
||
|
wsprintf(tmp,"Found: %d items [%d:%02d:%02d]",
|
||
|
m_songs_sorted->GetSize(),filterval,
|
||
|
filterlen/3600,(filterlen/60)%60,filterlen%60);
|
||
|
else
|
||
|
wsprintf(tmp,"%d items [%d:%02d:%02d]",m_songs.GetSize(),totallen/3600,(totallen/60)%60,totallen%60);
|
||
|
|
||
|
SetDlgItemText(m_hwnd,IDC_STATUS,tmp);
|
||
|
}
|
||
|
|
||
|
|
||
|
static ChildWndResizeItem resize_rlist[]={
|
||
|
{IDC_QUICKSEARCH,0x0010},
|
||
|
{IDC_LIST,0x0011},
|
||
|
{IDC_BUTTON_CONFIG,0x0101},
|
||
|
{IDC_STATUS,0x0111}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
int g_sortcol, g_sortdir;
|
||
|
static int STRCMP_NULLOK(const char *pa, const char *pb)
|
||
|
{
|
||
|
if (!pa) pa="";
|
||
|
else SKIP_THE_AND_WHITESPACE(pa)
|
||
|
|
||
|
if (!pb) pb="";
|
||
|
else SKIP_THE_AND_WHITESPACE(pb)
|
||
|
|
||
|
return _stricmp(pa,pb);
|
||
|
}
|
||
|
|
||
|
static int sortFunc(const void *elem1, const void *elem2)
|
||
|
{
|
||
|
Song *a=(Song *)*(void **)elem1;
|
||
|
Song *b=(Song *)*(void **)elem2;
|
||
|
|
||
|
int use_by=g_sortcol;
|
||
|
int use_dir=g_sortdir;
|
||
|
|
||
|
#define RETIFNZ(v) if ((v)<0) return use_dir?1:-1; if ((v)>0) return use_dir?-1:1;
|
||
|
|
||
|
// this might be too slow, but it'd be nice
|
||
|
int x;
|
||
|
for (x = 0; x < 4; x ++)
|
||
|
{
|
||
|
if (use_by == COL_YEAR) // year -> artist -> album -> track
|
||
|
{
|
||
|
int v1=a->year;
|
||
|
int v2=b->year;
|
||
|
if (v1<0)v1=0;
|
||
|
if (v2<0)v2=0;
|
||
|
RETIFNZ(v1-v2)
|
||
|
use_by=COL_ARTIST;
|
||
|
}
|
||
|
else if (use_by == COL_TITLE) // title -> artist -> album -> track
|
||
|
{
|
||
|
int v=STRCMP_NULLOK(a->title,b->title);
|
||
|
RETIFNZ(v)
|
||
|
use_by=COL_ARTIST;
|
||
|
}
|
||
|
else if (use_by == COL_ARTIST) // artist -> album -> track -> title
|
||
|
{
|
||
|
int v=STRCMP_NULLOK(a->artist,b->artist);
|
||
|
RETIFNZ(v)
|
||
|
use_by=COL_ALBUM;
|
||
|
}
|
||
|
else if (use_by == COL_ALBUM) // album -> track -> title -> artist
|
||
|
{
|
||
|
int v=STRCMP_NULLOK(a->album,b->album);
|
||
|
RETIFNZ(v)
|
||
|
use_dir=0;
|
||
|
use_by=COL_TRACK;
|
||
|
}
|
||
|
else if (use_by == COL_GENRE) // genre -> artist -> album -> track
|
||
|
{
|
||
|
int v=STRCMP_NULLOK(a->genre,b->genre);
|
||
|
RETIFNZ(v)
|
||
|
use_by=COL_ARTIST;
|
||
|
}
|
||
|
else if (use_by == COL_TRACK) // track -> title -> artist -> album
|
||
|
{
|
||
|
int v1=a->track_nr;
|
||
|
int v2=b->track_nr;
|
||
|
if (v1<0)v1=0;
|
||
|
if (v2<0)v2=0;
|
||
|
RETIFNZ(v1-v2)
|
||
|
use_by=COL_TITLE;
|
||
|
}
|
||
|
else if (use_by == COL_LENGTH) // length -> artist -> album -> track
|
||
|
{
|
||
|
int v1=a->songlen;
|
||
|
int v2=b->songlen;
|
||
|
if (v1<0)v1=0;
|
||
|
if (v2<0)v2=0;
|
||
|
RETIFNZ(v1-v2)
|
||
|
use_by=COL_ARTIST;
|
||
|
}
|
||
|
else break; // no sort order?
|
||
|
}
|
||
|
#undef RETIFNZ
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void sortResults()
|
||
|
{
|
||
|
if (!m_songs_sorted) return;
|
||
|
qsort(m_songs_sorted->GetAll(),m_songs_sorted->GetSize(),sizeof(void*),sortFunc);
|
||
|
|
||
|
ListView_SetItemCount(m_list.getwnd(),0);
|
||
|
ListView_SetItemCount(m_list.getwnd(),m_songs_sorted->GetSize());
|
||
|
ListView_RedrawItems(m_list.getwnd(),0,m_songs_sorted->GetSize()-1);
|
||
|
}
|
||
|
|
||
|
int (*wad_getColor)(int idx);
|
||
|
int (*wad_handleDialogMsgs)(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||
|
void (*wad_DrawChildWindowBorders)(HWND hwndDlg, int *tab, int tabsize);
|
||
|
void (*cr_init)(HWND hwndDlg, ChildWndResizeItem *list, int num);
|
||
|
void (*cr_resize)(HWND hwndDlg, ChildWndResizeItem *list, int num);
|
||
|
|
||
|
|
||
|
static BOOL CALLBACK dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
|
||
|
{
|
||
|
if (wad_handleDialogMsgs)
|
||
|
{
|
||
|
BOOL a=wad_handleDialogMsgs(hwndDlg,uMsg,wParam,lParam); if (a) return a;
|
||
|
}
|
||
|
switch (uMsg)
|
||
|
{
|
||
|
case WM_DISPLAYCHANGE:
|
||
|
ListView_SetTextColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMFG):RGB(0xff,0xff,0xff));
|
||
|
ListView_SetBkColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00));
|
||
|
ListView_SetTextBkColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00));
|
||
|
m_list.refreshFont();
|
||
|
return 0;
|
||
|
case WM_INITDIALOG:
|
||
|
m_hwnd=hwndDlg;
|
||
|
|
||
|
*(void **)&wad_getColor=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,1,ML_IPC_SKIN_WADLG_GETFUNC);
|
||
|
*(void **)&wad_handleDialogMsgs=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,2,ML_IPC_SKIN_WADLG_GETFUNC);
|
||
|
*(void **)&wad_DrawChildWindowBorders=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,3,ML_IPC_SKIN_WADLG_GETFUNC);
|
||
|
|
||
|
*(void **)&cr_init=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,32,ML_IPC_SKIN_WADLG_GETFUNC);
|
||
|
// woof: *(void **)&cr_resize=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,33,ML_IPC_SKIN_WADLG_GETFUNC);
|
||
|
|
||
|
if (cr_init) cr_init(hwndDlg,resize_rlist,sizeof(resize_rlist)/sizeof(resize_rlist[0]));
|
||
|
|
||
|
m_list.setLibraryParentWnd(plugin.hwndLibraryParent);
|
||
|
m_list.setwnd(GetDlgItem(hwndDlg,IDC_LIST));
|
||
|
m_list.AddCol("Artist",200);
|
||
|
m_list.AddCol("Title",200);
|
||
|
m_list.AddCol("Album",200);
|
||
|
m_list.AddCol("Length",64);
|
||
|
m_list.AddCol("Track #",64);
|
||
|
m_list.AddCol("Genre",100);
|
||
|
m_list.AddCol("Year",64);
|
||
|
m_list.AddCol("Filename",80);
|
||
|
ListView_SetTextColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMFG):RGB(0xff,0xff,0xff));
|
||
|
ListView_SetBkColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00));
|
||
|
ListView_SetTextBkColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00));
|
||
|
|
||
|
g_sortdir=GetPrivateProfileInt(CONFIG_SEC,"sortdir",0,conf_file);
|
||
|
g_sortcol=GetPrivateProfileInt(CONFIG_SEC,"sortcol",g_sortcol,conf_file);
|
||
|
|
||
|
m_skinlistview_handle=SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(int)m_list.getwnd(),ML_IPC_SKIN_LISTVIEW);
|
||
|
|
||
|
SetTimer(hwndDlg,32,50,NULL);
|
||
|
|
||
|
return 0;
|
||
|
case WM_NOTIFY:
|
||
|
{
|
||
|
LPNMHDR l=(LPNMHDR)lParam;
|
||
|
if (l->idFrom==IDC_LIST)
|
||
|
{
|
||
|
if (l->code == NM_DBLCLK)
|
||
|
{
|
||
|
playFiles(!!(GetAsyncKeyState(VK_SHIFT)&0x8000),0);
|
||
|
}
|
||
|
else if (l->code == LVN_BEGINDRAG)
|
||
|
{
|
||
|
SetCapture(hwndDlg);
|
||
|
}
|
||
|
else if (l->code == LVN_ODFINDITEM) // yay we find an item (for kb shortcuts)
|
||
|
{
|
||
|
NMLVFINDITEM *t = (NMLVFINDITEM *)lParam;
|
||
|
int i=t->iStart;
|
||
|
if (i >= m_songs_sorted->GetSize()) i=0;
|
||
|
|
||
|
int cnt=m_songs_sorted->GetSize()-i;
|
||
|
if (t->lvfi.flags & LVFI_WRAP) cnt+=i;
|
||
|
|
||
|
while (cnt-->0)
|
||
|
{
|
||
|
Song *thissong = (Song *)m_songs_sorted->Get(i);
|
||
|
char tmp[128];
|
||
|
char *name=0;
|
||
|
|
||
|
switch (g_sortcol)
|
||
|
{
|
||
|
case COL_ARTIST: name=thissong->artist; break;
|
||
|
case COL_TITLE: name=thissong->title; break;
|
||
|
case COL_ALBUM: name=thissong->album; break;
|
||
|
case COL_LENGTH:
|
||
|
wsprintf(tmp,"%d:%02d",thissong->songlen/60,(thissong->songlen)%60); name=tmp;
|
||
|
break;
|
||
|
case COL_TRACK: if (thissong->track_nr > 0 && thissong->track_nr < 1000) { wsprintf(tmp,"%d",thissong->track_nr); name=tmp; } break;
|
||
|
case COL_GENRE: name=thissong->genre; break;
|
||
|
case COL_YEAR: if (thissong->year < 5000 && thissong->year > 0) { wsprintf(tmp,"%d",thissong->year); name=tmp; } break;
|
||
|
case COL_FILENAME: name=thissong->filename; break;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!name) name="";
|
||
|
else SKIP_THE_AND_WHITESPACE(name)
|
||
|
|
||
|
if (t->lvfi.flags & (4|LVFI_PARTIAL))
|
||
|
{
|
||
|
if (!_strnicmp(name,t->lvfi.psz,strlen(t->lvfi.psz)))
|
||
|
{
|
||
|
SetWindowLong(hwndDlg,DWL_MSGRESULT,i);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
else if (t->lvfi.flags & LVFI_STRING)
|
||
|
{
|
||
|
if (!_stricmp(name,t->lvfi.psz))
|
||
|
{
|
||
|
SetWindowLong(hwndDlg,DWL_MSGRESULT,i);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetWindowLong(hwndDlg,DWL_MSGRESULT,-1);
|
||
|
return 1;
|
||
|
}
|
||
|
if (++i == m_songs_sorted->GetSize()) i=0;
|
||
|
}
|
||
|
SetWindowLong(hwndDlg,DWL_MSGRESULT,-1);
|
||
|
return 1;
|
||
|
}
|
||
|
else if (l->code == LVN_GETDISPINFO)
|
||
|
{
|
||
|
NMLVDISPINFO *lpdi = (NMLVDISPINFO*) lParam;
|
||
|
int item=lpdi->item.iItem;
|
||
|
|
||
|
if (item < 0 || item >= m_songs_sorted->GetSize()) return 0;
|
||
|
|
||
|
Song *thissong = (Song *)m_songs_sorted->Get(item);
|
||
|
|
||
|
if (lpdi->item.mask & (LVIF_TEXT|/*LVIF_IMAGE*/0)) // we can always do images too :)
|
||
|
{
|
||
|
if (lpdi->item.mask & LVIF_TEXT)
|
||
|
{
|
||
|
char tmpbuf[128];
|
||
|
char *nameptr=0;
|
||
|
switch (lpdi->item.iSubItem)
|
||
|
{
|
||
|
case COL_ARTIST: nameptr=thissong->artist; break;
|
||
|
case COL_TITLE: nameptr=thissong->title; break;
|
||
|
case COL_ALBUM: nameptr=thissong->album; break;
|
||
|
case COL_LENGTH:
|
||
|
wsprintf(tmpbuf,"%d:%02d",thissong->songlen/60,(thissong->songlen)%60); nameptr=tmpbuf;
|
||
|
break;
|
||
|
case COL_TRACK: if (thissong->track_nr > 0 && thissong->track_nr < 1000) { wsprintf(tmpbuf,"%d",thissong->track_nr); nameptr=tmpbuf; } break;
|
||
|
case COL_GENRE: nameptr=thissong->genre; break;
|
||
|
case COL_YEAR: if (thissong->year>0 && thissong->year<5000) { wsprintf(tmpbuf,"%d",thissong->year); nameptr=tmpbuf; } break;
|
||
|
case COL_FILENAME: nameptr=thissong->filename; break;
|
||
|
}
|
||
|
if (nameptr) lstrcpyn(lpdi->item.pszText,nameptr,lpdi->item.cchTextMax);
|
||
|
else lpdi->item.pszText[0]=0;
|
||
|
}
|
||
|
// if(lpdi->item.mask & LVIF_IMAGE)
|
||
|
} // bother
|
||
|
return 0;
|
||
|
} // LVN_GETDISPINFO
|
||
|
else if (l->code == LVN_COLUMNCLICK)
|
||
|
{
|
||
|
NMLISTVIEW *p=(NMLISTVIEW*)lParam;
|
||
|
if (p->iSubItem == g_sortcol) g_sortdir=!g_sortdir;
|
||
|
else g_sortcol=p->iSubItem;
|
||
|
|
||
|
char str[32];
|
||
|
sprintf(str,"%d",g_sortdir);
|
||
|
WritePrivateProfileString(CONFIG_SEC,"sortdir",str,conf_file);
|
||
|
sprintf(str,"%d",g_sortcol);
|
||
|
WritePrivateProfileString(CONFIG_SEC,"sortcol",str,conf_file);
|
||
|
|
||
|
sortResults();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case WM_COMMAND:
|
||
|
switch(LOWORD(wParam))
|
||
|
{
|
||
|
case IDC_BUTTON_CONFIG:
|
||
|
config(hwndDlg);
|
||
|
break;
|
||
|
case IDC_QUICKSEARCH:
|
||
|
if (HIWORD(wParam) == EN_CHANGE)
|
||
|
{
|
||
|
KillTimer(hwndDlg,500);
|
||
|
SetTimer(hwndDlg,500,150,NULL);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case WM_TIMER:
|
||
|
if (wParam == 500)
|
||
|
{
|
||
|
KillTimer(hwndDlg,500);
|
||
|
char buf[256];
|
||
|
GetDlgItemText(hwndDlg,IDC_QUICKSEARCH,buf,sizeof(buf));
|
||
|
buf[255]=0;
|
||
|
WritePrivateProfileString(CONFIG_SEC,"lastfilter",buf,conf_file);
|
||
|
updateList();
|
||
|
}
|
||
|
else if (wParam == 32)
|
||
|
{
|
||
|
KillTimer(hwndDlg,32);
|
||
|
if (!m_songs.GetSize()) loadSongList();
|
||
|
char buf[256];
|
||
|
GetPrivateProfileString(CONFIG_SEC,"lastfilter","",buf,sizeof(buf),conf_file);
|
||
|
SetDlgItemText(hwndDlg,IDC_QUICKSEARCH,buf); // automatically updates the list via EN_CHANGE
|
||
|
}
|
||
|
break;
|
||
|
case WM_SIZE:
|
||
|
#if 0 // BP:
|
||
|
if (wParam != SIZE_MINIMIZED)
|
||
|
{
|
||
|
if (cr_resize) cr_resize(hwndDlg,resize_rlist,sizeof(resize_rlist)/sizeof(resize_rlist[0]));
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
case WM_PAINT:
|
||
|
{
|
||
|
if (wad_DrawChildWindowBorders)
|
||
|
{
|
||
|
int tab[] = { IDC_QUICKSEARCH|DCW_SUNKENBORDER, IDC_LIST|DCW_SUNKENBORDER};
|
||
|
wad_DrawChildWindowBorders(hwndDlg,tab,2);
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
case WM_DESTROY:
|
||
|
//clearSongList();
|
||
|
m_hwnd=NULL;
|
||
|
SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,m_skinlistview_handle,ML_IPC_UNSKIN_LISTVIEW);
|
||
|
return 0;
|
||
|
case WM_ML_CHILDIPC:
|
||
|
if (lParam == ML_CHILDIPC_DROPITEM && wParam)
|
||
|
{
|
||
|
mlDropItemStruct *t=(mlDropItemStruct*)wParam;
|
||
|
if (t->type == ML_TYPE_ITEMRECORDLIST) t->result=1;
|
||
|
if (t->data)
|
||
|
{
|
||
|
if (t->type == ML_TYPE_ITEMRECORDLIST) // we got a drag&drop to our window, hot!
|
||
|
{
|
||
|
addItemListToSongs((itemRecordList*)t->data);
|
||
|
updateList();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
case WM_LBUTTONUP:
|
||
|
if (GetCapture() == hwndDlg)
|
||
|
{
|
||
|
ReleaseCapture();
|
||
|
|
||
|
POINT p;
|
||
|
p.x=GET_X_LPARAM(lParam);
|
||
|
p.y=GET_Y_LPARAM(lParam);
|
||
|
ClientToScreen(hwndDlg,&p);
|
||
|
|
||
|
HWND h=WindowFromPoint(p);
|
||
|
if (h != hwndDlg && !IsChild(hwndDlg,h))
|
||
|
{
|
||
|
mlDropItemStruct m={ML_TYPE_ITEMRECORDLIST,NULL,0};
|
||
|
m.p=p;
|
||
|
m.flags=ML_HANDLEDRAG_FLAG_NOCURSOR;
|
||
|
|
||
|
SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDRAG);
|
||
|
|
||
|
if (m.result>0)
|
||
|
{
|
||
|
itemRecordList o={0,};
|
||
|
SongsToItemList(&o,0);
|
||
|
if (o.Size)
|
||
|
{
|
||
|
//fill in this itemCacheObject if you want to provide drag&drop out of the window
|
||
|
m.flags=0;
|
||
|
m.result=0;
|
||
|
m.data=(void*)&o;
|
||
|
SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDROP);
|
||
|
}
|
||
|
free(o.Items);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case WM_MOUSEMOVE:
|
||
|
if (GetCapture()==hwndDlg)
|
||
|
{
|
||
|
POINT p;
|
||
|
p.x=GET_X_LPARAM(lParam);
|
||
|
p.y=GET_Y_LPARAM(lParam);
|
||
|
ClientToScreen(hwndDlg,&p);
|
||
|
mlDropItemStruct m={ML_TYPE_ITEMRECORDLIST,NULL,0};
|
||
|
m.p=p;
|
||
|
HWND h=WindowFromPoint(p);
|
||
|
if (!h || h == hwndDlg || IsChild(hwndDlg,h))
|
||
|
{
|
||
|
SetCursor(LoadCursor(NULL,IDC_NO));
|
||
|
}
|
||
|
else
|
||
|
SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDRAG);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static BOOL CALLBACK config_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
|
||
|
{
|
||
|
switch (uMsg)
|
||
|
{
|
||
|
case WM_INITDIALOG:
|
||
|
return 0;
|
||
|
case WM_COMMAND:
|
||
|
switch (LOWORD(wParam))
|
||
|
{
|
||
|
case IDOK:
|
||
|
// save combo box
|
||
|
case IDCANCEL:
|
||
|
EndDialog(hwndDlg,LOWORD(wParam) == IDOK);
|
||
|
break;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void config(HWND parent)
|
||
|
{
|
||
|
DialogBox(plugin.hDllInstance,MAKEINTRESOURCE(IDD_CONFIG),parent,config_dlgproc);
|
||
|
}
|
||
|
|
||
|
int onTreeItemClick(int param, int action, HWND hwndParent) // if param is not yours, return 0
|
||
|
{
|
||
|
if (action == ML_ACTION_RCLICK)
|
||
|
{
|
||
|
POINT p;
|
||
|
GetCursorPos(&p);
|
||
|
int r=TrackPopupMenu(GetSubMenu(m_context_menus,0),TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTBUTTON|TPM_NONOTIFY,p.x,p.y,0,hwndParent,NULL);
|
||
|
switch (r)
|
||
|
{
|
||
|
case ID_ABOUT:
|
||
|
MessageBox(hwndParent,"ml_ex!!!","About ml_ex!!!",MB_OK);
|
||
|
break;
|
||
|
case ID_CONFIG:
|
||
|
config(hwndParent);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
int onTreeItemDropTarget(int param, int type, void *obj)
|
||
|
{
|
||
|
if (type != ML_TYPE_ITEMRECORDLIST) return -1;
|
||
|
|
||
|
if (!obj) return 1;
|
||
|
|
||
|
// do somethihng with the itemCache object. do not free it however, since the caller owns it
|
||
|
addItemListToSongs((itemRecordList*)obj);
|
||
|
|
||
|
updateList();
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int onTreeItemDrag(int param, POINT p, int *type)
|
||
|
{
|
||
|
HWND h=WindowFromPoint(p);
|
||
|
if (h && (h == m_hwnd || IsChild(m_hwnd,h))) return -1; // prevent from dropping into ourselves
|
||
|
|
||
|
|
||
|
// if we wanted to be able to drag&drop our tree item to other people, we'd
|
||
|
// return 1 and set type to ML_TYPE_ITEMRECORDLIST or ML_TYPE_FILENAMES etc.
|
||
|
|
||
|
// *type = ML_TYPE_ITEMRECORDLIST;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int onTreeItemDrop(int param, POINT p) // you should send the appropriate ML_IPC_HANDLEDROP if you support it
|
||
|
{
|
||
|
HWND h=WindowFromPoint(p);
|
||
|
if (h && (h == m_hwnd || IsChild(m_hwnd,h))) return -1; // prevent from dropping into ourselves
|
||
|
|
||
|
// if we wanted to be able to drag&drop our tree item to other people, we'd
|
||
|
// create an itemCacheObject or a doublenull terminated list (depending on what we want),
|
||
|
// and send it back to the media library so it can route it to the appropriate destination:
|
||
|
//
|
||
|
// itemCacheObject o={0,};
|
||
|
// fillInMyObject(&o);
|
||
|
// mlDropItemStruct m={0,};
|
||
|
// m.type = ML_TYPE_ITEMRECORDLIST;
|
||
|
// m.data = (void*)&o;
|
||
|
// m.p=p;
|
||
|
// SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDROP);
|
||
|
// freeMyObject(&o);
|
||
|
|
||
|
// or this:
|
||
|
itemRecordList o={0,};
|
||
|
SongsToItemList(&o,1);
|
||
|
if (o.Size)
|
||
|
{
|
||
|
mlDropItemStruct m={0,};
|
||
|
m.type = ML_TYPE_ITEMRECORDLIST;
|
||
|
m.data = (void*)&o;
|
||
|
m.p=p;
|
||
|
SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDROP);
|
||
|
}
|
||
|
free(o.Items);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int PluginMessageProc(int message_type, int param1, int param2, int param3)
|
||
|
{
|
||
|
// check for any global messages here
|
||
|
|
||
|
if (message_type >= ML_MSG_TREE_BEGIN && message_type <= ML_MSG_TREE_END)
|
||
|
{
|
||
|
if (param1 != myParam) return 0;
|
||
|
// local messages for a tree item
|
||
|
|
||
|
switch (message_type)
|
||
|
{
|
||
|
case ML_MSG_TREE_ONCREATEVIEW:
|
||
|
return (int)CreateDialog(plugin.hDllInstance,MAKEINTRESOURCE(IDD_VIEW_EX),(HWND)param2,dlgproc);
|
||
|
case ML_MSG_TREE_ONCLICK:
|
||
|
return onTreeItemClick(param1,param2,(HWND)param3);
|
||
|
case ML_MSG_TREE_ONDROPTARGET:
|
||
|
return onTreeItemDropTarget(param1,param2,(void*)param3);
|
||
|
case ML_MSG_TREE_ONDRAG:
|
||
|
return onTreeItemDrag(param1,*(POINT*)param2,(int*)param3);
|
||
|
case ML_MSG_TREE_ONDROP:
|
||
|
return onTreeItemDrop(param1,*(POINT*)param2);
|
||
|
}
|
||
|
}
|
||
|
else if (message_type == ML_MSG_ONSENDTOBUILD)
|
||
|
{
|
||
|
if (param1 == ML_TYPE_ITEMRECORDLIST)
|
||
|
{
|
||
|
mlAddToSendToStruct s;
|
||
|
s.context=param2;
|
||
|
s.desc="ItemCacheEx";
|
||
|
s.user32=(int)PluginMessageProc;
|
||
|
SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&s,ML_IPC_ADDTOSENDTO);
|
||
|
}
|
||
|
}
|
||
|
else if (message_type == ML_MSG_ONSENDTOSELECT)
|
||
|
{
|
||
|
if (param1 == ML_TYPE_ITEMRECORDLIST && param2 && param3 == (int)PluginMessageProc)
|
||
|
{
|
||
|
addItemListToSongs((itemRecordList*)param2);
|
||
|
updateList();
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
winampMediaLibraryPlugin plugin =
|
||
|
{
|
||
|
MLHDR_VER,
|
||
|
"ml_ex v0.1",
|
||
|
init,
|
||
|
quit,
|
||
|
PluginMessageProc,
|
||
|
};
|
||
|
|
||
|
extern "C" {
|
||
|
|
||
|
__declspec( dllexport ) winampMediaLibraryPlugin * winampGetMediaLibraryPlugin()
|
||
|
{
|
||
|
return &plugin;
|
||
|
}
|
||
|
|
||
|
};
|