1 // Scintilla source code edit control
\r
2 /** @file PlatWin.cxx
\r
3 ** Implementation of platform facilities on Windows.
\r
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
\r
6 // The License.txt file describes the conditions under which this software may be distributed.
\r
15 #define _WIN32_WINNT 0x0400
\r
16 #include <windows.h>
\r
17 #include <commctrl.h>
\r
18 #include <richedit.h>
\r
19 #include <windowsx.h>
\r
21 #include "Platform.h"
\r
22 #include "PlatformRes.h"
\r
23 #include "UniConversion.h"
\r
27 #define IDC_HAND MAKEINTRESOURCE(32649)
\r
30 // Take care of 32/64 bit pointers
\r
31 #ifdef GetWindowLongPtr
\r
32 static void *PointerFromWindow(HWND hWnd) {
\r
33 return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
\r
35 static void SetWindowPointer(HWND hWnd, void *ptr) {
\r
36 ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
\r
39 static void *PointerFromWindow(HWND hWnd) {
\r
40 return reinterpret_cast<void *>(::GetWindowLong(hWnd, 0));
\r
42 static void SetWindowPointer(HWND hWnd, void *ptr) {
\r
43 ::SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(ptr));
\r
46 #ifndef GWLP_USERDATA
\r
47 #define GWLP_USERDATA GWL_USERDATA
\r
50 #ifndef GWLP_WNDPROC
\r
51 #define GWLP_WNDPROC GWL_WNDPROC
\r
55 #define LONG_PTR LONG
\r
58 static LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong) {
\r
59 return ::SetWindowLong(hWnd, nIndex, dwNewLong);
\r
62 static LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex) {
\r
63 return ::GetWindowLong(hWnd, nIndex);
\r
67 typedef BOOL (WINAPI *AlphaBlendSig)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION);
\r
69 static CRITICAL_SECTION crPlatformLock;
\r
70 static HINSTANCE hinstPlatformRes = 0;
\r
71 static bool onNT = false;
\r
72 static HMODULE hDLLImage = 0;
\r
73 static AlphaBlendSig AlphaBlendFn = 0;
\r
80 #ifdef SCI_NAMESPACE
\r
81 using namespace Scintilla;
\r
84 Point Point::FromLong(long lpoint) {
\r
85 return Point(static_cast<short>(LOWORD(lpoint)), static_cast<short>(HIWORD(lpoint)));
\r
88 static RECT RectFromPRectangle(PRectangle prc) {
\r
89 RECT rc = {prc.left, prc.top, prc.right, prc.bottom};
\r
93 Palette::Palette() {
\r
95 allowRealization = false;
\r
98 entries = new ColourPair[size];
\r
101 Palette::~Palette() {
\r
107 void Palette::Release() {
\r
110 ::DeleteObject(hpal);
\r
114 entries = new ColourPair[size];
\r
118 * This method either adds a colour to the list of wanted colours (want==true)
\r
119 * or retrieves the allocated colour back to the ColourPair.
\r
120 * This is one method to make it easier to keep the code for wanting and retrieving in sync.
\r
122 void Palette::WantFind(ColourPair &cp, bool want) {
\r
124 for (int i=0; i < used; i++) {
\r
125 if (entries[i].desired == cp.desired)
\r
129 if (used >= size) {
\r
130 int sizeNew = size * 2;
\r
131 ColourPair *entriesNew = new ColourPair[sizeNew];
\r
132 for (int j=0; j<size; j++) {
\r
133 entriesNew[j] = entries[j];
\r
136 entries = entriesNew;
\r
140 entries[used].desired = cp.desired;
\r
141 entries[used].allocated.Set(cp.desired.AsLong());
\r
144 for (int i=0; i < used; i++) {
\r
145 if (entries[i].desired == cp.desired) {
\r
146 cp.allocated = entries[i].allocated;
\r
150 cp.allocated.Set(cp.desired.AsLong());
\r
154 void Palette::Allocate(Window &) {
\r
156 ::DeleteObject(hpal);
\r
159 if (allowRealization) {
\r
160 char *pal = new char[sizeof(LOGPALETTE) + (used-1) * sizeof(PALETTEENTRY)];
\r
161 LOGPALETTE *logpal = reinterpret_cast<LOGPALETTE *>(pal);
\r
162 logpal->palVersion = 0x300;
\r
163 logpal->palNumEntries = static_cast<WORD>(used);
\r
164 for (int iPal=0;iPal<used;iPal++) {
\r
165 ColourDesired desired = entries[iPal].desired;
\r
166 logpal->palPalEntry[iPal].peRed = static_cast<BYTE>(desired.GetRed());
\r
167 logpal->palPalEntry[iPal].peGreen = static_cast<BYTE>(desired.GetGreen());
\r
168 logpal->palPalEntry[iPal].peBlue = static_cast<BYTE>(desired.GetBlue());
\r
169 entries[iPal].allocated.Set(
\r
170 PALETTERGB(desired.GetRed(), desired.GetGreen(), desired.GetBlue()));
\r
171 // PC_NOCOLLAPSE means exact colours allocated even when in background this means other windows
\r
172 // are less likely to get their colours and also flashes more when switching windows
\r
173 logpal->palPalEntry[iPal].peFlags = PC_NOCOLLAPSE;
\r
174 // 0 allows approximate colours when in background, yielding moe colours to other windows
\r
175 //logpal->palPalEntry[iPal].peFlags = 0;
\r
177 hpal = ::CreatePalette(logpal);
\r
182 static void SetLogFont(LOGFONTA &lf, const char *faceName, int characterSet, int size, bool bold, bool italic) {
\r
183 memset(&lf, 0, sizeof(lf));
\r
184 // The negative is to allow for leading
\r
185 lf.lfHeight = -(abs(size));
\r
186 lf.lfWeight = bold ? FW_BOLD : FW_NORMAL;
\r
187 lf.lfItalic = static_cast<BYTE>(italic ? 1 : 0);
\r
188 lf.lfCharSet = static_cast<BYTE>(characterSet);
\r
189 strncpy(lf.lfFaceName, faceName, sizeof(lf.lfFaceName));
\r
193 * Create a hash from the parameters for a font to allow easy checking for identity.
\r
194 * If one font is the same as another, its hash will be the same, but if the hash is the
\r
195 * same then they may still be different.
\r
197 static int HashFont(const char *faceName, int characterSet, int size, bool bold, bool italic) {
\r
200 (characterSet << 10) ^
\r
201 (bold ? 0x10000000 : 0) ^
\r
202 (italic ? 0x20000000 : 0) ^
\r
206 class FontCached : Font {
\r
211 FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
\r
213 bool SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
\r
214 virtual void Release();
\r
216 static FontCached *first;
\r
218 static FontID FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
\r
219 static void ReleaseId(FontID id_);
\r
222 FontCached *FontCached::first = 0;
\r
224 FontCached::FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) :
\r
225 next(0), usage(0), hash(0) {
\r
226 SetLogFont(lf, faceName_, characterSet_, size_, bold_, italic_);
\r
227 hash = HashFont(faceName_, characterSet_, size_, bold_, italic_);
\r
228 id = ::CreateFontIndirectA(&lf);
\r
232 bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
\r
234 (lf.lfHeight == -(abs(size_))) &&
\r
235 (lf.lfWeight == (bold_ ? FW_BOLD : FW_NORMAL)) &&
\r
236 (lf.lfItalic == static_cast<BYTE>(italic_ ? 1 : 0)) &&
\r
237 (lf.lfCharSet == characterSet_) &&
\r
238 0 == strcmp(lf.lfFaceName,faceName_);
\r
241 void FontCached::Release() {
\r
243 ::DeleteObject(id);
\r
247 FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
\r
249 ::EnterCriticalSection(&crPlatformLock);
\r
250 int hashFind = HashFont(faceName_, characterSet_, size_, bold_, italic_);
\r
251 for (FontCached *cur=first; cur; cur=cur->next) {
\r
252 if ((cur->hash == hashFind) &&
\r
253 cur->SameAs(faceName_, characterSet_, size_, bold_, italic_)) {
\r
259 FontCached *fc = new FontCached(faceName_, characterSet_, size_, bold_, italic_);
\r
266 ::LeaveCriticalSection(&crPlatformLock);
\r
270 void FontCached::ReleaseId(FontID id_) {
\r
271 ::EnterCriticalSection(&crPlatformLock);
\r
272 FontCached **pcur=&first;
\r
273 for (FontCached *cur=first; cur; cur=cur->next) {
\r
274 if (cur->id == id_) {
\r
276 if (cur->usage == 0) {
\r
286 ::LeaveCriticalSection(&crPlatformLock);
\r
296 #define FONTS_CACHED
\r
298 void Font::Create(const char *faceName, int characterSet, int size,
\r
299 bool bold, bool italic, bool) {
\r
301 #ifndef FONTS_CACHED
\r
303 SetLogFont(lf, faceName, characterSet, size, bold, italic);
\r
304 id = ::CreateFontIndirect(&lf);
\r
306 id = FontCached::FindOrCreate(faceName, characterSet, size, bold, italic);
\r
310 void Font::Release() {
\r
311 #ifndef FONTS_CACHED
\r
313 ::DeleteObject(id);
\r
316 FontCached::ReleaseId(id);
\r
321 #ifdef SCI_NAMESPACE
\r
322 namespace Scintilla {
\r
325 class SurfaceImpl : public Surface {
\r
337 HPALETTE paletteOld;
\r
338 int maxWidthMeasure;
\r
342 // If 9x OS and current code page is same as ANSI code page.
\r
345 void BrushColor(ColourAllocated back);
\r
346 void SetFont(Font &font_);
\r
348 // Private so SurfaceImpl objects can not be copied
\r
349 SurfaceImpl(const SurfaceImpl &) : Surface() {}
\r
350 SurfaceImpl &operator=(const SurfaceImpl &) { return *this; }
\r
353 virtual ~SurfaceImpl();
\r
355 void Init(WindowID wid);
\r
356 void Init(SurfaceID sid, WindowID wid);
\r
357 void InitPixMap(int width, int height, Surface *surface_, WindowID wid);
\r
360 bool Initialised();
\r
361 void PenColour(ColourAllocated fore);
\r
363 int DeviceHeightFont(int points);
\r
364 void MoveTo(int x_, int y_);
\r
365 void LineTo(int x_, int y_);
\r
366 void Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back);
\r
367 void RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back);
\r
368 void FillRectangle(PRectangle rc, ColourAllocated back);
\r
369 void FillRectangle(PRectangle rc, Surface &surfacePattern);
\r
370 void RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back);
\r
371 void AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
\r
372 ColourAllocated outline, int alphaOutline, int flags);
\r
373 void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back);
\r
374 void Copy(PRectangle rc, Point from, Surface &surfaceSource);
\r
376 void DrawTextCommon(PRectangle rc, Font &font_, int ybase, const char *s, int len, UINT fuOptions);
\r
377 void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
\r
378 void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
\r
379 void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
\r
380 void MeasureWidths(Font &font_, const char *s, int len, int *positions);
\r
381 int WidthText(Font &font_, const char *s, int len);
\r
382 int WidthChar(Font &font_, char ch);
\r
383 int Ascent(Font &font_);
\r
384 int Descent(Font &font_);
\r
385 int InternalLeading(Font &font_);
\r
386 int ExternalLeading(Font &font_);
\r
387 int Height(Font &font_);
\r
388 int AverageCharWidth(Font &font_);
\r
390 int SetPalette(Palette *pal, bool inBackGround);
\r
391 void SetClip(PRectangle rc);
\r
392 void FlushCachedState();
\r
394 void SetUnicodeMode(bool unicodeMode_);
\r
395 void SetDBCSMode(int codePage_);
\r
398 #ifdef SCI_NAMESPACE
\r
399 } //namespace Scintilla
\r
402 SurfaceImpl::SurfaceImpl() :
\r
403 unicodeMode(false),
\r
404 hdc(0), hdcOwned(false),
\r
406 brush(0), brushOld(0),
\r
407 font(0), fontOld(0),
\r
408 bitmap(0), bitmapOld(0),
\r
410 // Windows 9x has only a 16 bit coordinate system so break after 30000 pixels
\r
411 maxWidthMeasure = IsNT() ? 1000000 : 30000;
\r
412 // There appears to be a 16 bit string length limit in GDI on NT and a limit of
\r
413 // 8192 characters on Windows 95.
\r
414 maxLenText = IsNT() ? 65535 : 8192;
\r
417 win9xACPSame = false;
\r
420 SurfaceImpl::~SurfaceImpl() {
\r
424 void SurfaceImpl::Release() {
\r
426 ::SelectObject(reinterpret_cast<HDC>(hdc), penOld);
\r
427 ::DeleteObject(pen);
\r
432 ::SelectObject(reinterpret_cast<HDC>(hdc), brushOld);
\r
433 ::DeleteObject(brush);
\r
438 // Fonts are not deleted as they are owned by a Font object
\r
439 ::SelectObject(reinterpret_cast<HDC>(hdc), fontOld);
\r
444 ::SelectObject(reinterpret_cast<HDC>(hdc), bitmapOld);
\r
445 ::DeleteObject(bitmap);
\r
450 // Palettes are not deleted as they are owned by a Palette object
\r
451 ::SelectPalette(reinterpret_cast<HDC>(hdc),
\r
452 reinterpret_cast<HPALETTE>(paletteOld), TRUE);
\r
456 ::DeleteDC(reinterpret_cast<HDC>(hdc));
\r
462 bool SurfaceImpl::Initialised() {
\r
466 void SurfaceImpl::Init(WindowID) {
\r
468 hdc = ::CreateCompatibleDC(NULL);
\r
470 ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE);
\r
473 void SurfaceImpl::Init(SurfaceID sid, WindowID) {
\r
475 hdc = reinterpret_cast<HDC>(sid);
\r
476 ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE);
\r
479 void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID) {
\r
481 hdc = ::CreateCompatibleDC(static_cast<SurfaceImpl *>(surface_)->hdc);
\r
483 bitmap = ::CreateCompatibleBitmap(static_cast<SurfaceImpl *>(surface_)->hdc, width, height);
\r
484 bitmapOld = static_cast<HBITMAP>(::SelectObject(hdc, bitmap));
\r
485 ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE);
\r
488 void SurfaceImpl::PenColour(ColourAllocated fore) {
\r
490 ::SelectObject(hdc, penOld);
\r
491 ::DeleteObject(pen);
\r
495 pen = ::CreatePen(0,1,fore.AsLong());
\r
496 penOld = static_cast<HPEN>(::SelectObject(reinterpret_cast<HDC>(hdc), pen));
\r
499 void SurfaceImpl::BrushColor(ColourAllocated back) {
\r
501 ::SelectObject(hdc, brushOld);
\r
502 ::DeleteObject(brush);
\r
506 // Only ever want pure, non-dithered brushes
\r
507 ColourAllocated colourNearest = ::GetNearestColor(hdc, back.AsLong());
\r
508 brush = ::CreateSolidBrush(colourNearest.AsLong());
\r
509 brushOld = static_cast<HBRUSH>(::SelectObject(hdc, brush));
\r
512 void SurfaceImpl::SetFont(Font &font_) {
\r
513 if (font_.GetID() != font) {
\r
515 ::SelectObject(hdc, font_.GetID());
\r
517 fontOld = static_cast<HFONT>(::SelectObject(hdc, font_.GetID()));
\r
519 font = reinterpret_cast<HFONT>(font_.GetID());
\r
523 int SurfaceImpl::LogPixelsY() {
\r
524 return ::GetDeviceCaps(hdc, LOGPIXELSY);
\r
527 int SurfaceImpl::DeviceHeightFont(int points) {
\r
528 return ::MulDiv(points, LogPixelsY(), 72);
\r
531 void SurfaceImpl::MoveTo(int x_, int y_) {
\r
532 ::MoveToEx(hdc, x_, y_, 0);
\r
535 void SurfaceImpl::LineTo(int x_, int y_) {
\r
536 ::LineTo(hdc, x_, y_);
\r
539 void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back) {
\r
542 ::Polygon(hdc, reinterpret_cast<POINT *>(pts), npts);
\r
545 void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
\r
548 ::Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
\r
551 void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) {
\r
552 // Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
\r
553 // There is no need to allocate a brush either.
\r
554 RECT rcw = RectFromPRectangle(rc);
\r
555 ::SetBkColor(hdc, back.AsLong());
\r
556 ::ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, &rcw, TEXT(""), 0, NULL);
\r
559 void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) {
\r
561 if (static_cast<SurfaceImpl &>(surfacePattern).bitmap)
\r
562 br = ::CreatePatternBrush(static_cast<SurfaceImpl &>(surfacePattern).bitmap);
\r
563 else // Something is wrong so display in red
\r
564 br = ::CreateSolidBrush(RGB(0xff, 0, 0));
\r
565 RECT rcw = RectFromPRectangle(rc);
\r
566 ::FillRect(hdc, &rcw, br);
\r
567 ::DeleteObject(br);
\r
570 void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
\r
574 rc.left + 1, rc.top,
\r
575 rc.right - 1, rc.bottom,
\r
579 // Plot a point into a DWORD buffer symetrically to all 4 qudrants
\r
580 static void AllFour(DWORD *pixels, int width, int height, int x, int y, DWORD val) {
\r
581 pixels[y*width+x] = val;
\r
582 pixels[y*width+width-1-x] = val;
\r
583 pixels[(height-1-y)*width+x] = val;
\r
584 pixels[(height-1-y)*width+width-1-x] = val;
\r
587 #ifndef AC_SRC_OVER
\r
588 #define AC_SRC_OVER 0x00
\r
590 #ifndef AC_SRC_ALPHA
\r
591 #define AC_SRC_ALPHA 0x01
\r
594 void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
\r
595 ColourAllocated outline, int alphaOutline, int /* flags*/ ) {
\r
596 if (AlphaBlendFn && rc.Width() > 0) {
\r
597 HDC hMemDC = ::CreateCompatibleDC(reinterpret_cast<HDC>(hdc));
\r
598 int width = rc.Width();
\r
599 int height = rc.Height();
\r
600 // Ensure not distorted too much by corners when small
\r
601 cornerSize = Platform::Minimum(cornerSize, (Platform::Minimum(width, height) / 2) - 2);
\r
602 BITMAPINFO bpih = {sizeof(BITMAPINFOHEADER), width, height, 1, 32, BI_RGB, 0, 0, 0, 0, 0};
\r
604 HBITMAP hbmMem = CreateDIBSection(reinterpret_cast<HDC>(hMemDC), &bpih,
\r
605 DIB_RGB_COLORS, &image, NULL, 0);
\r
607 HBITMAP hbmOld = SelectBitmap(hMemDC, hbmMem);
\r
609 byte pixVal[4] = {0};
\r
610 DWORD valEmpty = *(reinterpret_cast<DWORD *>(pixVal));
\r
611 pixVal[0] = static_cast<byte>(GetBValue(fill.AsLong()) * alphaFill / 255);
\r
612 pixVal[1] = static_cast<byte>(GetGValue(fill.AsLong()) * alphaFill / 255);
\r
613 pixVal[2] = static_cast<byte>(GetRValue(fill.AsLong()) * alphaFill / 255);
\r
614 pixVal[3] = static_cast<byte>(alphaFill);
\r
615 DWORD valFill = *(reinterpret_cast<DWORD *>(pixVal));
\r
616 pixVal[0] = static_cast<byte>(GetBValue(outline.AsLong()) * alphaOutline / 255);
\r
617 pixVal[1] = static_cast<byte>(GetGValue(outline.AsLong()) * alphaOutline / 255);
\r
618 pixVal[2] = static_cast<byte>(GetRValue(outline.AsLong()) * alphaOutline / 255);
\r
619 pixVal[3] = static_cast<byte>(alphaOutline);
\r
620 DWORD valOutline = *(reinterpret_cast<DWORD *>(pixVal));
\r
621 DWORD *pixels = reinterpret_cast<DWORD *>(image);
\r
622 for (int y=0; y<height; y++) {
\r
623 for (int x=0; x<width; x++) {
\r
624 if ((x==0) || (x==width-1) || (y == 0) || (y == height-1)) {
\r
625 pixels[y*width+x] = valOutline;
\r
627 pixels[y*width+x] = valFill;
\r
631 for (int c=0;c<cornerSize; c++) {
\r
632 for (int x=0;x<c+1; x++) {
\r
633 AllFour(pixels, width, height, x, c-x, valEmpty);
\r
636 for (int x=1;x<cornerSize; x++) {
\r
637 AllFour(pixels, width, height, x, cornerSize-x, valOutline);
\r
640 BLENDFUNCTION merge = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
\r
642 AlphaBlendFn(reinterpret_cast<HDC>(hdc), rc.left, rc.top, width, height, hMemDC, 0, 0, width, height, merge);
\r
644 SelectBitmap(hMemDC, hbmOld);
\r
645 ::DeleteObject(hbmMem);
\r
646 ::DeleteDC(hMemDC);
\r
648 BrushColor(outline);
\r
649 RECT rcw = RectFromPRectangle(rc);
\r
650 FrameRect(hdc, &rcw, brush);
\r
654 void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
\r
657 ::Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom);
\r
660 void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
\r
662 rc.left, rc.top, rc.Width(), rc.Height(),
\r
663 static_cast<SurfaceImpl &>(surfaceSource).hdc, from.x, from.y, SRCCOPY);
\r
666 // Buffer to hold strings and string position arrays without always allocating on heap.
\r
667 // May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer
\r
668 // when less than safe size otherwise allocate on heap and free automatically.
\r
669 template<typename T, int lengthStandard>
\r
671 T bufferStandard[lengthStandard];
\r
674 VarBuffer(size_t length) : buffer(0) {
\r
675 if (length > lengthStandard) {
\r
676 buffer = new T[length];
\r
678 buffer = bufferStandard;
\r
682 if (buffer != bufferStandard) {
\r
689 const int stackBufferLength = 10000;
\r
690 class TextWide : public VarBuffer<wchar_t, stackBufferLength> {
\r
693 TextWide(const char *s, int len, bool unicodeMode, int codePage=0) :
\r
694 VarBuffer<wchar_t, stackBufferLength>(len) {
\r
696 tlen = UTF16FromUTF8(s, len, buffer, len);
\r
698 // Support Asian string display in 9x English
\r
699 tlen = ::MultiByteToWideChar(codePage, 0, s, len, buffer, len);
\r
703 typedef VarBuffer<int, stackBufferLength> TextPositions;
\r
705 void SurfaceImpl::DrawTextCommon(PRectangle rc, Font &font_, int ybase, const char *s, int len, UINT fuOptions) {
\r
707 RECT rcw = RectFromPRectangle(rc);
\r
712 // Text drawing may fail if the text is too big.
\r
713 // If it does fail, slice up into segments and draw each segment.
\r
714 const int maxSegmentLength = 0x200;
\r
716 if ((!unicodeMode) && (IsNT() || (codePage==0) || win9xACPSame)) {
\r
718 int lenDraw = Platform::Minimum(len, maxLenText);
\r
719 if (!::ExtTextOutA(hdc, x, ybase, fuOptions, &rcw, s, lenDraw, NULL)) {
\r
720 while (lenDraw > pos) {
\r
721 int seglen = Platform::Minimum(maxSegmentLength, lenDraw - pos);
\r
722 if (!::ExtTextOutA(hdc, x, ybase, fuOptions, &rcw, s+pos, seglen, NULL)) {
\r
723 PLATFORM_ASSERT(false);
\r
726 ::GetTextExtentPoint32A(hdc, s+pos, seglen, &sz);
\r
732 // Use Unicode calls
\r
733 const TextWide tbuf(s, len, unicodeMode, codePage);
\r
734 if (!::ExtTextOutW(hdc, x, ybase, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, NULL)) {
\r
735 while (tbuf.tlen > pos) {
\r
736 int seglen = Platform::Minimum(maxSegmentLength, tbuf.tlen - pos);
\r
737 if (!::ExtTextOutW(hdc, x, ybase, fuOptions, &rcw, tbuf.buffer+pos, seglen, NULL)) {
\r
738 PLATFORM_ASSERT(false);
\r
741 ::GetTextExtentPoint32W(hdc, tbuf.buffer+pos, seglen, &sz);
\r
749 void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len,
\r
750 ColourAllocated fore, ColourAllocated back) {
\r
751 ::SetTextColor(hdc, fore.AsLong());
\r
752 ::SetBkColor(hdc, back.AsLong());
\r
753 DrawTextCommon(rc, font_, ybase, s, len, ETO_OPAQUE);
\r
756 void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len,
\r
757 ColourAllocated fore, ColourAllocated back) {
\r
758 ::SetTextColor(hdc, fore.AsLong());
\r
759 ::SetBkColor(hdc, back.AsLong());
\r
760 DrawTextCommon(rc, font_, ybase, s, len, ETO_OPAQUE | ETO_CLIPPED);
\r
763 void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len,
\r
764 ColourAllocated fore) {
\r
765 // Avoid drawing spaces in transparent mode
\r
766 for (int i=0;i<len;i++) {
\r
768 ::SetTextColor(hdc, fore.AsLong());
\r
769 ::SetBkMode(hdc, TRANSPARENT);
\r
770 DrawTextCommon(rc, font_, ybase, s, len, 0);
\r
771 ::SetBkMode(hdc, OPAQUE);
\r
777 int SurfaceImpl::WidthText(Font &font_, const char *s, int len) {
\r
780 if ((!unicodeMode) && (IsNT() || (codePage==0) || win9xACPSame)) {
\r
781 ::GetTextExtentPoint32A(hdc, s, Platform::Minimum(len, maxLenText), &sz);
\r
783 const TextWide tbuf(s, len, unicodeMode, codePage);
\r
784 ::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);
\r
789 void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) {
\r
794 const TextWide tbuf(s, len, unicodeMode, codePage);
\r
795 TextPositions poses(tbuf.tlen);
\r
797 if (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) {
\r
798 // Likely to have failed because on Windows 9x where function not available
\r
799 // So measure the character widths by measuring each initial substring
\r
800 // Turns a linear operation into a qudratic but seems fast enough on test files
\r
801 for (int widthSS=0; widthSS < tbuf.tlen; widthSS++) {
\r
802 ::GetTextExtentPoint32W(hdc, tbuf.buffer, widthSS+1, &sz);
\r
803 poses.buffer[widthSS] = sz.cx;
\r
806 // Map the widths given for UTF-16 characters back onto the UTF-8 input string
\r
808 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
\r
811 unsigned char uch = us[i];
\r
812 unsigned int lenChar = 1;
\r
813 if (uch >= (0x80 + 0x40 + 0x20 + 0x10)) {
\r
816 } else if (uch >= (0x80 + 0x40 + 0x20)) {
\r
818 } else if (uch >= (0x80)) {
\r
821 for (unsigned int bytePos=0; (bytePos<lenChar) && (i<len); bytePos++) {
\r
822 positions[i++] = poses.buffer[ui];
\r
828 lastPos = positions[i-1];
\r
830 positions[i++] = lastPos;
\r
832 } else if (IsNT() || (codePage==0) || win9xACPSame) {
\r
833 // Zero positions to avoid random behaviour on failure.
\r
834 memset(positions, 0, len * sizeof(*positions));
\r
835 // len may be larger than platform supports so loop over segments small enough for platform
\r
836 int startOffset = 0;
\r
838 int lenBlock = Platform::Minimum(len, maxLenText);
\r
839 if (!::GetTextExtentExPointA(hdc, s, lenBlock, maxWidthMeasure, &fit, positions, &sz)) {
\r
840 // Eeek - a NULL DC or other foolishness could cause this.
\r
842 } else if (fit < lenBlock) {
\r
843 // For some reason, such as an incomplete DBCS character
\r
844 // Not all the positions are filled in so make them equal to end.
\r
845 for (int i=fit;i<lenBlock;i++)
\r
846 positions[i] = positions[fit-1];
\r
847 } else if (startOffset > 0) {
\r
848 for (int i=0;i<lenBlock;i++)
\r
849 positions[i] += startOffset;
\r
851 startOffset = positions[lenBlock-1];
\r
853 positions += lenBlock;
\r
857 // Support Asian string display in 9x English
\r
858 const TextWide tbuf(s, len, unicodeMode, codePage);
\r
859 TextPositions poses(tbuf.tlen);
\r
860 for (int widthSS=0; widthSS<tbuf.tlen; widthSS++) {
\r
861 ::GetTextExtentPoint32W(hdc, tbuf.buffer, widthSS+1, &sz);
\r
862 poses.buffer[widthSS] = sz.cx;
\r
866 for (int i=0;i<len;) {
\r
867 if (::IsDBCSLeadByteEx(codePage, s[i])) {
\r
868 positions[i] = poses.buffer[ui];
\r
869 positions[i+1] = poses.buffer[ui];
\r
872 positions[i] = poses.buffer[ui];
\r
881 int SurfaceImpl::WidthChar(Font &font_, char ch) {
\r
884 ::GetTextExtentPoint32A(hdc, &ch, 1, &sz);
\r
888 int SurfaceImpl::Ascent(Font &font_) {
\r
891 ::GetTextMetrics(hdc, &tm);
\r
892 return tm.tmAscent;
\r
895 int SurfaceImpl::Descent(Font &font_) {
\r
898 ::GetTextMetrics(hdc, &tm);
\r
899 return tm.tmDescent;
\r
902 int SurfaceImpl::InternalLeading(Font &font_) {
\r
905 ::GetTextMetrics(hdc, &tm);
\r
906 return tm.tmInternalLeading;
\r
909 int SurfaceImpl::ExternalLeading(Font &font_) {
\r
912 ::GetTextMetrics(hdc, &tm);
\r
913 return tm.tmExternalLeading;
\r
916 int SurfaceImpl::Height(Font &font_) {
\r
919 ::GetTextMetrics(hdc, &tm);
\r
920 return tm.tmHeight;
\r
923 int SurfaceImpl::AverageCharWidth(Font &font_) {
\r
926 ::GetTextMetrics(hdc, &tm);
\r
927 return tm.tmAveCharWidth;
\r
930 int SurfaceImpl::SetPalette(Palette *pal, bool inBackGround) {
\r
932 ::SelectPalette(hdc, paletteOld, TRUE);
\r
936 if (pal->allowRealization) {
\r
937 paletteOld = ::SelectPalette(hdc,
\r
938 reinterpret_cast<HPALETTE>(pal->hpal), inBackGround);
\r
939 changes = ::RealizePalette(hdc);
\r
944 void SurfaceImpl::SetClip(PRectangle rc) {
\r
945 ::IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
\r
948 void SurfaceImpl::FlushCachedState() {
\r
954 void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) {
\r
955 unicodeMode=unicodeMode_;
\r
958 void SurfaceImpl::SetDBCSMode(int codePage_) {
\r
959 // No action on window as automatically handled by system.
\r
960 codePage = codePage_;
\r
961 win9xACPSame = !IsNT() && ((unsigned int)codePage == ::GetACP());
\r
964 Surface *Surface::Allocate() {
\r
965 return new SurfaceImpl;
\r
968 Window::~Window() {
\r
971 void Window::Destroy() {
\r
973 ::DestroyWindow(reinterpret_cast<HWND>(id));
\r
977 bool Window::HasFocus() {
\r
978 return ::GetFocus() == id;
\r
981 PRectangle Window::GetPosition() {
\r
983 ::GetWindowRect(reinterpret_cast<HWND>(id), &rc);
\r
984 return PRectangle(rc.left, rc.top, rc.right, rc.bottom);
\r
987 void Window::SetPosition(PRectangle rc) {
\r
988 ::SetWindowPos(reinterpret_cast<HWND>(id),
\r
989 0, rc.left, rc.top, rc.Width(), rc.Height(), SWP_NOZORDER|SWP_NOACTIVATE);
\r
992 void Window::SetPositionRelative(PRectangle rc, Window w) {
\r
993 LONG style = ::GetWindowLong(reinterpret_cast<HWND>(id), GWL_STYLE);
\r
994 if (style & WS_POPUP) {
\r
996 ::GetWindowRect(reinterpret_cast<HWND>(w.GetID()), &rcOther);
\r
997 rc.Move(rcOther.left, rcOther.top);
\r
999 // Retrieve desktop bounds and make sure window popup's origin isn't left-top of the screen.
\r
1000 RECT rcDesktop = {0, 0, 0, 0};
\r
1001 #ifdef SM_XVIRTUALSCREEN
\r
1002 rcDesktop.left = ::GetSystemMetrics(SM_XVIRTUALSCREEN);
\r
1003 rcDesktop.top = ::GetSystemMetrics(SM_YVIRTUALSCREEN);
\r
1004 rcDesktop.right = rcDesktop.left + ::GetSystemMetrics(SM_CXVIRTUALSCREEN);
\r
1005 rcDesktop.bottom = rcDesktop.top + ::GetSystemMetrics(SM_CYVIRTUALSCREEN);
\r
1008 if (rc.left < rcDesktop.left) {
\r
1009 rc.Move(rcDesktop.left - rc.left,0);
\r
1011 if (rc.top < rcDesktop.top) {
\r
1012 rc.Move(0,rcDesktop.top - rc.top);
\r
1018 PRectangle Window::GetClientPosition() {
\r
1019 RECT rc={0,0,0,0};
\r
1021 ::GetClientRect(reinterpret_cast<HWND>(id), &rc);
\r
1022 return PRectangle(rc.left, rc.top, rc.right, rc.bottom);
\r
1025 void Window::Show(bool show) {
\r
1027 ::ShowWindow(reinterpret_cast<HWND>(id), SW_SHOWNOACTIVATE);
\r
1029 ::ShowWindow(reinterpret_cast<HWND>(id), SW_HIDE);
\r
1032 void Window::InvalidateAll() {
\r
1033 ::InvalidateRect(reinterpret_cast<HWND>(id), NULL, FALSE);
\r
1036 void Window::InvalidateRectangle(PRectangle rc) {
\r
1037 RECT rcw = RectFromPRectangle(rc);
\r
1038 ::InvalidateRect(reinterpret_cast<HWND>(id), &rcw, FALSE);
\r
1041 static LRESULT Window_SendMessage(Window *w, UINT msg, WPARAM wParam=0, LPARAM lParam=0) {
\r
1042 return ::SendMessage(reinterpret_cast<HWND>(w->GetID()), msg, wParam, lParam);
\r
1045 void Window::SetFont(Font &font) {
\r
1046 Window_SendMessage(this, WM_SETFONT,
\r
1047 reinterpret_cast<WPARAM>(font.GetID()), 0);
\r
1050 void Window::SetCursor(Cursor curs) {
\r
1053 ::SetCursor(::LoadCursor(NULL,IDC_IBEAM));
\r
1056 ::SetCursor(::LoadCursor(NULL,IDC_UPARROW));
\r
1059 ::SetCursor(::LoadCursor(NULL,IDC_WAIT));
\r
1062 ::SetCursor(::LoadCursor(NULL,IDC_SIZEWE));
\r
1065 ::SetCursor(::LoadCursor(NULL,IDC_SIZENS));
\r
1068 ::SetCursor(::LoadCursor(NULL,IDC_HAND));
\r
1070 case cursorReverseArrow: {
\r
1071 if (!hinstPlatformRes)
\r
1072 hinstPlatformRes = ::GetModuleHandle(TEXT("Scintilla"));
\r
1073 if (!hinstPlatformRes)
\r
1074 hinstPlatformRes = ::GetModuleHandle(TEXT("SciLexer"));
\r
1075 if (!hinstPlatformRes)
\r
1076 hinstPlatformRes = ::GetModuleHandle(NULL);
\r
1077 HCURSOR hcursor = ::LoadCursor(hinstPlatformRes, MAKEINTRESOURCE(IDC_MARGIN));
\r
1079 ::SetCursor(hcursor);
\r
1081 ::SetCursor(::LoadCursor(NULL,IDC_ARROW));
\r
1085 case cursorInvalid: // Should not occur, but just in case.
\r
1086 ::SetCursor(::LoadCursor(NULL,IDC_ARROW));
\r
1091 void Window::SetTitle(const char *s) {
\r
1092 ::SetWindowTextA(reinterpret_cast<HWND>(id), s);
\r
1095 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
\r
1097 #ifdef MULTIPLE_MONITOR_SUPPORT
\r
1098 PRectangle Window::GetMonitorRect(Point pt) {
\r
1099 // MonitorFromPoint and GetMonitorInfo are not available on Windows 95 so are not used.
\r
1100 // There could be conditional code and dynamic loading in a future version
\r
1101 // so this would work on those platforms where they are available.
\r
1102 PRectangle rcPosition = GetPosition();
\r
1103 POINT ptDesktop = {pt.x + rcPosition.left, pt.y + rcPosition.top};
\r
1104 HMONITOR hMonitor = ::MonitorFromPoint(ptDesktop, MONITOR_DEFAULTTONEAREST);
\r
1106 memset(&mi, 0, sizeof(mi));
\r
1107 mi.cbSize = sizeof(mi);
\r
1108 if (::GetMonitorInfo(hMonitor, &mi)) {
\r
1109 PRectangle rcMonitor(
\r
1110 mi.rcWork.left - rcPosition.left,
\r
1111 mi.rcWork.top - rcPosition.top,
\r
1112 mi.rcWork.right - rcPosition.left,
\r
1113 mi.rcWork.bottom - rcPosition.top);
\r
1118 PRectangle Window::GetMonitorRect(Point) {
\r
1119 return PRectangle();
\r
1123 struct ListItemData {
\r
1128 #define _ROUND2(n,pow2) \
\r
1129 ( ( (n) + (pow2) - 1) & ~((pow2) - 1) )
\r
1131 class LineToItem {
\r
1136 ListItemData *data;
\r
1141 void FreeWords() {
\r
1147 char *AllocWord(const char *word);
\r
1150 LineToItem() : words(NULL), wordsCount(0), wordsSize(0), data(NULL), len(0), count(0) {
\r
1163 ListItemData *Append(const char *text, int value);
\r
1165 ListItemData Get(int index) const {
\r
1166 if (index >= 0 && index < count) {
\r
1167 return data[index];
\r
1169 ListItemData missing = {"", -1};
\r
1173 int Count() const {
\r
1177 ListItemData *AllocItem();
\r
1179 void SetWords(char *s) {
\r
1180 words = s; // N.B. will be deleted on destruction
\r
1184 char *LineToItem::AllocWord(const char *text) {
\r
1185 int chars = strlen(text) + 1;
\r
1186 int newCount = wordsCount + chars;
\r
1187 if (newCount > wordsSize) {
\r
1188 wordsSize = _ROUND2(newCount * 2, 8192);
\r
1189 char *wordsNew = new char[wordsSize];
\r
1190 memcpy(wordsNew, words, wordsCount);
\r
1191 int offset = wordsNew - words;
\r
1192 for (int i=0; i<count; i++)
\r
1193 data[i].text += offset;
\r
1197 char *s = &words[wordsCount];
\r
1198 wordsCount = newCount;
\r
1199 strncpy(s, text, chars);
\r
1203 ListItemData *LineToItem::AllocItem() {
\r
1204 if (count >= len) {
\r
1205 int lenNew = _ROUND2((count+1) * 2, 1024);
\r
1206 ListItemData *dataNew = new ListItemData[lenNew];
\r
1207 memcpy(dataNew, data, count * sizeof(ListItemData));
\r
1212 ListItemData *item = &data[count];
\r
1217 ListItemData *LineToItem::Append(const char *text, int imageIndex) {
\r
1218 ListItemData *item = AllocItem();
\r
1219 item->text = AllocWord(text);
\r
1220 item->pixId = imageIndex;
\r
1224 const TCHAR ListBoxX_ClassName[] = TEXT("ListBoxX");
\r
1226 ListBox::ListBox() {
\r
1229 ListBox::~ListBox() {
\r
1232 class ListBoxX : public ListBox {
\r
1239 int desiredVisibleRows;
\r
1240 unsigned int maxItemCharacters;
\r
1241 unsigned int aveCharWidth;
\r
1244 CallBackAction doubleClickAction;
\r
1245 void *doubleClickActionData;
\r
1246 const char *widestItem;
\r
1247 unsigned int maxCharWidth;
\r
1249 PRectangle rcPreSize;
\r
1251 Point location; // Caret location at which the list is opened
\r
1253 HWND GetHWND() const;
\r
1254 void AppendListItem(const char *startword, const char *numword);
\r
1255 void AdjustWindowRect(PRectangle *rc) const;
\r
1256 int ItemHeight() const;
\r
1257 int MinClientWidth() const;
\r
1258 int TextOffset() const;
\r
1259 Point GetClientExtent() const;
\r
1260 Point MinTrackSize() const;
\r
1261 Point MaxTrackSize() const;
\r
1262 void SetRedraw(bool on);
\r
1263 void OnDoubleClick();
\r
1264 void ResizeToCursor();
\r
1265 void StartResize(WPARAM);
\r
1266 int NcHitTest(WPARAM, LPARAM) const;
\r
1267 void CentreItem(int);
\r
1270 static LRESULT PASCAL ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
\r
1272 static const Point ItemInset; // Padding around whole item
\r
1273 static const Point TextInset; // Padding around text
\r
1274 static const Point ImageInset; // Padding around image
\r
1277 ListBoxX() : lineHeight(10), fontCopy(0), lb(0), unicodeMode(false),
\r
1278 desiredVisibleRows(5), maxItemCharacters(0), aveCharWidth(8),
\r
1279 parent(NULL), ctrlID(0), doubleClickAction(NULL), doubleClickActionData(NULL),
\r
1280 widestItem(NULL), maxCharWidth(1), resizeHit(0) {
\r
1282 virtual ~ListBoxX() {
\r
1284 ::DeleteObject(fontCopy);
\r
1288 virtual void SetFont(Font &font);
\r
1289 virtual void Create(Window &parent, int ctrlID, Point location_, int lineHeight_, bool unicodeMode_);
\r
1290 virtual void SetAverageCharWidth(int width);
\r
1291 virtual void SetVisibleRows(int rows);
\r
1292 virtual int GetVisibleRows() const;
\r
1293 virtual PRectangle GetDesiredRect();
\r
1294 virtual int CaretFromEdge();
\r
1295 virtual void Clear();
\r
1296 virtual void Append(char *s, int type = -1);
\r
1297 virtual int Length();
\r
1298 virtual void Select(int n);
\r
1299 virtual int GetSelection();
\r
1300 virtual int Find(const char *prefix);
\r
1301 virtual void GetValue(int n, char *value, int len);
\r
1302 virtual void RegisterImage(int type, const char *xpm_data);
\r
1303 virtual void ClearRegisteredImages();
\r
1304 virtual void SetDoubleClickAction(CallBackAction action, void *data) {
\r
1305 doubleClickAction = action;
\r
1306 doubleClickActionData = data;
\r
1308 virtual void SetList(const char *list, char separator, char typesep);
\r
1309 void Draw(DRAWITEMSTRUCT *pDrawItem);
\r
1310 LRESULT WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
\r
1311 static LRESULT PASCAL StaticWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
\r
1314 const Point ListBoxX::ItemInset(0, 0);
\r
1315 const Point ListBoxX::TextInset(2, 0);
\r
1316 const Point ListBoxX::ImageInset(1, 0);
\r
1318 ListBox *ListBox::Allocate() {
\r
1319 ListBoxX *lb = new ListBoxX();
\r
1323 void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_) {
\r
1324 parent = &parent_;
\r
1326 location = location_;
\r
1327 lineHeight = lineHeight_;
\r
1328 unicodeMode = unicodeMode_;
\r
1329 HWND hwndParent = reinterpret_cast<HWND>(parent->GetID());
\r
1330 HINSTANCE hinstanceParent = GetWindowInstance(hwndParent);
\r
1331 // Window created as popup so not clipped within parent client area
\r
1332 id = ::CreateWindowEx(
\r
1333 WS_EX_WINDOWEDGE, ListBoxX_ClassName, TEXT(""),
\r
1334 WS_POPUP | WS_THICKFRAME,
\r
1335 100,100, 150,80, hwndParent,
\r
1340 ::MapWindowPoints(hwndParent, NULL, reinterpret_cast<POINT*>(&location), 1);
\r
1343 void ListBoxX::SetFont(Font &font) {
\r
1345 if (0 != ::GetObject(font.GetID(), sizeof(lf), &lf)) {
\r
1347 ::DeleteObject(fontCopy);
\r
1350 fontCopy = ::CreateFontIndirect(&lf);
\r
1351 ::SendMessage(lb, WM_SETFONT, reinterpret_cast<WPARAM>(fontCopy), 0);
\r
1355 void ListBoxX::SetAverageCharWidth(int width) {
\r
1356 aveCharWidth = width;
\r
1359 void ListBoxX::SetVisibleRows(int rows) {
\r
1360 desiredVisibleRows = rows;
\r
1363 int ListBoxX::GetVisibleRows() const {
\r
1364 return desiredVisibleRows;
\r
1367 HWND ListBoxX::GetHWND() const {
\r
1368 return reinterpret_cast<HWND>(GetID());
\r
1371 PRectangle ListBoxX::GetDesiredRect() {
\r
1372 PRectangle rcDesired = GetPosition();
\r
1374 int rows = Length();
\r
1375 if ((rows == 0) || (rows > desiredVisibleRows))
\r
1376 rows = desiredVisibleRows;
\r
1377 rcDesired.bottom = rcDesired.top + ItemHeight() * rows;
\r
1379 int width = MinClientWidth();
\r
1380 HDC hdc = ::GetDC(lb);
\r
1381 HFONT oldFont = SelectFont(hdc, fontCopy);
\r
1382 SIZE textSize = {0, 0};
\r
1383 int len = widestItem ? strlen(widestItem) : 0;
\r
1384 if (unicodeMode) {
\r
1385 const TextWide tbuf(widestItem, len, unicodeMode);
\r
1386 ::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &textSize);
\r
1388 ::GetTextExtentPoint32A(hdc, widestItem, len, &textSize);
\r
1391 ::GetTextMetrics(hdc, &tm);
\r
1392 maxCharWidth = tm.tmMaxCharWidth;
\r
1393 SelectFont(hdc, oldFont);
\r
1394 ::ReleaseDC(lb, hdc);
\r
1396 int widthDesired = Platform::Maximum(textSize.cx, (len + 1) * tm.tmAveCharWidth);
\r
1397 if (width < widthDesired)
\r
1398 width = widthDesired;
\r
1400 rcDesired.right = rcDesired.left + TextOffset() + width + (TextInset.x * 2);
\r
1401 if (Length() > rows)
\r
1402 rcDesired.right += ::GetSystemMetrics(SM_CXVSCROLL);
\r
1404 AdjustWindowRect(&rcDesired);
\r
1408 int ListBoxX::TextOffset() const {
\r
1409 int pixWidth = const_cast<XPMSet*>(&xset)->GetWidth();
\r
1410 return pixWidth == 0 ? ItemInset.x : ItemInset.x + pixWidth + (ImageInset.x * 2);
\r
1413 int ListBoxX::CaretFromEdge() {
\r
1415 AdjustWindowRect(&rc);
\r
1416 return TextOffset() + TextInset.x + (0 - rc.left) - 1;
\r
1419 void ListBoxX::Clear() {
\r
1420 ::SendMessage(lb, LB_RESETCONTENT, 0, 0);
\r
1421 maxItemCharacters = 0;
\r
1422 widestItem = NULL;
\r
1426 void ListBoxX::Append(char *s, int type) {
\r
1427 int index = ::SendMessage(lb, LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(s));
\r
1430 ListItemData *newItem = lti.Append(s, type);
\r
1431 unsigned int len = static_cast<unsigned int>(strlen(s));
\r
1432 if (maxItemCharacters < len) {
\r
1433 maxItemCharacters = len;
\r
1434 widestItem = newItem->text;
\r
1438 int ListBoxX::Length() {
\r
1439 return lti.Count();
\r
1442 void ListBoxX::Select(int n) {
\r
1443 // We are going to scroll to centre on the new selection and then select it, so disable
\r
1444 // redraw to avoid flicker caused by a painting new selection twice in unselected and then
\r
1445 // selected states
\r
1448 ::SendMessage(lb, LB_SETCURSEL, n, 0);
\r
1452 int ListBoxX::GetSelection() {
\r
1453 return ::SendMessage(lb, LB_GETCURSEL, 0, 0);
\r
1456 // This is not actually called at present
\r
1457 int ListBoxX::Find(const char *) {
\r
1461 void ListBoxX::GetValue(int n, char *value, int len) {
\r
1462 ListItemData item = lti.Get(n);
\r
1463 strncpy(value, item.text, len);
\r
1464 value[len-1] = '\0';
\r
1467 void ListBoxX::RegisterImage(int type, const char *xpm_data) {
\r
1468 xset.Add(type, xpm_data);
\r
1471 void ListBoxX::ClearRegisteredImages() {
\r
1475 void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) {
\r
1476 if ((pDrawItem->itemAction == ODA_SELECT) || (pDrawItem->itemAction == ODA_DRAWENTIRE)) {
\r
1477 RECT rcBox = pDrawItem->rcItem;
\r
1478 rcBox.left += TextOffset();
\r
1479 if (pDrawItem->itemState & ODS_SELECTED) {
\r
1480 RECT rcImage = pDrawItem->rcItem;
\r
1481 rcImage.right = rcBox.left;
\r
1482 // The image is not highlighted
\r
1483 ::FillRect(pDrawItem->hDC, &rcImage, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
\r
1484 ::FillRect(pDrawItem->hDC, &rcBox, reinterpret_cast<HBRUSH>(COLOR_HIGHLIGHT+1));
\r
1485 ::SetBkColor(pDrawItem->hDC, ::GetSysColor(COLOR_HIGHLIGHT));
\r
1486 ::SetTextColor(pDrawItem->hDC, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
\r
1488 ::FillRect(pDrawItem->hDC, &pDrawItem->rcItem, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
\r
1489 ::SetBkColor(pDrawItem->hDC, ::GetSysColor(COLOR_WINDOW));
\r
1490 ::SetTextColor(pDrawItem->hDC, ::GetSysColor(COLOR_WINDOWTEXT));
\r
1493 ListItemData item = lti.Get(pDrawItem->itemID);
\r
1494 int pixId = item.pixId;
\r
1495 const char *text = item.text;
\r
1496 int len = strlen(text);
\r
1498 RECT rcText = rcBox;
\r
1499 ::InsetRect(&rcText, TextInset.x, TextInset.y);
\r
1501 if (unicodeMode) {
\r
1502 const TextWide tbuf(text, len, unicodeMode);
\r
1503 ::DrawTextW(pDrawItem->hDC, tbuf.buffer, tbuf.tlen, &rcText, DT_NOPREFIX|DT_END_ELLIPSIS|DT_SINGLELINE|DT_NOCLIP);
\r
1505 ::DrawTextA(pDrawItem->hDC, text, len, &rcText, DT_NOPREFIX|DT_END_ELLIPSIS|DT_SINGLELINE|DT_NOCLIP);
\r
1507 if (pDrawItem->itemState & ODS_SELECTED) {
\r
1508 ::DrawFocusRect(pDrawItem->hDC, &rcBox);
\r
1511 // Draw the image, if any
\r
1512 XPM *pxpm = xset.Get(pixId);
\r
1514 Surface *surfaceItem = Surface::Allocate();
\r
1515 if (surfaceItem) {
\r
1516 surfaceItem->Init(pDrawItem->hDC, pDrawItem->hwndItem);
\r
1517 //surfaceItem->SetUnicodeMode(unicodeMode);
\r
1518 //surfaceItem->SetDBCSMode(codePage);
\r
1519 int left = pDrawItem->rcItem.left + ItemInset.x + ImageInset.x;
\r
1520 PRectangle rcImage(left, pDrawItem->rcItem.top,
\r
1521 left + xset.GetWidth(), pDrawItem->rcItem.bottom);
\r
1522 pxpm->Draw(surfaceItem, rcImage);
\r
1523 delete surfaceItem;
\r
1524 ::SetTextAlign(pDrawItem->hDC, TA_TOP);
\r
1530 void ListBoxX::AppendListItem(const char *startword, const char *numword) {
\r
1531 ListItemData *item = lti.AllocItem();
\r
1532 item->text = startword;
\r
1536 while ((ch = *++numword) != '\0') {
\r
1537 pixId = 10 * pixId + (ch - '0');
\r
1539 item->pixId = pixId;
\r
1544 unsigned int len = static_cast<unsigned int>(strlen(item->text));
\r
1545 if (maxItemCharacters < len) {
\r
1546 maxItemCharacters = len;
\r
1547 widestItem = item->text;
\r
1551 void ListBoxX::SetList(const char *list, char separator, char typesep) {
\r
1552 // Turn off redraw while populating the list - this has a significant effect, even if
\r
1553 // the listbox is not visible.
\r
1556 int size = strlen(list) + 1;
\r
1557 char *words = new char[size];
\r
1559 lti.SetWords(words);
\r
1560 memcpy(words, list, size);
\r
1561 char *startword = words;
\r
1562 char *numword = NULL;
\r
1564 for (; words[i]; i++) {
\r
1565 if (words[i] == separator) {
\r
1569 AppendListItem(startword, numword);
\r
1570 startword = words + i + 1;
\r
1572 } else if (words[i] == typesep) {
\r
1573 numword = words + i;
\r
1579 AppendListItem(startword, numword);
\r
1582 // Finally populate the listbox itself with the correct number of items
\r
1583 int count = lti.Count();
\r
1584 ::SendMessage(lb, LB_INITSTORAGE, count, 0);
\r
1585 for (int j=0; j<count; j++) {
\r
1586 ::SendMessage(lb, LB_ADDSTRING, 0, j+1);
\r
1592 void ListBoxX::AdjustWindowRect(PRectangle *rc) const {
\r
1593 ::AdjustWindowRectEx(reinterpret_cast<RECT*>(rc), WS_THICKFRAME, false, WS_EX_WINDOWEDGE);
\r
1596 int ListBoxX::ItemHeight() const {
\r
1597 int itemHeight = lineHeight + (TextInset.y * 2);
\r
1598 int pixHeight = const_cast<XPMSet*>(&xset)->GetHeight() + (ImageInset.y * 2);
\r
1599 if (itemHeight < pixHeight) {
\r
1600 itemHeight = pixHeight;
\r
1602 return itemHeight;
\r
1605 int ListBoxX::MinClientWidth() const {
\r
1606 return 12 * (aveCharWidth+aveCharWidth/3);
\r
1609 Point ListBoxX::MinTrackSize() const {
\r
1610 PRectangle rc(0, 0, MinClientWidth(), ItemHeight());
\r
1611 AdjustWindowRect(&rc);
\r
1612 return Point(rc.Width(), rc.Height());
\r
1615 Point ListBoxX::MaxTrackSize() const {
\r
1616 PRectangle rc(0, 0,
\r
1617 maxCharWidth * maxItemCharacters + TextInset.x * 2 +
\r
1618 TextOffset() + ::GetSystemMetrics(SM_CXVSCROLL),
\r
1619 ItemHeight() * lti.Count());
\r
1620 AdjustWindowRect(&rc);
\r
1621 return Point(rc.Width(), rc.Height());
\r
1624 void ListBoxX::SetRedraw(bool on) {
\r
1625 ::SendMessage(lb, WM_SETREDRAW, static_cast<BOOL>(on), 0);
\r
1627 ::InvalidateRect(lb, NULL, TRUE);
\r
1630 void ListBoxX::ResizeToCursor() {
\r
1631 PRectangle rc = GetPosition();
\r
1633 ::GetCursorPos(reinterpret_cast<POINT*>(&pt));
\r
1634 pt.x += dragOffset.x;
\r
1635 pt.y += dragOffset.y;
\r
1637 switch (resizeHit) {
\r
1658 case HTBOTTOMLEFT:
\r
1662 case HTBOTTOMRIGHT:
\r
1668 Point ptMin = MinTrackSize();
\r
1669 Point ptMax = MaxTrackSize();
\r
1670 // We don't allow the left edge to move at present, but just in case
\r
1671 rc.left = Platform::Maximum(Platform::Minimum(rc.left, rcPreSize.right - ptMin.x), rcPreSize.right - ptMax.x);
\r
1672 rc.top = Platform::Maximum(Platform::Minimum(rc.top, rcPreSize.bottom - ptMin.y), rcPreSize.bottom - ptMax.y);
\r
1673 rc.right = Platform::Maximum(Platform::Minimum(rc.right, rcPreSize.left + ptMax.x), rcPreSize.left + ptMin.x);
\r
1674 rc.bottom = Platform::Maximum(Platform::Minimum(rc.bottom, rcPreSize.top + ptMax.y), rcPreSize.top + ptMin.y);
\r
1679 void ListBoxX::StartResize(WPARAM hitCode) {
\r
1680 rcPreSize = GetPosition();
\r
1682 ::GetCursorPos(&cursorPos);
\r
1684 switch (hitCode) {
\r
1687 case HTBOTTOMRIGHT:
\r
1688 dragOffset.x = rcPreSize.right - cursorPos.x;
\r
1689 dragOffset.y = rcPreSize.bottom - cursorPos.y;
\r
1693 dragOffset.x = rcPreSize.right - cursorPos.x;
\r
1694 dragOffset.y = rcPreSize.top - cursorPos.y;
\r
1697 // Note that the current hit test code prevents the left edge cases ever firing
\r
1698 // as we don't want the left edge to be moveable
\r
1702 dragOffset.x = rcPreSize.left - cursorPos.x;
\r
1703 dragOffset.y = rcPreSize.top - cursorPos.y;
\r
1705 case HTBOTTOMLEFT:
\r
1706 dragOffset.x = rcPreSize.left - cursorPos.x;
\r
1707 dragOffset.y = rcPreSize.bottom - cursorPos.y;
\r
1714 ::SetCapture(GetHWND());
\r
1715 resizeHit = hitCode;
\r
1718 int ListBoxX::NcHitTest(WPARAM wParam, LPARAM lParam) const {
\r
1719 int hit = ::DefWindowProc(GetHWND(), WM_NCHITTEST, wParam, lParam);
\r
1720 // There is an apparent bug in the DefWindowProc hit test code whereby it will
\r
1721 // return HTTOPXXX if the window in question is shorter than the default
\r
1722 // window caption height + frame, even if one is hovering over the bottom edge of
\r
1723 // the frame, so workaround that here
\r
1724 if (hit >= HTTOP && hit <= HTTOPRIGHT) {
\r
1725 int minHeight = GetSystemMetrics(SM_CYMINTRACK);
\r
1726 PRectangle rc = const_cast<ListBoxX*>(this)->GetPosition();
\r
1727 int yPos = GET_Y_LPARAM(lParam);
\r
1728 if ((rc.Height() < minHeight) && (yPos > ((rc.top + rc.bottom)/2))) {
\r
1729 hit += HTBOTTOM - HTTOP;
\r
1733 // Nerver permit resizing that moves the left edge. Allow movement of top or bottom edge
\r
1734 // depending on whether the list is above or below the caret
\r
1738 case HTBOTTOMLEFT:
\r
1743 case HTTOPRIGHT: {
\r
1744 PRectangle rc = const_cast<ListBoxX*>(this)->GetPosition();
\r
1745 // Valid only if caret below list
\r
1746 if (location.y < rc.top)
\r
1752 case HTBOTTOMRIGHT: {
\r
1753 PRectangle rc = const_cast<ListBoxX*>(this)->GetPosition();
\r
1754 // Valid only if caret above list
\r
1755 if (rc.bottom < location.y)
\r
1764 void ListBoxX::OnDoubleClick() {
\r
1766 if (doubleClickAction != NULL) {
\r
1767 doubleClickAction(doubleClickActionData);
\r
1771 Point ListBoxX::GetClientExtent() const {
\r
1772 PRectangle rc = const_cast<ListBoxX*>(this)->GetClientPosition();
\r
1773 return Point(rc.Width(), rc.Height());
\r
1776 void ListBoxX::CentreItem(int n) {
\r
1777 // If below mid point, scroll up to centre, but with more items below if uneven
\r
1779 Point extent = GetClientExtent();
\r
1780 int visible = extent.y/ItemHeight();
\r
1781 if (visible < Length()) {
\r
1782 int top = ::SendMessage(lb, LB_GETTOPINDEX, 0, 0);
\r
1783 int half = (visible - 1) / 2;
\r
1784 if (n > (top + half))
\r
1785 ::SendMessage(lb, LB_SETTOPINDEX, n - half , 0);
\r
1790 // Performs a double-buffered paint operation to avoid flicker
\r
1791 void ListBoxX::Paint(HDC hDC) {
\r
1792 Point extent = GetClientExtent();
\r
1793 HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, extent.x, extent.y);
\r
1794 HDC bitmapDC = ::CreateCompatibleDC(hDC);
\r
1795 HBITMAP hBitmapOld = SelectBitmap(bitmapDC, hBitmap);
\r
1796 // The list background is mainly erased during painting, but can be a small
\r
1797 // unpainted area when at the end of a non-integrally sized list with a
\r
1798 // vertical scroll bar
\r
1799 RECT rc = { 0, 0, extent.x, extent.y };
\r
1800 ::FillRect(bitmapDC, &rc, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
\r
1801 // Paint the entire client area and vertical scrollbar
\r
1802 ::SendMessage(lb, WM_PRINT, reinterpret_cast<WPARAM>(bitmapDC), PRF_CLIENT|PRF_NONCLIENT);
\r
1803 ::BitBlt(hDC, 0, 0, extent.x, extent.y, bitmapDC, 0, 0, SRCCOPY);
\r
1804 // Select a stock brush to prevent warnings from BoundsChecker
\r
1805 ::SelectObject(bitmapDC, GetStockFont(WHITE_BRUSH));
\r
1806 SelectBitmap(bitmapDC, hBitmapOld);
\r
1807 ::DeleteDC(bitmapDC);
\r
1808 ::DeleteObject(hBitmap);
\r
1811 LRESULT PASCAL ListBoxX::ControlWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
\r
1813 case WM_ERASEBKGND:
\r
1818 HDC hDC = ::BeginPaint(hWnd, &ps);
\r
1819 ListBoxX *lbx = reinterpret_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)));
\r
1822 ::EndPaint(hWnd, &ps);
\r
1826 case WM_MOUSEACTIVATE:
\r
1827 // This prevents the view activating when the scrollbar is clicked
\r
1828 return MA_NOACTIVATE;
\r
1830 case WM_LBUTTONDOWN: {
\r
1831 // We must take control of selection to prevent the ListBox activating
\r
1833 LRESULT lResult = ::SendMessage(hWnd, LB_ITEMFROMPOINT, 0, lParam);
\r
1834 int item = LOWORD(lResult);
\r
1835 if (HIWORD(lResult) == 0 && item >= 0) {
\r
1836 ::SendMessage(hWnd, LB_SETCURSEL, item, 0);
\r
1841 case WM_LBUTTONUP:
\r
1844 case WM_LBUTTONDBLCLK: {
\r
1845 ListBoxX *lbx = reinterpret_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)));
\r
1847 lbx->OnDoubleClick();
\r
1853 WNDPROC prevWndProc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
\r
1854 if (prevWndProc) {
\r
1855 return ::CallWindowProc(prevWndProc, hWnd, uMsg, wParam, lParam);
\r
1857 return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
\r
1861 LRESULT ListBoxX::WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
\r
1862 switch (iMessage) {
\r
1864 HINSTANCE hinstanceParent = GetWindowInstance(reinterpret_cast<HWND>(parent->GetID()));
\r
1865 // Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list
\r
1866 // but has useful side effect of speeding up list population significantly
\r
1867 lb = ::CreateWindowEx(
\r
1868 0, TEXT("listbox"), TEXT(""),
\r
1869 WS_CHILD | WS_VSCROLL | WS_VISIBLE |
\r
1870 LBS_OWNERDRAWFIXED | LBS_NODATA | LBS_NOINTEGRALHEIGHT,
\r
1871 0, 0, 150,80, hWnd,
\r
1872 reinterpret_cast<HMENU>(ctrlID),
\r
1875 WNDPROC prevWndProc = reinterpret_cast<WNDPROC>(::SetWindowLongPtr(lb, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(ControlWndProc)));
\r
1876 ::SetWindowLongPtr(lb, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(prevWndProc));
\r
1883 ::SetWindowPos(lb, 0, 0,0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE);
\r
1884 // Ensure the selection remains visible
\r
1885 CentreItem(GetSelection());
\r
1892 ::BeginPaint(hWnd, &ps);
\r
1893 ::EndPaint(hWnd, &ps);
\r
1898 // This is not actually needed now - the registered double click action is used
\r
1899 // directly to action a choice from the list.
\r
1900 ::SendMessage(reinterpret_cast<HWND>(parent->GetID()), iMessage, wParam, lParam);
\r
1903 case WM_MEASUREITEM: {
\r
1904 MEASUREITEMSTRUCT *pMeasureItem = reinterpret_cast<MEASUREITEMSTRUCT *>(lParam);
\r
1905 pMeasureItem->itemHeight = static_cast<unsigned int>(ItemHeight());
\r
1910 Draw(reinterpret_cast<DRAWITEMSTRUCT *>(lParam));
\r
1915 ::SetWindowLong(hWnd, 0, 0);
\r
1916 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
\r
1918 case WM_ERASEBKGND:
\r
1919 // To reduce flicker we can elide background erasure since this window is
\r
1920 // completely covered by its child.
\r
1923 case WM_GETMINMAXINFO: {
\r
1924 MINMAXINFO *minMax = reinterpret_cast<MINMAXINFO*>(lParam);
\r
1925 *reinterpret_cast<Point*>(&minMax->ptMaxTrackSize) = MaxTrackSize();
\r
1926 *reinterpret_cast<Point*>(&minMax->ptMinTrackSize) = MinTrackSize();
\r
1930 case WM_MOUSEACTIVATE:
\r
1931 return MA_NOACTIVATE;
\r
1933 case WM_NCHITTEST:
\r
1934 return NcHitTest(wParam, lParam);
\r
1936 case WM_NCLBUTTONDOWN:
\r
1937 // We have to implement our own window resizing because the DefWindowProc
\r
1938 // implementation insists on activating the resized window
\r
1939 StartResize(wParam);
\r
1942 case WM_MOUSEMOVE: {
\r
1943 if (resizeHit == 0) {
\r
1944 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
\r
1951 case WM_LBUTTONUP:
\r
1952 case WM_CANCELMODE:
\r
1953 if (resizeHit != 0) {
\r
1955 ::ReleaseCapture();
\r
1957 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
\r
1960 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
\r
1966 LRESULT PASCAL ListBoxX::StaticWndProc(
\r
1967 HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
\r
1968 if (iMessage == WM_CREATE) {
\r
1969 CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
\r
1970 SetWindowPointer(hWnd, pCreate->lpCreateParams);
\r
1972 // Find C++ object associated with window.
\r
1973 ListBoxX *lbx = reinterpret_cast<ListBoxX *>(PointerFromWindow(hWnd));
\r
1975 return lbx->WndProc(hWnd, iMessage, wParam, lParam);
\r
1977 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
\r
1981 static bool ListBoxX_Register() {
\r
1982 WNDCLASSEX wndclassc;
\r
1983 wndclassc.cbSize = sizeof(wndclassc);
\r
1984 // We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for
\r
1985 // truncated items in the list and the appearance/disappearance of the vertical scroll bar.
\r
1986 // The list repaint is double-buffered to avoid the flicker this would otherwise cause.
\r
1987 wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
\r
1988 wndclassc.cbClsExtra = 0;
\r
1989 wndclassc.cbWndExtra = sizeof(ListBoxX *);
\r
1990 wndclassc.hInstance = hinstPlatformRes;
\r
1991 wndclassc.hIcon = NULL;
\r
1992 wndclassc.hbrBackground = NULL;
\r
1993 wndclassc.lpszMenuName = NULL;
\r
1994 wndclassc.lpfnWndProc = ListBoxX::StaticWndProc;
\r
1995 wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
\r
1996 wndclassc.lpszClassName = ListBoxX_ClassName;
\r
1997 wndclassc.hIconSm = 0;
\r
1999 return ::RegisterClassEx(&wndclassc) != 0;
\r
2002 bool ListBoxX_Unregister() {
\r
2003 return ::UnregisterClass(ListBoxX_ClassName, hinstPlatformRes) != 0;
\r
2006 Menu::Menu() : id(0) {
\r
2009 void Menu::CreatePopUp() {
\r
2011 id = ::CreatePopupMenu();
\r
2014 void Menu::Destroy() {
\r
2016 ::DestroyMenu(reinterpret_cast<HMENU>(id));
\r
2020 void Menu::Show(Point pt, Window &w) {
\r
2021 ::TrackPopupMenu(reinterpret_cast<HMENU>(id),
\r
2022 0, pt.x - 4, pt.y, 0,
\r
2023 reinterpret_cast<HWND>(w.GetID()), NULL);
\r
2027 static bool initialisedET = false;
\r
2028 static bool usePerformanceCounter = false;
\r
2029 static LARGE_INTEGER frequency;
\r
2031 ElapsedTime::ElapsedTime() {
\r
2032 if (!initialisedET) {
\r
2033 usePerformanceCounter = ::QueryPerformanceFrequency(&frequency) != 0;
\r
2034 initialisedET = true;
\r
2036 if (usePerformanceCounter) {
\r
2037 LARGE_INTEGER timeVal;
\r
2038 ::QueryPerformanceCounter(&timeVal);
\r
2039 bigBit = timeVal.HighPart;
\r
2040 littleBit = timeVal.LowPart;
\r
2046 double ElapsedTime::Duration(bool reset) {
\r
2049 long endLittleBit;
\r
2051 if (usePerformanceCounter) {
\r
2052 LARGE_INTEGER lEnd;
\r
2053 ::QueryPerformanceCounter(&lEnd);
\r
2054 endBigBit = lEnd.HighPart;
\r
2055 endLittleBit = lEnd.LowPart;
\r
2056 LARGE_INTEGER lBegin;
\r
2057 lBegin.HighPart = bigBit;
\r
2058 lBegin.LowPart = littleBit;
\r
2059 double elapsed = lEnd.QuadPart - lBegin.QuadPart;
\r
2060 result = elapsed / static_cast<double>(frequency.QuadPart);
\r
2062 endBigBit = clock();
\r
2064 double elapsed = endBigBit - bigBit;
\r
2065 result = elapsed / CLOCKS_PER_SEC;
\r
2068 bigBit = endBigBit;
\r
2069 littleBit = endLittleBit;
\r
2074 class DynamicLibraryImpl : public DynamicLibrary {
\r
2078 DynamicLibraryImpl(const char *modulePath) {
\r
2079 h = ::LoadLibraryA(modulePath);
\r
2082 virtual ~DynamicLibraryImpl() {
\r
2087 // Use GetProcAddress to get a pointer to the relevant function.
\r
2088 virtual Function FindFunction(const char *name) {
\r
2090 return static_cast<Function>(
\r
2091 (void *)(::GetProcAddress(h, name)));
\r
2096 virtual bool IsValid() {
\r
2101 DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {
\r
2102 return static_cast<DynamicLibrary *>(new DynamicLibraryImpl(modulePath));
\r
2105 ColourDesired Platform::Chrome() {
\r
2106 return ::GetSysColor(COLOR_3DFACE);
\r
2109 ColourDesired Platform::ChromeHighlight() {
\r
2110 return ::GetSysColor(COLOR_3DHIGHLIGHT);
\r
2113 const char *Platform::DefaultFont() {
\r
2117 int Platform::DefaultFontSize() {
\r
2121 unsigned int Platform::DoubleClickTime() {
\r
2122 return ::GetDoubleClickTime();
\r
2125 bool Platform::MouseButtonBounce() {
\r
2129 void Platform::DebugDisplay(const char *s) {
\r
2130 ::OutputDebugStringA(s);
\r
2133 bool Platform::IsKeyDown(int key) {
\r
2134 return (::GetKeyState(key) & 0x80000000) != 0;
\r
2137 long Platform::SendScintilla(WindowID w, unsigned int msg, unsigned long wParam, long lParam) {
\r
2138 return ::SendMessage(reinterpret_cast<HWND>(w), msg, wParam, lParam);
\r
2141 long Platform::SendScintillaPointer(WindowID w, unsigned int msg, unsigned long wParam, void *lParam) {
\r
2142 return ::SendMessage(reinterpret_cast<HWND>(w), msg, wParam,
\r
2143 reinterpret_cast<LPARAM>(lParam));
\r
2146 bool Platform::IsDBCSLeadByte(int codePage, char ch) {
\r
2147 return ::IsDBCSLeadByteEx(codePage, ch) != 0;
\r
2150 int Platform::DBCSCharLength(int codePage, const char *s) {
\r
2151 return (::IsDBCSLeadByteEx(codePage, s[0]) != 0) ? 2 : 1;
\r
2154 int Platform::DBCSCharMaxLength() {
\r
2158 // These are utility functions not really tied to a platform
\r
2160 int Platform::Minimum(int a, int b) {
\r
2167 int Platform::Maximum(int a, int b) {
\r
2177 void Platform::DebugPrintf(const char *format, ...) {
\r
2178 char buffer[2000];
\r
2179 va_list pArguments;
\r
2180 va_start(pArguments, format);
\r
2181 vsprintf(buffer,format,pArguments);
\r
2182 va_end(pArguments);
\r
2183 Platform::DebugDisplay(buffer);
\r
2186 void Platform::DebugPrintf(const char *, ...) {
\r
2190 static bool assertionPopUps = true;
\r
2192 bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {
\r
2193 bool ret = assertionPopUps;
\r
2194 assertionPopUps = assertionPopUps_;
\r
2198 void Platform::Assert(const char *c, const char *file, int line) {
\r
2199 char buffer[2000];
\r
2200 sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line);
\r
2201 if (assertionPopUps) {
\r
2202 int idButton = ::MessageBoxA(0, buffer, "Assertion failure",
\r
2203 MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
\r
2204 if (idButton == IDRETRY) {
\r
2206 } else if (idButton == IDIGNORE) {
\r
2212 strcat(buffer, "\r\n");
\r
2213 Platform::DebugDisplay(buffer);
\r
2219 int Platform::Clamp(int val, int minVal, int maxVal) {
\r
2227 void Platform_Initialise(void *hInstance) {
\r
2228 OSVERSIONINFO osv = {sizeof(OSVERSIONINFO),0,0,0,0,TEXT("")};
\r
2229 ::GetVersionEx(&osv);
\r
2230 onNT = osv.dwPlatformId == VER_PLATFORM_WIN32_NT;
\r
2231 ::InitializeCriticalSection(&crPlatformLock);
\r
2232 hinstPlatformRes = reinterpret_cast<HINSTANCE>(hInstance);
\r
2233 // This may be called from DllMain, in which case the call to LoadLibrary
\r
2234 // is bad because it can upset the DLL load order.
\r
2236 hDLLImage = ::LoadLibrary(TEXT("Msimg32"));
\r
2239 AlphaBlendFn = (AlphaBlendSig)::GetProcAddress(hDLLImage, "AlphaBlend");
\r
2241 ListBoxX_Register();
\r
2244 void Platform_Finalise() {
\r
2245 ListBoxX_Unregister();
\r
2246 ::DeleteCriticalSection(&crPlatformLock);
\r