/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-12 Miranda IM, 2012-13 Miranda NG project, all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "commonheaders.h" #include "file.h" static HWND hwndFtMgr = NULL; struct TFtMgrData { HWND hwndIncoming; HWND hwndOutgoing; HANDLE hhkPreshutdown; TBPFLAG errorState; }; #define M_CALCPROGRESS (WM_USER + 200) struct TFtProgressData { unsigned int init, run, scan; unsigned __int64 totalBytes, totalProgress; }; struct TLayoutWindowInfo { HWND hwnd; RECT rc; }; struct TLayoutWindowList { struct TLayoutWindowInfo **items; int realCount, limit, increment; FSortFunc sortFunc; }; struct TFtPageData { struct TLayoutWindowList *wnds; int runningCount; int height, dataHeight, scrollPos; }; static void LayoutTransfers(HWND hwnd, struct TFtPageData *dat) { int top = 0; RECT rc; GetClientRect(hwnd, &rc); dat->scrollPos = GetScrollPos(hwnd, SB_VERT); dat->height = rc.bottom - rc.top; if (dat->wnds->realCount) { HDWP hdwp = BeginDeferWindowPos(dat->wnds->realCount); top -= dat->scrollPos; for (int i=0; i < dat->wnds->realCount; ++i) { int height = dat->wnds->items[i]->rc.bottom - dat->wnds->items[i]->rc.top; hdwp = DeferWindowPos(hdwp, dat->wnds->items[i]->hwnd, NULL, 0, top, rc.right, height, SWP_NOZORDER); top += height; } top += dat->scrollPos; EndDeferWindowPos(hdwp); } dat->dataHeight = top; SCROLLINFO si = {0}; si.cbSize = sizeof(si); si.fMask = SIF_DISABLENOSCROLL|SIF_PAGE|SIF_RANGE; si.nPage = dat->height; si.nMin = 0; si.nMax = dat->dataHeight; SetScrollInfo(hwnd, SB_VERT, &si, TRUE); } static INT_PTR CALLBACK FtMgrPageDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { struct TFtPageData *dat = (struct TFtPageData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); int i; switch (msg) { case WM_INITDIALOG: { // Force scrollbar visibility SCROLLINFO si = {0}; si.cbSize = sizeof(si); si.fMask = SIF_DISABLENOSCROLL; SetScrollInfo(hwnd, SB_VERT, &si, TRUE); dat = (struct TFtPageData *)mir_alloc(sizeof(struct TFtPageData)); dat->wnds = (struct TLayoutWindowList *)List_Create(0, 1); dat->scrollPos = 0; dat->runningCount = 0; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)dat); } break; case WM_FT_ADD: { TLayoutWindowInfo *wnd = (struct TLayoutWindowInfo *)mir_alloc(sizeof(struct TLayoutWindowInfo)); wnd->hwnd = (HWND)lParam; GetWindowRect(wnd->hwnd, &wnd->rc); List_Insert((SortedList *)dat->wnds, wnd, dat->wnds->realCount); LayoutTransfers(hwnd, dat); dat->runningCount++; PostMessage(GetParent(hwnd), WM_TIMER, 1, NULL); } break; case WM_FT_RESIZE: for (i=0; i < dat->wnds->realCount; ++i) if (dat->wnds->items[i]->hwnd == (HWND)lParam) { GetWindowRect(dat->wnds->items[i]->hwnd, &dat->wnds->items[i]->rc); break; } LayoutTransfers(hwnd, dat); break; case WM_FT_REMOVE: for (i=0; i < dat->wnds->realCount; ++i) if (dat->wnds->items[i]->hwnd == (HWND)lParam) { mir_free(dat->wnds->items[i]); List_Remove((SortedList *)dat->wnds, i); break; } LayoutTransfers(hwnd, dat); break; case WM_FT_COMPLETED: //wParam: { ACKRESULT_SUCCESS | ACKRESULT_FAILED | ACKRESULT_DENIED } dat->runningCount--; for (i=0; i < dat->wnds->realCount; i++) { // no error when canceling (WM_FT_REMOVE is send first, check if hwnd is still registered) if (dat->wnds->items[i]->hwnd == (HWND)lParam) { SendMessage(GetParent(hwnd), WM_TIMER, 1, (LPARAM)wParam); break; } } if (i == dat->wnds->realCount) PostMessage(GetParent(hwnd), WM_TIMER, 1, NULL); if(dat->runningCount == 0 && (int)wParam == ACKRESULT_SUCCESS && db_get_b(NULL, "SRFile", "AutoClose", 0)) ShowWindow(hwndFtMgr, SW_HIDE); break; case WM_FT_CLEANUP: for (i=0; i < dat->wnds->realCount; ++i) SendMessage(dat->wnds->items[i]->hwnd, WM_FT_CLEANUP, wParam, lParam); break; case WM_SIZE: LayoutTransfers(hwnd, dat); break; case WM_MOUSEWHEEL: { int zDelta = GET_WHEEL_DELTA_WPARAM(wParam); if (zDelta) { int nScrollLines = 0; SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (void*)&nScrollLines, 0); for (i=0; i < (nScrollLines + 1) / 2; i++) SendMessage(hwnd, WM_VSCROLL, (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP, 0); } SetWindowLongPtr(hwnd, DWLP_MSGRESULT, 0); return TRUE; } case WM_VSCROLL: { int pos = dat->scrollPos; switch (LOWORD(wParam)) { case SB_LINEDOWN: pos += 15; break; case SB_LINEUP: pos -= 15; break; case SB_PAGEDOWN: pos += dat->height - 10; break; case SB_PAGEUP: pos -= dat->height - 10; break; case SB_THUMBTRACK: pos = HIWORD(wParam); break; } if (pos > dat->dataHeight - dat->height) pos = dat->dataHeight - dat->height; if (pos < 0) pos = 0; if (dat->scrollPos != pos) { ScrollWindow(hwnd, 0, dat->scrollPos - pos, NULL, NULL); SetScrollPos(hwnd, SB_VERT, pos, TRUE); dat->scrollPos = pos; } break; } case M_PRESHUTDOWN: for (i=0; i < dat->wnds->realCount; ++i) PostMessage(dat->wnds->items[i]->hwnd, WM_COMMAND, MAKEWPARAM(IDCANCEL, BN_CLICKED), 0); break; case M_CALCPROGRESS: { TFtProgressData *prg = (TFtProgressData *)wParam; for (i=0; i < dat->wnds->realCount; ++i) { struct FileDlgData *trdat = (struct FileDlgData *)GetWindowLongPtr(dat->wnds->items[i]->hwnd, GWLP_USERDATA); if (trdat->transferStatus.totalBytes && trdat->fs && !trdat->send && (trdat->transferStatus.totalBytes == trdat->transferStatus.totalProgress)) prg->scan++; else if (trdat->transferStatus.totalBytes && trdat->fs) { // in progress prg->run++; prg->totalBytes += trdat->transferStatus.totalBytes; prg->totalProgress += trdat->transferStatus.totalProgress; } else if (trdat->fs) // starting prg->init++; } } break; case WM_DESTROY: for (i=0; i < dat->wnds->realCount; ++i) mir_free(dat->wnds->items[i]); List_Destroy((SortedList *)dat->wnds); mir_free(dat->wnds); mir_free(dat); break; } return FALSE; } static INT_PTR CALLBACK FtMgrDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { struct TFtMgrData *dat = (struct TFtMgrData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); switch (msg) { case WM_INITDIALOG: { TCITEM tci = {0}; HWND hwndTab = GetDlgItem(hwnd, IDC_TABS); TranslateDialogDefault(hwnd); Window_SetIcon_IcoLib(hwnd, SKINICON_EVENT_FILE); dat = (struct TFtMgrData *)mir_calloc(sizeof(struct TFtMgrData)); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)dat); dat->hhkPreshutdown = HookEventMessage(ME_SYSTEM_PRESHUTDOWN, hwnd, M_PRESHUTDOWN); dat->hwndIncoming = CreateDialog(hInst, MAKEINTRESOURCE(IDD_FTPAGE), hwnd, FtMgrPageDlgProc); dat->hwndOutgoing = CreateDialog(hInst, MAKEINTRESOURCE(IDD_FTPAGE), hwnd, FtMgrPageDlgProc); ShowWindow(dat->hwndIncoming, SW_SHOW); tci.mask = TCIF_PARAM|TCIF_TEXT; tci.pszText = TranslateT("Incoming"); tci.lParam = (LPARAM)dat->hwndIncoming; TabCtrl_InsertItem(hwndTab, 0, &tci); tci.pszText = TranslateT("Outgoing"); tci.lParam = (LPARAM)dat->hwndOutgoing; TabCtrl_InsertItem(hwndTab, 1, &tci); // Utils_RestoreWindowPosition(hwnd, NULL, "SRFile", "FtMgrDlg_"); SAVEWINDOWPOS swp; swp.hwnd = hwnd; swp.hContact = NULL; swp.szModule = "SRFile"; swp.szNamePrefix = "FtMgrDlg_"; CallService(MS_UTILS_RESTOREWINDOWPOSITION, RWPF_NOACTIVATE, (LPARAM)&swp); // Fall through to setup initial placement } case WM_SIZE: { RECT rc, rcButton; HDWP hdwp; HWND hwndTab = GetDlgItem(hwnd, IDC_TABS); GetWindowRect(GetDlgItem(hwnd, IDCANCEL), &rcButton); OffsetRect(&rcButton, -rcButton.left, -rcButton.top); GetClientRect(hwnd, &rc); InflateRect(&rc, -6, -6); hdwp = BeginDeferWindowPos(3); hdwp = DeferWindowPos(hdwp, GetDlgItem(hwnd, IDC_CLEAR), NULL, rc.left, rc.bottom-rcButton.bottom, 0, 0, SWP_NOZORDER|SWP_NOSIZE); hdwp = DeferWindowPos(hdwp, GetDlgItem(hwnd, IDCANCEL), NULL, rc.right-rcButton.right, rc.bottom-rcButton.bottom, 0, 0, SWP_NOZORDER|SWP_NOSIZE); rc.bottom -= rcButton.bottom + 5; hdwp = DeferWindowPos(hdwp, hwndTab, NULL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, SWP_NOZORDER); EndDeferWindowPos(hdwp); GetWindowRect(hwndTab, &rc); MapWindowPoints(NULL, hwnd, (LPPOINT)&rc, 2); TabCtrl_AdjustRect(hwndTab, FALSE, &rc); InflateRect(&rc, -5, -5); hdwp = BeginDeferWindowPos(2); hdwp = DeferWindowPos(hdwp, dat->hwndIncoming, HWND_TOP, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0); hdwp = DeferWindowPos(hdwp, dat->hwndOutgoing, HWND_TOP, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0); EndDeferWindowPos(hdwp); break; } case WM_MOUSEWHEEL: if (IsWindowVisible(dat->hwndIncoming)) SendMessage(dat->hwndIncoming, msg, wParam, lParam); if (IsWindowVisible(dat->hwndOutgoing)) SendMessage(dat->hwndOutgoing, msg, wParam, lParam); break; case WM_FT_SELECTPAGE: { TCITEM tci = {0}; HWND hwndTab = GetDlgItem(hwnd, IDC_TABS); if (TabCtrl_GetCurSel(hwndTab) == (int)wParam) break; tci.mask = TCIF_PARAM; TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &tci); ShowWindow((HWND)tci.lParam, SW_HIDE); TabCtrl_SetCurSel(hwndTab, wParam); TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &tci); ShowWindow((HWND)tci.lParam, SW_SHOW); } break; case WM_GETMINMAXINFO: { LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam; lpmmi->ptMinTrackSize.x = 300; lpmmi->ptMinTrackSize.y = 400; return 0; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: PostMessage(hwnd, WM_CLOSE , 0, 0); break; case IDC_CLEAR: PostMessage(dat->hwndIncoming, WM_FT_CLEANUP, 0, 0); PostMessage(dat->hwndOutgoing, WM_FT_CLEANUP, 0, 0); break; } break; case WM_NOTIFY: switch (((LPNMHDR)lParam)->idFrom) { case IDC_TABS: { HWND hwndTab = GetDlgItem(hwnd, IDC_TABS); switch (((LPNMHDR)lParam)->code) { case TCN_SELCHANGING: { TCITEM tci = {0}; tci.mask = TCIF_PARAM; TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &tci); ShowWindow((HWND)tci.lParam, SW_HIDE); break; } case TCN_SELCHANGE: { TCITEM tci = {0}; tci.mask = TCIF_PARAM; TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &tci); ShowWindow((HWND)tci.lParam, SW_SHOW); break; } } break; } } break; case M_PRESHUTDOWN: SendMessage(dat->hwndIncoming, M_PRESHUTDOWN, 0, 0); SendMessage(dat->hwndOutgoing, M_PRESHUTDOWN, 0, 0); DestroyWindow(hwnd); break; case WM_CLOSE: ShowWindow(hwnd, SW_HIDE); if (db_get_b(NULL, "SRFile", "AutoClear", 1)) { PostMessage(dat->hwndIncoming, WM_FT_CLEANUP, 0, 0); PostMessage(dat->hwndOutgoing, WM_FT_CLEANUP, 0, 0); } return TRUE; /* Disable default IDCANCEL notification */ case WM_DESTROY: UnhookEvent(dat->hhkPreshutdown); Window_FreeIcon_IcoLib(hwnd); DestroyWindow(dat->hwndIncoming); DestroyWindow(dat->hwndOutgoing); mir_free(dat); SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); Utils_SaveWindowPosition(hwnd, NULL, "SRFile", "FtMgrDlg_"); break; case WM_ACTIVATE: dat->errorState = TBPF_NOPROGRESS; wParam = 1; break; case WM_SHOWWINDOW: if ( !wParam) { // hiding KillTimer(hwnd, 1); break; } lParam = 0; case WM_TIMER: if (pTaskbarInterface) { SetTimer(hwnd, 1, 400, NULL); if ((lParam == ACKRESULT_FAILED) || (lParam == ACKRESULT_DENIED)) dat->errorState = TBPF_ERROR; TFtProgressData prg = {0}; SendMessage(dat->hwndIncoming, M_CALCPROGRESS, (WPARAM)&prg, 0); SendMessage(dat->hwndOutgoing, M_CALCPROGRESS, (WPARAM)&prg, 0); if (dat->errorState) { pTaskbarInterface->SetProgressState(hwnd, dat->errorState); if ( !prg.run) pTaskbarInterface->SetProgressValue(hwnd, 1, 1); } else if (prg.run) pTaskbarInterface->SetProgressState(hwnd, TBPF_NORMAL); else if (prg.init || prg.scan) pTaskbarInterface->SetProgressState(hwnd, TBPF_INDETERMINATE); else { pTaskbarInterface->SetProgressState(hwnd, TBPF_NOPROGRESS); KillTimer(hwnd, 1); } if (prg.run) pTaskbarInterface->SetProgressValue(hwnd, prg.totalProgress, prg.totalBytes); } break; } return FALSE; } HWND FtMgr_Show(bool bForceActivate, bool bFromMenu) { bool bAutoMin = db_get_b(NULL, "SRFile", "AutoMin", 0) != 0; /* lqbe */ bool bJustCreated = (hwndFtMgr == NULL); if (bJustCreated) hwndFtMgr = CreateDialog(hInst, MAKEINTRESOURCE(IDD_FTMGR), NULL, FtMgrDlgProc); if (bFromMenu) { /* lqbe */ ShowWindow(hwndFtMgr, SW_RESTORE); ShowWindow(hwndFtMgr, SW_SHOW); SetForegroundWindow(hwndFtMgr); return hwndFtMgr; } if (bAutoMin && bJustCreated) { /* lqbe */ ShowWindow(hwndFtMgr, SW_HIDE); ShowWindow(hwndFtMgr, SW_MINIMIZE); return hwndFtMgr; } if (bForceActivate) { /* lqbe */ ShowWindow(hwndFtMgr, SW_RESTORE); ShowWindow(hwndFtMgr, SW_SHOWNOACTIVATE); SetForegroundWindow(hwndFtMgr); return hwndFtMgr; } if ( !bJustCreated && IsWindowVisible(hwndFtMgr)) return hwndFtMgr; ShowWindow(hwndFtMgr, bAutoMin ? SW_SHOWMINNOACTIVE : SW_SHOWNOACTIVATE); return hwndFtMgr; } void FtMgr_Destroy() { if (hwndFtMgr) DestroyWindow(hwndFtMgr); } void FtMgr_ShowPage(int page) { if (hwndFtMgr) SendMessage(hwndFtMgr, WM_FT_SELECTPAGE, page, 0); } HWND FtMgr_AddTransfer(FileDlgData *fdd) { bool bForceActivate = fdd->send || !db_get_b(NULL, "SRFile", "AutoAccept", 0); TFtMgrData *dat = (TFtMgrData*)GetWindowLongPtr(FtMgr_Show(bForceActivate, false), GWLP_USERDATA); if (dat == NULL) return NULL; HWND hwndBox = fdd->send ? dat->hwndOutgoing : dat->hwndIncoming; HWND hwndFt = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_FILETRANSFERINFO), hwndBox, DlgProcFileTransfer, (LPARAM)fdd); ShowWindow(hwndFt, SW_SHOWNA); SendMessage(hwndBox, WM_FT_ADD, 0, (LPARAM)hwndFt); FtMgr_ShowPage(fdd->send ? 1 : 0); return hwndFt; }