#include "../gracenote/gracenote.h" #include "api__ml_plg.h" #include #include "resource.h" #include "../../General/gen_ml/ml.h" #include "../winamp/wa_ipc.h" #include "../Agave/Language/api_language.h" #include "../nu/MediaLibraryInterface.h" #include "../nu/ComboBox.h" #include "main.h" #include #include #include "playlist.h" #include #include "IDScanner.h" //#include "../Wasabi/bfc/util/timefmt.h" //#include #include // should be last HWND hwndDlgCurrent = 0; bool optionsVisible = true; bool isGenerating = false; int originalWidth = 877; //#define DIALOG_WIDTH_OPTIONS 877 // use originalWidth instead #define DIALOG_WIDTH_NO_OPTIONS 610 #define DIALOG_HIDDEN_COLUMN_ID 4 // Pass in 0 for width or height in order to preserve its current dimension void SizeWindow(HWND hwnd, int width, int height) { if (width == 0 || height == 0) // Preserve only if one of the items is 0 { RECT windowRect; GetWindowRect(hwnd, &windowRect); if (width == 0) // Preserve the width width = windowRect.right - windowRect.left; if (height == 0) // Preserve the height height = windowRect.bottom - windowRect.top; } SetWindowPos(hwnd, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } void ClientResize(HWND hWnd, int nWidth, int nHeight) { RECT rcClient, rcWind; POINT ptDiff; GetClientRect(hWnd, &rcClient); GetWindowRect(hWnd, &rcWind); ptDiff.x = (rcWind.right - rcWind.left) - rcClient.right; ptDiff.y = (rcWind.bottom - rcWind.top) - rcClient.bottom; MoveWindow(hWnd,rcWind.left, rcWind.top, nWidth + ptDiff.x, nHeight + ptDiff.y, TRUE); } void SetMarqueeProgress(bool isMarquee) { HWND hwndProgress = GetDlgItem(hwndDlgCurrent,IDC_PROGRESS_GENERATE); static long state = GetWindowLongW(hwndProgress, GWL_STYLE); // Capture the initial state of the progress bar if (isMarquee) // Set it to marquee style { SetWindowLong (hwndProgress, GWL_STYLE, GetWindowLong(hwndProgress, GWL_STYLE) | PBS_MARQUEE); //SendMessage(hwndProgress, PBM_SETMARQUEE, 1, 10); SendMessage(hwndProgress, PBM_SETMARQUEE, 1, 30); } else // Restore the normal progress bar { SetWindowLong (hwndProgress, GWL_STYLE, state); //SendMessage(hwndProgress, WM_PAINT, 0, 0); InvalidateRect(hwndProgress, 0, 1); // Force a repaint of the marquee after turning it off because there are stuck pixels in XP } } // Sets the query check state as well as enabling all the controls involved void SetMLQueryCheckState(HWND hwndDlg, unsigned int checked) { // Get the handles to all the child controls we want to enable / disable HWND hwndButtonMlQuery = GetDlgItem(hwndDlg, IDC_BUTTON_ML_QUERY); HWND hwndEditMlQuery = GetDlgItem(hwndDlg, IDC_EDIT_ML_QUERY); HWND hwndButtonRestoreQueryDefault = GetDlgItem(hwndDlg, IDC_BUTTON_RESTORE_QUERY_DEFAULT); if (checked) // enable all the controls related to ML query { CheckDlgButton(hwndDlg, IDC_CHECK_ML_QUERY, TRUE); EnableWindow (hwndButtonMlQuery, TRUE ); EnableWindow (hwndEditMlQuery, TRUE ); EnableWindow (hwndButtonRestoreQueryDefault, TRUE ); useMLQuery = true; } else // disable all the controls related to ML query { CheckDlgButton(hwndDlg, IDC_CHECK_ML_QUERY, FALSE); EnableWindow (hwndButtonMlQuery, FALSE ); EnableWindow (hwndEditMlQuery, FALSE ); EnableWindow (hwndButtonRestoreQueryDefault, FALSE ); useMLQuery = false; } } void SetButtonsEnabledState(bool enabled_flag) { int itemIds[] = { IDC_BUTTON_PLAY_NOW, IDC_BUTTON_ENQUEUE_NOW, IDC_BUTTON_SAVEAS, IDC_BUTTON_REGENERATE }; for(int i = 0; i < sizeof(itemIds) / sizeof(itemIds[0]); i++) EnableWindow(GetDlgItem(hwndDlgCurrent, itemIds[i]), enabled_flag); } void ToggleOptions(bool reset) { if (reset) optionsVisible = false; else optionsVisible = !optionsVisible; // Toggle the options visible state // to resolve tabbing issues when in the collapsed // state we need to disable some of the controls (dro) int itemIds[] = { IDC_RADIO_PLAYLIST_ITEMS, IDC_RADIO_PLAYLIST_LENGTH, IDC_RADIO_PLAYLIST_SIZE, IDC_COMBO_LENGTH, IDC_CHECK_USE_SEED, IDC_CHECK_MULTIPLE_ARTISTS, IDC_CHECK_MULTIPLE_ALBUMS, IDC_CHECK_ML_QUERY, IDC_EDIT_ML_QUERY, IDC_BUTTON_ML_QUERY, IDC_BUTTON_RESTORE_QUERY_DEFAULT }; for(int i = 0; i < sizeof(itemIds) / sizeof(itemIds[0]); i++) EnableWindow(GetDlgItem(hwndDlgCurrent, itemIds[i]), optionsVisible); SetMLQueryCheckState(hwndDlgCurrent, useMLQuery); if (optionsVisible) { SizeWindow(hwndDlgCurrent, originalWidth, 0); // Resize the window to the correct width SetDlgItemText(hwndDlgCurrent, IDC_BUTTON_OPTIONS, WASABI_API_LNGSTRINGW(IDS_OPTIONS)); // Set the dialog button to show the correct options mode } else { SizeWindow(hwndDlgCurrent, DIALOG_WIDTH_NO_OPTIONS, 0); SetDlgItemText(hwndDlgCurrent, IDC_BUTTON_OPTIONS, WASABI_API_LNGSTRINGW(IDS_NO_OPTIONS)); // Set the dialog button to show the correct options mode } } // ToDo: Make this more human readable void FormatToMinutesAndSeconds(const int lengthInSeconds, wchar_t *buff, const size_t cchBuf) { //StringCchPrintfW(buff, cchBuf, L"%d:%02d", lengthInSeconds / 60, lengthInSeconds % 60); int total_length_s = lengthInSeconds; int uncert = 0; // Minutes and seconds if (total_length_s < 60*60) StringCchPrintfW(buff, 64, L"%s%u:%02u", uncert ? L"~" : L"", total_length_s / 60, total_length_s % 60); // Hours minutes and seconds else if (total_length_s < 60*60*24) StringCchPrintfW(buff, 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); // Calculate days total_length_s -= total_days * 60 * 60 * 24; // Remove days from length StringCchPrintfW(buff, 64, //WASABI_API_LNGSTRINGW(IDS_LENGTH_DURATION_STRING), L"%s%u %s+%u:%02u:%02u", ((uncert) ? L"~" : L""), total_days, // Approximate WASABI_API_LNGSTRINGW_BUF(total_days == 1 ? IDS_DAY : IDS_DAYS, days, 16), // Days total_length_s / 60 / 60, // Hours (total_length_s / 60) % 60, // Minutes total_length_s % 60); // Seconds } } // Refreashed the statistics about the generated playlist void UpdateStats(void) { const int MAX_STATS = 512; wchar_t stats[MAX_STATS] = {0}; wchar_t lengthText[MAX_STATS] = {0}; wchar_t sizeText[MAX_STATS] = {0}; int count = (int)currentPlaylist.GetNumItems(); uint64_t length = currentPlaylist.GetPlaylistLengthMilliseconds(); uint64_t size = currentPlaylist.GetPlaylistSizeBytes(); // Add the seed stats? if (useSeed == TRUE) { count += (int)seedPlaylist.GetNumItems(); length += seedPlaylist.GetPlaylistLengthMilliseconds(); size += seedPlaylist.GetPlaylistSizeBytes(); } FormatToMinutesAndSeconds((int)(length / 1000), lengthText, MAX_STATS); // / 1000 because we have it in milliseconds and not seconds StrFormatByteSizeW(size, sizeText, MAX_STATS); // Get the human readable formatting for filesize StringCchPrintf(stats, MAX_STATS, WASABI_API_LNGSTRINGW(IDS_STATS), count, lengthText, sizeText); SetDlgItemText(hwndDlgCurrent, IDC_STATIC_STATS, stats); // Set the dialog button to show the correct options mode } // Update the progress to the current static void doProgressBar(HWND h, int x, int t=-1) { h = GetDlgItem(h,IDC_PROGRESS_GENERATE); if(t!=-1 && SendMessage(h,PBM_GETRANGE,0,0) != t) SendMessage(h,PBM_SETRANGE32,0,t); SendMessage(h,PBM_SETPOS,x,0); } // Update the status while id scanner is active static void FillStatus(HWND hwndDlg) { long state, track, tracks; if (scanner.GetStatus(&state, &track, &tracks)) { static int x=0; wchar_t *ticker; switch (x++) { case 0: ticker=L""; break; case 1: ticker=L"."; break; case 2: ticker=L".."; break; default: ticker=L"..."; } x%=4; wchar_t status[1024]=L""; switch (state) { case IDScanner::STATE_ERROR: WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_INITIALIZING,status,1024); KillTimer(hwndDlg, 1); doProgressBar(hwndDlg,0,1); ShowErrorDlg(hwndDlg); break; case IDScanner::STATE_IDLE: WASABI_API_LNGSTRINGW_BUF(IDS_IDLE,status,1024); doProgressBar(hwndDlg,0); break; case IDScanner::STATE_INITIALIZING: StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_INITIALIZING), ticker); doProgressBar(hwndDlg,0); break; case IDScanner::STATE_SYNC: StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_SYNC), track, tracks, ticker); doProgressBar(hwndDlg,track,tracks); break; case IDScanner::STATE_METADATA: StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_METADATA), track, tracks, ticker); doProgressBar(hwndDlg,track,tracks); break; case IDScanner::STATE_MUSICID: StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_MUSICID), track, tracks, ticker); doProgressBar(hwndDlg,track,tracks); break; case IDScanner::STATE_DONE: if (!isGenerating) // Only set the done state if the gneeration has not started yet { WASABI_API_LNGSTRINGW_BUF(IDS_DONE,status,1024); doProgressBar(hwndDlg,0,0); // Turn off the progress bar to 0 } KillTimer(hwndDlg, 1); break; } if (!isGenerating) // Only set the done state if the gneeration has not started yet { SetDlgItemTextW(hwndDlg, IDC_STATIC_PROGRESS_STATE, status); } } } // Function calls appropriate items when a generation is requested void Regenerate(HWND hwndDlg) { SendMessage(GetDlgItem(hwndDlgCurrent, IDC_LIST_RESULTS2),LVM_DELETEALLITEMS,0,0); // Clear the listview of all playlist items SetTimer(hwndDlg, 1, 500, 0); // Set the progress timer for the scanner StartScan(); MoreLikeTheseSongs(&seedPlaylist); } // Function draws in colors for the seed listview items LRESULT CustomDrawListViewColors(LPARAM lParam) { LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam; switch(lplvcd->nmcd.dwDrawStage) { case CDDS_PREPAINT : //Before the paint cycle begins return CDRF_NOTIFYITEMDRAW; //request notifications for individual listview items case CDDS_ITEMPREPAINT: //Before an item is drawn if (lplvcd->nmcd.dwItemSpec < seedPlaylist.entries.size()) // Check how many seeds we have, thats how we know which rows in the view to color paint { if (useSeed == TRUE) { lplvcd->clrText = RGB(0,0,255); // Color seed tracks blue } else { lplvcd->clrText = RGB(100,100,100); // Color seed tracks a faded grey } } return CDRF_NEWFONT; break; } return CDRF_DODEFAULT; } int SetRadioControlsState(HWND hwndDlg) { // Set the radio buttons for playlist length type, items or minutes if(plLengthType == PL_ITEMS) { CheckDlgButton(hwndDlg,IDC_RADIO_PLAYLIST_ITEMS,TRUE); SendMessage(hwndDlg, WM_COMMAND, IDC_RADIO_PLAYLIST_ITEMS, 0); SetPlLengthTypeComboToItems(hwndDlg, plItems); } else if(plLengthType == PL_MINUTES) { CheckDlgButton(hwndDlg,IDC_RADIO_PLAYLIST_LENGTH,TRUE); SendMessage(hwndDlg, WM_COMMAND, IDC_RADIO_PLAYLIST_LENGTH, 0); SetPlLengthTypeComboToMinutes(hwndDlg, plMinutes); } else if(plLengthType == PL_MEGABYTES) { CheckDlgButton(hwndDlg,IDC_RADIO_PLAYLIST_SIZE,TRUE); SendMessage(hwndDlg, WM_COMMAND, IDC_RADIO_PLAYLIST_SIZE, 0); SetPlLengthTypeComboToMegabytes(hwndDlg, plMegabytes); } return 0; } // Update the combo box contents depending on which lengthType we are using int UpdateComboLength(HWND hwndDlg) { const int BUF_SIZE = 32; ComboBox combo(hwndDlg, IDC_COMBO_LENGTH); wchar_t buf[BUF_SIZE] = {0}; combo.GetEditText(buf, BUF_SIZE); switch(plLengthType) { case PL_ITEMS: plItems = _wtoi(buf); return 0; break; case PL_MINUTES: plMinutes = _wtoi(buf); return 0; break; case PL_MEGABYTES: plMegabytes = _wtoi(buf); return 0; break; } return 1; } LRESULT tab_fix_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg == WM_CHAR) { if(wParam == VK_TAB) { SendMessage(hwndDlgCurrent, WM_NEXTDLGCTL, (GetAsyncKeyState(VK_SHIFT)&0x8000), FALSE); return TRUE; } } return CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"tab_fix_proc"), hwndDlg, uMsg, wParam, lParam); } // this will prevent the hidden column (for making the headers work better) // from appearing as sizeable / disabled (as it effectively is) LRESULT header_block_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg == WM_SETCURSOR) { HDHITTESTINFO hitTest; GetCursorPos(&hitTest.pt); ScreenToClient(hwndDlg, &hitTest.pt); hitTest.flags = hitTest.iItem = 0; SendMessage(hwndDlg, HDM_HITTEST, FALSE, (LPARAM)&hitTest); if(hitTest.iItem == DIALOG_HIDDEN_COLUMN_ID || hitTest.iItem == -1) { SetCursor(LoadCursor(NULL, IDC_ARROW)); return TRUE; } } return CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"header_block_proc"), hwndDlg, uMsg, wParam, lParam); } INT_PTR CALLBACK GenerateProcedure(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { RECT r; GetWindowRect(hwndDlg, &r); originalWidth = r.right - r.left; // bit hacky but it will resolve issues with tabbing and the combobox in a // dropdown style still not 100% sure why it's failing to work though (dro) HWND combobox = GetWindow(GetDlgItem(hwndDlg, IDC_COMBO_LENGTH), GW_CHILD); SetPropW(combobox, L"tab_fix_proc",(HANDLE)SetWindowLongPtrW(combobox, GWLP_WNDPROC, (LONG_PTR)tab_fix_proc)); hwndDlgCurrent = hwndDlg; // Set the global so that we have a window open // this will make sure that we've got thr aacplus logo shown even when using a localised version SendDlgItemMessage(hwndDlg,IDC_LOGO,STM_SETIMAGE,IMAGE_BITMAP, (LPARAM)LoadImage(plugin.hDllInstance,MAKEINTRESOURCE(IDB_GN_LOGO),IMAGE_BITMAP,0,0,LR_SHARED)); BoldStatusText(GetDlgItem(hwndDlg, IDC_STATIC_PROGRESS_STATE) ); SetRadioControlsState(hwndDlg); // Set the playlist length state if(multipleArtists) CheckDlgButton(hwndDlg,IDC_CHECK_MULTIPLE_ARTISTS,TRUE); if(multipleAlbums) CheckDlgButton(hwndDlg,IDC_CHECK_MULTIPLE_ALBUMS,TRUE); if(useSeed) CheckDlgButton(hwndDlg,IDC_CHECK_USE_SEED,TRUE); // Set up the colums for the playlist listing #define ListView_InsertColumnW(hwnd, iCol, pcol) \ (int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol)) //SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam); // Add the columns to the listbox HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST_RESULTS2); ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP); LVCOLUMNW lvc = {0, }; lvc.mask = LVCF_TEXT|LVCF_WIDTH; lvc.pszText = WASABI_API_LNGSTRINGW(IDS_TITLE); // Initialize the columns of the listview lvc.cx = 160; ListView_InsertColumnW(hwndlist, 0, &lvc); lvc.pszText = WASABI_API_LNGSTRINGW(IDS_LENGTH); lvc.cx = 80; ListView_InsertColumnW(hwndlist, 1, &lvc); lvc.pszText = WASABI_API_LNGSTRINGW(IDS_SIZE); lvc.cx = 80; ListView_InsertColumnW(hwndlist, 2, &lvc); lvc.pszText = WASABI_API_LNGSTRINGW(IDS_SEED); lvc.cx = 80; ListView_InsertColumnW(hwndlist, 3, &lvc); lvc.pszText = 0; lvc.cx = 0; ListView_InsertColumnW(hwndlist, DIALOG_HIDDEN_COLUMN_ID, &lvc); // Autosize the columns taking the header into consideration ListView_SetColumnWidth(hwndlist,0,LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hwndlist,2,LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hwndlist,3,LVSCW_AUTOSIZE_USEHEADER); HWND hwndListHeader = ListView_GetHeader(hwndlist); SetPropW(hwndListHeader, L"header_block_proc",(HANDLE)SetWindowLongPtrW(hwndListHeader, GWLP_WNDPROC, (LONG_PTR)header_block_proc)); // Background color for highlighting seed tracks. //hbrBkcolor = CreateSolidBrush ( RGB(255,0,0) ); BoldStatusText(GetDlgItem(hwndDlg, IDC_STATIC_STATS) ); // Populate the query textbox SetDlgItemTextW(hwndDlg, IDC_EDIT_ML_QUERY, mlQuery); // Set the text for the query // Disable the regenerate button because we will be scanning the library and generating on initialization //EnableWindow (GetDlgItem(hwndDlgCurrent, IDC_BUTTON_REGENERATE), FALSE ); // This is for initialization SetButtonsEnabledState(false); // Set up the window with the options hidden ToggleOptions(true); // Show the window since we are modeless POINT pt = {(LONG)GetPrivateProfileInt(L"ml_plg", L"generate_x",-1, mediaLibrary.GetWinampIniW()), (LONG)GetPrivateProfileInt(L"ml_plg", L"generate_y",-1, mediaLibrary.GetWinampIniW())}; if (!windowOffScreen(hwndDlg, pt)) SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); else ShowWindow(hwndDlg, SW_SHOW); Regenerate(hwndDlg); if (WASABI_API_APP) // Add direct mousewheel support for the main tracklist view of seed and generated tracks WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(GetDlgItem(hwndDlg, IDC_LIST_RESULTS2), TRUE); return TRUE; } break; // Trying to change the background color of seed tracks here /*case WM_CTLCOLORSTATIC: { HDC hdc = (HDC) wParam; HWND hwndStatic = (HWND) lParam; if ( hwndStatic == GetDlgItem ( hwndDlg, IDC_LIST_RESULTS2 )) { SetBkMode ( hdc, TRANSPARENT ); return (LRESULT) hbrBkcolor; } } break;*/ case WM_TIMER: FillStatus(hwndDlg); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: { RECT rect = {0}; GetWindowRect(hwndDlg, &rect); char buf[16] = {0}; StringCchPrintfA(buf, 16, "%d", rect.left); WritePrivateProfileStringA("ml_plg", "generate_x", buf, mediaLibrary.GetWinampIni()); StringCchPrintfA(buf, 16, "%d", rect.top); WritePrivateProfileStringA("ml_plg", "generate_y", buf, mediaLibrary.GetWinampIni()); EndDialog(hwndDlg, 0); hwndDlgCurrent = 0; // Set to null so new instance can be opened WriteSettingsToIni(hwndDlg); // We need to free up our seed tracks because we no longer require them seedPlaylist.Clear(); // Clear the global seed list } break; case IDC_BUTTON_CANCEL: SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); break; case IDC_BUTTON_REGENERATE: Regenerate(hwndDlg); break; case IDC_RADIO_PLAYLIST_ITEMS: SetDlgItemText(hwndDlg, IDC_LENGTH_TYPE, WASABI_API_LNGSTRINGW(IDS_ITEMS)); plLengthType = PL_ITEMS; // Set to # of items SetPlLengthTypeComboToItems(hwndDlg, plItems); break; case IDC_RADIO_PLAYLIST_LENGTH: SetDlgItemText(hwndDlg, IDC_LENGTH_TYPE, WASABI_API_LNGSTRINGW(IDS_MINUTES)); plLengthType = PL_MINUTES; // Set to minutes SetPlLengthTypeComboToMinutes(hwndDlg, plMinutes); break; case IDC_RADIO_PLAYLIST_SIZE: SetDlgItemText(hwndDlg, IDC_LENGTH_TYPE, WASABI_API_LNGSTRINGW(IDS_MEGABYTES)); plLengthType = PL_MEGABYTES; // Set to megabytes SetPlLengthTypeComboToMegabytes(hwndDlg, plMegabytes); break; case IDC_COMBO_LENGTH: { UpdateComboLength(hwndDlg); } break; case IDC_BUTTON_OPTIONS: ToggleOptions(false); break; case IDC_BUTTON_PLAY_NOW: playPlaylist(currentPlaylist, false, 0, /*seed,*/ useSeed); // Play the current playlist taking the seed track into consideration SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); // Close up the dialog because we are done break; case IDC_BUTTON_ENQUEUE_NOW: playPlaylist(currentPlaylist, true, 0, /*seed,*/ useSeed); // Enqueue the current playlist taking the seed track into consideration SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); // Close up the dialog because we are done break; case IDC_BUTTON_SAVEAS: { // ToDo spawn a dialog to save the current playlist as a ML playlist int save_result = WASABI_API_DIALOGBOXPARAM(IDD_ADD_PLAYLIST, hwndDlg, AddPlaylistDialogProc, (LPARAM)&seedPlaylist/*seed*/); if (save_result == IDOK) // If the user accepted that playlist dialog then go ahead and close up everything { SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); // Close up the dialog because we are done } } break; case IDC_BUTTON_ML_QUERY: { char temp[1024] = {0}; GetDlgItemTextA(hwndDlg, IDC_EDIT_ML_QUERY, temp, sizeof(temp) - 1); // Retreive the current custom ML query ml_editview meq = {hwndDlg, (temp[0] == 0) ? DEFAULT_ML_QUERY : temp, "ML Query", -1}; // Create the editview meq.name = WASABI_API_LNGSTRING(IDS_ML_QUERY); // Set a custom title if(!(int)SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (LPARAM)&meq, ML_IPC_EDITVIEW)) return 0; // Spawn the edit view SetDlgItemTextA(hwndDlg, IDC_EDIT_ML_QUERY, meq.query); // Set the text back to the edited query } break; case IDC_BUTTON_RESTORE_QUERY_DEFAULT: SetDlgItemTextW(hwndDlg, IDC_EDIT_ML_QUERY, _T(DEFAULT_ML_QUERY)); // Set the text back to the edited query break; case IDC_CHECK_USE_SEED: useSeed = IsDlgButtonChecked(hwndDlg,IDC_CHECK_USE_SEED); UpdateStats(); // Update the track stats, because the seed status can change them RedrawWindow(GetDlgItem(hwndDlg,IDC_LIST_RESULTS2), 0, 0, RDW_INVALIDATE); // Refresh the colors in the list view break; case IDC_CHECK_MULTIPLE_ARTISTS: // Set the multiple tracks per artist option when checked multipleArtists = IsDlgButtonChecked(hwndDlg, IDC_CHECK_MULTIPLE_ARTISTS); break; case IDC_CHECK_MULTIPLE_ALBUMS: // Set the multiple tracks per album option when checked multipleAlbums = IsDlgButtonChecked(hwndDlg, IDC_CHECK_MULTIPLE_ALBUMS); break; case IDC_CHECK_ML_QUERY: SetMLQueryCheckState(hwndDlg, IsDlgButtonChecked(hwndDlg, IDC_CHECK_ML_QUERY)); break; case IDC_EDIT_ML_QUERY: if (HIWORD(wParam) == EN_CHANGE) { GetDlgItemTextW(hwndDlg, IDC_EDIT_ML_QUERY, mlQuery, MAX_ML_QUERY_SIZE); // Set the text back to the edited query break; } } break; case WM_NOTIFY: if(((LPNMHDR)lParam)->code == HDN_BEGINTRACKW || ((LPNMHDR)lParam)->code == HDN_BEGINTRACKA || ((LPNMHDR)lParam)->code == HDN_ITEMCHANGINGW || ((LPNMHDR)lParam)->code == HDN_ITEMCHANGINGA) { LPNMHEADER pNMHeader = (LPNMHEADER)lParam; if(pNMHeader->iItem == DIALOG_HIDDEN_COLUMN_ID) { SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)TRUE); return TRUE; } } else if(((LPNMHDR)lParam)->code == NM_CUSTOMDRAW) // Notify for List View custom redraw (seed track colors) { #if defined(_WIN64) SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG)CustomDrawListViewColors(lParam)); #else SetWindowLong(hwndDlg, DWL_MSGRESULT, (LONG)CustomDrawListViewColors(lParam)); #endif return TRUE; } { const int controls[] = { IDC_LIST_RESULTS2, }; if (WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, msg, wParam, lParam, controls, ARRAYSIZE(controls)) != FALSE) { return TRUE; } } break; case WM_DESTROY: { if (WASABI_API_APP) WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(GetDlgItem(hwndDlg, IDC_LIST_RESULTS2), FALSE); } break; } return 0; } int AddResultListItem(Playlist *playlist, int index, int position, bool seed) { const unsigned int MAX_INFO = 256; wchar_t filename[MAX_INFO] = {0}; wchar_t info[MAX_INFO] = {0}; wchar_t *seedText = 0; LVITEMW lvi={LVIF_TEXT, position, 0}; playlist->GetItem(index,filename,MAX_INFO); // Add the title column playlist->GetItemTitle(index, info, MAX_INFO); lvi.pszText=info; lvi.cchTextMax=sizeof(info) / sizeof(*info); SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_INSERTITEMW,0,(LPARAM)&lvi); // Add the length column int length = playlist->GetItemLengthMilliseconds(index); if (length <= 0) StringCchCopyW(info, MAX_INFO, WASABI_API_LNGSTRINGW(IDS_UNKNOWN)); else FormatToMinutesAndSeconds(length / 1000, info, MAX_INFO); // / 1000 because we have it in milliseconds and not seconds lvi.pszText=info; lvi.cchTextMax=sizeof(info) / sizeof(*info); lvi.iSubItem = 1; SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_SETITEMW,0,(LPARAM)&lvi); // Add the size column int size = playlist->GetItemSizeBytes(index); if (size <= 0) StringCchCopyW(info, MAX_INFO, WASABI_API_LNGSTRINGW(IDS_UNKNOWN)); else StrFormatByteSizeW(size, info, MAX_INFO); lvi.pszText=info; lvi.cchTextMax=sizeof(info) / sizeof(*info); lvi.iSubItem = 2; SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_SETITEMW,0,(LPARAM)&lvi); // Add the seed track column if (seed == true) seedText = WASABI_API_LNGSTRINGW(IDS_YES); else seedText = WASABI_API_LNGSTRINGW(IDS_NO); lvi.pszText=seedText; lvi.cchTextMax=sizeof(seedText) / sizeof(*seedText); lvi.iSubItem = 3; SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_SETITEMW,0,(LPARAM)&lvi); return 0; } void CantPopulateResults(void) { wchar_t message[256] = {0}; WASABI_API_LNGSTRINGW_BUF(IDS_EXCUSE_ME, message, 256); MessageBoxW(hwndDlgCurrent, message, WASABI_API_LNGSTRINGW(IDS_NULLSOFT_PLAYLIST_GENERATOR), MB_OK | MB_ICONINFORMATION); } void PopulateResults(Playlist *playlist) { // Add all of the seed tracks to the listview int listLength = (playlist) ? (int)playlist->GetNumItems() : 0; int seedLength = (int)seedPlaylist.GetNumItems(); for (int i = 0; i < seedLength; i++) { AddResultListItem(&seedPlaylist, i, i, true); } // Add all of the generated tracks to the listview for (int i = 0; i < listLength; i++) { AddResultListItem(playlist, i, seedLength + i, false); } // After we are done populating the data then we can size the columns accordingly HWND hwndlist = GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2); ListView_SetColumnWidth(hwndlist,0,(listLength ? LVSCW_AUTOSIZE : LVSCW_AUTOSIZE_USEHEADER)); ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hwndlist,2,LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hwndlist,3,LVSCW_AUTOSIZE_USEHEADER); // Refresh the playlist stats UpdateStats(); // Change the progress status to read done 'generated' SetDlgItemText(hwndDlgCurrent,IDC_STATIC_PROGRESS_STATE, WASABI_API_LNGSTRINGW(IDS_DONE)); SetMarqueeProgress(false); // Turn the marquee off because we are actually generating the tracks //EnableWindow (GetDlgItem(hwndDlgCurrent, IDC_BUTTON_REGENERATE), TRUE ); SetButtonsEnabledState(true); // Renable the buttons }