Basic TGitCache work with Cache Test problem.
[TortoiseGit.git] / src / TortoiseIDiff / PicWindow.cpp
blob4a550b68055885d3ba0680a5a56a9ab1868d198e
1 // TortoiseIDiff - an image diff viewer in TortoiseSVN
3 // Copyright (C) 2006-2009 - 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"
25 #pragma comment(lib, "Msimg32.lib")
26 #pragma comment(lib, "shell32.lib")
28 bool CPicWindow::RegisterAndCreateWindow(HWND hParent)
30 WNDCLASSEX wcx;
32 // Fill in the window class structure with default parameters
33 wcx.cbSize = sizeof(WNDCLASSEX);
34 wcx.style = CS_HREDRAW | CS_VREDRAW;
35 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
36 wcx.cbClsExtra = 0;
37 wcx.cbWndExtra = 0;
38 wcx.hInstance = hResource;
39 wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
40 wcx.lpszClassName = _T("TortoiseIDiffPicWindow");
41 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEIDIFF));
42 wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
43 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEIDIFF);
44 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEIDIFF));
45 RegisterWindow(&wcx);
46 if (CreateEx(WS_EX_ACCEPTFILES | WS_EX_CLIENTEDGE, WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE, hParent))
48 ShowWindow(m_hwnd, SW_SHOW);
49 UpdateWindow(m_hwnd);
50 CreateButtons();
51 return true;
53 return false;
56 void CPicWindow::PositionTrackBar()
58 RECT rc;
59 GetClientRect(&rc);
60 HWND slider = m_AlphaSlider.GetWindow();
61 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
63 MoveWindow(slider, 0, rc.top-4+SLIDER_WIDTH, SLIDER_WIDTH, rc.bottom-rc.top-SLIDER_WIDTH+8, true);
64 ShowWindow(slider, SW_SHOW);
65 MoveWindow(hwndAlphaToggleBtn, 0, rc.top-4, SLIDER_WIDTH, SLIDER_WIDTH, true);
66 ShowWindow(hwndAlphaToggleBtn, SW_SHOW);
68 else
70 ShowWindow(slider, SW_HIDE);
71 ShowWindow(hwndAlphaToggleBtn, SW_HIDE);
75 LRESULT CALLBACK CPicWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
77 TRACKMOUSEEVENT mevt;
78 switch (uMsg)
80 case WM_CREATE:
82 // create a slider control
83 CreateTrackbar(hwnd);
84 ShowWindow(m_AlphaSlider.GetWindow(), SW_HIDE);
85 //Create the tooltips
86 TOOLINFO ti;
87 RECT rect; // for client area coordinates
89 hwndTT = CreateWindowEx(WS_EX_TOPMOST,
90 TOOLTIPS_CLASS,
91 NULL,
92 WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
93 CW_USEDEFAULT,
94 CW_USEDEFAULT,
95 CW_USEDEFAULT,
96 CW_USEDEFAULT,
97 hwnd,
98 NULL,
99 hResource,
100 NULL
103 SetWindowPos(hwndTT,
104 HWND_TOPMOST,
109 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
111 ::GetClientRect(hwnd, &rect);
113 ti.cbSize = sizeof(TOOLINFO);
114 ti.uFlags = TTF_TRACK | TTF_ABSOLUTE;
115 ti.hwnd = hwnd;
116 ti.hinst = hResource;
117 ti.uId = 0;
118 ti.lpszText = LPSTR_TEXTCALLBACK;
119 // ToolTip control will cover the whole window
120 ti.rect.left = rect.left;
121 ti.rect.top = rect.top;
122 ti.rect.right = rect.right;
123 ti.rect.bottom = rect.bottom;
125 SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
126 SendMessage(hwndTT, TTM_SETMAXTIPWIDTH, 0, 600);
127 nHSecondScrollPos = 0;
128 nVSecondScrollPos = 0;
130 break;
131 case WM_SETFOCUS:
132 case WM_KILLFOCUS:
133 InvalidateRect(*this, NULL, FALSE);
134 break;
135 case WM_ERASEBKGND:
136 return 1;
137 break;
138 case WM_PAINT:
139 Paint(hwnd);
140 break;
141 case WM_SIZE:
142 PositionTrackBar();
143 SetupScrollBars();
144 break;
145 case WM_VSCROLL:
146 if ((pSecondPic)&&((HWND)lParam == m_AlphaSlider.GetWindow()))
148 if (LOWORD(wParam) == TB_THUMBTRACK)
150 // while tracking, only redraw after 50 milliseconds
151 ::SetTimer(*this, TIMER_ALPHASLIDER, 50, NULL);
153 else
154 SetBlendAlpha(m_blend, SendMessage(m_AlphaSlider.GetWindow(), TBM_GETPOS, 0, 0) / 16.0f);
156 else
158 UINT nPos = HIWORD(wParam);
159 bool bForceUpdate = false;
160 if (LOWORD(wParam) == SB_THUMBTRACK || LOWORD(wParam) == SB_THUMBPOSITION)
162 // Get true 32-bit scroll position
163 SCROLLINFO si;
164 si.cbSize = sizeof(SCROLLINFO);
165 si.fMask = SIF_TRACKPOS;
166 GetScrollInfo(*this, SB_VERT, &si);
167 nPos = si.nTrackPos;
168 bForceUpdate = true;
171 OnVScroll(LOWORD(wParam), nPos);
172 if (bLinkedPositions)
174 pTheOtherPic->OnVScroll(LOWORD(wParam), nPos);
175 if (bForceUpdate)
176 ::UpdateWindow(*pTheOtherPic);
179 break;
180 case WM_HSCROLL:
182 UINT nPos = HIWORD(wParam);
183 bool bForceUpdate = false;
184 if (LOWORD(wParam) == SB_THUMBTRACK || LOWORD(wParam) == SB_THUMBPOSITION)
186 // Get true 32-bit scroll position
187 SCROLLINFO si;
188 si.cbSize = sizeof(SCROLLINFO);
189 si.fMask = SIF_TRACKPOS;
190 GetScrollInfo(*this, SB_VERT, &si);
191 nPos = si.nTrackPos;
192 bForceUpdate = true;
195 OnHScroll(LOWORD(wParam), nPos);
196 if (bLinkedPositions)
198 pTheOtherPic->OnHScroll(LOWORD(wParam), nPos);
199 if (bForceUpdate)
200 ::UpdateWindow(*pTheOtherPic);
203 break;
204 case WM_MOUSEWHEEL:
206 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam));
207 if (bFitSizes)
208 pTheOtherPic->OnMouseWheel(GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam));
210 break;
211 case WM_MOUSEHWHEEL:
213 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam)|MK_SHIFT, GET_WHEEL_DELTA_WPARAM(wParam));
214 if (bFitSizes)
215 pTheOtherPic->OnMouseWheel(GET_KEYSTATE_WPARAM(wParam)|MK_SHIFT, GET_WHEEL_DELTA_WPARAM(wParam));
217 break;
218 case WM_LBUTTONDOWN:
219 SetFocus(*this);
220 ptPanStart.x = GET_X_LPARAM(lParam);
221 ptPanStart.y = GET_Y_LPARAM(lParam);
222 startVScrollPos = nVScrollPos;
223 startHScrollPos = nHScrollPos;
224 startVSecondScrollPos = nVSecondScrollPos;
225 startHSecondScrollPos = nHSecondScrollPos;
226 SetCapture(*this);
227 break;
228 case WM_LBUTTONUP:
229 ReleaseCapture();
230 break;
231 case WM_MOUSELEAVE:
232 m_lastTTPos.x = 0;
233 m_lastTTPos.y = 0;
234 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);
235 break;
236 case WM_MOUSEMOVE:
238 mevt.cbSize = sizeof(TRACKMOUSEEVENT);
239 mevt.dwFlags = TME_LEAVE;
240 mevt.dwHoverTime = HOVER_DEFAULT;
241 mevt.hwndTrack = *this;
242 ::TrackMouseEvent(&mevt);
243 POINT pt = {((int)(short)LOWORD(lParam)), ((int)(short)HIWORD(lParam))};
244 if (pt.y < HEADER_HEIGHT)
246 ClientToScreen(*this, &pt);
247 if ((abs(m_lastTTPos.x - pt.x) > 20)||(abs(m_lastTTPos.y - pt.y) > 10))
249 m_lastTTPos = pt;
250 pt.x += 15;
251 pt.y += 15;
252 SendMessage(hwndTT, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));
253 TOOLINFO ti = {0};
254 ti.cbSize = sizeof(TOOLINFO);
255 ti.hwnd = *this;
256 ti.uId = 0;
257 SendMessage(hwndTT, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
260 else
262 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);
263 m_lastTTPos.x = 0;
264 m_lastTTPos.y = 0;
266 if (wParam & MK_LBUTTON)
268 // pan the image
269 int xPos = GET_X_LPARAM(lParam);
270 int yPos = GET_Y_LPARAM(lParam);
271 if (wParam & MK_CONTROL)
273 nHSecondScrollPos = startHSecondScrollPos + (ptPanStart.x - xPos);
274 nVSecondScrollPos = startVSecondScrollPos + (ptPanStart.y - yPos);
276 else
278 nHScrollPos = startHScrollPos + (ptPanStart.x - xPos);
279 nVScrollPos = startVScrollPos + (ptPanStart.y - yPos);
281 SetupScrollBars();
282 InvalidateRect(*this, NULL, TRUE);
283 UpdateWindow(*this);
284 if (bLinkedPositions)
286 pTheOtherPic->nHScrollPos = nHScrollPos;
287 pTheOtherPic->nVScrollPos = nVScrollPos;
288 pTheOtherPic->SetupScrollBars();
289 InvalidateRect(*pTheOtherPic, NULL, TRUE);
290 UpdateWindow(*pTheOtherPic);
294 break;
295 case WM_SETCURSOR:
297 // we show a hand cursor if the image can be dragged,
298 // and a hand-down cursor if the image is currently dragged
299 if ((*this == (HWND)wParam)&&(LOWORD(lParam)==HTCLIENT))
301 RECT rect;
302 GetClientRect(&rect);
303 LONG width = picture.m_Width;
304 LONG height = picture.m_Height;
305 if (pSecondPic)
307 width = max(width, pSecondPic->m_Width);
308 height = max(height, pSecondPic->m_Height);
311 if ((GetKeyState(VK_LBUTTON)&0x8000)||(HIWORD(lParam) == WM_LBUTTONDOWN))
313 SetCursor(curHandDown);
315 else
317 SetCursor(curHand);
319 return TRUE;
321 return DefWindowProc(hwnd, uMsg, wParam, lParam);
323 break;
324 case WM_DROPFILES:
326 HDROP hDrop = (HDROP)wParam;
327 TCHAR szFileName[MAX_PATH];
328 // we only use the first file dropped (if multiple files are dropped)
329 DragQueryFile(hDrop, 0, szFileName, sizeof(szFileName)/sizeof(TCHAR));
330 SetPic(szFileName, _T(""), bMainPic);
331 FitImageInWindow();
332 InvalidateRect(*this, NULL, TRUE);
334 break;
335 case WM_COMMAND:
337 switch (LOWORD(wParam))
339 case LEFTBUTTON_ID:
341 PrevImage();
342 if (bLinkedPositions)
343 pTheOtherPic->PrevImage();
344 return 0;
346 break;
347 case RIGHTBUTTON_ID:
349 NextImage();
350 if (bLinkedPositions)
351 pTheOtherPic->NextImage();
352 return 0;
354 break;
355 case PLAYBUTTON_ID:
357 bPlaying = !bPlaying;
358 Animate(bPlaying);
359 if (bLinkedPositions)
360 pTheOtherPic->Animate(bPlaying);
361 return 0;
363 break;
364 case ALPHATOGGLEBUTTON_ID:
366 WORD msg = HIWORD(wParam);
367 switch (msg)
369 case BN_DOUBLECLICKED:
371 SendMessage(hwndAlphaToggleBtn, BM_SETSTATE, 1, 0);
372 SetTimer(*this, ID_ALPHATOGGLETIMER, 1000, NULL);
374 break;
375 case BN_CLICKED:
376 KillTimer(*this, ID_ALPHATOGGLETIMER);
377 ToggleAlpha();
378 break;
380 return 0;
382 break;
383 case BLENDALPHA_ID:
385 m_blend = BLEND_ALPHA;
386 PositionTrackBar();
387 InvalidateRect(*this, NULL, TRUE);
389 break;
390 case BLENDXOR_ID:
392 m_blend = BLEND_XOR;
393 PositionTrackBar();
394 InvalidateRect(*this, NULL, TRUE);
396 break;
399 break;
400 case WM_TIMER:
402 switch (wParam)
404 case ID_ANIMATIONTIMER:
406 nCurrentFrame++;
407 if (nCurrentFrame > picture.GetNumberOfFrames(0))
408 nCurrentFrame = 1;
409 long delay = picture.SetActiveFrame(nCurrentFrame);
410 delay = max(100, delay);
411 SetTimer(*this, ID_ANIMATIONTIMER, delay, NULL);
412 InvalidateRect(*this, NULL, FALSE);
414 break;
415 case TIMER_ALPHASLIDER:
417 SetBlendAlpha(m_blend, SendMessage(m_AlphaSlider.GetWindow(), TBM_GETPOS, 0, 0)/16.0f);
418 KillTimer(*this, TIMER_ALPHASLIDER);
420 break;
421 case ID_ALPHATOGGLETIMER:
423 ToggleAlpha();
425 break;
428 break;
429 case WM_NOTIFY:
431 LPNMHDR pNMHDR = (LPNMHDR)lParam;
432 if (pNMHDR->code == TTN_GETDISPINFO)
434 if ((HWND)wParam == m_AlphaSlider.GetWindow())
436 LPTOOLTIPTEXT lpttt;
438 lpttt = (LPTOOLTIPTEXT) lParam;
439 lpttt->hinst = hResource;
440 TCHAR stringbuf[MAX_PATH] = {0};
441 _stprintf_s(stringbuf, MAX_PATH, _T("%i%% alpha"), (int)(SendMessage(m_AlphaSlider.GetWindow(),TBM_GETPOS,0,0)/16.0f*100.0f));
442 lpttt->lpszText = stringbuf;
444 else
446 NMTTDISPINFOA* pTTTA = (NMTTDISPINFOA*)pNMHDR;
447 NMTTDISPINFOW* pTTTW = (NMTTDISPINFOW*)pNMHDR;
448 BuildInfoString(m_wszTip, sizeof(m_wszTip)/sizeof(TCHAR), true);
449 if (pNMHDR->code == TTN_NEEDTEXTW)
451 pTTTW->lpszText = m_wszTip;
453 else
455 pTTTA->lpszText = m_szTip;
456 ::WideCharToMultiByte(CP_ACP, 0, m_wszTip, -1, m_szTip, 8192, NULL, NULL);
461 break;
462 case WM_DESTROY:
463 DestroyIcon(hLeft);
464 DestroyIcon(hRight);
465 DestroyIcon(hPlay);
466 DestroyIcon(hStop);
467 bWindowClosed = TRUE;
468 break;
469 default:
470 return DefWindowProc(hwnd, uMsg, wParam, lParam);
473 return 0;
476 void CPicWindow::NextImage()
478 nCurrentDimension++;
479 if (nCurrentDimension > picture.GetNumberOfDimensions())
480 nCurrentDimension = picture.GetNumberOfDimensions();
481 nCurrentFrame++;
482 if (nCurrentFrame > picture.GetNumberOfFrames(0))
483 nCurrentFrame = picture.GetNumberOfFrames(0);
484 picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);
485 InvalidateRect(*this, NULL, FALSE);
486 PositionChildren();
489 void CPicWindow::PrevImage()
491 nCurrentDimension--;
492 if (nCurrentDimension < 1)
493 nCurrentDimension = 1;
494 nCurrentFrame--;
495 if (nCurrentFrame < 1)
496 nCurrentFrame = 1;
497 picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);
498 InvalidateRect(*this, NULL, FALSE);
499 PositionChildren();
502 void CPicWindow::Animate(bool bStart)
504 if (bStart)
506 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hStop);
507 SetTimer(*this, ID_ANIMATIONTIMER, 0, NULL);
509 else
511 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);
512 KillTimer(*this, ID_ANIMATIONTIMER);
516 void CPicWindow::SetPic(tstring path, tstring title, bool bFirst)
518 bMainPic = bFirst;
519 picpath=path;pictitle=title;
520 picture.SetInterpolationMode(InterpolationModeHighQualityBicubic);
521 bValid = picture.Load(picpath);
522 nDimensions = picture.GetNumberOfDimensions();
523 if (nDimensions)
524 nFrames = picture.GetNumberOfFrames(0);
525 if (bValid)
527 picscale = 1.0;
528 PositionChildren();
529 InvalidateRect(*this, NULL, FALSE);
533 void CPicWindow::DrawViewTitle(HDC hDC, RECT * rect)
535 HFONT hFont = NULL;
536 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"));
537 HFONT hFontOld = (HFONT)SelectObject(hDC, (HGDIOBJ)hFont);
539 RECT textrect;
540 textrect.left = rect->left;
541 textrect.top = rect->top;
542 textrect.right = rect->right;
543 textrect.bottom = rect->top + HEADER_HEIGHT;
544 if (HasMultipleImages())
545 textrect.bottom += HEADER_HEIGHT;
547 COLORREF crBk, crFg;
548 crBk = ::GetSysColor(COLOR_SCROLLBAR);
549 crFg = ::GetSysColor(COLOR_WINDOWTEXT);
550 SetBkColor(hDC, crBk);
551 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &textrect, NULL, 0, NULL);
553 if (GetFocus() == *this)
554 DrawEdge(hDC, &textrect, EDGE_BUMP, BF_RECT);
555 else
556 DrawEdge(hDC, &textrect, EDGE_ETCHED, BF_RECT);
558 SetTextColor(hDC, crFg);
560 // use the path if no title is set.
561 tstring * title = pictitle.empty() ? &picpath : &pictitle;
563 tstring realtitle = *title;
564 tstring imgnumstring;
566 if (HasMultipleImages())
568 TCHAR buf[MAX_PATH];
569 if (nFrames > 1)
570 _stprintf_s(buf, sizeof(buf)/sizeof(TCHAR), (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentFrame, nFrames);
571 else
572 _stprintf_s(buf, sizeof(buf)/sizeof(TCHAR), (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentDimension, nDimensions);
573 imgnumstring = buf;
576 SIZE stringsize;
577 if (GetTextExtentPoint32(hDC, realtitle.c_str(), (int)realtitle.size(), &stringsize))
579 int nStringLength = stringsize.cx;
580 int texttop = pSecondPic ? textrect.top + (HEADER_HEIGHT/2) - stringsize.cy : textrect.top + (HEADER_HEIGHT/2) - stringsize.cy/2;
581 ExtTextOut(hDC,
582 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
583 texttop,
584 ETO_CLIPPED,
585 &textrect,
586 realtitle.c_str(),
587 (UINT)realtitle.size(),
588 NULL);
589 if (pSecondPic)
591 realtitle = (pictitle2.empty() ? picpath2 : pictitle2);
592 ExtTextOut(hDC,
593 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
594 texttop + stringsize.cy,
595 ETO_CLIPPED,
596 &textrect,
597 realtitle.c_str(),
598 (UINT)realtitle.size(),
599 NULL);
602 if (HasMultipleImages())
604 if (GetTextExtentPoint32(hDC, imgnumstring.c_str(), (int)imgnumstring.size(), &stringsize))
606 int nStringLength = stringsize.cx;
608 ExtTextOut(hDC,
609 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
610 textrect.top + HEADER_HEIGHT + (HEADER_HEIGHT/2) - stringsize.cy/2,
611 ETO_CLIPPED,
612 &textrect,
613 imgnumstring.c_str(),
614 (UINT)imgnumstring.size(),
615 NULL);
618 SelectObject(hDC, (HGDIOBJ)hFontOld);
619 DeleteObject(hFont);
622 void CPicWindow::SetupScrollBars()
624 RECT rect;
625 GetClientRect(&rect);
627 SCROLLINFO si = {sizeof(si)};
629 si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE | SIF_DISABLENOSCROLL;
631 double width = double(picture.m_Width)*picscale;
632 double height = double(picture.m_Height)*picscale;
633 if (pSecondPic)
635 width = max(width, double(pSecondPic->m_Width)*pTheOtherPic->GetZoom());
636 height = max(height, double(pSecondPic->m_Height)*pTheOtherPic->GetZoom());
639 bool bShowHScrollBar = (nHScrollPos > 0); // left of pic is left of window
640 bShowHScrollBar = bShowHScrollBar || (width-nHScrollPos > rect.right); // right of pic is outside right of window
641 bShowHScrollBar = bShowHScrollBar || (width+nHScrollPos > rect.right); // right of pic is outside right of window
642 bool bShowVScrollBar = (nVScrollPos > 0); // top of pic is above window
643 bShowVScrollBar = bShowVScrollBar || (height-nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window
644 bShowVScrollBar = bShowVScrollBar || (height+nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window
646 // if the image is smaller than the window, we don't need the scrollbars
647 ShowScrollBar(*this, SB_HORZ, bShowHScrollBar);
648 ShowScrollBar(*this, SB_VERT, bShowVScrollBar);
650 if (bShowVScrollBar)
652 si.nPos = nVScrollPos;
653 si.nPage = rect.bottom-rect.top;
654 if (height < rect.bottom-rect.top)
656 if (nVScrollPos > 0)
658 si.nMin = 0;
659 si.nMax = rect.bottom+nVScrollPos-rect.top;
661 else
663 si.nMin = nVScrollPos;
664 si.nMax = int(height);
667 else
669 if (nVScrollPos > 0)
671 si.nMin = 0;
672 si.nMax = int(max(height, rect.bottom+nVScrollPos-rect.top));
674 else
676 si.nMin = 0;
677 si.nMax = int(height-nVScrollPos);
680 SetScrollInfo(*this, SB_VERT, &si, TRUE);
683 if (bShowHScrollBar)
685 si.nPos = nHScrollPos;
686 si.nPage = rect.right-rect.left;
687 if (width < rect.right-rect.left)
689 if (nHScrollPos > 0)
691 si.nMin = 0;
692 si.nMax = rect.right+nHScrollPos-rect.left;
694 else
696 si.nMin = nHScrollPos;
697 si.nMax = int(width);
700 else
702 if (nHScrollPos > 0)
704 si.nMin = 0;
705 si.nMax = int(max(width, rect.right+nHScrollPos-rect.left));
707 else
709 si.nMin = 0;
710 si.nMax = int(width-nHScrollPos);
713 SetScrollInfo(*this, SB_HORZ, &si, TRUE);
716 PositionChildren();
719 void CPicWindow::OnVScroll(UINT nSBCode, UINT nPos)
721 RECT rect;
722 GetClientRect(&rect);
724 switch (nSBCode)
726 case SB_BOTTOM:
727 nVScrollPos = LONG(double(picture.GetHeight())*picscale);;
728 break;
729 case SB_TOP:
730 nVScrollPos = 0;
731 break;
732 case SB_LINEDOWN:
733 nVScrollPos++;
734 break;
735 case SB_LINEUP:
736 nVScrollPos--;
737 break;
738 case SB_PAGEDOWN:
739 nVScrollPos += (rect.bottom-rect.top);
740 break;
741 case SB_PAGEUP:
742 nVScrollPos -= (rect.bottom-rect.top);
743 break;
744 case SB_THUMBPOSITION:
745 nVScrollPos = nPos;
746 break;
747 case SB_THUMBTRACK:
748 nVScrollPos = nPos;
749 break;
750 default:
751 return;
753 LONG height = LONG(double(picture.GetHeight())*picscale);
754 if (pSecondPic)
756 height = max(height, LONG(double(pSecondPic->GetHeight())*picscale));
758 SetupScrollBars();
759 PositionChildren();
760 InvalidateRect(*this, NULL, TRUE);
763 void CPicWindow::OnHScroll(UINT nSBCode, UINT nPos)
765 RECT rect;
766 GetClientRect(&rect);
768 switch (nSBCode)
770 case SB_RIGHT:
771 nHScrollPos = LONG(double(picture.GetWidth())*picscale);
772 break;
773 case SB_LEFT:
774 nHScrollPos = 0;
775 break;
776 case SB_LINERIGHT:
777 nHScrollPos++;
778 break;
779 case SB_LINELEFT:
780 nHScrollPos--;
781 break;
782 case SB_PAGERIGHT:
783 nHScrollPos += (rect.right-rect.left);
784 break;
785 case SB_PAGELEFT:
786 nHScrollPos -= (rect.right-rect.left);
787 break;
788 case SB_THUMBPOSITION:
789 nHScrollPos = nPos;
790 break;
791 case SB_THUMBTRACK:
792 nHScrollPos = nPos;
793 break;
794 default:
795 return;
797 LONG width = LONG(double(picture.GetWidth())*picscale);
798 if (pSecondPic)
800 width = max(width, LONG(double(pSecondPic->GetWidth())*picscale));
802 SetupScrollBars();
803 PositionChildren();
804 InvalidateRect(*this, NULL, TRUE);
807 void CPicWindow::OnMouseWheel(short fwKeys, short zDelta)
809 RECT rect;
810 GetClientRect(&rect);
811 LONG width = long(double(picture.m_Width)*picscale);
812 LONG height = long(double(picture.m_Height)*picscale);
813 if (pSecondPic)
815 width = max(width, long(double(pSecondPic->m_Width)*picscale));
816 height = max(height, long(double(pSecondPic->m_Height)*picscale));
818 if ((fwKeys & MK_SHIFT)&&(fwKeys & MK_CONTROL)&&(pSecondPic))
820 // ctrl+shift+wheel: change the alpha channel
821 float a = blendAlpha;
822 a -= float(zDelta)/120.0f/4.0f;
823 if (a < 0.0f)
824 a = 0.0f;
825 else if (a > 1.0f)
826 a = 1.0f;
827 SetBlendAlpha(m_blend, a);
829 else if (fwKeys & MK_SHIFT)
831 // shift means scrolling sideways
832 OnHScroll(SB_THUMBPOSITION, GetHPos()-zDelta);
833 if ((bLinkedPositions)&&(pTheOtherPic))
835 pTheOtherPic->OnHScroll(SB_THUMBPOSITION, pTheOtherPic->GetHPos()-zDelta);
838 else if (fwKeys & MK_CONTROL)
840 // control means adjusting the scale factor
841 Zoom(zDelta>0, true);
842 if ((!bFitSizes)&&(pTheOtherPic))
843 pTheOtherPic->Zoom(zDelta>0, true);
844 PositionChildren();
845 InvalidateRect(*this, NULL, FALSE);
846 SetWindowPos(*this, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOREPOSITION|SWP_NOMOVE);
848 else
850 OnVScroll(SB_THUMBPOSITION, GetVPos()-zDelta);
851 if ((bLinkedPositions)&&(pTheOtherPic))
853 pTheOtherPic->OnVScroll(SB_THUMBPOSITION, pTheOtherPic->GetVPos()-zDelta);
858 void CPicWindow::GetClientRect(RECT * pRect)
860 ::GetClientRect(*this, pRect);
861 pRect->top += HEADER_HEIGHT;
862 if (HasMultipleImages())
864 pRect->top += HEADER_HEIGHT;
866 if (pSecondPic)
867 pRect->left += SLIDER_WIDTH;
870 void CPicWindow::GetClientRectWithScrollbars(RECT * pRect)
872 GetClientRect(pRect);
873 ::GetWindowRect(*this, pRect);
874 pRect->right = pRect->right-pRect->left;
875 pRect->bottom = pRect->bottom-pRect->top;
876 pRect->top = 0;
877 pRect->left = 0;
878 pRect->top += HEADER_HEIGHT;
879 if (HasMultipleImages())
881 pRect->top += HEADER_HEIGHT;
883 if (pSecondPic)
884 pRect->left += SLIDER_WIDTH;
888 void CPicWindow::SetZoom(double dZoom, bool centermouse)
890 // Set the interpolation mode depending on zoom
891 double oldPicscale = picscale;
892 if (dZoom < 1.0)
893 { // Zoomed out, use high quality bicubic
894 picture.SetInterpolationMode(InterpolationModeHighQualityBicubic);
895 if (pSecondPic)
896 pSecondPic->SetInterpolationMode(InterpolationModeHighQualityBicubic);
898 else if (!((int)(dZoom*100.0)%100))
899 { // "Even" zoom sizes should be shown w-o any interpolation
900 picture.SetInterpolationMode(InterpolationModeNearestNeighbor);
901 if (pSecondPic)
902 pSecondPic->SetInterpolationMode(InterpolationModeNearestNeighbor);
904 else
905 { // Arbitrary zoomed in, use bilinear that is semi-smoothed
906 picture.SetInterpolationMode(InterpolationModeBilinear);
907 if (pSecondPic)
908 pSecondPic->SetInterpolationMode(InterpolationModeBilinear);
910 picscale = dZoom;
912 if ((bFitSizes)&&(bMainPic))
914 double width, height;
915 double zoomWidth, zoomHeight;
916 width = double(picture.m_Width)*dZoom;
917 height = double(picture.m_Height)*dZoom;
918 zoomWidth = width/double(pTheOtherPic->GetPic()->m_Width);
919 zoomHeight = height/double(pTheOtherPic->GetPic()->m_Height);
920 pTheOtherPic->SetZoom(min(zoomWidth, zoomHeight), centermouse);
923 // adjust the scrollbar positions according to the new zoom and the
924 // mouse position: if possible, keep the pixel where the mouse pointer
925 // is at the same position after the zoom
926 POINT cpos;
927 DWORD ptW = GetMessagePos();
928 cpos.x = GET_X_LPARAM(ptW);
929 cpos.y = GET_Y_LPARAM(ptW);
930 ScreenToClient(*this, &cpos);
931 RECT clientrect;
932 GetClientRect(&clientrect);
933 if ((PtInRect(&clientrect, cpos))&&(centermouse))
935 // the mouse pointer is over our window
936 nHScrollPos = int(double(nHScrollPos + cpos.x)*(dZoom/oldPicscale))-cpos.x;
937 nVScrollPos = int(double(nVScrollPos + cpos.y)*(dZoom/oldPicscale))-cpos.y;
939 else
941 nHScrollPos = int(double(nHScrollPos + ((clientrect.right-clientrect.left)/2))*(dZoom/oldPicscale))-((clientrect.right-clientrect.left)/2);
942 nVScrollPos = int(double(nVScrollPos + ((clientrect.bottom-clientrect.top)/2))*(dZoom/oldPicscale))-((clientrect.bottom-clientrect.top)/2);
945 SetupScrollBars();
946 PositionChildren();
947 InvalidateRect(*this, NULL, TRUE);
950 void CPicWindow::Zoom(bool in, bool centermouse)
952 double zoomFactor;
954 // Find correct zoom factor and quantize picscale
955 if (!in && picscale <= 0.2)
957 picscale = 0.1;
958 zoomFactor = 0;
960 else if ((in && picscale < 1.0) || (!in && picscale <= 1.0))
962 picscale = 0.1 * RoundDouble(picscale/0.1, 0); // Quantize to 0.1
963 zoomFactor = 0.1;
965 else if ((in && picscale < 2.0) || (!in && picscale <= 2.0))
967 picscale = 0.25 * RoundDouble(picscale/0.25, 0); // Quantize to 0.25
968 zoomFactor = 0.25;
970 else
972 picscale = RoundDouble(picscale,0);
973 zoomFactor = 1;
976 // Set zoom
977 if (in)
979 if ((pSecondPic)&&(!bFitSizes))
980 pTheOtherPic->SetZoom(pTheOtherPic->GetZoom()+zoomFactor, false);
981 SetZoom(picscale+zoomFactor, centermouse);
983 else
985 if ((pSecondPic)&&(!bFitSizes))
986 pTheOtherPic->SetZoom(pTheOtherPic->GetZoom()-zoomFactor, false);
987 SetZoom(picscale-zoomFactor, centermouse);
991 double CPicWindow::RoundDouble(double doValue, int nPrecision)
993 static const double doBase = 10.0;
994 double doComplete5, doComplete5i;
996 doComplete5 = doValue * pow(doBase, (double) (nPrecision + 1));
998 if (doValue < 0.0)
1000 doComplete5 -= 5.0;
1002 else
1004 doComplete5 += 5.0;
1007 doComplete5 /= doBase;
1008 modf(doComplete5, &doComplete5i);
1010 return doComplete5i / pow(doBase, (double) nPrecision);
1012 void CPicWindow::FitImageInWindow()
1014 RECT rect;
1015 double dZoom = 1.0;
1017 GetClientRectWithScrollbars(&rect);
1019 if (rect.right-rect.left)
1021 if (((rect.right - rect.left) > picture.m_Width+2)&&((rect.bottom - rect.top)> picture.m_Height+2))
1023 // image is smaller than the window
1024 dZoom = 1.0;
1026 else
1028 // image is bigger than the window
1029 double xscale = double(rect.right-rect.left-2)/double(picture.m_Width);
1030 double yscale = double(rect.bottom-rect.top-2)/double(picture.m_Height);
1031 dZoom = min(yscale, xscale);
1033 if (pSecondPic)
1035 if (((rect.right - rect.left) > pSecondPic->m_Width+2)&&((rect.bottom - rect.top)> pSecondPic->m_Height+2))
1037 // image is smaller than the window
1038 pTheOtherPic->SetZoom(min(1.0, dZoom), false);
1040 else
1042 // image is bigger than the window
1043 double xscale = double(rect.right-rect.left-2)/double(pSecondPic->m_Width);
1044 double yscale = double(rect.bottom-rect.top-2)/double(pSecondPic->m_Height);
1045 pTheOtherPic->SetZoom(min(yscale, xscale), false);
1047 nHSecondScrollPos = 0;
1048 nVSecondScrollPos = 0;
1050 SetZoom(dZoom, false);
1052 CenterImage();
1053 PositionChildren();
1054 InvalidateRect(*this, NULL, TRUE);
1057 void CPicWindow::CenterImage()
1059 RECT rect;
1060 GetClientRectWithScrollbars(&rect);
1061 double width = (double(picture.m_Width)*picscale) + 2.0;
1062 double height = (double(picture.m_Height)*picscale) + 2.0;
1063 if (pSecondPic)
1065 width = max(width, (double(pSecondPic->m_Width)*pTheOtherPic->GetZoom()) + 2.0);
1066 height = max(height, (double(pSecondPic->m_Height)*pTheOtherPic->GetZoom()) + 2.0);
1069 bool bPicWidthBigger = (int(width) > (rect.right-rect.left));
1070 bool bPicHeightBigger = (int(height) > (rect.bottom-rect.top));
1071 // set the scroll position so that the image is drawn centered in the window
1072 // if the window is bigger than the image
1073 if (!bPicWidthBigger)
1075 nHScrollPos = -((rect.right-rect.left+4)-int(width))/2;
1077 if (!bPicHeightBigger)
1079 nVScrollPos = -((rect.bottom-rect.top+4)-int(height))/2;
1081 SetupScrollBars();
1084 void CPicWindow::FitSizes(bool bFit)
1086 bFitSizes = bFit;
1088 if (bFitSizes)
1090 nHSecondScrollPos = 0;
1091 nVSecondScrollPos = 0;
1093 SetZoom(GetZoom(), false);
1096 void CPicWindow::ShowPicWithBorder(HDC hdc, const RECT &bounds, CPicture &pic, double scale)
1098 ::SetBkColor(hdc, transparentColor);
1099 ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1101 RECT picrect;
1102 picrect.left = bounds.left - nHScrollPos;
1103 picrect.top = bounds.top - nVScrollPos;
1104 if (!bLinkedPositions && (pTheOtherPic) && (&pic != &picture))
1106 picrect.left = bounds.left - nHSecondScrollPos;
1107 picrect.top = bounds.top - nVSecondScrollPos;
1109 picrect.right = (picrect.left + LONG(double(pic.m_Width) * scale));
1110 picrect.bottom = (picrect.top + LONG(double(pic.m_Height) * scale));
1112 pic.Show(hdc, picrect);
1114 RECT border;
1115 border.left = picrect.left-1;
1116 border.top = picrect.top-1;
1117 border.right = picrect.right+1;
1118 border.bottom = picrect.bottom+1;
1120 HPEN hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW));
1121 HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
1122 MoveToEx(hdc, border.left, border.top, NULL);
1123 LineTo(hdc, border.left, border.bottom);
1124 LineTo(hdc, border.right, border.bottom);
1125 LineTo(hdc, border.right, border.top);
1126 LineTo(hdc, border.left, border.top);
1127 SelectObject(hdc, hOldPen);
1128 DeleteObject(hPen);
1131 void CPicWindow::Paint(HWND hwnd)
1133 PAINTSTRUCT ps;
1134 HDC hdc;
1135 RECT rect, fullrect;
1137 GetUpdateRect(hwnd, &rect, FALSE);
1138 if (IsRectEmpty(&rect))
1139 return;
1141 ::GetClientRect(*this, &fullrect);
1142 hdc = BeginPaint(hwnd, &ps);
1144 // Exclude the alpha control and button
1145 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
1146 ExcludeClipRect(hdc, 0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4);
1148 CMyMemDC memDC(hdc);
1149 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))
1151 // erase the place where the alpha slider would be
1152 ::SetBkColor(memDC, transparentColor);
1153 RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};
1154 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1157 GetClientRect(&rect);
1158 if (bValid)
1160 ShowPicWithBorder(memDC, rect, picture, picscale);
1161 if (pSecondPic)
1163 HDC secondhdc = CreateCompatibleDC(hdc);
1164 HBITMAP hBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
1165 HBITMAP hOldBitmap = (HBITMAP)SelectObject(secondhdc, hBitmap);
1166 SetWindowOrgEx(secondhdc, rect.left, rect.top, NULL);
1168 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))
1170 // erase the place where the alpha slider would be
1171 ::SetBkColor(secondhdc, transparentColor);
1172 RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};
1173 ::ExtTextOut(secondhdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1175 ShowPicWithBorder(secondhdc, rect, *pSecondPic, pTheOtherPic->GetZoom());
1177 if (m_blend == BLEND_ALPHA)
1179 BLENDFUNCTION blender;
1180 blender.AlphaFormat = 0;
1181 blender.BlendFlags = 0;
1182 blender.BlendOp = AC_SRC_OVER;
1183 blender.SourceConstantAlpha = (BYTE)(blendAlpha*255);
1184 AlphaBlend(memDC,
1185 rect.left,
1186 rect.top,
1187 rect.right-rect.left,
1188 rect.bottom-rect.top,
1189 secondhdc,
1190 rect.left,
1191 rect.top,
1192 rect.right-rect.left,
1193 rect.bottom-rect.top,
1194 blender);
1196 else if (m_blend == BLEND_XOR)
1198 BitBlt(memDC,
1199 rect.left,
1200 rect.top,
1201 rect.right-rect.left,
1202 rect.bottom-rect.top,
1203 secondhdc,
1204 rect.left,
1205 rect.top,
1206 //rect.right-rect.left,
1207 //rect.bottom-rect.top,
1208 SRCINVERT);
1209 InvertRect(memDC, &rect);
1211 SelectObject(secondhdc, hOldBitmap);
1212 DeleteObject(hBitmap);
1213 DeleteDC(secondhdc);
1215 int sliderwidth = 0;
1216 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
1217 sliderwidth = SLIDER_WIDTH;
1218 m_inforect.left = rect.left+4+sliderwidth;
1219 m_inforect.top = rect.top;
1220 m_inforect.right = rect.right+sliderwidth;
1221 m_inforect.bottom = rect.bottom;
1223 SetBkColor(memDC, transparentColor);
1224 if (bShowInfo)
1226 TCHAR infostring[8192];
1227 BuildInfoString(infostring, sizeof(infostring)/sizeof(TCHAR), false);
1228 // set the font
1229 NONCLIENTMETRICS metrics = {0};
1230 metrics.cbSize = sizeof(NONCLIENTMETRICS);
1231 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);
1232 HFONT hFont = CreateFontIndirect(&metrics.lfStatusFont);
1233 HFONT hFontOld = (HFONT)SelectObject(memDC, (HGDIOBJ)hFont);
1234 // find out how big the rectangle for the text has to be
1235 DrawText(memDC, infostring, -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER | DT_CALCRECT);
1237 // the text should be drawn with a four pixel offset to the window borders
1238 m_inforect.top = rect.bottom - (m_inforect.bottom-m_inforect.top) - 4;
1239 m_inforect.bottom = rect.bottom-4;
1241 // first draw an edge rectangle
1242 RECT edgerect;
1243 edgerect.left = m_inforect.left-4;
1244 edgerect.top = m_inforect.top-4;
1245 edgerect.right = m_inforect.right+4;
1246 edgerect.bottom = m_inforect.bottom+4;
1247 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &edgerect, NULL, 0, NULL);
1248 DrawEdge(memDC, &edgerect, EDGE_BUMP, BF_RECT | BF_SOFT);
1250 SetTextColor(memDC, GetSysColor(COLOR_WINDOWTEXT));
1251 DrawText(memDC, infostring, -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER);
1252 SelectObject(memDC, (HGDIOBJ)hFontOld);
1253 DeleteObject(hFont);
1256 else
1258 SetBkColor(memDC, ::GetSysColor(COLOR_WINDOW));
1259 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
1260 SIZE stringsize;
1261 ResString str = ResString(hResource, IDS_INVALIDIMAGEINFO);
1262 if (GetTextExtentPoint32(memDC, str, (int)_tcslen(str), &stringsize))
1264 int nStringLength = stringsize.cx;
1266 ExtTextOut(memDC,
1267 max(rect.left + ((rect.right-rect.left)-nStringLength)/2, 1),
1268 rect.top + ((rect.bottom-rect.top) - stringsize.cy)/2,
1269 ETO_CLIPPED,
1270 &rect,
1271 str,
1272 (UINT)_tcslen(str),
1273 NULL);
1276 DrawViewTitle(memDC, &fullrect);
1278 EndPaint(hwnd, &ps);
1281 bool CPicWindow::CreateButtons()
1283 // Ensure that the common control DLL is loaded.
1284 INITCOMMONCONTROLSEX icex;
1285 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
1286 icex.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES;
1287 InitCommonControlsEx(&icex);
1289 hwndLeftBtn = CreateWindowEx(0,
1290 _T("BUTTON"),
1291 (LPCTSTR)NULL,
1292 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1293 0, 0, 0, 0,
1294 (HWND)*this,
1295 (HMENU)LEFTBUTTON_ID,
1296 hResource,
1297 NULL);
1298 if (hwndLeftBtn == INVALID_HANDLE_VALUE)
1299 return false;
1300 hLeft = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_BACKWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1301 SendMessage(hwndLeftBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hLeft);
1302 hwndRightBtn = CreateWindowEx(0,
1303 _T("BUTTON"),
1304 (LPCTSTR)NULL,
1305 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1306 0, 0, 0, 0,
1307 *this,
1308 (HMENU)RIGHTBUTTON_ID,
1309 hResource,
1310 NULL);
1311 if (hwndRightBtn == INVALID_HANDLE_VALUE)
1312 return false;
1313 hRight = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_FORWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1314 SendMessage(hwndRightBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hRight);
1315 hwndPlayBtn = CreateWindowEx(0,
1316 _T("BUTTON"),
1317 (LPCTSTR)NULL,
1318 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1319 0, 0, 0, 0,
1320 *this,
1321 (HMENU)PLAYBUTTON_ID,
1322 hResource,
1323 NULL);
1324 if (hwndPlayBtn == INVALID_HANDLE_VALUE)
1325 return false;
1326 hPlay = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_START), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1327 hStop = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1328 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);
1329 hwndAlphaToggleBtn = CreateWindowEx(0,
1330 _T("BUTTON"),
1331 (LPCTSTR)NULL,
1332 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT | BS_NOTIFY | BS_PUSHLIKE,
1333 0, 0, 0, 0,
1334 (HWND)*this,
1335 (HMENU)ALPHATOGGLEBUTTON_ID,
1336 hResource,
1337 NULL);
1338 if (hwndAlphaToggleBtn == INVALID_HANDLE_VALUE)
1339 return false;
1340 hAlphaToggle = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_ALPHATOGGLE), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1341 SendMessage(hwndAlphaToggleBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hAlphaToggle);
1343 return true;
1346 void CPicWindow::PositionChildren()
1348 RECT rect;
1349 ::GetClientRect(*this, &rect);
1350 if (HasMultipleImages())
1352 SetWindowPos(hwndLeftBtn, HWND_TOP, rect.left+3, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1353 SetWindowPos(hwndRightBtn, HWND_TOP, rect.left+23, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1354 if (nFrames > 1)
1355 SetWindowPos(hwndPlayBtn, HWND_TOP, rect.left+43, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1356 else
1357 ShowWindow(hwndPlayBtn, SW_HIDE);
1359 else
1361 ShowWindow(hwndLeftBtn, SW_HIDE);
1362 ShowWindow(hwndRightBtn, SW_HIDE);
1363 ShowWindow(hwndPlayBtn, SW_HIDE);
1365 PositionTrackBar();
1368 bool CPicWindow::HasMultipleImages()
1370 return (((nDimensions > 1)||(nFrames > 1))&&(pSecondPic == NULL));
1373 void CPicWindow::CreateTrackbar(HWND hwndParent)
1375 HWND hwndTrack = CreateWindowEx(
1376 0, // no extended styles
1377 TRACKBAR_CLASS, // class name
1378 _T("Trackbar Control"), // title (caption)
1379 WS_CHILD | WS_VISIBLE | TBS_VERT | TBS_TOOLTIPS | TBS_AUTOTICKS, // style
1380 10, 10, // position
1381 200, 30, // size
1382 hwndParent, // parent window
1383 (HMENU)TRACKBAR_ID, // control identifier
1384 hInst, // instance
1385 NULL // no WM_CREATE parameter
1388 SendMessage(hwndTrack, TBM_SETRANGE,
1389 (WPARAM) TRUE, // redraw flag
1390 (LPARAM) MAKELONG(0, 16)); // min. & max. positions
1391 SendMessage(hwndTrack, TBM_SETTIPSIDE,
1392 (WPARAM) TBTS_TOP,
1393 (LPARAM) 0);
1395 m_AlphaSlider.ConvertTrackbarToNice(hwndTrack);
1398 void CPicWindow::BuildInfoString(TCHAR * buf, int size, bool bTooltip)
1400 // Unfortunately, we need two different strings for the tooltip
1401 // and the info box. Because the tooltips use a different tab size
1402 // than ExtTextOut(), and to keep the output aligned we therefore
1403 // need two different strings.
1404 // Note: some translations could end up with two identical strings, but
1405 // in English we need two - even if we wouldn't need two in English, some
1406 // translation might then need two again.
1407 if (pSecondPic)
1409 _stprintf_s(buf, size,
1410 (TCHAR const *)ResString(hResource, bTooltip ? IDS_DUALIMAGEINFOTT : IDS_DUALIMAGEINFO),
1411 picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),
1412 picture.m_Width, picture.m_Height,
1413 picture.GetHorizontalResolution(), picture.GetVerticalResolution(),
1414 picture.m_ColorDepth,
1415 (UINT)(GetZoom()*100.0),
1416 pSecondPic->GetFileSizeAsText().c_str(), pSecondPic->GetFileSizeAsText(false).c_str(),
1417 pSecondPic->m_Width, pSecondPic->m_Height,
1418 pSecondPic->GetHorizontalResolution(), pSecondPic->GetVerticalResolution(),
1419 pSecondPic->m_ColorDepth,
1420 (UINT)(pTheOtherPic->GetZoom()*100.0));
1422 else
1424 _stprintf_s(buf, size,
1425 (TCHAR const *)ResString(hResource, bTooltip ? IDS_IMAGEINFOTT : IDS_IMAGEINFO),
1426 picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),
1427 picture.m_Width, picture.m_Height,
1428 picture.GetHorizontalResolution(), picture.GetVerticalResolution(),
1429 picture.m_ColorDepth,
1430 (UINT)(GetZoom()*100.0));