1 // Scintilla source code edit control
3 ** Implementation of platform facilities on Windows.
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
21 #define _WIN32_WINNT 0x0500
29 #if defined(NTDDI_WIN7) && !defined(DISABLE_D2D)
39 #include "StringCopy.h"
41 #include "UniConversion.h"
42 #include "FontQuality.h"
45 #define IDC_HAND MAKEINTRESOURCE(32649)
48 #ifndef SPI_GETFONTSMOOTHINGCONTRAST
49 #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
52 static void *PointerFromWindow(HWND hWnd
) {
53 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd
, 0));
56 static void SetWindowPointer(HWND hWnd
, void *ptr
) {
57 ::SetWindowLongPtr(hWnd
, 0, reinterpret_cast<LONG_PTR
>(ptr
));
60 extern UINT
CodePageFromCharSet(DWORD characterSet
, UINT documentCodePage
);
62 // Declarations needed for functions dynamically loaded as not available on all Windows versions.
63 typedef BOOL (WINAPI
*AlphaBlendSig
)(HDC
, int, int, int, int, HDC
, int, int, int, int, BLENDFUNCTION
);
64 typedef HMONITOR (WINAPI
*MonitorFromPointSig
)(POINT
, DWORD
);
65 typedef HMONITOR (WINAPI
*MonitorFromRectSig
)(LPCRECT
, DWORD
);
66 typedef BOOL (WINAPI
*GetMonitorInfoSig
)(HMONITOR
, LPMONITORINFO
);
68 static CRITICAL_SECTION crPlatformLock
;
69 static HINSTANCE hinstPlatformRes
= 0;
70 static bool onNT
= false;
72 static HMODULE hDLLImage
= 0;
73 static AlphaBlendSig AlphaBlendFn
= 0;
75 static HMODULE hDLLUser32
= 0;
76 static HMONITOR (WINAPI
*MonitorFromPointFn
)(POINT
, DWORD
) = 0;
77 static HMONITOR (WINAPI
*MonitorFromRectFn
)(LPCRECT
, DWORD
) = 0;
78 static BOOL (WINAPI
*GetMonitorInfoFn
)(HMONITOR
, LPMONITORINFO
) = 0;
80 static HCURSOR reverseArrowCursor
= NULL
;
90 Point
Point::FromLong(long lpoint
) {
91 return Point(static_cast<short>(LOWORD(lpoint
)), static_cast<short>(HIWORD(lpoint
)));
94 static RECT
RectFromPRectangle(PRectangle prc
) {
95 RECT rc
= {static_cast<LONG
>(prc
.left
), static_cast<LONG
>(prc
.top
),
96 static_cast<LONG
>(prc
.right
), static_cast<LONG
>(prc
.bottom
)};
101 IDWriteFactory
*pIDWriteFactory
= 0;
102 ID2D1Factory
*pD2DFactory
= 0;
103 IDWriteRenderingParams
*defaultRenderingParams
= 0;
104 IDWriteRenderingParams
*customClearTypeRenderingParams
= 0;
106 static HMODULE hDLLD2D
= NULL
;
107 static HMODULE hDLLDWrite
= NULL
;
110 static bool triedLoadingD2D
= false;
111 if (!triedLoadingD2D
) {
112 typedef HRESULT (WINAPI
*D2D1CFSig
)(D2D1_FACTORY_TYPE factoryType
, REFIID riid
,
113 CONST D2D1_FACTORY_OPTIONS
*pFactoryOptions
, IUnknown
**factory
);
114 typedef HRESULT (WINAPI
*DWriteCFSig
)(DWRITE_FACTORY_TYPE factoryType
, REFIID iid
,
117 hDLLD2D
= ::LoadLibraryEx(TEXT("D2D1.DLL"), 0, 0x00000800 /*LOAD_LIBRARY_SEARCH_SYSTEM32*/);
119 D2D1CFSig fnD2DCF
= (D2D1CFSig
)::GetProcAddress(hDLLD2D
, "D2D1CreateFactory");
121 // A single threaded factory as Scintilla always draw on the GUI thread
122 fnD2DCF(D2D1_FACTORY_TYPE_SINGLE_THREADED
,
123 __uuidof(ID2D1Factory
),
125 reinterpret_cast<IUnknown
**>(&pD2DFactory
));
128 hDLLDWrite
= ::LoadLibraryEx(TEXT("DWRITE.DLL"), 0, 0x00000800 /*LOAD_LIBRARY_SEARCH_SYSTEM32*/);
130 DWriteCFSig fnDWCF
= (DWriteCFSig
)::GetProcAddress(hDLLDWrite
, "DWriteCreateFactory");
132 fnDWCF(DWRITE_FACTORY_TYPE_SHARED
,
133 __uuidof(IDWriteFactory
),
134 reinterpret_cast<IUnknown
**>(&pIDWriteFactory
));
138 if (pIDWriteFactory
) {
139 HRESULT hr
= pIDWriteFactory
->CreateRenderingParams(&defaultRenderingParams
);
141 unsigned int clearTypeContrast
;
142 ::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST
, 0, &clearTypeContrast
, 0);
145 if (clearTypeContrast
>= 1000 && clearTypeContrast
<= 2200)
146 gamma
= static_cast<FLOAT
>(clearTypeContrast
) / 1000.0f
;
148 gamma
= defaultRenderingParams
->GetGamma();
150 pIDWriteFactory
->CreateCustomRenderingParams(gamma
, defaultRenderingParams
->GetEnhancedContrast(), defaultRenderingParams
->GetClearTypeLevel(),
151 defaultRenderingParams
->GetPixelGeometry(), defaultRenderingParams
->GetRenderingMode(), &customClearTypeRenderingParams
);
156 triedLoadingD2D
= true;
157 return pIDWriteFactory
&& pD2DFactory
;
161 struct FormatAndMetrics
{
165 IDWriteTextFormat
*pTextFormat
;
171 FLOAT yInternalLeading
;
172 FormatAndMetrics(HFONT hfont_
, int extraFontFlag_
, int characterSet_
) :
173 technology(SCWIN_TECH_GDI
), hfont(hfont_
),
177 extraFontFlag(extraFontFlag_
), characterSet(characterSet_
), yAscent(2), yDescent(1), yInternalLeading(0) {
180 FormatAndMetrics(IDWriteTextFormat
*pTextFormat_
,
185 FLOAT yInternalLeading_
) :
186 technology(SCWIN_TECH_DIRECTWRITE
),
188 pTextFormat(pTextFormat_
),
189 extraFontFlag(extraFontFlag_
),
190 characterSet(characterSet_
),
193 yInternalLeading(yInternalLeading_
) {
196 ~FormatAndMetrics() {
198 ::DeleteObject(hfont
);
201 pTextFormat
->Release();
208 yInternalLeading
= 0;
213 HFONT
FormatAndMetrics::HFont() {
216 if (technology
== SCWIN_TECH_GDI
) {
217 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
221 HRESULT hr
= pTextFormat
->GetFontFamilyName(lf
.lfFaceName
, LF_FACESIZE
);
222 if (!SUCCEEDED(hr
)) {
225 lf
.lfWeight
= pTextFormat
->GetFontWeight();
226 lf
.lfItalic
= pTextFormat
->GetFontStyle() == DWRITE_FONT_STYLE_ITALIC
;
227 lf
.lfHeight
= -static_cast<int>(pTextFormat
->GetFontSize());
230 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
234 return ::CreateFontIndirectW(&lf
);
237 #ifndef CLEARTYPE_QUALITY
238 #define CLEARTYPE_QUALITY 5
241 static BYTE
Win32MapFontQuality(int extraFontFlag
) {
242 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
244 case SC_EFF_QUALITY_NON_ANTIALIASED
:
245 return NONANTIALIASED_QUALITY
;
247 case SC_EFF_QUALITY_ANTIALIASED
:
248 return ANTIALIASED_QUALITY
;
250 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
251 return CLEARTYPE_QUALITY
;
254 return SC_EFF_QUALITY_DEFAULT
;
259 static D2D1_TEXT_ANTIALIAS_MODE
DWriteMapFontQuality(int extraFontFlag
) {
260 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
262 case SC_EFF_QUALITY_NON_ANTIALIASED
:
263 return D2D1_TEXT_ANTIALIAS_MODE_ALIASED
;
265 case SC_EFF_QUALITY_ANTIALIASED
:
266 return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE
;
268 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
269 return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
;
272 return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
;
277 static void SetLogFont(LOGFONTW
&lf
, const char *faceName
, int characterSet
, float size
, int weight
, bool italic
, int extraFontFlag
) {
279 // The negative is to allow for leading
280 lf
.lfHeight
= -(abs(static_cast<int>(size
+ 0.5)));
281 lf
.lfWeight
= weight
;
282 lf
.lfItalic
= static_cast<BYTE
>(italic
? 1 : 0);
283 lf
.lfCharSet
= static_cast<BYTE
>(characterSet
);
284 lf
.lfQuality
= Win32MapFontQuality(extraFontFlag
);
285 UTF16FromUTF8(faceName
, strlen(faceName
)+1, lf
.lfFaceName
, LF_FACESIZE
);
289 * Create a hash from the parameters for a font to allow easy checking for identity.
290 * If one font is the same as another, its hash will be the same, but if the hash is the
291 * same then they may still be different.
293 static int HashFont(const FontParameters
&fp
) {
295 static_cast<int>(fp
.size
) ^
296 (fp
.characterSet
<< 10) ^
297 ((fp
.extraFontFlag
& SC_EFF_QUALITY_MASK
) << 9) ^
298 ((fp
.weight
/100) << 12) ^
299 (fp
.italic
? 0x20000000 : 0) ^
300 (fp
.technology
<< 15) ^
304 class FontCached
: Font
{
311 explicit FontCached(const FontParameters
&fp
);
313 bool SameAs(const FontParameters
&fp
);
314 virtual void Release();
316 static FontCached
*first
;
318 static FontID
FindOrCreate(const FontParameters
&fp
);
319 static void ReleaseId(FontID fid_
);
322 FontCached
*FontCached::first
= 0;
324 FontCached::FontCached(const FontParameters
&fp
) :
325 next(0), usage(0), size(1.0), hash(0) {
326 SetLogFont(lf
, fp
.faceName
, fp
.characterSet
, fp
.size
, fp
.weight
, fp
.italic
, fp
.extraFontFlag
);
327 technology
= fp
.technology
;
330 if (technology
== SCWIN_TECH_GDI
) {
331 HFONT hfont
= ::CreateFontIndirectW(&lf
);
332 fid
= reinterpret_cast<void *>(new FormatAndMetrics(hfont
, fp
.extraFontFlag
, fp
.characterSet
));
335 IDWriteTextFormat
*pTextFormat
;
336 const int faceSize
= 200;
337 WCHAR wszFace
[faceSize
];
338 UTF16FromUTF8(fp
.faceName
, static_cast<unsigned int>(strlen(fp
.faceName
))+1, wszFace
, faceSize
);
339 FLOAT fHeight
= fp
.size
;
340 DWRITE_FONT_STYLE style
= fp
.italic
? DWRITE_FONT_STYLE_ITALIC
: DWRITE_FONT_STYLE_NORMAL
;
341 HRESULT hr
= pIDWriteFactory
->CreateTextFormat(wszFace
, NULL
,
342 static_cast<DWRITE_FONT_WEIGHT
>(fp
.weight
),
344 DWRITE_FONT_STRETCH_NORMAL
, fHeight
, L
"en-us", &pTextFormat
);
346 pTextFormat
->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP
);
348 const int maxLines
= 2;
349 DWRITE_LINE_METRICS lineMetrics
[maxLines
];
350 UINT32 lineCount
= 0;
351 FLOAT yAscent
= 1.0f
;
352 FLOAT yDescent
= 1.0f
;
353 FLOAT yInternalLeading
= 0.0f
;
354 IDWriteTextLayout
*pTextLayout
= 0;
355 hr
= pIDWriteFactory
->CreateTextLayout(L
"X", 1, pTextFormat
,
356 100.0f
, 100.0f
, &pTextLayout
);
358 hr
= pTextLayout
->GetLineMetrics(lineMetrics
, maxLines
, &lineCount
);
360 yAscent
= lineMetrics
[0].baseline
;
361 yDescent
= lineMetrics
[0].height
- lineMetrics
[0].baseline
;
364 hr
= pTextLayout
->GetFontSize(0, &emHeight
);
366 yInternalLeading
= lineMetrics
[0].height
- emHeight
;
369 pTextLayout
->Release();
370 pTextFormat
->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM
, lineMetrics
[0].height
, lineMetrics
[0].baseline
);
372 fid
= reinterpret_cast<void *>(new FormatAndMetrics(pTextFormat
, fp
.extraFontFlag
, fp
.characterSet
, yAscent
, yDescent
, yInternalLeading
));
379 bool FontCached::SameAs(const FontParameters
&fp
) {
382 (lf
.lfWeight
== fp
.weight
) &&
383 (lf
.lfItalic
== static_cast<BYTE
>(fp
.italic
? 1 : 0)) &&
384 (lf
.lfCharSet
== fp
.characterSet
) &&
385 (lf
.lfQuality
== Win32MapFontQuality(fp
.extraFontFlag
)) &&
386 (technology
== fp
.technology
)) {
387 wchar_t wszFace
[LF_FACESIZE
];
388 UTF16FromUTF8(fp
.faceName
, strlen(fp
.faceName
)+1, wszFace
, LF_FACESIZE
);
389 return 0 == wcscmp(lf
.lfFaceName
,wszFace
);
394 void FontCached::Release() {
395 delete reinterpret_cast<FormatAndMetrics
*>(fid
);
399 FontID
FontCached::FindOrCreate(const FontParameters
&fp
) {
401 ::EnterCriticalSection(&crPlatformLock
);
402 int hashFind
= HashFont(fp
);
403 for (FontCached
*cur
=first
; cur
; cur
=cur
->next
) {
404 if ((cur
->hash
== hashFind
) &&
411 FontCached
*fc
= new FontCached(fp
);
416 ::LeaveCriticalSection(&crPlatformLock
);
420 void FontCached::ReleaseId(FontID fid_
) {
421 ::EnterCriticalSection(&crPlatformLock
);
422 FontCached
**pcur
=&first
;
423 for (FontCached
*cur
=first
; cur
; cur
=cur
->next
) {
424 if (cur
->fid
== fid_
) {
426 if (cur
->usage
== 0) {
436 ::LeaveCriticalSection(&crPlatformLock
);
448 void Font::Create(const FontParameters
&fp
) {
451 fid
= FontCached::FindOrCreate(fp
);
454 void Font::Release() {
456 FontCached::ReleaseId(fid
);
460 // Buffer to hold strings and string position arrays without always allocating on heap.
461 // May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer
462 // when less than safe size otherwise allocate on heap and free automatically.
463 template<typename T
, int lengthStandard
>
465 T bufferStandard
[lengthStandard
];
466 // Private so VarBuffer objects can not be copied
467 VarBuffer(const VarBuffer
&);
468 VarBuffer
&operator=(const VarBuffer
&);
471 explicit VarBuffer(size_t length
) : buffer(0) {
472 if (length
> lengthStandard
) {
473 buffer
= new T
[length
];
475 buffer
= bufferStandard
;
479 if (buffer
!= bufferStandard
) {
486 const int stackBufferLength
= 10000;
487 class TextWide
: public VarBuffer
<wchar_t, stackBufferLength
> {
490 TextWide(const char *s
, int len
, bool unicodeMode
, int codePage
=0) :
491 VarBuffer
<wchar_t, stackBufferLength
>(len
) {
493 tlen
= UTF16FromUTF8(s
, len
, buffer
, len
);
495 // Support Asian string display in 9x English
496 tlen
= ::MultiByteToWideChar(codePage
, 0, s
, len
, buffer
, len
);
500 typedef VarBuffer
<XYPOSITION
, stackBufferLength
> TextPositions
;
502 class SurfaceGDI
: public Surface
{
518 // If 9x OS and current code page is same as ANSI code page.
521 void BrushColor(ColourDesired back
);
522 void SetFont(Font
&font_
);
524 // Private so SurfaceGDI objects can not be copied
525 SurfaceGDI(const SurfaceGDI
&);
526 SurfaceGDI
&operator=(const SurfaceGDI
&);
529 virtual ~SurfaceGDI();
531 void Init(WindowID wid
);
532 void Init(SurfaceID sid
, WindowID wid
);
533 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
537 void PenColour(ColourDesired fore
);
539 int DeviceHeightFont(int points
);
540 void MoveTo(int x_
, int y_
);
541 void LineTo(int x_
, int y_
);
542 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
543 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
544 void FillRectangle(PRectangle rc
, ColourDesired back
);
545 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
546 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
547 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
548 ColourDesired outline
, int alphaOutline
, int flags
);
549 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
550 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
551 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
553 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
554 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
555 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
556 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
557 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
558 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
559 XYPOSITION
WidthChar(Font
&font_
, char ch
);
560 XYPOSITION
Ascent(Font
&font_
);
561 XYPOSITION
Descent(Font
&font_
);
562 XYPOSITION
InternalLeading(Font
&font_
);
563 XYPOSITION
ExternalLeading(Font
&font_
);
564 XYPOSITION
Height(Font
&font_
);
565 XYPOSITION
AverageCharWidth(Font
&font_
);
567 void SetClip(PRectangle rc
);
568 void FlushCachedState();
570 void SetUnicodeMode(bool unicodeMode_
);
571 void SetDBCSMode(int codePage_
);
574 SurfaceGDI::SurfaceGDI() :
576 hdc(0), hdcOwned(false),
578 brush(0), brushOld(0),
580 bitmap(0), bitmapOld(0) {
581 // Windows 9x has only a 16 bit coordinate system so break after 30000 pixels
582 maxWidthMeasure
= IsNT() ? INT_MAX
: 30000;
583 // There appears to be a 16 bit string length limit in GDI on NT and a limit of
584 // 8192 characters on Windows 95.
585 maxLenText
= IsNT() ? 65535 : 8192;
588 win9xACPSame
= false;
591 SurfaceGDI::~SurfaceGDI() {
595 void SurfaceGDI::Release() {
597 ::SelectObject(reinterpret_cast<HDC
>(hdc
), penOld
);
603 ::SelectObject(reinterpret_cast<HDC
>(hdc
), brushOld
);
604 ::DeleteObject(brush
);
609 // Fonts are not deleted as they are owned by a Font object
610 ::SelectObject(reinterpret_cast<HDC
>(hdc
), fontOld
);
615 ::SelectObject(reinterpret_cast<HDC
>(hdc
), bitmapOld
);
616 ::DeleteObject(bitmap
);
621 ::DeleteDC(reinterpret_cast<HDC
>(hdc
));
627 bool SurfaceGDI::Initialised() {
631 void SurfaceGDI::Init(WindowID
) {
633 hdc
= ::CreateCompatibleDC(NULL
);
635 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
638 void SurfaceGDI::Init(SurfaceID sid
, WindowID
) {
640 hdc
= reinterpret_cast<HDC
>(sid
);
641 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
644 void SurfaceGDI::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
646 hdc
= ::CreateCompatibleDC(static_cast<SurfaceGDI
*>(surface_
)->hdc
);
648 bitmap
= ::CreateCompatibleBitmap(static_cast<SurfaceGDI
*>(surface_
)->hdc
, width
, height
);
649 bitmapOld
= static_cast<HBITMAP
>(::SelectObject(hdc
, bitmap
));
650 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
653 void SurfaceGDI::PenColour(ColourDesired fore
) {
655 ::SelectObject(hdc
, penOld
);
660 pen
= ::CreatePen(0,1,fore
.AsLong());
661 penOld
= static_cast<HPEN
>(::SelectObject(reinterpret_cast<HDC
>(hdc
), pen
));
664 void SurfaceGDI::BrushColor(ColourDesired back
) {
666 ::SelectObject(hdc
, brushOld
);
667 ::DeleteObject(brush
);
671 // Only ever want pure, non-dithered brushes
672 ColourDesired colourNearest
= ::GetNearestColor(hdc
, back
.AsLong());
673 brush
= ::CreateSolidBrush(colourNearest
.AsLong());
674 brushOld
= static_cast<HBRUSH
>(::SelectObject(hdc
, brush
));
677 void SurfaceGDI::SetFont(Font
&font_
) {
678 if (font_
.GetID() != font
) {
679 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
680 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_GDI
);
682 ::SelectObject(hdc
, pfm
->hfont
);
684 fontOld
= static_cast<HFONT
>(::SelectObject(hdc
, pfm
->hfont
));
686 font
= reinterpret_cast<HFONT
>(pfm
->hfont
);
690 int SurfaceGDI::LogPixelsY() {
691 return ::GetDeviceCaps(hdc
, LOGPIXELSY
);
694 int SurfaceGDI::DeviceHeightFont(int points
) {
695 return ::MulDiv(points
, LogPixelsY(), 72);
698 void SurfaceGDI::MoveTo(int x_
, int y_
) {
699 ::MoveToEx(hdc
, x_
, y_
, 0);
702 void SurfaceGDI::LineTo(int x_
, int y_
) {
703 ::LineTo(hdc
, x_
, y_
);
706 void SurfaceGDI::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
709 std::vector
<POINT
> outline
;
710 for (int i
=0; i
<npts
; i
++) {
711 POINT pt
= {static_cast<LONG
>(pts
[i
].x
), static_cast<LONG
>(pts
[i
].y
)};
712 outline
.push_back(pt
);
714 ::Polygon(hdc
, &outline
[0], npts
);
717 void SurfaceGDI::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
720 const RECT rcw
= RectFromPRectangle(rc
);
721 ::Rectangle(hdc
, rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
724 void SurfaceGDI::FillRectangle(PRectangle rc
, ColourDesired back
) {
725 // Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
726 // There is no need to allocate a brush either.
727 RECT rcw
= RectFromPRectangle(rc
);
728 ::SetBkColor(hdc
, back
.AsLong());
729 ::ExtTextOut(hdc
, rcw
.left
, rcw
.top
, ETO_OPAQUE
, &rcw
, TEXT(""), 0, NULL
);
732 void SurfaceGDI::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
734 if (static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
)
735 br
= ::CreatePatternBrush(static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
);
736 else // Something is wrong so display in red
737 br
= ::CreateSolidBrush(RGB(0xff, 0, 0));
738 RECT rcw
= RectFromPRectangle(rc
);
739 ::FillRect(hdc
, &rcw
, br
);
743 void SurfaceGDI::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
746 const RECT rcw
= RectFromPRectangle(rc
);
748 rcw
.left
+ 1, rcw
.top
,
749 rcw
.right
- 1, rcw
.bottom
,
753 // Plot a point into a DWORD buffer symetrically to all 4 qudrants
754 static void AllFour(DWORD
*pixels
, int width
, int height
, int x
, int y
, DWORD val
) {
755 pixels
[y
*width
+x
] = val
;
756 pixels
[y
*width
+width
-1-x
] = val
;
757 pixels
[(height
-1-y
)*width
+x
] = val
;
758 pixels
[(height
-1-y
)*width
+width
-1-x
] = val
;
762 #define AC_SRC_OVER 0x00
765 #define AC_SRC_ALPHA 0x01
768 static DWORD
dwordFromBGRA(byte b
, byte g
, byte r
, byte a
) {
773 converter
.pixVal
[0] = b
;
774 converter
.pixVal
[1] = g
;
775 converter
.pixVal
[2] = r
;
776 converter
.pixVal
[3] = a
;
777 return converter
.val
;
780 void SurfaceGDI::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
781 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
782 const RECT rcw
= RectFromPRectangle(rc
);
783 if (AlphaBlendFn
&& rc
.Width() > 0) {
784 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
785 int width
= static_cast<int>(rc
.Width());
786 int height
= static_cast<int>(rc
.Height());
787 // Ensure not distorted too much by corners when small
788 cornerSize
= Platform::Minimum(cornerSize
, (Platform::Minimum(width
, height
) / 2) - 2);
789 BITMAPINFO bpih
= {{sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0}};
791 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
792 DIB_RGB_COLORS
, &image
, NULL
, 0);
795 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
797 DWORD valEmpty
= dwordFromBGRA(0,0,0,0);
798 DWORD valFill
= dwordFromBGRA(
799 static_cast<byte
>(GetBValue(fill
.AsLong()) * alphaFill
/ 255),
800 static_cast<byte
>(GetGValue(fill
.AsLong()) * alphaFill
/ 255),
801 static_cast<byte
>(GetRValue(fill
.AsLong()) * alphaFill
/ 255),
802 static_cast<byte
>(alphaFill
));
803 DWORD valOutline
= dwordFromBGRA(
804 static_cast<byte
>(GetBValue(outline
.AsLong()) * alphaOutline
/ 255),
805 static_cast<byte
>(GetGValue(outline
.AsLong()) * alphaOutline
/ 255),
806 static_cast<byte
>(GetRValue(outline
.AsLong()) * alphaOutline
/ 255),
807 static_cast<byte
>(alphaOutline
));
808 DWORD
*pixels
= reinterpret_cast<DWORD
*>(image
);
809 for (int y
=0; y
<height
; y
++) {
810 for (int x
=0; x
<width
; x
++) {
811 if ((x
==0) || (x
==width
-1) || (y
== 0) || (y
== height
-1)) {
812 pixels
[y
*width
+x
] = valOutline
;
814 pixels
[y
*width
+x
] = valFill
;
818 for (int c
=0; c
<cornerSize
; c
++) {
819 for (int x
=0; x
<c
+1; x
++) {
820 AllFour(pixels
, width
, height
, x
, c
-x
, valEmpty
);
823 for (int x
=1; x
<cornerSize
; x
++) {
824 AllFour(pixels
, width
, height
, x
, cornerSize
-x
, valOutline
);
827 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
829 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), rcw
.left
, rcw
.top
, width
, height
, hMemDC
, 0, 0, width
, height
, merge
);
831 SelectBitmap(hMemDC
, hbmOld
);
832 ::DeleteObject(hbmMem
);
837 FrameRect(hdc
, &rcw
, brush
);
841 void SurfaceGDI::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
842 if (AlphaBlendFn
&& rc
.Width() > 0) {
843 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
844 if (rc
.Width() > width
)
845 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
846 rc
.right
= rc
.left
+ width
;
847 if (rc
.Height() > height
)
848 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
849 rc
.bottom
= rc
.top
+ height
;
851 BITMAPINFO bpih
= {{sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0}};
852 unsigned char *image
= 0;
853 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
854 DIB_RGB_COLORS
, reinterpret_cast<void **>(&image
), NULL
, 0);
856 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
858 for (int y
=height
-1; y
>=0; y
--) {
859 for (int x
=0; x
<width
; x
++) {
860 unsigned char *pixel
= image
+ (y
*width
+x
) * 4;
861 unsigned char alpha
= pixelsImage
[3];
862 // Input is RGBA, output is BGRA with premultiplied alpha
863 pixel
[2] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
864 pixel
[1] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
865 pixel
[0] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
866 pixel
[3] = static_cast<unsigned char>(*pixelsImage
++);
870 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
872 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
873 static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()), hMemDC
, 0, 0, width
, height
, merge
);
875 SelectBitmap(hMemDC
, hbmOld
);
876 ::DeleteObject(hbmMem
);
883 void SurfaceGDI::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
886 const RECT rcw
= RectFromPRectangle(rc
);
887 ::Ellipse(hdc
, rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
890 void SurfaceGDI::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
892 static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
893 static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()),
894 static_cast<SurfaceGDI
&>(surfaceSource
).hdc
,
895 static_cast<int>(from
.x
), static_cast<int>(from
.y
), SRCCOPY
);
898 typedef VarBuffer
<int, stackBufferLength
> TextPositionsI
;
900 void SurfaceGDI::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
) {
902 RECT rcw
= RectFromPRectangle(rc
);
905 int x
= static_cast<int>(rc
.left
);
906 const int yBaseInt
= static_cast<int>(ybase
);
908 // Text drawing may fail if the text is too big.
909 // If it does fail, slice up into segments and draw each segment.
910 const int maxSegmentLength
= 0x200;
912 if ((!unicodeMode
) && (IsNT() || (codePage
==0) || win9xACPSame
)) {
914 int lenDraw
= Platform::Minimum(len
, maxLenText
);
915 if (!::ExtTextOutA(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, s
, lenDraw
, NULL
)) {
916 while (lenDraw
> pos
) {
917 int seglen
= Platform::Minimum(maxSegmentLength
, lenDraw
- pos
);
918 if (!::ExtTextOutA(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, s
+ pos
, seglen
, NULL
)) {
919 PLATFORM_ASSERT(false);
922 ::GetTextExtentPoint32A(hdc
, s
+pos
, seglen
, &sz
);
929 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
930 if (!::ExtTextOutW(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, tbuf
.buffer
, tbuf
.tlen
, NULL
)) {
931 while (tbuf
.tlen
> pos
) {
932 int seglen
= Platform::Minimum(maxSegmentLength
, tbuf
.tlen
- pos
);
933 if (!::ExtTextOutW(hdc
, x
, yBaseInt
, fuOptions
, &rcw
, tbuf
.buffer
+ pos
, seglen
, NULL
)) {
934 PLATFORM_ASSERT(false);
937 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
+pos
, seglen
, &sz
);
945 void SurfaceGDI::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
946 ColourDesired fore
, ColourDesired back
) {
947 ::SetTextColor(hdc
, fore
.AsLong());
948 ::SetBkColor(hdc
, back
.AsLong());
949 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
952 void SurfaceGDI::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
953 ColourDesired fore
, ColourDesired back
) {
954 ::SetTextColor(hdc
, fore
.AsLong());
955 ::SetBkColor(hdc
, back
.AsLong());
956 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
959 void SurfaceGDI::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
960 ColourDesired fore
) {
961 // Avoid drawing spaces in transparent mode
962 for (int i
=0; i
<len
; i
++) {
964 ::SetTextColor(hdc
, fore
.AsLong());
965 ::SetBkMode(hdc
, TRANSPARENT
);
966 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
967 ::SetBkMode(hdc
, OPAQUE
);
973 XYPOSITION
SurfaceGDI::WidthText(Font
&font_
, const char *s
, int len
) {
976 if ((!unicodeMode
) && (IsNT() || (codePage
==0) || win9xACPSame
)) {
977 ::GetTextExtentPoint32A(hdc
, s
, Platform::Minimum(len
, maxLenText
), &sz
);
979 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
980 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &sz
);
982 return static_cast<XYPOSITION
>(sz
.cx
);
985 void SurfaceGDI::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
990 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
991 TextPositionsI
poses(tbuf
.tlen
);
993 if (!::GetTextExtentExPointW(hdc
, tbuf
.buffer
, tbuf
.tlen
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
994 // Likely to have failed because on Windows 9x where function not available
995 // So measure the character widths by measuring each initial substring
996 // Turns a linear operation into a qudratic but seems fast enough on test files
997 for (int widthSS
=0; widthSS
< tbuf
.tlen
; widthSS
++) {
998 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, widthSS
+1, &sz
);
999 poses
.buffer
[widthSS
] = sz
.cx
;
1002 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
1004 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1007 unsigned char uch
= us
[i
];
1008 unsigned int lenChar
= 1;
1009 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
1012 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
1014 } else if (uch
>= (0x80)) {
1017 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
1018 positions
[i
++] = static_cast<XYPOSITION
>(poses
.buffer
[ui
]);
1022 XYPOSITION lastPos
= 0.0f
;
1024 lastPos
= positions
[i
-1];
1026 positions
[i
++] = lastPos
;
1028 } else if (IsNT() || (codePage
==0) || win9xACPSame
) {
1029 // Zero positions to avoid random behaviour on failure.
1030 std::fill(positions
, positions
+ len
, 0.0f
);
1031 // len may be larger than platform supports so loop over segments small enough for platform
1032 int startOffset
= 0;
1034 int lenBlock
= Platform::Minimum(len
, maxLenText
);
1035 TextPositionsI
poses(len
);
1036 if (!::GetTextExtentExPointA(hdc
, s
, lenBlock
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
1037 // Eeek - a NULL DC or other foolishness could cause this.
1039 } else if (fit
< lenBlock
) {
1040 // For some reason, such as an incomplete DBCS character
1041 // Not all the positions are filled in so make them equal to end.
1043 poses
.buffer
[fit
++] = 0;
1044 for (int i
= fit
; i
<lenBlock
; i
++)
1045 poses
.buffer
[i
] = poses
.buffer
[fit
-1];
1047 for (int i
=0; i
<lenBlock
; i
++)
1048 positions
[i
] = static_cast<XYPOSITION
>(poses
.buffer
[i
] + startOffset
);
1049 startOffset
= poses
.buffer
[lenBlock
-1];
1051 positions
+= lenBlock
;
1055 // Support Asian string display in 9x English
1056 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
1057 TextPositionsI
poses(tbuf
.tlen
);
1058 for (int widthSS
=0; widthSS
<tbuf
.tlen
; widthSS
++) {
1059 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, widthSS
+1, &sz
);
1060 poses
.buffer
[widthSS
] = sz
.cx
;
1064 for (int i
=0; i
<len
;) {
1065 if (Platform::IsDBCSLeadByte(codePage
, s
[i
])) {
1066 positions
[i
] = static_cast<XYPOSITION
>(poses
.buffer
[ui
]);
1067 positions
[i
+ 1] = static_cast<XYPOSITION
>(poses
.buffer
[ui
]);
1070 positions
[i
] = static_cast<XYPOSITION
>(poses
.buffer
[ui
]);
1079 XYPOSITION
SurfaceGDI::WidthChar(Font
&font_
, char ch
) {
1082 ::GetTextExtentPoint32A(hdc
, &ch
, 1, &sz
);
1083 return static_cast<XYPOSITION
>(sz
.cx
);
1086 XYPOSITION
SurfaceGDI::Ascent(Font
&font_
) {
1089 ::GetTextMetrics(hdc
, &tm
);
1090 return static_cast<XYPOSITION
>(tm
.tmAscent
);
1093 XYPOSITION
SurfaceGDI::Descent(Font
&font_
) {
1096 ::GetTextMetrics(hdc
, &tm
);
1097 return static_cast<XYPOSITION
>(tm
.tmDescent
);
1100 XYPOSITION
SurfaceGDI::InternalLeading(Font
&font_
) {
1103 ::GetTextMetrics(hdc
, &tm
);
1104 return static_cast<XYPOSITION
>(tm
.tmInternalLeading
);
1107 XYPOSITION
SurfaceGDI::ExternalLeading(Font
&font_
) {
1110 ::GetTextMetrics(hdc
, &tm
);
1111 return static_cast<XYPOSITION
>(tm
.tmExternalLeading
);
1114 XYPOSITION
SurfaceGDI::Height(Font
&font_
) {
1117 ::GetTextMetrics(hdc
, &tm
);
1118 return static_cast<XYPOSITION
>(tm
.tmHeight
);
1121 XYPOSITION
SurfaceGDI::AverageCharWidth(Font
&font_
) {
1124 ::GetTextMetrics(hdc
, &tm
);
1125 return static_cast<XYPOSITION
>(tm
.tmAveCharWidth
);
1128 void SurfaceGDI::SetClip(PRectangle rc
) {
1129 ::IntersectClipRect(hdc
, static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
1130 static_cast<int>(rc
.right
), static_cast<int>(rc
.bottom
));
1133 void SurfaceGDI::FlushCachedState() {
1139 void SurfaceGDI::SetUnicodeMode(bool unicodeMode_
) {
1140 unicodeMode
=unicodeMode_
;
1143 void SurfaceGDI::SetDBCSMode(int codePage_
) {
1144 // No action on window as automatically handled by system.
1145 codePage
= codePage_
;
1146 win9xACPSame
= !IsNT() && ((unsigned int)codePage
== ::GetACP());
1149 #if defined(USE_D2D)
1151 class SurfaceD2D
: public Surface
{
1158 ID2D1RenderTarget
*pRenderTarget
;
1159 bool ownRenderTarget
;
1162 IDWriteTextFormat
*pTextFormat
;
1165 FLOAT yInternalLeading
;
1167 ID2D1SolidColorBrush
*pBrush
;
1173 void SetFont(Font
&font_
);
1175 // Private so SurfaceD2D objects can not be copied
1176 SurfaceD2D(const SurfaceD2D
&);
1177 SurfaceD2D
&operator=(const SurfaceD2D
&);
1180 virtual ~SurfaceD2D();
1183 void Init(WindowID wid
);
1184 void Init(SurfaceID sid
, WindowID wid
);
1185 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
1190 HRESULT
FlushDrawing();
1192 void PenColour(ColourDesired fore
);
1193 void D2DPenColour(ColourDesired fore
, int alpha
=255);
1195 int DeviceHeightFont(int points
);
1196 void MoveTo(int x_
, int y_
);
1197 void LineTo(int x_
, int y_
);
1198 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
1199 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1200 void FillRectangle(PRectangle rc
, ColourDesired back
);
1201 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
1202 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1203 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1204 ColourDesired outline
, int alphaOutline
, int flags
);
1205 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
1206 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1207 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
1209 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
1210 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1211 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1212 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
1213 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
1214 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
1215 XYPOSITION
WidthChar(Font
&font_
, char ch
);
1216 XYPOSITION
Ascent(Font
&font_
);
1217 XYPOSITION
Descent(Font
&font_
);
1218 XYPOSITION
InternalLeading(Font
&font_
);
1219 XYPOSITION
ExternalLeading(Font
&font_
);
1220 XYPOSITION
Height(Font
&font_
);
1221 XYPOSITION
AverageCharWidth(Font
&font_
);
1223 void SetClip(PRectangle rc
);
1224 void FlushCachedState();
1226 void SetUnicodeMode(bool unicodeMode_
);
1227 void SetDBCSMode(int codePage_
);
1230 SurfaceD2D::SurfaceD2D() :
1237 pRenderTarget
= NULL
;
1238 ownRenderTarget
= false;
1241 // From selected font
1245 yInternalLeading
= 0;
1254 SurfaceD2D::~SurfaceD2D() {
1258 void SurfaceD2D::Release() {
1263 if (pRenderTarget
) {
1264 while (clipsActive
) {
1265 pRenderTarget
->PopAxisAlignedClip();
1268 if (ownRenderTarget
) {
1269 pRenderTarget
->Release();
1275 void SurfaceD2D::SetScale() {
1276 HDC hdcMeasure
= ::CreateCompatibleDC(NULL
);
1277 logPixelsY
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSY
);
1278 dpiScaleX
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSX
) / 96.0f
;
1279 dpiScaleY
= logPixelsY
/ 96.0f
;
1280 ::DeleteDC(hdcMeasure
);
1283 bool SurfaceD2D::Initialised() {
1284 return pRenderTarget
!= 0;
1287 HRESULT
SurfaceD2D::FlushDrawing() {
1288 return pRenderTarget
->Flush();
1291 void SurfaceD2D::Init(WindowID
/* wid */) {
1296 void SurfaceD2D::Init(SurfaceID sid
, WindowID
) {
1299 pRenderTarget
= reinterpret_cast<ID2D1RenderTarget
*>(sid
);
1302 void SurfaceD2D::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
1305 SurfaceD2D
*psurfOther
= static_cast<SurfaceD2D
*>(surface_
);
1306 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= NULL
;
1307 D2D1_SIZE_F desiredSize
= D2D1::SizeF(static_cast<float>(width
), static_cast<float>(height
));
1308 D2D1_PIXEL_FORMAT desiredFormat
;
1310 desiredFormat
.format
= DXGI_FORMAT_UNKNOWN
;
1312 desiredFormat
= psurfOther
->pRenderTarget
->GetPixelFormat();
1314 desiredFormat
.alphaMode
= D2D1_ALPHA_MODE_IGNORE
;
1315 HRESULT hr
= psurfOther
->pRenderTarget
->CreateCompatibleRenderTarget(
1316 &desiredSize
, NULL
, &desiredFormat
, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE
, &pCompatibleRenderTarget
);
1317 if (SUCCEEDED(hr
)) {
1318 pRenderTarget
= pCompatibleRenderTarget
;
1319 pRenderTarget
->BeginDraw();
1320 ownRenderTarget
= true;
1324 void SurfaceD2D::PenColour(ColourDesired fore
) {
1328 void SurfaceD2D::D2DPenColour(ColourDesired fore
, int alpha
) {
1329 if (pRenderTarget
) {
1331 col
.r
= (fore
.AsLong() & 0xff) / 255.0f
;
1332 col
.g
= ((fore
.AsLong() & 0xff00) >> 8) / 255.0f
;
1333 col
.b
= (fore
.AsLong() >> 16) / 255.0f
;
1334 col
.a
= alpha
/ 255.0f
;
1336 pBrush
->SetColor(col
);
1338 HRESULT hr
= pRenderTarget
->CreateSolidColorBrush(col
, &pBrush
);
1339 if (!SUCCEEDED(hr
) && pBrush
) {
1347 void SurfaceD2D::SetFont(Font
&font_
) {
1348 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
1349 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_DIRECTWRITE
);
1350 pTextFormat
= pfm
->pTextFormat
;
1351 yAscent
= pfm
->yAscent
;
1352 yDescent
= pfm
->yDescent
;
1353 yInternalLeading
= pfm
->yInternalLeading
;
1354 codePageText
= codePage
;
1355 if (pfm
->characterSet
) {
1356 codePageText
= CodePageFromCharSet(pfm
->characterSet
, codePage
);
1358 if (pRenderTarget
) {
1359 D2D1_TEXT_ANTIALIAS_MODE aaMode
;
1360 aaMode
= DWriteMapFontQuality(pfm
->extraFontFlag
);
1362 if (aaMode
== D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
&& customClearTypeRenderingParams
)
1363 pRenderTarget
->SetTextRenderingParams(customClearTypeRenderingParams
);
1364 else if (defaultRenderingParams
)
1365 pRenderTarget
->SetTextRenderingParams(defaultRenderingParams
);
1367 pRenderTarget
->SetTextAntialiasMode(aaMode
);
1371 int SurfaceD2D::LogPixelsY() {
1375 int SurfaceD2D::DeviceHeightFont(int points
) {
1376 return ::MulDiv(points
, LogPixelsY(), 72);
1379 void SurfaceD2D::MoveTo(int x_
, int y_
) {
1384 static int Delta(int difference
) {
1387 else if (difference
> 0)
1393 static float RoundFloat(float f
) {
1394 return float(int(f
+0.5f
));
1397 void SurfaceD2D::LineTo(int x_
, int y_
) {
1398 if (pRenderTarget
) {
1400 int xDelta
= Delta(xDiff
);
1402 int yDelta
= Delta(yDiff
);
1403 if ((xDiff
== 0) || (yDiff
== 0)) {
1404 // Horizontal or vertical lines can be more precisely drawn as a filled rectangle
1405 int xEnd
= x_
- xDelta
;
1406 int left
= Platform::Minimum(x
, xEnd
);
1407 int width
= abs(x
- xEnd
) + 1;
1408 int yEnd
= y_
- yDelta
;
1409 int top
= Platform::Minimum(y
, yEnd
);
1410 int height
= abs(y
- yEnd
) + 1;
1411 D2D1_RECT_F rectangle1
= D2D1::RectF(static_cast<float>(left
), static_cast<float>(top
),
1412 static_cast<float>(left
+width
), static_cast<float>(top
+height
));
1413 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1414 } else if ((abs(xDiff
) == abs(yDiff
))) {
1416 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5f
, y
+ 0.5f
),
1417 D2D1::Point2F(x_
+ 0.5f
- xDelta
, y_
+ 0.5f
- yDelta
), pBrush
);
1419 // Line has a different slope so difficult to avoid last pixel
1420 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5f
, y
+ 0.5f
),
1421 D2D1::Point2F(x_
+ 0.5f
, y_
+ 0.5f
), pBrush
);
1428 void SurfaceD2D::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
1429 if (pRenderTarget
) {
1430 ID2D1Factory
*pFactory
= 0;
1431 pRenderTarget
->GetFactory(&pFactory
);
1432 ID2D1PathGeometry
*geometry
=0;
1433 HRESULT hr
= pFactory
->CreatePathGeometry(&geometry
);
1434 if (SUCCEEDED(hr
)) {
1435 ID2D1GeometrySink
*sink
= 0;
1436 hr
= geometry
->Open(&sink
);
1437 if (SUCCEEDED(hr
)) {
1438 sink
->BeginFigure(D2D1::Point2F(pts
[0].x
+ 0.5f
, pts
[0].y
+ 0.5f
), D2D1_FIGURE_BEGIN_FILLED
);
1439 for (size_t i
=1; i
<static_cast<size_t>(npts
); i
++) {
1440 sink
->AddLine(D2D1::Point2F(pts
[i
].x
+ 0.5f
, pts
[i
].y
+ 0.5f
));
1442 sink
->EndFigure(D2D1_FIGURE_END_CLOSED
);
1447 pRenderTarget
->FillGeometry(geometry
,pBrush
);
1449 pRenderTarget
->DrawGeometry(geometry
,pBrush
);
1452 geometry
->Release();
1457 void SurfaceD2D::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1458 if (pRenderTarget
) {
1459 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5f
, rc
.top
+0.5f
, RoundFloat(rc
.right
) - 0.5f
, rc
.bottom
-0.5f
);
1461 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1463 pRenderTarget
->DrawRectangle(&rectangle1
, pBrush
);
1467 void SurfaceD2D::FillRectangle(PRectangle rc
, ColourDesired back
) {
1468 if (pRenderTarget
) {
1470 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
), rc
.top
, RoundFloat(rc
.right
), rc
.bottom
);
1471 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1475 void SurfaceD2D::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
1476 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfacePattern
);
1477 surfOther
.FlushDrawing();
1478 ID2D1Bitmap
*pBitmap
= NULL
;
1479 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1480 surfOther
.pRenderTarget
);
1481 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1482 if (SUCCEEDED(hr
)) {
1483 ID2D1BitmapBrush
*pBitmapBrush
= NULL
;
1484 D2D1_BITMAP_BRUSH_PROPERTIES brushProperties
=
1485 D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP
, D2D1_EXTEND_MODE_WRAP
,
1486 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
1487 // Create the bitmap brush.
1488 hr
= pRenderTarget
->CreateBitmapBrush(pBitmap
, brushProperties
, &pBitmapBrush
);
1490 if (SUCCEEDED(hr
)) {
1491 pRenderTarget
->FillRectangle(
1492 D2D1::RectF(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
),
1494 pBitmapBrush
->Release();
1499 void SurfaceD2D::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1500 if (pRenderTarget
) {
1501 D2D1_ROUNDED_RECT roundedRectFill
= {
1502 D2D1::RectF(rc
.left
+1.0f
, rc
.top
+1.0f
, rc
.right
-1.0f
, rc
.bottom
-1.0f
),
1505 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1507 D2D1_ROUNDED_RECT roundedRect
= {
1508 D2D1::RectF(rc
.left
+ 0.5f
, rc
.top
+0.5f
, rc
.right
- 0.5f
, rc
.bottom
-0.5f
),
1511 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1515 void SurfaceD2D::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1516 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
1517 if (pRenderTarget
) {
1518 if (cornerSize
== 0) {
1519 // When corner size is zero, draw square rectangle to prevent blurry pixels at corners
1520 D2D1_RECT_F rectFill
= D2D1::RectF(RoundFloat(rc
.left
) + 1.0f
, rc
.top
+ 1.0f
, RoundFloat(rc
.right
) - 1.0f
, rc
.bottom
- 1.0f
);
1521 D2DPenColour(fill
, alphaFill
);
1522 pRenderTarget
->FillRectangle(rectFill
, pBrush
);
1524 D2D1_RECT_F rectOutline
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5f
, rc
.top
+ 0.5f
, RoundFloat(rc
.right
) - 0.5f
, rc
.bottom
- 0.5f
);
1525 D2DPenColour(outline
, alphaOutline
);
1526 pRenderTarget
->DrawRectangle(rectOutline
, pBrush
);
1528 const float cornerSizeF
= static_cast<float>(cornerSize
);
1529 D2D1_ROUNDED_RECT roundedRectFill
= {
1530 D2D1::RectF(RoundFloat(rc
.left
) + 1.0f
, rc
.top
+ 1.0f
, RoundFloat(rc
.right
) - 1.0f
, rc
.bottom
- 1.0f
),
1531 cornerSizeF
, cornerSizeF
};
1532 D2DPenColour(fill
, alphaFill
);
1533 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1535 D2D1_ROUNDED_RECT roundedRect
= {
1536 D2D1::RectF(RoundFloat(rc
.left
) + 0.5f
, rc
.top
+ 0.5f
, RoundFloat(rc
.right
) - 0.5f
, rc
.bottom
- 0.5f
),
1537 cornerSizeF
, cornerSizeF
};
1538 D2DPenColour(outline
, alphaOutline
);
1539 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1544 void SurfaceD2D::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
1545 if (pRenderTarget
) {
1546 if (rc
.Width() > width
)
1547 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
1548 rc
.right
= rc
.left
+ width
;
1549 if (rc
.Height() > height
)
1550 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
1551 rc
.bottom
= rc
.top
+ height
;
1553 std::vector
<unsigned char> image(height
* width
* 4);
1554 for (int yPixel
=0; yPixel
<height
; yPixel
++) {
1555 for (int xPixel
= 0; xPixel
<width
; xPixel
++) {
1556 unsigned char *pixel
= &image
[0] + (yPixel
*width
+ xPixel
) * 4;
1557 unsigned char alpha
= pixelsImage
[3];
1558 // Input is RGBA, output is BGRA with premultiplied alpha
1559 pixel
[2] = (*pixelsImage
++) * alpha
/ 255;
1560 pixel
[1] = (*pixelsImage
++) * alpha
/ 255;
1561 pixel
[0] = (*pixelsImage
++) * alpha
/ 255;
1562 pixel
[3] = *pixelsImage
++;
1566 ID2D1Bitmap
*bitmap
= 0;
1567 D2D1_SIZE_U size
= D2D1::SizeU(width
, height
);
1568 D2D1_BITMAP_PROPERTIES props
= {{DXGI_FORMAT_B8G8R8A8_UNORM
,
1569 D2D1_ALPHA_MODE_PREMULTIPLIED
}, 72.0, 72.0};
1570 HRESULT hr
= pRenderTarget
->CreateBitmap(size
, &image
[0],
1571 width
* 4, &props
, &bitmap
);
1572 if (SUCCEEDED(hr
)) {
1573 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1574 pRenderTarget
->DrawBitmap(bitmap
, rcDestination
);
1580 void SurfaceD2D::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1581 if (pRenderTarget
) {
1582 FLOAT radius
= rc
.Width() / 2.0f
;
1583 D2D1_ELLIPSE ellipse
= {
1584 D2D1::Point2F((rc
.left
+ rc
.right
) / 2.0f
, (rc
.top
+ rc
.bottom
) / 2.0f
),
1588 pRenderTarget
->FillEllipse(ellipse
, pBrush
);
1590 pRenderTarget
->DrawEllipse(ellipse
, pBrush
);
1594 void SurfaceD2D::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
1595 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfaceSource
);
1596 surfOther
.FlushDrawing();
1597 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1598 surfOther
.pRenderTarget
);
1599 ID2D1Bitmap
*pBitmap
= NULL
;
1600 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1601 if (SUCCEEDED(hr
)) {
1602 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1603 D2D1_RECT_F rcSource
= {from
.x
, from
.y
, from
.x
+ rc
.Width(), from
.y
+ rc
.Height()};
1604 pRenderTarget
->DrawBitmap(pBitmap
, rcDestination
, 1.0f
,
1605 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
, rcSource
);
1606 pRenderTarget
->Flush();
1611 void SurfaceD2D::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
) {
1614 // Use Unicode calls
1615 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1616 if (pRenderTarget
&& pTextFormat
&& pBrush
) {
1617 if (fuOptions
& ETO_CLIPPED
) {
1618 D2D1_RECT_F rcClip
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1619 pRenderTarget
->PushAxisAlignedClip(rcClip
, D2D1_ANTIALIAS_MODE_ALIASED
);
1622 // Explicitly creating a text layout appears a little faster
1623 IDWriteTextLayout
*pTextLayout
;
1624 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
,
1625 rc
.Width(), rc
.Height(), &pTextLayout
);
1626 if (SUCCEEDED(hr
)) {
1627 D2D1_POINT_2F origin
= {rc
.left
, ybase
-yAscent
};
1628 pRenderTarget
->DrawTextLayout(origin
, pTextLayout
, pBrush
, D2D1_DRAW_TEXT_OPTIONS_NONE
);
1629 pTextLayout
->Release();
1632 if (fuOptions
& ETO_CLIPPED
) {
1633 pRenderTarget
->PopAxisAlignedClip();
1638 void SurfaceD2D::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1639 ColourDesired fore
, ColourDesired back
) {
1640 if (pRenderTarget
) {
1641 FillRectangle(rc
, back
);
1643 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
1647 void SurfaceD2D::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1648 ColourDesired fore
, ColourDesired back
) {
1649 if (pRenderTarget
) {
1650 FillRectangle(rc
, back
);
1652 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
1656 void SurfaceD2D::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1657 ColourDesired fore
) {
1658 // Avoid drawing spaces in transparent mode
1659 for (int i
=0; i
<len
; i
++) {
1661 if (pRenderTarget
) {
1663 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
1670 XYPOSITION
SurfaceD2D::WidthText(Font
&font_
, const char *s
, int len
) {
1673 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1674 if (pIDWriteFactory
&& pTextFormat
) {
1676 IDWriteTextLayout
*pTextLayout
= 0;
1677 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1678 if (SUCCEEDED(hr
)) {
1679 DWRITE_TEXT_METRICS textMetrics
;
1680 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1681 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1682 pTextLayout
->Release();
1688 void SurfaceD2D::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
1691 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1692 TextPositions
poses(tbuf
.tlen
);
1694 const int clusters
= 1000;
1695 DWRITE_CLUSTER_METRICS clusterMetrics
[clusters
];
1697 if (pIDWriteFactory
&& pTextFormat
) {
1700 IDWriteTextLayout
*pTextLayout
= 0;
1701 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 10000.0, 1000.0, &pTextLayout
);
1704 // For now, assuming WCHAR == cluster
1705 if (!SUCCEEDED(pTextLayout
->GetClusterMetrics(clusterMetrics
, clusters
, &count
)))
1707 FLOAT position
= 0.0f
;
1709 for (size_t ci
=0; ci
<count
; ci
++) {
1710 position
+= clusterMetrics
[ci
].width
;
1711 for (size_t inCluster
=0; inCluster
<clusterMetrics
[ci
].length
; inCluster
++) {
1712 //poses.buffer[ti++] = int(position + 0.5);
1713 poses
.buffer
[ti
++] = position
;
1716 PLATFORM_ASSERT(ti
== static_cast<size_t>(tbuf
.tlen
));
1717 pTextLayout
->Release();
1720 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
1722 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1725 unsigned char uch
= us
[i
];
1726 unsigned int lenChar
= 1;
1727 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
1730 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
1732 } else if (uch
>= (0x80)) {
1735 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
1736 positions
[i
++] = poses
.buffer
[ui
];
1740 XYPOSITION lastPos
= 0.0f
;
1742 lastPos
= positions
[i
-1];
1744 positions
[i
++] = lastPos
;
1746 } else if (codePageText
== 0) {
1748 // One character per position
1749 PLATFORM_ASSERT(len
== tbuf
.tlen
);
1750 for (size_t kk
=0; kk
<static_cast<size_t>(len
); kk
++) {
1751 positions
[kk
] = poses
.buffer
[kk
];
1756 // May be more than one byte per position
1757 unsigned int ui
= 0;
1758 FLOAT position
= 0.0f
;
1759 for (int i
=0; i
<len
;) {
1761 position
= poses
.buffer
[ui
];
1762 if (Platform::IsDBCSLeadByte(codePageText
, s
[i
])) {
1763 positions
[i
] = position
;
1764 positions
[i
+1] = position
;
1767 positions
[i
] = position
;
1776 XYPOSITION
SurfaceD2D::WidthChar(Font
&font_
, char ch
) {
1779 if (pIDWriteFactory
&& pTextFormat
) {
1781 IDWriteTextLayout
*pTextLayout
= 0;
1782 const WCHAR wch
= ch
;
1783 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(&wch
, 1, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1784 if (SUCCEEDED(hr
)) {
1785 DWRITE_TEXT_METRICS textMetrics
;
1786 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1787 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1788 pTextLayout
->Release();
1794 XYPOSITION
SurfaceD2D::Ascent(Font
&font_
) {
1796 return ceil(yAscent
);
1799 XYPOSITION
SurfaceD2D::Descent(Font
&font_
) {
1801 return ceil(yDescent
);
1804 XYPOSITION
SurfaceD2D::InternalLeading(Font
&font_
) {
1806 return floor(yInternalLeading
);
1809 XYPOSITION
SurfaceD2D::ExternalLeading(Font
&) {
1810 // Not implemented, always return one
1814 XYPOSITION
SurfaceD2D::Height(Font
&font_
) {
1815 return Ascent(font_
) + Descent(font_
);
1818 XYPOSITION
SurfaceD2D::AverageCharWidth(Font
&font_
) {
1821 if (pIDWriteFactory
&& pTextFormat
) {
1823 IDWriteTextLayout
*pTextLayout
= 0;
1824 const WCHAR wszAllAlpha
[] = L
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1825 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(wszAllAlpha
, static_cast<UINT32
>(wcslen(wszAllAlpha
)),
1826 pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1827 if (SUCCEEDED(hr
)) {
1828 DWRITE_TEXT_METRICS textMetrics
;
1829 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1830 width
= textMetrics
.width
/ wcslen(wszAllAlpha
);
1831 pTextLayout
->Release();
1837 void SurfaceD2D::SetClip(PRectangle rc
) {
1838 if (pRenderTarget
) {
1839 D2D1_RECT_F rcClip
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1840 pRenderTarget
->PushAxisAlignedClip(rcClip
, D2D1_ANTIALIAS_MODE_ALIASED
);
1845 void SurfaceD2D::FlushCachedState() {
1848 void SurfaceD2D::SetUnicodeMode(bool unicodeMode_
) {
1849 unicodeMode
=unicodeMode_
;
1852 void SurfaceD2D::SetDBCSMode(int codePage_
) {
1853 // No action on window as automatically handled by system.
1854 codePage
= codePage_
;
1858 Surface
*Surface::Allocate(int technology
) {
1859 #if defined(USE_D2D)
1860 if (technology
== SCWIN_TECH_GDI
)
1861 return new SurfaceGDI
;
1863 return new SurfaceD2D
;
1865 return new SurfaceGDI
;
1872 void Window::Destroy() {
1874 ::DestroyWindow(reinterpret_cast<HWND
>(wid
));
1878 bool Window::HasFocus() {
1879 return ::GetFocus() == wid
;
1882 PRectangle
Window::GetPosition() {
1884 ::GetWindowRect(reinterpret_cast<HWND
>(wid
), &rc
);
1885 return PRectangle::FromInts(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1888 void Window::SetPosition(PRectangle rc
) {
1889 ::SetWindowPos(reinterpret_cast<HWND
>(wid
),
1890 0, static_cast<int>(rc
.left
), static_cast<int>(rc
.top
),
1891 static_cast<int>(rc
.Width()), static_cast<int>(rc
.Height()), SWP_NOZORDER
| SWP_NOACTIVATE
);
1894 static RECT
RectFromMonitor(HMONITOR hMonitor
) {
1895 if (GetMonitorInfoFn
) {
1896 MONITORINFO mi
= {0};
1897 mi
.cbSize
= sizeof(mi
);
1898 if (GetMonitorInfoFn(hMonitor
, &mi
)) {
1902 RECT rc
= {0, 0, 0, 0};
1903 if (::SystemParametersInfoA(SPI_GETWORKAREA
, 0, &rc
, 0) == 0) {
1912 void Window::SetPositionRelative(PRectangle rc
, Window w
) {
1913 LONG style
= ::GetWindowLong(reinterpret_cast<HWND
>(wid
), GWL_STYLE
);
1914 if (style
& WS_POPUP
) {
1915 POINT ptOther
= {0, 0};
1916 ::ClientToScreen(reinterpret_cast<HWND
>(w
.GetID()), &ptOther
);
1917 rc
.Move(static_cast<XYPOSITION
>(ptOther
.x
), static_cast<XYPOSITION
>(ptOther
.y
));
1919 RECT rcMonitor
= RectFromPRectangle(rc
);
1921 HMONITOR hMonitor
= NULL
;
1922 if (MonitorFromRectFn
)
1923 hMonitor
= MonitorFromRectFn(&rcMonitor
, MONITOR_DEFAULTTONEAREST
);
1924 // If hMonitor is NULL, that's just the main screen anyways.
1925 //::GetMonitorInfo(hMonitor, &mi);
1926 RECT rcWork
= RectFromMonitor(hMonitor
);
1928 if (rcWork
.left
< rcWork
.right
) {
1929 // Now clamp our desired rectangle to fit inside the work area
1930 // This way, the menu will fit wholly on one screen. An improvement even
1931 // if you don't have a second monitor on the left... Menu's appears half on
1932 // one screen and half on the other are just U.G.L.Y.!
1933 if (rc
.right
> rcWork
.right
)
1934 rc
.Move(rcWork
.right
- rc
.right
, 0);
1935 if (rc
.bottom
> rcWork
.bottom
)
1936 rc
.Move(0, rcWork
.bottom
- rc
.bottom
);
1937 if (rc
.left
< rcWork
.left
)
1938 rc
.Move(rcWork
.left
- rc
.left
, 0);
1939 if (rc
.top
< rcWork
.top
)
1940 rc
.Move(0, rcWork
.top
- rc
.top
);
1946 PRectangle
Window::GetClientPosition() {
1949 ::GetClientRect(reinterpret_cast<HWND
>(wid
), &rc
);
1950 return PRectangle::FromInts(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1953 void Window::Show(bool show
) {
1955 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_SHOWNOACTIVATE
);
1957 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_HIDE
);
1960 void Window::InvalidateAll() {
1961 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), NULL
, FALSE
);
1964 void Window::InvalidateRectangle(PRectangle rc
) {
1965 RECT rcw
= RectFromPRectangle(rc
);
1966 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), &rcw
, FALSE
);
1969 static LRESULT
Window_SendMessage(Window
*w
, UINT msg
, WPARAM wParam
=0, LPARAM lParam
=0) {
1970 return ::SendMessage(reinterpret_cast<HWND
>(w
->GetID()), msg
, wParam
, lParam
);
1973 void Window::SetFont(Font
&font
) {
1974 Window_SendMessage(this, WM_SETFONT
,
1975 reinterpret_cast<WPARAM
>(font
.GetID()), 0);
1978 static void FlipBitmap(HBITMAP bitmap
, int width
, int height
) {
1979 HDC hdc
= ::CreateCompatibleDC(NULL
);
1981 HGDIOBJ prevBmp
= ::SelectObject(hdc
, bitmap
);
1982 ::StretchBlt(hdc
, width
- 1, 0, -width
, height
, hdc
, 0, 0, width
, height
, SRCCOPY
);
1983 ::SelectObject(hdc
, prevBmp
);
1988 static HCURSOR
GetReverseArrowCursor() {
1989 if (reverseArrowCursor
!= NULL
)
1990 return reverseArrowCursor
;
1992 ::EnterCriticalSection(&crPlatformLock
);
1993 HCURSOR cursor
= reverseArrowCursor
;
1994 if (cursor
== NULL
) {
1995 cursor
= ::LoadCursor(NULL
, IDC_ARROW
);
1997 if (::GetIconInfo(cursor
, &info
)) {
1999 if (::GetObject(info
.hbmMask
, sizeof(bmp
), &bmp
)) {
2000 FlipBitmap(info
.hbmMask
, bmp
.bmWidth
, bmp
.bmHeight
);
2001 if (info
.hbmColor
!= NULL
)
2002 FlipBitmap(info
.hbmColor
, bmp
.bmWidth
, bmp
.bmHeight
);
2003 info
.xHotspot
= (DWORD
)bmp
.bmWidth
- 1 - info
.xHotspot
;
2005 reverseArrowCursor
= ::CreateIconIndirect(&info
);
2006 if (reverseArrowCursor
!= NULL
)
2007 cursor
= reverseArrowCursor
;
2010 ::DeleteObject(info
.hbmMask
);
2011 if (info
.hbmColor
!= NULL
)
2012 ::DeleteObject(info
.hbmColor
);
2015 ::LeaveCriticalSection(&crPlatformLock
);
2019 void Window::SetCursor(Cursor curs
) {
2022 ::SetCursor(::LoadCursor(NULL
,IDC_IBEAM
));
2025 ::SetCursor(::LoadCursor(NULL
,IDC_UPARROW
));
2028 ::SetCursor(::LoadCursor(NULL
,IDC_WAIT
));
2031 ::SetCursor(::LoadCursor(NULL
,IDC_SIZEWE
));
2034 ::SetCursor(::LoadCursor(NULL
,IDC_SIZENS
));
2037 ::SetCursor(::LoadCursor(NULL
,IDC_HAND
));
2039 case cursorReverseArrow
:
2040 ::SetCursor(GetReverseArrowCursor());
2043 case cursorInvalid
: // Should not occur, but just in case.
2044 ::SetCursor(::LoadCursor(NULL
,IDC_ARROW
));
2049 void Window::SetTitle(const char *s
) {
2050 ::SetWindowTextA(reinterpret_cast<HWND
>(wid
), s
);
2053 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
2055 PRectangle
Window::GetMonitorRect(Point pt
) {
2056 // MonitorFromPoint and GetMonitorInfo are not available on Windows 95 and NT 4.
2057 PRectangle rcPosition
= GetPosition();
2058 POINT ptDesktop
= {static_cast<LONG
>(pt
.x
+ rcPosition
.left
),
2059 static_cast<LONG
>(pt
.y
+ rcPosition
.top
)};
2060 HMONITOR hMonitor
= NULL
;
2061 if (MonitorFromPointFn
)
2062 hMonitor
= MonitorFromPointFn(ptDesktop
, MONITOR_DEFAULTTONEAREST
);
2064 RECT rcWork
= RectFromMonitor(hMonitor
);
2065 if (rcWork
.left
< rcWork
.right
) {
2066 PRectangle
rcMonitor(
2067 rcWork
.left
- rcPosition
.left
,
2068 rcWork
.top
- rcPosition
.top
,
2069 rcWork
.right
- rcPosition
.left
,
2070 rcWork
.bottom
- rcPosition
.top
);
2073 return PRectangle();
2077 struct ListItemData
{
2083 std::vector
<char> words
;
2085 std::vector
<ListItemData
> data
;
2098 ListItemData
Get(int index
) const {
2099 if (index
>= 0 && index
< static_cast<int>(data
.size())) {
2102 ListItemData missing
= {"", -1};
2107 return static_cast<int>(data
.size());
2110 void AllocItem(const char *text
, int pixId
) {
2111 ListItemData lid
= { text
, pixId
};
2112 data
.push_back(lid
);
2115 char *SetWords(const char *s
) {
2116 words
= std::vector
<char>(s
, s
+strlen(s
)+1);
2121 const TCHAR ListBoxX_ClassName
[] = TEXT("ListBoxX");
2123 ListBox::ListBox() {
2126 ListBox::~ListBox() {
2129 class ListBoxX
: public ListBox
{
2133 RGBAImageSet images
;
2137 int desiredVisibleRows
;
2138 unsigned int maxItemCharacters
;
2139 unsigned int aveCharWidth
;
2142 CallBackAction doubleClickAction
;
2143 void *doubleClickActionData
;
2144 const char *widestItem
;
2145 unsigned int maxCharWidth
;
2147 PRectangle rcPreSize
;
2149 Point location
; // Caret location at which the list is opened
2150 int wheelDelta
; // mouse wheel residue
2152 HWND
GetHWND() const;
2153 void AppendListItem(const char *text
, const char *numword
);
2154 static void AdjustWindowRect(PRectangle
*rc
);
2155 int ItemHeight() const;
2156 int MinClientWidth() const;
2157 int TextOffset() const;
2158 POINT
GetClientExtent() const;
2159 POINT
MinTrackSize() const;
2160 POINT
MaxTrackSize() const;
2161 void SetRedraw(bool on
);
2162 void OnDoubleClick();
2163 void ResizeToCursor();
2164 void StartResize(WPARAM
);
2165 LRESULT
NcHitTest(WPARAM
, LPARAM
) const;
2166 void CentreItem(int n
);
2168 static LRESULT PASCAL
ControlWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2170 static const Point ItemInset
; // Padding around whole item
2171 static const Point TextInset
; // Padding around text
2172 static const Point ImageInset
; // Padding around image
2175 ListBoxX() : lineHeight(10), fontCopy(0), technology(0), lb(0), unicodeMode(false),
2176 desiredVisibleRows(5), maxItemCharacters(0), aveCharWidth(8),
2177 parent(NULL
), ctrlID(0), doubleClickAction(NULL
), doubleClickActionData(NULL
),
2178 widestItem(NULL
), maxCharWidth(1), resizeHit(0), wheelDelta(0) {
2180 virtual ~ListBoxX() {
2182 ::DeleteObject(fontCopy
);
2186 virtual void SetFont(Font
&font
);
2187 virtual void Create(Window
&parent_
, int ctrlID_
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
);
2188 virtual void SetAverageCharWidth(int width
);
2189 virtual void SetVisibleRows(int rows
);
2190 virtual int GetVisibleRows() const;
2191 virtual PRectangle
GetDesiredRect();
2192 virtual int CaretFromEdge();
2193 virtual void Clear();
2194 virtual void Append(char *s
, int type
= -1);
2195 virtual int Length();
2196 virtual void Select(int n
);
2197 virtual int GetSelection();
2198 virtual int Find(const char *prefix
);
2199 virtual void GetValue(int n
, char *value
, int len
);
2200 virtual void RegisterImage(int type
, const char *xpm_data
);
2201 virtual void RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
);
2202 virtual void ClearRegisteredImages();
2203 virtual void SetDoubleClickAction(CallBackAction action
, void *data
) {
2204 doubleClickAction
= action
;
2205 doubleClickActionData
= data
;
2207 virtual void SetList(const char *list
, char separator
, char typesep
);
2208 void Draw(DRAWITEMSTRUCT
*pDrawItem
);
2209 LRESULT
WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2210 static LRESULT PASCAL
StaticWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2213 const Point
ListBoxX::ItemInset(0, 0);
2214 const Point
ListBoxX::TextInset(2, 0);
2215 const Point
ListBoxX::ImageInset(1, 0);
2217 ListBox
*ListBox::Allocate() {
2218 ListBoxX
*lb
= new ListBoxX();
2222 void ListBoxX::Create(Window
&parent_
, int ctrlID_
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
) {
2225 location
= location_
;
2226 lineHeight
= lineHeight_
;
2227 unicodeMode
= unicodeMode_
;
2228 technology
= technology_
;
2229 HWND hwndParent
= reinterpret_cast<HWND
>(parent
->GetID());
2230 HINSTANCE hinstanceParent
= GetWindowInstance(hwndParent
);
2231 // Window created as popup so not clipped within parent client area
2232 wid
= ::CreateWindowEx(
2233 WS_EX_WINDOWEDGE
, ListBoxX_ClassName
, TEXT(""),
2234 WS_POPUP
| WS_THICKFRAME
,
2235 100,100, 150,80, hwndParent
,
2240 POINT locationw
= {static_cast<LONG
>(location
.x
), static_cast<LONG
>(location
.y
)};
2241 ::MapWindowPoints(hwndParent
, NULL
, &locationw
, 1);
2242 location
= Point::FromInts(locationw
.x
, locationw
.y
);
2245 void ListBoxX::SetFont(Font
&font
) {
2248 ::DeleteObject(fontCopy
);
2251 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font
.GetID());
2252 fontCopy
= pfm
->HFont();
2253 ::SendMessage(lb
, WM_SETFONT
, reinterpret_cast<WPARAM
>(fontCopy
), 0);
2257 void ListBoxX::SetAverageCharWidth(int width
) {
2258 aveCharWidth
= width
;
2261 void ListBoxX::SetVisibleRows(int rows
) {
2262 desiredVisibleRows
= rows
;
2265 int ListBoxX::GetVisibleRows() const {
2266 return desiredVisibleRows
;
2269 HWND
ListBoxX::GetHWND() const {
2270 return reinterpret_cast<HWND
>(GetID());
2273 PRectangle
ListBoxX::GetDesiredRect() {
2274 PRectangle rcDesired
= GetPosition();
2276 int rows
= Length();
2277 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2278 rows
= desiredVisibleRows
;
2279 rcDesired
.bottom
= rcDesired
.top
+ ItemHeight() * rows
;
2281 int width
= MinClientWidth();
2282 HDC hdc
= ::GetDC(lb
);
2283 HFONT oldFont
= SelectFont(hdc
, fontCopy
);
2284 SIZE textSize
= {0, 0};
2287 len
= static_cast<int>(strlen(widestItem
));
2289 const TextWide
tbuf(widestItem
, len
, unicodeMode
);
2290 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &textSize
);
2292 ::GetTextExtentPoint32A(hdc
, widestItem
, len
, &textSize
);
2296 ::GetTextMetrics(hdc
, &tm
);
2297 maxCharWidth
= tm
.tmMaxCharWidth
;
2298 SelectFont(hdc
, oldFont
);
2299 ::ReleaseDC(lb
, hdc
);
2301 int widthDesired
= Platform::Maximum(textSize
.cx
, (len
+ 1) * tm
.tmAveCharWidth
);
2302 if (width
< widthDesired
)
2303 width
= widthDesired
;
2305 rcDesired
.right
= rcDesired
.left
+ TextOffset() + width
+ (TextInset
.x
* 2);
2306 if (Length() > rows
)
2307 rcDesired
.right
+= ::GetSystemMetrics(SM_CXVSCROLL
);
2309 AdjustWindowRect(&rcDesired
);
2313 int ListBoxX::TextOffset() const {
2314 int pixWidth
= images
.GetWidth();
2315 return static_cast<int>(pixWidth
== 0 ? ItemInset
.x
: ItemInset
.x
+ pixWidth
+ (ImageInset
.x
* 2));
2318 int ListBoxX::CaretFromEdge() {
2320 AdjustWindowRect(&rc
);
2321 return TextOffset() + static_cast<int>(TextInset
.x
+ (0 - rc
.left
) - 1);
2324 void ListBoxX::Clear() {
2325 ::SendMessage(lb
, LB_RESETCONTENT
, 0, 0);
2326 maxItemCharacters
= 0;
2331 void ListBoxX::Append(char *, int) {
2332 // This method is no longer called in Scintilla
2333 PLATFORM_ASSERT(false);
2336 int ListBoxX::Length() {
2340 void ListBoxX::Select(int n
) {
2341 // We are going to scroll to centre on the new selection and then select it, so disable
2342 // redraw to avoid flicker caused by a painting new selection twice in unselected and then
2346 ::SendMessage(lb
, LB_SETCURSEL
, n
, 0);
2350 int ListBoxX::GetSelection() {
2351 return static_cast<int>(::SendMessage(lb
, LB_GETCURSEL
, 0, 0));
2354 // This is not actually called at present
2355 int ListBoxX::Find(const char *) {
2359 void ListBoxX::GetValue(int n
, char *value
, int len
) {
2360 ListItemData item
= lti
.Get(n
);
2361 strncpy(value
, item
.text
, len
);
2362 value
[len
-1] = '\0';
2365 void ListBoxX::RegisterImage(int type
, const char *xpm_data
) {
2366 XPM
xpmImage(xpm_data
);
2367 images
.Add(type
, new RGBAImage(xpmImage
));
2370 void ListBoxX::RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
) {
2371 images
.Add(type
, new RGBAImage(width
, height
, 1.0, pixelsImage
));
2374 void ListBoxX::ClearRegisteredImages() {
2378 void ListBoxX::Draw(DRAWITEMSTRUCT
*pDrawItem
) {
2379 if ((pDrawItem
->itemAction
== ODA_SELECT
) || (pDrawItem
->itemAction
== ODA_DRAWENTIRE
)) {
2380 RECT rcBox
= pDrawItem
->rcItem
;
2381 rcBox
.left
+= TextOffset();
2382 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2383 RECT rcImage
= pDrawItem
->rcItem
;
2384 rcImage
.right
= rcBox
.left
;
2385 // The image is not highlighted
2386 ::FillRect(pDrawItem
->hDC
, &rcImage
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2387 ::FillRect(pDrawItem
->hDC
, &rcBox
, reinterpret_cast<HBRUSH
>(COLOR_HIGHLIGHT
+1));
2388 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHT
));
2389 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
2391 ::FillRect(pDrawItem
->hDC
, &pDrawItem
->rcItem
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2392 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOW
));
2393 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOWTEXT
));
2396 ListItemData item
= lti
.Get(pDrawItem
->itemID
);
2397 int pixId
= item
.pixId
;
2398 const char *text
= item
.text
;
2399 int len
= static_cast<int>(strlen(text
));
2401 RECT rcText
= rcBox
;
2402 ::InsetRect(&rcText
, static_cast<int>(TextInset
.x
), static_cast<int>(TextInset
.y
));
2405 const TextWide
tbuf(text
, len
, unicodeMode
);
2406 ::DrawTextW(pDrawItem
->hDC
, tbuf
.buffer
, tbuf
.tlen
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2408 ::DrawTextA(pDrawItem
->hDC
, text
, len
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2410 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2411 ::DrawFocusRect(pDrawItem
->hDC
, &rcBox
);
2414 // Draw the image, if any
2415 RGBAImage
*pimage
= images
.Get(pixId
);
2417 Surface
*surfaceItem
= Surface::Allocate(technology
);
2419 if (technology
== SCWIN_TECH_GDI
) {
2420 surfaceItem
->Init(pDrawItem
->hDC
, pDrawItem
->hwndItem
);
2421 long left
= pDrawItem
->rcItem
.left
+ static_cast<int>(ItemInset
.x
+ ImageInset
.x
);
2422 PRectangle rcImage
= PRectangle::FromInts(left
, pDrawItem
->rcItem
.top
,
2423 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2424 surfaceItem
->DrawRGBAImage(rcImage
,
2425 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2427 ::SetTextAlign(pDrawItem
->hDC
, TA_TOP
);
2429 #if defined(USE_D2D)
2430 D2D1_RENDER_TARGET_PROPERTIES props
= D2D1::RenderTargetProperties(
2431 D2D1_RENDER_TARGET_TYPE_DEFAULT
,
2433 DXGI_FORMAT_B8G8R8A8_UNORM
,
2434 D2D1_ALPHA_MODE_IGNORE
),
2437 D2D1_RENDER_TARGET_USAGE_NONE
,
2438 D2D1_FEATURE_LEVEL_DEFAULT
2440 ID2D1DCRenderTarget
*pDCRT
= 0;
2441 HRESULT hr
= pD2DFactory
->CreateDCRenderTarget(&props
, &pDCRT
);
2442 if (SUCCEEDED(hr
)) {
2444 GetClientRect(pDrawItem
->hwndItem
, &rcWindow
);
2445 hr
= pDCRT
->BindDC(pDrawItem
->hDC
, &rcWindow
);
2446 if (SUCCEEDED(hr
)) {
2447 surfaceItem
->Init(pDCRT
, pDrawItem
->hwndItem
);
2449 long left
= pDrawItem
->rcItem
.left
+ static_cast<long>(ItemInset
.x
+ ImageInset
.x
);
2450 PRectangle rcImage
= PRectangle::FromInts(left
, pDrawItem
->rcItem
.top
,
2451 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2452 surfaceItem
->DrawRGBAImage(rcImage
,
2453 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2470 void ListBoxX::AppendListItem(const char *text
, const char *numword
) {
2475 while ((ch
= *++numword
) != '\0') {
2476 pixId
= 10 * pixId
+ (ch
- '0');
2480 lti
.AllocItem(text
, pixId
);
2481 unsigned int len
= static_cast<unsigned int>(strlen(text
));
2482 if (maxItemCharacters
< len
) {
2483 maxItemCharacters
= len
;
2488 void ListBoxX::SetList(const char *list
, char separator
, char typesep
) {
2489 // Turn off redraw while populating the list - this has a significant effect, even if
2490 // the listbox is not visible.
2493 size_t size
= strlen(list
);
2494 char *words
= lti
.SetWords(list
);
2495 char *startword
= words
;
2496 char *numword
= NULL
;
2497 for (size_t i
=0; i
< size
; i
++) {
2498 if (words
[i
] == separator
) {
2502 AppendListItem(startword
, numword
);
2503 startword
= words
+ i
+ 1;
2505 } else if (words
[i
] == typesep
) {
2506 numword
= words
+ i
;
2512 AppendListItem(startword
, numword
);
2515 // Finally populate the listbox itself with the correct number of items
2516 int count
= lti
.Count();
2517 ::SendMessage(lb
, LB_INITSTORAGE
, count
, 0);
2518 for (int j
=0; j
<count
; j
++) {
2519 ::SendMessage(lb
, LB_ADDSTRING
, 0, j
+1);
2524 void ListBoxX::AdjustWindowRect(PRectangle
*rc
) {
2525 RECT rcw
= RectFromPRectangle(*rc
);
2526 ::AdjustWindowRectEx(&rcw
, WS_THICKFRAME
, false, WS_EX_WINDOWEDGE
);
2527 *rc
= PRectangle::FromInts(rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
2530 int ListBoxX::ItemHeight() const {
2531 int itemHeight
= lineHeight
+ (static_cast<int>(TextInset
.y
) * 2);
2532 int pixHeight
= images
.GetHeight() + (static_cast<int>(ImageInset
.y
) * 2);
2533 if (itemHeight
< pixHeight
) {
2534 itemHeight
= pixHeight
;
2539 int ListBoxX::MinClientWidth() const {
2540 return 12 * (aveCharWidth
+aveCharWidth
/3);
2543 POINT
ListBoxX::MinTrackSize() const {
2544 PRectangle rc
= PRectangle::FromInts(0, 0, MinClientWidth(), ItemHeight());
2545 AdjustWindowRect(&rc
);
2546 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2550 POINT
ListBoxX::MaxTrackSize() const {
2551 PRectangle rc
= PRectangle::FromInts(0, 0,
2552 Platform::Maximum(MinClientWidth(),
2553 maxCharWidth
* maxItemCharacters
+ static_cast<int>(TextInset
.x
) * 2 +
2554 TextOffset() + ::GetSystemMetrics(SM_CXVSCROLL
)),
2555 ItemHeight() * lti
.Count());
2556 AdjustWindowRect(&rc
);
2557 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2561 void ListBoxX::SetRedraw(bool on
) {
2562 ::SendMessage(lb
, WM_SETREDRAW
, static_cast<BOOL
>(on
), 0);
2564 ::InvalidateRect(lb
, NULL
, TRUE
);
2567 static XYPOSITION
XYMinimum(XYPOSITION a
, XYPOSITION b
) {
2574 static XYPOSITION
XYMaximum(XYPOSITION a
, XYPOSITION b
) {
2581 void ListBoxX::ResizeToCursor() {
2582 PRectangle rc
= GetPosition();
2584 ::GetCursorPos(&ptw
);
2585 Point pt
= Point::FromInts(ptw
.x
, ptw
.y
);
2586 pt
.x
+= dragOffset
.x
;
2587 pt
.y
+= dragOffset
.y
;
2589 switch (resizeHit
) {
2620 POINT ptMin
= MinTrackSize();
2621 POINT ptMax
= MaxTrackSize();
2622 // We don't allow the left edge to move at present, but just in case
2623 rc
.left
= XYMaximum(XYMinimum(rc
.left
, rcPreSize
.right
- ptMin
.x
), rcPreSize
.right
- ptMax
.x
);
2624 rc
.top
= XYMaximum(XYMinimum(rc
.top
, rcPreSize
.bottom
- ptMin
.y
), rcPreSize
.bottom
- ptMax
.y
);
2625 rc
.right
= XYMaximum(XYMinimum(rc
.right
, rcPreSize
.left
+ ptMax
.x
), rcPreSize
.left
+ ptMin
.x
);
2626 rc
.bottom
= XYMaximum(XYMinimum(rc
.bottom
, rcPreSize
.top
+ ptMax
.y
), rcPreSize
.top
+ ptMin
.y
);
2631 void ListBoxX::StartResize(WPARAM hitCode
) {
2632 rcPreSize
= GetPosition();
2634 ::GetCursorPos(&cursorPos
);
2640 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2641 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2645 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2646 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2649 // Note that the current hit test code prevents the left edge cases ever firing
2650 // as we don't want the left edge to be moveable
2654 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2655 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2658 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2659 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2666 ::SetCapture(GetHWND());
2667 resizeHit
= static_cast<int>(hitCode
);
2670 LRESULT
ListBoxX::NcHitTest(WPARAM wParam
, LPARAM lParam
) const {
2671 LRESULT hit
= ::DefWindowProc(GetHWND(), WM_NCHITTEST
, wParam
, lParam
);
2672 // There is an apparent bug in the DefWindowProc hit test code whereby it will
2673 // return HTTOPXXX if the window in question is shorter than the default
2674 // window caption height + frame, even if one is hovering over the bottom edge of
2675 // the frame, so workaround that here
2676 if (hit
>= HTTOP
&& hit
<= HTTOPRIGHT
) {
2677 int minHeight
= GetSystemMetrics(SM_CYMINTRACK
);
2678 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2679 int yPos
= GET_Y_LPARAM(lParam
);
2680 if ((rc
.Height() < minHeight
) && (yPos
> ((rc
.top
+ rc
.bottom
)/2))) {
2681 hit
+= HTBOTTOM
- HTTOP
;
2685 // Nerver permit resizing that moves the left edge. Allow movement of top or bottom edge
2686 // depending on whether the list is above or below the caret
2696 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2697 // Valid only if caret below list
2698 if (location
.y
< rc
.top
)
2704 case HTBOTTOMRIGHT
: {
2705 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2706 // Valid only if caret above list
2707 if (rc
.bottom
< location
.y
)
2716 void ListBoxX::OnDoubleClick() {
2718 if (doubleClickAction
!= NULL
) {
2719 doubleClickAction(doubleClickActionData
);
2723 POINT
ListBoxX::GetClientExtent() const {
2724 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetClientPosition();
2726 ret
.x
= static_cast<LONG
>(rc
.Width());
2727 ret
.y
= static_cast<LONG
>(rc
.Height());
2731 void ListBoxX::CentreItem(int n
) {
2732 // If below mid point, scroll up to centre, but with more items below if uneven
2734 POINT extent
= GetClientExtent();
2735 int visible
= extent
.y
/ItemHeight();
2736 if (visible
< Length()) {
2737 LRESULT top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0);
2738 int half
= (visible
- 1) / 2;
2739 if (n
> (top
+ half
))
2740 ::SendMessage(lb
, LB_SETTOPINDEX
, n
- half
, 0);
2745 // Performs a double-buffered paint operation to avoid flicker
2746 void ListBoxX::Paint(HDC hDC
) {
2747 POINT extent
= GetClientExtent();
2748 HBITMAP hBitmap
= ::CreateCompatibleBitmap(hDC
, extent
.x
, extent
.y
);
2749 HDC bitmapDC
= ::CreateCompatibleDC(hDC
);
2750 HBITMAP hBitmapOld
= SelectBitmap(bitmapDC
, hBitmap
);
2751 // The list background is mainly erased during painting, but can be a small
2752 // unpainted area when at the end of a non-integrally sized list with a
2753 // vertical scroll bar
2754 RECT rc
= { 0, 0, extent
.x
, extent
.y
};
2755 ::FillRect(bitmapDC
, &rc
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2756 // Paint the entire client area and vertical scrollbar
2757 ::SendMessage(lb
, WM_PRINT
, reinterpret_cast<WPARAM
>(bitmapDC
), PRF_CLIENT
|PRF_NONCLIENT
);
2758 ::BitBlt(hDC
, 0, 0, extent
.x
, extent
.y
, bitmapDC
, 0, 0, SRCCOPY
);
2759 // Select a stock brush to prevent warnings from BoundsChecker
2760 ::SelectObject(bitmapDC
, GetStockFont(WHITE_BRUSH
));
2761 SelectBitmap(bitmapDC
, hBitmapOld
);
2762 ::DeleteDC(bitmapDC
);
2763 ::DeleteObject(hBitmap
);
2766 LRESULT PASCAL
ListBoxX::ControlWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
2774 HDC hDC
= ::BeginPaint(hWnd
, &ps
);
2775 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2778 ::EndPaint(hWnd
, &ps
);
2782 case WM_MOUSEACTIVATE
:
2783 // This prevents the view activating when the scrollbar is clicked
2784 return MA_NOACTIVATE
;
2786 case WM_LBUTTONDOWN
: {
2787 // We must take control of selection to prevent the ListBox activating
2789 LRESULT lResult
= ::SendMessage(hWnd
, LB_ITEMFROMPOINT
, 0, lParam
);
2790 int item
= LOWORD(lResult
);
2791 if (HIWORD(lResult
) == 0 && item
>= 0) {
2792 ::SendMessage(hWnd
, LB_SETCURSEL
, item
, 0);
2800 case WM_LBUTTONDBLCLK
: {
2801 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2803 lbx
->OnDoubleClick();
2808 case WM_MBUTTONDOWN
:
2809 // disable the scroll wheel button click action
2813 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(GetWindowLongPtr(hWnd
, GWLP_USERDATA
));
2815 return ::CallWindowProc(prevWndProc
, hWnd
, uMsg
, wParam
, lParam
);
2817 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2821 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2824 LRESULT
ListBoxX::WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2827 HINSTANCE hinstanceParent
= GetWindowInstance(reinterpret_cast<HWND
>(parent
->GetID()));
2828 // Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list
2829 // but has useful side effect of speeding up list population significantly
2830 lb
= ::CreateWindowEx(
2831 0, TEXT("listbox"), TEXT(""),
2832 WS_CHILD
| WS_VSCROLL
| WS_VISIBLE
|
2833 LBS_OWNERDRAWFIXED
| LBS_NODATA
| LBS_NOINTEGRALHEIGHT
,
2835 reinterpret_cast<HMENU
>(ctrlID
),
2838 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(::SetWindowLongPtr(lb
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(ControlWndProc
)));
2839 ::SetWindowLongPtr(lb
, GWLP_USERDATA
, reinterpret_cast<LONG_PTR
>(prevWndProc
));
2846 ::SetWindowPos(lb
, 0, 0,0, LOWORD(lParam
), HIWORD(lParam
), SWP_NOZORDER
|SWP_NOACTIVATE
|SWP_NOMOVE
);
2847 // Ensure the selection remains visible
2848 CentreItem(GetSelection());
2855 ::BeginPaint(hWnd
, &ps
);
2856 ::EndPaint(hWnd
, &ps
);
2861 // This is not actually needed now - the registered double click action is used
2862 // directly to action a choice from the list.
2863 ::SendMessage(reinterpret_cast<HWND
>(parent
->GetID()), iMessage
, wParam
, lParam
);
2866 case WM_MEASUREITEM
: {
2867 MEASUREITEMSTRUCT
*pMeasureItem
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
2868 pMeasureItem
->itemHeight
= static_cast<unsigned int>(ItemHeight());
2873 Draw(reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
));
2878 ::SetWindowLong(hWnd
, 0, 0);
2879 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2882 // To reduce flicker we can elide background erasure since this window is
2883 // completely covered by its child.
2886 case WM_GETMINMAXINFO
: {
2887 MINMAXINFO
*minMax
= reinterpret_cast<MINMAXINFO
*>(lParam
);
2888 minMax
->ptMaxTrackSize
= MaxTrackSize();
2889 minMax
->ptMinTrackSize
= MinTrackSize();
2893 case WM_MOUSEACTIVATE
:
2894 return MA_NOACTIVATE
;
2897 return NcHitTest(wParam
, lParam
);
2899 case WM_NCLBUTTONDOWN
:
2900 // We have to implement our own window resizing because the DefWindowProc
2901 // implementation insists on activating the resized window
2902 StartResize(wParam
);
2905 case WM_MOUSEMOVE
: {
2906 if (resizeHit
== 0) {
2907 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2916 if (resizeHit
!= 0) {
2920 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2923 wheelDelta
-= static_cast<short>(HIWORD(wParam
));
2924 if (abs(wheelDelta
) >= WHEEL_DELTA
) {
2925 int nRows
= GetVisibleRows();
2926 int linesToScroll
= 1;
2928 linesToScroll
= nRows
- 1;
2930 if (linesToScroll
> 3) {
2933 linesToScroll
*= (wheelDelta
/ WHEEL_DELTA
);
2934 LRESULT top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0) + linesToScroll
;
2938 ::SendMessage(lb
, LB_SETTOPINDEX
, top
, 0);
2939 // update wheel delta residue
2940 if (wheelDelta
>= 0)
2941 wheelDelta
= wheelDelta
% WHEEL_DELTA
;
2943 wheelDelta
= - (-wheelDelta
% WHEEL_DELTA
);
2948 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2954 LRESULT PASCAL
ListBoxX::StaticWndProc(
2955 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2956 if (iMessage
== WM_CREATE
) {
2957 CREATESTRUCT
*pCreate
= reinterpret_cast<CREATESTRUCT
*>(lParam
);
2958 SetWindowPointer(hWnd
, pCreate
->lpCreateParams
);
2960 // Find C++ object associated with window.
2961 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(hWnd
));
2963 return lbx
->WndProc(hWnd
, iMessage
, wParam
, lParam
);
2965 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2969 static bool ListBoxX_Register() {
2970 WNDCLASSEX wndclassc
;
2971 wndclassc
.cbSize
= sizeof(wndclassc
);
2972 // We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for
2973 // truncated items in the list and the appearance/disappearance of the vertical scroll bar.
2974 // The list repaint is double-buffered to avoid the flicker this would otherwise cause.
2975 wndclassc
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
2976 wndclassc
.cbClsExtra
= 0;
2977 wndclassc
.cbWndExtra
= sizeof(ListBoxX
*);
2978 wndclassc
.hInstance
= hinstPlatformRes
;
2979 wndclassc
.hIcon
= NULL
;
2980 wndclassc
.hbrBackground
= NULL
;
2981 wndclassc
.lpszMenuName
= NULL
;
2982 wndclassc
.lpfnWndProc
= ListBoxX::StaticWndProc
;
2983 wndclassc
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
2984 wndclassc
.lpszClassName
= ListBoxX_ClassName
;
2985 wndclassc
.hIconSm
= 0;
2987 return ::RegisterClassEx(&wndclassc
) != 0;
2990 bool ListBoxX_Unregister() {
2991 return ::UnregisterClass(ListBoxX_ClassName
, hinstPlatformRes
) != 0;
2994 Menu::Menu() : mid(0) {
2997 void Menu::CreatePopUp() {
2999 mid
= ::CreatePopupMenu();
3002 void Menu::Destroy() {
3004 ::DestroyMenu(reinterpret_cast<HMENU
>(mid
));
3008 void Menu::Show(Point pt
, Window
&w
) {
3009 ::TrackPopupMenu(reinterpret_cast<HMENU
>(mid
),
3010 0, static_cast<int>(pt
.x
- 4), static_cast<int>(pt
.y
), 0,
3011 reinterpret_cast<HWND
>(w
.GetID()), NULL
);
3015 static bool initialisedET
= false;
3016 static bool usePerformanceCounter
= false;
3017 static LARGE_INTEGER frequency
;
3019 ElapsedTime::ElapsedTime() {
3020 if (!initialisedET
) {
3021 usePerformanceCounter
= ::QueryPerformanceFrequency(&frequency
) != 0;
3022 initialisedET
= true;
3024 if (usePerformanceCounter
) {
3025 LARGE_INTEGER timeVal
;
3026 ::QueryPerformanceCounter(&timeVal
);
3027 bigBit
= timeVal
.HighPart
;
3028 littleBit
= timeVal
.LowPart
;
3035 double ElapsedTime::Duration(bool reset
) {
3040 if (usePerformanceCounter
) {
3042 ::QueryPerformanceCounter(&lEnd
);
3043 endBigBit
= lEnd
.HighPart
;
3044 endLittleBit
= lEnd
.LowPart
;
3045 LARGE_INTEGER lBegin
;
3046 lBegin
.HighPart
= bigBit
;
3047 lBegin
.LowPart
= littleBit
;
3048 double elapsed
= static_cast<double>(lEnd
.QuadPart
- lBegin
.QuadPart
);
3049 result
= elapsed
/ static_cast<double>(frequency
.QuadPart
);
3051 endBigBit
= clock();
3053 double elapsed
= endBigBit
- bigBit
;
3054 result
= elapsed
/ CLOCKS_PER_SEC
;
3058 littleBit
= endLittleBit
;
3063 class DynamicLibraryImpl
: public DynamicLibrary
{
3067 explicit DynamicLibraryImpl(const char *modulePath
) {
3068 h
= ::LoadLibraryA(modulePath
);
3071 virtual ~DynamicLibraryImpl() {
3076 // Use GetProcAddress to get a pointer to the relevant function.
3077 virtual Function
FindFunction(const char *name
) {
3079 // C++ standard doesn't like casts betwen function pointers and void pointers so use a union
3084 fnConv
.fp
= ::GetProcAddress(h
, name
);
3091 virtual bool IsValid() {
3096 DynamicLibrary
*DynamicLibrary::Load(const char *modulePath
) {
3097 return static_cast<DynamicLibrary
*>(new DynamicLibraryImpl(modulePath
));
3100 ColourDesired
Platform::Chrome() {
3101 return ::GetSysColor(COLOR_3DFACE
);
3104 ColourDesired
Platform::ChromeHighlight() {
3105 return ::GetSysColor(COLOR_3DHIGHLIGHT
);
3108 const char *Platform::DefaultFont() {
3112 int Platform::DefaultFontSize() {
3116 unsigned int Platform::DoubleClickTime() {
3117 return ::GetDoubleClickTime();
3120 bool Platform::MouseButtonBounce() {
3124 void Platform::DebugDisplay(const char *s
) {
3125 ::OutputDebugStringA(s
);
3128 bool Platform::IsKeyDown(int key
) {
3129 return (::GetKeyState(key
) & 0x80000000) != 0;
3132 long Platform::SendScintilla(WindowID w
, unsigned int msg
, unsigned long wParam
, long lParam
) {
3133 // This should never be called - its here to satisfy an old interface
3134 return static_cast<long>(::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
, lParam
));
3137 long Platform::SendScintillaPointer(WindowID w
, unsigned int msg
, unsigned long wParam
, void *lParam
) {
3138 // This should never be called - its here to satisfy an old interface
3139 return static_cast<long>(::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
,
3140 reinterpret_cast<LPARAM
>(lParam
)));
3143 bool Platform::IsDBCSLeadByte(int codePage
, char ch
) {
3144 // Byte ranges found in Wikipedia articles with relevant search strings in each case
3145 unsigned char uch
= static_cast<unsigned char>(ch
);
3149 return ((uch
>= 0x81) && (uch
<= 0x9F)) ||
3150 ((uch
>= 0xE0) && (uch
<= 0xEF));
3153 return (uch
>= 0x81) && (uch
<= 0xFE);
3155 // Korean Wansung KS C-5601-1987
3156 return (uch
>= 0x81) && (uch
<= 0xFE);
3159 return (uch
>= 0x81) && (uch
<= 0xFE);
3161 // Korean Johab KS C-5601-1992
3163 ((uch
>= 0x84) && (uch
<= 0xD3)) ||
3164 ((uch
>= 0xD8) && (uch
<= 0xDE)) ||
3165 ((uch
>= 0xE0) && (uch
<= 0xF9));
3170 int Platform::DBCSCharLength(int codePage
, const char *s
) {
3171 if (codePage
== 932 || codePage
== 936 || codePage
== 949 ||
3172 codePage
== 950 || codePage
== 1361) {
3173 return Platform::IsDBCSLeadByte(codePage
, s
[0]) ? 2 : 1;
3179 int Platform::DBCSCharMaxLength() {
3183 // These are utility functions not really tied to a platform
3185 int Platform::Minimum(int a
, int b
) {
3192 int Platform::Maximum(int a
, int b
) {
3202 void Platform::DebugPrintf(const char *format
, ...) {
3205 va_start(pArguments
, format
);
3206 vsprintf(buffer
,format
,pArguments
);
3208 Platform::DebugDisplay(buffer
);
3211 void Platform::DebugPrintf(const char *, ...) {
3215 static bool assertionPopUps
= true;
3217 bool Platform::ShowAssertionPopUps(bool assertionPopUps_
) {
3218 bool ret
= assertionPopUps
;
3219 assertionPopUps
= assertionPopUps_
;
3223 void Platform::Assert(const char *c
, const char *file
, int line
) {
3225 sprintf(buffer
, "Assertion [%s] failed at %s %d%s", c
, file
, line
, assertionPopUps
? "" : "\r\n");
3226 if (assertionPopUps
) {
3227 int idButton
= ::MessageBoxA(0, buffer
, "Assertion failure",
3228 MB_ABORTRETRYIGNORE
|MB_ICONHAND
|MB_SETFOREGROUND
|MB_TASKMODAL
);
3229 if (idButton
== IDRETRY
) {
3231 } else if (idButton
== IDIGNORE
) {
3237 Platform::DebugDisplay(buffer
);
3243 int Platform::Clamp(int val
, int minVal
, int maxVal
) {
3252 // GetVersionEx has been deprecated fro Windows 8.1 but called here to determine if Windows 9x.
3253 // Too dangerous to find alternate check.
3254 #pragma warning(disable: 4996)
3257 void Platform_Initialise(void *hInstance
) {
3258 OSVERSIONINFO osv
= {sizeof(OSVERSIONINFO
),0,0,0,0,TEXT("")};
3259 ::GetVersionEx(&osv
);
3260 onNT
= osv
.dwPlatformId
== VER_PLATFORM_WIN32_NT
;
3261 ::InitializeCriticalSection(&crPlatformLock
);
3262 hinstPlatformRes
= reinterpret_cast<HINSTANCE
>(hInstance
);
3263 // This may be called from DllMain, in which case the call to LoadLibrary
3264 // is bad because it can upset the DLL load order.
3266 hDLLImage
= ::LoadLibrary(TEXT("Msimg32"));
3269 AlphaBlendFn
= (AlphaBlendSig
)::GetProcAddress(hDLLImage
, "AlphaBlend");
3272 hDLLUser32
= ::LoadLibrary(TEXT("User32"));
3275 MonitorFromPointFn
= (MonitorFromPointSig
)::GetProcAddress(hDLLUser32
, "MonitorFromPoint");
3276 MonitorFromRectFn
= (MonitorFromRectSig
)::GetProcAddress(hDLLUser32
, "MonitorFromRect");
3277 GetMonitorInfoFn
= (GetMonitorInfoSig
)::GetProcAddress(hDLLUser32
, "GetMonitorInfoA");
3280 ListBoxX_Register();
3284 #pragma warning(default: 4996)
3287 void Platform_Finalise(bool fromDllMain
) {
3288 #if defined(USE_D2D)
3290 if (defaultRenderingParams
) {
3291 defaultRenderingParams
->Release();
3292 defaultRenderingParams
= 0;
3294 if (customClearTypeRenderingParams
) {
3295 customClearTypeRenderingParams
->Release();
3296 customClearTypeRenderingParams
= 0;
3298 if (pIDWriteFactory
) {
3299 pIDWriteFactory
->Release();
3300 pIDWriteFactory
= 0;
3303 pD2DFactory
->Release();
3307 FreeLibrary(hDLLDWrite
);
3311 FreeLibrary(hDLLD2D
);
3316 if (reverseArrowCursor
!= NULL
)
3317 ::DestroyCursor(reverseArrowCursor
);
3318 ListBoxX_Unregister();
3319 ::DeleteCriticalSection(&crPlatformLock
);
3321 FreeLibrary(hDLLUser32
);
3325 FreeLibrary(hDLLImage
);
3330 #ifdef SCI_NAMESPACE