Update editorconfig
[TortoiseGit.git] / src / TortoiseIDiff / PicWindow.cpp
blobc3008161a0f89ee29baee8dcae50b037ada74146
1 // TortoiseIDiff - an image diff viewer in TortoiseSVN
3 // Copyright (C) 2006-2013 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include <shellapi.h>
21 #include <CommCtrl.h>
22 #include "PicWindow.h"
23 #include <math.h>
24 #include <memory>
26 #pragma comment(lib, "Msimg32.lib")
27 #pragma comment(lib, "shell32.lib")
29 bool CPicWindow::RegisterAndCreateWindow(HWND hParent)
31 WNDCLASSEX wcx;
33 // Fill in the window class structure with default parameters
34 wcx.cbSize = sizeof(WNDCLASSEX);
35 wcx.style = CS_HREDRAW | CS_VREDRAW;
36 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
37 wcx.cbClsExtra = 0;
38 wcx.cbWndExtra = 0;
39 wcx.hInstance = hResource;
40 wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
41 wcx.lpszClassName = _T("TortoiseGitIDiffPicWindow");
42 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEIDIFF));
43 wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
44 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEIDIFF);
45 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEIDIFF));
46 RegisterWindow(&wcx);
47 if (CreateEx(WS_EX_ACCEPTFILES | WS_EX_CLIENTEDGE, WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE, hParent))
49 ShowWindow(m_hwnd, SW_SHOW);
50 UpdateWindow(m_hwnd);
51 CreateButtons();
52 return true;
54 return false;
57 void CPicWindow::PositionTrackBar()
59 RECT rc;
60 GetClientRect(&rc);
61 HWND slider = m_AlphaSlider.GetWindow();
62 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
64 MoveWindow(slider, 0, rc.top-4+SLIDER_WIDTH, SLIDER_WIDTH, rc.bottom-rc.top-SLIDER_WIDTH+8, true);
65 ShowWindow(slider, SW_SHOW);
66 MoveWindow(hwndAlphaToggleBtn, 0, rc.top-4, SLIDER_WIDTH, SLIDER_WIDTH, true);
67 ShowWindow(hwndAlphaToggleBtn, SW_SHOW);
69 else
71 ShowWindow(slider, SW_HIDE);
72 ShowWindow(hwndAlphaToggleBtn, SW_HIDE);
76 LRESULT CALLBACK CPicWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
78 TRACKMOUSEEVENT mevt;
79 switch (uMsg)
81 case WM_CREATE:
83 // create a slider control
84 CreateTrackbar(hwnd);
85 ShowWindow(m_AlphaSlider.GetWindow(), SW_HIDE);
86 //Create the tooltips
87 TOOLINFO ti;
88 RECT rect; // for client area coordinates
90 hwndTT = CreateWindowEx(NULL,
91 TOOLTIPS_CLASS,
92 NULL,
93 WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
94 CW_USEDEFAULT,
95 CW_USEDEFAULT,
96 CW_USEDEFAULT,
97 CW_USEDEFAULT,
98 hwnd,
99 NULL,
100 hResource,
101 NULL
104 SetWindowPos(hwndTT,
105 HWND_TOP,
110 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
112 ::GetClientRect(hwnd, &rect);
114 ti.cbSize = sizeof(TOOLINFO);
115 ti.uFlags = TTF_TRACK | TTF_ABSOLUTE;
116 ti.hwnd = hwnd;
117 ti.hinst = hResource;
118 ti.uId = 0;
119 ti.lpszText = LPSTR_TEXTCALLBACK;
120 // ToolTip control will cover the whole window
121 ti.rect.left = rect.left;
122 ti.rect.top = rect.top;
123 ti.rect.right = rect.right;
124 ti.rect.bottom = rect.bottom;
126 SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
127 SendMessage(hwndTT, TTM_SETMAXTIPWIDTH, 0, 600);
128 nHSecondScrollPos = 0;
129 nVSecondScrollPos = 0;
131 break;
132 case WM_SETFOCUS:
133 case WM_KILLFOCUS:
134 InvalidateRect(*this, NULL, FALSE);
135 break;
136 case WM_ERASEBKGND:
137 return 1;
138 break;
139 case WM_PAINT:
140 Paint(hwnd);
141 break;
142 case WM_SIZE:
143 PositionTrackBar();
144 SetupScrollBars();
145 break;
146 case WM_VSCROLL:
147 if ((pSecondPic)&&((HWND)lParam == m_AlphaSlider.GetWindow()))
149 if (LOWORD(wParam) == TB_THUMBTRACK)
151 // while tracking, only redraw after 50 milliseconds
152 ::SetTimer(*this, TIMER_ALPHASLIDER, 50, NULL);
154 else
155 SetBlendAlpha(m_blend, SendMessage(m_AlphaSlider.GetWindow(), TBM_GETPOS, 0, 0) / 16.0f);
157 else
159 UINT nPos = HIWORD(wParam);
160 bool bForceUpdate = false;
161 if (LOWORD(wParam) == SB_THUMBTRACK || LOWORD(wParam) == SB_THUMBPOSITION)
163 // Get true 32-bit scroll position
164 SCROLLINFO si;
165 si.cbSize = sizeof(SCROLLINFO);
166 si.fMask = SIF_TRACKPOS;
167 GetScrollInfo(*this, SB_VERT, &si);
168 nPos = si.nTrackPos;
169 bForceUpdate = true;
172 OnVScroll(LOWORD(wParam), nPos);
173 if (bLinkedPositions && pTheOtherPic)
175 pTheOtherPic->OnVScroll(LOWORD(wParam), nPos);
176 if (bForceUpdate)
177 ::UpdateWindow(*pTheOtherPic);
180 break;
181 case WM_HSCROLL:
183 UINT nPos = HIWORD(wParam);
184 bool bForceUpdate = false;
185 if (LOWORD(wParam) == SB_THUMBTRACK || LOWORD(wParam) == SB_THUMBPOSITION)
187 // Get true 32-bit scroll position
188 SCROLLINFO si;
189 si.cbSize = sizeof(SCROLLINFO);
190 si.fMask = SIF_TRACKPOS;
191 GetScrollInfo(*this, SB_VERT, &si);
192 nPos = si.nTrackPos;
193 bForceUpdate = true;
196 OnHScroll(LOWORD(wParam), nPos);
197 if (bLinkedPositions && pTheOtherPic)
199 pTheOtherPic->OnHScroll(LOWORD(wParam), nPos);
200 if (bForceUpdate)
201 ::UpdateWindow(*pTheOtherPic);
204 break;
205 case WM_MOUSEWHEEL:
207 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam));
209 break;
210 case WM_MOUSEHWHEEL:
212 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam)|MK_SHIFT, GET_WHEEL_DELTA_WPARAM(wParam));
214 break;
215 case WM_LBUTTONDOWN:
216 SetFocus(*this);
217 ptPanStart.x = GET_X_LPARAM(lParam);
218 ptPanStart.y = GET_Y_LPARAM(lParam);
219 startVScrollPos = nVScrollPos;
220 startHScrollPos = nHScrollPos;
221 startVSecondScrollPos = nVSecondScrollPos;
222 startHSecondScrollPos = nHSecondScrollPos;
223 bDragging = true;
224 SetCapture(*this);
225 break;
226 case WM_LBUTTONUP:
227 bDragging = false;
228 ReleaseCapture();
229 InvalidateRect(*this, NULL, FALSE);
230 break;
231 case WM_MOUSELEAVE:
232 ptPanStart.x = -1;
233 ptPanStart.y = -1;
234 m_lastTTPos.x = 0;
235 m_lastTTPos.y = 0;
236 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);
237 break;
238 case WM_MOUSEMOVE:
240 mevt.cbSize = sizeof(TRACKMOUSEEVENT);
241 mevt.dwFlags = TME_LEAVE;
242 mevt.dwHoverTime = HOVER_DEFAULT;
243 mevt.hwndTrack = *this;
244 ::TrackMouseEvent(&mevt);
245 POINT pt = {((int)(short)LOWORD(lParam)), ((int)(short)HIWORD(lParam))};
246 if (pt.y < HEADER_HEIGHT)
248 ClientToScreen(*this, &pt);
249 if ((abs(m_lastTTPos.x - pt.x) > 20)||(abs(m_lastTTPos.y - pt.y) > 10))
251 m_lastTTPos = pt;
252 pt.x += 15;
253 pt.y += 15;
254 SendMessage(hwndTT, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));
255 TOOLINFO ti = {0};
256 ti.cbSize = sizeof(TOOLINFO);
257 ti.hwnd = *this;
258 ti.uId = 0;
259 SendMessage(hwndTT, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
262 else
264 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);
265 m_lastTTPos.x = 0;
266 m_lastTTPos.y = 0;
268 if ((wParam & MK_LBUTTON) &&
269 (ptPanStart.x >= 0) &&
270 (ptPanStart.y >= 0))
272 // pan the image
273 int xPos = GET_X_LPARAM(lParam);
274 int yPos = GET_Y_LPARAM(lParam);
276 if (wParam & MK_CONTROL)
278 nHSecondScrollPos = startHSecondScrollPos + (ptPanStart.x - xPos);
279 nVSecondScrollPos = startVSecondScrollPos + (ptPanStart.y - yPos);
281 else if (wParam & MK_SHIFT)
283 nHScrollPos = startHScrollPos + (ptPanStart.x - xPos);
284 nVScrollPos = startVScrollPos + (ptPanStart.y - yPos);
286 else
288 nHSecondScrollPos = startHSecondScrollPos + (ptPanStart.x - xPos);
289 nVSecondScrollPos = startVSecondScrollPos + (ptPanStart.y - yPos);
290 nHScrollPos = startHScrollPos + (ptPanStart.x - xPos);
291 nVScrollPos = startVScrollPos + (ptPanStart.y - yPos);
292 if (!bLinkedPositions && pTheOtherPic)
294 // snap to the other picture borders
295 if (abs(nVScrollPos-pTheOtherPic->nVScrollPos) < 10)
296 nVScrollPos = pTheOtherPic->nVScrollPos;
297 if (abs(nHScrollPos-pTheOtherPic->nHScrollPos) < 10)
298 nHScrollPos = pTheOtherPic->nHScrollPos;
301 SetupScrollBars();
302 InvalidateRect(*this, NULL, TRUE);
303 UpdateWindow(*this);
304 if (pTheOtherPic && (bLinkedPositions) && ((wParam & MK_SHIFT)==0))
306 pTheOtherPic->nHScrollPos = nHScrollPos;
307 pTheOtherPic->nVScrollPos = nVScrollPos;
308 pTheOtherPic->SetupScrollBars();
309 InvalidateRect(*pTheOtherPic, NULL, TRUE);
310 UpdateWindow(*pTheOtherPic);
314 break;
315 case WM_SETCURSOR:
317 // we show a hand cursor if the image can be dragged,
318 // and a hand-down cursor if the image is currently dragged
319 if ((*this == (HWND)wParam)&&(LOWORD(lParam)==HTCLIENT))
321 RECT rect;
322 GetClientRect(&rect);
323 LONG width = picture.m_Width;
324 LONG height = picture.m_Height;
325 if (pSecondPic)
327 width = max(width, pSecondPic->m_Width);
328 height = max(height, pSecondPic->m_Height);
331 if ((GetKeyState(VK_LBUTTON)&0x8000)||(HIWORD(lParam) == WM_LBUTTONDOWN))
333 SetCursor(curHandDown);
335 else
337 SetCursor(curHand);
339 return TRUE;
341 return DefWindowProc(hwnd, uMsg, wParam, lParam);
343 break;
344 case WM_DROPFILES:
346 HDROP hDrop = (HDROP)wParam;
347 TCHAR szFileName[MAX_PATH] = {0};
348 // we only use the first file dropped (if multiple files are dropped)
349 if (DragQueryFile(hDrop, 0, szFileName, _countof(szFileName)))
351 SetPic(szFileName, _T(""), bMainPic);
352 FitImageInWindow();
353 InvalidateRect(*this, NULL, TRUE);
356 break;
357 case WM_COMMAND:
359 switch (LOWORD(wParam))
361 case LEFTBUTTON_ID:
363 PrevImage();
364 if (bLinkedPositions && pTheOtherPic)
365 pTheOtherPic->PrevImage();
366 return 0;
368 break;
369 case RIGHTBUTTON_ID:
371 NextImage();
372 if (bLinkedPositions && pTheOtherPic)
373 pTheOtherPic->NextImage();
374 return 0;
376 break;
377 case PLAYBUTTON_ID:
379 bPlaying = !bPlaying;
380 Animate(bPlaying);
381 if (bLinkedPositions && pTheOtherPic)
382 pTheOtherPic->Animate(bPlaying);
383 return 0;
385 break;
386 case ALPHATOGGLEBUTTON_ID:
388 WORD msg = HIWORD(wParam);
389 switch (msg)
391 case BN_DOUBLECLICKED:
393 SendMessage(hwndAlphaToggleBtn, BM_SETSTATE, 1, 0);
394 SetTimer(*this, ID_ALPHATOGGLETIMER, 1000, NULL);
396 break;
397 case BN_CLICKED:
398 KillTimer(*this, ID_ALPHATOGGLETIMER);
399 ToggleAlpha();
400 break;
402 return 0;
404 break;
405 case BLENDALPHA_ID:
407 m_blend = BLEND_ALPHA;
408 PositionTrackBar();
409 InvalidateRect(*this, NULL, TRUE);
411 break;
412 case BLENDXOR_ID:
414 m_blend = BLEND_XOR;
415 PositionTrackBar();
416 InvalidateRect(*this, NULL, TRUE);
418 break;
419 case SELECTBUTTON_ID:
421 SendMessage(GetParent(m_hwnd), WM_COMMAND, MAKEWPARAM(SELECTBUTTON_ID, SELECTBUTTON_ID), (LPARAM)m_hwnd);
423 break;
426 break;
427 case WM_TIMER:
429 switch (wParam)
431 case ID_ANIMATIONTIMER:
433 nCurrentFrame++;
434 if (nCurrentFrame > picture.GetNumberOfFrames(0))
435 nCurrentFrame = 1;
436 long delay = picture.SetActiveFrame(nCurrentFrame);
437 delay = max(100, delay);
438 SetTimer(*this, ID_ANIMATIONTIMER, delay, NULL);
439 InvalidateRect(*this, NULL, FALSE);
441 break;
442 case TIMER_ALPHASLIDER:
444 SetBlendAlpha(m_blend, SendMessage(m_AlphaSlider.GetWindow(), TBM_GETPOS, 0, 0)/16.0f);
445 KillTimer(*this, TIMER_ALPHASLIDER);
447 break;
448 case ID_ALPHATOGGLETIMER:
450 ToggleAlpha();
452 break;
455 break;
456 case WM_NOTIFY:
458 LPNMHDR pNMHDR = (LPNMHDR)lParam;
459 if (pNMHDR->code == TTN_GETDISPINFO)
461 if (pNMHDR->hwndFrom == m_AlphaSlider.GetWindow())
463 LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT) lParam;
464 lpttt->hinst = hResource;
465 TCHAR stringbuf[MAX_PATH] = {0};
466 _stprintf_s(stringbuf, _T("%i%% alpha"), (int)(SendMessage(m_AlphaSlider.GetWindow(),TBM_GETPOS,0,0)/16.0f*100.0f));
467 _tcscpy_s(lpttt->lpszText, 80, stringbuf);
469 else if (pNMHDR->idFrom == (UINT_PTR)hwndAlphaToggleBtn)
471 _stprintf_s(m_wszTip, (TCHAR const *)ResString(hResource, IDS_ALPHABUTTONTT), (int)(SendMessage(m_AlphaSlider.GetWindow(),TBM_GETPOS,0,0)/16.0f*100.0f));
472 if (pNMHDR->code == TTN_NEEDTEXTW)
474 NMTTDISPINFOW* pTTTW = (NMTTDISPINFOW*)pNMHDR;
475 pTTTW->lpszText = m_wszTip;
477 else
479 NMTTDISPINFOA* pTTTA = (NMTTDISPINFOA*)pNMHDR;
480 pTTTA->lpszText = m_szTip;
481 ::WideCharToMultiByte(CP_ACP, 0, m_wszTip, -1, m_szTip, 8192, NULL, NULL);
484 else
486 BuildInfoString(m_wszTip, _countof(m_wszTip), true);
487 if (pNMHDR->code == TTN_NEEDTEXTW)
489 NMTTDISPINFOW* pTTTW = (NMTTDISPINFOW*)pNMHDR;
490 pTTTW->lpszText = m_wszTip;
492 else
494 NMTTDISPINFOA* pTTTA = (NMTTDISPINFOA*)pNMHDR;
495 pTTTA->lpszText = m_szTip;
496 ::WideCharToMultiByte(CP_ACP, 0, m_wszTip, -1, m_szTip, 8192, NULL, NULL);
501 break;
502 case WM_DESTROY:
503 DestroyIcon(hLeft);
504 DestroyIcon(hRight);
505 DestroyIcon(hPlay);
506 DestroyIcon(hStop);
507 bWindowClosed = TRUE;
508 break;
509 default:
510 return DefWindowProc(hwnd, uMsg, wParam, lParam);
513 return 0;
516 void CPicWindow::NextImage()
518 nCurrentDimension++;
519 if (nCurrentDimension > picture.GetNumberOfDimensions())
520 nCurrentDimension = picture.GetNumberOfDimensions();
521 nCurrentFrame++;
522 if (nCurrentFrame > picture.GetNumberOfFrames(0))
523 nCurrentFrame = picture.GetNumberOfFrames(0);
524 picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);
525 InvalidateRect(*this, NULL, FALSE);
526 PositionChildren();
529 void CPicWindow::PrevImage()
531 nCurrentDimension--;
532 if (nCurrentDimension < 1)
533 nCurrentDimension = 1;
534 nCurrentFrame--;
535 if (nCurrentFrame < 1)
536 nCurrentFrame = 1;
537 picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);
538 InvalidateRect(*this, NULL, FALSE);
539 PositionChildren();
542 void CPicWindow::Animate(bool bStart)
544 if (bStart)
546 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hStop);
547 SetTimer(*this, ID_ANIMATIONTIMER, 0, NULL);
549 else
551 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);
552 KillTimer(*this, ID_ANIMATIONTIMER);
556 void CPicWindow::SetPic(tstring path, tstring title, bool bFirst)
558 bMainPic = bFirst;
559 picpath=path;pictitle=title;
560 picture.SetInterpolationMode(InterpolationModeHighQualityBicubic);
561 bValid = picture.Load(picpath);
562 nDimensions = picture.GetNumberOfDimensions();
563 if (nDimensions)
564 nFrames = picture.GetNumberOfFrames(0);
565 if (bValid)
567 picscale = 100;
568 PositionChildren();
569 InvalidateRect(*this, NULL, FALSE);
573 void CPicWindow::DrawViewTitle(HDC hDC, RECT * rect)
575 HFONT hFont = NULL;
576 hFont = CreateFont(-MulDiv(pSecondPic ? 8 : 10, GetDeviceCaps(hDC, LOGPIXELSY), 72), 0, 0, 0, FW_DONTCARE, false, false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, _T("MS Shell Dlg"));
577 HFONT hFontOld = (HFONT)SelectObject(hDC, (HGDIOBJ)hFont);
579 RECT textrect;
580 textrect.left = rect->left;
581 textrect.top = rect->top;
582 textrect.right = rect->right;
583 textrect.bottom = rect->top + HEADER_HEIGHT;
584 if (HasMultipleImages())
585 textrect.bottom += HEADER_HEIGHT;
587 COLORREF crBk, crFg;
588 crBk = ::GetSysColor(COLOR_SCROLLBAR);
589 crFg = ::GetSysColor(COLOR_WINDOWTEXT);
590 SetBkColor(hDC, crBk);
591 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &textrect, NULL, 0, NULL);
593 if (GetFocus() == *this)
594 DrawEdge(hDC, &textrect, EDGE_BUMP, BF_RECT);
595 else
596 DrawEdge(hDC, &textrect, EDGE_ETCHED, BF_RECT);
598 SetTextColor(hDC, crFg);
600 // use the path if no title is set.
601 tstring * title = pictitle.empty() ? &picpath : &pictitle;
603 tstring realtitle = *title;
604 tstring imgnumstring;
606 if (HasMultipleImages())
608 TCHAR buf[MAX_PATH] = {0};
609 if (nFrames > 1)
610 _stprintf_s(buf, (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentFrame, nFrames);
611 else
612 _stprintf_s(buf, (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentDimension, nDimensions);
613 imgnumstring = buf;
616 SIZE stringsize;
617 if (GetTextExtentPoint32(hDC, realtitle.c_str(), (int)realtitle.size(), &stringsize))
619 int nStringLength = stringsize.cx;
620 int texttop = pSecondPic ? textrect.top + (HEADER_HEIGHT/2) - stringsize.cy : textrect.top + (HEADER_HEIGHT/2) - stringsize.cy/2;
621 ExtTextOut(hDC,
622 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
623 texttop,
624 ETO_CLIPPED,
625 &textrect,
626 realtitle.c_str(),
627 (UINT)realtitle.size(),
628 NULL);
629 if (pSecondPic)
631 realtitle = (pictitle2.empty() ? picpath2 : pictitle2);
632 ExtTextOut(hDC,
633 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
634 texttop + stringsize.cy,
635 ETO_CLIPPED,
636 &textrect,
637 realtitle.c_str(),
638 (UINT)realtitle.size(),
639 NULL);
642 if (HasMultipleImages())
644 if (GetTextExtentPoint32(hDC, imgnumstring.c_str(), (int)imgnumstring.size(), &stringsize))
646 int nStringLength = stringsize.cx;
648 ExtTextOut(hDC,
649 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
650 textrect.top + HEADER_HEIGHT + (HEADER_HEIGHT/2) - stringsize.cy/2,
651 ETO_CLIPPED,
652 &textrect,
653 imgnumstring.c_str(),
654 (UINT)imgnumstring.size(),
655 NULL);
658 SelectObject(hDC, (HGDIOBJ)hFontOld);
659 DeleteObject(hFont);
662 void CPicWindow::SetupScrollBars()
664 RECT rect;
665 GetClientRect(&rect);
667 SCROLLINFO si = {sizeof(si)};
669 si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE | SIF_DISABLENOSCROLL;
671 long width = picture.m_Width*picscale/100;
672 long height = picture.m_Height*picscale/100;
673 if (pSecondPic && pTheOtherPic)
675 width = max(width, pSecondPic->m_Width*pTheOtherPic->GetZoom()/100);
676 height = max(height, pSecondPic->m_Height*pTheOtherPic->GetZoom()/100);
679 bool bShowHScrollBar = (nHScrollPos > 0); // left of pic is left of window
680 bShowHScrollBar = bShowHScrollBar || (width-nHScrollPos > rect.right); // right of pic is outside right of window
681 bShowHScrollBar = bShowHScrollBar || (width+nHScrollPos > rect.right); // right of pic is outside right of window
682 bool bShowVScrollBar = (nVScrollPos > 0); // top of pic is above window
683 bShowVScrollBar = bShowVScrollBar || (height-nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window
684 bShowVScrollBar = bShowVScrollBar || (height+nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window
686 // if the image is smaller than the window, we don't need the scrollbars
687 ShowScrollBar(*this, SB_HORZ, bShowHScrollBar);
688 ShowScrollBar(*this, SB_VERT, bShowVScrollBar);
690 if (bShowVScrollBar)
692 si.nPos = nVScrollPos;
693 si.nPage = rect.bottom-rect.top;
694 if (height < rect.bottom-rect.top)
696 if (nVScrollPos > 0)
698 si.nMin = 0;
699 si.nMax = rect.bottom+nVScrollPos-rect.top;
701 else
703 si.nMin = nVScrollPos;
704 si.nMax = int(height);
707 else
709 if (nVScrollPos > 0)
711 si.nMin = 0;
712 si.nMax = int(max(height, rect.bottom+nVScrollPos-rect.top));
714 else
716 si.nMin = 0;
717 si.nMax = int(height-nVScrollPos);
720 SetScrollInfo(*this, SB_VERT, &si, TRUE);
723 if (bShowHScrollBar)
725 si.nPos = nHScrollPos;
726 si.nPage = rect.right-rect.left;
727 if (width < rect.right-rect.left)
729 if (nHScrollPos > 0)
731 si.nMin = 0;
732 si.nMax = rect.right+nHScrollPos-rect.left;
734 else
736 si.nMin = nHScrollPos;
737 si.nMax = int(width);
740 else
742 if (nHScrollPos > 0)
744 si.nMin = 0;
745 si.nMax = int(max(width, rect.right+nHScrollPos-rect.left));
747 else
749 si.nMin = 0;
750 si.nMax = int(width-nHScrollPos);
753 SetScrollInfo(*this, SB_HORZ, &si, TRUE);
756 PositionChildren();
759 void CPicWindow::OnVScroll(UINT nSBCode, UINT nPos)
761 RECT rect;
762 GetClientRect(&rect);
764 switch (nSBCode)
766 case SB_BOTTOM:
767 nVScrollPos = LONG(picture.GetHeight()*picscale/100);
768 break;
769 case SB_TOP:
770 nVScrollPos = 0;
771 break;
772 case SB_LINEDOWN:
773 nVScrollPos++;
774 break;
775 case SB_LINEUP:
776 nVScrollPos--;
777 break;
778 case SB_PAGEDOWN:
779 nVScrollPos += (rect.bottom-rect.top);
780 break;
781 case SB_PAGEUP:
782 nVScrollPos -= (rect.bottom-rect.top);
783 break;
784 case SB_THUMBPOSITION:
785 nVScrollPos = nPos;
786 break;
787 case SB_THUMBTRACK:
788 nVScrollPos = nPos;
789 break;
790 default:
791 return;
793 LONG height = LONG(picture.GetHeight()*picscale/100);
794 if (pSecondPic)
796 height = max(height, LONG(pSecondPic->GetHeight()*picscale/100));
797 nVSecondScrollPos = nVScrollPos;
799 SetupScrollBars();
800 PositionChildren();
801 InvalidateRect(*this, NULL, TRUE);
804 void CPicWindow::OnHScroll(UINT nSBCode, UINT nPos)
806 RECT rect;
807 GetClientRect(&rect);
809 switch (nSBCode)
811 case SB_RIGHT:
812 nHScrollPos = LONG(picture.GetWidth()*picscale/100);
813 break;
814 case SB_LEFT:
815 nHScrollPos = 0;
816 break;
817 case SB_LINERIGHT:
818 nHScrollPos++;
819 break;
820 case SB_LINELEFT:
821 nHScrollPos--;
822 break;
823 case SB_PAGERIGHT:
824 nHScrollPos += (rect.right-rect.left);
825 break;
826 case SB_PAGELEFT:
827 nHScrollPos -= (rect.right-rect.left);
828 break;
829 case SB_THUMBPOSITION:
830 nHScrollPos = nPos;
831 break;
832 case SB_THUMBTRACK:
833 nHScrollPos = nPos;
834 break;
835 default:
836 return;
838 LONG width = LONG(picture.GetWidth()*picscale/100);
839 if (pSecondPic)
841 width = max(width, LONG(pSecondPic->GetWidth()*picscale/100));
842 nHSecondScrollPos = nHScrollPos;
844 SetupScrollBars();
845 PositionChildren();
846 InvalidateRect(*this, NULL, TRUE);
849 void CPicWindow::OnMouseWheel(short fwKeys, short zDelta)
851 RECT rect;
852 GetClientRect(&rect);
853 LONG width = long(picture.m_Width*picscale/100);
854 LONG height = long(picture.m_Height*picscale/100);
855 if (pSecondPic)
857 width = max(width, long(pSecondPic->m_Width*picscale/100));
858 height = max(height, long(pSecondPic->m_Height*picscale/100));
860 if ((fwKeys & MK_SHIFT)&&(fwKeys & MK_CONTROL)&&(pSecondPic))
862 // ctrl+shift+wheel: change the alpha channel
863 float a = blendAlpha;
864 a -= float(zDelta)/120.0f/4.0f;
865 if (a < 0.0f)
866 a = 0.0f;
867 else if (a > 1.0f)
868 a = 1.0f;
869 SetBlendAlpha(m_blend, a);
871 else if (fwKeys & MK_SHIFT)
873 // shift means scrolling sideways
874 OnHScroll(SB_THUMBPOSITION, GetHPos()-zDelta);
875 if ((bLinkedPositions)&&(pTheOtherPic))
877 pTheOtherPic->OnHScroll(SB_THUMBPOSITION, pTheOtherPic->GetHPos()-zDelta);
880 else if (fwKeys & MK_CONTROL)
882 // control means adjusting the scale factor
883 Zoom(zDelta>0, true);
884 PositionChildren();
885 InvalidateRect(*this, NULL, FALSE);
886 SetWindowPos(*this, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOREPOSITION|SWP_NOMOVE);
887 UpdateWindow(*this);
888 if ((bLinkedPositions || bOverlap) && pTheOtherPic)
890 pTheOtherPic->nHScrollPos = nHScrollPos;
891 pTheOtherPic->nVScrollPos = nVScrollPos;
892 pTheOtherPic->SetupScrollBars();
893 InvalidateRect(*pTheOtherPic, NULL, TRUE);
894 UpdateWindow(*pTheOtherPic);
897 else
899 OnVScroll(SB_THUMBPOSITION, GetVPos()-zDelta);
900 if ((bLinkedPositions)&&(pTheOtherPic))
902 pTheOtherPic->OnVScroll(SB_THUMBPOSITION, pTheOtherPic->GetVPos()-zDelta);
907 void CPicWindow::GetClientRect(RECT * pRect)
909 ::GetClientRect(*this, pRect);
910 pRect->top += HEADER_HEIGHT;
911 if (HasMultipleImages())
913 pRect->top += HEADER_HEIGHT;
915 if (pSecondPic)
916 pRect->left += SLIDER_WIDTH;
919 void CPicWindow::GetClientRectWithScrollbars(RECT * pRect)
921 GetClientRect(pRect);
922 ::GetWindowRect(*this, pRect);
923 pRect->right = pRect->right-pRect->left;
924 pRect->bottom = pRect->bottom-pRect->top;
925 pRect->top = 0;
926 pRect->left = 0;
927 pRect->top += HEADER_HEIGHT;
928 if (HasMultipleImages())
930 pRect->top += HEADER_HEIGHT;
932 if (pSecondPic)
933 pRect->left += SLIDER_WIDTH;
937 void CPicWindow::SetZoom(int Zoom, bool centermouse, bool inzoom)
939 // Set the interpolation mode depending on zoom
940 int oldPicscale = picscale;
941 int oldOtherPicscale = picscale;
943 picture.SetInterpolationMode(InterpolationModeNearestNeighbor);
944 if (pSecondPic)
945 pSecondPic->SetInterpolationMode(InterpolationModeNearestNeighbor);
947 if ((oldPicscale == 0) || (Zoom == 0))
948 return;
950 picscale = Zoom;
952 if (pTheOtherPic && !inzoom)
954 if (bOverlap)
956 pTheOtherPic->SetZoom(Zoom, centermouse, true);
958 if (bFitHeights)
960 m_linkedHeight = 0;
961 pTheOtherPic->SetZoomToHeight(picture.m_Height*Zoom/100);
963 if (bFitWidths)
965 m_linkedWidth = 0;
966 pTheOtherPic->SetZoomToWidth(picture.m_Width*Zoom/100);
970 // adjust the scrollbar positions according to the new zoom and the
971 // mouse position: if possible, keep the pixel where the mouse pointer
972 // is at the same position after the zoom
973 if (!inzoom)
975 POINT cpos;
976 DWORD ptW = GetMessagePos();
977 cpos.x = GET_X_LPARAM(ptW);
978 cpos.y = GET_Y_LPARAM(ptW);
979 ScreenToClient(*this, &cpos);
980 RECT clientrect;
981 GetClientRect(&clientrect);
982 if ((PtInRect(&clientrect, cpos))&&(centermouse))
984 // the mouse pointer is over our window
985 nHScrollPos = (nHScrollPos + cpos.x)*Zoom/oldPicscale-cpos.x;
986 nVScrollPos = (nVScrollPos + cpos.y)*Zoom/oldPicscale-cpos.y;
987 if (pTheOtherPic && bMainPic)
989 int otherzoom = pTheOtherPic->GetZoom();
990 nHSecondScrollPos = (nHSecondScrollPos + cpos.x)*otherzoom/oldOtherPicscale-cpos.x;
991 nVSecondScrollPos = (nVSecondScrollPos + cpos.y)*otherzoom/oldOtherPicscale-cpos.y;
994 else
996 nHScrollPos = (nHScrollPos + ((clientrect.right-clientrect.left)/2))*Zoom/oldPicscale-((clientrect.right-clientrect.left)/2);
997 nVScrollPos = (nVScrollPos + ((clientrect.bottom-clientrect.top)/2))*Zoom/oldPicscale-((clientrect.bottom-clientrect.top)/2);
998 if (pTheOtherPic && bMainPic)
1000 int otherzoom = pTheOtherPic->GetZoom();
1001 nHSecondScrollPos = (nHSecondScrollPos + ((clientrect.right-clientrect.left)/2))*otherzoom/oldOtherPicscale-((clientrect.right-clientrect.left)/2);
1002 nVSecondScrollPos = (nVSecondScrollPos + ((clientrect.bottom-clientrect.top)/2))*otherzoom/oldOtherPicscale-((clientrect.bottom-clientrect.top)/2);
1007 SetupScrollBars();
1008 PositionChildren();
1009 InvalidateRect(*this, NULL, TRUE);
1012 void CPicWindow::Zoom(bool in, bool centermouse)
1014 int zoomFactor;
1016 // Find correct zoom factor and quantize picscale
1017 if (picscale % 10)
1019 picscale /= 10;
1020 picscale *= 10;
1021 if (!in)
1022 picscale += 10;
1025 if (!in && picscale <= 20)
1027 picscale = 10;
1028 zoomFactor = 0;
1030 else if ((in && picscale < 100) || (!in && picscale <= 100))
1032 zoomFactor = 10;
1034 else if ((in && picscale < 200) || (!in && picscale <= 200))
1036 zoomFactor = 20;
1038 else
1040 zoomFactor = 10;
1043 // Set zoom
1044 if (in)
1046 SetZoom(picscale+zoomFactor, centermouse);
1048 else
1050 SetZoom(picscale-zoomFactor, centermouse);
1054 void CPicWindow::FitImageInWindow()
1056 RECT rect;
1058 GetClientRectWithScrollbars(&rect);
1060 if (rect.right-rect.left)
1062 int Zoom = 100;
1063 if (((rect.right - rect.left) > picture.m_Width+2)&&((rect.bottom - rect.top)> picture.m_Height+2))
1065 // image is smaller than the window
1066 Zoom = 100;
1068 else
1070 // image is bigger than the window
1071 int xscale = (rect.right-rect.left-2)*100/picture.m_Width;
1072 int yscale = (rect.bottom-rect.top-2)*100/picture.m_Height;
1073 Zoom = min(yscale, xscale);
1075 if (pSecondPic)
1077 if (((rect.right - rect.left) > pSecondPic->m_Width+2)&&((rect.bottom - rect.top)> pSecondPic->m_Height+2))
1079 // image is smaller than the window
1080 if (pTheOtherPic)
1081 pTheOtherPic->SetZoom(min(100, Zoom), false);
1083 else
1085 // image is bigger than the window
1086 int xscale = (rect.right-rect.left-2)*100/pSecondPic->m_Width;
1087 int yscale = (rect.bottom-rect.top-2)*100/pSecondPic->m_Height;
1088 if (pTheOtherPic)
1089 pTheOtherPic->SetZoom(min(yscale, xscale), false);
1091 nHSecondScrollPos = 0;
1092 nVSecondScrollPos = 0;
1094 SetZoom(Zoom, false);
1096 CenterImage();
1097 PositionChildren();
1098 InvalidateRect(*this, NULL, TRUE);
1101 void CPicWindow::CenterImage()
1103 RECT rect;
1104 GetClientRectWithScrollbars(&rect);
1105 long width = picture.m_Width*picscale/100 + 2;
1106 long height = picture.m_Height*picscale/100 + 2;
1107 if (pSecondPic && pTheOtherPic)
1109 width = max(width, pSecondPic->m_Width*pTheOtherPic->GetZoom()/100 + 2);
1110 height = max(height, pSecondPic->m_Height*pTheOtherPic->GetZoom()/100 + 2);
1113 bool bPicWidthBigger = (int(width) > (rect.right-rect.left));
1114 bool bPicHeightBigger = (int(height) > (rect.bottom-rect.top));
1115 // set the scroll position so that the image is drawn centered in the window
1116 // if the window is bigger than the image
1117 if (!bPicWidthBigger)
1119 nHScrollPos = -((rect.right-rect.left+4)-int(width))/2;
1120 nHSecondScrollPos = nHScrollPos;
1122 if (!bPicHeightBigger)
1124 nVScrollPos = -((rect.bottom-rect.top+4)-int(height))/2;
1125 nVSecondScrollPos = nVScrollPos;
1127 SetupScrollBars();
1130 void CPicWindow::FitWidths(bool bFit)
1132 bFitWidths = bFit;
1134 SetZoom(GetZoom(), false);
1137 void CPicWindow::FitHeights(bool bFit)
1139 bFitHeights = bFit;
1141 SetZoom(GetZoom(), false);
1144 void CPicWindow::ShowPicWithBorder(HDC hdc, const RECT &bounds, CPicture &pic, int scale)
1146 ::SetBkColor(hdc, transparentColor);
1147 ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1149 RECT picrect;
1150 picrect.left = bounds.left - nHScrollPos;
1151 picrect.top = bounds.top - nVScrollPos;
1152 if ((!bLinkedPositions || bOverlap) && (pTheOtherPic) && (&pic != &picture))
1154 picrect.left = bounds.left - nHSecondScrollPos;
1155 picrect.top = bounds.top - nVSecondScrollPos;
1157 picrect.right = (picrect.left + pic.m_Width * scale / 100);
1158 picrect.bottom = (picrect.top + pic.m_Height * scale / 100);
1160 if (bFitWidths && m_linkedWidth)
1161 picrect.right = picrect.left + m_linkedWidth;
1162 if (bFitHeights && m_linkedHeight)
1163 picrect.bottom = picrect.top + m_linkedHeight;
1165 pic.Show(hdc, picrect);
1167 RECT border;
1168 border.left = picrect.left-1;
1169 border.top = picrect.top-1;
1170 border.right = picrect.right+1;
1171 border.bottom = picrect.bottom+1;
1173 HPEN hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW));
1174 HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
1175 MoveToEx(hdc, border.left, border.top, NULL);
1176 LineTo(hdc, border.left, border.bottom);
1177 LineTo(hdc, border.right, border.bottom);
1178 LineTo(hdc, border.right, border.top);
1179 LineTo(hdc, border.left, border.top);
1180 SelectObject(hdc, hOldPen);
1181 DeleteObject(hPen);
1184 void CPicWindow::Paint(HWND hwnd)
1186 PAINTSTRUCT ps;
1187 HDC hdc;
1188 RECT rect, fullrect;
1190 GetUpdateRect(hwnd, &rect, FALSE);
1191 if (IsRectEmpty(&rect))
1192 return;
1194 ::GetClientRect(*this, &fullrect);
1195 hdc = BeginPaint(hwnd, &ps);
1197 // Exclude the alpha control and button
1198 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
1199 ExcludeClipRect(hdc, 0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4);
1201 CMyMemDC memDC(hdc);
1202 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))
1204 // erase the place where the alpha slider would be
1205 ::SetBkColor(memDC, transparentColor);
1206 RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};
1207 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1210 GetClientRect(&rect);
1211 if (bValid)
1213 ShowPicWithBorder(memDC, rect, picture, picscale);
1214 if (pSecondPic)
1216 HDC secondhdc = CreateCompatibleDC(hdc);
1217 HBITMAP hBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
1218 HBITMAP hOldBitmap = (HBITMAP)SelectObject(secondhdc, hBitmap);
1219 SetWindowOrgEx(secondhdc, rect.left, rect.top, NULL);
1221 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))
1223 // erase the place where the alpha slider would be
1224 ::SetBkColor(secondhdc, transparentColor);
1225 RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};
1226 ::ExtTextOut(secondhdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1228 if (pTheOtherPic)
1229 ShowPicWithBorder(secondhdc, rect, *pSecondPic, pTheOtherPic->GetZoom());
1231 if (m_blend == BLEND_ALPHA)
1233 BLENDFUNCTION blender;
1234 blender.AlphaFormat = 0;
1235 blender.BlendFlags = 0;
1236 blender.BlendOp = AC_SRC_OVER;
1237 blender.SourceConstantAlpha = (BYTE)(blendAlpha*255);
1238 AlphaBlend(memDC,
1239 rect.left,
1240 rect.top,
1241 rect.right-rect.left,
1242 rect.bottom-rect.top,
1243 secondhdc,
1244 rect.left,
1245 rect.top,
1246 rect.right-rect.left,
1247 rect.bottom-rect.top,
1248 blender);
1250 else if (m_blend == BLEND_XOR)
1252 BitBlt(memDC,
1253 rect.left,
1254 rect.top,
1255 rect.right-rect.left,
1256 rect.bottom-rect.top,
1257 secondhdc,
1258 rect.left,
1259 rect.top,
1260 //rect.right-rect.left,
1261 //rect.bottom-rect.top,
1262 SRCINVERT);
1263 InvertRect(memDC, &rect);
1265 SelectObject(secondhdc, hOldBitmap);
1266 DeleteObject(hBitmap);
1267 DeleteDC(secondhdc);
1269 else if (bDragging && pTheOtherPic && !bLinkedPositions)
1271 // when dragging, show lines indicating the position of the other image
1272 HPEN hPen = CreatePen(PS_SOLID, 1, GetSysColor(/*COLOR_ACTIVEBORDER*/COLOR_HIGHLIGHT));
1273 HPEN hOldPen = (HPEN)SelectObject(memDC, hPen);
1274 int xpos = rect.left - pTheOtherPic->nHScrollPos - 1;
1275 MoveToEx(memDC, xpos, rect.top, NULL);
1276 LineTo(memDC, xpos, rect.bottom);
1277 xpos = rect.left - pTheOtherPic->nHScrollPos + pTheOtherPic->picture.m_Width*pTheOtherPic->GetZoom()/100 + 1;
1278 if (bFitWidths && m_linkedWidth)
1279 xpos = rect.left + pTheOtherPic->m_linkedWidth + 1;
1280 MoveToEx(memDC, xpos, rect.top, NULL);
1281 LineTo(memDC, xpos, rect.bottom);
1283 int ypos = rect.top - pTheOtherPic->nVScrollPos - 1;
1284 MoveToEx(memDC, rect.left, ypos, NULL);
1285 LineTo(memDC, rect.right, ypos);
1286 ypos = rect.top - pTheOtherPic->nVScrollPos + pTheOtherPic->picture.m_Height*pTheOtherPic->GetZoom()/100 + 1;
1287 if (bFitHeights && m_linkedHeight)
1288 ypos = rect.top - pTheOtherPic->m_linkedHeight + 1;
1289 MoveToEx(memDC, rect.left, ypos, NULL);
1290 LineTo(memDC, rect.right, ypos);
1292 SelectObject(memDC, hOldPen);
1293 DeleteObject(hPen);
1296 int sliderwidth = 0;
1297 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
1298 sliderwidth = SLIDER_WIDTH;
1299 m_inforect.left = rect.left+4+sliderwidth;
1300 m_inforect.top = rect.top;
1301 m_inforect.right = rect.right+sliderwidth;
1302 m_inforect.bottom = rect.bottom;
1304 SetBkColor(memDC, transparentColor);
1305 if (bShowInfo)
1307 auto infostring = std::make_unique<TCHAR[]>(8192);
1308 BuildInfoString(infostring.get(), 8192, false);
1309 // set the font
1310 NONCLIENTMETRICS metrics = {0};
1311 metrics.cbSize = sizeof(NONCLIENTMETRICS);
1313 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);
1314 HFONT hFont = CreateFontIndirect(&metrics.lfStatusFont);
1315 HFONT hFontOld = (HFONT)SelectObject(memDC, (HGDIOBJ)hFont);
1316 // find out how big the rectangle for the text has to be
1317 DrawText(memDC, infostring.get(), -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER | DT_CALCRECT);
1319 // the text should be drawn with a four pixel offset to the window borders
1320 m_inforect.top = rect.bottom - (m_inforect.bottom-m_inforect.top) - 4;
1321 m_inforect.bottom = rect.bottom-4;
1323 // first draw an edge rectangle
1324 RECT edgerect;
1325 edgerect.left = m_inforect.left-4;
1326 edgerect.top = m_inforect.top-4;
1327 edgerect.right = m_inforect.right+4;
1328 edgerect.bottom = m_inforect.bottom+4;
1329 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &edgerect, NULL, 0, NULL);
1330 DrawEdge(memDC, &edgerect, EDGE_BUMP, BF_RECT | BF_SOFT);
1332 SetTextColor(memDC, GetSysColor(COLOR_WINDOWTEXT));
1333 DrawText(memDC, infostring.get(), -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER);
1334 SelectObject(memDC, (HGDIOBJ)hFontOld);
1335 DeleteObject(hFont);
1338 else
1340 SetBkColor(memDC, ::GetSysColor(COLOR_WINDOW));
1341 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
1342 SIZE stringsize;
1343 ResString str = ResString(hResource, IDS_INVALIDIMAGEINFO);
1345 // set the font
1346 NONCLIENTMETRICS metrics = {0};
1347 metrics.cbSize = sizeof(NONCLIENTMETRICS);
1348 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);
1349 HFONT hFont = CreateFontIndirect(&metrics.lfStatusFont);
1350 HFONT hFontOld = (HFONT)SelectObject(memDC, (HGDIOBJ)hFont);
1352 if (GetTextExtentPoint32(memDC, str, (int)_tcslen(str), &stringsize))
1354 int nStringLength = stringsize.cx;
1356 ExtTextOut(memDC,
1357 max(rect.left + ((rect.right-rect.left)-nStringLength)/2, 1),
1358 rect.top + ((rect.bottom-rect.top) - stringsize.cy)/2,
1359 ETO_CLIPPED,
1360 &rect,
1361 str,
1362 (UINT)_tcslen(str),
1363 NULL);
1365 SelectObject(memDC, (HGDIOBJ)hFontOld);
1366 DeleteObject(hFont);
1368 DrawViewTitle(memDC, &fullrect);
1370 EndPaint(hwnd, &ps);
1373 bool CPicWindow::CreateButtons()
1375 // Ensure that the common control DLL is loaded.
1376 INITCOMMONCONTROLSEX icex;
1377 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
1378 icex.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES;
1379 InitCommonControlsEx(&icex);
1381 hwndLeftBtn = CreateWindowEx(0,
1382 _T("BUTTON"),
1383 (LPCTSTR)NULL,
1384 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1385 0, 0, 0, 0,
1386 (HWND)*this,
1387 (HMENU)LEFTBUTTON_ID,
1388 hResource,
1389 NULL);
1390 if (hwndLeftBtn == INVALID_HANDLE_VALUE)
1391 return false;
1392 hLeft = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_BACKWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1393 SendMessage(hwndLeftBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hLeft);
1394 hwndRightBtn = CreateWindowEx(0,
1395 _T("BUTTON"),
1396 (LPCTSTR)NULL,
1397 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1398 0, 0, 0, 0,
1399 *this,
1400 (HMENU)RIGHTBUTTON_ID,
1401 hResource,
1402 NULL);
1403 if (hwndRightBtn == INVALID_HANDLE_VALUE)
1404 return false;
1405 hRight = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_FORWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1406 SendMessage(hwndRightBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hRight);
1407 hwndPlayBtn = CreateWindowEx(0,
1408 _T("BUTTON"),
1409 (LPCTSTR)NULL,
1410 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1411 0, 0, 0, 0,
1412 *this,
1413 (HMENU)PLAYBUTTON_ID,
1414 hResource,
1415 NULL);
1416 if (hwndPlayBtn == INVALID_HANDLE_VALUE)
1417 return false;
1418 hPlay = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_START), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1419 hStop = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1420 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);
1421 hwndAlphaToggleBtn = CreateWindowEx(0,
1422 _T("BUTTON"),
1423 (LPCTSTR)NULL,
1424 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT | BS_NOTIFY | BS_PUSHLIKE,
1425 0, 0, 0, 0,
1426 (HWND)*this,
1427 (HMENU)ALPHATOGGLEBUTTON_ID,
1428 hResource,
1429 NULL);
1430 if (hwndAlphaToggleBtn == INVALID_HANDLE_VALUE)
1431 return false;
1432 hAlphaToggle = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_ALPHATOGGLE), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1433 SendMessage(hwndAlphaToggleBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hAlphaToggle);
1435 TOOLINFO ti = {0};
1436 ti.cbSize = sizeof(TOOLINFO);
1437 ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS;
1438 ti.hwnd = *this;
1439 ti.hinst = hResource;
1440 ti.uId = (UINT_PTR)hwndAlphaToggleBtn;
1441 ti.lpszText = LPSTR_TEXTCALLBACK;
1442 // ToolTip control will cover the whole window
1443 ti.rect.left = 0;
1444 ti.rect.top = 0;
1445 ti.rect.right = 0;
1446 ti.rect.bottom = 0;
1447 SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
1448 ResString sSelect(hResource, IDS_SELECT);
1449 hwndSelectBtn = CreateWindowEx(0,
1450 _T("BUTTON"),
1451 sSelect,
1452 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
1453 0, 0, 0, 0,
1454 *this,
1455 (HMENU)SELECTBUTTON_ID,
1456 hResource,
1457 NULL);
1458 if (hwndPlayBtn == INVALID_HANDLE_VALUE)
1459 return false;
1461 return true;
1464 void CPicWindow::PositionChildren()
1466 RECT rect;
1467 ::GetClientRect(*this, &rect);
1468 if (HasMultipleImages())
1470 SetWindowPos(hwndLeftBtn, HWND_TOP, rect.left+3, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1471 SetWindowPos(hwndRightBtn, HWND_TOP, rect.left+23, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1472 if (nFrames > 1)
1473 SetWindowPos(hwndPlayBtn, HWND_TOP, rect.left+43, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1474 else
1475 ShowWindow(hwndPlayBtn, SW_HIDE);
1477 else
1479 ShowWindow(hwndLeftBtn, SW_HIDE);
1480 ShowWindow(hwndRightBtn, SW_HIDE);
1481 ShowWindow(hwndPlayBtn, SW_HIDE);
1483 if (bSelectionMode)
1484 SetWindowPos(hwndSelectBtn, HWND_TOP, rect.right-100, rect.bottom-HEADER_HEIGHT, 100, HEADER_HEIGHT, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1485 else
1486 ShowWindow(hwndSelectBtn, SW_HIDE);
1487 PositionTrackBar();
1490 bool CPicWindow::HasMultipleImages()
1492 return (((nDimensions > 1)||(nFrames > 1))&&(pSecondPic == NULL));
1495 void CPicWindow::CreateTrackbar(HWND hwndParent)
1497 HWND hwndTrack = CreateWindowEx(
1498 0, // no extended styles
1499 TRACKBAR_CLASS, // class name
1500 _T("Trackbar Control"), // title (caption)
1501 WS_CHILD | WS_VISIBLE | TBS_VERT | TBS_TOOLTIPS | TBS_AUTOTICKS, // style
1502 10, 10, // position
1503 200, 30, // size
1504 hwndParent, // parent window
1505 (HMENU)TRACKBAR_ID, // control identifier
1506 hInst, // instance
1507 NULL // no WM_CREATE parameter
1510 SendMessage(hwndTrack, TBM_SETRANGE,
1511 (WPARAM) TRUE, // redraw flag
1512 (LPARAM) MAKELONG(0, 16)); // min. & max. positions
1513 SendMessage(hwndTrack, TBM_SETTIPSIDE,
1514 (WPARAM) TBTS_TOP,
1515 (LPARAM) 0);
1517 m_AlphaSlider.ConvertTrackbarToNice(hwndTrack);
1520 void CPicWindow::BuildInfoString(TCHAR * buf, int size, bool bTooltip)
1522 // Unfortunately, we need two different strings for the tooltip
1523 // and the info box. Because the tooltips use a different tab size
1524 // than ExtTextOut(), and to keep the output aligned we therefore
1525 // need two different strings.
1526 // Note: some translations could end up with two identical strings, but
1527 // in English we need two - even if we wouldn't need two in English, some
1528 // translation might then need two again.
1529 if (pSecondPic && pTheOtherPic)
1531 _stprintf_s(buf, size,
1532 (TCHAR const *)ResString(hResource, bTooltip ? IDS_DUALIMAGEINFOTT : IDS_DUALIMAGEINFO),
1533 picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),
1534 picture.m_Width, picture.m_Height,
1535 picture.GetHorizontalResolution(), picture.GetVerticalResolution(),
1536 picture.m_ColorDepth,
1537 (UINT)GetZoom(),
1538 pSecondPic->GetFileSizeAsText().c_str(), pSecondPic->GetFileSizeAsText(false).c_str(),
1539 pSecondPic->m_Width, pSecondPic->m_Height,
1540 pSecondPic->GetHorizontalResolution(), pSecondPic->GetVerticalResolution(),
1541 pSecondPic->m_ColorDepth,
1542 (UINT)pTheOtherPic->GetZoom());
1544 else
1546 _stprintf_s(buf, size,
1547 (TCHAR const *)ResString(hResource, bTooltip ? IDS_IMAGEINFOTT : IDS_IMAGEINFO),
1548 picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),
1549 picture.m_Width, picture.m_Height,
1550 picture.GetHorizontalResolution(), picture.GetVerticalResolution(),
1551 picture.m_ColorDepth,
1552 (UINT)GetZoom());
1556 void CPicWindow::SetZoomToWidth( long width )
1558 m_linkedWidth = width;
1559 if (picture.m_Width)
1561 int zoom = width*100/picture.m_Width;
1562 SetZoom(zoom, false, true);
1566 void CPicWindow::SetZoomToHeight( long height )
1568 m_linkedHeight = height;
1569 if (picture.m_Height)
1571 int zoom = height*100/picture.m_Height;
1572 SetZoom(zoom, false, true);