Add an UI to enable/disable specific overlay handlers.
[TortoiseGit.git] / ext / scintilla / gtk / PlatGTK.cxx
blob30aa73983a6e5f9a1d59f6b582f9e40015ced851
1 // Scintilla source code edit control
2 // PlatGTK.cxx - implementation of platform facilities on GTK+/Linux
3 // Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
4 // The License.txt file describes the conditions under which this software may be distributed.
6 #include <string.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stddef.h>
11 #include <glib.h>
12 #include <gmodule.h>
13 #include <gdk/gdk.h>
14 #include <gtk/gtk.h>
15 #include <gdk/gdkkeysyms.h>
17 #include "Platform.h"
19 #include "Scintilla.h"
20 #include "ScintillaWidget.h"
21 #include "UniConversion.h"
22 #include "XPM.h"
24 /* GLIB must be compiled with thread support, otherwise we
25 will bail on trying to use locks, and that could lead to
26 problems for someone. `glib-config --libs gthread` needs
27 to be used to get the glib libraries for linking, otherwise
28 g_thread_init will fail */
29 #define USE_LOCK defined(G_THREADS_ENABLED) && !defined(G_THREADS_IMPL_NONE)
30 /* Use fast way of getting char data on win32 to work around problems
31 with gdk_string_extents. */
32 #define FAST_WAY
34 #if GTK_MAJOR_VERSION >= 2
35 #define USE_PANGO 1
36 #include "Converter.h"
37 #endif
39 #ifdef _MSC_VER
40 // Ignore unreferenced local functions in GTK+ headers
41 #pragma warning(disable: 4505)
42 #endif
44 enum encodingType { singleByte, UTF8, dbcs};
46 struct LOGFONT {
47 int size;
48 bool bold;
49 bool italic;
50 int characterSet;
51 char faceName[300];
54 #if USE_LOCK
55 static GMutex *fontMutex = NULL;
57 static void InitializeGLIBThreads() {
58 if (!g_thread_supported()) {
59 g_thread_init(NULL);
62 #endif
64 static void FontMutexAllocate() {
65 #if USE_LOCK
66 if (!fontMutex) {
67 InitializeGLIBThreads();
68 fontMutex = g_mutex_new();
70 #endif
73 static void FontMutexFree() {
74 #if USE_LOCK
75 if (fontMutex) {
76 g_mutex_free(fontMutex);
77 fontMutex = NULL;
79 #endif
82 static void FontMutexLock() {
83 #if USE_LOCK
84 g_mutex_lock(fontMutex);
85 #endif
88 static void FontMutexUnlock() {
89 #if USE_LOCK
90 if (fontMutex) {
91 g_mutex_unlock(fontMutex);
93 #endif
96 // On GTK+ 1.x holds a GdkFont* but on GTK+ 2.x can hold a GdkFont* or a
97 // PangoFontDescription*.
98 class FontHandle {
99 int width[128];
100 encodingType et;
101 public:
102 int ascent;
103 GdkFont *pfont;
104 #ifdef USE_PANGO
105 PangoFontDescription *pfd;
106 int characterSet;
107 #endif
108 FontHandle(GdkFont *pfont_) {
109 et = singleByte;
110 ascent = 0;
111 pfont = pfont_;
112 #ifdef USE_PANGO
113 pfd = 0;
114 characterSet = -1;
115 #endif
116 ResetWidths(et);
118 #ifdef USE_PANGO
119 FontHandle(PangoFontDescription *pfd_, int characterSet_) {
120 et = singleByte;
121 ascent = 0;
122 pfont = 0;
123 pfd = pfd_;
124 characterSet = characterSet_;
125 ResetWidths(et);
127 #endif
128 ~FontHandle() {
129 if (pfont)
130 gdk_font_unref(pfont);
131 pfont = 0;
132 #ifdef USE_PANGO
133 if (pfd)
134 pango_font_description_free(pfd);
135 pfd = 0;
136 #endif
138 void ResetWidths(encodingType et_) {
139 et = et_;
140 for (int i=0; i<=127; i++) {
141 width[i] = 0;
144 int CharWidth(unsigned char ch, encodingType et_) {
145 int w = 0;
146 FontMutexLock();
147 if ((ch <= 127) && (et == et_)) {
148 w = width[ch];
150 FontMutexUnlock();
151 return w;
153 void SetCharWidth(unsigned char ch, int w, encodingType et_) {
154 if (ch <= 127) {
155 FontMutexLock();
156 if (et != et_) {
157 ResetWidths(et_);
159 width[ch] = w;
160 FontMutexUnlock();
165 // X has a 16 bit coordinate space, so stop drawing here to avoid wrapping
166 static const int maxCoordinate = 32000;
168 static FontHandle *PFont(Font &f) {
169 return reinterpret_cast<FontHandle *>(f.GetID());
172 static GtkWidget *PWidget(WindowID id) {
173 return reinterpret_cast<GtkWidget *>(id);
176 static GtkWidget *PWidget(Window &w) {
177 return PWidget(w.GetID());
180 Point Point::FromLong(long lpoint) {
181 return Point(
182 Platform::LowShortFromLong(lpoint),
183 Platform::HighShortFromLong(lpoint));
186 Palette::Palette() {
187 used = 0;
188 allowRealization = false;
189 allocatedPalette = 0;
190 allocatedLen = 0;
191 size = 100;
192 entries = new ColourPair[size];
195 Palette::~Palette() {
196 Release();
197 delete []entries;
198 entries = 0;
201 void Palette::Release() {
202 used = 0;
203 delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
204 allocatedPalette = 0;
205 allocatedLen = 0;
206 delete []entries;
207 size = 100;
208 entries = new ColourPair[size];
211 // This method either adds a colour to the list of wanted colours (want==true)
212 // or retrieves the allocated colour back to the ColourPair.
213 // This is one method to make it easier to keep the code for wanting and retrieving in sync.
214 void Palette::WantFind(ColourPair &cp, bool want) {
215 if (want) {
216 for (int i=0; i < used; i++) {
217 if (entries[i].desired == cp.desired)
218 return;
221 if (used >= size) {
222 int sizeNew = size * 2;
223 ColourPair *entriesNew = new ColourPair[sizeNew];
224 for (int j=0; j<size; j++) {
225 entriesNew[j] = entries[j];
227 delete []entries;
228 entries = entriesNew;
229 size = sizeNew;
232 entries[used].desired = cp.desired;
233 entries[used].allocated.Set(cp.desired.AsLong());
234 used++;
235 } else {
236 for (int i=0; i < used; i++) {
237 if (entries[i].desired == cp.desired) {
238 cp.allocated = entries[i].allocated;
239 return;
242 cp.allocated.Set(cp.desired.AsLong());
246 void Palette::Allocate(Window &w) {
247 if (allocatedPalette) {
248 gdk_colormap_free_colors(gtk_widget_get_colormap(PWidget(w)),
249 reinterpret_cast<GdkColor *>(allocatedPalette),
250 allocatedLen);
251 delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
252 allocatedPalette = 0;
253 allocatedLen = 0;
255 GdkColor *paletteNew = new GdkColor[used];
256 allocatedPalette = paletteNew;
257 gboolean *successPalette = new gboolean[used];
258 if (paletteNew) {
259 allocatedLen = used;
260 int iPal = 0;
261 for (iPal = 0; iPal < used; iPal++) {
262 paletteNew[iPal].red = entries[iPal].desired.GetRed() * (65535 / 255);
263 paletteNew[iPal].green = entries[iPal].desired.GetGreen() * (65535 / 255);
264 paletteNew[iPal].blue = entries[iPal].desired.GetBlue() * (65535 / 255);
265 paletteNew[iPal].pixel = entries[iPal].desired.AsLong();
267 gdk_colormap_alloc_colors(gtk_widget_get_colormap(PWidget(w)),
268 paletteNew, allocatedLen, FALSE, TRUE,
269 successPalette);
270 for (iPal = 0; iPal < used; iPal++) {
271 entries[iPal].allocated.Set(paletteNew[iPal].pixel);
274 delete []successPalette;
277 static const char *CharacterSetName(int characterSet) {
278 switch (characterSet) {
279 case SC_CHARSET_ANSI:
280 return "iso8859-*";
281 case SC_CHARSET_DEFAULT:
282 return "iso8859-*";
283 case SC_CHARSET_BALTIC:
284 return "iso8859-13";
285 case SC_CHARSET_CHINESEBIG5:
286 return "*-*";
287 case SC_CHARSET_EASTEUROPE:
288 return "*-2";
289 case SC_CHARSET_GB2312:
290 return "gb2312.1980-*";
291 case SC_CHARSET_GREEK:
292 return "*-7";
293 case SC_CHARSET_HANGUL:
294 return "ksc5601.1987-*";
295 case SC_CHARSET_MAC:
296 return "*-*";
297 case SC_CHARSET_OEM:
298 return "*-*";
299 case SC_CHARSET_RUSSIAN:
300 return "*-r";
301 case SC_CHARSET_CYRILLIC:
302 return "*-cp1251";
303 case SC_CHARSET_SHIFTJIS:
304 return "jisx0208.1983-*";
305 case SC_CHARSET_SYMBOL:
306 return "*-*";
307 case SC_CHARSET_TURKISH:
308 return "*-9";
309 case SC_CHARSET_JOHAB:
310 return "*-*";
311 case SC_CHARSET_HEBREW:
312 return "*-8";
313 case SC_CHARSET_ARABIC:
314 return "*-6";
315 case SC_CHARSET_VIETNAMESE:
316 return "*-*";
317 case SC_CHARSET_THAI:
318 return "iso8859-11";
319 case SC_CHARSET_8859_15:
320 return "iso8859-15";
321 default:
322 return "*-*";
326 static bool IsDBCSCharacterSet(int characterSet) {
327 switch (characterSet) {
328 case SC_CHARSET_GB2312:
329 case SC_CHARSET_HANGUL:
330 case SC_CHARSET_SHIFTJIS:
331 case SC_CHARSET_CHINESEBIG5:
332 return true;
333 default:
334 return false;
338 static void GenerateFontSpecStrings(const char *fontName, int characterSet,
339 char *foundary, int foundary_len,
340 char *faceName, int faceName_len,
341 char *charset, int charset_len) {
342 // supported font strings include:
343 // foundary-fontface-isoxxx-x
344 // fontface-isoxxx-x
345 // foundary-fontface
346 // fontface
347 if (strchr(fontName, '-')) {
348 char tmp[300];
349 char *d1 = NULL, *d2 = NULL, *d3 = NULL;
350 strncpy(tmp, fontName, sizeof(tmp) - 1);
351 d1 = strchr(tmp, '-');
352 // we know the first dash exists
353 d2 = strchr(d1 + 1, '-');
354 if (d2)
355 d3 = strchr(d2 + 1, '-');
356 if (d3) {
357 // foundary-fontface-isoxxx-x
358 *d2 = '\0';
359 foundary[0] = '-';
360 foundary[1] = '\0';
361 strncpy(faceName, tmp, foundary_len - 1);
362 strncpy(charset, d2 + 1, charset_len - 1);
363 } else if (d2) {
364 // fontface-isoxxx-x
365 *d1 = '\0';
366 strcpy(foundary, "-*-");
367 strncpy(faceName, tmp, faceName_len - 1);
368 strncpy(charset, d1 + 1, charset_len - 1);
369 } else {
370 // foundary-fontface
371 foundary[0] = '-';
372 foundary[1] = '\0';
373 strncpy(faceName, tmp, faceName_len - 1);
374 strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
376 } else {
377 strncpy(foundary, "-*-", foundary_len);
378 strncpy(faceName, fontName, faceName_len - 1);
379 strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
383 static void SetLogFont(LOGFONT &lf, const char *faceName, int characterSet, int size, bool bold, bool italic) {
384 memset(&lf, 0, sizeof(lf));
385 lf.size = size;
386 lf.bold = bold;
387 lf.italic = italic;
388 lf.characterSet = characterSet;
389 strncpy(lf.faceName, faceName, sizeof(lf.faceName) - 1);
393 * Create a hash from the parameters for a font to allow easy checking for identity.
394 * If one font is the same as another, its hash will be the same, but if the hash is the
395 * same then they may still be different.
397 static int HashFont(const char *faceName, int characterSet, int size, bool bold, bool italic) {
398 return
399 size ^
400 (characterSet << 10) ^
401 (bold ? 0x10000000 : 0) ^
402 (italic ? 0x20000000 : 0) ^
403 faceName[0];
406 class FontCached : Font {
407 FontCached *next;
408 int usage;
409 LOGFONT lf;
410 int hash;
411 FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
412 ~FontCached() {}
413 bool SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
414 virtual void Release();
415 static FontID CreateNewFont(const char *fontName, int characterSet,
416 int size, bool bold, bool italic);
417 static FontCached *first;
418 public:
419 static FontID FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
420 static void ReleaseId(FontID id_);
423 FontCached *FontCached::first = 0;
425 FontCached::FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) :
426 next(0), usage(0), hash(0) {
427 ::SetLogFont(lf, faceName_, characterSet_, size_, bold_, italic_);
428 hash = HashFont(faceName_, characterSet_, size_, bold_, italic_);
429 id = CreateNewFont(faceName_, characterSet_, size_, bold_, italic_);
430 usage = 1;
433 bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
434 return
435 lf.size == size_ &&
436 lf.bold == bold_ &&
437 lf.italic == italic_ &&
438 lf.characterSet == characterSet_ &&
439 0 == strcmp(lf.faceName, faceName_);
442 void FontCached::Release() {
443 if (id)
444 delete PFont(*this);
445 id = 0;
448 FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
449 FontID ret = 0;
450 FontMutexLock();
451 int hashFind = HashFont(faceName_, characterSet_, size_, bold_, italic_);
452 for (FontCached *cur = first; cur; cur = cur->next) {
453 if ((cur->hash == hashFind) &&
454 cur->SameAs(faceName_, characterSet_, size_, bold_, italic_)) {
455 cur->usage++;
456 ret = cur->id;
459 if (ret == 0) {
460 FontCached *fc = new FontCached(faceName_, characterSet_, size_, bold_, italic_);
461 if (fc) {
462 fc->next = first;
463 first = fc;
464 ret = fc->id;
467 FontMutexUnlock();
468 return ret;
471 void FontCached::ReleaseId(FontID id_) {
472 FontMutexLock();
473 FontCached **pcur = &first;
474 for (FontCached *cur = first; cur; cur = cur->next) {
475 if (cur->id == id_) {
476 cur->usage--;
477 if (cur->usage == 0) {
478 *pcur = cur->next;
479 cur->Release();
480 cur->next = 0;
481 delete cur;
483 break;
485 pcur = &cur->next;
487 FontMutexUnlock();
490 static GdkFont *LoadFontOrSet(const char *fontspec, int characterSet) {
491 if (IsDBCSCharacterSet(characterSet)) {
492 return gdk_fontset_load(fontspec);
493 } else {
494 return gdk_font_load(fontspec);
498 FontID FontCached::CreateNewFont(const char *fontName, int characterSet,
499 int size, bool bold, bool italic) {
500 char fontset[1024];
501 char fontspec[300];
502 char foundary[50];
503 char faceName[100];
504 char charset[50];
505 fontset[0] = '\0';
506 fontspec[0] = '\0';
507 foundary[0] = '\0';
508 faceName[0] = '\0';
509 charset[0] = '\0';
511 #ifdef USE_PANGO
512 if (fontName[0] == '!') {
513 PangoFontDescription *pfd = pango_font_description_new();
514 if (pfd) {
515 pango_font_description_set_family(pfd, fontName+1);
516 pango_font_description_set_size(pfd, size * PANGO_SCALE);
517 pango_font_description_set_weight(pfd, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
518 pango_font_description_set_style(pfd, italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
519 return new FontHandle(pfd, characterSet);
522 #endif
524 GdkFont *newid = 0;
525 // If name of the font begins with a '-', assume, that it is
526 // a full fontspec.
527 if (fontName[0] == '-') {
528 if (strchr(fontName, ',') || IsDBCSCharacterSet(characterSet)) {
529 newid = gdk_fontset_load(fontName);
530 } else {
531 newid = gdk_font_load(fontName);
533 if (!newid) {
534 // Font not available so substitute a reasonable code font
535 // iso8859 appears to only allow western characters.
536 newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
537 characterSet);
539 return new FontHandle(newid);
542 // it's not a full fontspec, build one.
544 // This supports creating a FONT_SET
545 // in a method that allows us to also set size, slant and
546 // weight for the fontset. The expected input is multiple
547 // partial fontspecs seperated by comma
548 // eg. adobe-courier-iso10646-1,*-courier-iso10646-1,*-*-*-*
549 if (strchr(fontName, ',')) {
550 // build a fontspec and use gdk_fontset_load
551 int remaining = sizeof(fontset);
552 char fontNameCopy[1024];
553 strncpy(fontNameCopy, fontName, sizeof(fontNameCopy) - 1);
554 char *fn = fontNameCopy;
555 char *fp = strchr(fn, ',');
556 for (;;) {
557 const char *spec = "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
558 if (fontset[0] != '\0') {
559 // if this is not the first font in the list,
560 // append a comma seperator
561 spec = ",%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
564 if (fp)
565 *fp = '\0'; // nullify the comma
566 GenerateFontSpecStrings(fn, characterSet,
567 foundary, sizeof(foundary),
568 faceName, sizeof(faceName),
569 charset, sizeof(charset));
571 g_snprintf(fontspec,
572 sizeof(fontspec) - 1,
573 spec,
574 foundary, faceName,
575 bold ? "-bold" : "-medium",
576 italic ? "-i" : "-r",
577 size * 10,
578 charset);
580 // if this is the first font in the list, and
581 // we are doing italic, add an oblique font
582 // to the list
583 if (italic && fontset[0] == '\0') {
584 strncat(fontset, fontspec, remaining - 1);
585 remaining -= strlen(fontset);
587 g_snprintf(fontspec,
588 sizeof(fontspec) - 1,
589 ",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s",
590 foundary, faceName,
591 bold ? "-bold" : "-medium",
592 size * 10,
593 charset);
596 strncat(fontset, fontspec, remaining - 1);
597 remaining -= strlen(fontset);
599 if (!fp)
600 break;
602 fn = fp + 1;
603 fp = strchr(fn, ',');
606 newid = gdk_fontset_load(fontset);
607 if (newid)
608 return new FontHandle(newid);
610 // if fontset load failed, fall through, we'll use
611 // the last font entry and continue to try and
612 // get something that matches
615 // single fontspec support
617 GenerateFontSpecStrings(fontName, characterSet,
618 foundary, sizeof(foundary),
619 faceName, sizeof(faceName),
620 charset, sizeof(charset));
622 g_snprintf(fontspec,
623 sizeof(fontspec) - 1,
624 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
625 foundary, faceName,
626 bold ? "-bold" : "-medium",
627 italic ? "-i" : "-r",
628 size * 10,
629 charset);
630 newid = LoadFontOrSet(fontspec, characterSet);
631 if (!newid) {
632 // some fonts have oblique, not italic
633 g_snprintf(fontspec,
634 sizeof(fontspec) - 1,
635 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
636 foundary, faceName,
637 bold ? "-bold" : "-medium",
638 italic ? "-o" : "-r",
639 size * 10,
640 charset);
641 newid = LoadFontOrSet(fontspec, characterSet);
643 if (!newid) {
644 g_snprintf(fontspec,
645 sizeof(fontspec) - 1,
646 "-*-*-*-*-*-*-*-%0d-*-*-*-*-%s",
647 size * 10,
648 charset);
649 newid = gdk_font_load(fontspec);
651 if (!newid) {
652 // Font not available so substitute a reasonable code font
653 // iso8859 appears to only allow western characters.
654 newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
655 characterSet);
657 return new FontHandle(newid);
660 Font::Font() : id(0) {}
662 Font::~Font() {}
664 void Font::Create(const char *faceName, int characterSet, int size,
665 bool bold, bool italic, bool) {
666 Release();
667 id = FontCached::FindOrCreate(faceName, characterSet, size, bold, italic);
670 void Font::Release() {
671 if (id)
672 FontCached::ReleaseId(id);
673 id = 0;
676 class SurfaceImpl : public Surface {
677 encodingType et;
678 GdkDrawable *drawable;
679 GdkGC *gc;
680 GdkPixmap *ppixmap;
681 int x;
682 int y;
683 bool inited;
684 bool createdGC;
685 #ifdef USE_PANGO
686 PangoContext *pcontext;
687 PangoLayout *layout;
688 Converter conv;
689 int characterSet;
690 void SetConverter(int characterSet_);
691 #endif
692 public:
693 SurfaceImpl();
694 virtual ~SurfaceImpl();
696 void Init(WindowID wid);
697 void Init(SurfaceID sid, WindowID wid);
698 void InitPixMap(int width, int height, Surface *surface_, WindowID wid);
700 void Release();
701 bool Initialised();
702 void PenColour(ColourAllocated fore);
703 int LogPixelsY();
704 int DeviceHeightFont(int points);
705 void MoveTo(int x_, int y_);
706 void LineTo(int x_, int y_);
707 void Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back);
708 void RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back);
709 void FillRectangle(PRectangle rc, ColourAllocated back);
710 void FillRectangle(PRectangle rc, Surface &surfacePattern);
711 void RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back);
712 void AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
713 ColourAllocated outline, int alphaOutline, int flags);
714 void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back);
715 void Copy(PRectangle rc, Point from, Surface &surfaceSource);
717 void DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
718 void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
719 void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
720 void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
721 void MeasureWidths(Font &font_, const char *s, int len, int *positions);
722 int WidthText(Font &font_, const char *s, int len);
723 int WidthChar(Font &font_, char ch);
724 int Ascent(Font &font_);
725 int Descent(Font &font_);
726 int InternalLeading(Font &font_);
727 int ExternalLeading(Font &font_);
728 int Height(Font &font_);
729 int AverageCharWidth(Font &font_);
731 int SetPalette(Palette *pal, bool inBackGround);
732 void SetClip(PRectangle rc);
733 void FlushCachedState();
735 void SetUnicodeMode(bool unicodeMode_);
736 void SetDBCSMode(int codePage);
739 const char *CharacterSetID(int characterSet) {
740 switch (characterSet) {
741 case SC_CHARSET_ANSI:
742 return "";
743 case SC_CHARSET_DEFAULT:
744 return "ISO-8859-1";
745 case SC_CHARSET_BALTIC:
746 return "ISO-8859-13";
747 case SC_CHARSET_CHINESEBIG5:
748 return "BIG-5";
749 case SC_CHARSET_EASTEUROPE:
750 return "ISO-8859-2";
751 case SC_CHARSET_GB2312:
752 return "GB2312";
753 case SC_CHARSET_GREEK:
754 return "ISO-8859-7";
755 case SC_CHARSET_HANGUL:
756 return "";
757 case SC_CHARSET_MAC:
758 return "MACINTOSH";
759 case SC_CHARSET_OEM:
760 return "ASCII";
761 case SC_CHARSET_RUSSIAN:
762 return "KOI8-R";
763 case SC_CHARSET_CYRILLIC:
764 return "CP1251";
765 case SC_CHARSET_SHIFTJIS:
766 return "SHIFT-JIS";
767 case SC_CHARSET_SYMBOL:
768 return "";
769 case SC_CHARSET_TURKISH:
770 return "ISO-8859-9";
771 case SC_CHARSET_JOHAB:
772 return "JOHAB";
773 case SC_CHARSET_HEBREW:
774 return "ISO-8859-8";
775 case SC_CHARSET_ARABIC:
776 return "ISO-8859-6";
777 case SC_CHARSET_VIETNAMESE:
778 return "";
779 case SC_CHARSET_THAI:
780 return "ISO-8859-11";
781 case SC_CHARSET_8859_15:
782 return "ISO-8859-15";
783 default:
784 return "";
788 #ifdef USE_PANGO
790 void SurfaceImpl::SetConverter(int characterSet_) {
791 if (characterSet != characterSet_) {
792 characterSet = characterSet_;
793 conv.Open("UTF-8", CharacterSetID(characterSet), false);
796 #endif
798 SurfaceImpl::SurfaceImpl() : et(singleByte), drawable(0), gc(0), ppixmap(0),
799 x(0), y(0), inited(false), createdGC(false)
800 #ifdef USE_PANGO
801 , pcontext(0), layout(0), characterSet(-1)
802 #endif
806 SurfaceImpl::~SurfaceImpl() {
807 Release();
810 void SurfaceImpl::Release() {
811 et = singleByte;
812 drawable = 0;
813 if (createdGC) {
814 createdGC = false;
815 gdk_gc_unref(gc);
817 gc = 0;
818 if (ppixmap)
819 gdk_pixmap_unref(ppixmap);
820 ppixmap = 0;
821 #ifdef USE_PANGO
822 if (layout)
823 g_object_unref(layout);
824 layout = 0;
825 if (pcontext)
826 g_object_unref(pcontext);
827 pcontext = 0;
828 conv.Close();
829 characterSet = -1;
830 #endif
831 x = 0;
832 y = 0;
833 inited = false;
834 createdGC = false;
837 bool SurfaceImpl::Initialised() {
838 return inited;
841 // The WindowID argument is only used for Pango builds
842 #ifdef USE_PANGO
843 #define WID_NAME wid
844 #else
845 #define WID_NAME
846 #endif
848 void SurfaceImpl::Init(WindowID WID_NAME) {
849 Release();
850 #ifdef USE_PANGO
851 PLATFORM_ASSERT(wid);
852 pcontext = gtk_widget_create_pango_context(PWidget(wid));
853 PLATFORM_ASSERT(pcontext);
854 layout = pango_layout_new(pcontext);
855 PLATFORM_ASSERT(layout);
856 #endif
857 inited = true;
860 void SurfaceImpl::Init(SurfaceID sid, WindowID WID_NAME) {
861 PLATFORM_ASSERT(sid);
862 GdkDrawable *drawable_ = reinterpret_cast<GdkDrawable *>(sid);
863 Release();
864 #ifdef USE_PANGO
865 PLATFORM_ASSERT(wid);
866 pcontext = gtk_widget_create_pango_context(PWidget(wid));
867 layout = pango_layout_new(pcontext);
868 #endif
869 drawable = drawable_;
870 gc = gdk_gc_new(drawable_);
871 // Ask for lines that do not paint the last pixel so is like Win32
872 gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
873 createdGC = true;
874 inited = true;
877 void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID WID_NAME) {
878 PLATFORM_ASSERT(surface_);
879 Release();
880 SurfaceImpl *surfImpl = static_cast<SurfaceImpl *>(surface_);
881 PLATFORM_ASSERT(surfImpl->drawable);
882 #ifdef USE_PANGO
883 PLATFORM_ASSERT(wid);
884 pcontext = gtk_widget_create_pango_context(PWidget(wid));
885 PLATFORM_ASSERT(pcontext);
886 layout = pango_layout_new(pcontext);
887 PLATFORM_ASSERT(layout);
888 #endif
889 if (height > 0 && width > 0)
890 ppixmap = gdk_pixmap_new(surfImpl->drawable, width, height, -1);
891 drawable = ppixmap;
892 gc = gdk_gc_new(surfImpl->drawable);
893 // Ask for lines that do not paint the last pixel so is like Win32
894 gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
895 createdGC = true;
896 inited = true;
899 void SurfaceImpl::PenColour(ColourAllocated fore) {
900 if (gc) {
901 GdkColor co;
902 co.pixel = fore.AsLong();
903 gdk_gc_set_foreground(gc, &co);
907 int SurfaceImpl::LogPixelsY() {
908 return 72;
911 int SurfaceImpl::DeviceHeightFont(int points) {
912 int logPix = LogPixelsY();
913 return (points * logPix + logPix / 2) / 72;
916 void SurfaceImpl::MoveTo(int x_, int y_) {
917 x = x_;
918 y = y_;
921 void SurfaceImpl::LineTo(int x_, int y_) {
922 if (drawable && gc) {
923 gdk_draw_line(drawable, gc,
924 x, y,
925 x_, y_);
927 x = x_;
928 y = y_;
931 void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore,
932 ColourAllocated back) {
933 GdkPoint gpts[20];
934 if (npts < static_cast<int>((sizeof(gpts) / sizeof(gpts[0])))) {
935 for (int i = 0;i < npts;i++) {
936 gpts[i].x = pts[i].x;
937 gpts[i].y = pts[i].y;
939 PenColour(back);
940 gdk_draw_polygon(drawable, gc, 1, gpts, npts);
941 PenColour(fore);
942 gdk_draw_polygon(drawable, gc, 0, gpts, npts);
946 void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
947 if (gc && drawable) {
948 PenColour(back);
949 gdk_draw_rectangle(drawable, gc, 1,
950 rc.left + 1, rc.top + 1,
951 rc.right - rc.left - 2, rc.bottom - rc.top - 2);
953 PenColour(fore);
954 // The subtraction of 1 off the width and height here shouldn't be needed but
955 // otherwise a different rectangle is drawn than would be done if the fill parameter == 1
956 gdk_draw_rectangle(drawable, gc, 0,
957 rc.left, rc.top,
958 rc.right - rc.left - 1, rc.bottom - rc.top - 1);
962 void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) {
963 PenColour(back);
964 if (drawable && (rc.left < maxCoordinate)) { // Protect against out of range
965 gdk_draw_rectangle(drawable, gc, 1,
966 rc.left, rc.top,
967 rc.right - rc.left, rc.bottom - rc.top);
971 void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) {
972 if (static_cast<SurfaceImpl &>(surfacePattern).drawable) {
973 // Tile pattern over rectangle
974 // Currently assumes 8x8 pattern
975 int widthPat = 8;
976 int heightPat = 8;
977 for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) {
978 int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat;
979 for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) {
980 int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat;
981 gdk_draw_pixmap(drawable,
983 static_cast<SurfaceImpl &>(surfacePattern).drawable,
984 0, 0,
985 xTile, yTile,
986 widthx, heighty);
989 } else {
990 // Something is wrong so try to show anyway
991 // Shows up black because colour not allocated
992 FillRectangle(rc, ColourAllocated(0));
996 void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
997 if (((rc.right - rc.left) > 4) && ((rc.bottom - rc.top) > 4)) {
998 // Approximate a round rect with some cut off corners
999 Point pts[] = {
1000 Point(rc.left + 2, rc.top),
1001 Point(rc.right - 2, rc.top),
1002 Point(rc.right, rc.top + 2),
1003 Point(rc.right, rc.bottom - 2),
1004 Point(rc.right - 2, rc.bottom),
1005 Point(rc.left + 2, rc.bottom),
1006 Point(rc.left, rc.bottom - 2),
1007 Point(rc.left, rc.top + 2),
1009 Polygon(pts, sizeof(pts) / sizeof(pts[0]), fore, back);
1010 } else {
1011 RectangleDraw(rc, fore, back);
1015 #if GTK_MAJOR_VERSION >= 2
1017 // Plot a point into a guint32 buffer symetrically to all 4 qudrants
1018 static void AllFour(guint32 *pixels, int stride, int width, int height, int x, int y, guint32 val) {
1019 pixels[y*stride+x] = val;
1020 pixels[y*stride+width-1-x] = val;
1021 pixels[(height-1-y)*stride+x] = val;
1022 pixels[(height-1-y)*stride+width-1-x] = val;
1025 static unsigned int GetRValue(unsigned int co) {
1026 return (co >> 16) & 0xff;
1029 static unsigned int GetGValue(unsigned int co) {
1030 return (co >> 8) & 0xff;
1033 static unsigned int GetBValue(unsigned int co) {
1034 return co & 0xff;
1037 #endif
1039 #if GTK_MAJOR_VERSION < 2
1040 void SurfaceImpl::AlphaRectangle(PRectangle rc, int , ColourAllocated , int , ColourAllocated outline, int , int ) {
1041 if (gc && drawable) {
1042 // Can't use GdkPixbuf on GTK+ 1.x, so draw an outline rather than use alpha.
1043 PenColour(outline);
1044 gdk_draw_rectangle(drawable, gc, 0,
1045 rc.left, rc.top,
1046 rc.right - rc.left - 1, rc.bottom - rc.top - 1);
1049 #else
1050 void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
1051 ColourAllocated outline, int alphaOutline, int flags) {
1052 if (gc && drawable && rc.Width() > 0) {
1053 int width = rc.Width();
1054 int height = rc.Height();
1055 // Ensure not distorted too much by corners when small
1056 cornerSize = Platform::Minimum(cornerSize, (Platform::Minimum(width, height) / 2) - 2);
1057 // Make a 32 bit deep pixbuf with alpha
1058 GdkPixbuf *pixalpha = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
1060 guint8 pixVal[4] = {0};
1061 guint32 valEmpty = *(reinterpret_cast<guint32 *>(pixVal));
1062 pixVal[0] = GetRValue(fill.AsLong());
1063 pixVal[1] = GetGValue(fill.AsLong());
1064 pixVal[2] = GetBValue(fill.AsLong());
1065 pixVal[3] = alphaFill;
1066 guint32 valFill = *(reinterpret_cast<guint32 *>(pixVal));
1067 pixVal[0] = GetRValue(outline.AsLong());
1068 pixVal[1] = GetGValue(outline.AsLong());
1069 pixVal[2] = GetBValue(outline.AsLong());
1070 pixVal[3] = alphaOutline;
1071 guint32 valOutline = *(reinterpret_cast<guint32 *>(pixVal));
1072 guint32 *pixels = reinterpret_cast<guint32 *>(gdk_pixbuf_get_pixels(pixalpha));
1073 int stride = gdk_pixbuf_get_rowstride(pixalpha) / 4;
1074 for (int yr=0; yr<height; yr++) {
1075 for (int xr=0; xr<width; xr++) {
1076 if ((xr==0) || (xr==width-1) || (yr == 0) || (yr == height-1)) {
1077 pixels[yr*stride+xr] = valOutline;
1078 } else {
1079 pixels[yr*stride+xr] = valFill;
1083 for (int c=0;c<cornerSize; c++) {
1084 for (int xr=0;xr<c+1; xr++) {
1085 AllFour(pixels, stride, width, height, xr, c-xr, valEmpty);
1088 for (int xr=1;xr<cornerSize; xr++) {
1089 AllFour(pixels, stride, width, height, xr, cornerSize-xr, valOutline);
1092 // Draw with alpha
1093 gdk_draw_pixbuf(drawable, gc, pixalpha,
1094 0,0, rc.left,rc.top, width,height, GDK_RGB_DITHER_NORMAL, 0, 0);
1096 g_object_unref(pixalpha);
1099 #endif
1101 void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
1102 PenColour(back);
1103 gdk_draw_arc(drawable, gc, 1,
1104 rc.left + 1, rc.top + 1,
1105 rc.right - rc.left - 2, rc.bottom - rc.top - 2,
1106 0, 32767);
1108 // The subtraction of 1 here is similar to the case for RectangleDraw
1109 PenColour(fore);
1110 gdk_draw_arc(drawable, gc, 0,
1111 rc.left, rc.top,
1112 rc.right - rc.left - 1, rc.bottom - rc.top - 1,
1113 0, 32767);
1116 void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
1117 if (static_cast<SurfaceImpl &>(surfaceSource).drawable) {
1118 gdk_draw_pixmap(drawable,
1120 static_cast<SurfaceImpl &>(surfaceSource).drawable,
1121 from.x, from.y,
1122 rc.left, rc.top,
1123 rc.right - rc.left, rc.bottom - rc.top);
1127 static size_t UTF8Len(char ch) {
1128 unsigned char uch = static_cast<unsigned char>(ch);
1129 if (uch < 0x80)
1130 return 1;
1131 else if (uch < (0x80 + 0x40 + 0x20))
1132 return 2;
1133 else
1134 return 3;
1137 char *UTF8FromLatin1(const char *s, int &len) {
1138 char *utfForm = new char[len*2+1];
1139 size_t lenU = 0;
1140 for (int i=0;i<len;i++) {
1141 unsigned int uch = static_cast<unsigned char>(s[i]);
1142 if (uch < 0x80) {
1143 utfForm[lenU++] = uch;
1144 } else {
1145 utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
1146 utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1149 utfForm[lenU] = '\0';
1150 len = lenU;
1151 return utfForm;
1154 #ifdef USE_PANGO
1155 static char *UTF8FromIconv(const Converter &conv, const char *s, int &len) {
1156 if (conv) {
1157 char *utfForm = new char[len*3+1];
1158 char *pin = const_cast<char *>(s);
1159 size_t inLeft = len;
1160 char *pout = utfForm;
1161 size_t outLeft = len*3+1;
1162 size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1163 if (conversions != ((size_t)(-1))) {
1164 *pout = '\0';
1165 len = pout - utfForm;
1166 return utfForm;
1168 delete []utfForm;
1170 return 0;
1173 // Work out how many bytes are in a character by trying to convert using iconv,
1174 // returning the first length that succeeds.
1175 static size_t MultiByteLenFromIconv(const Converter &conv, const char *s, size_t len) {
1176 for (size_t lenMB=1; (lenMB<4) && (lenMB <= len); lenMB++) {
1177 char wcForm[2];
1178 char *pin = const_cast<char *>(s);
1179 size_t inLeft = lenMB;
1180 char *pout = wcForm;
1181 size_t outLeft = 2;
1182 size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1183 if (conversions != ((size_t)(-1))) {
1184 return lenMB;
1187 return 1;
1190 static char *UTF8FromGdkWChar(GdkWChar *wctext, int wclen) {
1191 char *utfForm = new char[wclen*3+1]; // Maximum of 3 UTF-8 bytes per character
1192 size_t lenU = 0;
1193 for (int i = 0; i < wclen && wctext[i]; i++) {
1194 unsigned int uch = wctext[i];
1195 if (uch < 0x80) {
1196 utfForm[lenU++] = static_cast<char>(uch);
1197 } else if (uch < 0x800) {
1198 utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
1199 utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1200 } else {
1201 utfForm[lenU++] = static_cast<char>(0xE0 | (uch >> 12));
1202 utfForm[lenU++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
1203 utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1206 utfForm[lenU] = '\0';
1207 return utfForm;
1210 static char *UTF8FromDBCS(const char *s, int &len) {
1211 GdkWChar *wctext = new GdkWChar[len + 1];
1212 GdkWChar *wcp = wctext;
1213 int wclen = gdk_mbstowcs(wcp, s, len);
1214 if (wclen < 1) {
1215 // In the annoying case when non-locale chars in the line.
1216 // e.g. latin1 chars in Japanese locale.
1217 delete []wctext;
1218 return 0;
1221 char *utfForm = UTF8FromGdkWChar(wctext, wclen);
1222 delete []wctext;
1223 len = strlen(utfForm);
1224 return utfForm;
1227 static size_t UTF8CharLength(const char *s) {
1228 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
1229 unsigned char ch = *us;
1230 if (ch < 0x80) {
1231 return 1;
1232 } else if (ch < 0x80 + 0x40 + 0x20) {
1233 return 2;
1234 } else {
1235 return 3;
1239 #endif
1241 // On GTK+, wchar_t is 4 bytes
1243 const int maxLengthTextRun = 10000;
1245 void SurfaceImpl::DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1246 ColourAllocated fore) {
1247 PenColour(fore);
1248 if (gc && drawable) {
1249 int xText = rc.left;
1250 #ifdef USE_PANGO
1251 if (PFont(font_)->pfd) {
1252 char *utfForm = 0;
1253 bool useGFree = false;
1254 if (et == UTF8) {
1255 pango_layout_set_text(layout, s, len);
1256 } else {
1257 if (!utfForm) {
1258 SetConverter(PFont(font_)->characterSet);
1259 utfForm = UTF8FromIconv(conv, s, len);
1261 if (!utfForm) { // iconv failed so try DBCS if DBCS mode
1262 if (et == dbcs) {
1263 // Convert to utf8
1264 utfForm = UTF8FromDBCS(s, len);
1267 if (!utfForm) { // iconv and DBCS failed so treat as Latin1
1268 utfForm = UTF8FromLatin1(s, len);
1270 pango_layout_set_text(layout, utfForm, len);
1272 pango_layout_set_font_description(layout, PFont(font_)->pfd);
1273 PangoLayoutLine *pll = pango_layout_get_line(layout,0);
1274 gdk_draw_layout_line(drawable, gc, xText, ybase, pll);
1275 if (useGFree) {
1276 g_free(utfForm);
1277 } else {
1278 delete []utfForm;
1280 return;
1282 #endif
1283 // Draw text as a series of segments to avoid limitations in X servers
1284 const int segmentLength = 1000;
1285 bool draw8bit = true;
1286 if (et != singleByte) {
1287 GdkWChar wctext[maxLengthTextRun];
1288 if (len >= maxLengthTextRun)
1289 len = maxLengthTextRun-1;
1290 int wclen;
1291 if (et == UTF8) {
1292 wclen = UTF16FromUTF8(s, len,
1293 static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
1294 } else { // dbcs, so convert using current locale
1295 char sMeasure[maxLengthTextRun];
1296 memcpy(sMeasure, s, len);
1297 sMeasure[len] = '\0';
1298 wclen = gdk_mbstowcs(
1299 wctext, sMeasure, maxLengthTextRun - 1);
1301 if (wclen > 0) {
1302 draw8bit = false;
1303 wctext[wclen] = L'\0';
1304 GdkWChar *wcp = wctext;
1305 while ((wclen > 0) && (xText < maxCoordinate)) {
1306 int lenDraw = Platform::Minimum(wclen, segmentLength);
1307 gdk_draw_text_wc(drawable, PFont(font_)->pfont, gc,
1308 xText, ybase, wcp, lenDraw);
1309 wclen -= lenDraw;
1310 if (wclen > 0) { // Avoid next calculation if possible as may be expensive
1311 xText += gdk_text_width_wc(PFont(font_)->pfont,
1312 wcp, lenDraw);
1314 wcp += lenDraw;
1318 if (draw8bit) {
1319 while ((len > 0) && (xText < maxCoordinate)) {
1320 int lenDraw = Platform::Minimum(len, segmentLength);
1321 gdk_draw_text(drawable, PFont(font_)->pfont, gc,
1322 xText, ybase, s, lenDraw);
1323 len -= lenDraw;
1324 if (len > 0) { // Avoid next calculation if possible as may be expensive
1325 xText += gdk_text_width(PFont(font_)->pfont, s, lenDraw);
1327 s += lenDraw;
1333 void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1334 ColourAllocated fore, ColourAllocated back) {
1335 FillRectangle(rc, back);
1336 DrawTextBase(rc, font_, ybase, s, len, fore);
1339 // On GTK+, exactly same as DrawTextNoClip
1340 void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1341 ColourAllocated fore, ColourAllocated back) {
1342 FillRectangle(rc, back);
1343 DrawTextBase(rc, font_, ybase, s, len, fore);
1346 void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1347 ColourAllocated fore) {
1348 // Avoid drawing spaces in transparent mode
1349 for (int i=0;i<len;i++) {
1350 if (s[i] != ' ') {
1351 DrawTextBase(rc, font_, ybase, s, len, fore);
1352 return;
1357 void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) {
1358 if (font_.GetID()) {
1359 int totalWidth = 0;
1360 #ifdef USE_PANGO
1361 const int lenPositions = len;
1362 if (PFont(font_)->pfd) {
1363 if (len == 1) {
1364 int width = PFont(font_)->CharWidth(*s, et);
1365 if (width) {
1366 positions[0] = width;
1367 return;
1370 PangoRectangle pos;
1371 pango_layout_set_font_description(layout, PFont(font_)->pfd);
1372 if (et == UTF8) {
1373 // Simple and direct as UTF-8 is native Pango encoding
1374 pango_layout_set_text(layout, s, len);
1375 PangoLayoutIter *iter = pango_layout_get_iter(layout);
1376 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1377 int i = 0;
1378 while (pango_layout_iter_next_cluster(iter)) {
1379 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1380 int position = PANGO_PIXELS(pos.x);
1381 int curIndex = pango_layout_iter_get_index(iter);
1382 int places = curIndex - i;
1383 int distance = position - positions[i-1];
1384 while (i < curIndex) {
1385 // Evenly distribute space among bytes of this cluster.
1386 // Would be better to find number of characters and then
1387 // divide evenly between characters with each byte of a character
1388 // being at the same position.
1389 positions[i] = position - (curIndex - 1 - i) * distance / places;
1390 i++;
1393 while (i < lenPositions)
1394 positions[i++] = PANGO_PIXELS(pos.x + pos.width);
1395 pango_layout_iter_free(iter);
1396 PLATFORM_ASSERT(i == lenPositions);
1397 } else {
1398 int positionsCalculated = 0;
1399 if (et == dbcs) {
1400 SetConverter(PFont(font_)->characterSet);
1401 char *utfForm = UTF8FromIconv(conv, s, len);
1402 if (utfForm) {
1403 // Convert to UTF-8 so can ask Pango for widths, then
1404 // Loop through UTF-8 and DBCS forms, taking account of different
1405 // character byte lengths.
1406 Converter convMeasure("UCS-2", CharacterSetID(characterSet), false);
1407 pango_layout_set_text(layout, utfForm, strlen(utfForm));
1408 int i = 0;
1409 int utfIndex = 0;
1410 PangoLayoutIter *iter = pango_layout_get_iter(layout);
1411 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1412 while (pango_layout_iter_next_cluster(iter)) {
1413 pango_layout_iter_get_cluster_extents (iter, NULL, &pos);
1414 int position = PANGO_PIXELS(pos.x);
1415 int utfIndexNext = pango_layout_iter_get_index(iter);
1416 while (utfIndex < utfIndexNext) {
1417 size_t lenChar = MultiByteLenFromIconv(convMeasure, s+i, len-i);
1418 //size_t lenChar = mblen(s+i, MB_CUR_MAX);
1419 while (lenChar--) {
1420 positions[i++] = position;
1421 positionsCalculated++;
1423 utfIndex += UTF8CharLength(utfForm+utfIndex);
1426 while (i < lenPositions)
1427 positions[i++] = PANGO_PIXELS(pos.x + pos.width);
1428 pango_layout_iter_free(iter);
1429 delete []utfForm;
1430 PLATFORM_ASSERT(i == lenPositions);
1433 if (positionsCalculated < 1 ) {
1434 // Either Latin1 or DBCS conversion failed so treat as Latin1.
1435 bool useGFree = false;
1436 SetConverter(PFont(font_)->characterSet);
1437 char *utfForm = UTF8FromIconv(conv, s, len);
1438 if (!utfForm) {
1439 utfForm = UTF8FromLatin1(s, len);
1441 pango_layout_set_text(layout, utfForm, len);
1442 PangoLayoutIter *iter = pango_layout_get_iter(layout);
1443 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1444 int i = 0;
1445 int positionStart = 0;
1446 int clusterStart = 0;
1447 // Each Latin1 input character may take 1 or 2 bytes in UTF-8
1448 // and groups of up to 3 may be represented as ligatures.
1449 while (pango_layout_iter_next_cluster(iter)) {
1450 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1451 int position = PANGO_PIXELS(pos.x);
1452 int distance = position - positionStart;
1453 int clusterEnd = pango_layout_iter_get_index(iter);
1454 int ligatureLength = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
1455 PLATFORM_ASSERT(ligatureLength > 0 && ligatureLength <= 3);
1456 for (int charInLig=0; charInLig<ligatureLength; charInLig++) {
1457 positions[i++] = position - (ligatureLength - 1 - charInLig) * distance / ligatureLength;
1459 positionStart = position;
1460 clusterStart = clusterEnd;
1462 while (i < lenPositions)
1463 positions[i++] = PANGO_PIXELS(pos.x + pos.width);
1464 pango_layout_iter_free(iter);
1465 if (useGFree) {
1466 g_free(utfForm);
1467 } else {
1468 delete []utfForm;
1470 PLATFORM_ASSERT(i == lenPositions);
1473 if (len == 1) {
1474 PFont(font_)->SetCharWidth(*s, positions[0], et);
1476 return;
1478 #endif
1479 GdkFont *gf = PFont(font_)->pfont;
1480 bool measure8bit = true;
1481 if (et != singleByte) {
1482 GdkWChar wctext[maxLengthTextRun];
1483 if (len >= maxLengthTextRun)
1484 len = maxLengthTextRun-1;
1485 int wclen;
1486 if (et == UTF8) {
1487 wclen = UTF16FromUTF8(s, len,
1488 static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
1489 } else { // dbcsMode, so convert using current locale
1490 char sDraw[maxLengthTextRun];
1491 memcpy(sDraw, s, len);
1492 sDraw[len] = '\0';
1493 wclen = gdk_mbstowcs(
1494 wctext, sDraw, maxLengthTextRun - 1);
1496 if (wclen > 0) {
1497 measure8bit = false;
1498 wctext[wclen] = L'\0';
1499 // Map widths back to utf-8 or DBCS input string
1500 int i = 0;
1501 for (int iU = 0; iU < wclen; iU++) {
1502 int width = gdk_char_width_wc(gf, wctext[iU]);
1503 totalWidth += width;
1504 int lenChar;
1505 if (et == UTF8) {
1506 lenChar = UTF8Len(s[i]);
1507 } else {
1508 lenChar = mblen(s+i, MB_CUR_MAX);
1509 if (lenChar < 0)
1510 lenChar = 1;
1512 while (lenChar--) {
1513 positions[i++] = totalWidth;
1516 while (i < len) { // In case of problems with lengths
1517 positions[i++] = totalWidth;
1521 if (measure8bit) {
1522 // Either Latin1 or conversion failed so treat as Latin1.
1523 for (int i = 0; i < len; i++) {
1524 int width = gdk_char_width(gf, s[i]);
1525 totalWidth += width;
1526 positions[i] = totalWidth;
1529 } else {
1530 // No font so return an ascending range of values
1531 for (int i = 0; i < len; i++) {
1532 positions[i] = i + 1;
1537 int SurfaceImpl::WidthText(Font &font_, const char *s, int len) {
1538 if (font_.GetID()) {
1539 #ifdef USE_PANGO
1540 if (PFont(font_)->pfd) {
1541 char *utfForm = 0;
1542 pango_layout_set_font_description(layout, PFont(font_)->pfd);
1543 PangoRectangle pos;
1544 bool useGFree = false;
1545 if (et == UTF8) {
1546 pango_layout_set_text(layout, s, len);
1547 } else {
1548 if (et == dbcs) {
1549 // Convert to utf8
1550 utfForm = UTF8FromDBCS(s, len);
1552 if (!utfForm) { // DBCS failed so treat as iconv
1553 SetConverter(PFont(font_)->characterSet);
1554 utfForm = UTF8FromIconv(conv, s, len);
1556 if (!utfForm) { // g_locale_to_utf8 failed so treat as Latin1
1557 utfForm = UTF8FromLatin1(s, len);
1559 pango_layout_set_text(layout, utfForm, len);
1561 PangoLayoutLine *pangoLine = pango_layout_get_line(layout, 0);
1562 pango_layout_line_get_extents(pangoLine, NULL, &pos);
1563 if (useGFree) {
1564 g_free(utfForm);
1565 } else {
1566 delete []utfForm;
1568 return PANGO_PIXELS(pos.width);
1570 #endif
1571 if (et == UTF8) {
1572 GdkWChar wctext[maxLengthTextRun];
1573 size_t wclen = UTF16FromUTF8(s, len, static_cast<wchar_t *>(static_cast<void *>(wctext)),
1574 sizeof(wctext) / sizeof(GdkWChar) - 1);
1575 wctext[wclen] = L'\0';
1576 return gdk_text_width_wc(PFont(font_)->pfont, wctext, wclen);
1577 } else {
1578 return gdk_text_width(PFont(font_)->pfont, s, len);
1580 } else {
1581 return 1;
1585 int SurfaceImpl::WidthChar(Font &font_, char ch) {
1586 if (font_.GetID()) {
1587 #ifdef USE_PANGO
1588 if (PFont(font_)->pfd) {
1589 return WidthText(font_, &ch, 1);
1591 #endif
1592 return gdk_char_width(PFont(font_)->pfont, ch);
1593 } else {
1594 return 1;
1598 // Three possible strategies for determining ascent and descent of font:
1599 // 1) Call gdk_string_extents with string containing all letters, numbers and punctuation.
1600 // 2) Use the ascent and descent fields of GdkFont.
1601 // 3) Call gdk_string_extents with string as 1 but also including accented capitals.
1602 // Smallest values given by 1 and largest by 3 with 2 in between.
1603 // Techniques 1 and 2 sometimes chop off extreme portions of ascenders and
1604 // descenders but are mostly OK except for accented characters like Ã… which are
1605 // rarely used in code.
1607 // This string contains a good range of characters to test for size.
1608 //const char largeSizeString[] = "ÂÃÅÄ `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1609 // "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1610 #ifndef FAST_WAY
1611 const char sizeString[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1612 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1613 #endif
1615 int SurfaceImpl::Ascent(Font &font_) {
1616 if (!(font_.GetID()))
1617 return 1;
1618 #ifdef FAST_WAY
1619 FontMutexLock();
1620 int ascent = PFont(font_)->ascent;
1621 #ifdef USE_PANGO
1622 if ((ascent == 0) && (PFont(font_)->pfd)) {
1623 PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
1624 PFont(font_)->pfd, pango_context_get_language(pcontext));
1625 PFont(font_)->ascent =
1626 PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
1627 pango_font_metrics_unref(metrics);
1628 ascent = PFont(font_)->ascent;
1630 #endif
1631 if ((ascent == 0) && (PFont(font_)->pfont)) {
1632 ascent = PFont(font_)->pfont->ascent;
1634 if (ascent == 0) {
1635 ascent = 1;
1637 FontMutexUnlock();
1638 return ascent;
1639 #else
1641 gint lbearing;
1642 gint rbearing;
1643 gint width;
1644 gint ascent;
1645 gint descent;
1647 gdk_string_extents(PFont(font_)->pfont, sizeString,
1648 &lbearing, &rbearing, &width, &ascent, &descent);
1649 return ascent;
1650 #endif
1653 int SurfaceImpl::Descent(Font &font_) {
1654 if (!(font_.GetID()))
1655 return 1;
1656 #ifdef FAST_WAY
1658 #ifdef USE_PANGO
1659 if (PFont(font_)->pfd) {
1660 PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
1661 PFont(font_)->pfd, pango_context_get_language(pcontext));
1662 int descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
1663 pango_font_metrics_unref(metrics);
1664 return descent;
1666 #endif
1667 return PFont(font_)->pfont->descent;
1668 #else
1670 gint lbearing;
1671 gint rbearing;
1672 gint width;
1673 gint ascent;
1674 gint descent;
1676 gdk_string_extents(PFont(font_)->pfont, sizeString,
1677 &lbearing, &rbearing, &width, &ascent, &descent);
1678 return descent;
1679 #endif
1682 int SurfaceImpl::InternalLeading(Font &) {
1683 return 0;
1686 int SurfaceImpl::ExternalLeading(Font &) {
1687 return 0;
1690 int SurfaceImpl::Height(Font &font_) {
1691 return Ascent(font_) + Descent(font_);
1694 int SurfaceImpl::AverageCharWidth(Font &font_) {
1695 return WidthChar(font_, 'n');
1698 int SurfaceImpl::SetPalette(Palette *, bool) {
1699 // Handled in palette allocation for GTK so this does nothing
1700 return 0;
1703 void SurfaceImpl::SetClip(PRectangle rc) {
1704 GdkRectangle area = {rc.left, rc.top,
1705 rc.right - rc.left, rc.bottom - rc.top};
1706 gdk_gc_set_clip_rectangle(gc, &area);
1709 void SurfaceImpl::FlushCachedState() {}
1711 void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) {
1712 if (unicodeMode_)
1713 et = UTF8;
1716 void SurfaceImpl::SetDBCSMode(int codePage) {
1717 if (codePage && (codePage != SC_CP_UTF8))
1718 et = dbcs;
1721 Surface *Surface::Allocate() {
1722 return new SurfaceImpl;
1725 Window::~Window() {}
1727 void Window::Destroy() {
1728 if (id)
1729 gtk_widget_destroy(GTK_WIDGET(id));
1730 id = 0;
1733 bool Window::HasFocus() {
1734 return GTK_WIDGET_HAS_FOCUS(id);
1737 PRectangle Window::GetPosition() {
1738 // Before any size allocated pretend its 1000 wide so not scrolled
1739 PRectangle rc(0, 0, 1000, 1000);
1740 if (id) {
1741 rc.left = PWidget(id)->allocation.x;
1742 rc.top = PWidget(id)->allocation.y;
1743 if (PWidget(id)->allocation.width > 20) {
1744 rc.right = rc.left + PWidget(id)->allocation.width;
1745 rc.bottom = rc.top + PWidget(id)->allocation.height;
1748 return rc;
1751 void Window::SetPosition(PRectangle rc) {
1752 #if 1
1753 //gtk_widget_set_uposition(id, rc.left, rc.top);
1754 GtkAllocation alloc;
1755 alloc.x = rc.left;
1756 alloc.y = rc.top;
1757 alloc.width = rc.Width();
1758 alloc.height = rc.Height();
1759 gtk_widget_size_allocate(PWidget(id), &alloc);
1760 #else
1762 gtk_widget_set_uposition(id, rc.left, rc.top);
1763 gtk_widget_set_usize(id, rc.right - rc.left, rc.bottom - rc.top);
1764 #endif
1767 void Window::SetPositionRelative(PRectangle rc, Window relativeTo) {
1768 int ox = 0;
1769 int oy = 0;
1770 gdk_window_get_origin(PWidget(relativeTo.id)->window, &ox, &oy);
1771 ox += rc.left;
1772 if (ox < 0)
1773 ox = 0;
1774 oy += rc.top;
1775 if (oy < 0)
1776 oy = 0;
1778 /* do some corrections to fit into screen */
1779 int sizex = rc.right - rc.left;
1780 int sizey = rc.bottom - rc.top;
1781 int screenWidth = gdk_screen_width();
1782 int screenHeight = gdk_screen_height();
1783 if (sizex > screenWidth)
1784 ox = 0; /* the best we can do */
1785 else if (ox + sizex > screenWidth)
1786 ox = screenWidth - sizex;
1787 if (oy + sizey > screenHeight)
1788 oy = screenHeight - sizey;
1790 gtk_widget_set_uposition(PWidget(id), ox, oy);
1791 #if 0
1793 GtkAllocation alloc;
1794 alloc.x = rc.left + ox;
1795 alloc.y = rc.top + oy;
1796 alloc.width = rc.right - rc.left;
1797 alloc.height = rc.bottom - rc.top;
1798 gtk_widget_size_allocate(id, &alloc);
1799 #endif
1800 gtk_widget_set_usize(PWidget(id), sizex, sizey);
1803 PRectangle Window::GetClientPosition() {
1804 // On GTK+, the client position is the window position
1805 return GetPosition();
1808 void Window::Show(bool show) {
1809 if (show)
1810 gtk_widget_show(PWidget(id));
1813 void Window::InvalidateAll() {
1814 if (id) {
1815 gtk_widget_queue_draw(PWidget(id));
1819 void Window::InvalidateRectangle(PRectangle rc) {
1820 if (id) {
1821 gtk_widget_queue_draw_area(PWidget(id),
1822 rc.left, rc.top,
1823 rc.right - rc.left, rc.bottom - rc.top);
1827 void Window::SetFont(Font &) {
1828 // Can not be done generically but only needed for ListBox
1831 void Window::SetCursor(Cursor curs) {
1832 // We don't set the cursor to same value numerous times under gtk because
1833 // it stores the cursor in the window once it's set
1834 if (curs == cursorLast)
1835 return;
1837 cursorLast = curs;
1838 GdkCursor *gdkCurs;
1839 switch (curs) {
1840 case cursorText:
1841 gdkCurs = gdk_cursor_new(GDK_XTERM);
1842 break;
1843 case cursorArrow:
1844 gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
1845 break;
1846 case cursorUp:
1847 gdkCurs = gdk_cursor_new(GDK_CENTER_PTR);
1848 break;
1849 case cursorWait:
1850 gdkCurs = gdk_cursor_new(GDK_WATCH);
1851 break;
1852 case cursorHand:
1853 gdkCurs = gdk_cursor_new(GDK_HAND2);
1854 break;
1855 case cursorReverseArrow:
1856 gdkCurs = gdk_cursor_new(GDK_RIGHT_PTR);
1857 break;
1858 default:
1859 gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
1860 cursorLast = cursorArrow;
1861 break;
1864 if (PWidget(id)->window)
1865 gdk_window_set_cursor(PWidget(id)->window, gdkCurs);
1866 gdk_cursor_destroy(gdkCurs);
1869 void Window::SetTitle(const char *s) {
1870 gtk_window_set_title(GTK_WINDOW(id), s);
1873 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
1874 gdk window coordinates */
1875 PRectangle Window::GetMonitorRect(Point pt) {
1876 gint x_offset, y_offset;
1877 pt = pt;
1879 gdk_window_get_origin(PWidget(id)->window, &x_offset, &y_offset);
1881 // gtk 2.2+
1882 #if GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2)
1884 GdkScreen* screen;
1885 gint monitor_num;
1886 GdkRectangle rect;
1888 screen = gtk_widget_get_screen(PWidget(id));
1889 monitor_num = gdk_screen_get_monitor_at_point(screen, pt.x + x_offset, pt.y + y_offset);
1890 gdk_screen_get_monitor_geometry(screen, monitor_num, &rect);
1891 rect.x -= x_offset;
1892 rect.y -= y_offset;
1893 return PRectangle(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
1895 #else
1896 return PRectangle(-x_offset, -y_offset, (-x_offset) + gdk_screen_width(),
1897 (-y_offset) + gdk_screen_height());
1898 #endif
1901 struct ListImage {
1902 const char *xpm_data;
1903 #if GTK_MAJOR_VERSION < 2
1904 GdkPixmap *pixmap;
1905 GdkBitmap *bitmap;
1906 #else
1907 GdkPixbuf *pixbuf;
1908 #endif
1911 static void list_image_free(gpointer, gpointer value, gpointer) {
1912 ListImage *list_image = (ListImage *) value;
1913 #if GTK_MAJOR_VERSION < 2
1914 if (list_image->pixmap)
1915 gdk_pixmap_unref(list_image->pixmap);
1916 if (list_image->bitmap)
1917 gdk_bitmap_unref(list_image->bitmap);
1918 #else
1919 if (list_image->pixbuf)
1920 gdk_pixbuf_unref (list_image->pixbuf);
1921 #endif
1922 g_free(list_image);
1925 ListBox::ListBox() {
1928 ListBox::~ListBox() {
1931 #if GTK_MAJOR_VERSION >= 2
1932 enum {
1933 PIXBUF_COLUMN,
1934 TEXT_COLUMN,
1935 N_COLUMNS
1937 #endif
1939 class ListBoxX : public ListBox {
1940 WindowID list;
1941 WindowID scroller;
1942 #if GTK_MAJOR_VERSION < 2
1943 int current;
1944 #endif
1945 void *pixhash;
1946 int lineHeight;
1947 XPMSet xset;
1948 bool unicodeMode;
1949 int desiredVisibleRows;
1950 unsigned int maxItemCharacters;
1951 unsigned int aveCharWidth;
1952 public:
1953 CallBackAction doubleClickAction;
1954 void *doubleClickActionData;
1956 ListBoxX() : list(0), pixhash(NULL), desiredVisibleRows(5), maxItemCharacters(0),
1957 doubleClickAction(NULL), doubleClickActionData(NULL) {
1958 #if GTK_MAJOR_VERSION < 2
1959 current = 0;
1960 #endif
1962 virtual ~ListBoxX() {
1963 if (pixhash) {
1964 g_hash_table_foreach((GHashTable *) pixhash, list_image_free, NULL);
1965 g_hash_table_destroy((GHashTable *) pixhash);
1968 virtual void SetFont(Font &font);
1969 virtual void Create(Window &parent, int ctrlID, Point location_, int lineHeight_, bool unicodeMode_);
1970 virtual void SetAverageCharWidth(int width);
1971 virtual void SetVisibleRows(int rows);
1972 virtual int GetVisibleRows() const;
1973 virtual PRectangle GetDesiredRect();
1974 virtual int CaretFromEdge();
1975 virtual void Clear();
1976 virtual void Append(char *s, int type = -1);
1977 virtual int Length();
1978 virtual void Select(int n);
1979 virtual int GetSelection();
1980 virtual int Find(const char *prefix);
1981 virtual void GetValue(int n, char *value, int len);
1982 virtual void RegisterImage(int type, const char *xpm_data);
1983 virtual void ClearRegisteredImages();
1984 virtual void SetDoubleClickAction(CallBackAction action, void *data) {
1985 doubleClickAction = action;
1986 doubleClickActionData = data;
1988 virtual void SetList(const char *listText, char separator, char typesep);
1991 ListBox *ListBox::Allocate() {
1992 ListBoxX *lb = new ListBoxX();
1993 return lb;
1996 #if GTK_MAJOR_VERSION < 2
1997 static void UnselectionAC(GtkWidget *, gint, gint,
1998 GdkEventButton *, gpointer p) {
1999 int *pi = reinterpret_cast<int *>(p);
2000 *pi = -1;
2002 static void SelectionAC(GtkWidget *, gint row, gint,
2003 GdkEventButton *, gpointer p) {
2004 int *pi = reinterpret_cast<int *>(p);
2005 *pi = row;
2007 #endif
2009 static gboolean ButtonPress(GtkWidget *, GdkEventButton* ev, gpointer p) {
2010 ListBoxX* lb = reinterpret_cast<ListBoxX*>(p);
2011 if (ev->type == GDK_2BUTTON_PRESS && lb->doubleClickAction != NULL) {
2012 lb->doubleClickAction(lb->doubleClickActionData);
2013 return TRUE;
2016 return FALSE;
2019 #if GTK_MAJOR_VERSION >= 2
2020 /* Change the active color to the selected color so the listbox uses the color
2021 scheme that it would use if it had the focus. */
2022 static void StyleSet(GtkWidget *w, GtkStyle*, void*) {
2023 GtkStyle* style;
2025 g_return_if_fail(w != NULL);
2027 /* Copy the selected color to active. Note that the modify calls will cause
2028 recursive calls to this function after the value is updated and w->style to
2029 be set to a new object */
2030 style = gtk_widget_get_style(w);
2031 if (style == NULL)
2032 return;
2033 if (!gdk_color_equal(&style->base[GTK_STATE_SELECTED], &style->base[GTK_STATE_ACTIVE]))
2034 gtk_widget_modify_base(w, GTK_STATE_ACTIVE, &style->base[GTK_STATE_SELECTED]);
2036 style = gtk_widget_get_style(w);
2037 if (style == NULL)
2038 return;
2039 if (!gdk_color_equal(&style->text[GTK_STATE_SELECTED], &style->text[GTK_STATE_ACTIVE]))
2040 gtk_widget_modify_text(w, GTK_STATE_ACTIVE, &style->text[GTK_STATE_SELECTED]);
2042 #endif
2044 void ListBoxX::Create(Window &, int, Point, int, bool) {
2045 id = gtk_window_new(GTK_WINDOW_POPUP);
2047 GtkWidget *frame = gtk_frame_new(NULL);
2048 gtk_widget_show(frame);
2049 gtk_container_add(GTK_CONTAINER(GetID()), frame);
2050 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
2051 gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
2053 scroller = gtk_scrolled_window_new(NULL, NULL);
2054 gtk_container_set_border_width(GTK_CONTAINER(scroller), 0);
2055 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
2056 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2057 gtk_container_add(GTK_CONTAINER(frame), PWidget(scroller));
2058 gtk_widget_show(PWidget(scroller));
2060 #if GTK_MAJOR_VERSION < 2
2061 list = gtk_clist_new(1);
2062 GtkWidget *wid = PWidget(list); // No code inside the GTK_OBJECT macro
2063 gtk_widget_show(wid);
2064 gtk_container_add(GTK_CONTAINER(PWidget(scroller)), wid);
2065 gtk_clist_set_column_auto_resize(GTK_CLIST(wid), 0, TRUE);
2066 gtk_clist_set_selection_mode(GTK_CLIST(wid), GTK_SELECTION_BROWSE);
2067 gtk_signal_connect(GTK_OBJECT(wid), "unselect_row",
2068 GTK_SIGNAL_FUNC(UnselectionAC), &current);
2069 gtk_signal_connect(GTK_OBJECT(wid), "select_row",
2070 GTK_SIGNAL_FUNC(SelectionAC), &current);
2071 gtk_signal_connect(GTK_OBJECT(wid), "button_press_event",
2072 GTK_SIGNAL_FUNC(ButtonPress), this);
2073 gtk_clist_set_shadow_type(GTK_CLIST(wid), GTK_SHADOW_NONE);
2074 #else
2075 /* Tree and its model */
2076 GtkListStore *store =
2077 gtk_list_store_new(N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING);
2079 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2080 g_signal_connect(G_OBJECT(list), "style-set", G_CALLBACK(StyleSet), NULL);
2082 GtkTreeSelection *selection =
2083 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2084 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
2085 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
2086 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list), FALSE);
2088 /* Columns */
2089 GtkTreeViewColumn *column = gtk_tree_view_column_new();
2090 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2091 gtk_tree_view_column_set_title(column, "Autocomplete");
2093 GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
2094 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2095 gtk_tree_view_column_add_attribute(column, renderer,
2096 "pixbuf", PIXBUF_COLUMN);
2098 renderer = gtk_cell_renderer_text_new();
2099 gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), 1);
2100 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2101 gtk_tree_view_column_add_attribute(column, renderer,
2102 "text", TEXT_COLUMN);
2104 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
2105 if (g_object_class_find_property(G_OBJECT_GET_CLASS(list), "fixed-height-mode"))
2106 g_object_set(G_OBJECT(list), "fixed-height-mode", TRUE, NULL);
2108 GtkWidget *wid = PWidget(list); // No code inside the G_OBJECT macro
2109 gtk_container_add(GTK_CONTAINER(PWidget(scroller)), wid);
2110 gtk_widget_show(wid);
2111 g_signal_connect(G_OBJECT(wid), "button_press_event",
2112 G_CALLBACK(ButtonPress), this);
2113 #endif
2114 gtk_widget_realize(PWidget(id));
2117 void ListBoxX::SetFont(Font &scint_font) {
2118 #if GTK_MAJOR_VERSION < 2
2119 GtkStyle *style = gtk_widget_get_style(GTK_WIDGET(PWidget(list)));
2120 if (!gdk_font_equal(style->font, PFont(scint_font)->pfont)) {
2121 style = gtk_style_copy(style);
2122 gdk_font_unref(style->font);
2123 style->font = PFont(scint_font)->pfont;
2124 gdk_font_ref(style->font);
2125 gtk_widget_set_style(GTK_WIDGET(PWidget(list)), style);
2126 gtk_style_unref(style);
2128 #else
2129 // Only do for Pango font as there have been crashes for GDK fonts
2130 if (Created() && PFont(scint_font)->pfd) {
2131 // Current font is Pango font
2132 gtk_widget_modify_font(PWidget(list), PFont(scint_font)->pfd);
2134 #endif
2137 void ListBoxX::SetAverageCharWidth(int width) {
2138 aveCharWidth = width;
2141 void ListBoxX::SetVisibleRows(int rows) {
2142 desiredVisibleRows = rows;
2145 int ListBoxX::GetVisibleRows() const {
2146 return desiredVisibleRows;
2149 PRectangle ListBoxX::GetDesiredRect() {
2150 // Before any size allocated pretend its 100 wide so not scrolled
2151 PRectangle rc(0, 0, 100, 100);
2152 if (id) {
2153 int rows = Length();
2154 if ((rows == 0) || (rows > desiredVisibleRows))
2155 rows = desiredVisibleRows;
2157 GtkRequisition req;
2158 int height;
2160 // First calculate height of the clist for our desired visible
2161 // row count otherwise it tries to expand to the total # of rows
2162 #if GTK_MAJOR_VERSION < 2
2163 int ythickness = PWidget(list)->style->klass->ythickness;
2164 height = (rows * GTK_CLIST(list)->row_height
2165 + rows + 1
2166 + 2 * (ythickness
2167 + GTK_CONTAINER(PWidget(list))->border_width));
2168 #else
2169 // Get cell height
2170 int row_width=0;
2171 int row_height=0;
2172 GtkTreeViewColumn * column =
2173 gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
2174 gtk_tree_view_column_cell_get_size(column, NULL,
2175 NULL, NULL, &row_width, &row_height);
2176 int ythickness = PWidget(list)->style->ythickness;
2177 height = (rows * row_height
2178 + 2 * (ythickness
2179 + GTK_CONTAINER(PWidget(list))->border_width + 1));
2180 #endif
2181 gtk_widget_set_usize(GTK_WIDGET(PWidget(list)), -1, height);
2183 // Get the size of the scroller because we set usize on the window
2184 gtk_widget_size_request(GTK_WIDGET(scroller), &req);
2185 rc.right = req.width;
2186 rc.bottom = req.height;
2188 gtk_widget_set_usize(GTK_WIDGET(list), -1, -1);
2189 int width = maxItemCharacters;
2190 if (width < 12)
2191 width = 12;
2192 rc.right = width * (aveCharWidth + aveCharWidth / 3);
2193 if (Length() > rows)
2194 rc.right = rc.right + 16;
2196 return rc;
2199 int ListBoxX::CaretFromEdge() {
2200 return 4 + xset.GetWidth();
2203 void ListBoxX::Clear() {
2204 #if GTK_MAJOR_VERSION < 2
2205 gtk_clist_clear(GTK_CLIST(list));
2206 #else
2207 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2208 gtk_list_store_clear(GTK_LIST_STORE(model));
2209 #endif
2210 maxItemCharacters = 0;
2213 #if GTK_MAJOR_VERSION < 2
2214 static void init_pixmap(ListImage *list_image, GtkWidget *window) {
2215 #else
2216 static void init_pixmap(ListImage *list_image) {
2217 #endif
2218 const char *textForm = list_image->xpm_data;
2219 const char * const * xpm_lineform = reinterpret_cast<const char * const *>(textForm);
2220 const char **xpm_lineformfromtext = 0;
2221 // The XPM data can be either in atext form as will be read from a file
2222 // or in a line form (array of char *) as will be used for images defined in code.
2223 // Test for text form and convert to line form
2224 if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
2225 // Test done is two parts to avoid possibility of overstepping the memory
2226 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
2227 xpm_lineformfromtext = XPM::LinesFormFromTextForm(textForm);
2228 xpm_lineform = xpm_lineformfromtext;
2231 // Drop any existing pixmap/bitmap as data may have changed
2232 #if GTK_MAJOR_VERSION < 2
2233 if (list_image->pixmap)
2234 gdk_pixmap_unref(list_image->pixmap);
2235 list_image->pixmap = NULL;
2236 if (list_image->bitmap)
2237 gdk_bitmap_unref(list_image->bitmap);
2238 list_image->bitmap = NULL;
2240 list_image->pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL
2241 , gtk_widget_get_colormap(window), &(list_image->bitmap), NULL
2242 , (gchar **) xpm_lineform);
2243 if (NULL == list_image->pixmap) {
2244 if (list_image->bitmap)
2245 gdk_bitmap_unref(list_image->bitmap);
2246 list_image->bitmap = NULL;
2248 #else
2249 if (list_image->pixbuf)
2250 gdk_pixbuf_unref(list_image->pixbuf);
2251 list_image->pixbuf =
2252 gdk_pixbuf_new_from_xpm_data((const gchar**)xpm_lineform);
2253 #endif
2254 delete []xpm_lineformfromtext;
2257 #define SPACING 5
2259 void ListBoxX::Append(char *s, int type) {
2260 ListImage *list_image = NULL;
2261 if ((type >= 0) && pixhash) {
2262 list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash
2263 , (gconstpointer) GINT_TO_POINTER(type));
2265 #if GTK_MAJOR_VERSION < 2
2266 char * szs[] = { s, NULL };
2267 int rownum = gtk_clist_append(GTK_CLIST(list), szs);
2268 if (list_image) {
2269 if (NULL == list_image->pixmap)
2270 init_pixmap(list_image, (GtkWidget *) list);
2271 gtk_clist_set_pixtext(GTK_CLIST(list), rownum, 0, s, SPACING
2272 , list_image->pixmap, list_image->bitmap);
2274 #else
2275 GtkTreeIter iter;
2276 GtkListStore *store =
2277 GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
2278 gtk_list_store_append(GTK_LIST_STORE(store), &iter);
2279 if (list_image) {
2280 if (NULL == list_image->pixbuf)
2281 init_pixmap(list_image);
2282 if (list_image->pixbuf) {
2283 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2284 PIXBUF_COLUMN, list_image->pixbuf,
2285 TEXT_COLUMN, s, -1);
2286 } else {
2287 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2288 TEXT_COLUMN, s, -1);
2290 } else {
2291 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2292 TEXT_COLUMN, s, -1);
2294 #endif
2295 size_t len = strlen(s);
2296 if (maxItemCharacters < len)
2297 maxItemCharacters = len;
2300 int ListBoxX::Length() {
2301 if (id)
2302 #if GTK_MAJOR_VERSION < 2
2303 return GTK_CLIST(list)->rows;
2304 #else
2305 return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
2306 (GTK_TREE_VIEW(list)), NULL);
2307 #endif
2308 return 0;
2311 void ListBoxX::Select(int n) {
2312 #if GTK_MAJOR_VERSION < 2
2313 if (n == -1) {
2314 gtk_clist_unselect_row(GTK_CLIST(list), current, 0);
2315 } else {
2316 gtk_clist_select_row(GTK_CLIST(list), n, 0);
2317 gtk_clist_moveto(GTK_CLIST(list), n, 0, 0.5, 0.5);
2319 #else
2320 GtkTreeIter iter;
2321 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2322 GtkTreeSelection *selection =
2323 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2325 if (n < 0) {
2326 gtk_tree_selection_unselect_all(selection);
2327 return;
2330 bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
2331 if (valid) {
2332 gtk_tree_selection_select_iter(selection, &iter);
2334 // Move the scrollbar to show the selection.
2335 int total = Length();
2336 GtkAdjustment *adj =
2337 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list));
2338 gfloat value = ((gfloat)n / total) * (adj->upper - adj->lower)
2339 + adj->lower - adj->page_size / 2;
2341 // Get cell height
2342 int row_width;
2343 int row_height;
2344 GtkTreeViewColumn * column =
2345 gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
2346 gtk_tree_view_column_cell_get_size(column, NULL, NULL,
2347 NULL, &row_width, &row_height);
2349 int rows = Length();
2350 if ((rows == 0) || (rows > desiredVisibleRows))
2351 rows = desiredVisibleRows;
2352 if (rows & 0x1) {
2353 // Odd rows to display -- We are now in the middle.
2354 // Align it so that we don't chop off rows.
2355 value += (gfloat)row_height / 2.0;
2357 // Clamp it.
2358 value = (value < 0)? 0 : value;
2359 value = (value > (adj->upper - adj->page_size))?
2360 (adj->upper - adj->page_size) : value;
2362 // Set it.
2363 gtk_adjustment_set_value(adj, value);
2364 } else {
2365 gtk_tree_selection_unselect_all(selection);
2367 #endif
2370 int ListBoxX::GetSelection() {
2371 #if GTK_MAJOR_VERSION < 2
2372 return current;
2373 #else
2374 GtkTreeIter iter;
2375 GtkTreeModel *model;
2376 GtkTreeSelection *selection;
2377 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2378 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
2379 GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
2380 int *indices = gtk_tree_path_get_indices(path);
2381 // Don't free indices.
2382 if (indices)
2383 return indices[0];
2385 return -1;
2386 #endif
2389 int ListBoxX::Find(const char *prefix) {
2390 #if GTK_MAJOR_VERSION < 2
2391 int count = Length();
2392 for (int i = 0; i < count; i++) {
2393 char *s = 0;
2394 gtk_clist_get_text(GTK_CLIST(list), i, 0, &s);
2395 if (s && (0 == strncmp(prefix, s, strlen(prefix)))) {
2396 return i;
2399 #else
2400 GtkTreeIter iter;
2401 GtkTreeModel *model =
2402 gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2403 bool valid = gtk_tree_model_get_iter_first(model, &iter) != FALSE;
2404 int i = 0;
2405 while(valid) {
2406 gchar *s;
2407 gtk_tree_model_get(model, &iter, TEXT_COLUMN, &s, -1);
2408 if (s && (0 == strncmp(prefix, s, strlen(prefix)))) {
2409 return i;
2411 valid = gtk_tree_model_iter_next(model, &iter) != FALSE;
2412 i++;
2414 #endif
2415 return -1;
2418 void ListBoxX::GetValue(int n, char *value, int len) {
2419 char *text = NULL;
2420 #if GTK_MAJOR_VERSION < 2
2421 GtkCellType type = gtk_clist_get_cell_type(GTK_CLIST(list), n, 0);
2422 switch (type) {
2423 case GTK_CELL_TEXT:
2424 gtk_clist_get_text(GTK_CLIST(list), n, 0, &text);
2425 break;
2426 case GTK_CELL_PIXTEXT:
2427 gtk_clist_get_pixtext(GTK_CLIST(list), n, 0, &text, NULL, NULL, NULL);
2428 break;
2429 default:
2430 break;
2432 #else
2433 GtkTreeIter iter;
2434 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2435 bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
2436 if (valid) {
2437 gtk_tree_model_get(model, &iter, TEXT_COLUMN, &text, -1);
2439 #endif
2440 if (text && len > 0) {
2441 strncpy(value, text, len);
2442 value[len - 1] = '\0';
2443 } else {
2444 value[0] = '\0';
2448 // g_return_if_fail causes unnecessary compiler warning in release compile.
2449 #ifdef _MSC_VER
2450 #pragma warning(disable: 4127)
2451 #endif
2453 void ListBoxX::RegisterImage(int type, const char *xpm_data) {
2454 g_return_if_fail(xpm_data);
2456 // Saved and use the saved copy so caller's copy can disappear.
2457 xset.Add(type, xpm_data);
2458 XPM *pxpm = xset.Get(type);
2459 xpm_data = reinterpret_cast<const char *>(pxpm->InLinesForm());
2461 if (!pixhash) {
2462 pixhash = g_hash_table_new(g_direct_hash, g_direct_equal);
2464 ListImage *list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash,
2465 (gconstpointer) GINT_TO_POINTER(type));
2466 if (list_image) {
2467 // Drop icon already registered
2468 #if GTK_MAJOR_VERSION < 2
2469 if (list_image->pixmap)
2470 gdk_pixmap_unref(list_image->pixmap);
2471 list_image->pixmap = 0;
2472 if (list_image->bitmap)
2473 gdk_bitmap_unref(list_image->bitmap);
2474 list_image->bitmap = 0;
2475 #else
2476 if (list_image->pixbuf)
2477 gdk_pixbuf_unref(list_image->pixbuf);
2478 list_image->pixbuf = NULL;
2479 #endif
2480 list_image->xpm_data = xpm_data;
2481 } else {
2482 list_image = g_new0(ListImage, 1);
2483 list_image->xpm_data = xpm_data;
2484 g_hash_table_insert((GHashTable *) pixhash, GINT_TO_POINTER(type),
2485 (gpointer) list_image);
2489 void ListBoxX::ClearRegisteredImages() {
2490 xset.Clear();
2493 void ListBoxX::SetList(const char *listText, char separator, char typesep) {
2494 Clear();
2495 int count = strlen(listText) + 1;
2496 char *words = new char[count];
2497 if (words) {
2498 memcpy(words, listText, count);
2499 char *startword = words;
2500 char *numword = NULL;
2501 int i = 0;
2502 for (; words[i]; i++) {
2503 if (words[i] == separator) {
2504 words[i] = '\0';
2505 if (numword)
2506 *numword = '\0';
2507 Append(startword, numword?atoi(numword + 1):-1);
2508 startword = words + i + 1;
2509 numword = NULL;
2510 } else if (words[i] == typesep) {
2511 numword = words + i;
2514 if (startword) {
2515 if (numword)
2516 *numword = '\0';
2517 Append(startword, numword?atoi(numword + 1):-1);
2519 delete []words;
2523 Menu::Menu() : id(0) {}
2525 void Menu::CreatePopUp() {
2526 Destroy();
2527 id = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
2530 void Menu::Destroy() {
2531 if (id)
2532 #if GTK_MAJOR_VERSION < 2
2533 gtk_object_unref(GTK_OBJECT(id));
2534 #else
2535 g_object_unref(G_OBJECT(id));
2536 #endif
2537 id = 0;
2540 void Menu::Show(Point pt, Window &) {
2541 int screenHeight = gdk_screen_height();
2542 int screenWidth = gdk_screen_width();
2543 GtkItemFactory *factory = reinterpret_cast<GtkItemFactory *>(id);
2544 GtkWidget *widget = gtk_item_factory_get_widget(factory, "<main>");
2545 gtk_widget_show_all(widget);
2546 GtkRequisition requisition;
2547 gtk_widget_size_request(widget, &requisition);
2548 if ((pt.x + requisition.width) > screenWidth) {
2549 pt.x = screenWidth - requisition.width;
2551 if ((pt.y + requisition.height) > screenHeight) {
2552 pt.y = screenHeight - requisition.height;
2554 #if GTK_MAJOR_VERSION >= 2
2555 gtk_item_factory_popup(factory, pt.x - 4, pt.y - 4, 3,
2556 gtk_get_current_event_time());
2557 #else
2558 gtk_item_factory_popup(factory, pt.x - 4, pt.y - 4, 3, 0);
2559 #endif
2562 ElapsedTime::ElapsedTime() {
2563 GTimeVal curTime;
2564 g_get_current_time(&curTime);
2565 bigBit = curTime.tv_sec;
2566 littleBit = curTime.tv_usec;
2569 class DynamicLibraryImpl : public DynamicLibrary {
2570 protected:
2571 GModule* m;
2572 public:
2573 DynamicLibraryImpl(const char *modulePath) {
2574 m = g_module_open(modulePath, G_MODULE_BIND_LAZY);
2577 virtual ~DynamicLibraryImpl() {
2578 if (m != NULL)
2579 g_module_close(m);
2582 // Use g_module_symbol to get a pointer to the relevant function.
2583 virtual Function FindFunction(const char *name) {
2584 if (m != NULL) {
2585 gpointer fn_address = NULL;
2586 gboolean status = g_module_symbol(m, name, &fn_address);
2587 if (status)
2588 return static_cast<Function>(fn_address);
2589 else
2590 return NULL;
2591 } else
2592 return NULL;
2595 virtual bool IsValid() {
2596 return m != NULL;
2600 DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {
2601 return static_cast<DynamicLibrary *>( new DynamicLibraryImpl(modulePath) );
2604 double ElapsedTime::Duration(bool reset) {
2605 GTimeVal curTime;
2606 g_get_current_time(&curTime);
2607 long endBigBit = curTime.tv_sec;
2608 long endLittleBit = curTime.tv_usec;
2609 double result = 1000000.0 * (endBigBit - bigBit);
2610 result += endLittleBit - littleBit;
2611 result /= 1000000.0;
2612 if (reset) {
2613 bigBit = endBigBit;
2614 littleBit = endLittleBit;
2616 return result;
2619 ColourDesired Platform::Chrome() {
2620 return ColourDesired(0xe0, 0xe0, 0xe0);
2623 ColourDesired Platform::ChromeHighlight() {
2624 return ColourDesired(0xff, 0xff, 0xff);
2627 const char *Platform::DefaultFont() {
2628 #ifdef G_OS_WIN32
2629 return "Lucida Console";
2630 #else
2631 #ifdef USE_PANGO
2632 return "!Sans";
2633 #else
2634 return "lucidatypewriter";
2635 #endif
2636 #endif
2639 int Platform::DefaultFontSize() {
2640 #ifdef G_OS_WIN32
2641 return 10;
2642 #else
2643 return 12;
2644 #endif
2647 unsigned int Platform::DoubleClickTime() {
2648 return 500; // Half a second
2651 bool Platform::MouseButtonBounce() {
2652 return true;
2655 void Platform::DebugDisplay(const char *s) {
2656 fprintf(stderr, "%s", s);
2659 bool Platform::IsKeyDown(int) {
2660 // TODO: discover state of keys in GTK+/X
2661 return false;
2664 long Platform::SendScintilla(
2665 WindowID w, unsigned int msg, unsigned long wParam, long lParam) {
2666 return scintilla_send_message(SCINTILLA(w), msg, wParam, lParam);
2669 long Platform::SendScintillaPointer(
2670 WindowID w, unsigned int msg, unsigned long wParam, void *lParam) {
2671 return scintilla_send_message(SCINTILLA(w), msg, wParam,
2672 reinterpret_cast<sptr_t>(lParam));
2675 bool Platform::IsDBCSLeadByte(int /* codePage */, char /* ch */) {
2676 return false;
2679 int Platform::DBCSCharLength(int, const char *s) {
2680 int bytes = mblen(s, MB_CUR_MAX);
2681 if (bytes >= 1)
2682 return bytes;
2683 else
2684 return 1;
2687 int Platform::DBCSCharMaxLength() {
2688 return MB_CUR_MAX;
2689 //return 2;
2692 // These are utility functions not really tied to a platform
2694 int Platform::Minimum(int a, int b) {
2695 if (a < b)
2696 return a;
2697 else
2698 return b;
2701 int Platform::Maximum(int a, int b) {
2702 if (a > b)
2703 return a;
2704 else
2705 return b;
2708 //#define TRACE
2710 #ifdef TRACE
2711 void Platform::DebugPrintf(const char *format, ...) {
2712 char buffer[2000];
2713 va_list pArguments;
2714 va_start(pArguments, format);
2715 vsprintf(buffer, format, pArguments);
2716 va_end(pArguments);
2717 Platform::DebugDisplay(buffer);
2719 #else
2720 void Platform::DebugPrintf(const char *, ...) {}
2722 #endif
2724 // Not supported for GTK+
2725 static bool assertionPopUps = true;
2727 bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {
2728 bool ret = assertionPopUps;
2729 assertionPopUps = assertionPopUps_;
2730 return ret;
2733 void Platform::Assert(const char *c, const char *file, int line) {
2734 char buffer[2000];
2735 sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line);
2736 strcat(buffer, "\r\n");
2737 Platform::DebugDisplay(buffer);
2738 abort();
2741 int Platform::Clamp(int val, int minVal, int maxVal) {
2742 if (val > maxVal)
2743 val = maxVal;
2744 if (val < minVal)
2745 val = minVal;
2746 return val;
2749 void Platform_Initialise() {
2750 FontMutexAllocate();
2753 void Platform_Finalise() {
2754 FontMutexFree();