diff options
Diffstat (limited to 'protocols/Gadu-Gadu/image.c')
-rw-r--r-- | protocols/Gadu-Gadu/image.c | 1186 |
1 files changed, 1186 insertions, 0 deletions
diff --git a/protocols/Gadu-Gadu/image.c b/protocols/Gadu-Gadu/image.c new file mode 100644 index 0000000000..fa32fedee9 --- /dev/null +++ b/protocols/Gadu-Gadu/image.c @@ -0,0 +1,1186 @@ +////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2009 Adam Strzelecki <ono+miranda@java.pl>
+// 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 <io.h>
+
+#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;
+ char *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);
+INT_PTR gg_img_sendimg(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+
+////////////////////////////////////////////////////////////////////////////
+// Image Module : Adding item to contact menu, creating sync objects
+int gg_img_init(GGPROTO *gg)
+{
+ 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, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_img_sendimg, gg);
+ mi.position = -2000010000;
+ mi.icolibItem = GetIconHandle(IDI_IMAGE);
+ mi.pszName = LPGEN("&Image");
+ mi.pszService = service;
+ mi.pszContactOwner = GG_PROTO;
+ gg->hImageMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) &mi);
+
+ // Receive image
+ mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_img_recvimage, gg);
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Module : closing dialogs, sync objects
+int gg_img_shutdown(GGPROTO *gg)
+{
+ list_t l;
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_img_shutdown(): Closing all dialogs...");
+#endif
+ // Rather destroy window instead of just removing structures
+ for (l = gg->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))
+ gg_netlog(gg, "gg_img_shutdown(): Image dlg %x cannot be released !!", dat->hWnd);
+ }
+ else
+ gg_netlog(gg, "gg_img_shutdown(): Image dlg %x not exists, but structure does !!", dat->hWnd);
+ }
+ }
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Module : destroying list
+int gg_img_destroy(GGPROTO *gg)
+{
+ // Release all dialogs
+ while (gg->imagedlgs && gg_img_remove((GGIMAGEDLGDATA *)gg->imagedlgs->data));
+
+ // Destroy list
+ list_destroy(gg->imagedlgs, 1);
+ CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)gg->hImageMenuItem, (LPARAM) 0);
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// 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
+char *gg_img_getfilter(char *szFilter, int nSize)
+{
+ char *szFilterName, *szFilterMask;
+ char *pFilter = szFilter;
+
+ // Match relative to ImgDecoder presence
+ szFilterName = Translate("Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)");
+ szFilterMask = "*.bmp;*.gif;*.jpeg;*.jpg;*.png";
+
+ // Make up filter
+ strncpy(pFilter, szFilterName, nSize);
+ pFilter += strlen(pFilter) + 1;
+ if (pFilter >= szFilter + nSize) return NULL;
+ strncpy(pFilter, szFilterMask, nSize - (pFilter - szFilter));
+ pFilter += strlen(pFilter) + 1;
+ if (pFilter >= szFilter + nSize) return NULL;
+ *pFilter = 0;
+
+ return szFilter;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Save specified image entry
+int gg_img_saveimage(HWND hwnd, GGIMAGEENTRY *dat)
+{
+ OPENFILENAME ofn = {0};
+ char szFileName[MAX_PATH];
+ char szFilter[128];
+
+ if (!dat) return FALSE;
+
+ gg_img_getfilter(szFilter, sizeof(szFilter));
+ strncpy(szFileName, dat->lpszFileName, sizeof(szFileName));
+ 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 = fopen(szFileName, "w+b");
+ if (fp)
+ {
+ fwrite(dat->lpData, dat->nSize, 1, fp);
+ fclose(fp);
+ gg_netlog(((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg, "gg_img_saveimage(): Image saved to %s.", szFileName);
+ }
+ else
+ {
+ gg_netlog(((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg, "gg_img_saveimage(): Cannot save image to %s.", szFileName);
+ MessageBox(hwnd, Translate("Image cannot be written to disk."), ((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg->name, 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:
+ {
+ char *szName, szTitle[128];
+ 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 gg_netlog(dat->gg, "gg_img_dlgproc(): Creation event not found, but someone might be waiting.");
+
+ // Making buttons flat
+ SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONSETASFLATBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONSETASFLATBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONSETASFLATBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONSETASFLATBTN, 0, 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");
+
+ szName = (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)dat->hContact, 0);
+ if (dat->bReceiving)
+ mir_snprintf(szTitle, sizeof(szTitle), Translate("Image from %s"), szName);
+ else
+ mir_snprintf(szTitle, sizeof(szTitle), Translate("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)
+ {
+ gg_netlog(dat->gg, "gg_img_dlgproc(): Image was not found on the list. Cannot paint the window.");
+ return FALSE;
+ }
+
+ if (dat->bReceiving)
+ {
+ char szTitle[128];
+ mir_snprintf(szTitle, sizeof(szTitle),
+ "%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)
+ {
+ gg_netlog(dat->gg, "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)
+ {
+ gg_netlog(dat->gg, "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(gg))
+ {
+ uin_t uin = (uin_t)DBGetContactSettingDword(dat->hContact, gg->proto.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, 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:
+ {
+ char szFilter[128];
+ char 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 = Translate("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 *)gg_img_loadpicture(dat->gg, 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 gg_img_dlgcallthread(GGPROTO *gg, 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(gg, gg_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(char *szPath, GGIMAGEENTRY *dat)
+{
+ struct _stat st;
+
+ if (_stat(szPath, &st) != 0)
+ return 0;
+
+ if (st.st_size == dat->nSize)
+ {
+ char *lpData;
+ FILE *fp = fopen(szPath, "rb");
+ if (!fp) return 0;
+ lpData = mir_alloc(dat->nSize);
+ if (fread(lpData, 1, dat->nSize, fp) == dat->nSize)
+ {
+ if (dat->crc32 == gg_fix32(gg_crc32(0, 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
+char *gg_img_hasextension(const char *filename)
+{
+ if (filename != NULL && *filename != '\0')
+ {
+ char *imgtype = strrchr(filename, '.');
+ if (imgtype != NULL)
+ {
+ size_t len = strlen(imgtype);
+ imgtype++;
+ if (len == 4 && (_stricmp(imgtype, "bmp") == 0 ||
+ _stricmp(imgtype, "gif") == 0 ||
+ _stricmp(imgtype, "jpg") == 0 ||
+ _stricmp(imgtype, "png") == 0))
+ return --imgtype;
+ if (len == 5 && _stricmp(imgtype, "jpeg") == 0)
+ return --imgtype;
+ }
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Display received image using message with [img] BBCode
+int gg_img_displayasmsg(GGPROTO *gg, HANDLE hContact, void *img)
+{
+ GGIMAGEENTRY *dat = (GGIMAGEENTRY *)img;
+ char szPath[MAX_PATH], *path = (char*)alloca(MAX_PATH), *pImgext, imgext[6];
+ size_t tPathLen;
+ int i, res;
+
+ if (gg->hImagesFolder == NULL || FoldersGetCustomPath(gg->hImagesFolder, path, MAX_PATH, ""))
+ {
+ char *tmpPath = Utils_ReplaceVars("%miranda_userdata%");
+ tPathLen = mir_snprintf(szPath, MAX_PATH, "%s\\%s\\ImageCache", tmpPath, GG_PROTO);
+ mir_free(tmpPath);
+ }
+ else
+ {
+ strcpy(szPath, path);
+ tPathLen = strlen(szPath);
+ }
+
+ if (_access(szPath, 0))
+ CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)szPath);
+
+ mir_snprintf(szPath + tPathLen, MAX_PATH - tPathLen, "\\%s", dat->lpszFileName);
+ if ((pImgext = gg_img_hasextension(szPath)) == NULL)
+ pImgext = szPath + strlen(szPath);
+ mir_snprintf(imgext, SIZEOF(imgext), "%s", pImgext);
+ for (i = 1; ; ++i)
+ {
+ if ((res = gg_img_isexists(szPath, dat)) != -1) break;
+ mir_snprintf(szPath, MAX_PATH, "%.*s (%u)%s", pImgext - szPath, szPath, i, imgext);
+ }
+
+ if (res == 0)
+ {
+ // Image file not found, thus create it
+ FILE *fp = fopen(szPath, "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);
+ gg_netlog(gg, "gg_img_displayasmsg: Image saved to %s.", szPath);
+ }
+ else
+ {
+ gg_netlog(gg, "gg_img_displayasmsg: Cannot save image to %s.", szPath);
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Return if uin has it's window already opened
+BOOL gg_img_opened(GGPROTO *gg, uin_t uin)
+{
+ list_t l = gg->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
+gg_img_display(GGPROTO *gg, HANDLE hContact, void *img)
+{
+ list_t l = gg->imagedlgs;
+ GGIMAGEDLGDATA *dat;
+
+ if (!img) return FALSE;
+
+ // Look for already open dialog
+ EnterCriticalSection(&gg->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(gg, hContact);
+ dat->uin = DBGetContactSettingDword(hContact, gg->proto.m_szModuleName, GG_KEY_UIN, 0);
+
+ while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0);
+ CloseHandle(dat->hEvent);
+ dat->hEvent = NULL;
+
+ list_add(&gg->imagedlgs, dat, 0);
+ }
+ LeaveCriticalSection(&gg->img_mutex);
+
+ SendMessage(dat->hWnd, WM_ADDIMAGE, 0, (LPARAM)img);
+ if (/*DBGetContactSettingByte(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;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Window : Frees image entry structure
+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;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Helper function to determine image file format and the right extension
+const char *gg_img_guessfileextension(const char *lpData)
+{
+ if (lpData != NULL)
+ {
+ if (memcmp(lpData, "BM", 2) == 0)
+ return ".bmp";
+ if (memcmp(lpData, "GIF8", 4) == 0)
+ return ".gif";
+ if (memcmp(lpData, "\xFF\xD8", 2) == 0)
+ return ".jpg";
+ if (memcmp(lpData, "\x89PNG", 4) == 0)
+ return ".png";
+ }
+ return "";
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Window : Loading picture and sending for display
+void *gg_img_loadpicture(GGPROTO *gg, struct gg_event* e, char *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 = fopen(szFileName, "rb");
+ if (!fp)
+ {
+ free(dat);
+ gg_netlog(gg, "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);
+ gg_netlog(gg, "gg_img_loadpicture(): Zero file size \"%s\" failed.", szFileName);
+ return NULL;
+ }
+ // Maximum acceptable image size
+ if (dat->nSize > 255 * 1024)
+ {
+ fclose(fp);
+ free(dat);
+ gg_netlog(gg, "gg_img_loadpicture(): Image size of \"%s\" exceeds 255 KB.", szFileName);
+ MessageBox(NULL, Translate("Image exceeds maximum allowed size of 255 KB."), GG_PROTONAME, MB_OK | MB_ICONEXCLAMATION);
+ return NULL;
+ }
+ fseek(fp, 0, SEEK_SET);
+ dat->lpData = malloc(dat->nSize);
+ if (fread(dat->lpData, 1, dat->nSize, fp) < dat->nSize)
+ {
+ free(dat->lpData);
+ fclose(fp);
+ free(dat);
+ gg_netlog(gg, "gg_img_loadpicture(): Reading file \"%s\" failed.", szFileName);
+ return NULL;
+ }
+ fclose(fp);
+ dat->lpszFileName = _strdup(szFileName);
+ }
+ // Copy picture from packet
+ else if (e && e->event.image_reply.filename)
+ {
+ dat->nSize = e->event.image_reply.size;
+ dat->lpData = malloc(dat->nSize);
+ memcpy(dat->lpData, e->event.image_reply.image, dat->nSize);
+
+ if (!gg_img_hasextension(e->event.image_reply.filename))
+ {
+ // Add missing file extension
+ const char *szImgType = gg_img_guessfileextension(dat->lpData);
+ if (*szImgType)
+ {
+ dat->lpszFileName = malloc(strlen(e->event.image_reply.filename) + strlen(szImgType) + 1);
+ if (dat->lpszFileName != NULL)
+ {
+ strcpy(dat->lpszFileName, e->event.image_reply.filename);
+ strcat(dat->lpszFileName, szImgType);
+ }
+ }
+ }
+
+ if (dat->lpszFileName == NULL)
+ dat->lpszFileName = _strdup(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;
+
+ gg_netlog(gg, "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 gg_img_recvimage(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ CLISTEVENT *cle = (CLISTEVENT *)lParam;
+ GGIMAGEENTRY *img = (GGIMAGEENTRY *)cle->lParam;
+
+ gg_netlog(gg, "gg_img_recvimage(%x, %x): Popup new image.", wParam, lParam);
+ if (!img) return FALSE;
+
+ gg_img_display(gg, 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 = DBGetContactSettingDword(dat->hContact, dat->gg->proto.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, "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 gg_img_sendonrequest(GGPROTO *gg, struct gg_event* e)
+{
+ GGIMAGEDLGDATA *dat = gg_img_find(gg, e->event.image_request.sender, e->event.image_request.crc32);
+
+ if (!gg || !dat || !gg_isonline(gg)) return FALSE;
+
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_image_reply(gg->sess, e->event.image_request.sender, dat->lpImages->lpszFileName, dat->lpImages->lpData, dat->lpImages->nSize);
+ LeaveCriticalSection(&gg->sess_mutex);
+
+ gg_img_remove(dat);
+
+ return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Send Image : Run (Thread and main)
+INT_PTR gg_img_sendimg(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ GGIMAGEDLGDATA *dat = NULL;
+
+ EnterCriticalSection(&gg->img_mutex);
+ if (!dat)
+ {
+ dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA));
+ dat->hContact = hContact;
+ dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ dat->gg = gg;
+ ResetEvent(dat->hEvent);
+
+ // Create new dialog
+ gg_forkthread(gg, gg_img_dlgcallthread, dat);
+
+ while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0);
+ CloseHandle(dat->hEvent);
+ dat->hEvent = NULL;
+
+ list_add(&gg->imagedlgs, dat, 0);
+ }
+
+ // Request choose dialog
+ SendMessage(dat->hWnd, WM_CHOOSEIMG, 0, 0);
+ LeaveCriticalSection(&gg->img_mutex);
+
+ return 0;
+}
|