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
27 #if defined(_MSC_VER) && (_MSC_VER > 1200)
37 #include "UniConversion.h"
39 #include "FontQuality.h"
41 // We want to use multi monitor functions, but via LoadLibrary etc
42 // Luckily microsoft has done the heavy lifting for us, so we'll just use their stub functions!
43 #if defined(_MSC_VER) && (_MSC_VER > 1200)
44 #define COMPILE_MULTIMON_STUBS
49 #define IDC_HAND MAKEINTRESOURCE(32649)
52 // Take care of 32/64 bit pointers
53 #ifdef GetWindowLongPtr
54 static void *PointerFromWindow(HWND hWnd
) {
55 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd
, 0));
57 static void SetWindowPointer(HWND hWnd
, void *ptr
) {
58 ::SetWindowLongPtr(hWnd
, 0, reinterpret_cast<LONG_PTR
>(ptr
));
61 static void *PointerFromWindow(HWND hWnd
) {
62 return reinterpret_cast<void *>(::GetWindowLong(hWnd
, 0));
64 static void SetWindowPointer(HWND hWnd
, void *ptr
) {
65 ::SetWindowLong(hWnd
, 0, reinterpret_cast<LONG
>(ptr
));
69 #define GWLP_USERDATA GWL_USERDATA
73 #define GWLP_WNDPROC GWL_WNDPROC
80 static LONG_PTR
SetWindowLongPtr(HWND hWnd
, int nIndex
, LONG_PTR dwNewLong
) {
81 return ::SetWindowLong(hWnd
, nIndex
, dwNewLong
);
84 static LONG_PTR
GetWindowLongPtr(HWND hWnd
, int nIndex
) {
85 return ::GetWindowLong(hWnd
, nIndex
);
89 typedef BOOL (WINAPI
*AlphaBlendSig
)(HDC
, int, int, int, int, HDC
, int, int, int, int, BLENDFUNCTION
);
91 static CRITICAL_SECTION crPlatformLock
;
92 static HINSTANCE hinstPlatformRes
= 0;
93 static bool onNT
= false;
94 static HMODULE hDLLImage
= 0;
95 static AlphaBlendSig AlphaBlendFn
= 0;
96 static HCURSOR reverseArrowCursor
= NULL
;
103 using namespace Scintilla
;
106 Point
Point::FromLong(long lpoint
) {
107 return Point(static_cast<short>(LOWORD(lpoint
)), static_cast<short>(HIWORD(lpoint
)));
110 static RECT
RectFromPRectangle(PRectangle prc
) {
111 RECT rc
= {static_cast<LONG
>(prc
.left
), static_cast<LONG
>(prc
.top
),
112 static_cast<LONG
>(prc
.right
), static_cast<LONG
>(prc
.bottom
)};
117 IDWriteFactory
*pIDWriteFactory
= 0;
118 ID2D1Factory
*pD2DFactory
= 0;
121 static bool triedLoadingD2D
= false;
122 static HMODULE hDLLD2D
= 0;
123 static HMODULE hDLLDWrite
= 0;
124 if (!triedLoadingD2D
) {
125 typedef HRESULT (WINAPI
*D2D1CFSig
)(D2D1_FACTORY_TYPE factoryType
, REFIID riid
,
126 CONST D2D1_FACTORY_OPTIONS
*pFactoryOptions
, IUnknown
**factory
);
127 typedef HRESULT (WINAPI
*DWriteCFSig
)(DWRITE_FACTORY_TYPE factoryType
, REFIID iid
,
130 hDLLD2D
= ::LoadLibrary(TEXT("D2D1.DLL"));
132 D2D1CFSig fnD2DCF
= (D2D1CFSig
)::GetProcAddress(hDLLD2D
, "D2D1CreateFactory");
134 // A single threaded factory as Scintilla always draw on the GUI thread
135 fnD2DCF(D2D1_FACTORY_TYPE_SINGLE_THREADED
,
136 __uuidof(ID2D1Factory
),
138 reinterpret_cast<IUnknown
**>(&pD2DFactory
));
141 hDLLDWrite
= ::LoadLibrary(TEXT("DWRITE.DLL"));
143 DWriteCFSig fnDWCF
= (DWriteCFSig
)::GetProcAddress(hDLLDWrite
, "DWriteCreateFactory");
145 fnDWCF(DWRITE_FACTORY_TYPE_SHARED
,
146 __uuidof(IDWriteFactory
),
147 reinterpret_cast<IUnknown
**>(&pIDWriteFactory
));
151 triedLoadingD2D
= true;
152 return pIDWriteFactory
&& pD2DFactory
;
156 struct FormatAndMetrics
{
160 IDWriteTextFormat
*pTextFormat
;
165 FLOAT yInternalLeading
;
166 FormatAndMetrics(HFONT hfont_
, int extraFontFlag_
) :
167 technology(SCWIN_TECH_GDI
), hfont(hfont_
),
171 extraFontFlag(extraFontFlag_
), yAscent(2), yDescent(1), yInternalLeading(0) {
174 FormatAndMetrics(IDWriteTextFormat
*pTextFormat_
, int extraFontFlag_
, FLOAT yAscent_
, FLOAT yDescent_
, FLOAT yInternalLeading_
) :
175 technology(SCWIN_TECH_DIRECTWRITE
), hfont(0), pTextFormat(pTextFormat_
), extraFontFlag(extraFontFlag_
), yAscent(yAscent_
), yDescent(yDescent_
), yInternalLeading(yInternalLeading_
) {
178 ~FormatAndMetrics() {
180 ::DeleteObject(hfont
);
183 pTextFormat
->Release();
189 yInternalLeading
= 0;
194 HFONT
FormatAndMetrics::HFont() {
196 memset(&lf
, 0, sizeof(lf
));
198 if (technology
== SCWIN_TECH_GDI
) {
199 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
203 HRESULT hr
= pTextFormat
->GetFontFamilyName(lf
.lfFaceName
, LF_FACESIZE
);
204 if (!SUCCEEDED(hr
)) {
207 lf
.lfWeight
= pTextFormat
->GetFontWeight();
208 lf
.lfItalic
= pTextFormat
->GetFontStyle() == DWRITE_FONT_STYLE_ITALIC
;
209 lf
.lfHeight
= -static_cast<int>(pTextFormat
->GetFontSize());
212 if (0 == ::GetObjectW(hfont
, sizeof(lf
), &lf
)) {
216 return ::CreateFontIndirectW(&lf
);
219 #ifndef CLEARTYPE_QUALITY
220 #define CLEARTYPE_QUALITY 5
223 static BYTE
Win32MapFontQuality(int extraFontFlag
) {
224 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
226 case SC_EFF_QUALITY_NON_ANTIALIASED
:
227 return NONANTIALIASED_QUALITY
;
229 case SC_EFF_QUALITY_ANTIALIASED
:
230 return ANTIALIASED_QUALITY
;
232 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
233 return CLEARTYPE_QUALITY
;
236 return SC_EFF_QUALITY_DEFAULT
;
241 static D2D1_TEXT_ANTIALIAS_MODE
DWriteMapFontQuality(int extraFontFlag
) {
242 switch (extraFontFlag
& SC_EFF_QUALITY_MASK
) {
244 case SC_EFF_QUALITY_NON_ANTIALIASED
:
245 return D2D1_TEXT_ANTIALIAS_MODE_ALIASED
;
247 case SC_EFF_QUALITY_ANTIALIASED
:
248 return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE
;
250 case SC_EFF_QUALITY_LCD_OPTIMIZED
:
251 return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
;
254 return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
;
259 static void SetLogFont(LOGFONTA
&lf
, const char *faceName
, int characterSet
, float size
, int weight
, bool italic
, int extraFontFlag
) {
260 memset(&lf
, 0, sizeof(lf
));
261 // The negative is to allow for leading
262 lf
.lfHeight
= -(abs(static_cast<int>(size
+ 0.5)));
263 lf
.lfWeight
= weight
;
264 lf
.lfItalic
= static_cast<BYTE
>(italic
? 1 : 0);
265 lf
.lfCharSet
= static_cast<BYTE
>(characterSet
);
266 lf
.lfQuality
= Win32MapFontQuality(extraFontFlag
);
267 strncpy(lf
.lfFaceName
, faceName
, sizeof(lf
.lfFaceName
));
271 * Create a hash from the parameters for a font to allow easy checking for identity.
272 * If one font is the same as another, its hash will be the same, but if the hash is the
273 * same then they may still be different.
275 static int HashFont(const FontParameters
&fp
) {
277 static_cast<int>(fp
.size
) ^
278 (fp
.characterSet
<< 10) ^
279 ((fp
.extraFontFlag
& SC_EFF_QUALITY_MASK
) << 9) ^
280 ((fp
.weight
/100) << 12) ^
281 (fp
.italic
? 0x20000000 : 0) ^
282 (fp
.technology
<< 15) ^
286 class FontCached
: Font
{
293 FontCached(const FontParameters
&fp
);
295 bool SameAs(const FontParameters
&fp
);
296 virtual void Release();
298 static FontCached
*first
;
300 static FontID
FindOrCreate(const FontParameters
&fp
);
301 static void ReleaseId(FontID fid_
);
304 FontCached
*FontCached::first
= 0;
306 FontCached::FontCached(const FontParameters
&fp
) :
307 next(0), usage(0), size(1.0), hash(0) {
308 SetLogFont(lf
, fp
.faceName
, fp
.characterSet
, fp
.size
, fp
.weight
, fp
.italic
, fp
.extraFontFlag
);
309 technology
= fp
.technology
;
312 if (technology
== SCWIN_TECH_GDI
) {
313 HFONT hfont
= ::CreateFontIndirectA(&lf
);
314 fid
= reinterpret_cast<void *>(new FormatAndMetrics(hfont
, fp
.extraFontFlag
));
317 IDWriteTextFormat
*pTextFormat
;
318 const int faceSize
= 200;
319 WCHAR wszFace
[faceSize
];
320 UTF16FromUTF8(fp
.faceName
, static_cast<unsigned int>(strlen(fp
.faceName
))+1, wszFace
, faceSize
);
321 FLOAT fHeight
= fp
.size
;
322 DWRITE_FONT_STYLE style
= fp
.italic
? DWRITE_FONT_STYLE_ITALIC
: DWRITE_FONT_STYLE_NORMAL
;
323 HRESULT hr
= pIDWriteFactory
->CreateTextFormat(wszFace
, NULL
,
324 static_cast<DWRITE_FONT_WEIGHT
>(fp
.weight
),
326 DWRITE_FONT_STRETCH_NORMAL
, fHeight
, L
"en-us", &pTextFormat
);
328 pTextFormat
->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP
);
330 const int maxLines
= 2;
331 DWRITE_LINE_METRICS lineMetrics
[maxLines
];
332 UINT32 lineCount
= 0;
333 FLOAT yAscent
= 1.0f
;
334 FLOAT yDescent
= 1.0f
;
335 FLOAT yInternalLeading
= 0.0f
;
336 IDWriteTextLayout
*pTextLayout
= 0;
337 hr
= pIDWriteFactory
->CreateTextLayout(L
"X", 1, pTextFormat
,
338 100.0f
, 100.0f
, &pTextLayout
);
340 hr
= pTextLayout
->GetLineMetrics(lineMetrics
, maxLines
, &lineCount
);
342 yAscent
= lineMetrics
[0].baseline
;
343 yDescent
= lineMetrics
[0].height
- lineMetrics
[0].baseline
;
346 hr
= pTextLayout
->GetFontSize(0, &emHeight
);
348 yInternalLeading
= lineMetrics
[0].height
- emHeight
;
351 pTextLayout
->Release();
353 fid
= reinterpret_cast<void *>(new FormatAndMetrics(pTextFormat
, fp
.extraFontFlag
, yAscent
, yDescent
, yInternalLeading
));
360 bool FontCached::SameAs(const FontParameters
&fp
) {
363 (lf
.lfWeight
== fp
.weight
) &&
364 (lf
.lfItalic
== static_cast<BYTE
>(fp
.italic
? 1 : 0)) &&
365 (lf
.lfCharSet
== fp
.characterSet
) &&
366 (lf
.lfQuality
== Win32MapFontQuality(fp
.extraFontFlag
)) &&
367 (technology
== fp
.technology
) &&
368 0 == strcmp(lf
.lfFaceName
,fp
.faceName
);
371 void FontCached::Release() {
372 delete reinterpret_cast<FormatAndMetrics
*>(fid
);
376 FontID
FontCached::FindOrCreate(const FontParameters
&fp
) {
378 ::EnterCriticalSection(&crPlatformLock
);
379 int hashFind
= HashFont(fp
);
380 for (FontCached
*cur
=first
; cur
; cur
=cur
->next
) {
381 if ((cur
->hash
== hashFind
) &&
388 FontCached
*fc
= new FontCached(fp
);
395 ::LeaveCriticalSection(&crPlatformLock
);
399 void FontCached::ReleaseId(FontID fid_
) {
400 ::EnterCriticalSection(&crPlatformLock
);
401 FontCached
**pcur
=&first
;
402 for (FontCached
*cur
=first
; cur
; cur
=cur
->next
) {
403 if (cur
->fid
== fid_
) {
405 if (cur
->usage
== 0) {
415 ::LeaveCriticalSection(&crPlatformLock
);
427 void Font::Create(const FontParameters
&fp
) {
430 fid
= FontCached::FindOrCreate(fp
);
433 void Font::Release() {
435 FontCached::ReleaseId(fid
);
439 // Buffer to hold strings and string position arrays without always allocating on heap.
440 // May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer
441 // when less than safe size otherwise allocate on heap and free automatically.
442 template<typename T
, int lengthStandard
>
444 T bufferStandard
[lengthStandard
];
447 VarBuffer(size_t length
) : buffer(0) {
448 if (length
> lengthStandard
) {
449 buffer
= new T
[length
];
451 buffer
= bufferStandard
;
455 if (buffer
!= bufferStandard
) {
462 const int stackBufferLength
= 10000;
463 class TextWide
: public VarBuffer
<wchar_t, stackBufferLength
> {
466 TextWide(const char *s
, int len
, bool unicodeMode
, int codePage
=0) :
467 VarBuffer
<wchar_t, stackBufferLength
>(len
) {
469 tlen
= UTF16FromUTF8(s
, len
, buffer
, len
);
471 // Support Asian string display in 9x English
472 tlen
= ::MultiByteToWideChar(codePage
, 0, s
, len
, buffer
, len
);
476 typedef VarBuffer
<XYPOSITION
, stackBufferLength
> TextPositions
;
479 namespace Scintilla
{
482 class SurfaceGDI
: public Surface
{
498 // If 9x OS and current code page is same as ANSI code page.
501 void BrushColor(ColourDesired back
);
502 void SetFont(Font
&font_
);
504 // Private so SurfaceGDI objects can not be copied
505 SurfaceGDI(const SurfaceGDI
&);
506 SurfaceGDI
&operator=(const SurfaceGDI
&);
509 virtual ~SurfaceGDI();
511 void Init(WindowID wid
);
512 void Init(SurfaceID sid
, WindowID wid
);
513 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
517 void PenColour(ColourDesired fore
);
519 int DeviceHeightFont(int points
);
520 void MoveTo(int x_
, int y_
);
521 void LineTo(int x_
, int y_
);
522 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
523 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
524 void FillRectangle(PRectangle rc
, ColourDesired back
);
525 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
526 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
527 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
528 ColourDesired outline
, int alphaOutline
, int flags
);
529 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
530 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
531 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
533 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
534 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
535 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
536 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
537 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
538 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
539 XYPOSITION
WidthChar(Font
&font_
, char ch
);
540 XYPOSITION
Ascent(Font
&font_
);
541 XYPOSITION
Descent(Font
&font_
);
542 XYPOSITION
InternalLeading(Font
&font_
);
543 XYPOSITION
ExternalLeading(Font
&font_
);
544 XYPOSITION
Height(Font
&font_
);
545 XYPOSITION
AverageCharWidth(Font
&font_
);
547 void SetClip(PRectangle rc
);
548 void FlushCachedState();
550 void SetUnicodeMode(bool unicodeMode_
);
551 void SetDBCSMode(int codePage_
);
555 } //namespace Scintilla
558 SurfaceGDI::SurfaceGDI() :
560 hdc(0), hdcOwned(false),
562 brush(0), brushOld(0),
564 bitmap(0), bitmapOld(0) {
565 // Windows 9x has only a 16 bit coordinate system so break after 30000 pixels
566 maxWidthMeasure
= IsNT() ? INT_MAX
: 30000;
567 // There appears to be a 16 bit string length limit in GDI on NT and a limit of
568 // 8192 characters on Windows 95.
569 maxLenText
= IsNT() ? 65535 : 8192;
572 win9xACPSame
= false;
575 SurfaceGDI::~SurfaceGDI() {
579 void SurfaceGDI::Release() {
581 ::SelectObject(reinterpret_cast<HDC
>(hdc
), penOld
);
587 ::SelectObject(reinterpret_cast<HDC
>(hdc
), brushOld
);
588 ::DeleteObject(brush
);
593 // Fonts are not deleted as they are owned by a Font object
594 ::SelectObject(reinterpret_cast<HDC
>(hdc
), fontOld
);
599 ::SelectObject(reinterpret_cast<HDC
>(hdc
), bitmapOld
);
600 ::DeleteObject(bitmap
);
605 ::DeleteDC(reinterpret_cast<HDC
>(hdc
));
611 bool SurfaceGDI::Initialised() {
615 void SurfaceGDI::Init(WindowID
) {
617 hdc
= ::CreateCompatibleDC(NULL
);
619 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
622 void SurfaceGDI::Init(SurfaceID sid
, WindowID
) {
624 hdc
= reinterpret_cast<HDC
>(sid
);
625 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
628 void SurfaceGDI::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
630 hdc
= ::CreateCompatibleDC(static_cast<SurfaceGDI
*>(surface_
)->hdc
);
632 bitmap
= ::CreateCompatibleBitmap(static_cast<SurfaceGDI
*>(surface_
)->hdc
, width
, height
);
633 bitmapOld
= static_cast<HBITMAP
>(::SelectObject(hdc
, bitmap
));
634 ::SetTextAlign(reinterpret_cast<HDC
>(hdc
), TA_BASELINE
);
637 void SurfaceGDI::PenColour(ColourDesired fore
) {
639 ::SelectObject(hdc
, penOld
);
644 pen
= ::CreatePen(0,1,fore
.AsLong());
645 penOld
= static_cast<HPEN
>(::SelectObject(reinterpret_cast<HDC
>(hdc
), pen
));
648 void SurfaceGDI::BrushColor(ColourDesired back
) {
650 ::SelectObject(hdc
, brushOld
);
651 ::DeleteObject(brush
);
655 // Only ever want pure, non-dithered brushes
656 ColourDesired colourNearest
= ::GetNearestColor(hdc
, back
.AsLong());
657 brush
= ::CreateSolidBrush(colourNearest
.AsLong());
658 brushOld
= static_cast<HBRUSH
>(::SelectObject(hdc
, brush
));
661 void SurfaceGDI::SetFont(Font
&font_
) {
662 if (font_
.GetID() != font
) {
663 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
664 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_GDI
);
666 ::SelectObject(hdc
, pfm
->hfont
);
668 fontOld
= static_cast<HFONT
>(::SelectObject(hdc
, pfm
->hfont
));
670 font
= reinterpret_cast<HFONT
>(pfm
->hfont
);
674 int SurfaceGDI::LogPixelsY() {
675 return ::GetDeviceCaps(hdc
, LOGPIXELSY
);
678 int SurfaceGDI::DeviceHeightFont(int points
) {
679 return ::MulDiv(points
, LogPixelsY(), 72);
682 void SurfaceGDI::MoveTo(int x_
, int y_
) {
683 ::MoveToEx(hdc
, x_
, y_
, 0);
686 void SurfaceGDI::LineTo(int x_
, int y_
) {
687 ::LineTo(hdc
, x_
, y_
);
690 void SurfaceGDI::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
693 std::vector
<POINT
> outline
;
694 for (int i
=0;i
<npts
;i
++) {
695 POINT pt
= {static_cast<LONG
>(pts
[i
].x
), static_cast<LONG
>(pts
[i
].y
)};
696 outline
.push_back(pt
);
698 ::Polygon(hdc
, &outline
[0], npts
);
701 void SurfaceGDI::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
704 ::Rectangle(hdc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
707 void SurfaceGDI::FillRectangle(PRectangle rc
, ColourDesired back
) {
708 // Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
709 // There is no need to allocate a brush either.
710 RECT rcw
= RectFromPRectangle(rc
);
711 ::SetBkColor(hdc
, back
.AsLong());
712 ::ExtTextOut(hdc
, rc
.left
, rc
.top
, ETO_OPAQUE
, &rcw
, TEXT(""), 0, NULL
);
715 void SurfaceGDI::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
717 if (static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
)
718 br
= ::CreatePatternBrush(static_cast<SurfaceGDI
&>(surfacePattern
).bitmap
);
719 else // Something is wrong so display in red
720 br
= ::CreateSolidBrush(RGB(0xff, 0, 0));
721 RECT rcw
= RectFromPRectangle(rc
);
722 ::FillRect(hdc
, &rcw
, br
);
726 void SurfaceGDI::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
731 rc
.right
- 1, rc
.bottom
,
735 // Plot a point into a DWORD buffer symetrically to all 4 qudrants
736 static void AllFour(DWORD
*pixels
, int width
, int height
, int x
, int y
, DWORD val
) {
737 pixels
[y
*width
+x
] = val
;
738 pixels
[y
*width
+width
-1-x
] = val
;
739 pixels
[(height
-1-y
)*width
+x
] = val
;
740 pixels
[(height
-1-y
)*width
+width
-1-x
] = val
;
744 #define AC_SRC_OVER 0x00
747 #define AC_SRC_ALPHA 0x01
750 static DWORD
dwordFromBGRA(byte b
, byte g
, byte r
, byte a
) {
755 converter
.pixVal
[0] = b
;
756 converter
.pixVal
[1] = g
;
757 converter
.pixVal
[2] = r
;
758 converter
.pixVal
[3] = a
;
759 return converter
.val
;
762 void SurfaceGDI::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
763 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
764 if (AlphaBlendFn
&& rc
.Width() > 0) {
765 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
766 int width
= rc
.Width();
767 int height
= rc
.Height();
768 // Ensure not distorted too much by corners when small
769 cornerSize
= Platform::Minimum(cornerSize
, (Platform::Minimum(width
, height
) / 2) - 2);
770 BITMAPINFO bpih
= {sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0};
772 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
773 DIB_RGB_COLORS
, &image
, NULL
, 0);
775 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
777 DWORD valEmpty
= dwordFromBGRA(0,0,0,0);
778 DWORD valFill
= dwordFromBGRA(
779 static_cast<byte
>(GetBValue(fill
.AsLong()) * alphaFill
/ 255),
780 static_cast<byte
>(GetGValue(fill
.AsLong()) * alphaFill
/ 255),
781 static_cast<byte
>(GetRValue(fill
.AsLong()) * alphaFill
/ 255),
782 static_cast<byte
>(alphaFill
));
783 DWORD valOutline
= dwordFromBGRA(
784 static_cast<byte
>(GetBValue(outline
.AsLong()) * alphaOutline
/ 255),
785 static_cast<byte
>(GetGValue(outline
.AsLong()) * alphaOutline
/ 255),
786 static_cast<byte
>(GetRValue(outline
.AsLong()) * alphaOutline
/ 255),
787 static_cast<byte
>(alphaOutline
));
788 DWORD
*pixels
= reinterpret_cast<DWORD
*>(image
);
789 for (int y
=0; y
<height
; y
++) {
790 for (int x
=0; x
<width
; x
++) {
791 if ((x
==0) || (x
==width
-1) || (y
== 0) || (y
== height
-1)) {
792 pixels
[y
*width
+x
] = valOutline
;
794 pixels
[y
*width
+x
] = valFill
;
798 for (int c
=0;c
<cornerSize
; c
++) {
799 for (int x
=0;x
<c
+1; x
++) {
800 AllFour(pixels
, width
, height
, x
, c
-x
, valEmpty
);
803 for (int x
=1;x
<cornerSize
; x
++) {
804 AllFour(pixels
, width
, height
, x
, cornerSize
-x
, valOutline
);
807 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
809 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), rc
.left
, rc
.top
, width
, height
, hMemDC
, 0, 0, width
, height
, merge
);
811 SelectBitmap(hMemDC
, hbmOld
);
812 ::DeleteObject(hbmMem
);
816 RECT rcw
= RectFromPRectangle(rc
);
817 FrameRect(hdc
, &rcw
, brush
);
821 void SurfaceGDI::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
822 if (AlphaBlendFn
&& rc
.Width() > 0) {
823 HDC hMemDC
= ::CreateCompatibleDC(reinterpret_cast<HDC
>(hdc
));
824 if (rc
.Width() > width
)
825 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
826 rc
.right
= rc
.left
+ width
;
827 if (rc
.Height() > height
)
828 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
829 rc
.bottom
= rc
.top
+ height
;
831 BITMAPINFO bpih
= {sizeof(BITMAPINFOHEADER
), width
, height
, 1, 32, BI_RGB
, 0, 0, 0, 0, 0};
832 unsigned char *image
= 0;
833 HBITMAP hbmMem
= CreateDIBSection(reinterpret_cast<HDC
>(hMemDC
), &bpih
,
834 DIB_RGB_COLORS
, reinterpret_cast<void **>(&image
), NULL
, 0);
835 HBITMAP hbmOld
= SelectBitmap(hMemDC
, hbmMem
);
837 for (int y
=height
-1; y
>=0; y
--) {
838 for (int x
=0; x
<width
; x
++) {
839 unsigned char *pixel
= image
+ (y
*width
+x
) * 4;
840 unsigned char alpha
= pixelsImage
[3];
841 // Input is RGBA, output is BGRA with premultiplied alpha
842 pixel
[2] = (*pixelsImage
++) * alpha
/ 255;
843 pixel
[1] = (*pixelsImage
++) * alpha
/ 255;
844 pixel
[0] = (*pixelsImage
++) * alpha
/ 255;
845 pixel
[3] = *pixelsImage
++;
849 BLENDFUNCTION merge
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
851 AlphaBlendFn(reinterpret_cast<HDC
>(hdc
), rc
.left
, rc
.top
, rc
.Width(), rc
.Height(), hMemDC
, 0, 0, width
, height
, merge
);
853 SelectBitmap(hMemDC
, hbmOld
);
854 ::DeleteObject(hbmMem
);
860 void SurfaceGDI::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
863 ::Ellipse(hdc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
866 void SurfaceGDI::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
868 rc
.left
, rc
.top
, rc
.Width(), rc
.Height(),
869 static_cast<SurfaceGDI
&>(surfaceSource
).hdc
, from
.x
, from
.y
, SRCCOPY
);
872 typedef VarBuffer
<int, stackBufferLength
> TextPositionsI
;
874 void SurfaceGDI::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
) {
876 RECT rcw
= RectFromPRectangle(rc
);
881 // Text drawing may fail if the text is too big.
882 // If it does fail, slice up into segments and draw each segment.
883 const int maxSegmentLength
= 0x200;
885 if ((!unicodeMode
) && (IsNT() || (codePage
==0) || win9xACPSame
)) {
887 int lenDraw
= Platform::Minimum(len
, maxLenText
);
888 if (!::ExtTextOutA(hdc
, x
, ybase
, fuOptions
, &rcw
, s
, lenDraw
, NULL
)) {
889 while (lenDraw
> pos
) {
890 int seglen
= Platform::Minimum(maxSegmentLength
, lenDraw
- pos
);
891 if (!::ExtTextOutA(hdc
, x
, ybase
, fuOptions
, &rcw
, s
+pos
, seglen
, NULL
)) {
892 PLATFORM_ASSERT(false);
895 ::GetTextExtentPoint32A(hdc
, s
+pos
, seglen
, &sz
);
902 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
903 if (!::ExtTextOutW(hdc
, x
, ybase
, fuOptions
, &rcw
, tbuf
.buffer
, tbuf
.tlen
, NULL
)) {
904 while (tbuf
.tlen
> pos
) {
905 int seglen
= Platform::Minimum(maxSegmentLength
, tbuf
.tlen
- pos
);
906 if (!::ExtTextOutW(hdc
, x
, ybase
, fuOptions
, &rcw
, tbuf
.buffer
+pos
, seglen
, NULL
)) {
907 PLATFORM_ASSERT(false);
910 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
+pos
, seglen
, &sz
);
918 void SurfaceGDI::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
919 ColourDesired fore
, ColourDesired back
) {
920 ::SetTextColor(hdc
, fore
.AsLong());
921 ::SetBkColor(hdc
, back
.AsLong());
922 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
925 void SurfaceGDI::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
926 ColourDesired fore
, ColourDesired back
) {
927 ::SetTextColor(hdc
, fore
.AsLong());
928 ::SetBkColor(hdc
, back
.AsLong());
929 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
932 void SurfaceGDI::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
933 ColourDesired fore
) {
934 // Avoid drawing spaces in transparent mode
935 for (int i
=0;i
<len
;i
++) {
937 ::SetTextColor(hdc
, fore
.AsLong());
938 ::SetBkMode(hdc
, TRANSPARENT
);
939 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
940 ::SetBkMode(hdc
, OPAQUE
);
946 XYPOSITION
SurfaceGDI::WidthText(Font
&font_
, const char *s
, int len
) {
949 if ((!unicodeMode
) && (IsNT() || (codePage
==0) || win9xACPSame
)) {
950 ::GetTextExtentPoint32A(hdc
, s
, Platform::Minimum(len
, maxLenText
), &sz
);
952 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
953 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &sz
);
958 void SurfaceGDI::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
963 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
964 TextPositionsI
poses(tbuf
.tlen
);
966 if (!::GetTextExtentExPointW(hdc
, tbuf
.buffer
, tbuf
.tlen
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
967 // Likely to have failed because on Windows 9x where function not available
968 // So measure the character widths by measuring each initial substring
969 // Turns a linear operation into a qudratic but seems fast enough on test files
970 for (int widthSS
=0; widthSS
< tbuf
.tlen
; widthSS
++) {
971 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, widthSS
+1, &sz
);
972 poses
.buffer
[widthSS
] = sz
.cx
;
975 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
977 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
980 unsigned char uch
= us
[i
];
981 unsigned int lenChar
= 1;
982 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
985 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
987 } else if (uch
>= (0x80)) {
990 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
991 positions
[i
++] = poses
.buffer
[ui
];
997 lastPos
= positions
[i
-1];
999 positions
[i
++] = lastPos
;
1001 } else if (IsNT() || (codePage
==0) || win9xACPSame
) {
1002 // Zero positions to avoid random behaviour on failure.
1003 memset(positions
, 0, len
* sizeof(*positions
));
1004 // len may be larger than platform supports so loop over segments small enough for platform
1005 int startOffset
= 0;
1007 int lenBlock
= Platform::Minimum(len
, maxLenText
);
1008 TextPositionsI
poses(len
);
1009 if (!::GetTextExtentExPointA(hdc
, s
, lenBlock
, maxWidthMeasure
, &fit
, poses
.buffer
, &sz
)) {
1010 // Eeek - a NULL DC or other foolishness could cause this.
1012 } else if (fit
< lenBlock
) {
1013 // For some reason, such as an incomplete DBCS character
1014 // Not all the positions are filled in so make them equal to end.
1016 poses
.buffer
[fit
++] = 0;
1017 for (int i
= fit
;i
<lenBlock
;i
++)
1018 poses
.buffer
[i
] = poses
.buffer
[fit
-1];
1020 for (int i
=0;i
<lenBlock
;i
++)
1021 positions
[i
] = poses
.buffer
[i
] + startOffset
;
1022 startOffset
= poses
.buffer
[lenBlock
-1];
1024 positions
+= lenBlock
;
1028 // Support Asian string display in 9x English
1029 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
1030 TextPositionsI
poses(tbuf
.tlen
);
1031 for (int widthSS
=0; widthSS
<tbuf
.tlen
; widthSS
++) {
1032 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, widthSS
+1, &sz
);
1033 poses
.buffer
[widthSS
] = sz
.cx
;
1037 for (int i
=0;i
<len
;) {
1038 if (::IsDBCSLeadByteEx(codePage
, s
[i
])) {
1039 positions
[i
] = poses
.buffer
[ui
];
1040 positions
[i
+1] = poses
.buffer
[ui
];
1043 positions
[i
] = poses
.buffer
[ui
];
1052 XYPOSITION
SurfaceGDI::WidthChar(Font
&font_
, char ch
) {
1055 ::GetTextExtentPoint32A(hdc
, &ch
, 1, &sz
);
1059 XYPOSITION
SurfaceGDI::Ascent(Font
&font_
) {
1062 ::GetTextMetrics(hdc
, &tm
);
1066 XYPOSITION
SurfaceGDI::Descent(Font
&font_
) {
1069 ::GetTextMetrics(hdc
, &tm
);
1070 return tm
.tmDescent
;
1073 XYPOSITION
SurfaceGDI::InternalLeading(Font
&font_
) {
1076 ::GetTextMetrics(hdc
, &tm
);
1077 return tm
.tmInternalLeading
;
1080 XYPOSITION
SurfaceGDI::ExternalLeading(Font
&font_
) {
1083 ::GetTextMetrics(hdc
, &tm
);
1084 return tm
.tmExternalLeading
;
1087 XYPOSITION
SurfaceGDI::Height(Font
&font_
) {
1090 ::GetTextMetrics(hdc
, &tm
);
1094 XYPOSITION
SurfaceGDI::AverageCharWidth(Font
&font_
) {
1097 ::GetTextMetrics(hdc
, &tm
);
1098 return tm
.tmAveCharWidth
;
1101 void SurfaceGDI::SetClip(PRectangle rc
) {
1102 ::IntersectClipRect(hdc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1105 void SurfaceGDI::FlushCachedState() {
1111 void SurfaceGDI::SetUnicodeMode(bool unicodeMode_
) {
1112 unicodeMode
=unicodeMode_
;
1115 void SurfaceGDI::SetDBCSMode(int codePage_
) {
1116 // No action on window as automatically handled by system.
1117 codePage
= codePage_
;
1118 win9xACPSame
= !IsNT() && ((unsigned int)codePage
== ::GetACP());
1121 #if defined(USE_D2D)
1123 #ifdef SCI_NAMESPACE
1124 namespace Scintilla
{
1127 class SurfaceD2D
: public Surface
{
1133 ID2D1RenderTarget
*pRenderTarget
;
1134 bool ownRenderTarget
;
1137 IDWriteTextFormat
*pTextFormat
;
1140 FLOAT yInternalLeading
;
1142 ID2D1SolidColorBrush
*pBrush
;
1148 void SetFont(Font
&font_
);
1150 // Private so SurfaceD2D objects can not be copied
1151 SurfaceD2D(const SurfaceD2D
&);
1152 SurfaceD2D
&operator=(const SurfaceD2D
&);
1155 virtual ~SurfaceD2D();
1158 void Init(WindowID wid
);
1159 void Init(SurfaceID sid
, WindowID wid
);
1160 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
1165 HRESULT
FlushDrawing();
1167 void PenColour(ColourDesired fore
);
1168 void D2DPenColour(ColourDesired fore
, int alpha
=255);
1170 int DeviceHeightFont(int points
);
1171 void MoveTo(int x_
, int y_
);
1172 void LineTo(int x_
, int y_
);
1173 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
1174 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1175 void FillRectangle(PRectangle rc
, ColourDesired back
);
1176 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
1177 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1178 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1179 ColourDesired outline
, int alphaOutline
, int flags
);
1180 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
1181 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
1182 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
1184 void DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT fuOptions
);
1185 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1186 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
1187 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
1188 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
1189 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
1190 XYPOSITION
WidthChar(Font
&font_
, char ch
);
1191 XYPOSITION
Ascent(Font
&font_
);
1192 XYPOSITION
Descent(Font
&font_
);
1193 XYPOSITION
InternalLeading(Font
&font_
);
1194 XYPOSITION
ExternalLeading(Font
&font_
);
1195 XYPOSITION
Height(Font
&font_
);
1196 XYPOSITION
AverageCharWidth(Font
&font_
);
1198 void SetClip(PRectangle rc
);
1199 void FlushCachedState();
1201 void SetUnicodeMode(bool unicodeMode_
);
1202 void SetDBCSMode(int codePage_
);
1205 #ifdef SCI_NAMESPACE
1206 } //namespace Scintilla
1209 SurfaceD2D::SurfaceD2D() :
1215 pRenderTarget
= NULL
;
1216 ownRenderTarget
= false;
1219 // From selected font
1223 yInternalLeading
= 0;
1232 SurfaceD2D::~SurfaceD2D() {
1236 void SurfaceD2D::Release() {
1241 if (pRenderTarget
) {
1242 while (clipsActive
) {
1243 pRenderTarget
->PopAxisAlignedClip();
1246 if (ownRenderTarget
) {
1247 pRenderTarget
->Release();
1253 void SurfaceD2D::SetScale() {
1254 HDC hdcMeasure
= ::CreateCompatibleDC(NULL
);
1255 logPixelsY
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSY
);
1256 dpiScaleX
= ::GetDeviceCaps(hdcMeasure
, LOGPIXELSX
) / 96.0f
;
1257 dpiScaleY
= logPixelsY
/ 96.0f
;
1258 ::DeleteDC(hdcMeasure
);
1261 bool SurfaceD2D::Initialised() {
1262 return pRenderTarget
!= 0;
1265 HRESULT
SurfaceD2D::FlushDrawing() {
1266 return pRenderTarget
->Flush();
1269 void SurfaceD2D::Init(WindowID
/* wid */) {
1274 void SurfaceD2D::Init(SurfaceID sid
, WindowID
) {
1277 pRenderTarget
= reinterpret_cast<ID2D1RenderTarget
*>(sid
);
1280 void SurfaceD2D::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID
) {
1283 SurfaceD2D
*psurfOther
= static_cast<SurfaceD2D
*>(surface_
);
1284 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= NULL
;
1285 D2D1_SIZE_F desiredSize
= D2D1::SizeF(width
, height
);
1286 D2D1_PIXEL_FORMAT desiredFormat
= psurfOther
->pRenderTarget
->GetPixelFormat();
1287 desiredFormat
.alphaMode
= D2D1_ALPHA_MODE_IGNORE
;
1288 HRESULT hr
= psurfOther
->pRenderTarget
->CreateCompatibleRenderTarget(
1289 &desiredSize
, NULL
, &desiredFormat
, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE
, &pCompatibleRenderTarget
);
1290 if (SUCCEEDED(hr
)) {
1291 pRenderTarget
= pCompatibleRenderTarget
;
1292 pRenderTarget
->BeginDraw();
1293 ownRenderTarget
= true;
1297 void SurfaceD2D::PenColour(ColourDesired fore
) {
1301 void SurfaceD2D::D2DPenColour(ColourDesired fore
, int alpha
) {
1302 if (pRenderTarget
) {
1304 col
.r
= (fore
.AsLong() & 0xff) / 255.0;
1305 col
.g
= ((fore
.AsLong() & 0xff00) >> 8) / 255.0;
1306 col
.b
= (fore
.AsLong() >> 16) / 255.0;
1307 col
.a
= alpha
/ 255.0;
1309 pBrush
->SetColor(col
);
1311 HRESULT hr
= pRenderTarget
->CreateSolidColorBrush(col
, &pBrush
);
1312 if (!SUCCEEDED(hr
) && pBrush
) {
1320 void SurfaceD2D::SetFont(Font
&font_
) {
1321 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font_
.GetID());
1322 PLATFORM_ASSERT(pfm
->technology
== SCWIN_TECH_DIRECTWRITE
);
1323 pTextFormat
= pfm
->pTextFormat
;
1324 yAscent
= pfm
->yAscent
;
1325 yDescent
= pfm
->yDescent
;
1326 yInternalLeading
= pfm
->yInternalLeading
;
1327 if (pRenderTarget
) {
1328 pRenderTarget
->SetTextAntialiasMode(DWriteMapFontQuality(pfm
->extraFontFlag
));
1332 int SurfaceD2D::LogPixelsY() {
1336 int SurfaceD2D::DeviceHeightFont(int points
) {
1337 return ::MulDiv(points
, LogPixelsY(), 72);
1340 void SurfaceD2D::MoveTo(int x_
, int y_
) {
1345 static int Delta(int difference
) {
1348 else if (difference
> 0)
1354 static int RoundFloat(float f
) {
1358 void SurfaceD2D::LineTo(int x_
, int y_
) {
1359 if (pRenderTarget
) {
1361 int xDelta
= Delta(xDiff
);
1363 int yDelta
= Delta(yDiff
);
1364 if ((xDiff
== 0) || (yDiff
== 0)) {
1365 // Horizontal or vertical lines can be more precisely drawn as a filled rectangle
1366 int xEnd
= x_
- xDelta
;
1367 int left
= Platform::Minimum(x
, xEnd
);
1368 int width
= abs(x
- xEnd
) + 1;
1369 int yEnd
= y_
- yDelta
;
1370 int top
= Platform::Minimum(y
, yEnd
);
1371 int height
= abs(y
- yEnd
) + 1;
1372 D2D1_RECT_F rectangle1
= D2D1::RectF(left
, top
, left
+width
, top
+height
);
1373 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1374 } else if ((abs(xDiff
) == abs(yDiff
))) {
1376 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5, y
+ 0.5),
1377 D2D1::Point2F(x_
+ 0.5 - xDelta
, y_
+ 0.5 - yDelta
), pBrush
);
1379 // Line has a different slope so difficult to avoid last pixel
1380 pRenderTarget
->DrawLine(D2D1::Point2F(x
+ 0.5, y
+ 0.5),
1381 D2D1::Point2F(x_
+ 0.5, y_
+ 0.5), pBrush
);
1388 void SurfaceD2D::Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
) {
1389 if (pRenderTarget
) {
1390 ID2D1Factory
*pFactory
= 0;
1391 pRenderTarget
->GetFactory(&pFactory
);
1392 ID2D1PathGeometry
*geometry
=0;
1393 HRESULT hr
= pFactory
->CreatePathGeometry(&geometry
);
1394 if (SUCCEEDED(hr
)) {
1395 ID2D1GeometrySink
*sink
= 0;
1396 hr
= geometry
->Open(&sink
);
1397 if (SUCCEEDED(hr
)) {
1398 sink
->BeginFigure(D2D1::Point2F(pts
[0].x
+ 0.5f
, pts
[0].y
+ 0.5f
), D2D1_FIGURE_BEGIN_FILLED
);
1399 for (size_t i
=1; i
<static_cast<size_t>(npts
); i
++) {
1400 sink
->AddLine(D2D1::Point2F(pts
[i
].x
+ 0.5f
, pts
[i
].y
+ 0.5f
));
1402 sink
->EndFigure(D2D1_FIGURE_END_CLOSED
);
1407 pRenderTarget
->FillGeometry(geometry
,pBrush
);
1409 pRenderTarget
->DrawGeometry(geometry
,pBrush
);
1412 geometry
->Release();
1417 void SurfaceD2D::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1418 if (pRenderTarget
) {
1419 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5, rc
.top
+0.5, RoundFloat(rc
.right
) - 0.5, rc
.bottom
-0.5);
1421 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1423 pRenderTarget
->DrawRectangle(&rectangle1
, pBrush
);
1427 void SurfaceD2D::FillRectangle(PRectangle rc
, ColourDesired back
) {
1428 if (pRenderTarget
) {
1430 D2D1_RECT_F rectangle1
= D2D1::RectF(RoundFloat(rc
.left
), rc
.top
, RoundFloat(rc
.right
), rc
.bottom
);
1431 pRenderTarget
->FillRectangle(&rectangle1
, pBrush
);
1435 void SurfaceD2D::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
1436 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfacePattern
);
1437 surfOther
.FlushDrawing();
1438 ID2D1Bitmap
*pBitmap
= NULL
;
1439 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1440 surfOther
.pRenderTarget
);
1441 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1442 if (SUCCEEDED(hr
)) {
1443 ID2D1BitmapBrush
*pBitmapBrush
= NULL
;
1444 D2D1_BITMAP_BRUSH_PROPERTIES brushProperties
=
1445 D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP
, D2D1_EXTEND_MODE_WRAP
,
1446 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
1447 // Create the bitmap brush.
1448 hr
= pRenderTarget
->CreateBitmapBrush(pBitmap
, brushProperties
, &pBitmapBrush
);
1450 if (SUCCEEDED(hr
)) {
1451 pRenderTarget
->FillRectangle(
1452 D2D1::RectF(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
),
1454 pBitmapBrush
->Release();
1459 void SurfaceD2D::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1460 if (pRenderTarget
) {
1461 D2D1_ROUNDED_RECT roundedRectFill
= D2D1::RoundedRect(
1462 D2D1::RectF(rc
.left
+1.0, rc
.top
+1.0, rc
.right
-1.0, rc
.bottom
-1.0),
1465 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1467 D2D1_ROUNDED_RECT roundedRect
= D2D1::RoundedRect(
1468 D2D1::RectF(rc
.left
+ 0.5, rc
.top
+0.5, rc
.right
- 0.5, rc
.bottom
-0.5),
1471 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1475 void SurfaceD2D::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
1476 ColourDesired outline
, int alphaOutline
, int /* flags*/ ) {
1477 if (pRenderTarget
) {
1478 if (cornerSize
== 0) {
1479 // When corner size is zero, draw square rectangle to prevent blurry pixels at corners
1480 D2D1_RECT_F rectFill
= D2D1::RectF(RoundFloat(rc
.left
) + 1.0, rc
.top
+ 1.0, RoundFloat(rc
.right
) - 1.0, rc
.bottom
- 1.0);
1481 D2DPenColour(fill
, alphaFill
);
1482 pRenderTarget
->FillRectangle(rectFill
, pBrush
);
1484 D2D1_RECT_F rectOutline
= D2D1::RectF(RoundFloat(rc
.left
) + 0.5, rc
.top
+ 0.5, RoundFloat(rc
.right
) - 0.5, rc
.bottom
- 0.5);
1485 D2DPenColour(outline
, alphaOutline
);
1486 pRenderTarget
->DrawRectangle(rectOutline
, pBrush
);
1488 D2D1_ROUNDED_RECT roundedRectFill
= D2D1::RoundedRect(
1489 D2D1::RectF(RoundFloat(rc
.left
) + 1.0, rc
.top
+ 1.0, RoundFloat(rc
.right
) - 1.0, rc
.bottom
- 1.0),
1490 cornerSize
, cornerSize
);
1491 D2DPenColour(fill
, alphaFill
);
1492 pRenderTarget
->FillRoundedRectangle(roundedRectFill
, pBrush
);
1494 D2D1_ROUNDED_RECT roundedRect
= D2D1::RoundedRect(
1495 D2D1::RectF(RoundFloat(rc
.left
) + 0.5, rc
.top
+ 0.5, RoundFloat(rc
.right
) - 0.5, rc
.bottom
- 0.5),
1496 cornerSize
, cornerSize
);
1497 D2DPenColour(outline
, alphaOutline
);
1498 pRenderTarget
->DrawRoundedRectangle(roundedRect
, pBrush
);
1503 void SurfaceD2D::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
1504 if (pRenderTarget
) {
1505 if (rc
.Width() > width
)
1506 rc
.left
+= static_cast<int>((rc
.Width() - width
) / 2);
1507 rc
.right
= rc
.left
+ width
;
1508 if (rc
.Height() > height
)
1509 rc
.top
+= static_cast<int>((rc
.Height() - height
) / 2);
1510 rc
.bottom
= rc
.top
+ height
;
1512 std::vector
<unsigned char> image(height
* width
* 4);
1513 for (int y
=0; y
<height
; y
++) {
1514 for (int x
=0; x
<width
; x
++) {
1515 unsigned char *pixel
= &image
[0] + (y
*width
+x
) * 4;
1516 unsigned char alpha
= pixelsImage
[3];
1517 // Input is RGBA, output is BGRA with premultiplied alpha
1518 pixel
[2] = (*pixelsImage
++) * alpha
/ 255;
1519 pixel
[1] = (*pixelsImage
++) * alpha
/ 255;
1520 pixel
[0] = (*pixelsImage
++) * alpha
/ 255;
1521 pixel
[3] = *pixelsImage
++;
1525 ID2D1Bitmap
*bitmap
= 0;
1526 D2D1_SIZE_U size
= D2D1::SizeU(width
, height
);
1527 D2D1_BITMAP_PROPERTIES props
= {{DXGI_FORMAT_B8G8R8A8_UNORM
,
1528 D2D1_ALPHA_MODE_PREMULTIPLIED
}, 72.0, 72.0};
1529 HRESULT hr
= pRenderTarget
->CreateBitmap(size
, &image
[0],
1530 width
* 4, &props
, &bitmap
);
1531 if (SUCCEEDED(hr
)) {
1532 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1533 pRenderTarget
->DrawBitmap(bitmap
, rcDestination
);
1539 void SurfaceD2D::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
1540 if (pRenderTarget
) {
1541 FLOAT radius
= rc
.Width() / 2.0f
- 1.0f
;
1542 D2D1_ELLIPSE ellipse
= D2D1::Ellipse(
1543 D2D1::Point2F((rc
.left
+ rc
.right
) / 2.0f
, (rc
.top
+ rc
.bottom
) / 2.0f
),
1547 pRenderTarget
->FillEllipse(ellipse
, pBrush
);
1549 pRenderTarget
->DrawEllipse(ellipse
, pBrush
);
1553 void SurfaceD2D::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
1554 SurfaceD2D
&surfOther
= static_cast<SurfaceD2D
&>(surfaceSource
);
1555 surfOther
.FlushDrawing();
1556 ID2D1BitmapRenderTarget
*pCompatibleRenderTarget
= reinterpret_cast<ID2D1BitmapRenderTarget
*>(
1557 surfOther
.pRenderTarget
);
1558 ID2D1Bitmap
*pBitmap
= NULL
;
1559 HRESULT hr
= pCompatibleRenderTarget
->GetBitmap(&pBitmap
);
1560 if (SUCCEEDED(hr
)) {
1561 D2D1_RECT_F rcDestination
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1562 D2D1_RECT_F rcSource
= {from
.x
, from
.y
, from
.x
+ rc
.Width(), from
.y
+ rc
.Height()};
1563 pRenderTarget
->DrawBitmap(pBitmap
, rcDestination
, 1.0f
,
1564 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
, rcSource
);
1565 pRenderTarget
->Flush();
1570 void SurfaceD2D::DrawTextCommon(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, UINT
) {
1573 // Use Unicode calls
1574 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
1575 if (pRenderTarget
&& pTextFormat
&& pBrush
) {
1577 // Explicitly creating a text layout appears a little faster
1578 IDWriteTextLayout
*pTextLayout
;
1579 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
,
1580 rc
.Width(), rc
.Height(), &pTextLayout
);
1581 if (SUCCEEDED(hr
)) {
1582 D2D1_POINT_2F origin
= {rc
.left
, ybase
-yAscent
};
1583 pRenderTarget
->DrawTextLayout(origin
, pTextLayout
, pBrush
, D2D1_DRAW_TEXT_OPTIONS_NONE
);
1584 pTextLayout
->Release();
1589 void SurfaceD2D::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1590 ColourDesired fore
, ColourDesired back
) {
1591 if (pRenderTarget
) {
1592 FillRectangle(rc
, back
);
1594 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
);
1598 void SurfaceD2D::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1599 ColourDesired fore
, ColourDesired back
) {
1600 if (pRenderTarget
) {
1601 FillRectangle(rc
, back
);
1603 DrawTextCommon(rc
, font_
, ybase
, s
, len
, ETO_OPAQUE
| ETO_CLIPPED
);
1607 void SurfaceD2D::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
1608 ColourDesired fore
) {
1609 // Avoid drawing spaces in transparent mode
1610 for (int i
=0;i
<len
;i
++) {
1612 if (pRenderTarget
) {
1614 DrawTextCommon(rc
, font_
, ybase
, s
, len
, 0);
1621 XYPOSITION
SurfaceD2D::WidthText(Font
&font_
, const char *s
, int len
) {
1624 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
1625 if (pIDWriteFactory
&& pTextFormat
) {
1627 IDWriteTextLayout
*pTextLayout
= 0;
1628 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1629 if (SUCCEEDED(hr
)) {
1630 DWRITE_TEXT_METRICS textMetrics
;
1631 pTextLayout
->GetMetrics(&textMetrics
);
1632 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1633 pTextLayout
->Release();
1639 void SurfaceD2D::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
1642 const TextWide
tbuf(s
, len
, unicodeMode
, codePage
);
1643 TextPositions
poses(tbuf
.tlen
);
1645 const int clusters
= 1000;
1646 DWRITE_CLUSTER_METRICS clusterMetrics
[clusters
];
1648 if (pIDWriteFactory
&& pTextFormat
) {
1651 IDWriteTextLayout
*pTextLayout
= 0;
1652 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(tbuf
.buffer
, tbuf
.tlen
, pTextFormat
, 10000.0, 1000.0, &pTextLayout
);
1655 // For now, assuming WCHAR == cluster
1656 pTextLayout
->GetClusterMetrics(clusterMetrics
, clusters
, &count
);
1657 FLOAT position
= 0.0f
;
1659 for (size_t ci
=0;ci
<count
;ci
++) {
1660 position
+= clusterMetrics
[ci
].width
;
1661 for (size_t inCluster
=0; inCluster
<clusterMetrics
[ci
].length
; inCluster
++) {
1662 //poses.buffer[ti++] = int(position + 0.5);
1663 poses
.buffer
[ti
++] = position
;
1666 PLATFORM_ASSERT(ti
== static_cast<size_t>(tbuf
.tlen
));
1667 pTextLayout
->Release();
1670 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
1672 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1675 unsigned char uch
= us
[i
];
1676 unsigned int lenChar
= 1;
1677 if (uch
>= (0x80 + 0x40 + 0x20 + 0x10)) {
1680 } else if (uch
>= (0x80 + 0x40 + 0x20)) {
1682 } else if (uch
>= (0x80)) {
1685 for (unsigned int bytePos
=0; (bytePos
<lenChar
) && (i
<len
); bytePos
++) {
1686 positions
[i
++] = poses
.buffer
[ui
];
1692 lastPos
= positions
[i
-1];
1694 positions
[i
++] = lastPos
;
1696 } else if (codePage
== 0) {
1698 // One character per position
1699 PLATFORM_ASSERT(len
== tbuf
.tlen
);
1700 for (size_t kk
=0;kk
<static_cast<size_t>(len
);kk
++) {
1701 positions
[kk
] = poses
.buffer
[kk
];
1706 // May be more than one byte per position
1708 for (int i
=0;i
<len
;) {
1709 if (::IsDBCSLeadByteEx(codePage
, s
[i
])) {
1710 positions
[i
] = poses
.buffer
[ui
];
1711 positions
[i
+1] = poses
.buffer
[ui
];
1714 positions
[i
] = poses
.buffer
[ui
];
1723 XYPOSITION
SurfaceD2D::WidthChar(Font
&font_
, char ch
) {
1726 if (pIDWriteFactory
&& pTextFormat
) {
1728 IDWriteTextLayout
*pTextLayout
= 0;
1729 const WCHAR wch
= ch
;
1730 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(&wch
, 1, pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1731 if (SUCCEEDED(hr
)) {
1732 DWRITE_TEXT_METRICS textMetrics
;
1733 pTextLayout
->GetMetrics(&textMetrics
);
1734 width
= textMetrics
.widthIncludingTrailingWhitespace
;
1735 pTextLayout
->Release();
1741 XYPOSITION
SurfaceD2D::Ascent(Font
&font_
) {
1743 return ceil(yAscent
);
1746 XYPOSITION
SurfaceD2D::Descent(Font
&font_
) {
1748 return ceil(yDescent
);
1751 XYPOSITION
SurfaceD2D::InternalLeading(Font
&font_
) {
1753 return floor(yInternalLeading
);
1756 XYPOSITION
SurfaceD2D::ExternalLeading(Font
&) {
1757 // Not implemented, always return one
1761 XYPOSITION
SurfaceD2D::Height(Font
&font_
) {
1762 return Ascent(font_
) + Descent(font_
);
1765 XYPOSITION
SurfaceD2D::AverageCharWidth(Font
&font_
) {
1768 if (pIDWriteFactory
&& pTextFormat
) {
1770 IDWriteTextLayout
*pTextLayout
= 0;
1771 const WCHAR wszAllAlpha
[] = L
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1772 HRESULT hr
= pIDWriteFactory
->CreateTextLayout(wszAllAlpha
, static_cast<UINT32
>(wcslen(wszAllAlpha
)),
1773 pTextFormat
, 1000.0, 1000.0, &pTextLayout
);
1774 if (SUCCEEDED(hr
)) {
1775 DWRITE_TEXT_METRICS textMetrics
;
1776 pTextLayout
->GetMetrics(&textMetrics
);
1777 width
= textMetrics
.width
/ wcslen(wszAllAlpha
);
1778 pTextLayout
->Release();
1784 void SurfaceD2D::SetClip(PRectangle rc
) {
1785 if (pRenderTarget
) {
1786 D2D1_RECT_F rcClip
= {rc
.left
, rc
.top
, rc
.right
, rc
.bottom
};
1787 pRenderTarget
->PushAxisAlignedClip(rcClip
, D2D1_ANTIALIAS_MODE_ALIASED
);
1792 void SurfaceD2D::FlushCachedState() {
1795 void SurfaceD2D::SetUnicodeMode(bool unicodeMode_
) {
1796 unicodeMode
=unicodeMode_
;
1799 void SurfaceD2D::SetDBCSMode(int codePage_
) {
1800 // No action on window as automatically handled by system.
1801 codePage
= codePage_
;
1805 Surface
*Surface::Allocate(int technology
) {
1806 #if defined(USE_D2D)
1807 if (technology
== SCWIN_TECH_GDI
)
1808 return new SurfaceGDI
;
1810 return new SurfaceD2D
;
1812 return new SurfaceGDI
;
1819 void Window::Destroy() {
1821 ::DestroyWindow(reinterpret_cast<HWND
>(wid
));
1825 bool Window::HasFocus() {
1826 return ::GetFocus() == wid
;
1829 PRectangle
Window::GetPosition() {
1831 ::GetWindowRect(reinterpret_cast<HWND
>(wid
), &rc
);
1832 return PRectangle(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1835 void Window::SetPosition(PRectangle rc
) {
1836 ::SetWindowPos(reinterpret_cast<HWND
>(wid
),
1837 0, rc
.left
, rc
.top
, rc
.Width(), rc
.Height(), SWP_NOZORDER
|SWP_NOACTIVATE
);
1840 void Window::SetPositionRelative(PRectangle rc
, Window w
) {
1841 LONG style
= ::GetWindowLong(reinterpret_cast<HWND
>(wid
), GWL_STYLE
);
1842 if (style
& WS_POPUP
) {
1843 POINT ptOther
= {0, 0};
1844 ::ClientToScreen(reinterpret_cast<HWND
>(w
.GetID()), &ptOther
);
1845 rc
.Move(ptOther
.x
, ptOther
.y
);
1847 // This #ifdef is for VC 98 which has problems with MultiMon.h under some conditions.
1848 #ifdef MONITOR_DEFAULTTONULL
1849 // We're using the stub functionality of MultiMon.h to decay gracefully on machines
1850 // (ie, pre Win2000, Win95) that do not support the newer functions.
1851 RECT rcMonitor
= RectFromPRectangle(rc
);
1852 MONITORINFO mi
= {0};
1853 mi
.cbSize
= sizeof(mi
);
1855 HMONITOR hMonitor
= ::MonitorFromRect(&rcMonitor
, MONITOR_DEFAULTTONEAREST
);
1856 // If hMonitor is NULL, that's just the main screen anyways.
1857 ::GetMonitorInfo(hMonitor
, &mi
);
1859 // Now clamp our desired rectangle to fit inside the work area
1860 // This way, the menu will fit wholly on one screen. An improvement even
1861 // if you don't have a second monitor on the left... Menu's appears half on
1862 // one screen and half on the other are just U.G.L.Y.!
1863 if (rc
.right
> mi
.rcWork
.right
)
1864 rc
.Move(mi
.rcWork
.right
- rc
.right
, 0);
1865 if (rc
.bottom
> mi
.rcWork
.bottom
)
1866 rc
.Move(0, mi
.rcWork
.bottom
- rc
.bottom
);
1867 if (rc
.left
< mi
.rcWork
.left
)
1868 rc
.Move(mi
.rcWork
.left
- rc
.left
, 0);
1869 if (rc
.top
< mi
.rcWork
.top
)
1870 rc
.Move(0, mi
.rcWork
.top
- rc
.top
);
1876 PRectangle
Window::GetClientPosition() {
1879 ::GetClientRect(reinterpret_cast<HWND
>(wid
), &rc
);
1880 return PRectangle(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1883 void Window::Show(bool show
) {
1885 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_SHOWNOACTIVATE
);
1887 ::ShowWindow(reinterpret_cast<HWND
>(wid
), SW_HIDE
);
1890 void Window::InvalidateAll() {
1891 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), NULL
, FALSE
);
1894 void Window::InvalidateRectangle(PRectangle rc
) {
1895 RECT rcw
= RectFromPRectangle(rc
);
1896 ::InvalidateRect(reinterpret_cast<HWND
>(wid
), &rcw
, FALSE
);
1899 static LRESULT
Window_SendMessage(Window
*w
, UINT msg
, WPARAM wParam
=0, LPARAM lParam
=0) {
1900 return ::SendMessage(reinterpret_cast<HWND
>(w
->GetID()), msg
, wParam
, lParam
);
1903 void Window::SetFont(Font
&font
) {
1904 Window_SendMessage(this, WM_SETFONT
,
1905 reinterpret_cast<WPARAM
>(font
.GetID()), 0);
1908 static void FlipBitmap(HBITMAP bitmap
, int width
, int height
) {
1909 HDC hdc
= ::CreateCompatibleDC(NULL
);
1911 HGDIOBJ prevBmp
= ::SelectObject(hdc
, bitmap
);
1912 ::StretchBlt(hdc
, width
- 1, 0, -width
, height
, hdc
, 0, 0, width
, height
, SRCCOPY
);
1913 ::SelectObject(hdc
, prevBmp
);
1918 static HCURSOR
GetReverseArrowCursor() {
1919 if (reverseArrowCursor
!= NULL
)
1920 return reverseArrowCursor
;
1922 ::EnterCriticalSection(&crPlatformLock
);
1923 HCURSOR cursor
= reverseArrowCursor
;
1924 if (cursor
== NULL
) {
1925 cursor
= ::LoadCursor(NULL
, IDC_ARROW
);
1927 if (::GetIconInfo(cursor
, &info
)) {
1929 if (::GetObject(info
.hbmMask
, sizeof(bmp
), &bmp
)) {
1930 FlipBitmap(info
.hbmMask
, bmp
.bmWidth
, bmp
.bmHeight
);
1931 if (info
.hbmColor
!= NULL
)
1932 FlipBitmap(info
.hbmColor
, bmp
.bmWidth
, bmp
.bmHeight
);
1933 info
.xHotspot
= (DWORD
)bmp
.bmWidth
- 1 - info
.xHotspot
;
1935 reverseArrowCursor
= ::CreateIconIndirect(&info
);
1936 if (reverseArrowCursor
!= NULL
)
1937 cursor
= reverseArrowCursor
;
1940 ::DeleteObject(info
.hbmMask
);
1941 if (info
.hbmColor
!= NULL
)
1942 ::DeleteObject(info
.hbmColor
);
1945 ::LeaveCriticalSection(&crPlatformLock
);
1949 void Window::SetCursor(Cursor curs
) {
1952 ::SetCursor(::LoadCursor(NULL
,IDC_IBEAM
));
1955 ::SetCursor(::LoadCursor(NULL
,IDC_UPARROW
));
1958 ::SetCursor(::LoadCursor(NULL
,IDC_WAIT
));
1961 ::SetCursor(::LoadCursor(NULL
,IDC_SIZEWE
));
1964 ::SetCursor(::LoadCursor(NULL
,IDC_SIZENS
));
1967 ::SetCursor(::LoadCursor(NULL
,IDC_HAND
));
1969 case cursorReverseArrow
:
1970 ::SetCursor(GetReverseArrowCursor());
1973 case cursorInvalid
: // Should not occur, but just in case.
1974 ::SetCursor(::LoadCursor(NULL
,IDC_ARROW
));
1979 void Window::SetTitle(const char *s
) {
1980 ::SetWindowTextA(reinterpret_cast<HWND
>(wid
), s
);
1983 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
1985 PRectangle
Window::GetMonitorRect(Point pt
) {
1986 #ifdef MONITOR_DEFAULTTONULL
1987 // MonitorFromPoint and GetMonitorInfo are not available on Windows 95 so are not used.
1988 // There could be conditional code and dynamic loading in a future version
1989 // so this would work on those platforms where they are available.
1990 PRectangle rcPosition
= GetPosition();
1991 POINT ptDesktop
= {static_cast<LONG
>(pt
.x
+ rcPosition
.left
),
1992 static_cast<LONG
>(pt
.y
+ rcPosition
.top
)};
1993 HMONITOR hMonitor
= ::MonitorFromPoint(ptDesktop
, MONITOR_DEFAULTTONEAREST
);
1994 MONITORINFO mi
= {0};
1995 memset(&mi
, 0, sizeof(mi
));
1996 mi
.cbSize
= sizeof(mi
);
1997 if (::GetMonitorInfo(hMonitor
, &mi
)) {
1998 PRectangle
rcMonitor(
1999 mi
.rcWork
.left
- rcPosition
.left
,
2000 mi
.rcWork
.top
- rcPosition
.top
,
2001 mi
.rcWork
.right
- rcPosition
.left
,
2002 mi
.rcWork
.bottom
- rcPosition
.top
);
2005 return PRectangle();
2008 return PRectangle();
2012 struct ListItemData
{
2017 #define _ROUND2(n,pow2) \
2018 ( ( (n) + (pow2) - 1) & ~((pow2) - 1) )
2036 char *AllocWord(const char *word
);
2039 LineToItem() : words(NULL
), wordsCount(0), wordsSize(0), data(NULL
), len(0), count(0) {
2052 ListItemData
*Append(const char *text
, int value
);
2054 ListItemData
Get(int index
) const {
2055 if (index
>= 0 && index
< count
) {
2058 ListItemData missing
= {"", -1};
2066 ListItemData
*AllocItem();
2068 void SetWords(char *s
) {
2069 words
= s
; // N.B. will be deleted on destruction
2073 char *LineToItem::AllocWord(const char *text
) {
2074 int chars
= static_cast<int>(strlen(text
) + 1);
2075 int newCount
= wordsCount
+ chars
;
2076 if (newCount
> wordsSize
) {
2077 wordsSize
= _ROUND2(newCount
* 2, 8192);
2078 char *wordsNew
= new char[wordsSize
];
2079 memcpy(wordsNew
, words
, wordsCount
);
2080 int offset
= wordsNew
- words
;
2081 for (int i
=0; i
<count
; i
++)
2082 data
[i
].text
+= offset
;
2086 char *s
= &words
[wordsCount
];
2087 wordsCount
= newCount
;
2088 strncpy(s
, text
, chars
);
2092 ListItemData
*LineToItem::AllocItem() {
2094 int lenNew
= _ROUND2((count
+1) * 2, 1024);
2095 ListItemData
*dataNew
= new ListItemData
[lenNew
];
2096 memcpy(dataNew
, data
, count
* sizeof(ListItemData
));
2101 ListItemData
*item
= &data
[count
];
2106 ListItemData
*LineToItem::Append(const char *text
, int imageIndex
) {
2107 ListItemData
*item
= AllocItem();
2108 item
->text
= AllocWord(text
);
2109 item
->pixId
= imageIndex
;
2113 const TCHAR ListBoxX_ClassName
[] = TEXT("ListBoxX");
2115 ListBox::ListBox() {
2118 ListBox::~ListBox() {
2121 class ListBoxX
: public ListBox
{
2125 RGBAImageSet images
;
2129 int desiredVisibleRows
;
2130 unsigned int maxItemCharacters
;
2131 unsigned int aveCharWidth
;
2134 CallBackAction doubleClickAction
;
2135 void *doubleClickActionData
;
2136 const char *widestItem
;
2137 unsigned int maxCharWidth
;
2139 PRectangle rcPreSize
;
2141 Point location
; // Caret location at which the list is opened
2142 int wheelDelta
; // mouse wheel residue
2144 HWND
GetHWND() const;
2145 void AppendListItem(const char *startword
, const char *numword
);
2146 void AdjustWindowRect(PRectangle
*rc
) const;
2147 int ItemHeight() const;
2148 int MinClientWidth() const;
2149 int TextOffset() const;
2150 Point
GetClientExtent() const;
2151 POINT
MinTrackSize() const;
2152 POINT
MaxTrackSize() const;
2153 void SetRedraw(bool on
);
2154 void OnDoubleClick();
2155 void ResizeToCursor();
2156 void StartResize(WPARAM
);
2157 int NcHitTest(WPARAM
, LPARAM
) const;
2158 void CentreItem(int);
2160 static LRESULT PASCAL
ControlWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2162 static const Point ItemInset
; // Padding around whole item
2163 static const Point TextInset
; // Padding around text
2164 static const Point ImageInset
; // Padding around image
2167 ListBoxX() : lineHeight(10), fontCopy(0), technology(0), lb(0), unicodeMode(false),
2168 desiredVisibleRows(5), maxItemCharacters(0), aveCharWidth(8),
2169 parent(NULL
), ctrlID(0), doubleClickAction(NULL
), doubleClickActionData(NULL
),
2170 widestItem(NULL
), maxCharWidth(1), resizeHit(0), wheelDelta(0) {
2172 virtual ~ListBoxX() {
2174 ::DeleteObject(fontCopy
);
2178 virtual void SetFont(Font
&font
);
2179 virtual void Create(Window
&parent
, int ctrlID
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
);
2180 virtual void SetAverageCharWidth(int width
);
2181 virtual void SetVisibleRows(int rows
);
2182 virtual int GetVisibleRows() const;
2183 virtual PRectangle
GetDesiredRect();
2184 virtual int CaretFromEdge();
2185 virtual void Clear();
2186 virtual void Append(char *s
, int type
= -1);
2187 virtual int Length();
2188 virtual void Select(int n
);
2189 virtual int GetSelection();
2190 virtual int Find(const char *prefix
);
2191 virtual void GetValue(int n
, char *value
, int len
);
2192 virtual void RegisterImage(int type
, const char *xpm_data
);
2193 virtual void RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
);
2194 virtual void ClearRegisteredImages();
2195 virtual void SetDoubleClickAction(CallBackAction action
, void *data
) {
2196 doubleClickAction
= action
;
2197 doubleClickActionData
= data
;
2199 virtual void SetList(const char *list
, char separator
, char typesep
);
2200 void Draw(DRAWITEMSTRUCT
*pDrawItem
);
2201 LRESULT
WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2202 static LRESULT PASCAL
StaticWndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
);
2205 const Point
ListBoxX::ItemInset(0, 0);
2206 const Point
ListBoxX::TextInset(2, 0);
2207 const Point
ListBoxX::ImageInset(1, 0);
2209 ListBox
*ListBox::Allocate() {
2210 ListBoxX
*lb
= new ListBoxX();
2214 void ListBoxX::Create(Window
&parent_
, int ctrlID_
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
) {
2217 location
= location_
;
2218 lineHeight
= lineHeight_
;
2219 unicodeMode
= unicodeMode_
;
2220 technology
= technology_
;
2221 HWND hwndParent
= reinterpret_cast<HWND
>(parent
->GetID());
2222 HINSTANCE hinstanceParent
= GetWindowInstance(hwndParent
);
2223 // Window created as popup so not clipped within parent client area
2224 wid
= ::CreateWindowEx(
2225 WS_EX_WINDOWEDGE
, ListBoxX_ClassName
, TEXT(""),
2226 WS_POPUP
| WS_THICKFRAME
,
2227 100,100, 150,80, hwndParent
,
2232 POINT locationw
= {static_cast<LONG
>(location
.x
), static_cast<LONG
>(location
.y
)};
2233 ::MapWindowPoints(hwndParent
, NULL
, &locationw
, 1);
2234 location
= Point(locationw
.x
, locationw
.y
);
2237 void ListBoxX::SetFont(Font
&font
) {
2240 ::DeleteObject(fontCopy
);
2243 FormatAndMetrics
*pfm
= reinterpret_cast<FormatAndMetrics
*>(font
.GetID());
2244 fontCopy
= pfm
->HFont();
2245 ::SendMessage(lb
, WM_SETFONT
, reinterpret_cast<WPARAM
>(fontCopy
), 0);
2249 void ListBoxX::SetAverageCharWidth(int width
) {
2250 aveCharWidth
= width
;
2253 void ListBoxX::SetVisibleRows(int rows
) {
2254 desiredVisibleRows
= rows
;
2257 int ListBoxX::GetVisibleRows() const {
2258 return desiredVisibleRows
;
2261 HWND
ListBoxX::GetHWND() const {
2262 return reinterpret_cast<HWND
>(GetID());
2265 PRectangle
ListBoxX::GetDesiredRect() {
2266 PRectangle rcDesired
= GetPosition();
2268 int rows
= Length();
2269 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2270 rows
= desiredVisibleRows
;
2271 rcDesired
.bottom
= rcDesired
.top
+ ItemHeight() * rows
;
2273 int width
= MinClientWidth();
2274 HDC hdc
= ::GetDC(lb
);
2275 HFONT oldFont
= SelectFont(hdc
, fontCopy
);
2276 SIZE textSize
= {0, 0};
2277 int len
= static_cast<int>(widestItem
? strlen(widestItem
) : 0);
2279 const TextWide
tbuf(widestItem
, len
, unicodeMode
);
2280 ::GetTextExtentPoint32W(hdc
, tbuf
.buffer
, tbuf
.tlen
, &textSize
);
2282 ::GetTextExtentPoint32A(hdc
, widestItem
, len
, &textSize
);
2285 ::GetTextMetrics(hdc
, &tm
);
2286 maxCharWidth
= tm
.tmMaxCharWidth
;
2287 SelectFont(hdc
, oldFont
);
2288 ::ReleaseDC(lb
, hdc
);
2290 int widthDesired
= Platform::Maximum(textSize
.cx
, (len
+ 1) * tm
.tmAveCharWidth
);
2291 if (width
< widthDesired
)
2292 width
= widthDesired
;
2294 rcDesired
.right
= rcDesired
.left
+ TextOffset() + width
+ (TextInset
.x
* 2);
2295 if (Length() > rows
)
2296 rcDesired
.right
+= ::GetSystemMetrics(SM_CXVSCROLL
);
2298 AdjustWindowRect(&rcDesired
);
2302 int ListBoxX::TextOffset() const {
2303 int pixWidth
= images
.GetWidth();
2304 return pixWidth
== 0 ? ItemInset
.x
: ItemInset
.x
+ pixWidth
+ (ImageInset
.x
* 2);
2307 int ListBoxX::CaretFromEdge() {
2309 AdjustWindowRect(&rc
);
2310 return TextOffset() + TextInset
.x
+ (0 - rc
.left
) - 1;
2313 void ListBoxX::Clear() {
2314 ::SendMessage(lb
, LB_RESETCONTENT
, 0, 0);
2315 maxItemCharacters
= 0;
2320 void ListBoxX::Append(char *s
, int type
) {
2321 int index
= ::SendMessage(lb
, LB_ADDSTRING
, 0, reinterpret_cast<LPARAM
>(s
));
2324 ListItemData
*newItem
= lti
.Append(s
, type
);
2325 unsigned int len
= static_cast<unsigned int>(strlen(s
));
2326 if (maxItemCharacters
< len
) {
2327 maxItemCharacters
= len
;
2328 widestItem
= newItem
->text
;
2332 int ListBoxX::Length() {
2336 void ListBoxX::Select(int n
) {
2337 // We are going to scroll to centre on the new selection and then select it, so disable
2338 // redraw to avoid flicker caused by a painting new selection twice in unselected and then
2342 ::SendMessage(lb
, LB_SETCURSEL
, n
, 0);
2346 int ListBoxX::GetSelection() {
2347 return ::SendMessage(lb
, LB_GETCURSEL
, 0, 0);
2350 // This is not actually called at present
2351 int ListBoxX::Find(const char *) {
2355 void ListBoxX::GetValue(int n
, char *value
, int len
) {
2356 ListItemData item
= lti
.Get(n
);
2357 strncpy(value
, item
.text
, len
);
2358 value
[len
-1] = '\0';
2361 void ListBoxX::RegisterImage(int type
, const char *xpm_data
) {
2362 XPM
xpmImage(xpm_data
);
2363 images
.Add(type
, new RGBAImage(xpmImage
));
2366 void ListBoxX::RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
) {
2367 images
.Add(type
, new RGBAImage(width
, height
, pixelsImage
));
2370 void ListBoxX::ClearRegisteredImages() {
2374 void ListBoxX::Draw(DRAWITEMSTRUCT
*pDrawItem
) {
2375 if ((pDrawItem
->itemAction
== ODA_SELECT
) || (pDrawItem
->itemAction
== ODA_DRAWENTIRE
)) {
2376 RECT rcBox
= pDrawItem
->rcItem
;
2377 rcBox
.left
+= TextOffset();
2378 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2379 RECT rcImage
= pDrawItem
->rcItem
;
2380 rcImage
.right
= rcBox
.left
;
2381 // The image is not highlighted
2382 ::FillRect(pDrawItem
->hDC
, &rcImage
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2383 ::FillRect(pDrawItem
->hDC
, &rcBox
, reinterpret_cast<HBRUSH
>(COLOR_HIGHLIGHT
+1));
2384 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHT
));
2385 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_HIGHLIGHTTEXT
));
2387 ::FillRect(pDrawItem
->hDC
, &pDrawItem
->rcItem
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2388 ::SetBkColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOW
));
2389 ::SetTextColor(pDrawItem
->hDC
, ::GetSysColor(COLOR_WINDOWTEXT
));
2392 ListItemData item
= lti
.Get(pDrawItem
->itemID
);
2393 int pixId
= item
.pixId
;
2394 const char *text
= item
.text
;
2395 int len
= static_cast<int>(strlen(text
));
2397 RECT rcText
= rcBox
;
2398 ::InsetRect(&rcText
, TextInset
.x
, TextInset
.y
);
2401 const TextWide
tbuf(text
, len
, unicodeMode
);
2402 ::DrawTextW(pDrawItem
->hDC
, tbuf
.buffer
, tbuf
.tlen
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2404 ::DrawTextA(pDrawItem
->hDC
, text
, len
, &rcText
, DT_NOPREFIX
|DT_END_ELLIPSIS
|DT_SINGLELINE
|DT_NOCLIP
);
2406 if (pDrawItem
->itemState
& ODS_SELECTED
) {
2407 ::DrawFocusRect(pDrawItem
->hDC
, &rcBox
);
2410 // Draw the image, if any
2411 RGBAImage
*pimage
= images
.Get(pixId
);
2413 Surface
*surfaceItem
= Surface::Allocate(technology
);
2415 if (technology
== SCWIN_TECH_GDI
) {
2416 surfaceItem
->Init(pDrawItem
->hDC
, pDrawItem
->hwndItem
);
2417 int left
= pDrawItem
->rcItem
.left
+ ItemInset
.x
+ ImageInset
.x
;
2418 PRectangle
rcImage(left
, pDrawItem
->rcItem
.top
,
2419 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2420 surfaceItem
->DrawRGBAImage(rcImage
,
2421 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2423 ::SetTextAlign(pDrawItem
->hDC
, TA_TOP
);
2425 #if defined(USE_D2D)
2426 D2D1_RENDER_TARGET_PROPERTIES props
= D2D1::RenderTargetProperties(
2427 D2D1_RENDER_TARGET_TYPE_DEFAULT
,
2429 DXGI_FORMAT_B8G8R8A8_UNORM
,
2430 D2D1_ALPHA_MODE_IGNORE
),
2433 D2D1_RENDER_TARGET_USAGE_NONE
,
2434 D2D1_FEATURE_LEVEL_DEFAULT
2436 ID2D1DCRenderTarget
*pDCRT
= 0;
2437 HRESULT hr
= pD2DFactory
->CreateDCRenderTarget(&props
, &pDCRT
);
2439 GetClientRect(pDrawItem
->hwndItem
, &rcWindow
);
2440 hr
= pDCRT
->BindDC(pDrawItem
->hDC
, &rcWindow
);
2441 if (SUCCEEDED(hr
)) {
2442 surfaceItem
->Init(pDCRT
, pDrawItem
->hwndItem
);
2444 int left
= pDrawItem
->rcItem
.left
+ ItemInset
.x
+ ImageInset
.x
;
2445 PRectangle
rcImage(left
, pDrawItem
->rcItem
.top
,
2446 left
+ images
.GetWidth(), pDrawItem
->rcItem
.bottom
);
2447 surfaceItem
->DrawRGBAImage(rcImage
,
2448 pimage
->GetWidth(), pimage
->GetHeight(), pimage
->Pixels());
2460 void ListBoxX::AppendListItem(const char *startword
, const char *numword
) {
2461 ListItemData
*item
= lti
.AllocItem();
2462 item
->text
= startword
;
2466 while ((ch
= *++numword
) != '\0') {
2467 pixId
= 10 * pixId
+ (ch
- '0');
2469 item
->pixId
= pixId
;
2474 unsigned int len
= static_cast<unsigned int>(strlen(item
->text
));
2475 if (maxItemCharacters
< len
) {
2476 maxItemCharacters
= len
;
2477 widestItem
= item
->text
;
2481 void ListBoxX::SetList(const char *list
, char separator
, char typesep
) {
2482 // Turn off redraw while populating the list - this has a significant effect, even if
2483 // the listbox is not visible.
2486 size_t size
= strlen(list
) + 1;
2487 char *words
= new char[size
];
2488 lti
.SetWords(words
);
2489 memcpy(words
, list
, size
);
2490 char *startword
= words
;
2491 char *numword
= NULL
;
2493 for (; words
[i
]; i
++) {
2494 if (words
[i
] == separator
) {
2498 AppendListItem(startword
, numword
);
2499 startword
= words
+ i
+ 1;
2501 } else if (words
[i
] == typesep
) {
2502 numword
= words
+ i
;
2508 AppendListItem(startword
, numword
);
2511 // Finally populate the listbox itself with the correct number of items
2512 int count
= lti
.Count();
2513 ::SendMessage(lb
, LB_INITSTORAGE
, count
, 0);
2514 for (int j
=0; j
<count
; j
++) {
2515 ::SendMessage(lb
, LB_ADDSTRING
, 0, j
+1);
2520 void ListBoxX::AdjustWindowRect(PRectangle
*rc
) const {
2521 RECT rcw
= RectFromPRectangle(*rc
);
2522 ::AdjustWindowRectEx(&rcw
, WS_THICKFRAME
, false, WS_EX_WINDOWEDGE
);
2523 *rc
= PRectangle(rcw
.left
, rcw
.top
, rcw
.right
, rcw
.bottom
);
2526 int ListBoxX::ItemHeight() const {
2527 int itemHeight
= lineHeight
+ (TextInset
.y
* 2);
2528 int pixHeight
= images
.GetHeight() + (ImageInset
.y
* 2);
2529 if (itemHeight
< pixHeight
) {
2530 itemHeight
= pixHeight
;
2535 int ListBoxX::MinClientWidth() const {
2536 return 12 * (aveCharWidth
+aveCharWidth
/3);
2539 POINT
ListBoxX::MinTrackSize() const {
2540 PRectangle
rc(0, 0, MinClientWidth(), ItemHeight());
2541 AdjustWindowRect(&rc
);
2542 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2546 POINT
ListBoxX::MaxTrackSize() const {
2548 maxCharWidth
* maxItemCharacters
+ TextInset
.x
* 2 +
2549 TextOffset() + ::GetSystemMetrics(SM_CXVSCROLL
),
2550 ItemHeight() * lti
.Count());
2551 AdjustWindowRect(&rc
);
2552 POINT ret
= {static_cast<LONG
>(rc
.Width()), static_cast<LONG
>(rc
.Height())};
2556 void ListBoxX::SetRedraw(bool on
) {
2557 ::SendMessage(lb
, WM_SETREDRAW
, static_cast<BOOL
>(on
), 0);
2559 ::InvalidateRect(lb
, NULL
, TRUE
);
2562 void ListBoxX::ResizeToCursor() {
2563 PRectangle rc
= GetPosition();
2565 ::GetCursorPos(&ptw
);
2566 Point
pt(ptw
.x
, ptw
.y
);
2567 pt
.x
+= dragOffset
.x
;
2568 pt
.y
+= dragOffset
.y
;
2570 switch (resizeHit
) {
2601 POINT ptMin
= MinTrackSize();
2602 POINT ptMax
= MaxTrackSize();
2603 // We don't allow the left edge to move at present, but just in case
2604 rc
.left
= Platform::Maximum(Platform::Minimum(rc
.left
, rcPreSize
.right
- ptMin
.x
), rcPreSize
.right
- ptMax
.x
);
2605 rc
.top
= Platform::Maximum(Platform::Minimum(rc
.top
, rcPreSize
.bottom
- ptMin
.y
), rcPreSize
.bottom
- ptMax
.y
);
2606 rc
.right
= Platform::Maximum(Platform::Minimum(rc
.right
, rcPreSize
.left
+ ptMax
.x
), rcPreSize
.left
+ ptMin
.x
);
2607 rc
.bottom
= Platform::Maximum(Platform::Minimum(rc
.bottom
, rcPreSize
.top
+ ptMax
.y
), rcPreSize
.top
+ ptMin
.y
);
2612 void ListBoxX::StartResize(WPARAM hitCode
) {
2613 rcPreSize
= GetPosition();
2615 ::GetCursorPos(&cursorPos
);
2621 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2622 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2626 dragOffset
.x
= rcPreSize
.right
- cursorPos
.x
;
2627 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2630 // Note that the current hit test code prevents the left edge cases ever firing
2631 // as we don't want the left edge to be moveable
2635 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2636 dragOffset
.y
= rcPreSize
.top
- cursorPos
.y
;
2639 dragOffset
.x
= rcPreSize
.left
- cursorPos
.x
;
2640 dragOffset
.y
= rcPreSize
.bottom
- cursorPos
.y
;
2647 ::SetCapture(GetHWND());
2648 resizeHit
= hitCode
;
2651 int ListBoxX::NcHitTest(WPARAM wParam
, LPARAM lParam
) const {
2652 int hit
= ::DefWindowProc(GetHWND(), WM_NCHITTEST
, wParam
, lParam
);
2653 // There is an apparent bug in the DefWindowProc hit test code whereby it will
2654 // return HTTOPXXX if the window in question is shorter than the default
2655 // window caption height + frame, even if one is hovering over the bottom edge of
2656 // the frame, so workaround that here
2657 if (hit
>= HTTOP
&& hit
<= HTTOPRIGHT
) {
2658 int minHeight
= GetSystemMetrics(SM_CYMINTRACK
);
2659 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2660 int yPos
= GET_Y_LPARAM(lParam
);
2661 if ((rc
.Height() < minHeight
) && (yPos
> ((rc
.top
+ rc
.bottom
)/2))) {
2662 hit
+= HTBOTTOM
- HTTOP
;
2666 // Nerver permit resizing that moves the left edge. Allow movement of top or bottom edge
2667 // depending on whether the list is above or below the caret
2677 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2678 // Valid only if caret below list
2679 if (location
.y
< rc
.top
)
2685 case HTBOTTOMRIGHT
: {
2686 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetPosition();
2687 // Valid only if caret above list
2688 if (rc
.bottom
< location
.y
)
2697 void ListBoxX::OnDoubleClick() {
2699 if (doubleClickAction
!= NULL
) {
2700 doubleClickAction(doubleClickActionData
);
2704 Point
ListBoxX::GetClientExtent() const {
2705 PRectangle rc
= const_cast<ListBoxX
*>(this)->GetClientPosition();
2706 return Point(rc
.Width(), rc
.Height());
2709 void ListBoxX::CentreItem(int n
) {
2710 // If below mid point, scroll up to centre, but with more items below if uneven
2712 Point extent
= GetClientExtent();
2713 int visible
= extent
.y
/ItemHeight();
2714 if (visible
< Length()) {
2715 int top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0);
2716 int half
= (visible
- 1) / 2;
2717 if (n
> (top
+ half
))
2718 ::SendMessage(lb
, LB_SETTOPINDEX
, n
- half
, 0);
2723 // Performs a double-buffered paint operation to avoid flicker
2724 void ListBoxX::Paint(HDC hDC
) {
2725 Point extent
= GetClientExtent();
2726 HBITMAP hBitmap
= ::CreateCompatibleBitmap(hDC
, extent
.x
, extent
.y
);
2727 HDC bitmapDC
= ::CreateCompatibleDC(hDC
);
2728 HBITMAP hBitmapOld
= SelectBitmap(bitmapDC
, hBitmap
);
2729 // The list background is mainly erased during painting, but can be a small
2730 // unpainted area when at the end of a non-integrally sized list with a
2731 // vertical scroll bar
2732 RECT rc
= { 0, 0, static_cast<LONG
>(extent
.x
), static_cast<LONG
>(extent
.y
) };
2733 ::FillRect(bitmapDC
, &rc
, reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1));
2734 // Paint the entire client area and vertical scrollbar
2735 ::SendMessage(lb
, WM_PRINT
, reinterpret_cast<WPARAM
>(bitmapDC
), PRF_CLIENT
|PRF_NONCLIENT
);
2736 ::BitBlt(hDC
, 0, 0, extent
.x
, extent
.y
, bitmapDC
, 0, 0, SRCCOPY
);
2737 // Select a stock brush to prevent warnings from BoundsChecker
2738 ::SelectObject(bitmapDC
, GetStockFont(WHITE_BRUSH
));
2739 SelectBitmap(bitmapDC
, hBitmapOld
);
2740 ::DeleteDC(bitmapDC
);
2741 ::DeleteObject(hBitmap
);
2744 LRESULT PASCAL
ListBoxX::ControlWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
) {
2752 HDC hDC
= ::BeginPaint(hWnd
, &ps
);
2753 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2756 ::EndPaint(hWnd
, &ps
);
2760 case WM_MOUSEACTIVATE
:
2761 // This prevents the view activating when the scrollbar is clicked
2762 return MA_NOACTIVATE
;
2764 case WM_LBUTTONDOWN
: {
2765 // We must take control of selection to prevent the ListBox activating
2767 LRESULT lResult
= ::SendMessage(hWnd
, LB_ITEMFROMPOINT
, 0, lParam
);
2768 int item
= LOWORD(lResult
);
2769 if (HIWORD(lResult
) == 0 && item
>= 0) {
2770 ::SendMessage(hWnd
, LB_SETCURSEL
, item
, 0);
2778 case WM_LBUTTONDBLCLK
: {
2779 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(::GetParent(hWnd
)));
2781 lbx
->OnDoubleClick();
2786 case WM_MBUTTONDOWN
:
2787 // disable the scroll wheel button click action
2791 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(GetWindowLongPtr(hWnd
, GWLP_USERDATA
));
2793 return ::CallWindowProc(prevWndProc
, hWnd
, uMsg
, wParam
, lParam
);
2795 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2799 return ::DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);
2802 LRESULT
ListBoxX::WndProc(HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2805 HINSTANCE hinstanceParent
= GetWindowInstance(reinterpret_cast<HWND
>(parent
->GetID()));
2806 // Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list
2807 // but has useful side effect of speeding up list population significantly
2808 lb
= ::CreateWindowEx(
2809 0, TEXT("listbox"), TEXT(""),
2810 WS_CHILD
| WS_VSCROLL
| WS_VISIBLE
|
2811 LBS_OWNERDRAWFIXED
| LBS_NODATA
| LBS_NOINTEGRALHEIGHT
,
2813 reinterpret_cast<HMENU
>(ctrlID
),
2816 WNDPROC prevWndProc
= reinterpret_cast<WNDPROC
>(::SetWindowLongPtr(lb
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(ControlWndProc
)));
2817 ::SetWindowLongPtr(lb
, GWLP_USERDATA
, reinterpret_cast<LONG_PTR
>(prevWndProc
));
2824 ::SetWindowPos(lb
, 0, 0,0, LOWORD(lParam
), HIWORD(lParam
), SWP_NOZORDER
|SWP_NOACTIVATE
|SWP_NOMOVE
);
2825 // Ensure the selection remains visible
2826 CentreItem(GetSelection());
2833 ::BeginPaint(hWnd
, &ps
);
2834 ::EndPaint(hWnd
, &ps
);
2839 // This is not actually needed now - the registered double click action is used
2840 // directly to action a choice from the list.
2841 ::SendMessage(reinterpret_cast<HWND
>(parent
->GetID()), iMessage
, wParam
, lParam
);
2844 case WM_MEASUREITEM
: {
2845 MEASUREITEMSTRUCT
*pMeasureItem
= reinterpret_cast<MEASUREITEMSTRUCT
*>(lParam
);
2846 pMeasureItem
->itemHeight
= static_cast<unsigned int>(ItemHeight());
2851 Draw(reinterpret_cast<DRAWITEMSTRUCT
*>(lParam
));
2856 ::SetWindowLong(hWnd
, 0, 0);
2857 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2860 // To reduce flicker we can elide background erasure since this window is
2861 // completely covered by its child.
2864 case WM_GETMINMAXINFO
: {
2865 MINMAXINFO
*minMax
= reinterpret_cast<MINMAXINFO
*>(lParam
);
2866 minMax
->ptMaxTrackSize
= MaxTrackSize();
2867 minMax
->ptMinTrackSize
= MinTrackSize();
2871 case WM_MOUSEACTIVATE
:
2872 return MA_NOACTIVATE
;
2875 return NcHitTest(wParam
, lParam
);
2877 case WM_NCLBUTTONDOWN
:
2878 // We have to implement our own window resizing because the DefWindowProc
2879 // implementation insists on activating the resized window
2880 StartResize(wParam
);
2883 case WM_MOUSEMOVE
: {
2884 if (resizeHit
== 0) {
2885 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2894 if (resizeHit
!= 0) {
2898 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2901 wheelDelta
-= static_cast<short>(HIWORD(wParam
));
2902 if (abs(wheelDelta
) >= WHEEL_DELTA
) {
2903 int nRows
= GetVisibleRows();
2904 int linesToScroll
= 1;
2906 linesToScroll
= nRows
- 1;
2908 if (linesToScroll
> 3) {
2911 linesToScroll
*= (wheelDelta
/ WHEEL_DELTA
);
2912 int top
= ::SendMessage(lb
, LB_GETTOPINDEX
, 0, 0) + linesToScroll
;
2916 ::SendMessage(lb
, LB_SETTOPINDEX
, top
, 0);
2917 // update wheel delta residue
2918 if (wheelDelta
>= 0)
2919 wheelDelta
= wheelDelta
% WHEEL_DELTA
;
2921 wheelDelta
= - (-wheelDelta
% WHEEL_DELTA
);
2926 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2932 LRESULT PASCAL
ListBoxX::StaticWndProc(
2933 HWND hWnd
, UINT iMessage
, WPARAM wParam
, LPARAM lParam
) {
2934 if (iMessage
== WM_CREATE
) {
2935 CREATESTRUCT
*pCreate
= reinterpret_cast<CREATESTRUCT
*>(lParam
);
2936 SetWindowPointer(hWnd
, pCreate
->lpCreateParams
);
2938 // Find C++ object associated with window.
2939 ListBoxX
*lbx
= reinterpret_cast<ListBoxX
*>(PointerFromWindow(hWnd
));
2941 return lbx
->WndProc(hWnd
, iMessage
, wParam
, lParam
);
2943 return ::DefWindowProc(hWnd
, iMessage
, wParam
, lParam
);
2947 static bool ListBoxX_Register() {
2948 WNDCLASSEX wndclassc
;
2949 wndclassc
.cbSize
= sizeof(wndclassc
);
2950 // We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for
2951 // truncated items in the list and the appearance/disappearance of the vertical scroll bar.
2952 // The list repaint is double-buffered to avoid the flicker this would otherwise cause.
2953 wndclassc
.style
= CS_GLOBALCLASS
| CS_HREDRAW
| CS_VREDRAW
;
2954 wndclassc
.cbClsExtra
= 0;
2955 wndclassc
.cbWndExtra
= sizeof(ListBoxX
*);
2956 wndclassc
.hInstance
= hinstPlatformRes
;
2957 wndclassc
.hIcon
= NULL
;
2958 wndclassc
.hbrBackground
= NULL
;
2959 wndclassc
.lpszMenuName
= NULL
;
2960 wndclassc
.lpfnWndProc
= ListBoxX::StaticWndProc
;
2961 wndclassc
.hCursor
= ::LoadCursor(NULL
, IDC_ARROW
);
2962 wndclassc
.lpszClassName
= ListBoxX_ClassName
;
2963 wndclassc
.hIconSm
= 0;
2965 return ::RegisterClassEx(&wndclassc
) != 0;
2968 bool ListBoxX_Unregister() {
2969 return ::UnregisterClass(ListBoxX_ClassName
, hinstPlatformRes
) != 0;
2972 Menu::Menu() : mid(0) {
2975 void Menu::CreatePopUp() {
2977 mid
= ::CreatePopupMenu();
2980 void Menu::Destroy() {
2982 ::DestroyMenu(reinterpret_cast<HMENU
>(mid
));
2986 void Menu::Show(Point pt
, Window
&w
) {
2987 ::TrackPopupMenu(reinterpret_cast<HMENU
>(mid
),
2988 0, pt
.x
- 4, pt
.y
, 0,
2989 reinterpret_cast<HWND
>(w
.GetID()), NULL
);
2993 static bool initialisedET
= false;
2994 static bool usePerformanceCounter
= false;
2995 static LARGE_INTEGER frequency
;
2997 ElapsedTime::ElapsedTime() {
2998 if (!initialisedET
) {
2999 usePerformanceCounter
= ::QueryPerformanceFrequency(&frequency
) != 0;
3000 initialisedET
= true;
3002 if (usePerformanceCounter
) {
3003 LARGE_INTEGER timeVal
;
3004 ::QueryPerformanceCounter(&timeVal
);
3005 bigBit
= timeVal
.HighPart
;
3006 littleBit
= timeVal
.LowPart
;
3012 double ElapsedTime::Duration(bool reset
) {
3017 if (usePerformanceCounter
) {
3019 ::QueryPerformanceCounter(&lEnd
);
3020 endBigBit
= lEnd
.HighPart
;
3021 endLittleBit
= lEnd
.LowPart
;
3022 LARGE_INTEGER lBegin
;
3023 lBegin
.HighPart
= bigBit
;
3024 lBegin
.LowPart
= littleBit
;
3025 double elapsed
= lEnd
.QuadPart
- lBegin
.QuadPart
;
3026 result
= elapsed
/ static_cast<double>(frequency
.QuadPart
);
3028 endBigBit
= clock();
3030 double elapsed
= endBigBit
- bigBit
;
3031 result
= elapsed
/ CLOCKS_PER_SEC
;
3035 littleBit
= endLittleBit
;
3040 class DynamicLibraryImpl
: public DynamicLibrary
{
3044 DynamicLibraryImpl(const char *modulePath
) {
3045 h
= ::LoadLibraryA(modulePath
);
3048 virtual ~DynamicLibraryImpl() {
3053 // Use GetProcAddress to get a pointer to the relevant function.
3054 virtual Function
FindFunction(const char *name
) {
3056 // C++ standard doesn't like casts betwen function pointers and void pointers so use a union
3061 fnConv
.fp
= ::GetProcAddress(h
, name
);
3067 virtual bool IsValid() {
3072 DynamicLibrary
*DynamicLibrary::Load(const char *modulePath
) {
3073 return static_cast<DynamicLibrary
*>(new DynamicLibraryImpl(modulePath
));
3076 ColourDesired
Platform::Chrome() {
3077 return ::GetSysColor(COLOR_3DFACE
);
3080 ColourDesired
Platform::ChromeHighlight() {
3081 return ::GetSysColor(COLOR_3DHIGHLIGHT
);
3084 const char *Platform::DefaultFont() {
3088 int Platform::DefaultFontSize() {
3092 unsigned int Platform::DoubleClickTime() {
3093 return ::GetDoubleClickTime();
3096 bool Platform::MouseButtonBounce() {
3100 void Platform::DebugDisplay(const char *s
) {
3101 ::OutputDebugStringA(s
);
3104 bool Platform::IsKeyDown(int key
) {
3105 return (::GetKeyState(key
) & 0x80000000) != 0;
3108 long Platform::SendScintilla(WindowID w
, unsigned int msg
, unsigned long wParam
, long lParam
) {
3109 return ::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
, lParam
);
3112 long Platform::SendScintillaPointer(WindowID w
, unsigned int msg
, unsigned long wParam
, void *lParam
) {
3113 return ::SendMessage(reinterpret_cast<HWND
>(w
), msg
, wParam
,
3114 reinterpret_cast<LPARAM
>(lParam
));
3117 bool Platform::IsDBCSLeadByte(int codePage
, char ch
) {
3118 return ::IsDBCSLeadByteEx(codePage
, ch
) != 0;
3121 int Platform::DBCSCharLength(int codePage
, const char *s
) {
3122 return (::IsDBCSLeadByteEx(codePage
, s
[0]) != 0) ? 2 : 1;
3125 int Platform::DBCSCharMaxLength() {
3129 // These are utility functions not really tied to a platform
3131 int Platform::Minimum(int a
, int b
) {
3138 int Platform::Maximum(int a
, int b
) {
3148 void Platform::DebugPrintf(const char *format
, ...) {
3151 va_start(pArguments
, format
);
3152 vsprintf(buffer
,format
,pArguments
);
3154 Platform::DebugDisplay(buffer
);
3157 void Platform::DebugPrintf(const char *, ...) {
3161 static bool assertionPopUps
= true;
3163 bool Platform::ShowAssertionPopUps(bool assertionPopUps_
) {
3164 bool ret
= assertionPopUps
;
3165 assertionPopUps
= assertionPopUps_
;
3169 void Platform::Assert(const char *c
, const char *file
, int line
) {
3171 sprintf(buffer
, "Assertion [%s] failed at %s %d", c
, file
, line
);
3172 if (assertionPopUps
) {
3173 int idButton
= ::MessageBoxA(0, buffer
, "Assertion failure",
3174 MB_ABORTRETRYIGNORE
|MB_ICONHAND
|MB_SETFOREGROUND
|MB_TASKMODAL
);
3175 if (idButton
== IDRETRY
) {
3177 } else if (idButton
== IDIGNORE
) {
3183 strcat(buffer
, "\r\n");
3184 Platform::DebugDisplay(buffer
);
3190 int Platform::Clamp(int val
, int minVal
, int maxVal
) {
3198 void Platform_Initialise(void *hInstance
) {
3199 OSVERSIONINFO osv
= {sizeof(OSVERSIONINFO
),0,0,0,0,TEXT("")};
3200 ::GetVersionEx(&osv
);
3201 onNT
= osv
.dwPlatformId
== VER_PLATFORM_WIN32_NT
;
3202 ::InitializeCriticalSection(&crPlatformLock
);
3203 hinstPlatformRes
= reinterpret_cast<HINSTANCE
>(hInstance
);
3204 // This may be called from DllMain, in which case the call to LoadLibrary
3205 // is bad because it can upset the DLL load order.
3207 hDLLImage
= ::LoadLibrary(TEXT("Msimg32"));
3210 AlphaBlendFn
= (AlphaBlendSig
)::GetProcAddress(hDLLImage
, "AlphaBlend");
3212 ListBoxX_Register();
3215 void Platform_Finalise() {
3216 if (reverseArrowCursor
!= NULL
)
3217 ::DestroyCursor(reverseArrowCursor
);
3218 ListBoxX_Unregister();
3219 ::DeleteCriticalSection(&crPlatformLock
);