ec42a33bf69d0d11ea08c400aa8e5af7f0931959
[reactos.git] / rosapps / smartpdf / baseutils / ms_ui_helper.c
1 /*++
2
3 Copyright (c) 2003 Microsoft Corporation
4
5 Abstract:
6
7 Helper functions for HIDPI/Landscape support.
8
9 --*/
10
11 #include "ms_ui_helper.h"
12
13 HIDPI_ENABLE;
14
15 BOOL HIDPI_StretchBitmap(
16 HBITMAP* phbm,
17 int cxDstImg,
18 int cyDstImg,
19 int cImagesX,
20 int cImagesY
21 )
22 {
23 BOOL fRet = FALSE;
24 HBITMAP hbmNew;
25 BITMAP bm;
26 HDC hdcSrc, hdcDst, hdcScreen;
27 HBITMAP hbmOldSrc, hbmOldDst;
28 int cxSrcImg, cySrcImg;
29 int i, j, xDest, yDest, xBmp, yBmp;
30
31 if (!phbm || !*phbm || (cxDstImg == 0 && cyDstImg == 0) || (cImagesX == 0 || cImagesY == 0))
32 goto donestretch;
33
34 if ((sizeof(bm) != GetObject(*phbm, sizeof(bm), &bm)))
35 goto donestretch;
36
37 // If you hit this ASSERT, that mean your passed in image count in row and
38 // the column number of images is not correct.
39 // ASSERT(((bm.bmWidth % cImagesX) == 0) && ((bm.bmHeight % cImagesY) == 0));
40
41 cxSrcImg = bm.bmWidth / cImagesX;
42 cySrcImg = bm.bmHeight / cImagesY;
43
44 if (cxSrcImg == cxDstImg && cySrcImg == cyDstImg)
45 {
46 fRet = TRUE;
47 goto donestretch;
48 }
49
50 if (cxDstImg == 0)
51 cxDstImg = HIDPIMulDiv(cyDstImg, cxSrcImg, cySrcImg);
52 else if (cyDstImg == 0)
53 cyDstImg = HIDPIMulDiv(cxDstImg, cySrcImg, cxSrcImg);
54
55 hdcSrc = CreateCompatibleDC(NULL);
56 hdcDst = CreateCompatibleDC(NULL);
57 hdcScreen = GetDC(NULL);
58 hbmOldSrc = (HBITMAP)SelectObject(hdcSrc, *phbm);
59 hbmNew = CreateCompatibleBitmap(hdcScreen, cxDstImg * cImagesX, cyDstImg * cImagesY);
60 hbmOldDst = (HBITMAP)SelectObject(hdcDst, hbmNew);
61 ReleaseDC(NULL, hdcScreen);
62
63 // BLAST!
64 for (j = 0, yDest = 0, yBmp = 0; j < cImagesY; j++, yDest += cyDstImg, yBmp += cySrcImg)
65 {
66 for (i = 0, xDest = 0, xBmp = 0; i < cImagesX; i++, xDest += cxDstImg, xBmp += cxSrcImg)
67 {
68 StretchBlt(hdcDst, xDest, yDest, cxDstImg, cyDstImg,
69 hdcSrc, xBmp, yBmp, cxSrcImg, cySrcImg,
70 SRCCOPY);
71 }
72 }
73
74 // Free allocated memory
75 SelectObject(hdcSrc, hbmOldSrc);
76 SelectObject(hdcDst, hbmOldDst);
77 DeleteDC(hdcSrc);
78 DeleteDC(hdcDst);
79
80 // Delete the passed in bitmap
81 DeleteObject(*phbm);
82 *phbm = hbmNew;
83
84 fRet = TRUE;
85
86 donestretch:
87 return fRet;
88 }
89
90 static BOOL HIDPI_StretchIcon_Internal(
91 HICON hiconIn,
92 HICON* phiconOut,
93 int cxIcon,
94 int cyIcon
95 )
96 {
97 ICONINFO iconinfo;
98
99 HDC hdc;
100 HBITMAP hbmImage, hbmMask;
101 HBITMAP hbmOld;
102 BOOL fDrawMaskOK;
103 BOOL fDrawImageOK;
104
105 *phiconOut = NULL;
106 hdc = CreateCompatibleDC(NULL);
107
108 hbmMask = CreateCompatibleBitmap(hdc, cxIcon, cyIcon);
109 hbmOld = (HBITMAP)SelectObject(hdc, hbmMask);
110 fDrawMaskOK = DrawIconEx(hdc, 0, 0, hiconIn, cxIcon, cyIcon, 0, NULL, DI_MASK);
111 SelectObject(hdc, hbmOld);
112
113 hbmImage = CreateBitmap(cxIcon, cyIcon, 1, GetDeviceCaps(hdc, BITSPIXEL), NULL);
114 hbmOld = (HBITMAP)SelectObject(hdc, hbmImage);
115 fDrawImageOK = DrawIconEx(hdc, 0, 0, hiconIn, cxIcon, cyIcon, 0, NULL, DI_IMAGE);
116 SelectObject(hdc, hbmOld);
117
118 if (fDrawImageOK && fDrawMaskOK)
119 {
120 iconinfo.fIcon = TRUE;
121 iconinfo.hbmColor = hbmImage;
122 iconinfo.hbmMask = hbmMask;
123 *phiconOut = CreateIconIndirect(&iconinfo);
124 }
125
126 DeleteObject(hbmImage);
127 DeleteObject(hbmMask);
128
129 DeleteDC(hdc);
130
131 return (fDrawImageOK && fDrawMaskOK && *phiconOut != NULL) ? TRUE : FALSE;
132 }
133
134 BOOL HIDPI_StretchIcon(
135 HICON* phic,
136 int cxIcon,
137 int cyIcon
138 )
139 {
140 HICON hiconOut;
141
142 if (HIDPI_StretchIcon_Internal(*phic, &hiconOut, cxIcon, cyIcon))
143 {
144 DestroyIcon(*phic);
145 *phic = hiconOut;
146 return TRUE;
147 }
148
149 return FALSE;
150 }
151
152
153 BOOL HIDPI_GetBitmapLogPixels(
154 HINSTANCE hinst,
155 LPCTSTR lpbmp,
156 int* pnLogPixelsX,
157 int* pnLogPixelsY
158 )
159 {
160 BOOL fRet = FALSE;
161 HRSRC hResource;
162 HGLOBAL hResourceBitmap = NULL;
163 BITMAPINFO* pBitmapInfo;
164 int PelsPerMeterX, PelsPerMeterY;
165
166 *pnLogPixelsX = 0;
167 *pnLogPixelsY = 0;
168
169 hResource = FindResource(hinst, lpbmp, RT_BITMAP);
170 if (!hResource)
171 {
172 goto error;
173 }
174 hResourceBitmap = LoadResource(hinst, hResource);
175 if (!hResourceBitmap)
176 {
177 goto error;
178 }
179 pBitmapInfo = (BITMAPINFO*)LockResource(hResourceBitmap);
180 if (!pBitmapInfo)
181 {
182 goto error;
183 }
184
185 // There are at least three kind value of PslsPerMeter used for 96 DPI bitmap:
186 // 0 - the bitmap just simply doesn't set this value
187 // 2834 - 72 DPI
188 // 3780 - 96 DPI
189 // So any value of PslsPerMeter under 3780 should be treated as 96 DPI bitmap.
190 PelsPerMeterX = (pBitmapInfo->bmiHeader.biXPelsPerMeter < 3780) ? 3780 : pBitmapInfo->bmiHeader.biXPelsPerMeter;
191 PelsPerMeterY = (pBitmapInfo->bmiHeader.biYPelsPerMeter < 3780) ? 3780 : pBitmapInfo->bmiHeader.biYPelsPerMeter;
192
193 // The formula for converting PelsPerMeter to LogPixels(DPI) is:
194 // LogPixels = PelsPerMeter / 39.37
195 // ( PelsPerMeter : Pixels per meter )
196 // ( LogPixels : Pixels per inch )
197 // Note: We need to round up.
198 *pnLogPixelsX = (int)((PelsPerMeterX * 100 + 1968) / 3937);
199 *pnLogPixelsY = (int)((PelsPerMeterY * 100 + 1968) / 3937);
200
201 fRet = TRUE;
202
203 error:
204 return fRet;
205 }
206
207
208 HIMAGELIST HIDPI_ImageList_LoadImage(
209 HINSTANCE hinst,
210 LPCTSTR lpbmp,
211 int cx,
212 int cGrow,
213 COLORREF crMask,
214 UINT uType,
215 UINT uFlags
216 )
217 {
218 HBITMAP hbmImage = NULL;
219 HIMAGELIST piml = NULL;
220 BITMAP bm;
221 int cImages, cxImage, cy;
222 int BmpLogPixelsX, BmpLogPixelsY;
223 UINT flags;
224
225 if ((uType != IMAGE_BITMAP) || // Image type is not IMAGE_BITMAP
226 (cx == 0)) // Caller doesn't care about the dimensions of the image - assumes the ones in the file
227 {
228 piml = ImageList_LoadImage(hinst, lpbmp, cx, cGrow, crMask, uType, uFlags);
229 goto cleanup;
230 }
231
232 if (!HIDPI_GetBitmapLogPixels(hinst, lpbmp, &BmpLogPixelsX, &BmpLogPixelsY))
233 {
234 goto cleanup;
235 }
236
237 hbmImage = (HBITMAP)LoadImage(hinst, lpbmp, uType, 0, 0, uFlags);
238 if (!hbmImage || (sizeof(bm) != GetObject(hbmImage, sizeof(bm), &bm)))
239 {
240 goto cleanup;
241 }
242
243 // do we need to scale this image?
244 if (BmpLogPixelsX == g_HIDPI_LogPixelsX)
245 {
246 piml = ImageList_LoadImage(hinst, lpbmp, cx, cGrow, crMask, uType, uFlags);
247 goto cleanup;
248 }
249
250 cxImage = HIDPIMulDiv(cx, BmpLogPixelsX, g_HIDPI_LogPixelsX);
251
252 // Bitmap width should be multiple integral of image width.
253 // If not, that means either your bitmap is wrong or passed in cx is wrong.
254 // ASSERT((bm.bmWidth % cxImage) == 0);
255
256 cImages = bm.bmWidth / cxImage;
257
258 cy = HIDPIMulDiv(bm.bmHeight, g_HIDPI_LogPixelsY, BmpLogPixelsY);
259
260 if ((g_HIDPI_LogPixelsX % BmpLogPixelsX) == 0)
261 {
262 HIDPI_StretchBitmap(&hbmImage, cx * cImages, cy, 1, 1);
263 }
264 else
265 {
266 // Here means the DPI is not integral multiple of standard DPI (96DPI).
267 // So if we stretch entire bitmap together, we are not sure each indivisual
268 // image will be stretch to right place. It is controled by StretchBlt().
269 // (for example, a 16 pixel icon, the first one might be stretch to 22 pixels
270 // and next one might be stretched to 20 pixels)
271 // What we have to do here is stretching indivisual image separately to make sure
272 // every one is stretched properly.
273 HIDPI_StretchBitmap(&hbmImage, cx, cy, cImages, 1);
274 }
275
276 flags = 0;
277 // ILC_MASK is important for supporting CLR_DEFAULT
278 if (crMask != CLR_NONE)
279 {
280 flags |= ILC_MASK;
281 }
282 // ILC_COLORMASK bits are important if we ever want to Merge ImageLists
283 if (bm.bmBits)
284 {
285 flags |= (bm.bmBitsPixel & ILC_COLORMASK);
286 }
287
288 // bitmap MUST be de-selected from the DC
289 // create the image list of the size asked for.
290 piml = ImageList_Create(cx, cy, flags, cImages, cGrow);
291
292 if (piml)
293 {
294 int added;
295
296 if (crMask == CLR_NONE)
297 {
298 added = ImageList_Add(piml, hbmImage, NULL);
299 }
300 else
301 {
302 added = ImageList_AddMasked(piml, hbmImage, crMask);
303 }
304
305 if (added < 0)
306 {
307 ImageList_Destroy(piml);
308 piml = NULL;
309 }
310 }
311
312 cleanup:
313 DeleteObject(hbmImage);
314 return piml;
315 }
316
317 int HIDPI_ImageList_ReplaceIcon(HIMAGELIST himl, int i, HICON hicon)
318 {
319 int iRet;
320 int cxIcon, cyIcon;
321 HICON hiconStretched;
322
323 ImageList_GetIconSize(himl, &cxIcon, &cyIcon);
324 HIDPI_StretchIcon_Internal(hicon, &hiconStretched, cxIcon, cyIcon);
325 if (hiconStretched != NULL)
326 {
327 iRet = ImageList_ReplaceIcon(himl, i, hiconStretched);
328 DestroyIcon(hiconStretched);
329 }
330 else
331 {
332 iRet = ImageList_ReplaceIcon(himl, i, hicon);
333 }
334
335 return iRet;
336 }
337
338 BOOL HIDPI_RectangleInternal(HDC hdc, int nLeft, int nTop, int nRight, int nBottom, int nThickness)
339 {
340 int nOff = nThickness/2;
341
342 nLeft += nOff;
343 nTop += nOff;
344 nRight -= nOff;
345 nBottom -= nOff;
346
347 return Rectangle(hdc, nLeft, nTop, nRight, nBottom);
348 }
349
350 #define BORDERX_PEN 32
351
352 BOOL HIDPI_BorderRectangle(HDC hdc, int nLeft, int nTop, int nRight, int nBottom)
353 {
354 HPEN hpenOld;
355 BOOL bRet;
356
357 hpenOld = (HPEN)SelectObject(hdc, (HPEN) GetStockObject(BORDERX_PEN));
358 bRet = HIDPI_RectangleInternal(hdc, nLeft, nTop, nRight, nBottom, GetSystemMetrics(SM_CXBORDER));
359 SelectObject(hdc, hpenOld);
360
361 return bRet;
362 }
363
364 BOOL HIDPI_Rectangle(HDC hdc, int nLeft, int nTop, int nRight, int nBottom)
365 {
366 LOGPEN lpenSel;
367 HPEN hpenSel;
368
369 // Obtain current pen thickness
370 hpenSel = (HPEN)GetCurrentObject(hdc, OBJ_PEN);
371 GetObject(hpenSel, sizeof(lpenSel), &lpenSel);
372
373 return HIDPI_RectangleInternal(hdc, nLeft, nTop, nRight, nBottom, lpenSel.lopnWidth.x);
374 }
375
376
377 BOOL HIDPI_PolylineInternal(HDC hdc, const POINT *lppt, int cPoints, int nStyle, int nThickness)
378 {
379 int i;
380 int nHOff = 0, nVOff = 0;
381 BOOL bRet = TRUE;
382 POINT pts[2];
383
384 if (! (nStyle & PS_BIAS_MASK))
385 {
386 // No drawing bias. Draw normally
387 return Polyline(hdc, lppt, cPoints);
388 }
389
390 // Make sure caller didn't try to get both a left and a right bias or both a down and an up bias
391 // ASSERT(!(nStyle & PS_LEFTBIAS) || !(nStyle & PS_RIGHTBIAS));
392 // ASSERT(!(nStyle & PS_UPBIAS) || !(nStyle & PS_DOWNBIAS));
393
394 if (nStyle & PS_LEFTBIAS)
395 {
396 nHOff = -((nThickness-1)/2);
397 }
398
399 if (nStyle & PS_RIGHTBIAS)
400 {
401 nHOff = nThickness/2;
402 }
403
404 if (nStyle & PS_UPBIAS)
405 {
406 nVOff = -((nThickness-1)/2);
407 }
408
409 if (nStyle & PS_DOWNBIAS)
410 {
411 nVOff = nThickness/2;
412 }
413
414 for (i = 1; i < cPoints; i++)
415 {
416 // Use the two points that specify current line segment
417 memcpy(pts, &lppt[i-1], 2*sizeof(POINT));
418 if (abs(lppt[i].x - lppt[i-1].x) <= abs(lppt[i].y - lppt[i-1].y))
419 {
420 // Shift current line segment horizontally if abs(slope) >= 1
421 pts[0].x += nHOff;
422 pts[1].x += nHOff;
423 }
424 else
425 {
426 // Shift current line segment vertically if abs(slope) < 1
427 pts[0].y += nVOff;
428 pts[1].y += nVOff;
429 }
430 bRet = bRet && Polyline(hdc, pts, 2);
431 if (!bRet)
432 {
433 goto Error;
434 }
435 }
436
437 Error:
438 return bRet;
439 }
440
441 BOOL HIDPI_BorderPolyline(HDC hdc, const POINT *lppt, int cPoints, int nStyle)
442 {
443 HPEN hpenOld;
444 BOOL bRet;
445
446 hpenOld = (HPEN)SelectObject(hdc, (HPEN) GetStockObject(BORDERX_PEN));
447 bRet = HIDPI_PolylineInternal(hdc, lppt, cPoints, nStyle, GetSystemMetrics(SM_CXBORDER));
448 SelectObject(hdc, hpenOld);
449
450 return bRet;
451 }
452
453 BOOL HIDPI_Polyline(HDC hdc, const POINT *lppt, int cPoints, int nStyle)
454 {
455 LOGPEN lpenSel;
456 HPEN hpenSel;
457
458 // Obtain current pen thickness
459 hpenSel = (HPEN)GetCurrentObject(hdc, OBJ_PEN);
460 GetObject(hpenSel, sizeof(lpenSel), &lpenSel);
461
462 return HIDPI_PolylineInternal(hdc, lppt, cPoints, nStyle, lpenSel.lopnWidth.x);
463 }
464
465 //
466 // Called by RelayoutDialog to advance to the next item in the dialog template.
467 //
468 static LPBYTE WalkDialogData(LPBYTE lpData)
469 {
470 LPWORD lpWord = (LPWORD)lpData;
471 if (*lpWord == 0xFFFF)
472 {
473 return (LPBYTE)(lpWord + 2);
474 }
475 while (*lpWord != 0x0000)
476 {
477 lpWord++;
478 }
479 return (LPBYTE)(lpWord + 1);
480 }
481
482 //
483 // Post-processing step for each dialog item.
484 // Static controls and buttons: change text and bitmaps.
485 // Listboxes and combo boxes: ensures that the selected item is visible.
486 //
487 static void FixupDialogItem(
488 HINSTANCE hInst,
489 HWND hDlg,
490 LPDLGITEMTEMPLATE lpDlgItem,
491 LPWORD lpClass,
492 LPWORD lpData)
493 {
494 if (lpClass[0] == 0xFFFF)
495 {
496 switch (lpClass[1])
497 {
498 case 0x0080: // button
499 case 0x0082: // static
500 {
501 if (lpData[0] == 0xFFFF)
502 {
503 if (lpDlgItem->style & SS_ICON)
504 {
505 HICON hOld = (HICON)SendDlgItemMessageW(hDlg, lpDlgItem->id, STM_GETIMAGE, IMAGE_ICON, 0);
506 HICON hNew = LoadIcon(hInst, MAKEINTRESOURCE(lpData[1]));
507 SendDlgItemMessageW(hDlg, lpDlgItem->id, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hNew);
508 DestroyIcon(hOld);
509 }
510 else if (lpDlgItem->style & SS_BITMAP)
511 {
512 HBITMAP hOld = (HBITMAP)SendDlgItemMessageW(hDlg, lpDlgItem->id, STM_GETIMAGE, IMAGE_BITMAP, 0);
513 HBITMAP hNew = LoadBitmap(hInst, MAKEINTRESOURCE(lpData[1]));
514 SendDlgItemMessageW(hDlg, lpDlgItem->id, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hNew);
515 DeleteObject(hOld);
516 }
517 }
518 else // lpData[0] is not 0xFFFF (it's text).
519 {
520 SetDlgItemTextW(hDlg, lpDlgItem->id, (LPCTSTR)lpData);
521 }
522 }
523 break;
524
525 case 0x0083: // list box
526 {
527 INT nSel = SendDlgItemMessageW(hDlg, lpDlgItem->id, LB_GETCURSEL, 0, 0);
528 if (nSel != LB_ERR)
529 {
530 SendDlgItemMessageW(hDlg, lpDlgItem->id, LB_SETCURSEL, nSel, 0);
531 }
532 }
533 break;
534
535 case 0x0085: // combo box
536 {
537 INT nSel = SendDlgItemMessageW(hDlg, lpDlgItem->id, CB_GETCURSEL, 0, 0);
538 if (nSel != CB_ERR)
539 {
540 SendDlgItemMessageW(hDlg, lpDlgItem->id, CB_SETCURSEL, nSel, 0);
541 }
542 }
543 break;
544 }
545 }
546 }
547
548 BOOL RelayoutDialog(HINSTANCE hInst, HWND hDlg, LPCWSTR iddTemplate)
549 {
550 HRSRC hRsrc = FindResource((HMODULE)hInst, iddTemplate, RT_DIALOG);
551 INT nStatics = 0;
552 HGLOBAL hGlobal;
553 LPBYTE lpData;
554 LPDLGTEMPLATE lpTemplate;
555 HDWP hDWP;
556 int i;
557 LPDLGITEMTEMPLATE lpDlgItem;
558 HWND hwndCtl;
559 LPWORD lpClass;
560 WORD cbExtra;
561
562 if (hRsrc == NULL)
563 {
564 return FALSE;
565 }
566
567 hGlobal = LoadResource((HMODULE)hInst, hRsrc);
568 if (hGlobal == NULL)
569 {
570 return FALSE;
571 }
572
573 lpData = (LPBYTE)LockResource(hGlobal);
574 lpTemplate = (LPDLGTEMPLATE)lpData;
575 hDWP = BeginDeferWindowPos(lpTemplate->cdit);
576
577 //
578 // For more information about the data structures that we are walking,
579 // consult the DLGTEMPLATE and DLGITEMTEMPLATE documentation on MSDN.
580 //
581 lpData += sizeof(DLGTEMPLATE);
582 lpData = WalkDialogData(lpData); // menu
583 lpData = WalkDialogData(lpData); // class
584 lpData = WalkDialogData(lpData); // title
585
586 if (lpTemplate->style & DS_SETFONT)
587 {
588 lpData += sizeof(WORD); // font size.
589 lpData = WalkDialogData(lpData); // font face.
590 }
591
592 for (i = 0; i < lpTemplate->cdit; i++)
593 {
594 lpData = (LPBYTE) (((INT)lpData + 3) & ~3); // force to DWORD boundary.
595 lpDlgItem = (LPDLGITEMTEMPLATE)lpData;
596 hwndCtl = GetDlgItem(hDlg, lpDlgItem->id);
597
598 if (lpDlgItem->id == 0xFFFF)
599 {
600 nStatics++;
601 }
602
603 //
604 // Move the item around.
605 //
606 {
607 RECT r;
608 r.left = lpDlgItem->x;
609 r.top = lpDlgItem->y;
610 r.right = lpDlgItem->x + lpDlgItem->cx;
611 r.bottom = lpDlgItem->y + lpDlgItem->cy;
612 MapDialogRect(hDlg, &r);
613 DeferWindowPos(hDWP, hwndCtl, NULL,
614 r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER);
615 }
616
617 lpData += sizeof(DLGITEMTEMPLATE);
618 lpClass = (LPWORD)lpData;
619 lpData = WalkDialogData(lpData); // class
620
621 //
622 // Do some special handling for each dialog item (changing text,
623 // bitmaps, ensuring visible, etc.
624 //
625 FixupDialogItem(hInst, hDlg, lpDlgItem, lpClass, (LPWORD)lpData);
626
627 lpData = WalkDialogData(lpData); // title
628 cbExtra = *((LPWORD)lpData); // extra class data.
629 lpData += (cbExtra ? cbExtra : sizeof(WORD));
630 }
631
632 EndDeferWindowPos(hDWP);
633 return nStatics < 2 ? TRUE : FALSE;
634 }