OSDN Git Service

Add SCI Edit to GitBlameView
[tortoisegit/TortoiseGitJp.git] / ext / scintilla / win32 / PlatWin.cxx
1 // Scintilla source code edit control\r
2 /** @file PlatWin.cxx\r
3  ** Implementation of platform facilities on Windows.\r
4  **/\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
7 \r
8 #include <stdlib.h>\r
9 #include <string.h>\r
10 #include <ctype.h>\r
11 #include <stdarg.h>\r
12 #include <stdio.h>\r
13 #include <time.h>\r
14 \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
20 \r
21 #include "Platform.h"\r
22 #include "PlatformRes.h"\r
23 #include "UniConversion.h"\r
24 #include "XPM.h"\r
25 \r
26 #ifndef IDC_HAND\r
27 #define IDC_HAND MAKEINTRESOURCE(32649)\r
28 #endif\r
29 \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
34 }\r
35 static void SetWindowPointer(HWND hWnd, void *ptr) {\r
36         ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));\r
37 }\r
38 #else\r
39 static void *PointerFromWindow(HWND hWnd) {\r
40         return reinterpret_cast<void *>(::GetWindowLong(hWnd, 0));\r
41 }\r
42 static void SetWindowPointer(HWND hWnd, void *ptr) {\r
43         ::SetWindowLong(hWnd, 0, reinterpret_cast<LONG>(ptr));\r
44 }\r
45 \r
46 #ifndef GWLP_USERDATA\r
47 #define GWLP_USERDATA GWL_USERDATA\r
48 #endif\r
49 \r
50 #ifndef GWLP_WNDPROC\r
51 #define GWLP_WNDPROC GWL_WNDPROC\r
52 #endif\r
53 \r
54 #ifndef LONG_PTR\r
55 #define LONG_PTR LONG\r
56 #endif\r
57 \r
58 static LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong) {\r
59         return ::SetWindowLong(hWnd, nIndex, dwNewLong);\r
60 }\r
61 \r
62 static LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex) {\r
63         return ::GetWindowLong(hWnd, nIndex);\r
64 }\r
65 #endif\r
66 \r
67 typedef BOOL (WINAPI *AlphaBlendSig)(HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION);\r
68 \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
74 \r
75 \r
76 bool IsNT() {\r
77         return onNT;\r
78 }\r
79 \r
80 #ifdef SCI_NAMESPACE\r
81 using namespace Scintilla;\r
82 #endif\r
83 \r
84 Point Point::FromLong(long lpoint) {\r
85         return Point(static_cast<short>(LOWORD(lpoint)), static_cast<short>(HIWORD(lpoint)));\r
86 }\r
87 \r
88 static RECT RectFromPRectangle(PRectangle prc) {\r
89         RECT rc = {prc.left, prc.top, prc.right, prc.bottom};\r
90         return rc;\r
91 }\r
92 \r
93 Palette::Palette() {\r
94         used = 0;\r
95         allowRealization = false;\r
96         hpal = 0;\r
97         size = 100;\r
98         entries = new ColourPair[size];\r
99 }\r
100 \r
101 Palette::~Palette() {\r
102         Release();\r
103         delete []entries;\r
104         entries = 0;\r
105 }\r
106 \r
107 void Palette::Release() {\r
108         used = 0;\r
109         if (hpal)\r
110                 ::DeleteObject(hpal);\r
111         hpal = 0;\r
112         delete []entries;\r
113         size = 100;\r
114         entries = new ColourPair[size];\r
115 }\r
116 \r
117 /**\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
121  */\r
122 void Palette::WantFind(ColourPair &cp, bool want) {\r
123         if (want) {\r
124                 for (int i=0; i < used; i++) {\r
125                         if (entries[i].desired == cp.desired)\r
126                                 return;\r
127                 }\r
128 \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
134                         }\r
135                         delete []entries;\r
136                         entries = entriesNew;\r
137                         size = sizeNew;\r
138                 }\r
139 \r
140                 entries[used].desired = cp.desired;\r
141                 entries[used].allocated.Set(cp.desired.AsLong());\r
142                 used++;\r
143         } else {\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
147                                 return;\r
148                         }\r
149                 }\r
150                 cp.allocated.Set(cp.desired.AsLong());\r
151         }\r
152 }\r
153 \r
154 void Palette::Allocate(Window &) {\r
155         if (hpal)\r
156                 ::DeleteObject(hpal);\r
157         hpal = 0;\r
158 \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
176                 }\r
177                 hpal = ::CreatePalette(logpal);\r
178                 delete []pal;\r
179         }\r
180 }\r
181 \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
190 }\r
191 \r
192 /**\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
196  */\r
197 static int HashFont(const char *faceName, int characterSet, int size, bool bold, bool italic) {\r
198         return\r
199                 size ^\r
200                 (characterSet << 10) ^\r
201                 (bold ? 0x10000000 : 0) ^\r
202                 (italic ? 0x20000000 : 0) ^\r
203                 faceName[0];\r
204 }\r
205 \r
206 class FontCached : Font {\r
207         FontCached *next;\r
208         int usage;\r
209         LOGFONTA lf;\r
210         int hash;\r
211         FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);\r
212         ~FontCached() {}\r
213         bool SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);\r
214         virtual void Release();\r
215 \r
216         static FontCached *first;\r
217 public:\r
218         static FontID FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);\r
219         static void ReleaseId(FontID id_);\r
220 };\r
221 \r
222 FontCached *FontCached::first = 0;\r
223 \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
229         usage = 1;\r
230 }\r
231 \r
232 bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {\r
233         return\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
239 }\r
240 \r
241 void FontCached::Release() {\r
242         if (id)\r
243                 ::DeleteObject(id);\r
244         id = 0;\r
245 }\r
246 \r
247 FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {\r
248         FontID ret = 0;\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
254                         cur->usage++;\r
255                         ret = cur->id;\r
256                 }\r
257         }\r
258         if (ret == 0) {\r
259                 FontCached *fc = new FontCached(faceName_, characterSet_, size_, bold_, italic_);\r
260                 if (fc) {\r
261                         fc->next = first;\r
262                         first = fc;\r
263                         ret = fc->id;\r
264                 }\r
265         }\r
266         ::LeaveCriticalSection(&crPlatformLock);\r
267         return ret;\r
268 }\r
269 \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
275                         cur->usage--;\r
276                         if (cur->usage == 0) {\r
277                                 *pcur = cur->next;\r
278                                 cur->Release();\r
279                                 cur->next = 0;\r
280                                 delete cur;\r
281                         }\r
282                         break;\r
283                 }\r
284                 pcur=&cur->next;\r
285         }\r
286         ::LeaveCriticalSection(&crPlatformLock);\r
287 }\r
288 \r
289 Font::Font() {\r
290         id = 0;\r
291 }\r
292 \r
293 Font::~Font() {\r
294 }\r
295 \r
296 #define FONTS_CACHED\r
297 \r
298 void Font::Create(const char *faceName, int characterSet, int size,\r
299         bool bold, bool italic, bool) {\r
300         Release();\r
301 #ifndef FONTS_CACHED\r
302         LOGFONT lf;\r
303         SetLogFont(lf, faceName, characterSet, size, bold, italic);\r
304         id = ::CreateFontIndirect(&lf);\r
305 #else\r
306         id = FontCached::FindOrCreate(faceName, characterSet, size, bold, italic);\r
307 #endif\r
308 }\r
309 \r
310 void Font::Release() {\r
311 #ifndef FONTS_CACHED\r
312         if (id)\r
313                 ::DeleteObject(id);\r
314 #else\r
315         if (id)\r
316                 FontCached::ReleaseId(id);\r
317 #endif\r
318         id = 0;\r
319 }\r
320 \r
321 #ifdef SCI_NAMESPACE\r
322 namespace Scintilla {\r
323 #endif\r
324 \r
325 class SurfaceImpl : public Surface {\r
326         bool unicodeMode;\r
327         HDC hdc;\r
328         bool hdcOwned;\r
329         HPEN pen;\r
330         HPEN penOld;\r
331         HBRUSH brush;\r
332         HBRUSH brushOld;\r
333         HFONT font;\r
334         HFONT fontOld;\r
335         HBITMAP bitmap;\r
336         HBITMAP bitmapOld;\r
337         HPALETTE paletteOld;\r
338         int maxWidthMeasure;\r
339         int maxLenText;\r
340 \r
341         int codePage;\r
342         // If 9x OS and current code page is same as ANSI code page.\r
343         bool win9xACPSame;\r
344 \r
345         void BrushColor(ColourAllocated back);\r
346         void SetFont(Font &font_);\r
347 \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
351 public:\r
352         SurfaceImpl();\r
353         virtual ~SurfaceImpl();\r
354 \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
358 \r
359         void Release();\r
360         bool Initialised();\r
361         void PenColour(ColourAllocated fore);\r
362         int LogPixelsY();\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
375 \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
389 \r
390         int SetPalette(Palette *pal, bool inBackGround);\r
391         void SetClip(PRectangle rc);\r
392         void FlushCachedState();\r
393 \r
394         void SetUnicodeMode(bool unicodeMode_);\r
395         void SetDBCSMode(int codePage_);\r
396 };\r
397 \r
398 #ifdef SCI_NAMESPACE\r
399 } //namespace Scintilla\r
400 #endif\r
401 \r
402 SurfaceImpl::SurfaceImpl() :\r
403         unicodeMode(false),\r
404         hdc(0),         hdcOwned(false),\r
405         pen(0),         penOld(0),\r
406         brush(0), brushOld(0),\r
407         font(0),        fontOld(0),\r
408         bitmap(0), bitmapOld(0),\r
409         paletteOld(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
415 \r
416         codePage = 0;\r
417         win9xACPSame = false;\r
418 }\r
419 \r
420 SurfaceImpl::~SurfaceImpl() {\r
421         Release();\r
422 }\r
423 \r
424 void SurfaceImpl::Release() {\r
425         if (penOld) {\r
426                 ::SelectObject(reinterpret_cast<HDC>(hdc), penOld);\r
427                 ::DeleteObject(pen);\r
428                 penOld = 0;\r
429         }\r
430         pen = 0;\r
431         if (brushOld) {\r
432                 ::SelectObject(reinterpret_cast<HDC>(hdc), brushOld);\r
433                 ::DeleteObject(brush);\r
434                 brushOld = 0;\r
435         }\r
436         brush = 0;\r
437         if (fontOld) {\r
438                 // Fonts are not deleted as they are owned by a Font object\r
439                 ::SelectObject(reinterpret_cast<HDC>(hdc), fontOld);\r
440                 fontOld = 0;\r
441         }\r
442         font = 0;\r
443         if (bitmapOld) {\r
444                 ::SelectObject(reinterpret_cast<HDC>(hdc), bitmapOld);\r
445                 ::DeleteObject(bitmap);\r
446                 bitmapOld = 0;\r
447         }\r
448         bitmap = 0;\r
449         if (paletteOld) {\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
453                 paletteOld = 0;\r
454         }\r
455         if (hdcOwned) {\r
456                 ::DeleteDC(reinterpret_cast<HDC>(hdc));\r
457                 hdc = 0;\r
458                 hdcOwned = false;\r
459         }\r
460 }\r
461 \r
462 bool SurfaceImpl::Initialised() {\r
463         return hdc != 0;\r
464 }\r
465 \r
466 void SurfaceImpl::Init(WindowID) {\r
467         Release();\r
468         hdc = ::CreateCompatibleDC(NULL);\r
469         hdcOwned = true;\r
470         ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE);\r
471 }\r
472 \r
473 void SurfaceImpl::Init(SurfaceID sid, WindowID) {\r
474         Release();\r
475         hdc = reinterpret_cast<HDC>(sid);\r
476         ::SetTextAlign(reinterpret_cast<HDC>(hdc), TA_BASELINE);\r
477 }\r
478 \r
479 void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID) {\r
480         Release();\r
481         hdc = ::CreateCompatibleDC(static_cast<SurfaceImpl *>(surface_)->hdc);\r
482         hdcOwned = true;\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
486 }\r
487 \r
488 void SurfaceImpl::PenColour(ColourAllocated fore) {\r
489         if (pen) {\r
490                 ::SelectObject(hdc, penOld);\r
491                 ::DeleteObject(pen);\r
492                 pen = 0;\r
493                 penOld = 0;\r
494         }\r
495         pen = ::CreatePen(0,1,fore.AsLong());\r
496         penOld = static_cast<HPEN>(::SelectObject(reinterpret_cast<HDC>(hdc), pen));\r
497 }\r
498 \r
499 void SurfaceImpl::BrushColor(ColourAllocated back) {\r
500         if (brush) {\r
501                 ::SelectObject(hdc, brushOld);\r
502                 ::DeleteObject(brush);\r
503                 brush = 0;\r
504                 brushOld = 0;\r
505         }\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
510 }\r
511 \r
512 void SurfaceImpl::SetFont(Font &font_) {\r
513         if (font_.GetID() != font) {\r
514                 if (fontOld) {\r
515                         ::SelectObject(hdc, font_.GetID());\r
516                 } else {\r
517                         fontOld = static_cast<HFONT>(::SelectObject(hdc, font_.GetID()));\r
518                 }\r
519                 font = reinterpret_cast<HFONT>(font_.GetID());\r
520         }\r
521 }\r
522 \r
523 int SurfaceImpl::LogPixelsY() {\r
524         return ::GetDeviceCaps(hdc, LOGPIXELSY);\r
525 }\r
526 \r
527 int SurfaceImpl::DeviceHeightFont(int points) {\r
528         return ::MulDiv(points, LogPixelsY(), 72);\r
529 }\r
530 \r
531 void SurfaceImpl::MoveTo(int x_, int y_) {\r
532         ::MoveToEx(hdc, x_, y_, 0);\r
533 }\r
534 \r
535 void SurfaceImpl::LineTo(int x_, int y_) {\r
536         ::LineTo(hdc, x_, y_);\r
537 }\r
538 \r
539 void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back) {\r
540         PenColour(fore);\r
541         BrushColor(back);\r
542         ::Polygon(hdc, reinterpret_cast<POINT *>(pts), npts);\r
543 }\r
544 \r
545 void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) {\r
546         PenColour(fore);\r
547         BrushColor(back);\r
548         ::Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);\r
549 }\r
550 \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
557 }\r
558 \r
559 void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) {\r
560         HBRUSH br;\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
568 }\r
569 \r
570 void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) {\r
571         PenColour(fore);\r
572         BrushColor(back);\r
573         ::RoundRect(hdc,\r
574                 rc.left + 1, rc.top,\r
575                 rc.right - 1, rc.bottom,\r
576                 8, 8);\r
577 }\r
578 \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
585 }\r
586 \r
587 #ifndef AC_SRC_OVER\r
588 #define AC_SRC_OVER                 0x00\r
589 #endif\r
590 #ifndef AC_SRC_ALPHA\r
591 #define AC_SRC_ALPHA            0x01\r
592 #endif\r
593 \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
603                 void *image = 0;\r
604                 HBITMAP hbmMem = CreateDIBSection(reinterpret_cast<HDC>(hMemDC), &bpih,\r
605                         DIB_RGB_COLORS, &image, NULL, 0);\r
606 \r
607                 HBITMAP hbmOld = SelectBitmap(hMemDC, hbmMem);\r
608 \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
626                                 } else {\r
627                                         pixels[y*width+x] = valFill;\r
628                                 }\r
629                         }\r
630                 }\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
634                         }\r
635                 }\r
636                 for (int x=1;x<cornerSize; x++) {\r
637                         AllFour(pixels, width, height, x, cornerSize-x, valOutline);\r
638                 }\r
639 \r
640                 BLENDFUNCTION merge = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };\r
641 \r
642                 AlphaBlendFn(reinterpret_cast<HDC>(hdc), rc.left, rc.top, width, height, hMemDC, 0, 0, width, height, merge);\r
643 \r
644                 SelectBitmap(hMemDC, hbmOld);\r
645                 ::DeleteObject(hbmMem);\r
646                 ::DeleteDC(hMemDC);\r
647         } else {\r
648                 BrushColor(outline);\r
649                 RECT rcw = RectFromPRectangle(rc);\r
650                 FrameRect(hdc, &rcw, brush);\r
651         }\r
652 }\r
653 \r
654 void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) {\r
655         PenColour(fore);\r
656         BrushColor(back);\r
657         ::Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom);\r
658 }\r
659 \r
660 void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {\r
661         ::BitBlt(hdc,\r
662                 rc.left, rc.top, rc.Width(), rc.Height(),\r
663                 static_cast<SurfaceImpl &>(surfaceSource).hdc, from.x, from.y, SRCCOPY);\r
664 }\r
665 \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
670 class VarBuffer {\r
671         T bufferStandard[lengthStandard];\r
672 public:\r
673         T *buffer;\r
674         VarBuffer(size_t length) : buffer(0) {\r
675                 if (length > lengthStandard) {\r
676                         buffer = new T[length];\r
677                 } else {\r
678                         buffer = bufferStandard;\r
679                 }\r
680         }\r
681         ~VarBuffer() {\r
682                 if (buffer != bufferStandard) {\r
683                         delete []buffer;\r
684                         buffer = 0;\r
685                 }\r
686         }\r
687 };\r
688 \r
689 const int stackBufferLength = 10000;\r
690 class TextWide : public VarBuffer<wchar_t, stackBufferLength> {\r
691 public:\r
692         int tlen;\r
693         TextWide(const char *s, int len, bool unicodeMode, int codePage=0) :\r
694                 VarBuffer<wchar_t, stackBufferLength>(len) {\r
695                 if (unicodeMode) {\r
696                         tlen = UTF16FromUTF8(s, len, buffer, len);\r
697                 } else {\r
698                         // Support Asian string display in 9x English\r
699                         tlen = ::MultiByteToWideChar(codePage, 0, s, len, buffer, len);\r
700                 }\r
701         }\r
702 };\r
703 typedef VarBuffer<int, stackBufferLength> TextPositions;\r
704 \r
705 void SurfaceImpl::DrawTextCommon(PRectangle rc, Font &font_, int ybase, const char *s, int len, UINT fuOptions) {\r
706         SetFont(font_);\r
707         RECT rcw = RectFromPRectangle(rc);\r
708         SIZE sz={0,0};\r
709         int pos = 0;\r
710         int x = rc.left;\r
711 \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
715 \r
716         if ((!unicodeMode) && (IsNT() || (codePage==0) || win9xACPSame)) {\r
717                 // Use ANSI calls\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
724                                         return;\r
725                                 }\r
726                                 ::GetTextExtentPoint32A(hdc, s+pos, seglen, &sz);\r
727                                 x += sz.cx;\r
728                                 pos += seglen;\r
729                         }\r
730                 }\r
731         } else {\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
739                                         return;\r
740                                 }\r
741                                 ::GetTextExtentPoint32W(hdc, tbuf.buffer+pos, seglen, &sz);\r
742                                 x += sz.cx;\r
743                                 pos += seglen;\r
744                         }\r
745                 }\r
746         }\r
747 }\r
748 \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
754 }\r
755 \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
761 }\r
762 \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
767                 if (s[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
772                         return;\r
773                 }\r
774         }\r
775 }\r
776 \r
777 int SurfaceImpl::WidthText(Font &font_, const char *s, int len) {\r
778         SetFont(font_);\r
779         SIZE sz={0,0};\r
780         if ((!unicodeMode) && (IsNT() || (codePage==0) || win9xACPSame)) {\r
781                 ::GetTextExtentPoint32A(hdc, s, Platform::Minimum(len, maxLenText), &sz);\r
782         } else {\r
783                 const TextWide tbuf(s, len, unicodeMode, codePage);\r
784                 ::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);\r
785         }\r
786         return sz.cx;\r
787 }\r
788 \r
789 void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) {\r
790         SetFont(font_);\r
791         SIZE sz={0,0};\r
792         int fit = 0;\r
793         if (unicodeMode) {\r
794                 const TextWide tbuf(s, len, unicodeMode, codePage);\r
795                 TextPositions poses(tbuf.tlen);\r
796                 fit = 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
804                         }\r
805                 }\r
806                 // Map the widths given for UTF-16 characters back onto the UTF-8 input string\r
807                 int ui=0;\r
808                 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);\r
809                 int i=0;\r
810                 while (ui<fit) {\r
811                         unsigned char uch = us[i];\r
812                         unsigned int lenChar = 1;\r
813                         if (uch >= (0x80 + 0x40 + 0x20 + 0x10)) {\r
814                                 lenChar = 4;\r
815                                 ui++;\r
816                         } else if (uch >= (0x80 + 0x40 + 0x20)) {\r
817                                 lenChar = 3;\r
818                         } else if (uch >= (0x80)) {\r
819                                 lenChar = 2;\r
820                         }\r
821                         for (unsigned int bytePos=0; (bytePos<lenChar) && (i<len); bytePos++) {\r
822                                 positions[i++] = poses.buffer[ui];\r
823                         }\r
824                         ui++;\r
825                 }\r
826                 int lastPos = 0;\r
827                 if (i > 0)\r
828                         lastPos = positions[i-1];\r
829                 while (i<len) {\r
830                         positions[i++] = lastPos;\r
831                 }\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
837                 while (len > 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
841                                 return;\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
850                         }\r
851                         startOffset = positions[lenBlock-1];\r
852                         len -= lenBlock;\r
853                         positions += lenBlock;\r
854                         s += lenBlock;\r
855                 }\r
856         } else {\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
863                 }\r
864 \r
865                 int ui = 0;\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
870                                 i += 2;\r
871                         } else {\r
872                                 positions[i] = poses.buffer[ui];\r
873                                 i++;\r
874                         }\r
875 \r
876                         ui++;\r
877                 }\r
878         }\r
879 }\r
880 \r
881 int SurfaceImpl::WidthChar(Font &font_, char ch) {\r
882         SetFont(font_);\r
883         SIZE sz;\r
884         ::GetTextExtentPoint32A(hdc, &ch, 1, &sz);\r
885         return sz.cx;\r
886 }\r
887 \r
888 int SurfaceImpl::Ascent(Font &font_) {\r
889         SetFont(font_);\r
890         TEXTMETRIC tm;\r
891         ::GetTextMetrics(hdc, &tm);\r
892         return tm.tmAscent;\r
893 }\r
894 \r
895 int SurfaceImpl::Descent(Font &font_) {\r
896         SetFont(font_);\r
897         TEXTMETRIC tm;\r
898         ::GetTextMetrics(hdc, &tm);\r
899         return tm.tmDescent;\r
900 }\r
901 \r
902 int SurfaceImpl::InternalLeading(Font &font_) {\r
903         SetFont(font_);\r
904         TEXTMETRIC tm;\r
905         ::GetTextMetrics(hdc, &tm);\r
906         return tm.tmInternalLeading;\r
907 }\r
908 \r
909 int SurfaceImpl::ExternalLeading(Font &font_) {\r
910         SetFont(font_);\r
911         TEXTMETRIC tm;\r
912         ::GetTextMetrics(hdc, &tm);\r
913         return tm.tmExternalLeading;\r
914 }\r
915 \r
916 int SurfaceImpl::Height(Font &font_) {\r
917         SetFont(font_);\r
918         TEXTMETRIC tm;\r
919         ::GetTextMetrics(hdc, &tm);\r
920         return tm.tmHeight;\r
921 }\r
922 \r
923 int SurfaceImpl::AverageCharWidth(Font &font_) {\r
924         SetFont(font_);\r
925         TEXTMETRIC tm;\r
926         ::GetTextMetrics(hdc, &tm);\r
927         return tm.tmAveCharWidth;\r
928 }\r
929 \r
930 int SurfaceImpl::SetPalette(Palette *pal, bool inBackGround) {\r
931         if (paletteOld) {\r
932                 ::SelectPalette(hdc, paletteOld, TRUE);\r
933         }\r
934         paletteOld = 0;\r
935         int changes = 0;\r
936         if (pal->allowRealization) {\r
937                 paletteOld = ::SelectPalette(hdc,\r
938                         reinterpret_cast<HPALETTE>(pal->hpal), inBackGround);\r
939                 changes = ::RealizePalette(hdc);\r
940         }\r
941         return changes;\r
942 }\r
943 \r
944 void SurfaceImpl::SetClip(PRectangle rc) {\r
945         ::IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);\r
946 }\r
947 \r
948 void SurfaceImpl::FlushCachedState() {\r
949         pen = 0;\r
950         brush = 0;\r
951         font = 0;\r
952 }\r
953 \r
954 void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) {\r
955         unicodeMode=unicodeMode_;\r
956 }\r
957 \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
962 }\r
963 \r
964 Surface *Surface::Allocate() {\r
965         return new SurfaceImpl;\r
966 }\r
967 \r
968 Window::~Window() {\r
969 }\r
970 \r
971 void Window::Destroy() {\r
972         if (id)\r
973                 ::DestroyWindow(reinterpret_cast<HWND>(id));\r
974         id = 0;\r
975 }\r
976 \r
977 bool Window::HasFocus() {\r
978         return ::GetFocus() == id;\r
979 }\r
980 \r
981 PRectangle Window::GetPosition() {\r
982         RECT rc;\r
983         ::GetWindowRect(reinterpret_cast<HWND>(id), &rc);\r
984         return PRectangle(rc.left, rc.top, rc.right, rc.bottom);\r
985 }\r
986 \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
990 }\r
991 \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
995                 RECT rcOther;\r
996                 ::GetWindowRect(reinterpret_cast<HWND>(w.GetID()), &rcOther);\r
997                 rc.Move(rcOther.left, rcOther.top);\r
998 \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
1006 #endif\r
1007 \r
1008                 if (rc.left < rcDesktop.left) {\r
1009                         rc.Move(rcDesktop.left - rc.left,0);\r
1010                 }\r
1011                 if (rc.top < rcDesktop.top) {\r
1012                         rc.Move(0,rcDesktop.top - rc.top);\r
1013                 }\r
1014         }\r
1015         SetPosition(rc);\r
1016 }\r
1017 \r
1018 PRectangle Window::GetClientPosition() {\r
1019         RECT rc={0,0,0,0};\r
1020         if (id)\r
1021                 ::GetClientRect(reinterpret_cast<HWND>(id), &rc);\r
1022         return  PRectangle(rc.left, rc.top, rc.right, rc.bottom);\r
1023 }\r
1024 \r
1025 void Window::Show(bool show) {\r
1026         if (show)\r
1027                 ::ShowWindow(reinterpret_cast<HWND>(id), SW_SHOWNOACTIVATE);\r
1028         else\r
1029                 ::ShowWindow(reinterpret_cast<HWND>(id), SW_HIDE);\r
1030 }\r
1031 \r
1032 void Window::InvalidateAll() {\r
1033         ::InvalidateRect(reinterpret_cast<HWND>(id), NULL, FALSE);\r
1034 }\r
1035 \r
1036 void Window::InvalidateRectangle(PRectangle rc) {\r
1037         RECT rcw = RectFromPRectangle(rc);\r
1038         ::InvalidateRect(reinterpret_cast<HWND>(id), &rcw, FALSE);\r
1039 }\r
1040 \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
1043 }\r
1044 \r
1045 void Window::SetFont(Font &font) {\r
1046         Window_SendMessage(this, WM_SETFONT,\r
1047                 reinterpret_cast<WPARAM>(font.GetID()), 0);\r
1048 }\r
1049 \r
1050 void Window::SetCursor(Cursor curs) {\r
1051         switch (curs) {\r
1052         case cursorText:\r
1053                 ::SetCursor(::LoadCursor(NULL,IDC_IBEAM));\r
1054                 break;\r
1055         case cursorUp:\r
1056                 ::SetCursor(::LoadCursor(NULL,IDC_UPARROW));\r
1057                 break;\r
1058         case cursorWait:\r
1059                 ::SetCursor(::LoadCursor(NULL,IDC_WAIT));\r
1060                 break;\r
1061         case cursorHoriz:\r
1062                 ::SetCursor(::LoadCursor(NULL,IDC_SIZEWE));\r
1063                 break;\r
1064         case cursorVert:\r
1065                 ::SetCursor(::LoadCursor(NULL,IDC_SIZENS));\r
1066                 break;\r
1067         case cursorHand:\r
1068                 ::SetCursor(::LoadCursor(NULL,IDC_HAND));\r
1069                 break;\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
1078                         if (hcursor)\r
1079                                 ::SetCursor(hcursor);\r
1080                         else\r
1081                                 ::SetCursor(::LoadCursor(NULL,IDC_ARROW));\r
1082                 }\r
1083                 break;\r
1084         case cursorArrow:\r
1085         case cursorInvalid:     // Should not occur, but just in case.\r
1086                 ::SetCursor(::LoadCursor(NULL,IDC_ARROW));\r
1087                 break;\r
1088         }\r
1089 }\r
1090 \r
1091 void Window::SetTitle(const char *s) {\r
1092         ::SetWindowTextA(reinterpret_cast<HWND>(id), s);\r
1093 }\r
1094 \r
1095 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's\r
1096    coordinates */\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
1105         MONITORINFOEX mi;\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
1114                 return rcMonitor;\r
1115         }\r
1116 }\r
1117 #else\r
1118 PRectangle Window::GetMonitorRect(Point) {\r
1119         return PRectangle();\r
1120 }\r
1121 #endif\r
1122 \r
1123 struct ListItemData {\r
1124         const char *text;\r
1125         int pixId;\r
1126 };\r
1127 \r
1128 #define _ROUND2(n,pow2) \\r
1129         ( ( (n) + (pow2) - 1) & ~((pow2) - 1) )\r
1130 \r
1131 class LineToItem {\r
1132         char *words;\r
1133         int wordsCount;\r
1134         int wordsSize;\r
1135 \r
1136         ListItemData *data;\r
1137         int len;\r
1138         int count;\r
1139 \r
1140 private:\r
1141         void FreeWords() {\r
1142                 delete []words;\r
1143                 words = NULL;\r
1144                 wordsCount = 0;\r
1145                 wordsSize = 0;\r
1146         }\r
1147         char *AllocWord(const char *word);\r
1148 \r
1149 public:\r
1150         LineToItem() : words(NULL), wordsCount(0), wordsSize(0), data(NULL), len(0), count(0) {\r
1151         }\r
1152         ~LineToItem() {\r
1153                 Clear();\r
1154         }\r
1155         void Clear() {\r
1156                 FreeWords();\r
1157                 delete []data;\r
1158                 data = NULL;\r
1159                 len = 0;\r
1160                 count = 0;\r
1161         }\r
1162 \r
1163         ListItemData *Append(const char *text, int value);\r
1164 \r
1165         ListItemData Get(int index) const {\r
1166                 if (index >= 0 && index < count) {\r
1167                         return data[index];\r
1168                 } else {\r
1169                         ListItemData missing = {"", -1};\r
1170                         return missing;\r
1171                 }\r
1172         }\r
1173         int Count() const {\r
1174                 return count;\r
1175         }\r
1176 \r
1177         ListItemData *AllocItem();\r
1178 \r
1179         void SetWords(char *s) {\r
1180                 words = s;      // N.B. will be deleted on destruction\r
1181         }\r
1182 };\r
1183 \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
1194                 delete []words;\r
1195                 words = wordsNew;\r
1196         }\r
1197         char *s = &words[wordsCount];\r
1198         wordsCount = newCount;\r
1199         strncpy(s, text, chars);\r
1200         return s;\r
1201 }\r
1202 \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
1208                 delete []data;\r
1209                 data = dataNew;\r
1210                 len = lenNew;\r
1211         }\r
1212         ListItemData *item = &data[count];\r
1213         count++;\r
1214         return item;\r
1215 }\r
1216 \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
1221         return item;\r
1222 }\r
1223 \r
1224 const TCHAR ListBoxX_ClassName[] = TEXT("ListBoxX");\r
1225 \r
1226 ListBox::ListBox() {\r
1227 }\r
1228 \r
1229 ListBox::~ListBox() {\r
1230 }\r
1231 \r
1232 class ListBoxX : public ListBox {\r
1233         int lineHeight;\r
1234         FontID fontCopy;\r
1235         XPMSet xset;\r
1236         LineToItem lti;\r
1237         HWND lb;\r
1238         bool unicodeMode;\r
1239         int desiredVisibleRows;\r
1240         unsigned int maxItemCharacters;\r
1241         unsigned int aveCharWidth;\r
1242         Window *parent;\r
1243         int ctrlID;\r
1244         CallBackAction doubleClickAction;\r
1245         void *doubleClickActionData;\r
1246         const char *widestItem;\r
1247         unsigned int maxCharWidth;\r
1248         int resizeHit;\r
1249         PRectangle rcPreSize;\r
1250         Point dragOffset;\r
1251         Point location; // Caret location at which the list is opened\r
1252 \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
1268         void Paint(HDC);\r
1269         void Erase(HDC);\r
1270         static LRESULT PASCAL ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);\r
1271 \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
1275 \r
1276 public:\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
1281         }\r
1282         virtual ~ListBoxX() {\r
1283                 if (fontCopy) {\r
1284                         ::DeleteObject(fontCopy);\r
1285                         fontCopy = 0;\r
1286                 }\r
1287         }\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
1307         }\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
1312 };\r
1313 \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
1317 \r
1318 ListBox *ListBox::Allocate() {\r
1319         ListBoxX *lb = new ListBoxX();\r
1320         return lb;\r
1321 }\r
1322 \r
1323 void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_) {\r
1324         parent = &parent_;\r
1325         ctrlID = ctrlID_;\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
1336                 NULL,\r
1337                 hinstanceParent,\r
1338                 this);\r
1339 \r
1340         ::MapWindowPoints(hwndParent, NULL, reinterpret_cast<POINT*>(&location), 1);\r
1341 }\r
1342 \r
1343 void ListBoxX::SetFont(Font &font) {\r
1344         LOGFONT lf;\r
1345         if (0 != ::GetObject(font.GetID(), sizeof(lf), &lf)) {\r
1346                 if (fontCopy) {\r
1347                         ::DeleteObject(fontCopy);\r
1348                         fontCopy = 0;\r
1349                 }\r
1350                 fontCopy = ::CreateFontIndirect(&lf);\r
1351                 ::SendMessage(lb, WM_SETFONT, reinterpret_cast<WPARAM>(fontCopy), 0);\r
1352         }\r
1353 }\r
1354 \r
1355 void ListBoxX::SetAverageCharWidth(int width) {\r
1356         aveCharWidth = width;\r
1357 }\r
1358 \r
1359 void ListBoxX::SetVisibleRows(int rows) {\r
1360         desiredVisibleRows = rows;\r
1361 }\r
1362 \r
1363 int ListBoxX::GetVisibleRows() const {\r
1364         return desiredVisibleRows;\r
1365 }\r
1366 \r
1367 HWND ListBoxX::GetHWND() const {\r
1368         return reinterpret_cast<HWND>(GetID());\r
1369 }\r
1370 \r
1371 PRectangle ListBoxX::GetDesiredRect() {\r
1372         PRectangle rcDesired = GetPosition();\r
1373 \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
1378 \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
1387         } else {\r
1388                 ::GetTextExtentPoint32A(hdc, widestItem, len, &textSize);\r
1389         }\r
1390         TEXTMETRIC tm;\r
1391         ::GetTextMetrics(hdc, &tm);\r
1392         maxCharWidth = tm.tmMaxCharWidth;\r
1393         SelectFont(hdc, oldFont);\r
1394         ::ReleaseDC(lb, hdc);\r
1395 \r
1396         int widthDesired = Platform::Maximum(textSize.cx, (len + 1) * tm.tmAveCharWidth);\r
1397         if (width < widthDesired)\r
1398                 width = widthDesired;\r
1399 \r
1400         rcDesired.right = rcDesired.left + TextOffset() + width + (TextInset.x * 2);\r
1401         if (Length() > rows)\r
1402                 rcDesired.right += ::GetSystemMetrics(SM_CXVSCROLL);\r
1403 \r
1404         AdjustWindowRect(&rcDesired);\r
1405         return rcDesired;\r
1406 }\r
1407 \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
1411 }\r
1412 \r
1413 int ListBoxX::CaretFromEdge() {\r
1414         PRectangle rc;\r
1415         AdjustWindowRect(&rc);\r
1416         return TextOffset() + TextInset.x + (0 - rc.left) - 1;\r
1417 }\r
1418 \r
1419 void ListBoxX::Clear() {\r
1420         ::SendMessage(lb, LB_RESETCONTENT, 0, 0);\r
1421         maxItemCharacters = 0;\r
1422         widestItem = NULL;\r
1423         lti.Clear();\r
1424 }\r
1425 \r
1426 void ListBoxX::Append(char *s, int type) {\r
1427         int index = ::SendMessage(lb, LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(s));\r
1428         if (index < 0)\r
1429                 return;\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
1435         }\r
1436 }\r
1437 \r
1438 int ListBoxX::Length() {\r
1439         return lti.Count();\r
1440 }\r
1441 \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
1446         SetRedraw(false);\r
1447         CentreItem(n);\r
1448         ::SendMessage(lb, LB_SETCURSEL, n, 0);\r
1449         SetRedraw(true);\r
1450 }\r
1451 \r
1452 int ListBoxX::GetSelection() {\r
1453         return ::SendMessage(lb, LB_GETCURSEL, 0, 0);\r
1454 }\r
1455 \r
1456 // This is not actually called at present\r
1457 int ListBoxX::Find(const char *) {\r
1458         return LB_ERR;\r
1459 }\r
1460 \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
1465 }\r
1466 \r
1467 void ListBoxX::RegisterImage(int type, const char *xpm_data) {\r
1468         xset.Add(type, xpm_data);\r
1469 }\r
1470 \r
1471 void ListBoxX::ClearRegisteredImages() {\r
1472         xset.Clear();\r
1473 }\r
1474 \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
1487                 } else {\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
1491                 }\r
1492 \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
1497 \r
1498                 RECT rcText = rcBox;\r
1499                 ::InsetRect(&rcText, TextInset.x, TextInset.y);\r
1500 \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
1504                 } else {\r
1505                         ::DrawTextA(pDrawItem->hDC, text, len, &rcText, DT_NOPREFIX|DT_END_ELLIPSIS|DT_SINGLELINE|DT_NOCLIP);\r
1506                 }\r
1507                 if (pDrawItem->itemState & ODS_SELECTED) {\r
1508                         ::DrawFocusRect(pDrawItem->hDC, &rcBox);\r
1509                 }\r
1510 \r
1511                 // Draw the image, if any\r
1512                 XPM *pxpm = xset.Get(pixId);\r
1513                 if (pxpm) {\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
1525                         }\r
1526                 }\r
1527         }\r
1528 }\r
1529 \r
1530 void ListBoxX::AppendListItem(const char *startword, const char *numword) {\r
1531         ListItemData *item = lti.AllocItem();\r
1532         item->text = startword;\r
1533         if (numword) {\r
1534                 int pixId = 0;\r
1535                 char ch;\r
1536                 while ((ch = *++numword) != '\0') {\r
1537                         pixId = 10 * pixId + (ch - '0');\r
1538                 }\r
1539                 item->pixId = pixId;\r
1540         } else {\r
1541                 item->pixId = -1;\r
1542         }\r
1543 \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
1548         }\r
1549 }\r
1550 \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
1554         SetRedraw(false);\r
1555         Clear();\r
1556         int size = strlen(list) + 1;\r
1557         char *words = new char[size];\r
1558         if (words) {\r
1559                 lti.SetWords(words);\r
1560                 memcpy(words, list, size);\r
1561                 char *startword = words;\r
1562                 char *numword = NULL;\r
1563                 int i = 0;\r
1564                 for (; words[i]; i++) {\r
1565                         if (words[i] == separator) {\r
1566                                 words[i] = '\0';\r
1567                                 if (numword)\r
1568                                         *numword = '\0';\r
1569                                 AppendListItem(startword, numword);\r
1570                                 startword = words + i + 1;\r
1571                                 numword = NULL;\r
1572                         } else if (words[i] == typesep) {\r
1573                                 numword = words + i;\r
1574                         }\r
1575                 }\r
1576                 if (startword) {\r
1577                         if (numword)\r
1578                                 *numword = '\0';\r
1579                         AppendListItem(startword, numword);\r
1580                 }\r
1581 \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
1587                 }\r
1588         }\r
1589         SetRedraw(true);\r
1590 }\r
1591 \r
1592 void ListBoxX::AdjustWindowRect(PRectangle *rc) const {\r
1593         ::AdjustWindowRectEx(reinterpret_cast<RECT*>(rc), WS_THICKFRAME, false, WS_EX_WINDOWEDGE);\r
1594 }\r
1595 \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
1601         }\r
1602         return itemHeight;\r
1603 }\r
1604 \r
1605 int ListBoxX::MinClientWidth() const {\r
1606         return 12 * (aveCharWidth+aveCharWidth/3);\r
1607 }\r
1608 \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
1613 }\r
1614 \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
1622 }\r
1623 \r
1624 void ListBoxX::SetRedraw(bool on) {\r
1625         ::SendMessage(lb, WM_SETREDRAW, static_cast<BOOL>(on), 0);\r
1626         if (on)\r
1627                 ::InvalidateRect(lb, NULL, TRUE);\r
1628 }\r
1629 \r
1630 void ListBoxX::ResizeToCursor() {\r
1631         PRectangle rc = GetPosition();\r
1632         Point pt;\r
1633         ::GetCursorPos(reinterpret_cast<POINT*>(&pt));\r
1634         pt.x += dragOffset.x;\r
1635         pt.y += dragOffset.y;\r
1636 \r
1637         switch (resizeHit) {\r
1638                 case HTLEFT:\r
1639                         rc.left = pt.x;\r
1640                         break;\r
1641                 case HTRIGHT:\r
1642                         rc.right = pt.x;\r
1643                         break;\r
1644                 case HTTOP:\r
1645                         rc.top = pt.y;\r
1646                         break;\r
1647                 case HTTOPLEFT:\r
1648                         rc.top = pt.y;\r
1649                         rc.left = pt.x;\r
1650                         break;\r
1651                 case HTTOPRIGHT:\r
1652                         rc.top = pt.y;\r
1653                         rc.right = pt.x;\r
1654                         break;\r
1655                 case HTBOTTOM:\r
1656                         rc.bottom = pt.y;\r
1657                         break;\r
1658                 case HTBOTTOMLEFT:\r
1659                         rc.bottom = pt.y;\r
1660                         rc.left = pt.x;\r
1661                         break;\r
1662                 case HTBOTTOMRIGHT:\r
1663                         rc.bottom = pt.y;\r
1664                         rc.right = pt.x;\r
1665                         break;\r
1666         }\r
1667 \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
1675 \r
1676         SetPosition(rc);\r
1677 }\r
1678 \r
1679 void ListBoxX::StartResize(WPARAM hitCode) {\r
1680         rcPreSize = GetPosition();\r
1681         POINT cursorPos;\r
1682         ::GetCursorPos(&cursorPos);\r
1683 \r
1684         switch (hitCode) {\r
1685                 case HTRIGHT:\r
1686                 case HTBOTTOM:\r
1687                 case HTBOTTOMRIGHT:\r
1688                         dragOffset.x = rcPreSize.right - cursorPos.x;\r
1689                         dragOffset.y = rcPreSize.bottom - cursorPos.y;\r
1690                         break;\r
1691 \r
1692                 case HTTOPRIGHT:\r
1693                         dragOffset.x = rcPreSize.right - cursorPos.x;\r
1694                         dragOffset.y = rcPreSize.top - cursorPos.y;\r
1695                         break;\r
1696 \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
1699                 case HTLEFT:\r
1700                 case HTTOP:\r
1701                 case HTTOPLEFT:\r
1702                         dragOffset.x = rcPreSize.left - cursorPos.x;\r
1703                         dragOffset.y = rcPreSize.top - cursorPos.y;\r
1704                         break;\r
1705                 case HTBOTTOMLEFT:\r
1706                         dragOffset.x = rcPreSize.left - cursorPos.x;\r
1707                         dragOffset.y = rcPreSize.bottom - cursorPos.y;\r
1708                         break;\r
1709 \r
1710                 default:\r
1711                         return;\r
1712         }\r
1713 \r
1714         ::SetCapture(GetHWND());\r
1715         resizeHit = hitCode;\r
1716 }\r
1717 \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
1730                 }\r
1731         }\r
1732 \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
1735         switch (hit) {\r
1736                 case HTLEFT:\r
1737                 case HTTOPLEFT:\r
1738                 case HTBOTTOMLEFT:\r
1739                         hit = HTERROR;\r
1740                         break;\r
1741 \r
1742                 case HTTOP:\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
1747                                         hit = HTERROR;\r
1748                         }\r
1749                         break;\r
1750 \r
1751                 case HTBOTTOM:\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
1756                                         hit = HTERROR;\r
1757                         }\r
1758                         break;\r
1759         }\r
1760 \r
1761         return hit;\r
1762 }\r
1763 \r
1764 void ListBoxX::OnDoubleClick() {\r
1765 \r
1766         if (doubleClickAction != NULL) {\r
1767                 doubleClickAction(doubleClickActionData);\r
1768         }\r
1769 }\r
1770 \r
1771 Point ListBoxX::GetClientExtent() const {\r
1772         PRectangle rc = const_cast<ListBoxX*>(this)->GetClientPosition();\r
1773         return Point(rc.Width(), rc.Height());\r
1774 }\r
1775 \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
1778         if (n >= 0) {\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
1786                 }\r
1787         }\r
1788 }\r
1789 \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
1809 }\r
1810 \r
1811 LRESULT PASCAL ListBoxX::ControlWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {\r
1812         switch (uMsg) {\r
1813         case WM_ERASEBKGND:\r
1814                 return TRUE;\r
1815 \r
1816         case WM_PAINT: {\r
1817                         PAINTSTRUCT ps;\r
1818                         HDC hDC = ::BeginPaint(hWnd, &ps);\r
1819                         ListBoxX *lbx = reinterpret_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)));\r
1820                         if (lbx)\r
1821                                 lbx->Paint(hDC);\r
1822                         ::EndPaint(hWnd, &ps);\r
1823                 }\r
1824                 return 0;\r
1825 \r
1826         case WM_MOUSEACTIVATE:\r
1827                 // This prevents the view activating when the scrollbar is clicked\r
1828                 return MA_NOACTIVATE;\r
1829 \r
1830         case WM_LBUTTONDOWN: {\r
1831                         // We must take control of selection to prevent the ListBox activating\r
1832                         // the popup\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
1837                         }\r
1838                 }\r
1839                 return 0;\r
1840 \r
1841         case WM_LBUTTONUP:\r
1842                 return 0;\r
1843 \r
1844         case WM_LBUTTONDBLCLK: {\r
1845                         ListBoxX *lbx = reinterpret_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)));\r
1846                         if (lbx) {\r
1847                                 lbx->OnDoubleClick();\r
1848                         }\r
1849                 }\r
1850                 return 0;\r
1851         }\r
1852 \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
1856         } else {\r
1857                 return ::DefWindowProc(hWnd, uMsg, wParam, lParam);\r
1858         }\r
1859 }\r
1860 \r
1861 LRESULT ListBoxX::WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {\r
1862         switch (iMessage) {\r
1863         case WM_CREATE: {\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
1873                                 hinstanceParent,\r
1874                                 0);\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
1877                 }\r
1878                 break;\r
1879 \r
1880         case WM_SIZE:\r
1881                 if (lb) {\r
1882                         SetRedraw(false);\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
1886                         SetRedraw(true);\r
1887                 }\r
1888                 break;\r
1889 \r
1890         case WM_PAINT: {\r
1891                         PAINTSTRUCT ps;\r
1892                         ::BeginPaint(hWnd, &ps);\r
1893                         ::EndPaint(hWnd, &ps);\r
1894                 }\r
1895                 break;\r
1896 \r
1897         case WM_COMMAND:\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
1901                 break;\r
1902 \r
1903         case WM_MEASUREITEM: {\r
1904                         MEASUREITEMSTRUCT *pMeasureItem = reinterpret_cast<MEASUREITEMSTRUCT *>(lParam);\r
1905                         pMeasureItem->itemHeight = static_cast<unsigned int>(ItemHeight());\r
1906                 }\r
1907                 break;\r
1908 \r
1909         case WM_DRAWITEM:\r
1910                 Draw(reinterpret_cast<DRAWITEMSTRUCT *>(lParam));\r
1911                 break;\r
1912 \r
1913         case WM_DESTROY:\r
1914                 lb = 0;\r
1915                 ::SetWindowLong(hWnd, 0, 0);\r
1916                 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);\r
1917 \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
1921                 return TRUE;\r
1922 \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
1927                 }\r
1928                 break;\r
1929 \r
1930         case WM_MOUSEACTIVATE:\r
1931                 return MA_NOACTIVATE;\r
1932 \r
1933         case WM_NCHITTEST:\r
1934                 return NcHitTest(wParam, lParam);\r
1935 \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
1940                 return 0;\r
1941 \r
1942         case WM_MOUSEMOVE: {\r
1943                         if (resizeHit == 0) {\r
1944                                 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);\r
1945                         } else {\r
1946                                 ResizeToCursor();\r
1947                         }\r
1948                 }\r
1949                 break;\r
1950 \r
1951         case WM_LBUTTONUP:\r
1952         case WM_CANCELMODE:\r
1953                 if (resizeHit != 0) {\r
1954                         resizeHit = 0;\r
1955                         ::ReleaseCapture();\r
1956                 }\r
1957                 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);\r
1958 \r
1959         default:\r
1960                 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);\r
1961         }\r
1962 \r
1963         return 0;\r
1964 }\r
1965 \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
1971         }\r
1972         // Find C++ object associated with window.\r
1973         ListBoxX *lbx = reinterpret_cast<ListBoxX *>(PointerFromWindow(hWnd));\r
1974         if (lbx) {\r
1975                 return lbx->WndProc(hWnd, iMessage, wParam, lParam);\r
1976         } else {\r
1977                 return ::DefWindowProc(hWnd, iMessage, wParam, lParam);\r
1978         }\r
1979 }\r
1980 \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
1998 \r
1999         return ::RegisterClassEx(&wndclassc) != 0;\r
2000 }\r
2001 \r
2002 bool ListBoxX_Unregister() {\r
2003         return ::UnregisterClass(ListBoxX_ClassName, hinstPlatformRes) != 0;\r
2004 }\r
2005 \r
2006 Menu::Menu() : id(0) {\r
2007 }\r
2008 \r
2009 void Menu::CreatePopUp() {\r
2010         Destroy();\r
2011         id = ::CreatePopupMenu();\r
2012 }\r
2013 \r
2014 void Menu::Destroy() {\r
2015         if (id)\r
2016                 ::DestroyMenu(reinterpret_cast<HMENU>(id));\r
2017         id = 0;\r
2018 }\r
2019 \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
2024         Destroy();\r
2025 }\r
2026 \r
2027 static bool initialisedET = false;\r
2028 static bool usePerformanceCounter = false;\r
2029 static LARGE_INTEGER frequency;\r
2030 \r
2031 ElapsedTime::ElapsedTime() {\r
2032         if (!initialisedET) {\r
2033                 usePerformanceCounter = ::QueryPerformanceFrequency(&frequency) != 0;\r
2034                 initialisedET = true;\r
2035         }\r
2036         if (usePerformanceCounter) {\r
2037                 LARGE_INTEGER timeVal;\r
2038                 ::QueryPerformanceCounter(&timeVal);\r
2039                 bigBit = timeVal.HighPart;\r
2040                 littleBit = timeVal.LowPart;\r
2041         } else {\r
2042                 bigBit = clock();\r
2043         }\r
2044 }\r
2045 \r
2046 double ElapsedTime::Duration(bool reset) {\r
2047         double result;\r
2048         long endBigBit;\r
2049         long endLittleBit;\r
2050 \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
2061         } else {\r
2062                 endBigBit = clock();\r
2063                 endLittleBit = 0;\r
2064                 double elapsed = endBigBit - bigBit;\r
2065                 result = elapsed / CLOCKS_PER_SEC;\r
2066         }\r
2067         if (reset) {\r
2068                 bigBit = endBigBit;\r
2069                 littleBit = endLittleBit;\r
2070         }\r
2071         return result;\r
2072 }\r
2073 \r
2074 class DynamicLibraryImpl : public DynamicLibrary {\r
2075 protected:\r
2076         HMODULE h;\r
2077 public:\r
2078         DynamicLibraryImpl(const char *modulePath) {\r
2079                 h = ::LoadLibraryA(modulePath);\r
2080         }\r
2081 \r
2082         virtual ~DynamicLibraryImpl() {\r
2083                 if (h != NULL)\r
2084                         ::FreeLibrary(h);\r
2085         }\r
2086 \r
2087         // Use GetProcAddress to get a pointer to the relevant function.\r
2088         virtual Function FindFunction(const char *name) {\r
2089                 if (h != NULL) {\r
2090                         return static_cast<Function>(\r
2091                                 (void *)(::GetProcAddress(h, name)));\r
2092                 } else\r
2093                         return NULL;\r
2094         }\r
2095 \r
2096         virtual bool IsValid() {\r
2097                 return h != NULL;\r
2098         }\r
2099 };\r
2100 \r
2101 DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {\r
2102         return static_cast<DynamicLibrary *>(new DynamicLibraryImpl(modulePath));\r
2103 }\r
2104 \r
2105 ColourDesired Platform::Chrome() {\r
2106         return ::GetSysColor(COLOR_3DFACE);\r
2107 }\r
2108 \r
2109 ColourDesired Platform::ChromeHighlight() {\r
2110         return ::GetSysColor(COLOR_3DHIGHLIGHT);\r
2111 }\r
2112 \r
2113 const char *Platform::DefaultFont() {\r
2114         return "Verdana";\r
2115 }\r
2116 \r
2117 int Platform::DefaultFontSize() {\r
2118         return 8;\r
2119 }\r
2120 \r
2121 unsigned int Platform::DoubleClickTime() {\r
2122         return ::GetDoubleClickTime();\r
2123 }\r
2124 \r
2125 bool Platform::MouseButtonBounce() {\r
2126         return false;\r
2127 }\r
2128 \r
2129 void Platform::DebugDisplay(const char *s) {\r
2130         ::OutputDebugStringA(s);\r
2131 }\r
2132 \r
2133 bool Platform::IsKeyDown(int key) {\r
2134         return (::GetKeyState(key) & 0x80000000) != 0;\r
2135 }\r
2136 \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
2139 }\r
2140 \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
2144 }\r
2145 \r
2146 bool Platform::IsDBCSLeadByte(int codePage, char ch) {\r
2147         return ::IsDBCSLeadByteEx(codePage, ch) != 0;\r
2148 }\r
2149 \r
2150 int Platform::DBCSCharLength(int codePage, const char *s) {\r
2151         return (::IsDBCSLeadByteEx(codePage, s[0]) != 0) ? 2 : 1;\r
2152 }\r
2153 \r
2154 int Platform::DBCSCharMaxLength() {\r
2155         return 2;\r
2156 }\r
2157 \r
2158 // These are utility functions not really tied to a platform\r
2159 \r
2160 int Platform::Minimum(int a, int b) {\r
2161         if (a < b)\r
2162                 return a;\r
2163         else\r
2164                 return b;\r
2165 }\r
2166 \r
2167 int Platform::Maximum(int a, int b) {\r
2168         if (a > b)\r
2169                 return a;\r
2170         else\r
2171                 return b;\r
2172 }\r
2173 \r
2174 //#define TRACE\r
2175 \r
2176 #ifdef TRACE\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
2184 }\r
2185 #else\r
2186 void Platform::DebugPrintf(const char *, ...) {\r
2187 }\r
2188 #endif\r
2189 \r
2190 static bool assertionPopUps = true;\r
2191 \r
2192 bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {\r
2193         bool ret = assertionPopUps;\r
2194         assertionPopUps = assertionPopUps_;\r
2195         return ret;\r
2196 }\r
2197 \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
2205                         ::DebugBreak();\r
2206                 } else if (idButton == IDIGNORE) {\r
2207                         // all OK\r
2208                 } else {\r
2209                         abort();\r
2210                 }\r
2211         } else {\r
2212                 strcat(buffer, "\r\n");\r
2213                 Platform::DebugDisplay(buffer);\r
2214                 ::DebugBreak();\r
2215                 abort();\r
2216         }\r
2217 }\r
2218 \r
2219 int Platform::Clamp(int val, int minVal, int maxVal) {\r
2220         if (val > maxVal)\r
2221                 val = maxVal;\r
2222         if (val < minVal)\r
2223                 val = minVal;\r
2224         return val;\r
2225 }\r
2226 \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
2235         if (!hDLLImage) {\r
2236                 hDLLImage = ::LoadLibrary(TEXT("Msimg32"));\r
2237         }\r
2238         if (hDLLImage) {\r
2239                 AlphaBlendFn = (AlphaBlendSig)::GetProcAddress(hDLLImage, "AlphaBlend");\r
2240         }\r
2241         ListBoxX_Register();\r
2242 }\r
2243 \r
2244 void Platform_Finalise() {\r
2245         ListBoxX_Unregister();\r
2246         ::DeleteCriticalSection(&crPlatformLock);\r
2247 }\r