Refactor snippets_complete_constructs().
[geany-mirror.git] / scintilla / PlatGTK.cxx
blob30d8d7da261817d0b49dc64377fde82c72e22ef5
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 #ifdef SCI_NAMESPACE
45 using namespace Scintilla;
46 #endif
48 enum encodingType { singleByte, UTF8, dbcs};
50 struct LOGFONT {
51 int size;
52 bool bold;
53 bool italic;
54 int characterSet;
55 char faceName[300];
58 #if USE_LOCK
59 static GMutex *fontMutex = NULL;
61 static void InitializeGLIBThreads() {
62 if (!g_thread_supported()) {
63 g_thread_init(NULL);
66 #endif
68 static void FontMutexAllocate() {
69 #if USE_LOCK
70 if (!fontMutex) {
71 InitializeGLIBThreads();
72 fontMutex = g_mutex_new();
74 #endif
77 static void FontMutexFree() {
78 #if USE_LOCK
79 if (fontMutex) {
80 g_mutex_free(fontMutex);
81 fontMutex = NULL;
83 #endif
86 static void FontMutexLock() {
87 #if USE_LOCK
88 g_mutex_lock(fontMutex);
89 #endif
92 static void FontMutexUnlock() {
93 #if USE_LOCK
94 if (fontMutex) {
95 g_mutex_unlock(fontMutex);
97 #endif
100 // On GTK+ 1.x holds a GdkFont* but on GTK+ 2.x can hold a GdkFont* or a
101 // PangoFontDescription*.
102 class FontHandle {
103 int width[128];
104 encodingType et;
105 public:
106 int ascent;
107 GdkFont *pfont;
108 #ifdef USE_PANGO
109 PangoFontDescription *pfd;
110 int characterSet;
111 #endif
112 FontHandle(GdkFont *pfont_) {
113 et = singleByte;
114 ascent = 0;
115 pfont = pfont_;
116 #ifdef USE_PANGO
117 pfd = 0;
118 characterSet = -1;
119 #endif
120 ResetWidths(et);
122 #ifdef USE_PANGO
123 FontHandle(PangoFontDescription *pfd_, int characterSet_) {
124 et = singleByte;
125 ascent = 0;
126 pfont = 0;
127 pfd = pfd_;
128 characterSet = characterSet_;
129 ResetWidths(et);
131 #endif
132 ~FontHandle() {
133 if (pfont)
134 gdk_font_unref(pfont);
135 pfont = 0;
136 #ifdef USE_PANGO
137 if (pfd)
138 pango_font_description_free(pfd);
139 pfd = 0;
140 #endif
142 void ResetWidths(encodingType et_) {
143 et = et_;
144 for (int i=0; i<=127; i++) {
145 width[i] = 0;
148 int CharWidth(unsigned char ch, encodingType et_) {
149 int w = 0;
150 FontMutexLock();
151 if ((ch <= 127) && (et == et_)) {
152 w = width[ch];
154 FontMutexUnlock();
155 return w;
157 void SetCharWidth(unsigned char ch, int w, encodingType et_) {
158 if (ch <= 127) {
159 FontMutexLock();
160 if (et != et_) {
161 ResetWidths(et_);
163 width[ch] = w;
164 FontMutexUnlock();
169 // X has a 16 bit coordinate space, so stop drawing here to avoid wrapping
170 static const int maxCoordinate = 32000;
172 static FontHandle *PFont(Font &f) {
173 return reinterpret_cast<FontHandle *>(f.GetID());
176 static GtkWidget *PWidget(WindowID wid) {
177 return reinterpret_cast<GtkWidget *>(wid);
180 static GtkWidget *PWidget(Window &w) {
181 return PWidget(w.GetID());
184 Point Point::FromLong(long lpoint) {
185 return Point(
186 Platform::LowShortFromLong(lpoint),
187 Platform::HighShortFromLong(lpoint));
190 Palette::Palette() {
191 used = 0;
192 allowRealization = false;
193 allocatedPalette = 0;
194 allocatedLen = 0;
195 size = 100;
196 entries = new ColourPair[size];
199 Palette::~Palette() {
200 Release();
201 delete []entries;
202 entries = 0;
205 void Palette::Release() {
206 used = 0;
207 delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
208 allocatedPalette = 0;
209 allocatedLen = 0;
210 delete []entries;
211 size = 100;
212 entries = new ColourPair[size];
215 // This method either adds a colour to the list of wanted colours (want==true)
216 // or retrieves the allocated colour back to the ColourPair.
217 // This is one method to make it easier to keep the code for wanting and retrieving in sync.
218 void Palette::WantFind(ColourPair &cp, bool want) {
219 if (want) {
220 for (int i=0; i < used; i++) {
221 if (entries[i].desired == cp.desired)
222 return;
225 if (used >= size) {
226 int sizeNew = size * 2;
227 ColourPair *entriesNew = new ColourPair[sizeNew];
228 for (int j=0; j<size; j++) {
229 entriesNew[j] = entries[j];
231 delete []entries;
232 entries = entriesNew;
233 size = sizeNew;
236 entries[used].desired = cp.desired;
237 entries[used].allocated.Set(cp.desired.AsLong());
238 used++;
239 } else {
240 for (int i=0; i < used; i++) {
241 if (entries[i].desired == cp.desired) {
242 cp.allocated = entries[i].allocated;
243 return;
246 cp.allocated.Set(cp.desired.AsLong());
250 void Palette::Allocate(Window &w) {
251 if (allocatedPalette) {
252 gdk_colormap_free_colors(gtk_widget_get_colormap(PWidget(w)),
253 reinterpret_cast<GdkColor *>(allocatedPalette),
254 allocatedLen);
255 delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
256 allocatedPalette = 0;
257 allocatedLen = 0;
259 GdkColor *paletteNew = new GdkColor[used];
260 allocatedPalette = paletteNew;
261 gboolean *successPalette = new gboolean[used];
262 if (paletteNew) {
263 allocatedLen = used;
264 int iPal = 0;
265 for (iPal = 0; iPal < used; iPal++) {
266 paletteNew[iPal].red = entries[iPal].desired.GetRed() * (65535 / 255);
267 paletteNew[iPal].green = entries[iPal].desired.GetGreen() * (65535 / 255);
268 paletteNew[iPal].blue = entries[iPal].desired.GetBlue() * (65535 / 255);
269 paletteNew[iPal].pixel = entries[iPal].desired.AsLong();
271 gdk_colormap_alloc_colors(gtk_widget_get_colormap(PWidget(w)),
272 paletteNew, allocatedLen, FALSE, TRUE,
273 successPalette);
274 for (iPal = 0; iPal < used; iPal++) {
275 entries[iPal].allocated.Set(paletteNew[iPal].pixel);
278 delete []successPalette;
281 static const char *CharacterSetName(int characterSet) {
282 switch (characterSet) {
283 case SC_CHARSET_ANSI:
284 return "iso8859-*";
285 case SC_CHARSET_DEFAULT:
286 return "iso8859-*";
287 case SC_CHARSET_BALTIC:
288 return "iso8859-13";
289 case SC_CHARSET_CHINESEBIG5:
290 return "*-*";
291 case SC_CHARSET_EASTEUROPE:
292 return "*-2";
293 case SC_CHARSET_GB2312:
294 return "gb2312.1980-*";
295 case SC_CHARSET_GREEK:
296 return "*-7";
297 case SC_CHARSET_HANGUL:
298 return "ksc5601.1987-*";
299 case SC_CHARSET_MAC:
300 return "*-*";
301 case SC_CHARSET_OEM:
302 return "*-*";
303 case SC_CHARSET_RUSSIAN:
304 return "*-r";
305 case SC_CHARSET_CYRILLIC:
306 return "*-cp1251";
307 case SC_CHARSET_SHIFTJIS:
308 return "jisx0208.1983-*";
309 case SC_CHARSET_SYMBOL:
310 return "*-*";
311 case SC_CHARSET_TURKISH:
312 return "*-9";
313 case SC_CHARSET_JOHAB:
314 return "*-*";
315 case SC_CHARSET_HEBREW:
316 return "*-8";
317 case SC_CHARSET_ARABIC:
318 return "*-6";
319 case SC_CHARSET_VIETNAMESE:
320 return "*-*";
321 case SC_CHARSET_THAI:
322 return "iso8859-11";
323 case SC_CHARSET_8859_15:
324 return "iso8859-15";
325 default:
326 return "*-*";
330 static bool IsDBCSCharacterSet(int characterSet) {
331 switch (characterSet) {
332 case SC_CHARSET_GB2312:
333 case SC_CHARSET_HANGUL:
334 case SC_CHARSET_SHIFTJIS:
335 case SC_CHARSET_CHINESEBIG5:
336 return true;
337 default:
338 return false;
342 static void GenerateFontSpecStrings(const char *fontName, int characterSet,
343 char *foundary, int foundary_len,
344 char *faceName, int faceName_len,
345 char *charset, int charset_len) {
346 // supported font strings include:
347 // foundary-fontface-isoxxx-x
348 // fontface-isoxxx-x
349 // foundary-fontface
350 // fontface
351 if (strchr(fontName, '-')) {
352 char tmp[300];
353 char *d1 = NULL, *d2 = NULL, *d3 = NULL;
354 strncpy(tmp, fontName, sizeof(tmp) - 1);
355 tmp[sizeof(tmp) - 1] = '\0';
356 d1 = strchr(tmp, '-');
357 // we know the first dash exists
358 d2 = strchr(d1 + 1, '-');
359 if (d2)
360 d3 = strchr(d2 + 1, '-');
361 if (d3) {
362 // foundary-fontface-isoxxx-x
363 *d2 = '\0';
364 foundary[0] = '-';
365 foundary[1] = '\0';
366 strncpy(faceName, tmp, foundary_len - 1);
367 strncpy(charset, d2 + 1, charset_len - 1);
368 } else if (d2) {
369 // fontface-isoxxx-x
370 *d1 = '\0';
371 strcpy(foundary, "-*-");
372 strncpy(faceName, tmp, faceName_len - 1);
373 strncpy(charset, d1 + 1, charset_len - 1);
374 } else {
375 // foundary-fontface
376 foundary[0] = '-';
377 foundary[1] = '\0';
378 strncpy(faceName, tmp, faceName_len - 1);
379 strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
381 } else {
382 strncpy(foundary, "-*-", foundary_len);
383 strncpy(faceName, fontName, faceName_len - 1);
384 strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
388 static void SetLogFont(LOGFONT &lf, const char *faceName, int characterSet, int size, bool bold, bool italic) {
389 memset(&lf, 0, sizeof(lf));
390 lf.size = size;
391 lf.bold = bold;
392 lf.italic = italic;
393 lf.characterSet = characterSet;
394 strncpy(lf.faceName, faceName, sizeof(lf.faceName) - 1);
398 * Create a hash from the parameters for a font to allow easy checking for identity.
399 * If one font is the same as another, its hash will be the same, but if the hash is the
400 * same then they may still be different.
402 static int HashFont(const char *faceName, int characterSet, int size, bool bold, bool italic) {
403 return
404 size ^
405 (characterSet << 10) ^
406 (bold ? 0x10000000 : 0) ^
407 (italic ? 0x20000000 : 0) ^
408 faceName[0];
411 class FontCached : Font {
412 FontCached *next;
413 int usage;
414 LOGFONT lf;
415 int hash;
416 FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
417 ~FontCached() {}
418 bool SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
419 virtual void Release();
420 static FontID CreateNewFont(const char *fontName, int characterSet,
421 int size, bool bold, bool italic);
422 static FontCached *first;
423 public:
424 static FontID FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
425 static void ReleaseId(FontID fid_);
428 FontCached *FontCached::first = 0;
430 FontCached::FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) :
431 next(0), usage(0), hash(0) {
432 ::SetLogFont(lf, faceName_, characterSet_, size_, bold_, italic_);
433 hash = HashFont(faceName_, characterSet_, size_, bold_, italic_);
434 fid = CreateNewFont(faceName_, characterSet_, size_, bold_, italic_);
435 usage = 1;
438 bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
439 return
440 lf.size == size_ &&
441 lf.bold == bold_ &&
442 lf.italic == italic_ &&
443 lf.characterSet == characterSet_ &&
444 0 == strcmp(lf.faceName, faceName_);
447 void FontCached::Release() {
448 if (fid)
449 delete PFont(*this);
450 fid = 0;
453 FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
454 FontID ret = 0;
455 FontMutexLock();
456 int hashFind = HashFont(faceName_, characterSet_, size_, bold_, italic_);
457 for (FontCached *cur = first; cur; cur = cur->next) {
458 if ((cur->hash == hashFind) &&
459 cur->SameAs(faceName_, characterSet_, size_, bold_, italic_)) {
460 cur->usage++;
461 ret = cur->fid;
464 if (ret == 0) {
465 FontCached *fc = new FontCached(faceName_, characterSet_, size_, bold_, italic_);
466 if (fc) {
467 fc->next = first;
468 first = fc;
469 ret = fc->fid;
472 FontMutexUnlock();
473 return ret;
476 void FontCached::ReleaseId(FontID fid_) {
477 FontMutexLock();
478 FontCached **pcur = &first;
479 for (FontCached *cur = first; cur; cur = cur->next) {
480 if (cur->fid == fid_) {
481 cur->usage--;
482 if (cur->usage == 0) {
483 *pcur = cur->next;
484 cur->Release();
485 cur->next = 0;
486 delete cur;
488 break;
490 pcur = &cur->next;
492 FontMutexUnlock();
495 static GdkFont *LoadFontOrSet(const char *fontspec, int characterSet) {
496 if (IsDBCSCharacterSet(characterSet)) {
497 return gdk_fontset_load(fontspec);
498 } else {
499 return gdk_font_load(fontspec);
503 FontID FontCached::CreateNewFont(const char *fontName, int characterSet,
504 int size, bool bold, bool italic) {
505 char fontset[1024];
506 char fontspec[300];
507 char foundary[50];
508 char faceName[100];
509 char charset[50];
510 fontset[0] = '\0';
511 fontspec[0] = '\0';
512 foundary[0] = '\0';
513 faceName[0] = '\0';
514 charset[0] = '\0';
516 #ifdef USE_PANGO
517 if (fontName[0] == '!') {
518 PangoFontDescription *pfd = pango_font_description_new();
519 if (pfd) {
520 pango_font_description_set_family(pfd, fontName+1);
521 pango_font_description_set_size(pfd, size * PANGO_SCALE);
522 pango_font_description_set_weight(pfd, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
523 pango_font_description_set_style(pfd, italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
524 return new FontHandle(pfd, characterSet);
527 #endif
529 GdkFont *newid = 0;
530 // If name of the font begins with a '-', assume, that it is
531 // a full fontspec.
532 if (fontName[0] == '-') {
533 if (strchr(fontName, ',') || IsDBCSCharacterSet(characterSet)) {
534 newid = gdk_fontset_load(fontName);
535 } else {
536 newid = gdk_font_load(fontName);
538 if (!newid) {
539 // Font not available so substitute a reasonable code font
540 // iso8859 appears to only allow western characters.
541 newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
542 characterSet);
544 return new FontHandle(newid);
547 // it's not a full fontspec, build one.
549 // This supports creating a FONT_SET
550 // in a method that allows us to also set size, slant and
551 // weight for the fontset. The expected input is multiple
552 // partial fontspecs seperated by comma
553 // eg. adobe-courier-iso10646-1,*-courier-iso10646-1,*-*-*-*
554 if (strchr(fontName, ',')) {
555 // build a fontspec and use gdk_fontset_load
556 int remaining = sizeof(fontset);
557 char fontNameCopy[1024];
558 strncpy(fontNameCopy, fontName, sizeof(fontNameCopy) - 1);
559 char *fn = fontNameCopy;
560 char *fp = strchr(fn, ',');
561 for (;;) {
562 const char *spec = "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
563 if (fontset[0] != '\0') {
564 // if this is not the first font in the list,
565 // append a comma seperator
566 spec = ",%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
569 if (fp)
570 *fp = '\0'; // nullify the comma
571 GenerateFontSpecStrings(fn, characterSet,
572 foundary, sizeof(foundary),
573 faceName, sizeof(faceName),
574 charset, sizeof(charset));
576 g_snprintf(fontspec,
577 sizeof(fontspec) - 1,
578 spec,
579 foundary, faceName,
580 bold ? "-bold" : "-medium",
581 italic ? "-i" : "-r",
582 size * 10,
583 charset);
585 // if this is the first font in the list, and
586 // we are doing italic, add an oblique font
587 // to the list
588 if (italic && fontset[0] == '\0') {
589 strncat(fontset, fontspec, remaining - 1);
590 remaining -= strlen(fontset);
592 g_snprintf(fontspec,
593 sizeof(fontspec) - 1,
594 ",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s",
595 foundary, faceName,
596 bold ? "-bold" : "-medium",
597 size * 10,
598 charset);
601 strncat(fontset, fontspec, remaining - 1);
602 remaining -= strlen(fontset);
604 if (!fp)
605 break;
607 fn = fp + 1;
608 fp = strchr(fn, ',');
611 newid = gdk_fontset_load(fontset);
612 if (newid)
613 return new FontHandle(newid);
615 // if fontset load failed, fall through, we'll use
616 // the last font entry and continue to try and
617 // get something that matches
620 // single fontspec support
622 GenerateFontSpecStrings(fontName, characterSet,
623 foundary, sizeof(foundary),
624 faceName, sizeof(faceName),
625 charset, sizeof(charset));
627 g_snprintf(fontspec,
628 sizeof(fontspec) - 1,
629 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
630 foundary, faceName,
631 bold ? "-bold" : "-medium",
632 italic ? "-i" : "-r",
633 size * 10,
634 charset);
635 newid = LoadFontOrSet(fontspec, characterSet);
636 if (!newid) {
637 // some fonts have oblique, not italic
638 g_snprintf(fontspec,
639 sizeof(fontspec) - 1,
640 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
641 foundary, faceName,
642 bold ? "-bold" : "-medium",
643 italic ? "-o" : "-r",
644 size * 10,
645 charset);
646 newid = LoadFontOrSet(fontspec, characterSet);
648 if (!newid) {
649 g_snprintf(fontspec,
650 sizeof(fontspec) - 1,
651 "-*-*-*-*-*-*-*-%0d-*-*-*-*-%s",
652 size * 10,
653 charset);
654 newid = gdk_font_load(fontspec);
656 if (!newid) {
657 // Font not available so substitute a reasonable code font
658 // iso8859 appears to only allow western characters.
659 newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
660 characterSet);
662 return new FontHandle(newid);
665 Font::Font() : fid(0) {}
667 Font::~Font() {}
669 void Font::Create(const char *faceName, int characterSet, int size,
670 bool bold, bool italic, int) {
671 Release();
672 fid = FontCached::FindOrCreate(faceName, characterSet, size, bold, italic);
675 void Font::Release() {
676 if (fid)
677 FontCached::ReleaseId(fid);
678 fid = 0;
681 // Required on OS X
682 #ifdef SCI_NAMESPACE
683 class Scintilla::SurfaceImpl : public Surface
684 #else
685 class SurfaceImpl : public Surface
686 #endif
688 encodingType et;
689 GdkDrawable *drawable;
690 GdkGC *gc;
691 GdkPixmap *ppixmap;
692 int x;
693 int y;
694 bool inited;
695 bool createdGC;
696 #ifdef USE_PANGO
697 PangoContext *pcontext;
698 PangoLayout *layout;
699 Converter conv;
700 int characterSet;
701 void SetConverter(int characterSet_);
702 #endif
703 public:
704 SurfaceImpl();
705 virtual ~SurfaceImpl();
707 void Init(WindowID wid);
708 void Init(SurfaceID sid, WindowID wid);
709 void InitPixMap(int width, int height, Surface *surface_, WindowID wid);
711 void Release();
712 bool Initialised();
713 void PenColour(ColourAllocated fore);
714 int LogPixelsY();
715 int DeviceHeightFont(int points);
716 void MoveTo(int x_, int y_);
717 void LineTo(int x_, int y_);
718 void Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back);
719 void RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back);
720 void FillRectangle(PRectangle rc, ColourAllocated back);
721 void FillRectangle(PRectangle rc, Surface &surfacePattern);
722 void RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back);
723 void AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
724 ColourAllocated outline, int alphaOutline, int flags);
725 void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back);
726 void Copy(PRectangle rc, Point from, Surface &surfaceSource);
728 void DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
729 void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
730 void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
731 void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
732 void MeasureWidths(Font &font_, const char *s, int len, int *positions);
733 int WidthText(Font &font_, const char *s, int len);
734 int WidthChar(Font &font_, char ch);
735 int Ascent(Font &font_);
736 int Descent(Font &font_);
737 int InternalLeading(Font &font_);
738 int ExternalLeading(Font &font_);
739 int Height(Font &font_);
740 int AverageCharWidth(Font &font_);
742 int SetPalette(Palette *pal, bool inBackGround);
743 void SetClip(PRectangle rc);
744 void FlushCachedState();
746 void SetUnicodeMode(bool unicodeMode_);
747 void SetDBCSMode(int codePage);
750 const char *CharacterSetID(int characterSet) {
751 switch (characterSet) {
752 case SC_CHARSET_ANSI:
753 return "";
754 case SC_CHARSET_DEFAULT:
755 return "ISO-8859-1";
756 case SC_CHARSET_BALTIC:
757 return "ISO-8859-13";
758 case SC_CHARSET_CHINESEBIG5:
759 return "BIG-5";
760 case SC_CHARSET_EASTEUROPE:
761 return "ISO-8859-2";
762 case SC_CHARSET_GB2312:
763 return "GB2312";
764 case SC_CHARSET_GREEK:
765 return "ISO-8859-7";
766 case SC_CHARSET_HANGUL:
767 return "";
768 case SC_CHARSET_MAC:
769 return "MACINTOSH";
770 case SC_CHARSET_OEM:
771 return "ASCII";
772 case SC_CHARSET_RUSSIAN:
773 return "KOI8-R";
774 case SC_CHARSET_CYRILLIC:
775 return "CP1251";
776 case SC_CHARSET_SHIFTJIS:
777 return "SHIFT-JIS";
778 case SC_CHARSET_SYMBOL:
779 return "";
780 case SC_CHARSET_TURKISH:
781 return "ISO-8859-9";
782 case SC_CHARSET_JOHAB:
783 return "JOHAB";
784 case SC_CHARSET_HEBREW:
785 return "ISO-8859-8";
786 case SC_CHARSET_ARABIC:
787 return "ISO-8859-6";
788 case SC_CHARSET_VIETNAMESE:
789 return "";
790 case SC_CHARSET_THAI:
791 return "ISO-8859-11";
792 case SC_CHARSET_8859_15:
793 return "ISO-8859-15";
794 default:
795 return "";
799 #ifdef USE_PANGO
801 void SurfaceImpl::SetConverter(int characterSet_) {
802 if (characterSet != characterSet_) {
803 characterSet = characterSet_;
804 conv.Open("UTF-8", CharacterSetID(characterSet), false);
807 #endif
809 SurfaceImpl::SurfaceImpl() : et(singleByte), drawable(0), gc(0), ppixmap(0),
810 x(0), y(0), inited(false), createdGC(false)
811 #ifdef USE_PANGO
812 , pcontext(0), layout(0), characterSet(-1)
813 #endif
817 SurfaceImpl::~SurfaceImpl() {
818 Release();
821 void SurfaceImpl::Release() {
822 et = singleByte;
823 drawable = 0;
824 if (createdGC) {
825 createdGC = false;
826 gdk_gc_unref(gc);
828 gc = 0;
829 if (ppixmap)
830 gdk_pixmap_unref(ppixmap);
831 ppixmap = 0;
832 #ifdef USE_PANGO
833 if (layout)
834 g_object_unref(layout);
835 layout = 0;
836 if (pcontext)
837 g_object_unref(pcontext);
838 pcontext = 0;
839 conv.Close();
840 characterSet = -1;
841 #endif
842 x = 0;
843 y = 0;
844 inited = false;
845 createdGC = false;
848 bool SurfaceImpl::Initialised() {
849 return inited;
852 // The WindowID argument is only used for Pango builds
853 #ifdef USE_PANGO
854 #define WID_NAME wid
855 #else
856 #define WID_NAME
857 #endif
859 void SurfaceImpl::Init(WindowID WID_NAME) {
860 Release();
861 #ifdef USE_PANGO
862 PLATFORM_ASSERT(wid);
863 pcontext = gtk_widget_create_pango_context(PWidget(wid));
864 PLATFORM_ASSERT(pcontext);
865 layout = pango_layout_new(pcontext);
866 PLATFORM_ASSERT(layout);
867 #endif
868 inited = true;
871 void SurfaceImpl::Init(SurfaceID sid, WindowID WID_NAME) {
872 PLATFORM_ASSERT(sid);
873 GdkDrawable *drawable_ = reinterpret_cast<GdkDrawable *>(sid);
874 Release();
875 #ifdef USE_PANGO
876 PLATFORM_ASSERT(wid);
877 pcontext = gtk_widget_create_pango_context(PWidget(wid));
878 layout = pango_layout_new(pcontext);
879 #endif
880 drawable = drawable_;
881 gc = gdk_gc_new(drawable_);
882 // Ask for lines that do not paint the last pixel so is like Win32
883 gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
884 createdGC = true;
885 inited = true;
888 void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID WID_NAME) {
889 PLATFORM_ASSERT(surface_);
890 Release();
891 SurfaceImpl *surfImpl = static_cast<SurfaceImpl *>(surface_);
892 PLATFORM_ASSERT(surfImpl->drawable);
893 #ifdef USE_PANGO
894 PLATFORM_ASSERT(wid);
895 pcontext = gtk_widget_create_pango_context(PWidget(wid));
896 PLATFORM_ASSERT(pcontext);
897 layout = pango_layout_new(pcontext);
898 PLATFORM_ASSERT(layout);
899 #endif
900 if (height > 0 && width > 0)
901 ppixmap = gdk_pixmap_new(surfImpl->drawable, width, height, -1);
902 drawable = ppixmap;
903 gc = gdk_gc_new(surfImpl->drawable);
904 // Ask for lines that do not paint the last pixel so is like Win32
905 gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
906 createdGC = true;
907 inited = true;
910 void SurfaceImpl::PenColour(ColourAllocated fore) {
911 if (gc) {
912 GdkColor co;
913 co.pixel = fore.AsLong();
914 gdk_gc_set_foreground(gc, &co);
918 int SurfaceImpl::LogPixelsY() {
919 return 72;
922 int SurfaceImpl::DeviceHeightFont(int points) {
923 int logPix = LogPixelsY();
924 return (points * logPix + logPix / 2) / 72;
927 void SurfaceImpl::MoveTo(int x_, int y_) {
928 x = x_;
929 y = y_;
932 void SurfaceImpl::LineTo(int x_, int y_) {
933 if (drawable && gc) {
934 gdk_draw_line(drawable, gc,
935 x, y,
936 x_, y_);
938 x = x_;
939 y = y_;
942 void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore,
943 ColourAllocated back) {
944 GdkPoint gpts[20];
945 if (npts < static_cast<int>((sizeof(gpts) / sizeof(gpts[0])))) {
946 for (int i = 0;i < npts;i++) {
947 gpts[i].x = pts[i].x;
948 gpts[i].y = pts[i].y;
950 PenColour(back);
951 gdk_draw_polygon(drawable, gc, 1, gpts, npts);
952 PenColour(fore);
953 gdk_draw_polygon(drawable, gc, 0, gpts, npts);
957 void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
958 if (gc && drawable) {
959 PenColour(back);
960 gdk_draw_rectangle(drawable, gc, 1,
961 rc.left + 1, rc.top + 1,
962 rc.right - rc.left - 2, rc.bottom - rc.top - 2);
964 PenColour(fore);
965 // The subtraction of 1 off the width and height here shouldn't be needed but
966 // otherwise a different rectangle is drawn than would be done if the fill parameter == 1
967 gdk_draw_rectangle(drawable, gc, 0,
968 rc.left, rc.top,
969 rc.right - rc.left - 1, rc.bottom - rc.top - 1);
973 void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) {
974 PenColour(back);
975 if (drawable && (rc.left < maxCoordinate)) { // Protect against out of range
976 gdk_draw_rectangle(drawable, gc, 1,
977 rc.left, rc.top,
978 rc.right - rc.left, rc.bottom - rc.top);
982 void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) {
983 if (static_cast<SurfaceImpl &>(surfacePattern).drawable) {
984 // Tile pattern over rectangle
985 // Currently assumes 8x8 pattern
986 int widthPat = 8;
987 int heightPat = 8;
988 for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) {
989 int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat;
990 for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) {
991 int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat;
992 gdk_draw_pixmap(drawable,
994 static_cast<SurfaceImpl &>(surfacePattern).drawable,
995 0, 0,
996 xTile, yTile,
997 widthx, heighty);
1000 } else {
1001 // Something is wrong so try to show anyway
1002 // Shows up black because colour not allocated
1003 FillRectangle(rc, ColourAllocated(0));
1007 void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
1008 if (((rc.right - rc.left) > 4) && ((rc.bottom - rc.top) > 4)) {
1009 // Approximate a round rect with some cut off corners
1010 Point pts[] = {
1011 Point(rc.left + 2, rc.top),
1012 Point(rc.right - 2, rc.top),
1013 Point(rc.right, rc.top + 2),
1014 Point(rc.right, rc.bottom - 2),
1015 Point(rc.right - 2, rc.bottom),
1016 Point(rc.left + 2, rc.bottom),
1017 Point(rc.left, rc.bottom - 2),
1018 Point(rc.left, rc.top + 2),
1020 Polygon(pts, sizeof(pts) / sizeof(pts[0]), fore, back);
1021 } else {
1022 RectangleDraw(rc, fore, back);
1026 #if GTK_MAJOR_VERSION >= 2
1028 // Plot a point into a guint32 buffer symetrically to all 4 qudrants
1029 static void AllFour(guint32 *pixels, int stride, int width, int height, int x, int y, guint32 val) {
1030 pixels[y*stride+x] = val;
1031 pixels[y*stride+width-1-x] = val;
1032 pixels[(height-1-y)*stride+x] = val;
1033 pixels[(height-1-y)*stride+width-1-x] = val;
1036 static unsigned int GetRValue(unsigned int co) {
1037 return (co >> 16) & 0xff;
1040 static unsigned int GetGValue(unsigned int co) {
1041 return (co >> 8) & 0xff;
1044 static unsigned int GetBValue(unsigned int co) {
1045 return co & 0xff;
1048 #endif
1050 #if GTK_MAJOR_VERSION < 2
1051 void SurfaceImpl::AlphaRectangle(PRectangle rc, int , ColourAllocated , int , ColourAllocated outline, int , int ) {
1052 if (gc && drawable) {
1053 // Can't use GdkPixbuf on GTK+ 1.x, so draw an outline rather than use alpha.
1054 PenColour(outline);
1055 gdk_draw_rectangle(drawable, gc, 0,
1056 rc.left, rc.top,
1057 rc.right - rc.left - 1, rc.bottom - rc.top - 1);
1060 #else
1062 static guint32 u32FromRGBA(guint8 r, guint8 g, guint8 b, guint8 a) {
1063 union {
1064 guint8 pixVal[4];
1065 guint32 val;
1066 } converter;
1067 converter.pixVal[0] = r;
1068 converter.pixVal[1] = g;
1069 converter.pixVal[2] = b;
1070 converter.pixVal[3] = a;
1071 return converter.val;
1074 void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
1075 ColourAllocated outline, int alphaOutline, int flags) {
1076 if (gc && drawable && rc.Width() > 0) {
1077 int width = rc.Width();
1078 int height = rc.Height();
1079 // Ensure not distorted too much by corners when small
1080 cornerSize = Platform::Minimum(cornerSize, (Platform::Minimum(width, height) / 2) - 2);
1081 // Make a 32 bit deep pixbuf with alpha
1082 GdkPixbuf *pixalpha = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
1084 guint32 valEmpty = u32FromRGBA(0,0,0,0);
1085 guint32 valFill = u32FromRGBA(GetRValue(fill.AsLong()),
1086 GetGValue(fill.AsLong()), GetBValue(fill.AsLong()), alphaFill);
1087 guint32 valOutline = u32FromRGBA(GetRValue(outline.AsLong()),
1088 GetGValue(outline.AsLong()), GetBValue(outline.AsLong()), alphaOutline);
1089 guint32 *pixels = reinterpret_cast<guint32 *>(gdk_pixbuf_get_pixels(pixalpha));
1090 int stride = gdk_pixbuf_get_rowstride(pixalpha) / 4;
1091 for (int yr=0; yr<height; yr++) {
1092 for (int xr=0; xr<width; xr++) {
1093 if ((xr==0) || (xr==width-1) || (yr == 0) || (yr == height-1)) {
1094 pixels[yr*stride+xr] = valOutline;
1095 } else {
1096 pixels[yr*stride+xr] = valFill;
1100 for (int c=0;c<cornerSize; c++) {
1101 for (int xr=0;xr<c+1; xr++) {
1102 AllFour(pixels, stride, width, height, xr, c-xr, valEmpty);
1105 for (int xr=1;xr<cornerSize; xr++) {
1106 AllFour(pixels, stride, width, height, xr, cornerSize-xr, valOutline);
1109 // Draw with alpha
1110 gdk_draw_pixbuf(drawable, gc, pixalpha,
1111 0,0, rc.left,rc.top, width,height, GDK_RGB_DITHER_NORMAL, 0, 0);
1113 g_object_unref(pixalpha);
1117 #endif
1119 void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
1120 PenColour(back);
1121 gdk_draw_arc(drawable, gc, 1,
1122 rc.left + 1, rc.top + 1,
1123 rc.right - rc.left - 2, rc.bottom - rc.top - 2,
1124 0, 32767);
1126 // The subtraction of 1 here is similar to the case for RectangleDraw
1127 PenColour(fore);
1128 gdk_draw_arc(drawable, gc, 0,
1129 rc.left, rc.top,
1130 rc.right - rc.left - 1, rc.bottom - rc.top - 1,
1131 0, 32767);
1134 void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
1135 if (static_cast<SurfaceImpl &>(surfaceSource).drawable) {
1136 gdk_draw_pixmap(drawable,
1138 static_cast<SurfaceImpl &>(surfaceSource).drawable,
1139 from.x, from.y,
1140 rc.left, rc.top,
1141 rc.right - rc.left, rc.bottom - rc.top);
1145 static size_t UTF8Len(char ch) {
1146 unsigned char uch = static_cast<unsigned char>(ch);
1147 if (uch < 0x80)
1148 return 1;
1149 else if (uch < (0x80 + 0x40 + 0x20))
1150 return 2;
1151 else
1152 return 3;
1155 char *UTF8FromLatin1(const char *s, int &len) {
1156 char *utfForm = new char[len*2+1];
1157 size_t lenU = 0;
1158 for (int i=0;i<len;i++) {
1159 unsigned int uch = static_cast<unsigned char>(s[i]);
1160 if (uch < 0x80) {
1161 utfForm[lenU++] = uch;
1162 } else {
1163 utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
1164 utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1167 utfForm[lenU] = '\0';
1168 len = lenU;
1169 return utfForm;
1172 #ifdef USE_PANGO
1173 static char *UTF8FromIconv(const Converter &conv, const char *s, int &len) {
1174 if (conv) {
1175 char *utfForm = new char[len*3+1];
1176 char *pin = const_cast<char *>(s);
1177 size_t inLeft = len;
1178 char *pout = utfForm;
1179 size_t outLeft = len*3+1;
1180 size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1181 if (conversions != ((size_t)(-1))) {
1182 *pout = '\0';
1183 len = pout - utfForm;
1184 return utfForm;
1186 delete []utfForm;
1188 return 0;
1191 // Work out how many bytes are in a character by trying to convert using iconv,
1192 // returning the first length that succeeds.
1193 static size_t MultiByteLenFromIconv(const Converter &conv, const char *s, size_t len) {
1194 for (size_t lenMB=1; (lenMB<4) && (lenMB <= len); lenMB++) {
1195 char wcForm[2];
1196 char *pin = const_cast<char *>(s);
1197 size_t inLeft = lenMB;
1198 char *pout = wcForm;
1199 size_t outLeft = 2;
1200 size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1201 if (conversions != ((size_t)(-1))) {
1202 return lenMB;
1205 return 1;
1208 static char *UTF8FromGdkWChar(GdkWChar *wctext, int wclen) {
1209 char *utfForm = new char[wclen*3+1]; // Maximum of 3 UTF-8 bytes per character
1210 size_t lenU = 0;
1211 for (int i = 0; i < wclen && wctext[i]; i++) {
1212 unsigned int uch = wctext[i];
1213 if (uch < 0x80) {
1214 utfForm[lenU++] = static_cast<char>(uch);
1215 } else if (uch < 0x800) {
1216 utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
1217 utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1218 } else {
1219 utfForm[lenU++] = static_cast<char>(0xE0 | (uch >> 12));
1220 utfForm[lenU++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
1221 utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1224 utfForm[lenU] = '\0';
1225 return utfForm;
1228 static char *UTF8FromDBCS(const char *s, int &len) {
1229 GdkWChar *wctext = new GdkWChar[len + 1];
1230 GdkWChar *wcp = wctext;
1231 int wclen = gdk_mbstowcs(wcp, s, len);
1232 if (wclen < 1) {
1233 // In the annoying case when non-locale chars in the line.
1234 // e.g. latin1 chars in Japanese locale.
1235 delete []wctext;
1236 return 0;
1239 char *utfForm = UTF8FromGdkWChar(wctext, wclen);
1240 delete []wctext;
1241 len = strlen(utfForm);
1242 return utfForm;
1245 static size_t UTF8CharLength(const char *s) {
1246 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
1247 unsigned char ch = *us;
1248 if (ch < 0x80) {
1249 return 1;
1250 } else if (ch < 0x80 + 0x40 + 0x20) {
1251 return 2;
1252 } else {
1253 return 3;
1257 #endif
1259 // On GTK+, wchar_t is 4 bytes
1261 const int maxLengthTextRun = 10000;
1263 void SurfaceImpl::DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1264 ColourAllocated fore) {
1265 PenColour(fore);
1266 if (gc && drawable) {
1267 int xText = rc.left;
1268 #ifdef USE_PANGO
1269 if (PFont(font_)->pfd) {
1270 char *utfForm = 0;
1271 bool useGFree = false;
1272 if (et == UTF8) {
1273 pango_layout_set_text(layout, s, len);
1274 } else {
1275 if (!utfForm) {
1276 SetConverter(PFont(font_)->characterSet);
1277 utfForm = UTF8FromIconv(conv, s, len);
1279 if (!utfForm) { // iconv failed so try DBCS if DBCS mode
1280 if (et == dbcs) {
1281 // Convert to utf8
1282 utfForm = UTF8FromDBCS(s, len);
1285 if (!utfForm) { // iconv and DBCS failed so treat as Latin1
1286 utfForm = UTF8FromLatin1(s, len);
1288 pango_layout_set_text(layout, utfForm, len);
1290 pango_layout_set_font_description(layout, PFont(font_)->pfd);
1291 #ifdef PANGO_VERSION
1292 PangoLayoutLine *pll = pango_layout_get_line_readonly(layout,0);
1293 #else
1294 PangoLayoutLine *pll = pango_layout_get_line(layout,0);
1295 #endif
1296 gdk_draw_layout_line(drawable, gc, xText, ybase, pll);
1297 if (useGFree) {
1298 g_free(utfForm);
1299 } else {
1300 delete []utfForm;
1302 return;
1304 #endif
1305 // Draw text as a series of segments to avoid limitations in X servers
1306 const int segmentLength = 1000;
1307 bool draw8bit = true;
1308 if (et != singleByte) {
1309 GdkWChar wctext[maxLengthTextRun];
1310 if (len >= maxLengthTextRun)
1311 len = maxLengthTextRun-1;
1312 int wclen;
1313 if (et == UTF8) {
1314 wclen = UTF16FromUTF8(s, len,
1315 static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
1316 } else { // dbcs, so convert using current locale
1317 char sMeasure[maxLengthTextRun];
1318 memcpy(sMeasure, s, len);
1319 sMeasure[len] = '\0';
1320 wclen = gdk_mbstowcs(
1321 wctext, sMeasure, maxLengthTextRun - 1);
1323 if (wclen > 0) {
1324 draw8bit = false;
1325 wctext[wclen] = L'\0';
1326 GdkWChar *wcp = wctext;
1327 while ((wclen > 0) && (xText < maxCoordinate)) {
1328 int lenDraw = Platform::Minimum(wclen, segmentLength);
1329 gdk_draw_text_wc(drawable, PFont(font_)->pfont, gc,
1330 xText, ybase, wcp, lenDraw);
1331 wclen -= lenDraw;
1332 if (wclen > 0) { // Avoid next calculation if possible as may be expensive
1333 xText += gdk_text_width_wc(PFont(font_)->pfont,
1334 wcp, lenDraw);
1336 wcp += lenDraw;
1340 if (draw8bit) {
1341 while ((len > 0) && (xText < maxCoordinate)) {
1342 int lenDraw = Platform::Minimum(len, segmentLength);
1343 gdk_draw_text(drawable, PFont(font_)->pfont, gc,
1344 xText, ybase, s, lenDraw);
1345 len -= lenDraw;
1346 if (len > 0) { // Avoid next calculation if possible as may be expensive
1347 xText += gdk_text_width(PFont(font_)->pfont, s, lenDraw);
1349 s += lenDraw;
1355 void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1356 ColourAllocated fore, ColourAllocated back) {
1357 FillRectangle(rc, back);
1358 DrawTextBase(rc, font_, ybase, s, len, fore);
1361 // On GTK+, exactly same as DrawTextNoClip
1362 void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1363 ColourAllocated fore, ColourAllocated back) {
1364 FillRectangle(rc, back);
1365 DrawTextBase(rc, font_, ybase, s, len, fore);
1368 void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1369 ColourAllocated fore) {
1370 // Avoid drawing spaces in transparent mode
1371 for (int i=0;i<len;i++) {
1372 if (s[i] != ' ') {
1373 DrawTextBase(rc, font_, ybase, s, len, fore);
1374 return;
1379 #ifdef USE_PANGO
1381 class ClusterIterator {
1382 PangoLayoutIter *iter;
1383 PangoRectangle pos;
1384 int lenPositions;
1385 public:
1386 bool finished;
1387 int positionStart;
1388 int position;
1389 int distance;
1390 int curIndex;
1391 ClusterIterator(PangoLayout *layout, int len) : lenPositions(len), finished(false),
1392 positionStart(0), position(0), distance(0) {
1393 iter = pango_layout_get_iter(layout);
1394 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1396 ~ClusterIterator() {
1397 pango_layout_iter_free(iter);
1400 void Next() {
1401 positionStart = position;
1402 if (pango_layout_iter_next_cluster(iter)) {
1403 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1404 position = PANGO_PIXELS(pos.x);
1405 curIndex = pango_layout_iter_get_index(iter);
1406 } else {
1407 finished = true;
1408 position = PANGO_PIXELS(pos.x + pos.width);
1409 curIndex = lenPositions;
1411 distance = position - positionStart;
1415 #endif
1417 void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) {
1418 if (font_.GetID()) {
1419 int totalWidth = 0;
1420 #ifdef USE_PANGO
1421 const int lenPositions = len;
1422 if (PFont(font_)->pfd) {
1423 if (len == 1) {
1424 int width = PFont(font_)->CharWidth(*s, et);
1425 if (width) {
1426 positions[0] = width;
1427 return;
1430 pango_layout_set_font_description(layout, PFont(font_)->pfd);
1431 if (et == UTF8) {
1432 // Simple and direct as UTF-8 is native Pango encoding
1433 int i = 0;
1434 pango_layout_set_text(layout, s, len);
1435 ClusterIterator iti(layout, lenPositions);
1436 while (!iti.finished) {
1437 iti.Next();
1438 int places = iti.curIndex - i;
1439 while (i < iti.curIndex) {
1440 // Evenly distribute space among bytes of this cluster.
1441 // Would be better to find number of characters and then
1442 // divide evenly between characters with each byte of a character
1443 // being at the same position.
1444 positions[i] = iti.position - (iti.curIndex - 1 - i) * iti.distance / places;
1445 i++;
1448 PLATFORM_ASSERT(i == lenPositions);
1449 } else {
1450 int positionsCalculated = 0;
1451 if (et == dbcs) {
1452 SetConverter(PFont(font_)->characterSet);
1453 char *utfForm = UTF8FromIconv(conv, s, len);
1454 if (utfForm) {
1455 // Convert to UTF-8 so can ask Pango for widths, then
1456 // Loop through UTF-8 and DBCS forms, taking account of different
1457 // character byte lengths.
1458 Converter convMeasure("UCS-2", CharacterSetID(characterSet), false);
1459 pango_layout_set_text(layout, utfForm, strlen(utfForm));
1460 int i = 0;
1461 int clusterStart = 0;
1462 ClusterIterator iti(layout, strlen(utfForm));
1463 while (!iti.finished) {
1464 iti.Next();
1465 int clusterEnd = iti.curIndex;
1466 int places = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
1467 int place = 1;
1468 while (clusterStart < clusterEnd) {
1469 size_t lenChar = MultiByteLenFromIconv(convMeasure, s+i, len-i);
1470 while (lenChar--) {
1471 positions[i++] = iti.position - (places - place) * iti.distance / places;
1472 positionsCalculated++;
1474 clusterStart += UTF8CharLength(utfForm+clusterStart);
1475 place++;
1478 delete []utfForm;
1479 PLATFORM_ASSERT(i == lenPositions);
1482 if (positionsCalculated < 1 ) {
1483 // Either Latin1 or DBCS conversion failed so treat as Latin1.
1484 bool useGFree = false;
1485 SetConverter(PFont(font_)->characterSet);
1486 char *utfForm = UTF8FromIconv(conv, s, len);
1487 if (!utfForm) {
1488 utfForm = UTF8FromLatin1(s, len);
1490 pango_layout_set_text(layout, utfForm, len);
1491 int i = 0;
1492 int clusterStart = 0;
1493 // Each Latin1 input character may take 1 or 2 bytes in UTF-8
1494 // and groups of up to 3 may be represented as ligatures.
1495 ClusterIterator iti(layout, strlen(utfForm));
1496 while (!iti.finished) {
1497 iti.Next();
1498 int clusterEnd = iti.curIndex;
1499 int ligatureLength = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
1500 PLATFORM_ASSERT(ligatureLength > 0 && ligatureLength <= 3);
1501 for (int charInLig=0; charInLig<ligatureLength; charInLig++) {
1502 positions[i++] = iti.position - (ligatureLength - 1 - charInLig) * iti.distance / ligatureLength;
1504 clusterStart = clusterEnd;
1506 if (useGFree) {
1507 g_free(utfForm);
1508 } else {
1509 delete []utfForm;
1511 PLATFORM_ASSERT(i == lenPositions);
1514 if (len == 1) {
1515 PFont(font_)->SetCharWidth(*s, positions[0], et);
1517 return;
1519 #endif
1520 GdkFont *gf = PFont(font_)->pfont;
1521 bool measure8bit = true;
1522 if (et != singleByte) {
1523 GdkWChar wctext[maxLengthTextRun];
1524 if (len >= maxLengthTextRun)
1525 len = maxLengthTextRun-1;
1526 int wclen;
1527 if (et == UTF8) {
1528 wclen = UTF16FromUTF8(s, len,
1529 static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
1530 } else { // dbcsMode, so convert using current locale
1531 char sDraw[maxLengthTextRun];
1532 memcpy(sDraw, s, len);
1533 sDraw[len] = '\0';
1534 wclen = gdk_mbstowcs(
1535 wctext, sDraw, maxLengthTextRun - 1);
1537 if (wclen > 0) {
1538 measure8bit = false;
1539 wctext[wclen] = L'\0';
1540 // Map widths back to utf-8 or DBCS input string
1541 int i = 0;
1542 for (int iU = 0; iU < wclen; iU++) {
1543 int width = gdk_char_width_wc(gf, wctext[iU]);
1544 totalWidth += width;
1545 int lenChar;
1546 if (et == UTF8) {
1547 lenChar = UTF8Len(s[i]);
1548 } else {
1549 lenChar = mblen(s+i, MB_CUR_MAX);
1550 if (lenChar < 0)
1551 lenChar = 1;
1553 while (lenChar--) {
1554 positions[i++] = totalWidth;
1557 while (i < len) { // In case of problems with lengths
1558 positions[i++] = totalWidth;
1562 if (measure8bit) {
1563 // Either Latin1 or conversion failed so treat as Latin1.
1564 for (int i = 0; i < len; i++) {
1565 int width = gdk_char_width(gf, s[i]);
1566 totalWidth += width;
1567 positions[i] = totalWidth;
1570 } else {
1571 // No font so return an ascending range of values
1572 for (int i = 0; i < len; i++) {
1573 positions[i] = i + 1;
1578 int SurfaceImpl::WidthText(Font &font_, const char *s, int len) {
1579 if (font_.GetID()) {
1580 #ifdef USE_PANGO
1581 if (PFont(font_)->pfd) {
1582 char *utfForm = 0;
1583 pango_layout_set_font_description(layout, PFont(font_)->pfd);
1584 PangoRectangle pos;
1585 bool useGFree = false;
1586 if (et == UTF8) {
1587 pango_layout_set_text(layout, s, len);
1588 } else {
1589 if (et == dbcs) {
1590 // Convert to utf8
1591 utfForm = UTF8FromDBCS(s, len);
1593 if (!utfForm) { // DBCS failed so treat as iconv
1594 SetConverter(PFont(font_)->characterSet);
1595 utfForm = UTF8FromIconv(conv, s, len);
1597 if (!utfForm) { // g_locale_to_utf8 failed so treat as Latin1
1598 utfForm = UTF8FromLatin1(s, len);
1600 pango_layout_set_text(layout, utfForm, len);
1602 #ifdef PANGO_VERSION
1603 PangoLayoutLine *pangoLine = pango_layout_get_line_readonly(layout,0);
1604 #else
1605 PangoLayoutLine *pangoLine = pango_layout_get_line(layout,0);
1606 #endif
1607 pango_layout_line_get_extents(pangoLine, NULL, &pos);
1608 if (useGFree) {
1609 g_free(utfForm);
1610 } else {
1611 delete []utfForm;
1613 return PANGO_PIXELS(pos.width);
1615 #endif
1616 if (et == UTF8) {
1617 GdkWChar wctext[maxLengthTextRun];
1618 size_t wclen = UTF16FromUTF8(s, len, static_cast<wchar_t *>(static_cast<void *>(wctext)),
1619 sizeof(wctext) / sizeof(GdkWChar) - 1);
1620 wctext[wclen] = L'\0';
1621 return gdk_text_width_wc(PFont(font_)->pfont, wctext, wclen);
1622 } else {
1623 return gdk_text_width(PFont(font_)->pfont, s, len);
1625 } else {
1626 return 1;
1630 int SurfaceImpl::WidthChar(Font &font_, char ch) {
1631 if (font_.GetID()) {
1632 #ifdef USE_PANGO
1633 if (PFont(font_)->pfd) {
1634 return WidthText(font_, &ch, 1);
1636 #endif
1637 return gdk_char_width(PFont(font_)->pfont, ch);
1638 } else {
1639 return 1;
1643 // Three possible strategies for determining ascent and descent of font:
1644 // 1) Call gdk_string_extents with string containing all letters, numbers and punctuation.
1645 // 2) Use the ascent and descent fields of GdkFont.
1646 // 3) Call gdk_string_extents with string as 1 but also including accented capitals.
1647 // Smallest values given by 1 and largest by 3 with 2 in between.
1648 // Techniques 1 and 2 sometimes chop off extreme portions of ascenders and
1649 // descenders but are mostly OK except for accented characters like Ã… which are
1650 // rarely used in code.
1652 // This string contains a good range of characters to test for size.
1653 //const char largeSizeString[] = "ÂÃÅÄ `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1654 // "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1655 #ifndef FAST_WAY
1656 const char sizeString[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1657 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1658 #endif
1660 int SurfaceImpl::Ascent(Font &font_) {
1661 if (!(font_.GetID()))
1662 return 1;
1663 #ifdef FAST_WAY
1664 FontMutexLock();
1665 int ascent = PFont(font_)->ascent;
1666 #ifdef USE_PANGO
1667 if ((ascent == 0) && (PFont(font_)->pfd)) {
1668 PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
1669 PFont(font_)->pfd, pango_context_get_language(pcontext));
1670 PFont(font_)->ascent =
1671 PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
1672 pango_font_metrics_unref(metrics);
1673 ascent = PFont(font_)->ascent;
1675 #endif
1676 if ((ascent == 0) && (PFont(font_)->pfont)) {
1677 ascent = PFont(font_)->pfont->ascent;
1679 if (ascent == 0) {
1680 ascent = 1;
1682 FontMutexUnlock();
1683 return ascent;
1684 #else
1686 gint lbearing;
1687 gint rbearing;
1688 gint width;
1689 gint ascent;
1690 gint descent;
1692 gdk_string_extents(PFont(font_)->pfont, sizeString,
1693 &lbearing, &rbearing, &width, &ascent, &descent);
1694 return ascent;
1695 #endif
1698 int SurfaceImpl::Descent(Font &font_) {
1699 if (!(font_.GetID()))
1700 return 1;
1701 #ifdef FAST_WAY
1703 #ifdef USE_PANGO
1704 if (PFont(font_)->pfd) {
1705 PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
1706 PFont(font_)->pfd, pango_context_get_language(pcontext));
1707 int descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
1708 pango_font_metrics_unref(metrics);
1709 return descent;
1711 #endif
1712 return PFont(font_)->pfont->descent;
1713 #else
1715 gint lbearing;
1716 gint rbearing;
1717 gint width;
1718 gint ascent;
1719 gint descent;
1721 gdk_string_extents(PFont(font_)->pfont, sizeString,
1722 &lbearing, &rbearing, &width, &ascent, &descent);
1723 return descent;
1724 #endif
1727 int SurfaceImpl::InternalLeading(Font &) {
1728 return 0;
1731 int SurfaceImpl::ExternalLeading(Font &) {
1732 return 0;
1735 int SurfaceImpl::Height(Font &font_) {
1736 return Ascent(font_) + Descent(font_);
1739 int SurfaceImpl::AverageCharWidth(Font &font_) {
1740 return WidthChar(font_, 'n');
1743 int SurfaceImpl::SetPalette(Palette *, bool) {
1744 // Handled in palette allocation for GTK so this does nothing
1745 return 0;
1748 void SurfaceImpl::SetClip(PRectangle rc) {
1749 GdkRectangle area = {rc.left, rc.top,
1750 rc.right - rc.left, rc.bottom - rc.top};
1751 gdk_gc_set_clip_rectangle(gc, &area);
1754 void SurfaceImpl::FlushCachedState() {}
1756 void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) {
1757 if (unicodeMode_)
1758 et = UTF8;
1761 void SurfaceImpl::SetDBCSMode(int codePage) {
1762 if (codePage && (codePage != SC_CP_UTF8))
1763 et = dbcs;
1766 Surface *Surface::Allocate() {
1767 return new SurfaceImpl;
1770 Window::~Window() {}
1772 void Window::Destroy() {
1773 if (wid)
1774 gtk_widget_destroy(GTK_WIDGET(wid));
1775 wid = 0;
1778 bool Window::HasFocus() {
1779 return GTK_WIDGET_HAS_FOCUS(wid);
1782 PRectangle Window::GetPosition() {
1783 // Before any size allocated pretend its 1000 wide so not scrolled
1784 PRectangle rc(0, 0, 1000, 1000);
1785 if (wid) {
1786 rc.left = PWidget(wid)->allocation.x;
1787 rc.top = PWidget(wid)->allocation.y;
1788 if (PWidget(wid)->allocation.width > 20) {
1789 rc.right = rc.left + PWidget(wid)->allocation.width;
1790 rc.bottom = rc.top + PWidget(wid)->allocation.height;
1793 return rc;
1796 void Window::SetPosition(PRectangle rc) {
1797 #if 1
1798 GtkAllocation alloc;
1799 alloc.x = rc.left;
1800 alloc.y = rc.top;
1801 alloc.width = rc.Width();
1802 alloc.height = rc.Height();
1803 gtk_widget_size_allocate(PWidget(wid), &alloc);
1804 #else
1806 gtk_widget_set_uposition(wid, rc.left, rc.top);
1807 gtk_widget_set_usize(wid, rc.right - rc.left, rc.bottom - rc.top);
1808 #endif
1811 void Window::SetPositionRelative(PRectangle rc, Window relativeTo) {
1812 int ox = 0;
1813 int oy = 0;
1814 gdk_window_get_origin(PWidget(relativeTo.wid)->window, &ox, &oy);
1815 ox += rc.left;
1816 if (ox < 0)
1817 ox = 0;
1818 oy += rc.top;
1819 if (oy < 0)
1820 oy = 0;
1822 /* do some corrections to fit into screen */
1823 int sizex = rc.right - rc.left;
1824 int sizey = rc.bottom - rc.top;
1825 int screenWidth = gdk_screen_width();
1826 int screenHeight = gdk_screen_height();
1827 if (sizex > screenWidth)
1828 ox = 0; /* the best we can do */
1829 else if (ox + sizex > screenWidth)
1830 ox = screenWidth - sizex;
1831 if (oy + sizey > screenHeight)
1832 oy = screenHeight - sizey;
1834 #if GTK_MAJOR_VERSION >= 2
1835 gtk_window_move(GTK_WINDOW(PWidget(wid)), ox, oy);
1836 #else
1837 gtk_widget_set_uposition(PWidget(wid), ox, oy);
1838 #endif
1840 #if 0
1842 GtkAllocation alloc;
1843 alloc.x = rc.left + ox;
1844 alloc.y = rc.top + oy;
1845 alloc.width = rc.right - rc.left;
1846 alloc.height = rc.bottom - rc.top;
1847 gtk_widget_size_allocate(wid, &alloc);
1848 #endif
1849 gtk_widget_set_usize(PWidget(wid), sizex, sizey);
1852 PRectangle Window::GetClientPosition() {
1853 // On GTK+, the client position is the window position
1854 return GetPosition();
1857 void Window::Show(bool show) {
1858 if (show)
1859 gtk_widget_show(PWidget(wid));
1862 void Window::InvalidateAll() {
1863 if (wid) {
1864 gtk_widget_queue_draw(PWidget(wid));
1868 void Window::InvalidateRectangle(PRectangle rc) {
1869 if (wid) {
1870 gtk_widget_queue_draw_area(PWidget(wid),
1871 rc.left, rc.top,
1872 rc.right - rc.left, rc.bottom - rc.top);
1876 void Window::SetFont(Font &) {
1877 // Can not be done generically but only needed for ListBox
1880 void Window::SetCursor(Cursor curs) {
1881 // We don't set the cursor to same value numerous times under gtk because
1882 // it stores the cursor in the window once it's set
1883 if (curs == cursorLast)
1884 return;
1886 cursorLast = curs;
1887 GdkCursor *gdkCurs;
1888 switch (curs) {
1889 case cursorText:
1890 gdkCurs = gdk_cursor_new(GDK_XTERM);
1891 break;
1892 case cursorArrow:
1893 gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
1894 break;
1895 case cursorUp:
1896 gdkCurs = gdk_cursor_new(GDK_CENTER_PTR);
1897 break;
1898 case cursorWait:
1899 gdkCurs = gdk_cursor_new(GDK_WATCH);
1900 break;
1901 case cursorHand:
1902 gdkCurs = gdk_cursor_new(GDK_HAND2);
1903 break;
1904 case cursorReverseArrow:
1905 gdkCurs = gdk_cursor_new(GDK_RIGHT_PTR);
1906 break;
1907 default:
1908 gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
1909 cursorLast = cursorArrow;
1910 break;
1913 if (PWidget(wid)->window)
1914 gdk_window_set_cursor(PWidget(wid)->window, gdkCurs);
1915 gdk_cursor_destroy(gdkCurs);
1918 void Window::SetTitle(const char *s) {
1919 gtk_window_set_title(GTK_WINDOW(wid), s);
1922 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
1923 gdk window coordinates */
1924 PRectangle Window::GetMonitorRect(Point pt) {
1925 gint x_offset, y_offset;
1926 pt = pt;
1928 gdk_window_get_origin(PWidget(wid)->window, &x_offset, &y_offset);
1930 // gtk 2.2+
1931 #if GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2)
1933 GdkScreen* screen;
1934 gint monitor_num;
1935 GdkRectangle rect;
1937 screen = gtk_widget_get_screen(PWidget(wid));
1938 monitor_num = gdk_screen_get_monitor_at_point(screen, pt.x + x_offset, pt.y + y_offset);
1939 gdk_screen_get_monitor_geometry(screen, monitor_num, &rect);
1940 rect.x -= x_offset;
1941 rect.y -= y_offset;
1942 return PRectangle(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
1944 #else
1945 return PRectangle(-x_offset, -y_offset, (-x_offset) + gdk_screen_width(),
1946 (-y_offset) + gdk_screen_height());
1947 #endif
1950 struct ListImage {
1951 const char *xpm_data;
1952 #if GTK_MAJOR_VERSION < 2
1953 GdkPixmap *pixmap;
1954 GdkBitmap *bitmap;
1955 #else
1956 GdkPixbuf *pixbuf;
1957 #endif
1960 static void list_image_free(gpointer, gpointer value, gpointer) {
1961 ListImage *list_image = (ListImage *) value;
1962 #if GTK_MAJOR_VERSION < 2
1963 if (list_image->pixmap)
1964 gdk_pixmap_unref(list_image->pixmap);
1965 if (list_image->bitmap)
1966 gdk_bitmap_unref(list_image->bitmap);
1967 #else
1968 if (list_image->pixbuf)
1969 gdk_pixbuf_unref (list_image->pixbuf);
1970 #endif
1971 g_free(list_image);
1974 ListBox::ListBox() {
1977 ListBox::~ListBox() {
1980 #if GTK_MAJOR_VERSION >= 2
1981 enum {
1982 PIXBUF_COLUMN,
1983 TEXT_COLUMN,
1984 N_COLUMNS
1986 #endif
1988 class ListBoxX : public ListBox {
1989 WindowID list;
1990 WindowID scroller;
1991 #if GTK_MAJOR_VERSION < 2
1992 int current;
1993 #endif
1994 void *pixhash;
1995 #if GTK_MAJOR_VERSION >= 2
1996 GtkCellRenderer* pixbuf_renderer;
1997 #endif
1998 XPMSet xset;
1999 int desiredVisibleRows;
2000 unsigned int maxItemCharacters;
2001 unsigned int aveCharWidth;
2002 public:
2003 CallBackAction doubleClickAction;
2004 void *doubleClickActionData;
2006 ListBoxX() : list(0), pixhash(NULL),
2007 desiredVisibleRows(5), maxItemCharacters(0),
2008 aveCharWidth(1), doubleClickAction(NULL), doubleClickActionData(NULL) {
2009 #if GTK_MAJOR_VERSION < 2
2010 current = 0;
2011 #endif
2012 #if GTK_MAJOR_VERSION >= 2
2013 pixbuf_renderer = 0;
2014 #endif
2016 virtual ~ListBoxX() {
2017 if (pixhash) {
2018 g_hash_table_foreach((GHashTable *) pixhash, list_image_free, NULL);
2019 g_hash_table_destroy((GHashTable *) pixhash);
2022 virtual void SetFont(Font &font);
2023 virtual void Create(Window &parent, int ctrlID, Point location_, int lineHeight_, bool unicodeMode_);
2024 virtual void SetAverageCharWidth(int width);
2025 virtual void SetVisibleRows(int rows);
2026 virtual int GetVisibleRows() const;
2027 virtual PRectangle GetDesiredRect();
2028 virtual int CaretFromEdge();
2029 virtual void Clear();
2030 virtual void Append(char *s, int type = -1);
2031 virtual int Length();
2032 virtual void Select(int n);
2033 virtual int GetSelection();
2034 virtual int Find(const char *prefix);
2035 virtual void GetValue(int n, char *value, int len);
2036 virtual void RegisterImage(int type, const char *xpm_data);
2037 virtual void ClearRegisteredImages();
2038 virtual void SetDoubleClickAction(CallBackAction action, void *data) {
2039 doubleClickAction = action;
2040 doubleClickActionData = data;
2042 virtual void SetList(const char *listText, char separator, char typesep);
2045 ListBox *ListBox::Allocate() {
2046 ListBoxX *lb = new ListBoxX();
2047 return lb;
2050 #if GTK_MAJOR_VERSION < 2
2051 static void UnselectionAC(GtkWidget *, gint, gint,
2052 GdkEventButton *, gpointer p) {
2053 int *pi = reinterpret_cast<int *>(p);
2054 *pi = -1;
2056 static void SelectionAC(GtkWidget *, gint row, gint,
2057 GdkEventButton *, gpointer p) {
2058 int *pi = reinterpret_cast<int *>(p);
2059 *pi = row;
2061 #endif
2063 static gboolean ButtonPress(GtkWidget *, GdkEventButton* ev, gpointer p) {
2064 try {
2065 ListBoxX* lb = reinterpret_cast<ListBoxX*>(p);
2066 if (ev->type == GDK_2BUTTON_PRESS && lb->doubleClickAction != NULL) {
2067 lb->doubleClickAction(lb->doubleClickActionData);
2068 return TRUE;
2071 } catch (...) {
2072 // No pointer back to Scintilla to save status
2074 return FALSE;
2077 #if GTK_MAJOR_VERSION >= 2
2078 /* Change the active color to the selected color so the listbox uses the color
2079 scheme that it would use if it had the focus. */
2080 static void StyleSet(GtkWidget *w, GtkStyle*, void*) {
2081 GtkStyle* style;
2083 g_return_if_fail(w != NULL);
2085 /* Copy the selected color to active. Note that the modify calls will cause
2086 recursive calls to this function after the value is updated and w->style to
2087 be set to a new object */
2088 style = gtk_widget_get_style(w);
2089 if (style == NULL)
2090 return;
2091 if (!gdk_color_equal(&style->base[GTK_STATE_SELECTED], &style->base[GTK_STATE_ACTIVE]))
2092 gtk_widget_modify_base(w, GTK_STATE_ACTIVE, &style->base[GTK_STATE_SELECTED]);
2094 style = gtk_widget_get_style(w);
2095 if (style == NULL)
2096 return;
2097 if (!gdk_color_equal(&style->text[GTK_STATE_SELECTED], &style->text[GTK_STATE_ACTIVE]))
2098 gtk_widget_modify_text(w, GTK_STATE_ACTIVE, &style->text[GTK_STATE_SELECTED]);
2100 #endif
2102 void ListBoxX::Create(Window &, int, Point, int, bool) {
2103 wid = gtk_window_new(GTK_WINDOW_POPUP);
2105 GtkWidget *frame = gtk_frame_new(NULL);
2106 gtk_widget_show(frame);
2107 gtk_container_add(GTK_CONTAINER(GetID()), frame);
2108 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
2109 gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
2111 scroller = gtk_scrolled_window_new(NULL, NULL);
2112 gtk_container_set_border_width(GTK_CONTAINER(scroller), 0);
2113 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
2114 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2115 gtk_container_add(GTK_CONTAINER(frame), PWidget(scroller));
2116 gtk_widget_show(PWidget(scroller));
2118 #if GTK_MAJOR_VERSION < 2
2119 list = gtk_clist_new(1);
2120 GtkWidget *wid = PWidget(list); // No code inside the GTK_OBJECT macro
2121 gtk_widget_show(wid);
2122 gtk_container_add(GTK_CONTAINER(PWidget(scroller)), wid);
2123 gtk_clist_set_column_auto_resize(GTK_CLIST(wid), 0, TRUE);
2124 gtk_clist_set_selection_mode(GTK_CLIST(wid), GTK_SELECTION_BROWSE);
2125 gtk_signal_connect(GTK_OBJECT(wid), "unselect_row",
2126 GTK_SIGNAL_FUNC(UnselectionAC), &current);
2127 gtk_signal_connect(GTK_OBJECT(wid), "select_row",
2128 GTK_SIGNAL_FUNC(SelectionAC), &current);
2129 gtk_signal_connect(GTK_OBJECT(wid), "button_press_event",
2130 GTK_SIGNAL_FUNC(ButtonPress), this);
2131 gtk_clist_set_shadow_type(GTK_CLIST(wid), GTK_SHADOW_NONE);
2132 #else
2133 /* Tree and its model */
2134 GtkListStore *store =
2135 gtk_list_store_new(N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING);
2137 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2138 g_signal_connect(G_OBJECT(list), "style-set", G_CALLBACK(StyleSet), NULL);
2140 GtkTreeSelection *selection =
2141 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2142 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
2143 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
2144 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list), FALSE);
2146 /* Columns */
2147 GtkTreeViewColumn *column = gtk_tree_view_column_new();
2148 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2149 gtk_tree_view_column_set_title(column, "Autocomplete");
2151 pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
2152 gtk_cell_renderer_set_fixed_size(pixbuf_renderer, 0, -1);
2153 gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE);
2154 gtk_tree_view_column_add_attribute(column, pixbuf_renderer,
2155 "pixbuf", PIXBUF_COLUMN);
2157 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
2158 gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), 1);
2159 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2160 gtk_tree_view_column_add_attribute(column, renderer,
2161 "text", TEXT_COLUMN);
2163 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
2164 if (g_object_class_find_property(G_OBJECT_GET_CLASS(list), "fixed-height-mode"))
2165 g_object_set(G_OBJECT(list), "fixed-height-mode", TRUE, NULL);
2167 GtkWidget *wid = PWidget(list); // No code inside the G_OBJECT macro
2168 gtk_container_add(GTK_CONTAINER(PWidget(scroller)), wid);
2169 gtk_widget_show(wid);
2170 g_signal_connect(G_OBJECT(wid), "button_press_event",
2171 G_CALLBACK(ButtonPress), this);
2172 #endif
2173 gtk_widget_realize(PWidget(wid));
2176 void ListBoxX::SetFont(Font &scint_font) {
2177 #if GTK_MAJOR_VERSION < 2
2178 GtkStyle *style = gtk_widget_get_style(GTK_WIDGET(PWidget(list)));
2179 if (!gdk_font_equal(style->font, PFont(scint_font)->pfont)) {
2180 style = gtk_style_copy(style);
2181 gdk_font_unref(style->font);
2182 style->font = PFont(scint_font)->pfont;
2183 gdk_font_ref(style->font);
2184 gtk_widget_set_style(GTK_WIDGET(PWidget(list)), style);
2185 gtk_style_unref(style);
2187 #else
2188 // Only do for Pango font as there have been crashes for GDK fonts
2189 if (Created() && PFont(scint_font)->pfd) {
2190 // Current font is Pango font
2191 gtk_widget_modify_font(PWidget(list), PFont(scint_font)->pfd);
2193 #endif
2196 void ListBoxX::SetAverageCharWidth(int width) {
2197 aveCharWidth = width;
2200 void ListBoxX::SetVisibleRows(int rows) {
2201 desiredVisibleRows = rows;
2204 int ListBoxX::GetVisibleRows() const {
2205 return desiredVisibleRows;
2208 PRectangle ListBoxX::GetDesiredRect() {
2209 // Before any size allocated pretend its 100 wide so not scrolled
2210 PRectangle rc(0, 0, 100, 100);
2211 if (wid) {
2212 int rows = Length();
2213 if ((rows == 0) || (rows > desiredVisibleRows))
2214 rows = desiredVisibleRows;
2216 GtkRequisition req;
2217 int height;
2219 // First calculate height of the clist for our desired visible
2220 // row count otherwise it tries to expand to the total # of rows
2221 #if GTK_MAJOR_VERSION < 2
2222 int ythickness = PWidget(list)->style->klass->ythickness;
2223 height = (rows * GTK_CLIST(list)->row_height
2224 + rows + 1
2225 + 2 * (ythickness
2226 + GTK_CONTAINER(PWidget(list))->border_width));
2227 #else
2228 // Get cell height
2229 int row_width=0;
2230 int row_height=0;
2231 GtkTreeViewColumn * column =
2232 gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
2233 gtk_tree_view_column_cell_get_size(column, NULL,
2234 NULL, NULL, &row_width, &row_height);
2235 int ythickness = PWidget(list)->style->ythickness;
2236 height = (rows * row_height
2237 + 2 * (ythickness
2238 + GTK_CONTAINER(PWidget(list))->border_width + 1));
2239 #endif
2240 gtk_widget_set_usize(GTK_WIDGET(PWidget(list)), -1, height);
2242 // Get the size of the scroller because we set usize on the window
2243 gtk_widget_size_request(GTK_WIDGET(scroller), &req);
2244 rc.right = req.width;
2245 rc.bottom = req.height;
2247 gtk_widget_set_usize(GTK_WIDGET(list), -1, -1);
2248 int width = maxItemCharacters;
2249 if (width < 12)
2250 width = 12;
2251 rc.right = width * (aveCharWidth + aveCharWidth / 3);
2252 if (Length() > rows)
2253 rc.right = rc.right + 16;
2255 return rc;
2258 int ListBoxX::CaretFromEdge() {
2259 #if GTK_MAJOR_VERSION >= 2
2260 gint renderer_width, renderer_height;
2261 gtk_cell_renderer_get_fixed_size(pixbuf_renderer, &renderer_width,
2262 &renderer_height);
2263 return 4 + renderer_width;
2264 #endif
2265 return 4 + xset.GetWidth();
2268 void ListBoxX::Clear() {
2269 #if GTK_MAJOR_VERSION < 2
2270 gtk_clist_clear(GTK_CLIST(list));
2271 #else
2272 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2273 gtk_list_store_clear(GTK_LIST_STORE(model));
2274 #endif
2275 maxItemCharacters = 0;
2278 #if GTK_MAJOR_VERSION < 2
2279 static void init_pixmap(ListImage *list_image, GtkWidget *window) {
2280 #else
2281 static void init_pixmap(ListImage *list_image) {
2282 #endif
2283 const char *textForm = list_image->xpm_data;
2284 const char * const * xpm_lineform = reinterpret_cast<const char * const *>(textForm);
2285 const char **xpm_lineformfromtext = 0;
2286 // The XPM data can be either in atext form as will be read from a file
2287 // or in a line form (array of char *) as will be used for images defined in code.
2288 // Test for text form and convert to line form
2289 if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
2290 // Test done is two parts to avoid possibility of overstepping the memory
2291 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
2292 xpm_lineformfromtext = XPM::LinesFormFromTextForm(textForm);
2293 xpm_lineform = xpm_lineformfromtext;
2296 // Drop any existing pixmap/bitmap as data may have changed
2297 #if GTK_MAJOR_VERSION < 2
2298 if (list_image->pixmap)
2299 gdk_pixmap_unref(list_image->pixmap);
2300 list_image->pixmap = NULL;
2301 if (list_image->bitmap)
2302 gdk_bitmap_unref(list_image->bitmap);
2303 list_image->bitmap = NULL;
2305 list_image->pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL
2306 , gtk_widget_get_colormap(window), &(list_image->bitmap), NULL
2307 , (gchar **) xpm_lineform);
2308 if (NULL == list_image->pixmap) {
2309 if (list_image->bitmap)
2310 gdk_bitmap_unref(list_image->bitmap);
2311 list_image->bitmap = NULL;
2313 #else
2314 if (list_image->pixbuf)
2315 gdk_pixbuf_unref(list_image->pixbuf);
2316 list_image->pixbuf =
2317 gdk_pixbuf_new_from_xpm_data((const gchar**)xpm_lineform);
2318 #endif
2319 delete []xpm_lineformfromtext;
2322 #define SPACING 5
2324 void ListBoxX::Append(char *s, int type) {
2325 ListImage *list_image = NULL;
2326 if ((type >= 0) && pixhash) {
2327 list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash
2328 , (gconstpointer) GINT_TO_POINTER(type));
2330 #if GTK_MAJOR_VERSION < 2
2331 char * szs[] = { s, NULL };
2332 int rownum = gtk_clist_append(GTK_CLIST(list), szs);
2333 if (list_image) {
2334 if (NULL == list_image->pixmap)
2335 init_pixmap(list_image, (GtkWidget *) list);
2336 gtk_clist_set_pixtext(GTK_CLIST(list), rownum, 0, s, SPACING
2337 , list_image->pixmap, list_image->bitmap);
2339 #else
2340 GtkTreeIter iter;
2341 GtkListStore *store =
2342 GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
2343 gtk_list_store_append(GTK_LIST_STORE(store), &iter);
2344 if (list_image) {
2345 if (NULL == list_image->pixbuf)
2346 init_pixmap(list_image);
2347 if (list_image->pixbuf) {
2348 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2349 PIXBUF_COLUMN, list_image->pixbuf,
2350 TEXT_COLUMN, s, -1);
2352 gint pixbuf_width = gdk_pixbuf_get_width(list_image->pixbuf);
2353 gint renderer_height, renderer_width;
2354 gtk_cell_renderer_get_fixed_size(pixbuf_renderer,
2355 &renderer_width, &renderer_height);
2356 if (pixbuf_width > renderer_width)
2357 gtk_cell_renderer_set_fixed_size(pixbuf_renderer,
2358 pixbuf_width, -1);
2359 } else {
2360 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2361 TEXT_COLUMN, s, -1);
2363 } else {
2364 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2365 TEXT_COLUMN, s, -1);
2367 #endif
2368 size_t len = strlen(s);
2369 if (maxItemCharacters < len)
2370 maxItemCharacters = len;
2373 int ListBoxX::Length() {
2374 if (wid)
2375 #if GTK_MAJOR_VERSION < 2
2376 return GTK_CLIST(list)->rows;
2377 #else
2378 return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
2379 (GTK_TREE_VIEW(list)), NULL);
2380 #endif
2381 return 0;
2384 void ListBoxX::Select(int n) {
2385 #if GTK_MAJOR_VERSION < 2
2386 if (n == -1) {
2387 gtk_clist_unselect_row(GTK_CLIST(list), current, 0);
2388 } else {
2389 gtk_clist_select_row(GTK_CLIST(list), n, 0);
2390 gtk_clist_moveto(GTK_CLIST(list), n, 0, 0.5, 0.5);
2392 #else
2393 GtkTreeIter iter;
2394 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2395 GtkTreeSelection *selection =
2396 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2398 if (n < 0) {
2399 gtk_tree_selection_unselect_all(selection);
2400 return;
2403 bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
2404 if (valid) {
2405 gtk_tree_selection_select_iter(selection, &iter);
2407 // Move the scrollbar to show the selection.
2408 int total = Length();
2409 GtkAdjustment *adj =
2410 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list));
2411 gfloat value = ((gfloat)n / total) * (adj->upper - adj->lower)
2412 + adj->lower - adj->page_size / 2;
2414 // Get cell height
2415 int row_width;
2416 int row_height;
2417 GtkTreeViewColumn * column =
2418 gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
2419 gtk_tree_view_column_cell_get_size(column, NULL, NULL,
2420 NULL, &row_width, &row_height);
2422 int rows = Length();
2423 if ((rows == 0) || (rows > desiredVisibleRows))
2424 rows = desiredVisibleRows;
2425 if (rows & 0x1) {
2426 // Odd rows to display -- We are now in the middle.
2427 // Align it so that we don't chop off rows.
2428 value += (gfloat)row_height / 2.0;
2430 // Clamp it.
2431 value = (value < 0)? 0 : value;
2432 value = (value > (adj->upper - adj->page_size))?
2433 (adj->upper - adj->page_size) : value;
2435 // Set it.
2436 gtk_adjustment_set_value(adj, value);
2437 } else {
2438 gtk_tree_selection_unselect_all(selection);
2440 #endif
2443 int ListBoxX::GetSelection() {
2444 #if GTK_MAJOR_VERSION < 2
2445 return current;
2446 #else
2447 GtkTreeIter iter;
2448 GtkTreeModel *model;
2449 GtkTreeSelection *selection;
2450 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2451 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
2452 GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
2453 int *indices = gtk_tree_path_get_indices(path);
2454 // Don't free indices.
2455 if (indices)
2456 return indices[0];
2458 return -1;
2459 #endif
2462 int ListBoxX::Find(const char *prefix) {
2463 #if GTK_MAJOR_VERSION < 2
2464 int count = Length();
2465 for (int i = 0; i < count; i++) {
2466 char *s = 0;
2467 gtk_clist_get_text(GTK_CLIST(list), i, 0, &s);
2468 if (s && (0 == strncmp(prefix, s, strlen(prefix)))) {
2469 return i;
2472 #else
2473 GtkTreeIter iter;
2474 GtkTreeModel *model =
2475 gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2476 bool valid = gtk_tree_model_get_iter_first(model, &iter) != FALSE;
2477 int i = 0;
2478 while(valid) {
2479 gchar *s;
2480 gtk_tree_model_get(model, &iter, TEXT_COLUMN, &s, -1);
2481 if (s && (0 == strncmp(prefix, s, strlen(prefix)))) {
2482 return i;
2484 valid = gtk_tree_model_iter_next(model, &iter) != FALSE;
2485 i++;
2487 #endif
2488 return -1;
2491 void ListBoxX::GetValue(int n, char *value, int len) {
2492 char *text = NULL;
2493 #if GTK_MAJOR_VERSION < 2
2494 GtkCellType type = gtk_clist_get_cell_type(GTK_CLIST(list), n, 0);
2495 switch (type) {
2496 case GTK_CELL_TEXT:
2497 gtk_clist_get_text(GTK_CLIST(list), n, 0, &text);
2498 break;
2499 case GTK_CELL_PIXTEXT:
2500 gtk_clist_get_pixtext(GTK_CLIST(list), n, 0, &text, NULL, NULL, NULL);
2501 break;
2502 default:
2503 break;
2505 #else
2506 GtkTreeIter iter;
2507 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2508 bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
2509 if (valid) {
2510 gtk_tree_model_get(model, &iter, TEXT_COLUMN, &text, -1);
2512 #endif
2513 if (text && len > 0) {
2514 strncpy(value, text, len);
2515 value[len - 1] = '\0';
2516 } else {
2517 value[0] = '\0';
2521 // g_return_if_fail causes unnecessary compiler warning in release compile.
2522 #ifdef _MSC_VER
2523 #pragma warning(disable: 4127)
2524 #endif
2526 void ListBoxX::RegisterImage(int type, const char *xpm_data) {
2527 g_return_if_fail(xpm_data);
2529 // Saved and use the saved copy so caller's copy can disappear.
2530 xset.Add(type, xpm_data);
2531 XPM *pxpm = xset.Get(type);
2532 xpm_data = reinterpret_cast<const char *>(pxpm->InLinesForm());
2534 if (!pixhash) {
2535 pixhash = g_hash_table_new(g_direct_hash, g_direct_equal);
2537 ListImage *list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash,
2538 (gconstpointer) GINT_TO_POINTER(type));
2539 if (list_image) {
2540 // Drop icon already registered
2541 #if GTK_MAJOR_VERSION < 2
2542 if (list_image->pixmap)
2543 gdk_pixmap_unref(list_image->pixmap);
2544 list_image->pixmap = 0;
2545 if (list_image->bitmap)
2546 gdk_bitmap_unref(list_image->bitmap);
2547 list_image->bitmap = 0;
2548 #else
2549 if (list_image->pixbuf)
2550 gdk_pixbuf_unref(list_image->pixbuf);
2551 list_image->pixbuf = NULL;
2552 #endif
2553 list_image->xpm_data = xpm_data;
2554 } else {
2555 list_image = g_new0(ListImage, 1);
2556 list_image->xpm_data = xpm_data;
2557 g_hash_table_insert((GHashTable *) pixhash, GINT_TO_POINTER(type),
2558 (gpointer) list_image);
2562 void ListBoxX::ClearRegisteredImages() {
2563 xset.Clear();
2566 void ListBoxX::SetList(const char *listText, char separator, char typesep) {
2567 Clear();
2568 int count = strlen(listText) + 1;
2569 char *words = new char[count];
2570 if (words) {
2571 memcpy(words, listText, count);
2572 char *startword = words;
2573 char *numword = NULL;
2574 int i = 0;
2575 for (; words[i]; i++) {
2576 if (words[i] == separator) {
2577 words[i] = '\0';
2578 if (numword)
2579 *numword = '\0';
2580 Append(startword, numword?atoi(numword + 1):-1);
2581 startword = words + i + 1;
2582 numword = NULL;
2583 } else if (words[i] == typesep) {
2584 numword = words + i;
2587 if (startword) {
2588 if (numword)
2589 *numword = '\0';
2590 Append(startword, numword?atoi(numword + 1):-1);
2592 delete []words;
2596 Menu::Menu() : mid(0) {}
2598 void Menu::CreatePopUp() {
2599 Destroy();
2600 mid = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
2603 void Menu::Destroy() {
2604 if (mid)
2605 #if GTK_MAJOR_VERSION < 2
2606 gtk_object_unref(GTK_OBJECT(mid));
2607 #else
2608 g_object_unref(G_OBJECT(mid));
2609 #endif
2610 mid = 0;
2613 void Menu::Show(Point pt, Window &) {
2614 int screenHeight = gdk_screen_height();
2615 int screenWidth = gdk_screen_width();
2616 GtkItemFactory *factory = reinterpret_cast<GtkItemFactory *>(mid);
2617 GtkWidget *widget = gtk_item_factory_get_widget(factory, "<main>");
2618 gtk_widget_show_all(widget);
2619 GtkRequisition requisition;
2620 gtk_widget_size_request(widget, &requisition);
2621 if ((pt.x + requisition.width) > screenWidth) {
2622 pt.x = screenWidth - requisition.width;
2624 if ((pt.y + requisition.height) > screenHeight) {
2625 pt.y = screenHeight - requisition.height;
2627 #if GTK_MAJOR_VERSION >= 2
2628 gtk_item_factory_popup(factory, pt.x - 4, pt.y - 4, 3,
2629 gtk_get_current_event_time());
2630 #else
2631 gtk_item_factory_popup(factory, pt.x - 4, pt.y - 4, 3, 0);
2632 #endif
2635 ElapsedTime::ElapsedTime() {
2636 GTimeVal curTime;
2637 g_get_current_time(&curTime);
2638 bigBit = curTime.tv_sec;
2639 littleBit = curTime.tv_usec;
2642 class DynamicLibraryImpl : public DynamicLibrary {
2643 protected:
2644 GModule* m;
2645 public:
2646 DynamicLibraryImpl(const char *modulePath) {
2647 m = g_module_open(modulePath, G_MODULE_BIND_LAZY);
2650 virtual ~DynamicLibraryImpl() {
2651 if (m != NULL)
2652 g_module_close(m);
2655 // Use g_module_symbol to get a pointer to the relevant function.
2656 virtual Function FindFunction(const char *name) {
2657 if (m != NULL) {
2658 gpointer fn_address = NULL;
2659 gboolean status = g_module_symbol(m, name, &fn_address);
2660 if (status)
2661 return static_cast<Function>(fn_address);
2662 else
2663 return NULL;
2664 } else
2665 return NULL;
2668 virtual bool IsValid() {
2669 return m != NULL;
2673 DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {
2674 return static_cast<DynamicLibrary *>( new DynamicLibraryImpl(modulePath) );
2677 double ElapsedTime::Duration(bool reset) {
2678 GTimeVal curTime;
2679 g_get_current_time(&curTime);
2680 long endBigBit = curTime.tv_sec;
2681 long endLittleBit = curTime.tv_usec;
2682 double result = 1000000.0 * (endBigBit - bigBit);
2683 result += endLittleBit - littleBit;
2684 result /= 1000000.0;
2685 if (reset) {
2686 bigBit = endBigBit;
2687 littleBit = endLittleBit;
2689 return result;
2692 ColourDesired Platform::Chrome() {
2693 return ColourDesired(0xe0, 0xe0, 0xe0);
2696 ColourDesired Platform::ChromeHighlight() {
2697 return ColourDesired(0xff, 0xff, 0xff);
2700 const char *Platform::DefaultFont() {
2701 #ifdef G_OS_WIN32
2702 return "Lucida Console";
2703 #else
2704 #ifdef USE_PANGO
2705 return "!Sans";
2706 #else
2707 return "lucidatypewriter";
2708 #endif
2709 #endif
2712 int Platform::DefaultFontSize() {
2713 #ifdef G_OS_WIN32
2714 return 10;
2715 #else
2716 return 12;
2717 #endif
2720 unsigned int Platform::DoubleClickTime() {
2721 return 500; // Half a second
2724 bool Platform::MouseButtonBounce() {
2725 return true;
2728 void Platform::DebugDisplay(const char *s) {
2729 fprintf(stderr, "%s", s);
2732 bool Platform::IsKeyDown(int) {
2733 // TODO: discover state of keys in GTK+/X
2734 return false;
2737 long Platform::SendScintilla(
2738 WindowID w, unsigned int msg, unsigned long wParam, long lParam) {
2739 return scintilla_send_message(SCINTILLA(w), msg, wParam, lParam);
2742 long Platform::SendScintillaPointer(
2743 WindowID w, unsigned int msg, unsigned long wParam, void *lParam) {
2744 return scintilla_send_message(SCINTILLA(w), msg, wParam,
2745 reinterpret_cast<sptr_t>(lParam));
2748 bool Platform::IsDBCSLeadByte(int codePage, char ch) {
2749 // Byte ranges found in Wikipedia articles with relevant search strings in each case
2750 unsigned char uch = static_cast<unsigned char>(ch);
2751 switch (codePage) {
2752 case 932:
2753 // Shift_jis
2754 return ((uch >= 0x81) && (uch <= 0x9F)) ||
2755 ((uch >= 0xE0) && (uch <= 0xEF));
2756 case 936:
2757 // GBK
2758 return (uch >= 0x81) && (uch <= 0xFE);
2759 case 950:
2760 // Big5
2761 return (uch >= 0x81) && (uch <= 0xFE);
2762 // Korean EUC-KR may be code page 949.
2764 return false;
2767 int Platform::DBCSCharLength(int codePage, const char *s) {
2768 if (codePage == 932 || codePage == 936 || codePage == 950) {
2769 return IsDBCSLeadByte(codePage, s[0]) ? 2 : 1;
2770 } else {
2771 int bytes = mblen(s, MB_CUR_MAX);
2772 if (bytes >= 1)
2773 return bytes;
2774 else
2775 return 1;
2779 int Platform::DBCSCharMaxLength() {
2780 return MB_CUR_MAX;
2781 //return 2;
2784 // These are utility functions not really tied to a platform
2786 int Platform::Minimum(int a, int b) {
2787 if (a < b)
2788 return a;
2789 else
2790 return b;
2793 int Platform::Maximum(int a, int b) {
2794 if (a > b)
2795 return a;
2796 else
2797 return b;
2800 //#define TRACE
2802 #ifdef TRACE
2803 void Platform::DebugPrintf(const char *format, ...) {
2804 char buffer[2000];
2805 va_list pArguments;
2806 va_start(pArguments, format);
2807 vsprintf(buffer, format, pArguments);
2808 va_end(pArguments);
2809 Platform::DebugDisplay(buffer);
2811 #else
2812 void Platform::DebugPrintf(const char *, ...) {}
2814 #endif
2816 // Not supported for GTK+
2817 static bool assertionPopUps = true;
2819 bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {
2820 bool ret = assertionPopUps;
2821 assertionPopUps = assertionPopUps_;
2822 return ret;
2825 void Platform::Assert(const char *c, const char *file, int line) {
2826 char buffer[2000];
2827 sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line);
2828 strcat(buffer, "\r\n");
2829 Platform::DebugDisplay(buffer);
2830 abort();
2833 int Platform::Clamp(int val, int minVal, int maxVal) {
2834 if (val > maxVal)
2835 val = maxVal;
2836 if (val < minVal)
2837 val = minVal;
2838 return val;
2841 void Platform_Initialise() {
2842 FontMutexAllocate();
2845 void Platform_Finalise() {
2846 FontMutexFree();