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 "UniConversion.h"
41 #include "FontQuality.h"
44 #define IDC_HAND MAKEINTRESOURCE(32649)
47 #ifndef SPI_GETFONTSMOOTHINGCONTRAST
48 #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
51 // Take care of 32/64 bit pointers
52 #ifdef GetWindowLongPtr
53 static void *PointerFromWindow(HWND hWnd
) {
54 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 static void *PointerFromWindow(HWND hWnd
) {
61 return reinterpret_cast<void *>(::GetWindowLong(hWnd
, 0));
63 static void SetWindowPointer(HWND hWnd
, void *ptr
) {
64 ::SetWindowLong(hWnd
, 0, reinterpret_cast<LONG
>(ptr
));
68 #define GWLP_USERDATA GWL_USERDATA
72 #define GWLP_WNDPROC GWL_WNDPROC
79 static LONG_PTR
SetWindowLongPtr(HWND hWnd
, int nIndex
, LONG_PTR dwNewLong
) {
80 return ::SetWindowLong(hWnd
, nIndex
, dwNewLong
);
83 static LONG_PTR
GetWindowLongPtr(HWND hWnd
, int nIndex
) {
84 return ::GetWindowLong(hWnd
, nIndex
);
88 extern UINT
CodePageFromCharSet(DWORD characterSet
, UINT documentCodePage
);
90 // Declarations needed for functions dynamically loaded as not available on all Windows versions.
91 typedef BOOL (WINAPI
*AlphaBlendSig
)(HDC
, int, int, int, int, HDC
, int, int, int, int, BLENDFUNCTION
);
92 typedef HMONITOR (WINAPI
*MonitorFromPointSig
)(POINT
, DWORD
);
93 typedef HMONITOR (WINAPI
*MonitorFromRectSig
)(LPCRECT
, DWORD
);
94 typedef BOOL (WINAPI
*GetMonitorInfoSig
)(HMONITOR
, LPMONITORINFO
);
96 static CRITICAL_SECTION crPlatformLock
;
97 static HINSTANCE hinstPlatformRes
= 0;
98 static bool onNT
= false;
100 static HMODULE hDLLImage
= 0;
101 static AlphaBlendSig AlphaBlendFn
= 0;
103 static HMODULE hDLLUser32
= 0;
104 static HMONITOR (WINAPI
*MonitorFromPointFn
)(POINT
, DWORD
) = 0;
105 static HMONITOR (WINAPI
*MonitorFromRectFn
)(LPCRECT
, DWORD
) = 0;
106 static BOOL (WINAPI
*GetMonitorInfoFn
)(HMONITOR
, LPMONITORINFO
) = 0;
108 static HCURSOR reverseArrowCursor
= NULL
;
111 namespace Scintilla
{
118 Point
Point::FromLong(long lpoint
) {
119 return Point(static_cast<short>(LOWORD(lpoint
)), static_cast<short>(HIWORD(lpoint
)));
122 static RECT
RectFromPRectangle(PRectangle prc
) {
123 RECT rc
= {static_cast<LONG
>(prc
.left
), static_cast<LONG
>(prc
.top
),
124 static_cast<LONG
>(prc
.right
), static_cast<LONG
>(prc
.bottom
)};
129 IDWriteFactory
*pIDWriteFactory
= 0;
130 ID2D1Factory
*pD2DFactory
= 0;
131 IDWriteRenderingParams
*defaultRenderingParams
= 0;
132 IDWriteRenderingParams
*customClearTypeRenderingParams
= 0;
135 static bool triedLoadingD2D
= false;
136 if (!triedLoadingD2D
) {
137 typedef HRESULT (WINAPI
*D2D1CFSig
)(D2D1_FACTORY_TYPE factoryType
, REFIID riid
,
138 CONST D2D1_FACTORY_OPTIONS
*pFactoryOptions
, IUnknown
**factory
);
139 typedef HRESULT (WINAPI
*DWriteCFSig
)(DWRITE_FACTORY_TYPE factoryType
, REFIID iid
,
142 HMODULE hDLLD2D
= ::LoadLibraryEx(TEXT("D2D1.DLL"), 0, 0x00000800 /*LOAD_LIBRARY_SEARCH_SYSTEM32*/);
144 D2D1CFSig fnD2DCF
= (D2D1CFSig
)::GetProcAddress(hDLLD2D
, "D2D1CreateFactory");
146 // A single threaded factory as Scintilla always draw on the GUI thread
147 fnD2DCF(D2D1_FACTORY_TYPE_SINGLE_THREADED
,
148 __uuidof(ID2D1Factory
),
150 reinterpret_cast<IUnknown
**>(&pD2DFactory
));
153 HMODULE hDLLDWrite
= ::LoadLibraryEx(TEXT("DWRITE.DLL"), 0, 0x00000800 /*LOAD_LIBRARY_SEARCH_SYSTEM32*/);
155 DWriteCFSig fnDWCF
= (DWriteCFSig
)::GetProcAddress(hDLLDWrite
, "DWriteCreateFactory");
157 fnDWCF(DWRITE_FACTORY_TYPE_SHARED
,
158 __uuidof(IDWriteFactory
),
159 reinterpret_cast<IUnknown
**>(&pIDWriteFactory
));
163 if (pIDWriteFactory
) {
164 HRESULT hr
= pIDWriteFactory
->CreateRenderingParams(&defaultRenderingParams
);
166 unsigned int clearTypeContrast
;
167 ::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST
, 0, &clearTypeContrast
, 0);
170 if (clearTypeContrast
>= 1000 && clearTypeContrast
<= 2200)
171 gamma
= static_cast<FLOAT
>(clearTypeContrast
) / 1000.0f
;
173 gamma
= defaultRenderingParams
->GetGamma();
175 pIDWriteFactory
->CreateCustomRenderingParams(gamma
, defaultRenderingParams
->GetEnhancedContrast(), defaultRenderingParams
->GetClearTypeLevel(),
176 defaultRenderingParams
->GetPixelGeometry(), defaultRenderingParams
->GetRenderingMode(), &customClearTypeRenderingParams
);
181 triedLoadingD2D
= true;
182 return pIDWriteFactory
&& pD2DFactory
;
186 struct FormatAndMetrics
{
190 IDWriteTextFormat
*pTextFormat
;
196 FLOAT yInternalLeading
;
197 FormatAndMetrics(HFONT hfont_
, int extraFontFlag_
, int characterSet_
) :
198 technology(SCWIN_TECH_GDI
), hfont(hfont_
),
202 extraFontFlag(extraFontFlag_
), characterSet(characterSet_
), yAscent(2), yDescent(1), yInternalLeading(0) {
205 FormatAndMetrics(IDWriteTextFormat
*pTextFormat_
,
210 FLOAT yInternalLeading_
) :
211 technology(SCWIN_TECH_DIRECTWRITE
),
213 pTextFormat(pTextFormat_
),
214 extraFontFlag(extraFontFlag_
),
215 characterSet(characterSet_
),
218 yInternalLeading(yInternalLeading_
) {
221 ~FormatAndMetrics() {
223 ::DeleteObject(hfont
);
226 pTextFormat
->Release();
233 yInternalLeading
= 0;
238 HFONT
FormatAndMetrics::HFont() {
241 if (technology
== SCWIN_TECH_GDI
) {
242 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
246 HRESULT hr
= pTextFormat
->GetFontFamilyName(lf
.lfFaceName
, LF_FACESIZE
);
247 if (!SUCCEEDED(hr
)) {
250 lf
.lfWeight
= pTextFormat
->GetFontWeight();
251 lf
.lfItalic
= pTextFormat
->GetFontStyle() == DWRITE_FONT_STYLE_ITALIC
;
252 lf
.lfHeight
= -static_cast<int>(pTextFormat
->GetFontSize());
255 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
259 return ::CreateFontIndirectW(&lf
);
262 #ifndef CLEARTYPE_QUALITY
263 #define CLEARTYPE_QUALITY 5
266 static BYTE
Win32MapFontQuality(int extraFontFlag
) {
267 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
269 case SC_EFF_QUALITY_NON_ANTIALIASED
:
270 return NONANTIALIASED_QUALITY
;
272 case SC_EFF_QUALITY_ANTIALIASED
:
273 return ANTIALIASED_QUALITY
;
275 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
276 return CLEARTYPE_QUALITY
;
279 return SC_EFF_QUALITY_DEFAULT
;
284 static D2D1_TEXT_ANTIALIAS_MODE
DWriteMapFontQuality(int extraFontFlag
) {
285 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
287 case SC_EFF_QUALITY_NON_ANTIALIASED
:
288 return D2D1_TEXT_ANTIALIAS_MODE_ALIASED
;
290 case SC_EFF_QUALITY_ANTIALIASED
:
291 return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE
;
293 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
294 return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
;
297 return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
;
302 static void SetLogFont(LOGFONTA
&lf
, const char *faceName
, int characterSet
, float size
, int weight
, bool italic
, int extraFontFlag
) {
303 memset(&lf
, 0, sizeof(lf
));
304 // The negative is to allow for leading
305 lf
.lfHeight
= -(abs(static_cast<int>(size
+ 0.5)));
306 lf
.lfWeight
= weight
;
307 lf
.lfItalic
= static_cast<BYTE
>(italic
? 1 : 0);
308 lf
.lfCharSet
= static_cast<BYTE
>(characterSet
);
309 lf
.lfQuality
= Win32MapFontQuality(extraFontFlag
);
310 strncpy(lf
.lfFaceName
, faceName
, sizeof(lf
.lfFaceName
));
311 lf
.lfFaceName
[sizeof(lf
.lfFaceName
)-1] = '\0';
315 * Create a hash from the parameters for a font to allow easy checking for identity.
316 * If one font is the same as another, its hash will be the same, but if the hash is the
317 * same then they may still be different.
319 static int HashFont(const FontParameters
&fp
) {
321 static_cast<int>(fp
.size
) ^
322 (fp
.characterSet
<< 10) ^
323 ((fp
.extraFontFlag
& SC_EFF_QUALITY_MASK
) << 9) ^
324 ((fp
.weight
/100) << 12) ^
325 (fp
.italic
? 0x20000000 : 0) ^
326 (fp
.technology
<< 15) ^
330 class FontCached
: Font
{
337 FontCached(const FontParameters
&fp
);
339 bool SameAs(const FontParameters
&fp
);
340 virtual void Release();
342 static FontCached
*first
;
344 static FontID
FindOrCreate(const FontParameters
&fp
);
345 static void ReleaseId(FontID fid_
);
348 FontCached
*FontCached::first
= 0;
350 FontCached::FontCached(const FontParameters
&fp
) :
351 next(0), usage(0), size(1.0), hash(0) {
352 SetLogFont(lf
, fp
.faceName
, fp
.characterSet
, fp
.size
, fp
.weight
, fp
.italic
, fp
.extraFontFlag
);
353 technology
= fp
.technology
;
356 if (technology
== SCWIN_TECH_GDI
) {
357 HFONT hfont
= ::CreateFontIndirectA(&lf
);
358 fid
= reinterpret_cast<void *>(new FormatAndMetrics(hfont
, fp
.extraFontFlag
, fp
.characterSet
));
361 IDWriteTextFormat
*pTextFormat
;
362 const int faceSize
= 200;
363 WCHAR wszFace
[faceSize
];
364 UTF16FromUTF8(fp
.faceName
, static_cast<unsigned int>(strlen(fp
.faceName
))+1, wszFace
, faceSize
);
365 FLOAT fHeight
= fp
.size
;
366 DWRITE_FONT_STYLE style
= fp
.italic
? DWRITE_FONT_STYLE_ITALIC
: DWRITE_FONT_STYLE_NORMAL
;
367 HRESULT hr
= pIDWriteFactory
->CreateTextFormat(wszFace
, NULL
,
368 static_cast<DWRITE_FONT_WEIGHT
>(fp
.weight
),
370 DWRITE_FONT_STRETCH_NORMAL
, fHeight
, L
"en-us", &pTextFormat
);
372 pTextFormat
->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP
);
374 const int maxLines
= 2;
375 DWRITE_LINE_METRICS lineMetrics
[maxLines
];
376 UINT32 lineCount
= 0;
377 FLOAT yAscent
= 1.0f
;
378 FLOAT yDescent
= 1.0f
;
379 FLOAT yInternalLeading
= 0.0f
;
380 IDWriteTextLayout
*pTextLayout
= 0;
381 hr
= pIDWriteFactory
->CreateTextLayout(L
"X", 1, pTextFormat
,
382 100.0f
, 100.0f
, &pTextLayout
);
384 hr
= pTextLayout
->GetLineMetrics(lineMetrics
, maxLines
, &lineCount
);
386 yAscent
= lineMetrics
[0].baseline
;
387 yDescent
= lineMetrics
[0].height
- lineMetrics
[0].baseline
;
390 hr
= pTextLayout
->GetFontSize(0, &emHeight
);
392 yInternalLeading
= lineMetrics
[0].height
- emHeight
;
395 pTextLayout
->Release();
396 pTextFormat
->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM
, lineMetrics
[0].height
, lineMetrics
[0].baseline
);
398 fid
= reinterpret_cast<void *>(new FormatAndMetrics(pTextFormat
, fp
.extraFontFlag
, fp
.characterSet
, yAscent
, yDescent
, yInternalLeading
));
405 bool FontCached::SameAs(const FontParameters
&fp
) {
408 (lf
.lfWeight
== fp
.weight
) &&
409 (lf
.lfItalic
== static_cast<BYTE
>(fp
.italic
? 1 : 0)) &&
410 (lf
.lfCharSet
== fp
.characterSet
) &&
411 (lf
.lfQuality
== Win32MapFontQuality(fp
.extraFontFlag
)) &&
412 (technology
== fp
.technology
) &&
413 0 == strcmp(lf
.lfFaceName
,fp
.faceName
);
416 void FontCached::Release() {
417 delete reinterpret_cast<FormatAndMetrics
*>(fid
);
421 FontID
FontCached::FindOrCreate(const FontParameters
&fp
) {
423 ::EnterCriticalSection(&crPlatformLock
);
424 int hashFind
= HashFont(fp
);
425 for (FontCached
*cur
=first
; cur
; cur
=cur
->next
) {
426 if ((cur
->hash
== hashFind
) &&
433 FontCached
*fc
= new FontCached(fp
);
438 ::LeaveCriticalSection(&crPlatformLock
);
442 void FontCached::ReleaseId(FontID fid_
) {
443 ::EnterCriticalSection(&crPlatformLock
);
444 FontCached
**pcur
=&first
;
445 for (FontCached
*cur
=first
; cur
; cur
=cur
->next
) {
446 if (cur
->fid
== fid_
) {
448 if (cur
->usage
== 0) {
458 ::LeaveCriticalSection(&crPlatformLock
);
470 void Font::Create(const FontParameters
&fp
) {
473 fid
= FontCached::FindOrCreate(fp
);
476 void Font::Release() {
478 FontCached::ReleaseId(fid
);
482 // Buffer to hold strings and string position arrays without always allocating on heap.
483 // May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer
484 // when less than safe size otherwise allocate on heap and free automatically.
485 template<typename T
, int lengthStandard
>
487 T bufferStandard
[lengthStandard
];
488 // Private so VarBuffer objects can not be copied
489 VarBuffer(const VarBuffer
&);
490 VarBuffer
&operator=(const VarBuffer
&);
493 VarBuffer(size_t length
) : buffer(0) {
494 if (length
> lengthStandard
) {
495 buffer
= new T
[length
];
497 buffer
= bufferStandard
;
501 if (buffer
!= bufferStandard
) {
508 const int stackBufferLength
= 10000;
509 class TextWide
: public VarBuffer
<wchar_t, stackBufferLength
> {
512 TextWide(const char *s
, int len
, bool unicodeMode
, int codePage
=0) :
513 VarBuffer
<wchar_t, stackBufferLength
>(len
) {
515 tlen
= UTF16FromUTF8(s
, len
, buffer
, len
);
517 // Support Asian string display in 9x English
518 tlen
= ::MultiByteToWideChar(codePage
, 0, s
, len
, buffer
, len
);
522 typedef VarBuffer
<XYPOSITION
, stackBufferLength
> TextPositions
;
524 class SurfaceGDI
: public Surface
{
540 // If 9x OS and current code page is same as ANSI code page.
543 void BrushColor(ColourDesired back
);
544 void SetFont(Font
&font_
);
546 // Private so SurfaceGDI objects can not be copied
547 SurfaceGDI(const SurfaceGDI
&);
548 SurfaceGDI
&operator=(const SurfaceGDI
&);
551 virtual ~SurfaceGDI();
553 void Init(WindowID wid
);
554 void Init(SurfaceID sid
, WindowID wid
);
555 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
559 void PenColour(ColourDesired fore
);
561 int DeviceHeightFont(int points
);
562 void MoveTo(int x_
, int y_
);
563 void LineTo(int x_
, int y_
);
564 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
565 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
566 void FillRectangle(PRectangle rc
, ColourDesired back
);
567 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
568 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
569 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
570 ColourDesired outline
, int alphaOutline
, int flags
);
571 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
572 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
573 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
575 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
576 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
577 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
578 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
579 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
580 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
581 XYPOSITION
WidthChar(Font
&font_
, char ch
);
582 XYPOSITION
Ascent(Font
&font_
);
583 XYPOSITION
Descent(Font
&font_
);
584 XYPOSITION
InternalLeading(Font
&font_
);
585 XYPOSITION
ExternalLeading(Font
&font_
);
586 XYPOSITION
Height(Font
&font_
);
587 XYPOSITION
AverageCharWidth(Font
&font_
);
589 void SetClip(PRectangle rc
);
590 void FlushCachedState();
592 void SetUnicodeMode(bool unicodeMode_
);
593 void SetDBCSMode(int codePage_
);
596 SurfaceGDI::SurfaceGDI() :
598 hdc(0), hdcOwned(false),
600 brush(0), brushOld(0),
602 bitmap(0), bitmapOld(0) {
603 // Windows 9x has only a 16 bit coordinate system so break after 30000 pixels
604 maxWidthMeasure
= IsNT() ? INT_MAX
: 30000;
605 // There appears to be a 16 bit string length limit in GDI on NT and a limit of
606 // 8192 characters on Windows 95.
607 maxLenText
= IsNT() ? 65535 : 8192;
610 win9xACPSame
= false;
613 SurfaceGDI::~SurfaceGDI() {
617 void SurfaceGDI::Release() {
619 ::SelectObject(reinterpret_cast<HDC
>(hdc
), penOld
);
625 ::SelectObject(reinterpret_cast<HDC
>(hdc
), brushOld
);
626 ::DeleteObject(brush
);
631 // Fonts are not deleted as they are owned by a Font object
632 ::SelectObject(reinterpret_cast<HDC
>(hdc
), fontOld
);
637 ::SelectObject(reinterpret_cast<HDC
>(hdc
), bitmapOld
);
638 ::DeleteObject(bitmap
);
643 ::DeleteDC(reinterpret_cast<HDC
>(hdc
));
649 bool SurfaceGDI::Initialised() {
653 void SurfaceGDI::Init(WindowID
) {
655 hdc
= ::CreateCompatibleDC(NULL
);
657 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
660 void SurfaceGDI::Init(SurfaceID sid
, WindowID
) {
662 hdc
= reinterpret_cast<HDC
>(sid
);
663 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
666 void SurfaceGDI::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
668 hdc
= ::CreateCompatibleDC(static_cast<SurfaceGDI
*>(surface_
)->hdc
);
670 bitmap
= ::CreateCompatibleBitmap(static_cast<SurfaceGDI
*>(surface_
)->hdc
, width
, height
);
671 bitmapOld
= static_cast<HBITMAP
>(::SelectObject(hdc
, bitmap
));
672 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
675 void SurfaceGDI::PenColour(ColourDesired fore
) {
677 ::SelectObject(hdc
, penOld
);
682 pen
= ::CreatePen(0,1,fore
.AsLong());
683 penOld
= static_cast<HPEN
>(::SelectObject(reinterpret_cast<HDC
>(hdc
), pen
));
686 void SurfaceGDI::BrushColor(ColourDesired back
) {
688 ::SelectObject(hdc
, brushOld
);
689 ::DeleteObject(brush
);
693 // Only ever want pure, non-dithered brushes
694 ColourDesired colourNearest
= ::GetNearestColor(hdc
, back
.AsLong());
695 brush
= ::CreateSolidBrush(colourNearest
.AsLong());
696 brushOld
= static_cast<HBRUSH
>(::SelectObject(hdc
, brush
));
699 void SurfaceGDI::SetFont(Font
&font_
) {
700 if (font_
.GetID() != font
) {
701 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
702 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_GDI
);
704 ::SelectObject(hdc
, pfm
->hfont
);
706 fontOld
= static_cast<HFONT
>(::SelectObject(hdc
, pfm
->hfont
));
708 font
= reinterpret_cast<HFONT
>(pfm
->hfont
);
712 int SurfaceGDI::LogPixelsY() {
713 return ::GetDeviceCaps(hdc
, LOGPIXELSY
);
716 int SurfaceGDI::DeviceHeightFont(int points
) {
717 return ::MulDiv(points
, LogPixelsY(), 72);
720 void SurfaceGDI::MoveTo(int x_
, int y_
) {
721 ::MoveToEx(hdc
, x_
, y_
, 0);
724 void SurfaceGDI::LineTo(int x_
, int y_
) {
725 ::LineTo(hdc
, x_
, y_
);
728 void SurfaceGDI::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
731 std::vector
<POINT
> outline
;
732 for (int i
=0;i
<npts
;i
++) {
733 POINT pt
= {static_cast<LONG
>(pts
[i
].x
), static_cast<LONG
>(pts
[i
].y
)};
734 outline
.push_back(pt
);
736 ::Polygon(hdc
, &outline
[0], npts
);
739 void SurfaceGDI::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
742 ::Rectangle(hdc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
745 void SurfaceGDI::FillRectangle(PRectangle rc
, ColourDesired back
) {
746 // Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
747 // There is no need to allocate a brush either.
748 RECT rcw
= RectFromPRectangle(rc
);
749 ::SetBkColor(hdc
, back
.AsLong());
750 ::ExtTextOut(hdc
, rc
.left
, rc
.top
, ETO_OPAQUE
, &rcw
, TEXT(""), 0, NULL
);
753 void SurfaceGDI::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
755 if (static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
)
756 br
= ::CreatePatternBrush(static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
);
757 else // Something is wrong so display in red
758 br
= ::CreateSolidBrush(RGB(0xff, 0, 0));
759 RECT rcw
= RectFromPRectangle(rc
);
760 ::FillRect(hdc
, &rcw
, br
);
764 void SurfaceGDI::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
769 rc
.right
- 1, rc
.bottom
,
773 // Plot a point into a DWORD buffer symetrically to all 4 qudrants
774 static void AllFour(DWORD
*pixels
, int width
, int height
, int x
, int y
, DWORD val
) {
775 pixels
[y
*width
+x
] = val
;
776 pixels
[y
*width
+width
-1-x
] = val
;
777 pixels
[(height
-1-y
)*width
+x
] = val
;
778 pixels
[(height
-1-y
)*width
+width
-1-x
] = val
;
782 #define AC_SRC_OVER 0x00
785 #define AC_SRC_ALPHA 0x01
788 static DWORD
dwordFromBGRA(byte b
, byte g
, byte r
, byte a
) {
793 converter
.pixVal
[0] = b
;
794 converter
.pixVal
[1] = g
;
795 converter
.pixVal
[2] = r
;
796 converter
.pixVal
[3] = a
;
797 return converter
.val
;
800 void SurfaceGDI::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
801 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
802 if (AlphaBlendFn
&& rc
.Width() > 0) {
803 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
804 int width
= rc
.Width();
805 int height
= rc
.Height();
806 // Ensure not distorted too much by corners when small
807 cornerSize
= Platform::Minimum(cornerSize
, (Platform::Minimum(width
, height
) / 2) - 2);
808 BITMAPINFO bpih
= {{sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0}};
810 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
811 DIB_RGB_COLORS
, &image
, NULL
, 0);
814 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
816 DWORD valEmpty
= dwordFromBGRA(0,0,0,0);
817 DWORD valFill
= dwordFromBGRA(
818 static_cast<byte
>(GetBValue(fill
.AsLong()) * alphaFill
/ 255),
819 static_cast<byte
>(GetGValue(fill
.AsLong()) * alphaFill
/ 255),
820 static_cast<byte
>(GetRValue(fill
.AsLong()) * alphaFill
/ 255),
821 static_cast<byte
>(alphaFill
));
822 DWORD valOutline
= dwordFromBGRA(
823 static_cast<byte
>(GetBValue(outline
.AsLong()) * alphaOutline
/ 255),
824 static_cast<byte
>(GetGValue(outline
.AsLong()) * alphaOutline
/ 255),
825 static_cast<byte
>(GetRValue(outline
.AsLong()) * alphaOutline
/ 255),
826 static_cast<byte
>(alphaOutline
));
827 DWORD
*pixels
= reinterpret_cast<DWORD
*>(image
);
828 for (int y
=0; y
<height
; y
++) {
829 for (int x
=0; x
<width
; x
++) {
830 if ((x
==0) || (x
==width
-1) || (y
== 0) || (y
== height
-1)) {
831 pixels
[y
*width
+x
] = valOutline
;
833 pixels
[y
*width
+x
] = valFill
;
837 for (int c
=0;c
<cornerSize
; c
++) {
838 for (int x
=0;x
<c
+1; x
++) {
839 AllFour(pixels
, width
, height
, x
, c
-x
, valEmpty
);
842 for (int x
=1;x
<cornerSize
; x
++) {
843 AllFour(pixels
, width
, height
, x
, cornerSize
-x
, valOutline
);
846 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
848 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), rc
.left
, rc
.top
, width
, height
, hMemDC
, 0, 0, width
, height
, merge
);
850 SelectBitmap(hMemDC
, hbmOld
);
851 ::DeleteObject(hbmMem
);
856 RECT rcw
= RectFromPRectangle(rc
);
857 FrameRect(hdc
, &rcw
, brush
);
861 void SurfaceGDI::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
862 if (AlphaBlendFn
&& rc
.Width() > 0) {
863 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
864 if (rc
.Width() > width
)
865 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
866 rc
.right
= rc
.left
+ width
;
867 if (rc
.Height() > height
)
868 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
869 rc
.bottom
= rc
.top
+ height
;
871 BITMAPINFO bpih
= {{sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0}};
872 unsigned char *image
= 0;
873 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
874 DIB_RGB_COLORS
, reinterpret_cast<void **>(&image
), NULL
, 0);
876 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
878 for (int y
=height
-1; y
>=0; y
--) {
879 for (int x
=0; x
<width
; x
++) {
880 unsigned char *pixel
= image
+ (y
*width
+x
) * 4;
881 unsigned char alpha
= pixelsImage
[3];
882 // Input is RGBA, output is BGRA with premultiplied alpha
883 pixel
[2] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
884 pixel
[1] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
885 pixel
[0] = static_cast<unsigned char>((*pixelsImage
++) * alpha
/ 255);
886 pixel
[3] = static_cast<unsigned char>(*pixelsImage
++);
890 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
892 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), rc
.left
, rc
.top
, rc
.Width(), rc
.Height(), hMemDC
, 0, 0, width
, height
, merge
);
894 SelectBitmap(hMemDC
, hbmOld
);
895 ::DeleteObject(hbmMem
);
902 void SurfaceGDI::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
905 ::Ellipse(hdc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
908 void SurfaceGDI::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
910 rc
.left
, rc
.top
, rc
.Width(), rc
.Height(),
911 static_cast<SurfaceGDI
&>(surfaceSource
).hdc
, from
.x
, from
.y
, SRCCOPY
);
914 typedef VarBuffer
<int, stackBufferLength
> TextPositionsI
;
916 void SurfaceGDI::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
) {
918 RECT rcw
= RectFromPRectangle(rc
);
923 // Text drawing may fail if the text is too big.
924 // If it does fail, slice up into segments and draw each segment.
925 const int maxSegmentLength
= 0x200;
927 if ((!unicodeMode
) && (IsNT() || (codePage
==0) || win9xACPSame
)) {
929 int lenDraw
= Platform::Minimum(len
, maxLenText
);
930 if (!::ExtTextOutA(hdc
, x
, ybase
, fuOptions
, &rcw
, s
, lenDraw
, NULL
)) {
931 while (lenDraw
> pos
) {
932 int seglen
= Platform::Minimum(maxSegmentLength
, lenDraw
- pos
);
933 if (!::ExtTextOutA(hdc
, x
, ybase
, fuOptions
, &rcw
, s
+pos
, seglen
, NULL
)) {
934 PLATFORM_ASSERT(false);
937 ::GetTextExtentPoint32A(hdc
, s
+pos
, seglen
, &sz
);
944 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
945 if (!::ExtTextOutW(hdc
, x
, ybase
, fuOptions
, &rcw
, tbuf
.buffer
, tbuf
.tlen
, NULL
)) {
946 while (tbuf
.tlen
> pos
) {
947 int seglen
= Platform::Minimum(maxSegmentLength
, tbuf
.tlen
- pos
);
948 if (!::ExtTextOutW(hdc
, x
, ybase
, fuOptions
, &rcw
, tbuf
.buffer
+pos
, seglen
, NULL
)) {
949 PLATFORM_ASSERT(false);
952 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
+pos
, seglen
, &sz
);
960 void SurfaceGDI::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
961 ColourDesired fore
, ColourDesired back
) {
962 ::SetTextColor(hdc
, fore
.AsLong());
963 ::SetBkColor(hdc
, back
.AsLong());
964 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
967 void SurfaceGDI::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
968 ColourDesired fore
, ColourDesired back
) {
969 ::SetTextColor(hdc
, fore
.AsLong());
970 ::SetBkColor(hdc
, back
.AsLong());
971 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
974 void SurfaceGDI::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
975 ColourDesired fore
) {
976 // Avoid drawing spaces in transparent mode
977 for (int i
=0;i
<len
;i
++) {
979 ::SetTextColor(hdc
, fore
.AsLong());
980 ::SetBkMode(hdc
, TRANSPARENT
);
981 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
982 ::SetBkMode(hdc
, OPAQUE
);
988 XYPOSITION
SurfaceGDI::WidthText(Font
&font_
, const char *s
, int len
) {
991 if ((!unicodeMode
) && (IsNT() || (codePage
==0) || win9xACPSame
)) {
992 ::GetTextExtentPoint32A(hdc
, s
, Platform::Minimum(len
, maxLenText
), &sz
);
994 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
995 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &sz
);
1000 void SurfaceGDI::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
1005 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
1006 TextPositionsI
poses(tbuf
.tlen
);
1008 if (!::GetTextExtentExPointW(hdc
, tbuf
.buffer
, tbuf
.tlen
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
1009 // Likely to have failed because on Windows 9x where function not available
1010 // So measure the character widths by measuring each initial substring
1011 // Turns a linear operation into a qudratic but seems fast enough on test files
1012 for (int widthSS
=0; widthSS
< tbuf
.tlen
; widthSS
++) {
1013 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, widthSS
+1, &sz
);
1014 poses
.buffer
[widthSS
] = sz
.cx
;
1017 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
1019 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1022 unsigned char uch
= us
[i
];
1023 unsigned int lenChar
= 1;
1024 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
1027 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
1029 } else if (uch
>= (0x80)) {
1032 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
1033 positions
[i
++] = poses
.buffer
[ui
];
1039 lastPos
= positions
[i
-1];
1041 positions
[i
++] = lastPos
;
1043 } else if (IsNT() || (codePage
==0) || win9xACPSame
) {
1044 // Zero positions to avoid random behaviour on failure.
1045 memset(positions
, 0, len
* sizeof(*positions
));
1046 // len may be larger than platform supports so loop over segments small enough for platform
1047 int startOffset
= 0;
1049 int lenBlock
= Platform::Minimum(len
, maxLenText
);
1050 TextPositionsI
poses(len
);
1051 if (!::GetTextExtentExPointA(hdc
, s
, lenBlock
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
1052 // Eeek - a NULL DC or other foolishness could cause this.
1054 } else if (fit
< lenBlock
) {
1055 // For some reason, such as an incomplete DBCS character
1056 // Not all the positions are filled in so make them equal to end.
1058 poses
.buffer
[fit
++] = 0;
1059 for (int i
= fit
;i
<lenBlock
;i
++)
1060 poses
.buffer
[i
] = poses
.buffer
[fit
-1];
1062 for (int i
=0;i
<lenBlock
;i
++)
1063 positions
[i
] = poses
.buffer
[i
] + startOffset
;
1064 startOffset
= poses
.buffer
[lenBlock
-1];
1066 positions
+= lenBlock
;
1070 // Support Asian string display in 9x English
1071 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
1072 TextPositionsI
poses(tbuf
.tlen
);
1073 for (int widthSS
=0; widthSS
<tbuf
.tlen
; widthSS
++) {
1074 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, widthSS
+1, &sz
);
1075 poses
.buffer
[widthSS
] = sz
.cx
;
1079 for (int i
=0;i
<len
;) {
1080 if (Platform::IsDBCSLeadByte(codePage
, s
[i
])) {
1081 positions
[i
] = poses
.buffer
[ui
];
1082 positions
[i
+1] = poses
.buffer
[ui
];
1085 positions
[i
] = poses
.buffer
[ui
];
1094 XYPOSITION
SurfaceGDI::WidthChar(Font
&font_
, char ch
) {
1097 ::GetTextExtentPoint32A(hdc
, &ch
, 1, &sz
);
1101 XYPOSITION
SurfaceGDI::Ascent(Font
&font_
) {
1104 ::GetTextMetrics(hdc
, &tm
);
1108 XYPOSITION
SurfaceGDI::Descent(Font
&font_
) {
1111 ::GetTextMetrics(hdc
, &tm
);
1112 return tm
.tmDescent
;
1115 XYPOSITION
SurfaceGDI::InternalLeading(Font
&font_
) {
1118 ::GetTextMetrics(hdc
, &tm
);
1119 return tm
.tmInternalLeading
;
1122 XYPOSITION
SurfaceGDI::ExternalLeading(Font
&font_
) {
1125 ::GetTextMetrics(hdc
, &tm
);
1126 return tm
.tmExternalLeading
;
1129 XYPOSITION
SurfaceGDI::Height(Font
&font_
) {
1132 ::GetTextMetrics(hdc
, &tm
);
1136 XYPOSITION
SurfaceGDI::AverageCharWidth(Font
&font_
) {
1139 ::GetTextMetrics(hdc
, &tm
);
1140 return tm
.tmAveCharWidth
;
1143 void SurfaceGDI::SetClip(PRectangle rc
) {
1144 ::IntersectClipRect(hdc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1147 void SurfaceGDI::FlushCachedState() {
1153 void SurfaceGDI::SetUnicodeMode(bool unicodeMode_
) {
1154 unicodeMode
=unicodeMode_
;
1157 void SurfaceGDI::SetDBCSMode(int codePage_
) {
1158 // No action on window as automatically handled by system.
1159 codePage
= codePage_
;
1160 win9xACPSame
= !IsNT() && ((unsigned int)codePage
== ::GetACP());
1163 #if defined(USE_D2D)
1165 class SurfaceD2D
: public Surface
{
1172 ID2D1RenderTarget
*pRenderTarget
;
1173 bool ownRenderTarget
;
1176 IDWriteTextFormat
*pTextFormat
;
1179 FLOAT yInternalLeading
;
1181 ID2D1SolidColorBrush
*pBrush
;
1187 void SetFont(Font
&font_
);
1189 // Private so SurfaceD2D objects can not be copied
1190 SurfaceD2D(const SurfaceD2D
&);
1191 SurfaceD2D
&operator=(const SurfaceD2D
&);
1194 virtual ~SurfaceD2D();
1197 void Init(WindowID wid
);
1198 void Init(SurfaceID sid
, WindowID wid
);
1199 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
1204 HRESULT
FlushDrawing();
1206 void PenColour(ColourDesired fore
);
1207 void D2DPenColour(ColourDesired fore
, int alpha
=255);
1209 int DeviceHeightFont(int points
);
1210 void MoveTo(int x_
, int y_
);
1211 void LineTo(int x_
, int y_
);
1212 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
1213 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1214 void FillRectangle(PRectangle rc
, ColourDesired back
);
1215 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
1216 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1217 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1218 ColourDesired outline
, int alphaOutline
, int flags
);
1219 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
1220 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1221 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
1223 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
1224 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1225 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1226 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
1227 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
1228 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
1229 XYPOSITION
WidthChar(Font
&font_
, char ch
);
1230 XYPOSITION
Ascent(Font
&font_
);
1231 XYPOSITION
Descent(Font
&font_
);
1232 XYPOSITION
InternalLeading(Font
&font_
);
1233 XYPOSITION
ExternalLeading(Font
&font_
);
1234 XYPOSITION
Height(Font
&font_
);
1235 XYPOSITION
AverageCharWidth(Font
&font_
);
1237 void SetClip(PRectangle rc
);
1238 void FlushCachedState();
1240 void SetUnicodeMode(bool unicodeMode_
);
1241 void SetDBCSMode(int codePage_
);
1244 SurfaceD2D::SurfaceD2D() :
1251 pRenderTarget
= NULL
;
1252 ownRenderTarget
= false;
1255 // From selected font
1259 yInternalLeading
= 0;
1268 SurfaceD2D::~SurfaceD2D() {
1272 void SurfaceD2D::Release() {
1277 if (pRenderTarget
) {
1278 while (clipsActive
) {
1279 pRenderTarget
->PopAxisAlignedClip();
1282 if (ownRenderTarget
) {
1283 pRenderTarget
->Release();
1289 void SurfaceD2D::SetScale() {
1290 HDC hdcMeasure
= ::CreateCompatibleDC(NULL
);
1291 logPixelsY
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSY
);
1292 dpiScaleX
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSX
) / 96.0f
;
1293 dpiScaleY
= logPixelsY
/ 96.0f
;
1294 ::DeleteDC(hdcMeasure
);
1297 bool SurfaceD2D::Initialised() {
1298 return pRenderTarget
!= 0;
1301 HRESULT
SurfaceD2D::FlushDrawing() {
1302 return pRenderTarget
->Flush();
1305 void SurfaceD2D::Init(WindowID
/* wid */) {
1310 void SurfaceD2D::Init(SurfaceID sid
, WindowID
) {
1313 pRenderTarget
= reinterpret_cast<ID2D1RenderTarget
*>(sid
);
1316 void SurfaceD2D::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
1319 SurfaceD2D
*psurfOther
= static_cast<SurfaceD2D
*>(surface_
);
1320 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= NULL
;
1321 D2D1_SIZE_F desiredSize
= D2D1::SizeF(width
, height
);
1322 D2D1_PIXEL_FORMAT desiredFormat
;
1324 desiredFormat
.format
= DXGI_FORMAT_UNKNOWN
;
1326 desiredFormat
= psurfOther
->pRenderTarget
->GetPixelFormat();
1328 desiredFormat
.alphaMode
= D2D1_ALPHA_MODE_IGNORE
;
1329 HRESULT hr
= psurfOther
->pRenderTarget
->CreateCompatibleRenderTarget(
1330 &desiredSize
, NULL
, &desiredFormat
, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE
, &pCompatibleRenderTarget
);
1331 if (SUCCEEDED(hr
)) {
1332 pRenderTarget
= pCompatibleRenderTarget
;
1333 pRenderTarget
->BeginDraw();
1334 ownRenderTarget
= true;
1338 void SurfaceD2D::PenColour(ColourDesired fore
) {
1342 void SurfaceD2D::D2DPenColour(ColourDesired fore
, int alpha
) {
1343 if (pRenderTarget
) {
1345 col
.r
= (fore
.AsLong() & 0xff) / 255.0;
1346 col
.g
= ((fore
.AsLong() & 0xff00) >> 8) / 255.0;
1347 col
.b
= (fore
.AsLong() >> 16) / 255.0;
1348 col
.a
= alpha
/ 255.0;
1350 pBrush
->SetColor(col
);
1352 HRESULT hr
= pRenderTarget
->CreateSolidColorBrush(col
, &pBrush
);
1353 if (!SUCCEEDED(hr
) && pBrush
) {
1361 void SurfaceD2D::SetFont(Font
&font_
) {
1362 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
1363 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_DIRECTWRITE
);
1364 pTextFormat
= pfm
->pTextFormat
;
1365 yAscent
= pfm
->yAscent
;
1366 yDescent
= pfm
->yDescent
;
1367 yInternalLeading
= pfm
->yInternalLeading
;
1368 codePageText
= codePage
;
1369 if (pfm
->characterSet
) {
1370 codePageText
= CodePageFromCharSet(pfm
->characterSet
, codePage
);
1372 if (pRenderTarget
) {
1373 D2D1_TEXT_ANTIALIAS_MODE aaMode
;
1374 aaMode
= DWriteMapFontQuality(pfm
->extraFontFlag
);
1376 if (aaMode
== D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
&& customClearTypeRenderingParams
)
1377 pRenderTarget
->SetTextRenderingParams(customClearTypeRenderingParams
);
1378 else if (defaultRenderingParams
)
1379 pRenderTarget
->SetTextRenderingParams(defaultRenderingParams
);
1381 pRenderTarget
->SetTextAntialiasMode(aaMode
);
1385 int SurfaceD2D::LogPixelsY() {
1389 int SurfaceD2D::DeviceHeightFont(int points
) {
1390 return ::MulDiv(points
, LogPixelsY(), 72);
1393 void SurfaceD2D::MoveTo(int x_
, int y_
) {
1398 static int Delta(int difference
) {
1401 else if (difference
> 0)
1407 static int RoundFloat(float f
) {
1411 void SurfaceD2D::LineTo(int x_
, int y_
) {
1412 if (pRenderTarget
) {
1414 int xDelta
= Delta(xDiff
);
1416 int yDelta
= Delta(yDiff
);
1417 if ((xDiff
== 0) || (yDiff
== 0)) {
1418 // Horizontal or vertical lines can be more precisely drawn as a filled rectangle
1419 int xEnd
= x_
- xDelta
;
1420 int left
= Platform::Minimum(x
, xEnd
);
1421 int width
= abs(x
- xEnd
) + 1;
1422 int yEnd
= y_
- yDelta
;
1423 int top
= Platform::Minimum(y
, yEnd
);
1424 int height
= abs(y
- yEnd
) + 1;
1425 D2D1_RECT_F rectangle1
= D2D1::RectF(left
, top
, left
+width
, top
+height
);
1426 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1427 } else if ((abs(xDiff
) == abs(yDiff
))) {
1429 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5, y
+ 0.5),
1430 D2D1::Point2F(x_
+ 0.5 - xDelta
, y_
+ 0.5 - yDelta
), pBrush
);
1432 // Line has a different slope so difficult to avoid last pixel
1433 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5, y
+ 0.5),
1434 D2D1::Point2F(x_
+ 0.5, y_
+ 0.5), pBrush
);
1441 void SurfaceD2D::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
1442 if (pRenderTarget
) {
1443 ID2D1Factory
*pFactory
= 0;
1444 pRenderTarget
->GetFactory(&pFactory
);
1445 ID2D1PathGeometry
*geometry
=0;
1446 HRESULT hr
= pFactory
->CreatePathGeometry(&geometry
);
1447 if (SUCCEEDED(hr
)) {
1448 ID2D1GeometrySink
*sink
= 0;
1449 hr
= geometry
->Open(&sink
);
1450 if (SUCCEEDED(hr
)) {
1451 sink
->BeginFigure(D2D1::Point2F(pts
[0].x
+ 0.5f
, pts
[0].y
+ 0.5f
), D2D1_FIGURE_BEGIN_FILLED
);
1452 for (size_t i
=1; i
<static_cast<size_t>(npts
); i
++) {
1453 sink
->AddLine(D2D1::Point2F(pts
[i
].x
+ 0.5f
, pts
[i
].y
+ 0.5f
));
1455 sink
->EndFigure(D2D1_FIGURE_END_CLOSED
);
1460 pRenderTarget
->FillGeometry(geometry
,pBrush
);
1462 pRenderTarget
->DrawGeometry(geometry
,pBrush
);
1465 geometry
->Release();
1470 void SurfaceD2D::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1471 if (pRenderTarget
) {
1472 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5, rc
.top
+0.5, RoundFloat(rc
.right
) - 0.5, rc
.bottom
-0.5);
1474 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1476 pRenderTarget
->DrawRectangle(&rectangle1
, pBrush
);
1480 void SurfaceD2D::FillRectangle(PRectangle rc
, ColourDesired back
) {
1481 if (pRenderTarget
) {
1483 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
), rc
.top
, RoundFloat(rc
.right
), rc
.bottom
);
1484 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1488 void SurfaceD2D::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
1489 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfacePattern
);
1490 surfOther
.FlushDrawing();
1491 ID2D1Bitmap
*pBitmap
= NULL
;
1492 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1493 surfOther
.pRenderTarget
);
1494 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1495 if (SUCCEEDED(hr
)) {
1496 ID2D1BitmapBrush
*pBitmapBrush
= NULL
;
1497 D2D1_BITMAP_BRUSH_PROPERTIES brushProperties
=
1498 D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP
, D2D1_EXTEND_MODE_WRAP
,
1499 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
1500 // Create the bitmap brush.
1501 hr
= pRenderTarget
->CreateBitmapBrush(pBitmap
, brushProperties
, &pBitmapBrush
);
1503 if (SUCCEEDED(hr
)) {
1504 pRenderTarget
->FillRectangle(
1505 D2D1::RectF(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
),
1507 pBitmapBrush
->Release();
1512 void SurfaceD2D::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1513 if (pRenderTarget
) {
1514 D2D1_ROUNDED_RECT roundedRectFill
= {
1515 D2D1::RectF(rc
.left
+1.0, rc
.top
+1.0, rc
.right
-1.0, rc
.bottom
-1.0),
1518 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1520 D2D1_ROUNDED_RECT roundedRect
= {
1521 D2D1::RectF(rc
.left
+ 0.5, rc
.top
+0.5, rc
.right
- 0.5, rc
.bottom
-0.5),
1524 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1528 void SurfaceD2D::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1529 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
1530 if (pRenderTarget
) {
1531 if (cornerSize
== 0) {
1532 // When corner size is zero, draw square rectangle to prevent blurry pixels at corners
1533 D2D1_RECT_F rectFill
= D2D1::RectF(RoundFloat(rc
.left
) + 1.0, rc
.top
+ 1.0, RoundFloat(rc
.right
) - 1.0, rc
.bottom
- 1.0);
1534 D2DPenColour(fill
, alphaFill
);
1535 pRenderTarget
->FillRectangle(rectFill
, pBrush
);
1537 D2D1_RECT_F rectOutline
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5, rc
.top
+ 0.5, RoundFloat(rc
.right
) - 0.5, rc
.bottom
- 0.5);
1538 D2DPenColour(outline
, alphaOutline
);
1539 pRenderTarget
->DrawRectangle(rectOutline
, pBrush
);
1541 const float cornerSizeF
= static_cast<float>(cornerSize
);
1542 D2D1_ROUNDED_RECT roundedRectFill
= {
1543 D2D1::RectF(RoundFloat(rc
.left
) + 1.0, rc
.top
+ 1.0, RoundFloat(rc
.right
) - 1.0, rc
.bottom
- 1.0),
1544 cornerSizeF
, cornerSizeF
};
1545 D2DPenColour(fill
, alphaFill
);
1546 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1548 D2D1_ROUNDED_RECT roundedRect
= {
1549 D2D1::RectF(RoundFloat(rc
.left
) + 0.5, rc
.top
+ 0.5, RoundFloat(rc
.right
) - 0.5, rc
.bottom
- 0.5),
1550 cornerSizeF
, cornerSizeF
};
1551 D2DPenColour(outline
, alphaOutline
);
1552 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1557 void SurfaceD2D::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
1558 if (pRenderTarget
) {
1559 if (rc
.Width() > width
)
1560 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
1561 rc
.right
= rc
.left
+ width
;
1562 if (rc
.Height() > height
)
1563 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
1564 rc
.bottom
= rc
.top
+ height
;
1566 std::vector
<unsigned char> image(height
* width
* 4);
1567 for (int y
=0; y
<height
; y
++) {
1568 for (int x
=0; x
<width
; x
++) {
1569 unsigned char *pixel
= &image
[0] + (y
*width
+x
) * 4;
1570 unsigned char alpha
= pixelsImage
[3];
1571 // Input is RGBA, output is BGRA with premultiplied alpha
1572 pixel
[2] = (*pixelsImage
++) * alpha
/ 255;
1573 pixel
[1] = (*pixelsImage
++) * alpha
/ 255;
1574 pixel
[0] = (*pixelsImage
++) * alpha
/ 255;
1575 pixel
[3] = *pixelsImage
++;
1579 ID2D1Bitmap
*bitmap
= 0;
1580 D2D1_SIZE_U size
= D2D1::SizeU(width
, height
);
1581 D2D1_BITMAP_PROPERTIES props
= {{DXGI_FORMAT_B8G8R8A8_UNORM
,
1582 D2D1_ALPHA_MODE_PREMULTIPLIED
}, 72.0, 72.0};
1583 HRESULT hr
= pRenderTarget
->CreateBitmap(size
, &image
[0],
1584 width
* 4, &props
, &bitmap
);
1585 if (SUCCEEDED(hr
)) {
1586 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1587 pRenderTarget
->DrawBitmap(bitmap
, rcDestination
);
1593 void SurfaceD2D::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1594 if (pRenderTarget
) {
1595 FLOAT radius
= rc
.Width() / 2.0f
- 1.0f
;
1596 D2D1_ELLIPSE ellipse
= {
1597 D2D1::Point2F((rc
.left
+ rc
.right
) / 2.0f
, (rc
.top
+ rc
.bottom
) / 2.0f
),
1601 pRenderTarget
->FillEllipse(ellipse
, pBrush
);
1603 pRenderTarget
->DrawEllipse(ellipse
, pBrush
);
1607 void SurfaceD2D::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
1608 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfaceSource
);
1609 surfOther
.FlushDrawing();
1610 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1611 surfOther
.pRenderTarget
);
1612 ID2D1Bitmap
*pBitmap
= NULL
;
1613 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1614 if (SUCCEEDED(hr
)) {
1615 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1616 D2D1_RECT_F rcSource
= {from
.x
, from
.y
, from
.x
+ rc
.Width(), from
.y
+ rc
.Height()};
1617 pRenderTarget
->DrawBitmap(pBitmap
, rcDestination
, 1.0f
,
1618 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
, rcSource
);
1619 pRenderTarget
->Flush();
1624 void SurfaceD2D::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
) {
1627 // Use Unicode calls
1628 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1629 if (pRenderTarget
&& pTextFormat
&& pBrush
) {
1630 if (fuOptions
& ETO_CLIPPED
) {
1631 D2D1_RECT_F rcClip
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1632 pRenderTarget
->PushAxisAlignedClip(rcClip
, D2D1_ANTIALIAS_MODE_ALIASED
);
1635 // Explicitly creating a text layout appears a little faster
1636 IDWriteTextLayout
*pTextLayout
;
1637 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
,
1638 rc
.Width(), rc
.Height(), &pTextLayout
);
1639 if (SUCCEEDED(hr
)) {
1640 D2D1_POINT_2F origin
= {rc
.left
, ybase
-yAscent
};
1641 pRenderTarget
->DrawTextLayout(origin
, pTextLayout
, pBrush
, D2D1_DRAW_TEXT_OPTIONS_NONE
);
1642 pTextLayout
->Release();
1645 if (fuOptions
& ETO_CLIPPED
) {
1646 pRenderTarget
->PopAxisAlignedClip();
1651 void SurfaceD2D::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1652 ColourDesired fore
, ColourDesired back
) {
1653 if (pRenderTarget
) {
1654 FillRectangle(rc
, back
);
1656 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
1660 void SurfaceD2D::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1661 ColourDesired fore
, ColourDesired back
) {
1662 if (pRenderTarget
) {
1663 FillRectangle(rc
, back
);
1665 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
1669 void SurfaceD2D::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1670 ColourDesired fore
) {
1671 // Avoid drawing spaces in transparent mode
1672 for (int i
=0;i
<len
;i
++) {
1674 if (pRenderTarget
) {
1676 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
1683 XYPOSITION
SurfaceD2D::WidthText(Font
&font_
, const char *s
, int len
) {
1686 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1687 if (pIDWriteFactory
&& pTextFormat
) {
1689 IDWriteTextLayout
*pTextLayout
= 0;
1690 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1691 if (SUCCEEDED(hr
)) {
1692 DWRITE_TEXT_METRICS textMetrics
;
1693 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1694 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1695 pTextLayout
->Release();
1701 void SurfaceD2D::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
1704 const TextWide
tbuf(s
, len
, unicodeMode
, codePageText
);
1705 TextPositions
poses(tbuf
.tlen
);
1707 const int clusters
= 1000;
1708 DWRITE_CLUSTER_METRICS clusterMetrics
[clusters
];
1710 if (pIDWriteFactory
&& pTextFormat
) {
1713 IDWriteTextLayout
*pTextLayout
= 0;
1714 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 10000.0, 1000.0, &pTextLayout
);
1717 // For now, assuming WCHAR == cluster
1718 if (!SUCCEEDED(pTextLayout
->GetClusterMetrics(clusterMetrics
, clusters
, &count
)))
1720 FLOAT position
= 0.0f
;
1722 for (size_t ci
=0;ci
<count
;ci
++) {
1723 position
+= clusterMetrics
[ci
].width
;
1724 for (size_t inCluster
=0; inCluster
<clusterMetrics
[ci
].length
; inCluster
++) {
1725 //poses.buffer[ti++] = int(position + 0.5);
1726 poses
.buffer
[ti
++] = position
;
1729 PLATFORM_ASSERT(ti
== static_cast<size_t>(tbuf
.tlen
));
1730 pTextLayout
->Release();
1733 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
1735 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1738 unsigned char uch
= us
[i
];
1739 unsigned int lenChar
= 1;
1740 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
1743 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
1745 } else if (uch
>= (0x80)) {
1748 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
1749 positions
[i
++] = poses
.buffer
[ui
];
1755 lastPos
= positions
[i
-1];
1757 positions
[i
++] = lastPos
;
1759 } else if (codePageText
== 0) {
1761 // One character per position
1762 PLATFORM_ASSERT(len
== tbuf
.tlen
);
1763 for (size_t kk
=0;kk
<static_cast<size_t>(len
);kk
++) {
1764 positions
[kk
] = poses
.buffer
[kk
];
1769 // May be more than one byte per position
1770 unsigned int ui
= 0;
1771 FLOAT position
= 0.0f
;
1772 for (int i
=0;i
<len
;) {
1774 position
= poses
.buffer
[ui
];
1775 if (Platform::IsDBCSLeadByte(codePageText
, s
[i
])) {
1776 positions
[i
] = position
;
1777 positions
[i
+1] = position
;
1780 positions
[i
] = position
;
1789 XYPOSITION
SurfaceD2D::WidthChar(Font
&font_
, char ch
) {
1792 if (pIDWriteFactory
&& pTextFormat
) {
1794 IDWriteTextLayout
*pTextLayout
= 0;
1795 const WCHAR wch
= ch
;
1796 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(&wch
, 1, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1797 if (SUCCEEDED(hr
)) {
1798 DWRITE_TEXT_METRICS textMetrics
;
1799 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1800 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1801 pTextLayout
->Release();
1807 XYPOSITION
SurfaceD2D::Ascent(Font
&font_
) {
1809 return ceil(yAscent
);
1812 XYPOSITION
SurfaceD2D::Descent(Font
&font_
) {
1814 return ceil(yDescent
);
1817 XYPOSITION
SurfaceD2D::InternalLeading(Font
&font_
) {
1819 return floor(yInternalLeading
);
1822 XYPOSITION
SurfaceD2D::ExternalLeading(Font
&) {
1823 // Not implemented, always return one
1827 XYPOSITION
SurfaceD2D::Height(Font
&font_
) {
1828 return Ascent(font_
) + Descent(font_
);
1831 XYPOSITION
SurfaceD2D::AverageCharWidth(Font
&font_
) {
1834 if (pIDWriteFactory
&& pTextFormat
) {
1836 IDWriteTextLayout
*pTextLayout
= 0;
1837 const WCHAR wszAllAlpha
[] = L
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1838 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(wszAllAlpha
, static_cast<UINT32
>(wcslen(wszAllAlpha
)),
1839 pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1840 if (SUCCEEDED(hr
)) {
1841 DWRITE_TEXT_METRICS textMetrics
;
1842 if (SUCCEEDED(pTextLayout
->GetMetrics(&textMetrics
)))
1843 width
= textMetrics
.width
/ wcslen(wszAllAlpha
);
1844 pTextLayout
->Release();
1850 void SurfaceD2D::SetClip(PRectangle rc
) {
1851 if (pRenderTarget
) {
1852 D2D1_RECT_F rcClip
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1853 pRenderTarget
->PushAxisAlignedClip(rcClip
, D2D1_ANTIALIAS_MODE_ALIASED
);
1858 void SurfaceD2D::FlushCachedState() {
1861 void SurfaceD2D::SetUnicodeMode(bool unicodeMode_
) {
1862 unicodeMode
=unicodeMode_
;
1865 void SurfaceD2D::SetDBCSMode(int codePage_
) {
1866 // No action on window as automatically handled by system.
1867 codePage
= codePage_
;
1871 Surface
*Surface::Allocate(int technology
) {
1872 #if defined(USE_D2D)
1873 if (technology
== SCWIN_TECH_GDI
)
1874 return new SurfaceGDI
;
1876 return new SurfaceD2D
;
1878 return new SurfaceGDI
;
1885 void Window::Destroy() {
1887 ::DestroyWindow(reinterpret_cast<HWND
>(wid
));
1891 bool Window::HasFocus() {
1892 return ::GetFocus() == wid
;
1895 PRectangle
Window::GetPosition() {
1897 ::GetWindowRect(reinterpret_cast<HWND
>(wid
), &rc
);
1898 return PRectangle(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1901 void Window::SetPosition(PRectangle rc
) {
1902 ::SetWindowPos(reinterpret_cast<HWND
>(wid
),
1903 0, rc
.left
, rc
.top
, rc
.Width(), rc
.Height(), SWP_NOZORDER
|SWP_NOACTIVATE
);
1906 static RECT
RectFromMonitor(HMONITOR hMonitor
) {
1907 if (GetMonitorInfoFn
) {
1908 MONITORINFO mi
= {0};
1909 mi
.cbSize
= sizeof(mi
);
1910 if (GetMonitorInfoFn(hMonitor
, &mi
)) {
1914 RECT rc
= {0, 0, 0, 0};
1915 if (::SystemParametersInfoA(SPI_GETWORKAREA
, 0, &rc
, 0) == 0) {
1924 void Window::SetPositionRelative(PRectangle rc
, Window w
) {
1925 LONG style
= ::GetWindowLong(reinterpret_cast<HWND
>(wid
), GWL_STYLE
);
1926 if (style
& WS_POPUP
) {
1927 POINT ptOther
= {0, 0};
1928 ::ClientToScreen(reinterpret_cast<HWND
>(w
.GetID()), &ptOther
);
1929 rc
.Move(ptOther
.x
, ptOther
.y
);
1931 RECT rcMonitor
= RectFromPRectangle(rc
);
1933 HMONITOR hMonitor
= NULL
;
1934 if (MonitorFromRectFn
)
1935 hMonitor
= MonitorFromRectFn(&rcMonitor
, MONITOR_DEFAULTTONEAREST
);
1936 // If hMonitor is NULL, that's just the main screen anyways.
1937 //::GetMonitorInfo(hMonitor, &mi);
1938 RECT rcWork
= RectFromMonitor(hMonitor
);
1940 if (rcWork
.left
< rcWork
.right
) {
1941 // Now clamp our desired rectangle to fit inside the work area
1942 // This way, the menu will fit wholly on one screen. An improvement even
1943 // if you don't have a second monitor on the left... Menu's appears half on
1944 // one screen and half on the other are just U.G.L.Y.!
1945 if (rc
.right
> rcWork
.right
)
1946 rc
.Move(rcWork
.right
- rc
.right
, 0);
1947 if (rc
.bottom
> rcWork
.bottom
)
1948 rc
.Move(0, rcWork
.bottom
- rc
.bottom
);
1949 if (rc
.left
< rcWork
.left
)
1950 rc
.Move(rcWork
.left
- rc
.left
, 0);
1951 if (rc
.top
< rcWork
.top
)
1952 rc
.Move(0, rcWork
.top
- rc
.top
);
1958 PRectangle
Window::GetClientPosition() {
1961 ::GetClientRect(reinterpret_cast<HWND
>(wid
), &rc
);
1962 return PRectangle(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1965 void Window::Show(bool show
) {
1967 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_SHOWNOACTIVATE
);
1969 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_HIDE
);
1972 void Window::InvalidateAll() {
1973 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), NULL
, FALSE
);
1976 void Window::InvalidateRectangle(PRectangle rc
) {
1977 RECT rcw
= RectFromPRectangle(rc
);
1978 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), &rcw
, FALSE
);
1981 static LRESULT
Window_SendMessage(Window
*w
, UINT msg
, WPARAM wParam
=0, LPARAM lParam
=0) {
1982 return ::SendMessage(reinterpret_cast<HWND
>(w
->GetID()), msg
, wParam
, lParam
);
1985 void Window::SetFont(Font
&font
) {
1986 Window_SendMessage(this, WM_SETFONT
,
1987 reinterpret_cast<WPARAM
>(font
.GetID()), 0);
1990 static void FlipBitmap(HBITMAP bitmap
, int width
, int height
) {
1991 HDC hdc
= ::CreateCompatibleDC(NULL
);
1993 HGDIOBJ prevBmp
= ::SelectObject(hdc
, bitmap
);
1994 ::StretchBlt(hdc
, width
- 1, 0, -width
, height
, hdc
, 0, 0, width
, height
, SRCCOPY
);
1995 ::SelectObject(hdc
, prevBmp
);
2000 static HCURSOR
GetReverseArrowCursor() {
2001 if (reverseArrowCursor
!= NULL
)
2002 return reverseArrowCursor
;
2004 ::EnterCriticalSection(&crPlatformLock
);
2005 HCURSOR cursor
= reverseArrowCursor
;
2006 if (cursor
== NULL
) {
2007 cursor
= ::LoadCursor(NULL
, IDC_ARROW
);
2009 if (::GetIconInfo(cursor
, &info
)) {
2011 if (::GetObject(info
.hbmMask
, sizeof(bmp
), &bmp
)) {
2012 FlipBitmap(info
.hbmMask
, bmp
.bmWidth
, bmp
.bmHeight
);
2013 if (info
.hbmColor
!= NULL
)
2014 FlipBitmap(info
.hbmColor
, bmp
.bmWidth
, bmp
.bmHeight
);
2015 info
.xHotspot
= (DWORD
)bmp
.bmWidth
- 1 - info
.xHotspot
;
2017 reverseArrowCursor
= ::CreateIconIndirect(&info
);
2018 if (reverseArrowCursor
!= NULL
)
2019 cursor
= reverseArrowCursor
;
2022 ::DeleteObject(info
.hbmMask
);
2023 if (info
.hbmColor
!= NULL
)
2024 ::DeleteObject(info
.hbmColor
);
2027 ::LeaveCriticalSection(&crPlatformLock
);
2031 void Window::SetCursor(Cursor curs
) {
2034 ::SetCursor(::LoadCursor(NULL
,IDC_IBEAM
));
2037 ::SetCursor(::LoadCursor(NULL
,IDC_UPARROW
));
2040 ::SetCursor(::LoadCursor(NULL
,IDC_WAIT
));
2043 ::SetCursor(::LoadCursor(NULL
,IDC_SIZEWE
));
2046 ::SetCursor(::LoadCursor(NULL
,IDC_SIZENS
));
2049 ::SetCursor(::LoadCursor(NULL
,IDC_HAND
));
2051 case cursorReverseArrow
:
2052 ::SetCursor(GetReverseArrowCursor());
2055 case cursorInvalid
: // Should not occur, but just in case.
2056 ::SetCursor(::LoadCursor(NULL
,IDC_ARROW
));
2061 void Window::SetTitle(const char *s
) {
2062 ::SetWindowTextA(reinterpret_cast<HWND
>(wid
), s
);
2065 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
2067 PRectangle
Window::GetMonitorRect(Point pt
) {
2068 // MonitorFromPoint and GetMonitorInfo are not available on Windows 95 and NT 4.
2069 PRectangle rcPosition
= GetPosition();
2070 POINT ptDesktop
= {static_cast<LONG
>(pt
.x
+ rcPosition
.left
),
2071 static_cast<LONG
>(pt
.y
+ rcPosition
.top
)};
2072 HMONITOR hMonitor
= NULL
;
2073 if (MonitorFromPointFn
)
2074 hMonitor
= MonitorFromPointFn(ptDesktop
, MONITOR_DEFAULTTONEAREST
);
2076 RECT rcWork
= RectFromMonitor(hMonitor
);
2077 if (rcWork
.left
< rcWork
.right
) {
2078 PRectangle
rcMonitor(
2079 rcWork
.left
- rcPosition
.left
,
2080 rcWork
.top
- rcPosition
.top
,
2081 rcWork
.right
- rcPosition
.left
,
2082 rcWork
.bottom
- rcPosition
.top
);
2085 return PRectangle();
2089 struct ListItemData
{
2095 std::vector
<char> words
;
2097 std::vector
<ListItemData
> data
;
2110 ListItemData
Get(int index
) const {
2111 if (index
>= 0 && index
< static_cast<int>(data
.size())) {
2114 ListItemData missing
= {"", -1};
2119 return static_cast<int>(data
.size());
2122 void AllocItem(const char *text
, int pixId
) {
2123 ListItemData lid
= { text
, pixId
};
2124 data
.push_back(lid
);
2127 char *SetWords(const char *s
) {
2128 words
= std::vector
<char>(s
, s
+strlen(s
)+1);
2133 const TCHAR ListBoxX_ClassName
[] = TEXT("ListBoxX");
2135 ListBox::ListBox() {
2138 ListBox::~ListBox() {
2141 class ListBoxX
: public ListBox
{
2145 RGBAImageSet images
;
2149 int desiredVisibleRows
;
2150 unsigned int maxItemCharacters
;
2151 unsigned int aveCharWidth
;
2154 CallBackAction doubleClickAction
;
2155 void *doubleClickActionData
;
2156 const char *widestItem
;
2157 unsigned int maxCharWidth
;
2159 PRectangle rcPreSize
;
2161 Point location
; // Caret location at which the list is opened
2162 int wheelDelta
; // mouse wheel residue
2164 HWND
GetHWND() const;
2165 void AppendListItem(const char *text
, const char *numword
);
2166 static void AdjustWindowRect(PRectangle
*rc
);
2167 int ItemHeight() const;
2168 int MinClientWidth() const;
2169 int TextOffset() const;
2170 Point
GetClientExtent() const;
2171 POINT
MinTrackSize() const;
2172 POINT
MaxTrackSize() const;
2173 void SetRedraw(bool on
);
2174 void OnDoubleClick();
2175 void ResizeToCursor();
2176 void StartResize(WPARAM
);
2177 int NcHitTest(WPARAM
, LPARAM
) const;
2178 void CentreItem(int);
2180 static LRESULT PASCAL
ControlWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2182 static const Point ItemInset
; // Padding around whole item
2183 static const Point TextInset
; // Padding around text
2184 static const Point ImageInset
; // Padding around image
2187 ListBoxX() : lineHeight(10), fontCopy(0), technology(0), lb(0), unicodeMode(false),
2188 desiredVisibleRows(5), maxItemCharacters(0), aveCharWidth(8),
2189 parent(NULL
), ctrlID(0), doubleClickAction(NULL
), doubleClickActionData(NULL
),
2190 widestItem(NULL
), maxCharWidth(1), resizeHit(0), wheelDelta(0) {
2192 virtual ~ListBoxX() {
2194 ::DeleteObject(fontCopy
);
2198 virtual void SetFont(Font
&font
);
2199 virtual void Create(Window
&parent
, int ctrlID
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
);
2200 virtual void SetAverageCharWidth(int width
);
2201 virtual void SetVisibleRows(int rows
);
2202 virtual int GetVisibleRows() const;
2203 virtual PRectangle
GetDesiredRect();
2204 virtual int CaretFromEdge();
2205 virtual void Clear();
2206 virtual void Append(char *s
, int type
= -1);
2207 virtual int Length();
2208 virtual void Select(int n
);
2209 virtual int GetSelection();
2210 virtual int Find(const char *prefix
);
2211 virtual void GetValue(int n
, char *value
, int len
);
2212 virtual void RegisterImage(int type
, const char *xpm_data
);
2213 virtual void RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
);
2214 virtual void ClearRegisteredImages();
2215 virtual void SetDoubleClickAction(CallBackAction action
, void *data
) {
2216 doubleClickAction
= action
;
2217 doubleClickActionData
= data
;
2219 virtual void SetList(const char *list
, char separator
, char typesep
);
2220 void Draw(DRAWITEMSTRUCT
*pDrawItem
);
2221 LRESULT
WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2222 static LRESULT PASCAL
StaticWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2225 const Point
ListBoxX::ItemInset(0, 0);
2226 const Point
ListBoxX::TextInset(2, 0);
2227 const Point
ListBoxX::ImageInset(1, 0);
2229 ListBox
*ListBox::Allocate() {
2230 ListBoxX
*lb
= new ListBoxX();
2234 void ListBoxX::Create(Window
&parent_
, int ctrlID_
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
) {
2237 location
= location_
;
2238 lineHeight
= lineHeight_
;
2239 unicodeMode
= unicodeMode_
;
2240 technology
= technology_
;
2241 HWND hwndParent
= reinterpret_cast<HWND
>(parent
->GetID());
2242 HINSTANCE hinstanceParent
= GetWindowInstance(hwndParent
);
2243 // Window created as popup so not clipped within parent client area
2244 wid
= ::CreateWindowEx(
2245 WS_EX_WINDOWEDGE
, ListBoxX_ClassName
, TEXT(""),
2246 WS_POPUP
| WS_THICKFRAME
,
2247 100,100, 150,80, hwndParent
,
2252 POINT locationw
= {static_cast<LONG
>(location
.x
), static_cast<LONG
>(location
.y
)};
2253 ::MapWindowPoints(hwndParent
, NULL
, &locationw
, 1);
2254 location
= Point(locationw
.x
, locationw
.y
);
2257 void ListBoxX::SetFont(Font
&font
) {
2260 ::DeleteObject(fontCopy
);
2263 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font
.GetID());
2264 fontCopy
= pfm
->HFont();
2265 ::SendMessage(lb
, WM_SETFONT
, reinterpret_cast<WPARAM
>(fontCopy
), 0);
2269 void ListBoxX::SetAverageCharWidth(int width
) {
2270 aveCharWidth
= width
;
2273 void ListBoxX::SetVisibleRows(int rows
) {
2274 desiredVisibleRows
= rows
;
2277 int ListBoxX::GetVisibleRows() const {
2278 return desiredVisibleRows
;
2281 HWND
ListBoxX::GetHWND() const {
2282 return reinterpret_cast<HWND
>(GetID());
2285 PRectangle
ListBoxX::GetDesiredRect() {
2286 PRectangle rcDesired
= GetPosition();
2288 int rows
= Length();
2289 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2290 rows
= desiredVisibleRows
;
2291 rcDesired
.bottom
= rcDesired
.top
+ ItemHeight() * rows
;
2293 int width
= MinClientWidth();
2294 HDC hdc
= ::GetDC(lb
);
2295 HFONT oldFont
= SelectFont(hdc
, fontCopy
);
2296 SIZE textSize
= {0, 0};
2299 len
= static_cast<int>(strlen(widestItem
));
2301 const TextWide
tbuf(widestItem
, len
, unicodeMode
);
2302 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &textSize
);
2304 ::GetTextExtentPoint32A(hdc
, widestItem
, len
, &textSize
);
2308 ::GetTextMetrics(hdc
, &tm
);
2309 maxCharWidth
= tm
.tmMaxCharWidth
;
2310 SelectFont(hdc
, oldFont
);
2311 ::ReleaseDC(lb
, hdc
);
2313 int widthDesired
= Platform::Maximum(textSize
.cx
, (len
+ 1) * tm
.tmAveCharWidth
);
2314 if (width
< widthDesired
)
2315 width
= widthDesired
;
2317 rcDesired
.right
= rcDesired
.left
+ TextOffset() + width
+ (TextInset
.x
* 2);
2318 if (Length() > rows
)
2319 rcDesired
.right
+= ::GetSystemMetrics(SM_CXVSCROLL
);
2321 AdjustWindowRect(&rcDesired
);
2325 int ListBoxX::TextOffset() const {
2326 int pixWidth
= images
.GetWidth();
2327 return pixWidth
== 0 ? ItemInset
.x
: ItemInset
.x
+ pixWidth
+ (ImageInset
.x
* 2);
2330 int ListBoxX::CaretFromEdge() {
2332 AdjustWindowRect(&rc
);
2333 return TextOffset() + TextInset
.x
+ (0 - rc
.left
) - 1;
2336 void ListBoxX::Clear() {
2337 ::SendMessage(lb
, LB_RESETCONTENT
, 0, 0);
2338 maxItemCharacters
= 0;
2343 void ListBoxX::Append(char *, int) {
2344 // This method is no longer called in Scintilla
2345 PLATFORM_ASSERT(false);
2348 int ListBoxX::Length() {
2352 void ListBoxX::Select(int n
) {
2353 // We are going to scroll to centre on the new selection and then select it, so disable
2354 // redraw to avoid flicker caused by a painting new selection twice in unselected and then
2358 ::SendMessage(lb
, LB_SETCURSEL
, n
, 0);
2362 int ListBoxX::GetSelection() {
2363 return ::SendMessage(lb
, LB_GETCURSEL
, 0, 0);
2366 // This is not actually called at present
2367 int ListBoxX::Find(const char *) {
2371 void ListBoxX::GetValue(int n
, char *value
, int len
) {
2372 ListItemData item
= lti
.Get(n
);
2373 strncpy(value
, item
.text
, len
);
2374 value
[len
-1] = '\0';
2377 void ListBoxX::RegisterImage(int type
, const char *xpm_data
) {
2378 XPM
xpmImage(xpm_data
);
2379 images
.Add(type
, new RGBAImage(xpmImage
));
2382 void ListBoxX::RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
) {
2383 images
.Add(type
, new RGBAImage(width
, height
, 1.0, pixelsImage
));
2386 void ListBoxX::ClearRegisteredImages() {
2390 void ListBoxX::Draw(DRAWITEMSTRUCT
*pDrawItem
) {
2391 if ((pDrawItem
->itemAction
== ODA_SELECT
) || (pDrawItem
->itemAction
== ODA_DRAWENTIRE
)) {
2392 RECT rcBox
= pDrawItem
->rcItem
;
2393 rcBox
.left
+= TextOffset();
2394 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2395 RECT rcImage
= pDrawItem
->rcItem
;
2396 rcImage
.right
= rcBox
.left
;
2397 // The image is not highlighted
2398 ::FillRect(pDrawItem
->hDC
, &rcImage
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2399 ::FillRect(pDrawItem
->hDC
, &rcBox
, reinterpret_cast<HBRUSH
>(COLOR_HIGHLIGHT
+1));
2400 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHT
));
2401 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
2403 ::FillRect(pDrawItem
->hDC
, &pDrawItem
->rcItem
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2404 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOW
));
2405 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOWTEXT
));
2408 ListItemData item
= lti
.Get(pDrawItem
->itemID
);
2409 int pixId
= item
.pixId
;
2410 const char *text
= item
.text
;
2411 int len
= static_cast<int>(strlen(text
));
2413 RECT rcText
= rcBox
;
2414 ::InsetRect(&rcText
, TextInset
.x
, TextInset
.y
);
2417 const TextWide
tbuf(text
, len
, unicodeMode
);
2418 ::DrawTextW(pDrawItem
->hDC
, tbuf
.buffer
, tbuf
.tlen
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2420 ::DrawTextA(pDrawItem
->hDC
, text
, len
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2422 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2423 ::DrawFocusRect(pDrawItem
->hDC
, &rcBox
);
2426 // Draw the image, if any
2427 RGBAImage
*pimage
= images
.Get(pixId
);
2429 Surface
*surfaceItem
= Surface::Allocate(technology
);
2431 if (technology
== SCWIN_TECH_GDI
) {
2432 surfaceItem
->Init(pDrawItem
->hDC
, pDrawItem
->hwndItem
);
2433 int left
= pDrawItem
->rcItem
.left
+ ItemInset
.x
+ ImageInset
.x
;
2434 PRectangle
rcImage(left
, pDrawItem
->rcItem
.top
,
2435 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2436 surfaceItem
->DrawRGBAImage(rcImage
,
2437 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2439 ::SetTextAlign(pDrawItem
->hDC
, TA_TOP
);
2441 #if defined(USE_D2D)
2442 D2D1_RENDER_TARGET_PROPERTIES props
= D2D1::RenderTargetProperties(
2443 D2D1_RENDER_TARGET_TYPE_DEFAULT
,
2445 DXGI_FORMAT_B8G8R8A8_UNORM
,
2446 D2D1_ALPHA_MODE_IGNORE
),
2449 D2D1_RENDER_TARGET_USAGE_NONE
,
2450 D2D1_FEATURE_LEVEL_DEFAULT
2452 ID2D1DCRenderTarget
*pDCRT
= 0;
2453 HRESULT hr
= pD2DFactory
->CreateDCRenderTarget(&props
, &pDCRT
);
2454 if (SUCCEEDED(hr
)) {
2456 GetClientRect(pDrawItem
->hwndItem
, &rcWindow
);
2457 hr
= pDCRT
->BindDC(pDrawItem
->hDC
, &rcWindow
);
2458 if (SUCCEEDED(hr
)) {
2459 surfaceItem
->Init(pDCRT
, pDrawItem
->hwndItem
);
2461 int left
= pDrawItem
->rcItem
.left
+ ItemInset
.x
+ ImageInset
.x
;
2462 PRectangle
rcImage(left
, pDrawItem
->rcItem
.top
,
2463 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2464 surfaceItem
->DrawRGBAImage(rcImage
,
2465 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2482 void ListBoxX::AppendListItem(const char *text
, const char *numword
) {
2487 while ((ch
= *++numword
) != '\0') {
2488 pixId
= 10 * pixId
+ (ch
- '0');
2492 lti
.AllocItem(text
, pixId
);
2493 unsigned int len
= static_cast<unsigned int>(strlen(text
));
2494 if (maxItemCharacters
< len
) {
2495 maxItemCharacters
= len
;
2500 void ListBoxX::SetList(const char *list
, char separator
, char typesep
) {
2501 // Turn off redraw while populating the list - this has a significant effect, even if
2502 // the listbox is not visible.
2505 size_t size
= strlen(list
);
2506 char *words
= lti
.SetWords(list
);
2507 char *startword
= words
;
2508 char *numword
= NULL
;
2509 for (size_t i
=0; i
< size
; i
++) {
2510 if (words
[i
] == separator
) {
2514 AppendListItem(startword
, numword
);
2515 startword
= words
+ i
+ 1;
2517 } else if (words
[i
] == typesep
) {
2518 numword
= words
+ i
;
2524 AppendListItem(startword
, numword
);
2527 // Finally populate the listbox itself with the correct number of items
2528 int count
= lti
.Count();
2529 ::SendMessage(lb
, LB_INITSTORAGE
, count
, 0);
2530 for (int j
=0; j
<count
; j
++) {
2531 ::SendMessage(lb
, LB_ADDSTRING
, 0, j
+1);
2536 void ListBoxX::AdjustWindowRect(PRectangle
*rc
) {
2537 RECT rcw
= RectFromPRectangle(*rc
);
2538 ::AdjustWindowRectEx(&rcw
, WS_THICKFRAME
, false, WS_EX_WINDOWEDGE
);
2539 *rc
= PRectangle(rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
2542 int ListBoxX::ItemHeight() const {
2543 int itemHeight
= lineHeight
+ (TextInset
.y
* 2);
2544 int pixHeight
= images
.GetHeight() + (ImageInset
.y
* 2);
2545 if (itemHeight
< pixHeight
) {
2546 itemHeight
= pixHeight
;
2551 int ListBoxX::MinClientWidth() const {
2552 return 12 * (aveCharWidth
+aveCharWidth
/3);
2555 POINT
ListBoxX::MinTrackSize() const {
2556 PRectangle
rc(0, 0, MinClientWidth(), ItemHeight());
2557 AdjustWindowRect(&rc
);
2558 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2562 POINT
ListBoxX::MaxTrackSize() const {
2564 maxCharWidth
* maxItemCharacters
+ TextInset
.x
* 2 +
2565 TextOffset() + ::GetSystemMetrics(SM_CXVSCROLL
),
2566 ItemHeight() * lti
.Count());
2567 AdjustWindowRect(&rc
);
2568 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2572 void ListBoxX::SetRedraw(bool on
) {
2573 ::SendMessage(lb
, WM_SETREDRAW
, static_cast<BOOL
>(on
), 0);
2575 ::InvalidateRect(lb
, NULL
, TRUE
);
2578 void ListBoxX::ResizeToCursor() {
2579 PRectangle rc
= GetPosition();
2581 ::GetCursorPos(&ptw
);
2582 Point
pt(ptw
.x
, ptw
.y
);
2583 pt
.x
+= dragOffset
.x
;
2584 pt
.y
+= dragOffset
.y
;
2586 switch (resizeHit
) {
2617 POINT ptMin
= MinTrackSize();
2618 POINT ptMax
= MaxTrackSize();
2619 // We don't allow the left edge to move at present, but just in case
2620 rc
.left
= Platform::Maximum(Platform::Minimum(rc
.left
, rcPreSize
.right
- ptMin
.x
), rcPreSize
.right
- ptMax
.x
);
2621 rc
.top
= Platform::Maximum(Platform::Minimum(rc
.top
, rcPreSize
.bottom
- ptMin
.y
), rcPreSize
.bottom
- ptMax
.y
);
2622 rc
.right
= Platform::Maximum(Platform::Minimum(rc
.right
, rcPreSize
.left
+ ptMax
.x
), rcPreSize
.left
+ ptMin
.x
);
2623 rc
.bottom
= Platform::Maximum(Platform::Minimum(rc
.bottom
, rcPreSize
.top
+ ptMax
.y
), rcPreSize
.top
+ ptMin
.y
);
2628 void ListBoxX::StartResize(WPARAM hitCode
) {
2629 rcPreSize
= GetPosition();
2631 ::GetCursorPos(&cursorPos
);
2637 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2638 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2642 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2643 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2646 // Note that the current hit test code prevents the left edge cases ever firing
2647 // as we don't want the left edge to be moveable
2651 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2652 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2655 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2656 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2663 ::SetCapture(GetHWND());
2664 resizeHit
= hitCode
;
2667 int ListBoxX::NcHitTest(WPARAM wParam
, LPARAM lParam
) const {
2668 int hit
= ::DefWindowProc(GetHWND(), WM_NCHITTEST
, wParam
, lParam
);
2669 // There is an apparent bug in the DefWindowProc hit test code whereby it will
2670 // return HTTOPXXX if the window in question is shorter than the default
2671 // window caption height + frame, even if one is hovering over the bottom edge of
2672 // the frame, so workaround that here
2673 if (hit
>= HTTOP
&& hit
<= HTTOPRIGHT
) {
2674 int minHeight
= GetSystemMetrics(SM_CYMINTRACK
);
2675 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2676 int yPos
= GET_Y_LPARAM(lParam
);
2677 if ((rc
.Height() < minHeight
) && (yPos
> ((rc
.top
+ rc
.bottom
)/2))) {
2678 hit
+= HTBOTTOM
- HTTOP
;
2682 // Nerver permit resizing that moves the left edge. Allow movement of top or bottom edge
2683 // depending on whether the list is above or below the caret
2693 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2694 // Valid only if caret below list
2695 if (location
.y
< rc
.top
)
2701 case HTBOTTOMRIGHT
: {
2702 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2703 // Valid only if caret above list
2704 if (rc
.bottom
< location
.y
)
2713 void ListBoxX::OnDoubleClick() {
2715 if (doubleClickAction
!= NULL
) {
2716 doubleClickAction(doubleClickActionData
);
2720 Point
ListBoxX::GetClientExtent() const {
2721 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetClientPosition();
2722 return Point(rc
.Width(), rc
.Height());
2725 void ListBoxX::CentreItem(int n
) {
2726 // If below mid point, scroll up to centre, but with more items below if uneven
2728 Point extent
= GetClientExtent();
2729 int visible
= extent
.y
/ItemHeight();
2730 if (visible
< Length()) {
2731 int top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0);
2732 int half
= (visible
- 1) / 2;
2733 if (n
> (top
+ half
))
2734 ::SendMessage(lb
, LB_SETTOPINDEX
, n
- half
, 0);
2739 // Performs a double-buffered paint operation to avoid flicker
2740 void ListBoxX::Paint(HDC hDC
) {
2741 Point extent
= GetClientExtent();
2742 HBITMAP hBitmap
= ::CreateCompatibleBitmap(hDC
, extent
.x
, extent
.y
);
2743 HDC bitmapDC
= ::CreateCompatibleDC(hDC
);
2744 HBITMAP hBitmapOld
= SelectBitmap(bitmapDC
, hBitmap
);
2745 // The list background is mainly erased during painting, but can be a small
2746 // unpainted area when at the end of a non-integrally sized list with a
2747 // vertical scroll bar
2748 RECT rc
= { 0, 0, static_cast<LONG
>(extent
.x
), static_cast<LONG
>(extent
.y
) };
2749 ::FillRect(bitmapDC
, &rc
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2750 // Paint the entire client area and vertical scrollbar
2751 ::SendMessage(lb
, WM_PRINT
, reinterpret_cast<WPARAM
>(bitmapDC
), PRF_CLIENT
|PRF_NONCLIENT
);
2752 ::BitBlt(hDC
, 0, 0, extent
.x
, extent
.y
, bitmapDC
, 0, 0, SRCCOPY
);
2753 // Select a stock brush to prevent warnings from BoundsChecker
2754 ::SelectObject(bitmapDC
, GetStockFont(WHITE_BRUSH
));
2755 SelectBitmap(bitmapDC
, hBitmapOld
);
2756 ::DeleteDC(bitmapDC
);
2757 ::DeleteObject(hBitmap
);
2760 LRESULT PASCAL
ListBoxX::ControlWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
2768 HDC hDC
= ::BeginPaint(hWnd
, &ps
);
2769 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2772 ::EndPaint(hWnd
, &ps
);
2776 case WM_MOUSEACTIVATE
:
2777 // This prevents the view activating when the scrollbar is clicked
2778 return MA_NOACTIVATE
;
2780 case WM_LBUTTONDOWN
: {
2781 // We must take control of selection to prevent the ListBox activating
2783 LRESULT lResult
= ::SendMessage(hWnd
, LB_ITEMFROMPOINT
, 0, lParam
);
2784 int item
= LOWORD(lResult
);
2785 if (HIWORD(lResult
) == 0 && item
>= 0) {
2786 ::SendMessage(hWnd
, LB_SETCURSEL
, item
, 0);
2794 case WM_LBUTTONDBLCLK
: {
2795 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2797 lbx
->OnDoubleClick();
2802 case WM_MBUTTONDOWN
:
2803 // disable the scroll wheel button click action
2807 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(GetWindowLongPtr(hWnd
, GWLP_USERDATA
));
2809 return ::CallWindowProc(prevWndProc
, hWnd
, uMsg
, wParam
, lParam
);
2811 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2815 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2818 LRESULT
ListBoxX::WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2821 HINSTANCE hinstanceParent
= GetWindowInstance(reinterpret_cast<HWND
>(parent
->GetID()));
2822 // Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list
2823 // but has useful side effect of speeding up list population significantly
2824 lb
= ::CreateWindowEx(
2825 0, TEXT("listbox"), TEXT(""),
2826 WS_CHILD
| WS_VSCROLL
| WS_VISIBLE
|
2827 LBS_OWNERDRAWFIXED
| LBS_NODATA
| LBS_NOINTEGRALHEIGHT
,
2829 reinterpret_cast<HMENU
>(ctrlID
),
2832 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(::SetWindowLongPtr(lb
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(ControlWndProc
)));
2833 ::SetWindowLongPtr(lb
, GWLP_USERDATA
, reinterpret_cast<LONG_PTR
>(prevWndProc
));
2840 ::SetWindowPos(lb
, 0, 0,0, LOWORD(lParam
), HIWORD(lParam
), SWP_NOZORDER
|SWP_NOACTIVATE
|SWP_NOMOVE
);
2841 // Ensure the selection remains visible
2842 CentreItem(GetSelection());
2849 ::BeginPaint(hWnd
, &ps
);
2850 ::EndPaint(hWnd
, &ps
);
2855 // This is not actually needed now - the registered double click action is used
2856 // directly to action a choice from the list.
2857 ::SendMessage(reinterpret_cast<HWND
>(parent
->GetID()), iMessage
, wParam
, lParam
);
2860 case WM_MEASUREITEM
: {
2861 MEASUREITEMSTRUCT
*pMeasureItem
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
2862 pMeasureItem
->itemHeight
= static_cast<unsigned int>(ItemHeight());
2867 Draw(reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
));
2872 ::SetWindowLong(hWnd
, 0, 0);
2873 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2876 // To reduce flicker we can elide background erasure since this window is
2877 // completely covered by its child.
2880 case WM_GETMINMAXINFO
: {
2881 MINMAXINFO
*minMax
= reinterpret_cast<MINMAXINFO
*>(lParam
);
2882 minMax
->ptMaxTrackSize
= MaxTrackSize();
2883 minMax
->ptMinTrackSize
= MinTrackSize();
2887 case WM_MOUSEACTIVATE
:
2888 return MA_NOACTIVATE
;
2891 return NcHitTest(wParam
, lParam
);
2893 case WM_NCLBUTTONDOWN
:
2894 // We have to implement our own window resizing because the DefWindowProc
2895 // implementation insists on activating the resized window
2896 StartResize(wParam
);
2899 case WM_MOUSEMOVE
: {
2900 if (resizeHit
== 0) {
2901 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2910 if (resizeHit
!= 0) {
2914 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2917 wheelDelta
-= static_cast<short>(HIWORD(wParam
));
2918 if (abs(wheelDelta
) >= WHEEL_DELTA
) {
2919 int nRows
= GetVisibleRows();
2920 int linesToScroll
= 1;
2922 linesToScroll
= nRows
- 1;
2924 if (linesToScroll
> 3) {
2927 linesToScroll
*= (wheelDelta
/ WHEEL_DELTA
);
2928 int top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0) + linesToScroll
;
2932 ::SendMessage(lb
, LB_SETTOPINDEX
, top
, 0);
2933 // update wheel delta residue
2934 if (wheelDelta
>= 0)
2935 wheelDelta
= wheelDelta
% WHEEL_DELTA
;
2937 wheelDelta
= - (-wheelDelta
% WHEEL_DELTA
);
2942 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2948 LRESULT PASCAL
ListBoxX::StaticWndProc(
2949 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2950 if (iMessage
== WM_CREATE
) {
2951 CREATESTRUCT
*pCreate
= reinterpret_cast<CREATESTRUCT
*>(lParam
);
2952 SetWindowPointer(hWnd
, pCreate
->lpCreateParams
);
2954 // Find C++ object associated with window.
2955 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(hWnd
));
2957 return lbx
->WndProc(hWnd
, iMessage
, wParam
, lParam
);
2959 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2963 static bool ListBoxX_Register() {
2964 WNDCLASSEX wndclassc
;
2965 wndclassc
.cbSize
= sizeof(wndclassc
);
2966 // We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for
2967 // truncated items in the list and the appearance/disappearance of the vertical scroll bar.
2968 // The list repaint is double-buffered to avoid the flicker this would otherwise cause.
2969 wndclassc
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
2970 wndclassc
.cbClsExtra
= 0;
2971 wndclassc
.cbWndExtra
= sizeof(ListBoxX
*);
2972 wndclassc
.hInstance
= hinstPlatformRes
;
2973 wndclassc
.hIcon
= NULL
;
2974 wndclassc
.hbrBackground
= NULL
;
2975 wndclassc
.lpszMenuName
= NULL
;
2976 wndclassc
.lpfnWndProc
= ListBoxX::StaticWndProc
;
2977 wndclassc
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
2978 wndclassc
.lpszClassName
= ListBoxX_ClassName
;
2979 wndclassc
.hIconSm
= 0;
2981 return ::RegisterClassEx(&wndclassc
) != 0;
2984 bool ListBoxX_Unregister() {
2985 return ::UnregisterClass(ListBoxX_ClassName
, hinstPlatformRes
) != 0;
2988 Menu::Menu() : mid(0) {
2991 void Menu::CreatePopUp() {
2993 mid
= ::CreatePopupMenu();
2996 void Menu::Destroy() {
2998 ::DestroyMenu(reinterpret_cast<HMENU
>(mid
));
3002 void Menu::Show(Point pt
, Window
&w
) {
3003 ::TrackPopupMenu(reinterpret_cast<HMENU
>(mid
),
3004 0, pt
.x
- 4, pt
.y
, 0,
3005 reinterpret_cast<HWND
>(w
.GetID()), NULL
);
3009 static bool initialisedET
= false;
3010 static bool usePerformanceCounter
= false;
3011 static LARGE_INTEGER frequency
;
3013 ElapsedTime::ElapsedTime() {
3014 if (!initialisedET
) {
3015 usePerformanceCounter
= ::QueryPerformanceFrequency(&frequency
) != 0;
3016 initialisedET
= true;
3018 if (usePerformanceCounter
) {
3019 LARGE_INTEGER timeVal
;
3020 ::QueryPerformanceCounter(&timeVal
);
3021 bigBit
= timeVal
.HighPart
;
3022 littleBit
= timeVal
.LowPart
;
3029 double ElapsedTime::Duration(bool reset
) {
3034 if (usePerformanceCounter
) {
3036 ::QueryPerformanceCounter(&lEnd
);
3037 endBigBit
= lEnd
.HighPart
;
3038 endLittleBit
= lEnd
.LowPart
;
3039 LARGE_INTEGER lBegin
;
3040 lBegin
.HighPart
= bigBit
;
3041 lBegin
.LowPart
= littleBit
;
3042 double elapsed
= lEnd
.QuadPart
- lBegin
.QuadPart
;
3043 result
= elapsed
/ static_cast<double>(frequency
.QuadPart
);
3045 endBigBit
= clock();
3047 double elapsed
= endBigBit
- bigBit
;
3048 result
= elapsed
/ CLOCKS_PER_SEC
;
3052 littleBit
= endLittleBit
;
3057 class DynamicLibraryImpl
: public DynamicLibrary
{
3061 DynamicLibraryImpl(const char *modulePath
) {
3062 h
= ::LoadLibraryA(modulePath
);
3065 virtual ~DynamicLibraryImpl() {
3070 // Use GetProcAddress to get a pointer to the relevant function.
3071 virtual Function
FindFunction(const char *name
) {
3073 // C++ standard doesn't like casts betwen function pointers and void pointers so use a union
3078 fnConv
.fp
= ::GetProcAddress(h
, name
);
3084 virtual bool IsValid() {
3089 DynamicLibrary
*DynamicLibrary::Load(const char *modulePath
) {
3090 return static_cast<DynamicLibrary
*>(new DynamicLibraryImpl(modulePath
));
3093 ColourDesired
Platform::Chrome() {
3094 return ::GetSysColor(COLOR_3DFACE
);
3097 ColourDesired
Platform::ChromeHighlight() {
3098 return ::GetSysColor(COLOR_3DHIGHLIGHT
);
3101 const char *Platform::DefaultFont() {
3105 int Platform::DefaultFontSize() {
3109 unsigned int Platform::DoubleClickTime() {
3110 return ::GetDoubleClickTime();
3113 bool Platform::MouseButtonBounce() {
3117 void Platform::DebugDisplay(const char *s
) {
3118 ::OutputDebugStringA(s
);
3121 bool Platform::IsKeyDown(int key
) {
3122 return (::GetKeyState(key
) & 0x80000000) != 0;
3125 long Platform::SendScintilla(WindowID w
, unsigned int msg
, unsigned long wParam
, long lParam
) {
3126 return ::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
, lParam
);
3129 long Platform::SendScintillaPointer(WindowID w
, unsigned int msg
, unsigned long wParam
, void *lParam
) {
3130 return ::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
,
3131 reinterpret_cast<LPARAM
>(lParam
));
3134 bool Platform::IsDBCSLeadByte(int codePage
, char ch
) {
3135 // Byte ranges found in Wikipedia articles with relevant search strings in each case
3136 unsigned char uch
= static_cast<unsigned char>(ch
);
3140 return ((uch
>= 0x81) && (uch
<= 0x9F)) ||
3141 ((uch
>= 0xE0) && (uch
<= 0xEF));
3144 return (uch
>= 0x81) && (uch
<= 0xFE);
3146 // Korean Wansung KS C-5601-1987
3147 return (uch
>= 0x81) && (uch
<= 0xFE);
3150 return (uch
>= 0x81) && (uch
<= 0xFE);
3152 // Korean Johab KS C-5601-1992
3154 ((uch
>= 0x84) && (uch
<= 0xD3)) ||
3155 ((uch
>= 0xD8) && (uch
<= 0xDE)) ||
3156 ((uch
>= 0xE0) && (uch
<= 0xF9));
3161 int Platform::DBCSCharLength(int codePage
, const char *s
) {
3162 if (codePage
== 932 || codePage
== 936 || codePage
== 949 ||
3163 codePage
== 950 || codePage
== 1361) {
3164 return Platform::IsDBCSLeadByte(codePage
, s
[0]) ? 2 : 1;
3170 int Platform::DBCSCharMaxLength() {
3174 // These are utility functions not really tied to a platform
3176 int Platform::Minimum(int a
, int b
) {
3183 int Platform::Maximum(int a
, int b
) {
3193 void Platform::DebugPrintf(const char *format
, ...) {
3196 va_start(pArguments
, format
);
3197 vsprintf(buffer
,format
,pArguments
);
3199 Platform::DebugDisplay(buffer
);
3202 void Platform::DebugPrintf(const char *, ...) {
3206 static bool assertionPopUps
= true;
3208 bool Platform::ShowAssertionPopUps(bool assertionPopUps_
) {
3209 bool ret
= assertionPopUps
;
3210 assertionPopUps
= assertionPopUps_
;
3214 void Platform::Assert(const char *c
, const char *file
, int line
) {
3216 sprintf(buffer
, "Assertion [%s] failed at %s %d", c
, file
, line
);
3217 if (assertionPopUps
) {
3218 int idButton
= ::MessageBoxA(0, buffer
, "Assertion failure",
3219 MB_ABORTRETRYIGNORE
|MB_ICONHAND
|MB_SETFOREGROUND
|MB_TASKMODAL
);
3220 if (idButton
== IDRETRY
) {
3222 } else if (idButton
== IDIGNORE
) {
3228 strcat(buffer
, "\r\n");
3229 Platform::DebugDisplay(buffer
);
3235 int Platform::Clamp(int val
, int minVal
, int maxVal
) {
3244 // GetVersionEx has been deprecated fro Windows 8.1 but called here to determine if Windows 9x.
3245 // Too dangerous to find alternate check.
3246 #pragma warning(disable: 4996)
3249 void Platform_Initialise(void *hInstance
) {
3250 OSVERSIONINFO osv
= {sizeof(OSVERSIONINFO
),0,0,0,0,TEXT("")};
3251 ::GetVersionEx(&osv
);
3252 onNT
= osv
.dwPlatformId
== VER_PLATFORM_WIN32_NT
;
3253 ::InitializeCriticalSection(&crPlatformLock
);
3254 hinstPlatformRes
= reinterpret_cast<HINSTANCE
>(hInstance
);
3255 // This may be called from DllMain, in which case the call to LoadLibrary
3256 // is bad because it can upset the DLL load order.
3258 hDLLImage
= ::LoadLibrary(TEXT("Msimg32"));
3261 AlphaBlendFn
= (AlphaBlendSig
)::GetProcAddress(hDLLImage
, "AlphaBlend");
3264 hDLLUser32
= ::LoadLibrary(TEXT("User32"));
3267 MonitorFromPointFn
= (MonitorFromPointSig
)::GetProcAddress(hDLLUser32
, "MonitorFromPoint");
3268 MonitorFromRectFn
= (MonitorFromRectSig
)::GetProcAddress(hDLLUser32
, "MonitorFromRect");
3269 GetMonitorInfoFn
= (GetMonitorInfoSig
)::GetProcAddress(hDLLUser32
, "GetMonitorInfoA");
3272 ListBoxX_Register();
3276 #pragma warning(default: 4996)
3279 void Platform_Finalise() {
3280 #if defined(USE_D2D)
3281 if (defaultRenderingParams
) {
3282 defaultRenderingParams
->Release();
3283 defaultRenderingParams
= 0;
3285 if (customClearTypeRenderingParams
) {
3286 customClearTypeRenderingParams
->Release();
3287 customClearTypeRenderingParams
= 0;
3290 if (reverseArrowCursor
!= NULL
)
3291 ::DestroyCursor(reverseArrowCursor
);
3292 ListBoxX_Unregister();
3293 ::DeleteCriticalSection(&crPlatformLock
);
3296 #ifdef SCI_NAMESPACE