From 0ecadfc45326fce5fc4ba28b27a0a7ad484e5b84 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Fri, 12 Oct 2012 14:18:20 +0000 Subject: Gadu-Gadu: folders restructurization git-svn-id: http://svn.miranda-ng.org/main/trunk@1888 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/Gadu-Gadu/src/image.cpp | 1191 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1191 insertions(+) create mode 100644 protocols/Gadu-Gadu/src/image.cpp (limited to 'protocols/Gadu-Gadu/src/image.cpp') diff --git a/protocols/Gadu-Gadu/src/image.cpp b/protocols/Gadu-Gadu/src/image.cpp new file mode 100644 index 0000000000..0ad225ad06 --- /dev/null +++ b/protocols/Gadu-Gadu/src/image.cpp @@ -0,0 +1,1191 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// Copyright (c) 2009-2012 Bartosz Białek +// +// 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 "gg.h" +#include + +#define WM_ADDIMAGE WM_USER + 1 +#define WM_SENDIMG WM_USER + 2 +#define WM_CHOOSEIMG WM_USER + 3 +#define TIMERID_FLASHWND WM_USER + 4 + +//////////////////////////////////////////////////////////////////////////// +// Image Window : Data + +// tablica zawiera uin kolesia i uchwyt do okna, okno zawiera GGIMAGEDLGDATA +// ktore przechowuje handle kontaktu, i wskaznik na pierwszy obrazek +// obrazki sa poukladane jako lista jednokierunkowa. +// przy tworzeniu okna podaje sie handle do kontaktu +// dodajac obrazek tworzy sie element listy i wysyla do okna +// wyswietlajac obrazek idzie po liscie jednokierunkowej + +typedef struct _GGIMAGEENTRY +{ + HBITMAP hBitmap; + TCHAR *lpszFileName; + char *lpData; + unsigned long nSize; + struct _GGIMAGEENTRY *lpNext; + uint32_t crc32; +} GGIMAGEENTRY; + +typedef struct +{ + HANDLE hContact; + HANDLE hEvent; + HWND hWnd; + uin_t uin; + int nImg, nImgTotal; + GGIMAGEENTRY *lpImages; + SIZE minSize; + BOOL bReceiving; + GGPROTO *gg; +} GGIMAGEDLGDATA; + +// Prototypes +int gg_img_remove(GGIMAGEDLGDATA *dat); + +//////////////////////////////////////////////////////////////////////////// +// Image Module : Adding item to contact menu, creating sync objects + +int GGPROTO::img_init() +{ + CLISTMENUITEM mi = {0}; + char service[64]; + + mi.cbSize = sizeof(mi); + mi.flags = CMIF_ICONFROMICOLIB; + + // Send image contact menu item + mir_snprintf(service, sizeof(service), GGS_SENDIMAGE, m_szModuleName); + createObjService(service, &GGPROTO::img_sendimg); + mi.position = -2000010000; + mi.icolibItem = GetIconHandle(IDI_IMAGE); + mi.pszName = LPGEN("&Image"); + mi.pszService = service; + mi.pszContactOwner = m_szModuleName; + hImageMenuItem = Menu_AddContactMenuItem(&mi); + + // Receive image + mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, m_szModuleName); + createObjService(service, &GGPROTO::img_recvimage); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Module : closing dialogs, sync objects +int GGPROTO::img_shutdown() +{ + list_t l; +#ifdef DEBUGMODE + netlog("gg_img_shutdown(): Closing all dialogs..."); +#endif + // Rather destroy window instead of just removing structures + for (l = imagedlgs; l;) + { + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)l->data; + l = l->next; + + if (dat && dat->hWnd) + { + if (IsWindow(dat->hWnd)) + { + // Post message async, since it maybe be different thread + if (!PostMessage(dat->hWnd, WM_CLOSE, 0, 0)) + netlog("gg_img_shutdown(): Image dlg %x cannot be released !!", dat->hWnd); + } + else + netlog("gg_img_shutdown(): Image dlg %x not exists, but structure does !!", dat->hWnd); + } + } + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Module : destroying list + +int GGPROTO::img_destroy() +{ + // Release all dialogs + while (imagedlgs && gg_img_remove((GGIMAGEDLGDATA *)imagedlgs->data)); + + // Destroy list + list_destroy(imagedlgs, 1); + CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)hImageMenuItem, (LPARAM) 0); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Window : Frees image entry structure + +static int gg_img_releasepicture(void *img) +{ + if (!img) + return FALSE; + if (((GGIMAGEENTRY *)img)->lpszFileName) + free(((GGIMAGEENTRY *)img)->lpszFileName); + if (((GGIMAGEENTRY *)img)->hBitmap) + DeleteObject(((GGIMAGEENTRY *)img)->hBitmap); + if (((GGIMAGEENTRY *)img)->lpData) + free(((GGIMAGEENTRY *)img)->lpData); + free(img); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// Painting image +int gg_img_paint(HWND hwnd, GGIMAGEENTRY *dat) +{ + PAINTSTRUCT paintStruct; + HDC hdc = BeginPaint(hwnd, &paintStruct); + RECT rc; + + GetWindowRect(GetDlgItem(hwnd, IDC_IMG_IMAGE), &rc); + ScreenToClient(hwnd, (POINT *)&rc.left); + ScreenToClient(hwnd, (POINT *)&rc.right); + FillRect(hdc, &rc, (HBRUSH)GetSysColorBrush(COLOR_WINDOW)); + + if (dat->hBitmap) + { + HDC hdcBmp = NULL; + int nWidth, nHeight; + BITMAP bmp; + + GetObject(dat->hBitmap, sizeof(bmp), &bmp); + nWidth = bmp.bmWidth; nHeight = bmp.bmHeight; + + hdcBmp = CreateCompatibleDC(hdc); + SelectObject(hdcBmp, dat->hBitmap); + if (hdcBmp) + { + SetStretchBltMode(hdc, HALFTONE); + // Draw bitmap + if (nWidth > (rc.right-rc.left) || nHeight > (rc.bottom-rc.top)) + { + if ((double)nWidth / (double)nHeight > (double) (rc.right-rc.left) / (double)(rc.bottom-rc.top)) + { + StretchBlt(hdc, + rc.left, + ((rc.top + rc.bottom) - (rc.right - rc.left) * nHeight / nWidth) / 2, + (rc.right - rc.left), + (rc.right - rc.left) * nHeight / nWidth, + hdcBmp, 0, 0, nWidth, nHeight, SRCCOPY); + } + else + { + StretchBlt(hdc, + ((rc.left + rc.right) - (rc.bottom - rc.top) * nWidth / nHeight) / 2, + rc.top, + (rc.bottom - rc.top) * nWidth / nHeight, + (rc.bottom - rc.top), + hdcBmp, 0, 0, nWidth, nHeight, SRCCOPY); + } + } + else + { + BitBlt(hdc, + (rc.left + rc.right - nWidth) / 2, + (rc.top + rc.bottom - nHeight) / 2, + nWidth, nHeight, + hdcBmp, 0, 0, SRCCOPY); + } + DeleteDC(hdcBmp); + } + } + EndPaint(hwnd, &paintStruct); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////////// +// Returns supported image filters + +TCHAR *gg_img_getfilter(TCHAR *szFilter, int nSize) +{ + TCHAR *szFilterName, *szFilterMask; + TCHAR *pFilter = szFilter; + + // Match relative to ImgDecoder presence + szFilterName = TranslateT("Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)"); + szFilterMask = _T("*.bmp;*.gif;*.jpeg;*.jpg;*.png"); + + // Make up filter + _tcsncpy(pFilter, szFilterName, nSize); + pFilter += _tcslen(pFilter) + 1; + if (pFilter >= szFilter + nSize) return NULL; + _tcsncpy(pFilter, szFilterMask, nSize - (pFilter - szFilter)); + pFilter += _tcslen(pFilter) + 1; + if (pFilter >= szFilter + nSize) return NULL; + *pFilter = 0; + + return szFilter; +} + +//////////////////////////////////////////////////////////////////////////////// +// Save specified image entry + +int gg_img_saveimage(HWND hwnd, GGIMAGEENTRY *dat) +{ + if (!dat) + return FALSE; + + GGPROTO* gg = ((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg; + + TCHAR szFilter[128]; + gg_img_getfilter(szFilter, SIZEOF(szFilter)); + + TCHAR szFileName[MAX_PATH]; + _tcsncpy(szFileName, dat->lpszFileName, SIZEOF(szFileName)); + + OPENFILENAME ofn = {0}; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = hwnd; + ofn.hInstance = hInstance; + ofn.lpstrFile = szFileName; + ofn.lpstrFilter = szFilter; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn)) + { + FILE *fp = _tfopen(szFileName, _T("w+b")); + if (fp) + { + fwrite(dat->lpData, dat->nSize, 1, fp); + fclose(fp); + gg->netlog("gg_img_saveimage(): Image saved to %s.", szFileName); + } + else + { + gg->netlog("gg_img_saveimage(): Cannot save image to %s.", szFileName); + MessageBox(hwnd, TranslateT("Image cannot be written to disk."), gg->m_tszUserName, MB_OK | MB_ICONERROR); + } + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// +// Fit window size to image size + +BOOL gg_img_fit(HWND hwndDlg) +{ + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + RECT dlgRect, imgRect, wrkRect; + int nWidth, nHeight; + int rWidth = 0, rHeight = 0; + int oWidth = 0, oHeight = 0; + BITMAP bmp; + GGIMAGEENTRY *img = NULL; + HDC hdc; + + // Check if image is loaded + if (!dat || !dat->lpImages || !dat->lpImages->hBitmap) + return FALSE; + + img = dat->lpImages; + + // Go to last image + while (img->lpNext && dat->lpImages->hBitmap) + img = img->lpNext; + + // Get rects of display + GetWindowRect(hwndDlg, &dlgRect); + GetClientRect(GetDlgItem(hwndDlg, IDC_IMG_IMAGE), &imgRect); + + hdc = GetDC(hwndDlg); + + GetObject(img->hBitmap, sizeof(bmp), &bmp); + nWidth = bmp.bmWidth; nHeight = bmp.bmHeight; + SystemParametersInfo(SPI_GETWORKAREA, 0, &wrkRect, 0); + + ReleaseDC(hwndDlg, hdc); + + if ((imgRect.right - imgRect.left) < nWidth) + rWidth = nWidth - imgRect.right + imgRect.left; + if ((imgRect.bottom - imgRect.top) < nWidth) + rHeight = nHeight - imgRect.bottom + imgRect.top; + + // Check if anything needs resize + if (!rWidth && !rHeight) + return FALSE; + + oWidth = dlgRect.right - dlgRect.left + rWidth; + oHeight = dlgRect.bottom - dlgRect.top + rHeight; + + if (oHeight > wrkRect.bottom - wrkRect.top) + { + oWidth = (int)((double)(wrkRect.bottom - wrkRect.top + imgRect.bottom - imgRect.top - dlgRect.bottom + dlgRect.top) * nWidth / nHeight) + - imgRect.right + imgRect.left + dlgRect.right - dlgRect.left; + if (oWidth < dlgRect.right - dlgRect.left) + oWidth = dlgRect.right - dlgRect.left; + oHeight = wrkRect.bottom - wrkRect.top; + } + if (oWidth > wrkRect.right - wrkRect.left) + { + oHeight = (int)((double)(wrkRect.right - wrkRect.left + imgRect.right - imgRect.left - dlgRect.right + dlgRect.left) * nHeight / nWidth) + - imgRect.bottom + imgRect.top + dlgRect.bottom - dlgRect.top; + if (oHeight < dlgRect.bottom - dlgRect.top) + oHeight = dlgRect.bottom - dlgRect.top; + oWidth = wrkRect.right - wrkRect.left; + } + SetWindowPos(hwndDlg, NULL, + (wrkRect.left + wrkRect.right - oWidth) / 2, + (wrkRect.top + wrkRect.bottom - oHeight) / 2, + oWidth, oHeight, + SWP_SHOWWINDOW | SWP_NOZORDER /* | SWP_NOACTIVATE */); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// Dialog resizer procedure +static int sttImageDlgResizer(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL* urc) +{ + switch (urc->wId) + { + case IDC_IMG_PREV: + case IDC_IMG_NEXT: + case IDC_IMG_DELETE: + case IDC_IMG_SAVE: + return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; + case IDC_IMG_IMAGE: + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH; + case IDC_IMG_SEND: + case IDC_IMG_CANCEL: + return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; +} + +//////////////////////////////////////////////////////////////////////////// +// Send / Recv main dialog procedure +static INT_PTR CALLBACK gg_img_dlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + { + RECT rect; + + TranslateDialogDefault(hwndDlg); + // This should be already initialized + // InitCommonControls(); + + // Get dialog data + dat = (GGIMAGEDLGDATA *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + + // Save dialog handle + dat->hWnd = hwndDlg; + + // Send event if someone's waiting + if (dat->hEvent) SetEvent(dat->hEvent); + else dat->gg->netlog("gg_img_dlgproc(): Creation event not found, but someone might be waiting."); + + // Making buttons flat + SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONSETASFLATBTN, TRUE, 0); + + // Setting images for buttons + SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("previous", FALSE)); + SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("next", FALSE)); + SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("delete", FALSE)); + SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("save", FALSE)); + + // Setting tooltips for buttons + SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Previous image"), BATF_TCHAR); + SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Next image"), BATF_TCHAR); + SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete image from the list"), BATF_TCHAR); + SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save image to disk"), BATF_TCHAR); + + // Set main window image + WindowSetIcon(hwndDlg, "image"); + + TCHAR *szName = pcli->pfnGetContactDisplayName(dat->hContact, 0), szTitle[128]; + if (dat->bReceiving) + mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("Image from %s"), szName); + else + mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("Image for %s"), szName); + SetWindowText(hwndDlg, szTitle); + + // Store client extents + GetClientRect(hwndDlg, &rect); + dat->minSize.cx = rect.right - rect.left; + dat->minSize.cy = rect.bottom - rect.top; + } + return TRUE; + + case WM_SIZE: + { + UTILRESIZEDIALOG urd = {0}; + urd.cbSize = sizeof(urd); + urd.hInstance = hInstance; + urd.hwndDlg = hwndDlg; + urd.lpTemplate = dat->bReceiving ? MAKEINTRESOURCEA(IDD_IMAGE_RECV) : MAKEINTRESOURCEA(IDD_IMAGE_SEND); + urd.pfnResizer = sttImageDlgResizer; + CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); + if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) + InvalidateRect(hwndDlg, NULL, FALSE); + return 0; + } + + case WM_SIZING: + { + RECT *pRect = (RECT *)lParam; + if (pRect->right - pRect->left < dat->minSize.cx) + { + if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT) + pRect->left = pRect->right - dat->minSize.cx; + else + pRect->right = pRect->left + dat->minSize.cx; + } + if (pRect->bottom - pRect->top < dat->minSize.cy) + { + if (wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT) + pRect->top = pRect->bottom - dat->minSize.cy; + else + pRect->bottom = pRect->top + dat->minSize.cy; + } + } + return TRUE; + + case WM_CLOSE: + EndDialog(hwndDlg, 0); + break; + + // Flash the window + case WM_TIMER: + if (wParam == TIMERID_FLASHWND) + FlashWindow(hwndDlg, TRUE); + break; + + // Kill the timer + case WM_ACTIVATE: + if (LOWORD(wParam) != WA_ACTIVE) + break; + case WM_MOUSEACTIVATE: + if (KillTimer(hwndDlg, TIMERID_FLASHWND)) + FlashWindow(hwndDlg, FALSE); + break; + + case WM_PAINT: + if (dat->lpImages) + { + GGIMAGEENTRY *img = dat->lpImages; + int i; + + for (i = 1; img && (i < dat->nImg); i++) + img = img->lpNext; + + if (!img) + { + dat->gg->netlog("gg_img_dlgproc(): Image was not found on the list. Cannot paint the window."); + return FALSE; + } + + if (dat->bReceiving) + { + TCHAR szTitle[128]; + mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%s (%d / %d)"), img->lpszFileName, dat->nImg, dat->nImgTotal); + SetDlgItemText(hwndDlg, IDC_IMG_NAME, szTitle); + } + else + SetDlgItemText(hwndDlg, IDC_IMG_NAME, img->lpszFileName); + gg_img_paint(hwndDlg, img); + } + break; + + case WM_DESTROY: + if (dat) + { + // Deleting all image entries + GGIMAGEENTRY *temp, *img = dat->lpImages; + GGPROTO *gg = dat->gg; + while (temp = img) + { + img = img->lpNext; + gg_img_releasepicture(temp); + } + ReleaseIconEx("previous", FALSE); + ReleaseIconEx("next", FALSE); + ReleaseIconEx("delete", FALSE); + ReleaseIconEx("save", FALSE); + WindowFreeIcon(hwndDlg); + EnterCriticalSection(&gg->img_mutex); + list_remove(&gg->imagedlgs, dat, 1); + LeaveCriticalSection(&gg->img_mutex); + } + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_IMG_CANCEL: + EndDialog(hwndDlg, 0); + return TRUE; + + case IDC_IMG_PREV: + if (dat->nImg > 1) + { + dat->nImg--; + InvalidateRect(hwndDlg, NULL, FALSE); + } + return TRUE; + + case IDC_IMG_NEXT: + if (dat->nImg < dat->nImgTotal) + { + dat->nImg++; + InvalidateRect(hwndDlg, NULL, FALSE); + } + return TRUE; + + case IDC_IMG_DELETE: + { + GGIMAGEENTRY *del, *img = dat->lpImages; + if (dat->nImg == 1) + { + del = dat->lpImages; + dat->lpImages = img->lpNext; + } + else + { + int i; + for (i = 1; img && (i < dat->nImg - 1); i++) + img = img->lpNext; + if (!img) + { + dat->gg->netlog("gg_img_dlgproc(): Image was not found on the list. Cannot delete it from the list."); + return FALSE; + } + del = img->lpNext; + img->lpNext = del->lpNext; + dat->nImg --; + } + + if ((-- dat->nImgTotal) == 0) + EndDialog(hwndDlg, 0); + else + InvalidateRect(hwndDlg, NULL, FALSE); + + gg_img_releasepicture(del); + } + return TRUE; + + case IDC_IMG_SAVE: + { + GGIMAGEENTRY *img = dat->lpImages; + int i; + + for (i = 1; img && (i < dat->nImg); i++) + img = img->lpNext; + if (!img) + { + dat->gg->netlog("gg_img_dlgproc(): Image was not found on the list. Cannot launch saving."); + return FALSE; + } + gg_img_saveimage(hwndDlg, img); + } + return TRUE; + + case IDC_IMG_SEND: + { + unsigned char format[20]; + char *msg = "\xA0\0"; + GGPROTO *gg = dat->gg; + + if (dat->lpImages && gg->isonline()) + { + uin_t uin = (uin_t)db_get_dw(dat->hContact, gg->m_szModuleName, GG_KEY_UIN, 0); + struct gg_msg_richtext_format *r = NULL; + struct gg_msg_richtext_image *p = NULL; + LPVOID pvData = NULL; + int len; + + ((struct gg_msg_richtext*)format)->flag = 2; + + r = (struct gg_msg_richtext_format *)(format + sizeof(struct gg_msg_richtext)); + r->position = 0; + r->font = GG_FONT_IMAGE; + + p = (struct gg_msg_richtext_image *)(format + sizeof(struct gg_msg_richtext) + sizeof(struct gg_msg_richtext_format)); + p->unknown1 = 0x109; + p->size = dat->lpImages->nSize; + + dat->lpImages->crc32 = p->crc32 = gg_fix32(gg_crc32(0, (BYTE*)dat->lpImages->lpData, dat->lpImages->nSize)); + + len = sizeof(struct gg_msg_richtext_format) + sizeof(struct gg_msg_richtext_image); + ((struct gg_msg_richtext*)format)->length = len; + + EnterCriticalSection(&gg->sess_mutex); + gg_send_message_richtext(gg->sess, GG_CLASS_CHAT, (uin_t)uin, (unsigned char*)msg, format, len + sizeof(struct gg_msg_richtext)); + LeaveCriticalSection(&gg->sess_mutex); + + // Protect dat from releasing + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)0); + + EndDialog(hwndDlg, 0); + } + return TRUE; + } + break; + } + break; + + case WM_ADDIMAGE: // lParam == GGIMAGEENTRY *dat + { + GGIMAGEENTRY *lpImage = (GGIMAGEENTRY *)lParam; + GGIMAGEENTRY *lpImages = dat->lpImages; + + if (!dat->lpImages) // first image entry + dat->lpImages = lpImage; + else // adding at the end of the list + { + while (lpImages->lpNext) + lpImages = lpImages->lpNext; + lpImages->lpNext = lpImage; + } + dat->nImg = ++ dat->nImgTotal; + } + // Fit window to image + if (!gg_img_fit(hwndDlg)) + InvalidateRect(hwndDlg, NULL, FALSE); + return TRUE; + + case WM_CHOOSEIMG: + { + TCHAR szFilter[128]; + TCHAR szFileName[MAX_PATH]; + OPENFILENAME ofn = {0}; + + gg_img_getfilter(szFilter, sizeof(szFilter)); + *szFileName = 0; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = hwndDlg; + ofn.hInstance = hInstance; + ofn.lpstrFilter = szFilter; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrTitle = TranslateT("Select picture to send"); + ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; + if (GetOpenFileName(&ofn)) + { + if (dat->lpImages) + gg_img_releasepicture(dat->lpImages); + if (!(dat->lpImages = (GGIMAGEENTRY *)dat->gg->img_loadpicture(0, szFileName))) + { + EndDialog(hwndDlg, 0); + return FALSE; + } + if (!gg_img_fit(hwndDlg)) + InvalidateRect(hwndDlg, NULL, FALSE); + } + else + { + EndDialog(hwndDlg, 0); + return FALSE; + } + return TRUE; + } + } + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image dialog call thread + +void __cdecl GGPROTO::img_dlgcallthread(void *param) +{ + HWND hMIWnd = 0; //(HWND) CallService(MS_CLUI_GETHWND, 0, 0); + + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)param; + DialogBoxParam(hInstance, dat->bReceiving ? MAKEINTRESOURCE(IDD_IMAGE_RECV) : MAKEINTRESOURCE(IDD_IMAGE_SEND), + hMIWnd, gg_img_dlgproc, (LPARAM) dat); +} + +//////////////////////////////////////////////////////////////////////////// +// Open dialog receive for specified contact +GGIMAGEDLGDATA *gg_img_recvdlg(GGPROTO *gg, HANDLE hContact) +{ + // Create dialog data + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA)); + dat->hContact = hContact; + dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dat->bReceiving = TRUE; + dat->gg = gg; + ResetEvent(dat->hEvent); + gg->forkthread(&GGPROTO::img_dlgcallthread, dat); + return dat; +} + +//////////////////////////////////////////////////////////////////////////// +// Checks if an image is already saved to the specified path +// Returns 1 if yes, 0 if no or -1 if different image on this path is found +int gg_img_isexists(TCHAR *szPath, GGIMAGEENTRY *dat) +{ + struct _stat st; + + if (_tstat(szPath, &st) != 0) + return 0; + + if (st.st_size == dat->nSize) + { + FILE *fp = _tfopen(szPath, _T("rb")); + if (!fp) return 0; + char *lpData = (char*)mir_alloc(dat->nSize); + if (fread(lpData, 1, dat->nSize, fp) == dat->nSize) + { + if (dat->crc32 == gg_fix32(gg_crc32(0, (BYTE*)lpData, dat->nSize)) || + memcmp(lpData, dat->lpData, dat->nSize) == 0) + { + mir_free(lpData); + fclose(fp); + return 1; + } + } + mir_free(lpData); + fclose(fp); + } + + return -1; +} + +//////////////////////////////////////////////////////////////////////////// +// Determine if image's file name has the proper extension +TCHAR *gg_img_hasextension(TCHAR *filename) +{ + if (filename != NULL && *filename != '\0') + { + TCHAR *imgtype = _tcsrchr(filename, '.'); + if (imgtype != NULL) + { + size_t len = _tcslen(imgtype); + imgtype++; + if (len == 4 && (_tcsicmp(imgtype, _T("bmp")) == 0 || + _tcsicmp(imgtype, _T("gif")) == 0 || + _tcsicmp(imgtype, _T("jpg")) == 0 || + _tcsicmp(imgtype, _T("png")) == 0)) + return --imgtype; + if (len == 5 && _tcsicmp(imgtype, _T("jpeg")) == 0) + return --imgtype; + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// Display received image using message with [img] BBCode + +int GGPROTO::img_displayasmsg(HANDLE hContact, void *img) +{ + GGIMAGEENTRY *dat = (GGIMAGEENTRY *)img; + TCHAR szPath[MAX_PATH], path[MAX_PATH], *pImgext, imgext[6]; + size_t tPathLen; + int i, res; + + if (hImagesFolder == NULL || FoldersGetCustomPathT(hImagesFolder, path, MAX_PATH, _T(""))) { + TCHAR *tmpPath = Utils_ReplaceVarsT( _T("%miranda_userdata%")); + tPathLen = mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s\\ImageCache"), tmpPath, m_tszUserName); + mir_free(tmpPath); + } + else { + _tcscpy(szPath, path); + tPathLen = _tcslen(szPath); + } + + if ( _taccess(szPath, 0)) + CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)szPath); + + mir_sntprintf(szPath + tPathLen, MAX_PATH - tPathLen, _T("\\%s"), dat->lpszFileName); + if ((pImgext = gg_img_hasextension(szPath)) == NULL) + pImgext = szPath + _tcslen(szPath); + mir_sntprintf(imgext, SIZEOF(imgext), _T("%s"), pImgext); + for (i = 1; ; ++i) + { + if ((res = gg_img_isexists(szPath, dat)) != -1) break; + mir_sntprintf(szPath, MAX_PATH, _T("%.*s (%u)%s"), pImgext - szPath, szPath, i, imgext); + } + + if (res == 0) { + // Image file not found, thus create it + FILE *fp = _tfopen(szPath, _T("w+b")); + if (fp) { + res = fwrite(dat->lpData, dat->nSize, 1, fp) > 0; + fclose(fp); + } + } + + if (res != 0) { + char image_msg[MAX_PATH + 11]; + CCSDATA ccs = {0}; + PROTORECVEVENT pre = {0}; + + ccs.szProtoService = PSR_MESSAGE; + ccs.hContact = hContact; + ccs.lParam = (LPARAM)⪯ + mir_snprintf(image_msg, SIZEOF(image_msg), "[img]%s[/img]", szPath); + pre.timestamp = time(NULL); + pre.szMessage = image_msg; + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM) &ccs); + netlog("gg_img_displayasmsg: Image saved to %s.", szPath); + } + else + { + netlog("gg_img_displayasmsg: Cannot save image to %s.", szPath); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// +// Return if uin has it's window already opened + +BOOL GGPROTO::img_opened(uin_t uin) +{ + list_t l = imagedlgs; + while (l) + { + GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)l->data; + if (dat->uin == uin) + return TRUE; + l = l->next; + } + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Module : Looking for window entry, create if not found + +int GGPROTO::img_display(HANDLE hContact, void *img) +{ + list_t l = imagedlgs; + GGIMAGEDLGDATA *dat; + + if (!img) return FALSE; + + // Look for already open dialog + EnterCriticalSection(&img_mutex); + while (l) + { + dat = (GGIMAGEDLGDATA *)l->data; + if (dat->bReceiving && dat->hContact == hContact) + break; + l = l->next; + } + + if (!l) dat = NULL; + + if (!dat) + { + dat = gg_img_recvdlg(this, hContact); + dat->uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); + + while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(dat->hEvent); + dat->hEvent = NULL; + + list_add(&imagedlgs, dat, 0); + } + LeaveCriticalSection(&img_mutex); + + SendMessage(dat->hWnd, WM_ADDIMAGE, 0, (LPARAM)img); + if (/*db_get_b(NULL, "Chat", "FlashWindowHighlight", 0) != 0 && */ + GetActiveWindow() != dat->hWnd && GetForegroundWindow() != dat->hWnd) + SetTimer(dat->hWnd, TIMERID_FLASHWND, 900, NULL); + + /* DEPRECATED: No more grabbing the focus... just flashing + SetForegroundWindow(dat->hWnd); + SetFocus(dat->hWnd); + */ + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// Helper function to determine image file format and the right extension + +const TCHAR *gg_img_guessfileextension(const char *lpData) +{ + if (lpData != NULL) + { + if (memcmp(lpData, "BM", 2) == 0) + return _T(".bmp"); + if (memcmp(lpData, "GIF8", 4) == 0) + return _T(".gif"); + if (memcmp(lpData, "\xFF\xD8", 2) == 0) + return _T(".jpg"); + if (memcmp(lpData, "\x89PNG", 4) == 0) + return _T(".png"); + } + return _T(""); +} + +//////////////////////////////////////////////////////////////////////////// +// Image Window : Loading picture and sending for display + +void* GGPROTO::img_loadpicture(gg_event* e, TCHAR *szFileName) +{ + GGIMAGEENTRY *dat; + + if (!szFileName && + (!e || !e->event.image_reply.size || !e->event.image_reply.image || !e->event.image_reply.filename)) + return NULL; + + dat = (GGIMAGEENTRY *)calloc(1, sizeof(GGIMAGEENTRY)); + if (dat == NULL) + return NULL; + + // Copy the file name + if (szFileName) + { + FILE *fp = _tfopen(szFileName, _T("rb")); + if (!fp) { + free(dat); + netlog("gg_img_loadpicture(): fopen(\"%s\", \"rb\") failed.", szFileName); + return NULL; + } + fseek(fp, 0, SEEK_END); + dat->nSize = ftell(fp); + if (dat->nSize <= 0) + { + fclose(fp); + free(dat); + netlog("gg_img_loadpicture(): Zero file size \"%s\" failed.", szFileName); + return NULL; + } + // Maximum acceptable image size + if (dat->nSize > 255 * 1024) + { + fclose(fp); + free(dat); + netlog("gg_img_loadpicture(): Image size of \"%s\" exceeds 255 KB.", szFileName); + MessageBox(NULL, TranslateT("Image exceeds maximum allowed size of 255 KB."), m_tszUserName, MB_OK | MB_ICONEXCLAMATION); + return NULL; + } + fseek(fp, 0, SEEK_SET); + dat->lpData = (char*)malloc(dat->nSize); + if (fread(dat->lpData, 1, dat->nSize, fp) < dat->nSize) + { + free(dat->lpData); + fclose(fp); + free(dat); + netlog("gg_img_loadpicture(): Reading file \"%s\" failed.", szFileName); + return NULL; + } + fclose(fp); + dat->lpszFileName = _tcsdup(szFileName); + } + // Copy picture from packet + else if (e && e->event.image_reply.filename) + { + dat->nSize = e->event.image_reply.size; + dat->lpData = (char*)malloc(dat->nSize); + memcpy(dat->lpData, e->event.image_reply.image, dat->nSize); + + mir_ptr tmpFileName( mir_a2t(e->event.image_reply.filename)); + if (!gg_img_hasextension(tmpFileName)) { + // Add missing file extension + const TCHAR *szImgType = gg_img_guessfileextension(dat->lpData); + if (*szImgType) { + dat->lpszFileName = (TCHAR*)calloc(sizeof(TCHAR), lstrlen(tmpFileName) + lstrlen(szImgType) + 1); + if (dat->lpszFileName != NULL) { + _tcscpy(dat->lpszFileName, tmpFileName); + _tcscat(dat->lpszFileName, szImgType); + } + } + } + + if (dat->lpszFileName == NULL) + dat->lpszFileName = _tcsdup( _A2T( e->event.image_reply.filename)); + } + + //////////////////////////////////////////////////////////////////// + // Loading picture using Miranda Image services + + // Load image from memory + if (!szFileName) + { + IMGSRVC_MEMIO memio; + memio.iLen = dat->nSize; + memio.pBuf = (void *)dat->lpData; + memio.fif = FIF_UNKNOWN; /* detect */ + memio.flags = 0; + dat->hBitmap = (HBITMAP) CallService(MS_IMG_LOADFROMMEM, (WPARAM) &memio, 0); + } + // Load image from file + else + dat->hBitmap = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) szFileName, 0); + + // If everything is fine return the handle + if (dat->hBitmap) return dat; + + netlog("gg_img_loadpicture(): MS_IMG_LOAD(MEM) failed."); + if (dat) + { + if (dat->lpData) + free(dat->lpData); + if (dat->lpszFileName) + free(dat->lpszFileName); + free(dat); + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Recv : AddEvent proc +INT_PTR GGPROTO::img_recvimage(WPARAM wParam, LPARAM lParam) +{ + CLISTEVENT *cle = (CLISTEVENT *)lParam; + GGIMAGEENTRY *img = (GGIMAGEENTRY *)cle->lParam; + + netlog("gg_img_recvimage(%x, %x): Popup new image.", wParam, lParam); + if (!img) return FALSE; + + img_display(cle->hContact, img); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Windows queue management +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// Removes dat structure +int gg_img_remove(GGIMAGEDLGDATA *dat) +{ + GGIMAGEENTRY *temp = NULL, *img = NULL; + GGPROTO *gg; + + if (!dat) return FALSE; + gg = dat->gg; + + EnterCriticalSection(&gg->img_mutex); + + // Remove the structure + img = dat->lpImages; + + // Destroy picture handle + while (temp = img) + { + img = img->lpNext; + gg_img_releasepicture(temp); + } + + // Remove from list + list_remove(&gg->imagedlgs, dat, 1); + LeaveCriticalSection(&gg->img_mutex); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// +GGIMAGEDLGDATA* gg_img_find(GGPROTO *gg, uin_t uin, uint32_t crc32) +{ + int res = 0; + list_t l = gg->imagedlgs; + GGIMAGEDLGDATA *dat; + + EnterCriticalSection(&gg->img_mutex); + while (l) + { + uin_t c_uin; + + dat = (GGIMAGEDLGDATA *)l->data; + if (!dat) break; + + c_uin = db_get_dw(dat->hContact, dat->gg->m_szModuleName, GG_KEY_UIN, 0); + + if (!dat->bReceiving && dat->lpImages && dat->lpImages->crc32 == crc32 && c_uin == uin) + { + LeaveCriticalSection(&gg->img_mutex); + return dat; + } + + l = l->next; + } + LeaveCriticalSection(&gg->img_mutex); + + gg->netlog("gg_img_find(): Image not found on the list. It might be released before calling this function."); + return NULL; +} + + +//////////////////////////////////////////////////////////////////////////// +// Image Module : Send on Request + +BOOL GGPROTO::img_sendonrequest(gg_event* e) +{ + GGIMAGEDLGDATA *dat = gg_img_find(this, e->event.image_request.sender, e->event.image_request.crc32); + if (!this || !dat || !isonline()) + return FALSE; + + EnterCriticalSection(&sess_mutex); + gg_image_reply(sess, e->event.image_request.sender, dat->lpImages->lpszFileName, dat->lpImages->lpData, dat->lpImages->nSize); + LeaveCriticalSection(&sess_mutex); + + gg_img_remove(dat); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// Send Image : Run (Thread and main) + +INT_PTR GGPROTO::img_sendimg(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + GGIMAGEDLGDATA *dat = NULL; + + EnterCriticalSection(&img_mutex); + if (!dat) + { + dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA)); + dat->hContact = hContact; + dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dat->gg = this; + ResetEvent(dat->hEvent); + + // Create new dialog + forkthread(&GGPROTO::img_dlgcallthread, dat); + + while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(dat->hEvent); + dat->hEvent = NULL; + + list_add(&imagedlgs, dat, 0); + } + + // Request choose dialog + SendMessage(dat->hWnd, WM_CHOOSEIMG, 0, 0); + LeaveCriticalSection(&img_mutex); + + return 0; +} -- cgit v1.2.3