// Queue.cpp: implementation of the Queue class.
//
//////////////////////////////////////////////////////////////////////
#include <windowsx.h>
#include "Queue.h"
#include <tchar.h>
#include <shellapi.h>
#include "resource.h"
#include <shlobj.h>
#include "Helpers.h"
#include <shlwapi.h>
//VOID CALLBACK OnTimer(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
LRESULT CALLBACK LocalListProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Queue::Queue(HWND hList):
hwndList(hList),AutoAdvance(true),speed(0)
{
*DefaultDestination=0;
SetWindowLong(hList, GWL_USERDATA, (LONG) this);
oldListProc = (WNDPROC) SetWindowLong(hList, GWL_WNDPROC, (LONG) LocalListProc);
GetQueueFile();
TimerID = SetTimer(hList, (UINT) this, 1000, NULL);
hMenu = LoadMenu(GetHinstance, MAKEINTRESOURCE(IDR_MENU_LIST));
DWORD threadID;
HANDLE hThread = CreateRunningThread(LoadAsync, this, &threadID);
CloseHandle(hThread);
}
Queue::~Queue()
{
DWORD cnt=ListBox_GetCount(hwndList);
Job *pjob;
SetWindowLong(hwndList, GWL_WNDPROC, (LONG) oldListProc);
KillTimer(hwndList, TimerID);
while (cnt--) {
pjob = (Job*) ListBox_GetItemData(hwndList, cnt);
if (pjob!=(Job*)LB_ERR) delete pjob;
ListBox_SetItemData(hwndList, cnt, NULL);
}
ListBox_ResetContent(hwndList);
DestroyMenu(hMenu);
if (lpQueFile) delete lpQueFile;
}
DWORD Queue::LoadAsync(Queue* This)
{
return This->Load();
}
bool Queue::Load(LPTSTR file)
{
HANDLE hFile;
Job *pjob;
if (file == NULL) file=lpQueFile;
hFile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile!=INVALID_HANDLE_VALUE) {
while(true) {
pjob = new Job(hFile, hwndList);
// mem alloc is checking inside
if (!AddItem(pjob)) {
delete pjob; // NULL pointers can be safely deleted
break;
}
}
CloseHandle(hFile);
return true;
}
return false;
}
bool Queue::Save(LPTSTR file)
{
HANDLE hFile;
Job *pjob;
DWORD idx=0;
if (file == NULL) file=lpQueFile;
hFile = CreateFile(file, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile!=INVALID_HANDLE_VALUE) {
while(true) {
pjob = (Job*) ListBox_GetItemData(hwndList, idx);
if (pjob==(Job*)LB_ERR) break;
if (!pjob->Store(hFile)) break;
idx++;
}
CloseHandle(hFile);
return true;
}
return false;
}
bool Queue::Import(LPTSTR file)
{
return true;
}
bool Queue::Export(LPTSTR file)
{
return true;
}
void Queue::GetQueueFile()
{
LPTSTR tmp;
DWORD ln;
lpQueFile=new TCHAR[MAX_PATH];
ln= GetModuleFileName(NULL, lpQueFile, MAX_PATH);
if (ln>0) {
tmp = StrRChar(lpQueFile, '.');
if (tmp) {
StrNCopy(tmp+1,TEXT("que"), 3);
}
} else {
//TEXT("CopyFile.que");
}
}
Job* Queue::AddItem(LPCTSTR src, LPTSTR dst)
{
Job *pjob;
if (dst && *dst) {
if (PathIsDirectory(dst)) {
PathAddBackslash(dst);
}
} else {
if (StrLen(DefaultDestination)==0) {
if (!ResolveDestFolder()) {
OutputDebugString(TEXT("ResolveDestFolder failed\n"));
return NULL;
}
}
dst = DefaultDestination;
}
pjob=new Job(src, dst, hwndList);
if (AddItem(pjob)) {
Save();
return pjob;
} else {
MessageBox(GetActiveWindow(), TEXT("Adding job failed"), TEXT("Queue::AddJob(char*,char*)"), MB_ICONSTOP);
}
delete pjob;
return NULL;
}
bool Queue::AddItem(Job *pjob)
{
DWORD res;
if (pjob->Ready()) { // checks memory allocation too
res=ListBox_AddString(hwndList, pjob);
if (res!=LB_ERR) return true;
}
return false;
}
BOOL Queue::MeasureItem(LPMEASUREITEMSTRUCT pMsr)
{
//pMsr->itemWidth = 60;
pMsr->itemHeight = 58;
return TRUE;
}
bool Queue::StartSelected()
{
DWORD cnt= ListBox_GetSelCount(hwndList);
SetWindowRedraw(hwndList, false);
if (cnt != LB_ERR) {
DWORD i, *sel=new DWORD[cnt];
if (sel) {
ListBox_GetSelItems(hwndList, cnt, sel);
for (i=0; i<cnt; i++) {
Job *pjob=(Job*)ListBox_GetItemData(hwndList, sel[i]);
if (pjob != (Job*) LB_ERR) {
pjob->Start();
}
}
delete sel;
}
}
SetWindowRedraw(hwndList, true);
return true;
}
bool Queue::StopSelected(void)
{
DWORD cnt=ListBox_GetSelCount(hwndList);
if (cnt != LB_ERR) {
DWORD i, *sel=new DWORD[cnt];
if (sel) {
SetWindowRedraw(hwndList, false);
ListBox_GetSelItems(hwndList, cnt, sel);
for (i=0; i<cnt; i++) {
Job *pjob=(Job*)ListBox_GetItemData(hwndList, sel[i]);
if (pjob != (Job*) LB_ERR) {
pjob->Stop(false);
}
}
delete sel;
SetWindowRedraw(hwndList, true);
}
}
return true;
}
bool Queue::RemoveSelected(void)
{
DWORD cnt=ListBox_GetSelCount(hwndList);
if (cnt != LB_ERR) {
DWORD i, *sel=new DWORD[cnt];
if (sel) {
SetWindowRedraw(hwndList, false);
ListBox_GetSelItems(hwndList, cnt, sel);
for (i=cnt; i--; ) {
Job *pjob=(Job*)ListBox_GetItemData(hwndList, sel[i]);
if (pjob!=(Job*)LB_ERR) delete pjob;
ListBox_DeleteString(hwndList, sel[i]);
}
delete sel;
Save();
SetWindowRedraw(hwndList, true);
}
}
return true;
}
bool Queue::SelSetRange(Job::RANGE &range)
{
DWORD cnt=ListBox_GetSelCount(hwndList);
if (cnt != LB_ERR) {
DWORD i, *sel=new DWORD[cnt];
if (sel) {
SetWindowRedraw(hwndList, false);
ListBox_GetSelItems(hwndList, cnt, sel);
for (i=0; i<cnt; i++) {
Job *pjob=(Job*)ListBox_GetItemData(hwndList, sel[i]);
if (pjob != (Job*) LB_ERR) {
pjob->SetRange(range);
}
}
delete sel;
SetWindowRedraw(hwndList, true);
}
}
return true;
}
bool Queue::StartNext(Job *job)
{
DWORD idx, cnt;
idx = ListBox_FindStringExact(hwndList, -1, job);
if (idx != LB_ERR) {
Job* pjob;
cnt = ListBox_GetCount(hwndList);
for (DWORD i=cnt; i--;) {
idx=(idx+1)%cnt;
pjob =(Job*)ListBox_GetItemData(hwndList, idx);
if (pjob != (Job*)LB_ERR && pjob->Start()) {
return true;
}
}
}
return false;
}
DWORD Queue::CalcTotalSpeed()
{
Job *pjob;
bool update=false;
Job::JobStates js;
DWORD cnt=ListBox_GetCount(hwndList);
speed=0;
while (cnt--) {
pjob = (Job*) ListBox_GetItemData(hwndList, cnt);
if (pjob!=(Job*)LB_ERR) {
speed+=pjob->CalcSpeed();
if ((js=pjob->GetState()) != Job::JS_STOPPED && js!=Job::JS_COMPLETED && js!=Job::JS_ERROR) update=true;
}
}
//if (update) {
InvalidateRect(hwndList, NULL, FALSE);
PostMessage(GetParent(hwndList),WM_COMMAND, GetDlgCtrlID(hwndList),0);
//}
return speed;
}
LRESULT CALLBACK Queue::LocalListProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL ret;
Queue *queue=(Queue*)GetWindowLong(hwnd, GWL_USERDATA);
switch (uMsg) {
case LB_ADDSTRING:
if (LB_ERR != ListBox_FindStringExact(hwnd, -1, lParam)) {
return LB_ERR;
}
break;
case Job::JNM_NEWJOB:
ret = queue->AddItem((Job*) lParam);
if (!ret) delete (Job*) lParam;
break;
case Job::JNM_STATECHANGED:
if (queue->AutoAdvance) {
switch (wParam) {
case Job::JS_ERROR:
if (((Job*)lParam)->retries>0) break;
case Job::JS_COMPLETED:
queue->StartNext((Job*)lParam);
break;
}
}
return TRUE;
case WM_TIMER:
queue->CalcTotalSpeed();
break;
case WM_CONTEXTMENU: {
POINT pt={LOWORD(lParam), HIWORD(lParam)};
DWORD idMenu,idx;
HMENU hPopup;
Job* pjob;
ScreenToClient(hwnd, &pt);
idx=queue->oldListProc(hwnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y));
if (HIWORD(idx)==0) {
if (ListBox_GetSel(hwnd, idx)==FALSE) {
ListBox_SetSel(hwnd, FALSE, -1);
ListBox_SetSel(hwnd, TRUE, idx);
//ListBox_SetCaretIndex(hwnd,idx);
}
pjob=(Job*)queue->oldListProc(hwnd, LB_GETITEMDATA, idx, 0);
if (pjob != (Job*)LB_ERR) {
idMenu=pjob->GetState();
if (idMenu == Job::JS_START_PENDING || idMenu == Job::JS_STOP_PENDING) idMenu= Job::JS_RUN;
hPopup=GetSubMenu(queue->hMenu, idMenu);
idMenu=TrackPopupMenu(hPopup, TPM_RIGHTBUTTON,LOWORD(lParam), HIWORD(lParam), 0, GetParent(hwnd), NULL);
/*
idMenu=TrackPopupMenu(hPopup, TPM_RIGHTBUTTON | TPM_NONOTIFY |TPM_RETURNCMD,LOWORD(lParam), HIWORD(lParam), 0, hwnd, NULL);
if (idMenu) {
//PostMessage(GetParent(hwnd), WM_COMMAND, idMenu, 0);
switch(idMenu) {
case IDC_BTN_START:
pjob->Start();
break;
case IDC_BTN_STOP:
pjob->Stop(false);
break;
case IDC_BTN_REMOVE:
//pjob->
ListBox_DeleteString(hwnd, idx);
delete pjob;
break;
}
}
*/
}
}
return TRUE;
}
case WM_DROPFILES:
HDROP hDrop = (HDROP) wParam;
UINT count = DragQueryFile(hDrop, -1, NULL, 0);
TCHAR lpSrc[MAX_PATH];
bool QueChanged = false;
LPTSTR lpTemp, lpDst=queue->DefaultDestination;
if (StrLen(lpDst)==0) {// lpDst empty. Display "Choose destination"
if (!queue->ResolveDestFolder()) {
DragFinish(hDrop);
return TRUE;
}
}
if (PathIsDirectory(lpDst)) {
//PathAddBackslash(lpDst);
} else if (PathFileExists(lpDst)) { // file exist -> get parent
lpTemp=StrRChar(lpDst,'\\');
if (lpTemp) *(lpTemp+1)=0;
} else { // doesn't exit -> assume it's directory to make
if (CreateDirRecursive(lpDst)) {
PathAddBackslash(lpDst); // optional - backslash append isn't neccessary
} else {
wParam=GetLastError();
MessageBox(hwnd, TEXT("Can't create destination"), TEXT("WM_DROPFILES"), 0);
//DebugBreak(); // unexpected error
}
}
SetWindowRedraw(hwnd, false);
while (count--) {
if (DragQueryFile(hDrop, count, lpSrc, sizeof(lpSrc)) > 0) {
if (queue->AddItem(lpSrc, lpDst)) QueChanged=true; // may be other files wont fail
//if (!queue->AddItem(lpSrc, lpDst)) break; // adding new job failed
} else {
// error query file!
}
}
SetWindowRedraw(hwnd, true);
DragFinish(hDrop);
if (QueChanged) queue->Save(); // save queue if any files were added
return TRUE; // no need to call old win proc.
}
return queue->oldListProc(hwnd, uMsg, wParam, lParam);
}
Job& Queue::GetCurrent()
{
int idx = ListBox_GetCaretIndex(hwndList);
Job *job = (Job*) ListBox_GetItemData(hwndList, idx);
if (job == (Job*) LB_ERR) job = NULL;
return (*job);
}
void Queue::Prune()
{
DWORD i, cnt=ListBox_GetCount(hwndList);
if (cnt != LB_ERR && cnt>0) {
SetWindowRedraw(hwndList, false);
for (i=cnt; i--; ) {
Job *pjob=(Job*)ListBox_GetItemData(hwndList, i);
if (pjob!=(Job*)LB_ERR && pjob->GetState()==Job::JS_COMPLETED) {
//SendMessage(hwndList, LB_SETITEMDATA, cnt, NULL);
ListBox_DeleteString(hwndList,i);
delete pjob;
}
}
Save();
SetWindowRedraw(hwndList, true);
}
}
bool Queue::ResolveDestFolder()
{
OPENFILENAME ofn={sizeof(ofn)};
BOOL ret;
//if (lpInit[len-1]=='\\') {
// StrCopy(lpInit+len,TEXT("*.*\0"));
//}
DefaultDestination[0] = '*';
DefaultDestination[1] = 0;
//ofn.lStructSize=sizeof(ofn);
if (IsWindowVisible(GetParent(hwndList))) {
ofn.hwndOwner = GetParent(hwndList);
}
ofn.hInstance = GetHinstance;
ofn.lpstrFilter = TEXT("All files\0*.*\0Movies\0*.avi,*.mpg,*.mpeg,*.divx\0CD Images\0*.iso,*.bin\0\0");
ofn.nFilterIndex = 1; // all
ofn.lpstrFile = DefaultDestination;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrTitle = TEXT("Current directory will be used as destination");
ofn.Flags = OFN_ENABLESIZING | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_NOTESTFILECREATE | OFN_NOVALIDATE;
ret = GetSaveFileName(&ofn);
if (ret) {
if (ofn.lpstrFile[ofn.nFileOffset] == '*' || ofn.lpstrFile[ofn.nFileExtension] == '*') {
ofn.lpstrFile[ofn.nFileOffset] = 0;
}
LPTSTR eofn = ofn.lpstrFile + ofn.nFileOffset;
if (PathIsDirectory(DefaultDestination)) {
PathAddBackslash(DefaultDestination);
} else {
ofn.lpstrFile[ofn.nFileOffset] = 0;
}
return true;
} else {
//DWORD errcode = CommDlgExtendedError();
*DefaultDestination = 0;
OutputDebugString(TEXT("ResolveDestFolder -> GetOpenFielName failed\n"));
}
//delete lpTmp;
return false;
}
/*
bool Queue::ResolveDestFolder()
{
BROWSEINFO bi={NULL};
LPITEMIDLIST iil;
LPMALLOC IMalloc;
bool ret = false;
if (SHGetMalloc(&IMalloc)==NOERROR) {
//StrCopy(DefaultDestination,TEXT("c:\\apps\\downloads"));
bi.hwndOwner = GetParent(hwndList);
bi.pszDisplayName = DefaultDestination;
bi.lpszTitle = TEXT("Choose destination folder");
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS | BIF_STATUSTEXT | BIF_VALIDATE; //| BIF_USENEWUI ;
//MessageBox(GetActiveWindow(), TEXT("Destintaion is not specified"), TEXT("Queue::AddJob(char*,char*)"), MB_ICONSTOP);
iil = SHBrowseForFolder(&bi);
if (iil) {
if (SHGetPathFromIDList(iil, DefaultDestination)) {
//if (SHGetTargetFolderPath(iil, DefaultDestination, MAX_PATH)==S_OK) {
PathAddBackslash(DefaultDestination);
ret = true;
} else {
OutputDebugString(TEXT("SHGetPathFromIDList failed\n"));
}
IMalloc->Free(iil);
}
IMalloc->Release();
}
return ret;
}
*/