r5116 | eht16 | 2010-08-05 22:13:47 +0100 (Thu, 05 Aug 2010) | 3 lines
[geany-mirror.git] / scintilla / PlatGTK.cxx
blob9e84815e2e933ba44d7fbb75bbd667347878c653
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 #include "Converter.h"
36 #ifdef _MSC_VER
37 // Ignore unreferenced local functions in GTK+ headers
38 #pragma warning(disable: 4505)
39 #endif
41 #ifdef SCI_NAMESPACE
42 using namespace Scintilla;
43 #endif
45 enum encodingType { singleByte, UTF8, dbcs};
47 struct LOGFONT {
48 int size;
49 bool bold;
50 bool italic;
51 int characterSet;
52 char faceName[300];
55 #if USE_LOCK
56 static GMutex *fontMutex = NULL;
58 static void InitializeGLIBThreads() {
59 if (!g_thread_supported()) {
60 g_thread_init(NULL);
63 #endif
65 static void FontMutexAllocate() {
66 #if USE_LOCK
67 if (!fontMutex) {
68 InitializeGLIBThreads();
69 fontMutex = g_mutex_new();
71 #endif
74 static void FontMutexFree() {
75 #if USE_LOCK
76 if (fontMutex) {
77 g_mutex_free(fontMutex);
78 fontMutex = NULL;
80 #endif
83 static void FontMutexLock() {
84 #if USE_LOCK
85 g_mutex_lock(fontMutex);
86 #endif
89 static void FontMutexUnlock() {
90 #if USE_LOCK
91 if (fontMutex) {
92 g_mutex_unlock(fontMutex);
94 #endif
97 // On GTK+ 1.x holds a GdkFont* but on GTK+ 2.x can hold a GdkFont* or a
98 // PangoFontDescription*.
99 class FontHandle {
100 int width[128];
101 encodingType et;
102 public:
103 int ascent;
104 GdkFont *pfont;
105 PangoFontDescription *pfd;
106 int characterSet;
107 FontHandle(GdkFont *pfont_) {
108 et = singleByte;
109 ascent = 0;
110 pfont = pfont_;
111 pfd = 0;
112 characterSet = -1;
113 ResetWidths(et);
115 FontHandle(PangoFontDescription *pfd_, int characterSet_) {
116 et = singleByte;
117 ascent = 0;
118 pfont = 0;
119 pfd = pfd_;
120 characterSet = characterSet_;
121 ResetWidths(et);
123 ~FontHandle() {
124 if (pfont)
125 gdk_font_unref(pfont);
126 pfont = 0;
127 if (pfd)
128 pango_font_description_free(pfd);
129 pfd = 0;
131 void ResetWidths(encodingType et_) {
132 et = et_;
133 for (int i=0; i<=127; i++) {
134 width[i] = 0;
137 int CharWidth(unsigned char ch, encodingType et_) {
138 int w = 0;
139 FontMutexLock();
140 if ((ch <= 127) && (et == et_)) {
141 w = width[ch];
143 FontMutexUnlock();
144 return w;
146 void SetCharWidth(unsigned char ch, int w, encodingType et_) {
147 if (ch <= 127) {
148 FontMutexLock();
149 if (et != et_) {
150 ResetWidths(et_);
152 width[ch] = w;
153 FontMutexUnlock();
158 // X has a 16 bit coordinate space, so stop drawing here to avoid wrapping
159 static const int maxCoordinate = 32000;
161 static FontHandle *PFont(Font &f) {
162 return reinterpret_cast<FontHandle *>(f.GetID());
165 static GtkWidget *PWidget(WindowID wid) {
166 return reinterpret_cast<GtkWidget *>(wid);
169 static GtkWidget *PWidget(Window &w) {
170 return PWidget(w.GetID());
173 Point Point::FromLong(long lpoint) {
174 return Point(
175 Platform::LowShortFromLong(lpoint),
176 Platform::HighShortFromLong(lpoint));
179 Palette::Palette() {
180 used = 0;
181 allowRealization = false;
182 allocatedPalette = 0;
183 allocatedLen = 0;
184 size = 100;
185 entries = new ColourPair[size];
188 Palette::~Palette() {
189 Release();
190 delete []entries;
191 entries = 0;
194 void Palette::Release() {
195 used = 0;
196 delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
197 allocatedPalette = 0;
198 allocatedLen = 0;
199 delete []entries;
200 size = 100;
201 entries = new ColourPair[size];
204 // This method either adds a colour to the list of wanted colours (want==true)
205 // or retrieves the allocated colour back to the ColourPair.
206 // This is one method to make it easier to keep the code for wanting and retrieving in sync.
207 void Palette::WantFind(ColourPair &cp, bool want) {
208 if (want) {
209 for (int i=0; i < used; i++) {
210 if (entries[i].desired == cp.desired)
211 return;
214 if (used >= size) {
215 int sizeNew = size * 2;
216 ColourPair *entriesNew = new ColourPair[sizeNew];
217 for (int j=0; j<size; j++) {
218 entriesNew[j] = entries[j];
220 delete []entries;
221 entries = entriesNew;
222 size = sizeNew;
225 entries[used].desired = cp.desired;
226 entries[used].allocated.Set(cp.desired.AsLong());
227 used++;
228 } else {
229 for (int i=0; i < used; i++) {
230 if (entries[i].desired == cp.desired) {
231 cp.allocated = entries[i].allocated;
232 return;
235 cp.allocated.Set(cp.desired.AsLong());
239 void Palette::Allocate(Window &w) {
240 if (allocatedPalette) {
241 gdk_colormap_free_colors(gtk_widget_get_colormap(PWidget(w)),
242 reinterpret_cast<GdkColor *>(allocatedPalette),
243 allocatedLen);
244 delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
245 allocatedPalette = 0;
246 allocatedLen = 0;
248 GdkColor *paletteNew = new GdkColor[used];
249 allocatedPalette = paletteNew;
250 gboolean *successPalette = new gboolean[used];
251 if (paletteNew) {
252 allocatedLen = used;
253 int iPal = 0;
254 for (iPal = 0; iPal < used; iPal++) {
255 paletteNew[iPal].red = entries[iPal].desired.GetRed() * (65535 / 255);
256 paletteNew[iPal].green = entries[iPal].desired.GetGreen() * (65535 / 255);
257 paletteNew[iPal].blue = entries[iPal].desired.GetBlue() * (65535 / 255);
258 paletteNew[iPal].pixel = entries[iPal].desired.AsLong();
260 gdk_colormap_alloc_colors(gtk_widget_get_colormap(PWidget(w)),
261 paletteNew, allocatedLen, FALSE, TRUE,
262 successPalette);
263 for (iPal = 0; iPal < used; iPal++) {
264 entries[iPal].allocated.Set(paletteNew[iPal].pixel);
267 delete []successPalette;
270 static const char *CharacterSetName(int characterSet) {
271 switch (characterSet) {
272 case SC_CHARSET_ANSI:
273 return "iso8859-*";
274 case SC_CHARSET_DEFAULT:
275 return "iso8859-*";
276 case SC_CHARSET_BALTIC:
277 return "iso8859-13";
278 case SC_CHARSET_CHINESEBIG5:
279 return "*-*";
280 case SC_CHARSET_EASTEUROPE:
281 return "*-2";
282 case SC_CHARSET_GB2312:
283 return "gb2312.1980-*";
284 case SC_CHARSET_GREEK:
285 return "*-7";
286 case SC_CHARSET_HANGUL:
287 return "ksc5601.1987-*";
288 case SC_CHARSET_MAC:
289 return "*-*";
290 case SC_CHARSET_OEM:
291 return "*-*";
292 case SC_CHARSET_RUSSIAN:
293 return "*-r";
294 case SC_CHARSET_CYRILLIC:
295 return "*-cp1251";
296 case SC_CHARSET_SHIFTJIS:
297 return "jisx0208.1983-*";
298 case SC_CHARSET_SYMBOL:
299 return "*-*";
300 case SC_CHARSET_TURKISH:
301 return "*-9";
302 case SC_CHARSET_JOHAB:
303 return "*-*";
304 case SC_CHARSET_HEBREW:
305 return "*-8";
306 case SC_CHARSET_ARABIC:
307 return "*-6";
308 case SC_CHARSET_VIETNAMESE:
309 return "*-*";
310 case SC_CHARSET_THAI:
311 return "iso8859-11";
312 case SC_CHARSET_8859_15:
313 return "iso8859-15";
314 default:
315 return "*-*";
319 static bool IsDBCSCharacterSet(int characterSet) {
320 switch (characterSet) {
321 case SC_CHARSET_GB2312:
322 case SC_CHARSET_HANGUL:
323 case SC_CHARSET_SHIFTJIS:
324 case SC_CHARSET_CHINESEBIG5:
325 return true;
326 default:
327 return false;
331 static void GenerateFontSpecStrings(const char *fontName, int characterSet,
332 char *foundary, int foundary_len,
333 char *faceName, int faceName_len,
334 char *charset, int charset_len) {
335 // supported font strings include:
336 // foundary-fontface-isoxxx-x
337 // fontface-isoxxx-x
338 // foundary-fontface
339 // fontface
340 if (strchr(fontName, '-')) {
341 char tmp[300];
342 char *d1 = NULL, *d2 = NULL, *d3 = NULL;
343 strncpy(tmp, fontName, sizeof(tmp) - 1);
344 tmp[sizeof(tmp) - 1] = '\0';
345 d1 = strchr(tmp, '-');
346 // we know the first dash exists
347 d2 = strchr(d1 + 1, '-');
348 if (d2)
349 d3 = strchr(d2 + 1, '-');
350 if (d3 && d2) {
351 // foundary-fontface-isoxxx-x
352 *d2 = '\0';
353 foundary[0] = '-';
354 foundary[1] = '\0';
355 strncpy(faceName, tmp, foundary_len - 1);
356 strncpy(charset, d2 + 1, charset_len - 1);
357 } else if (d2) {
358 // fontface-isoxxx-x
359 *d1 = '\0';
360 strcpy(foundary, "-*-");
361 strncpy(faceName, tmp, faceName_len - 1);
362 strncpy(charset, d1 + 1, charset_len - 1);
363 } else {
364 // foundary-fontface
365 foundary[0] = '-';
366 foundary[1] = '\0';
367 strncpy(faceName, tmp, faceName_len - 1);
368 strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
370 } else {
371 strncpy(foundary, "-*-", foundary_len);
372 strncpy(faceName, fontName, faceName_len - 1);
373 strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
377 static void SetLogFont(LOGFONT &lf, const char *faceName, int characterSet, int size, bool bold, bool italic) {
378 memset(&lf, 0, sizeof(lf));
379 lf.size = size;
380 lf.bold = bold;
381 lf.italic = italic;
382 lf.characterSet = characterSet;
383 strncpy(lf.faceName, faceName, sizeof(lf.faceName) - 1);
387 * Create a hash from the parameters for a font to allow easy checking for identity.
388 * If one font is the same as another, its hash will be the same, but if the hash is the
389 * same then they may still be different.
391 static int HashFont(const char *faceName, int characterSet, int size, bool bold, bool italic) {
392 return
393 size ^
394 (characterSet << 10) ^
395 (bold ? 0x10000000 : 0) ^
396 (italic ? 0x20000000 : 0) ^
397 faceName[0];
400 class FontCached : Font {
401 FontCached *next;
402 int usage;
403 LOGFONT lf;
404 int hash;
405 FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
406 ~FontCached() {}
407 bool SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
408 virtual void Release();
409 static FontID CreateNewFont(const char *fontName, int characterSet,
410 int size, bool bold, bool italic);
411 static FontCached *first;
412 public:
413 static FontID FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
414 static void ReleaseId(FontID fid_);
417 FontCached *FontCached::first = 0;
419 FontCached::FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) :
420 next(0), usage(0), hash(0) {
421 ::SetLogFont(lf, faceName_, characterSet_, size_, bold_, italic_);
422 hash = HashFont(faceName_, characterSet_, size_, bold_, italic_);
423 fid = CreateNewFont(faceName_, characterSet_, size_, bold_, italic_);
424 usage = 1;
427 bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
428 return
429 lf.size == size_ &&
430 lf.bold == bold_ &&
431 lf.italic == italic_ &&
432 lf.characterSet == characterSet_ &&
433 0 == strcmp(lf.faceName, faceName_);
436 void FontCached::Release() {
437 if (fid)
438 delete PFont(*this);
439 fid = 0;
442 FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
443 FontID ret = 0;
444 FontMutexLock();
445 int hashFind = HashFont(faceName_, characterSet_, size_, bold_, italic_);
446 for (FontCached *cur = first; cur; cur = cur->next) {
447 if ((cur->hash == hashFind) &&
448 cur->SameAs(faceName_, characterSet_, size_, bold_, italic_)) {
449 cur->usage++;
450 ret = cur->fid;
453 if (ret == 0) {
454 FontCached *fc = new FontCached(faceName_, characterSet_, size_, bold_, italic_);
455 if (fc) {
456 fc->next = first;
457 first = fc;
458 ret = fc->fid;
461 FontMutexUnlock();
462 return ret;
465 void FontCached::ReleaseId(FontID fid_) {
466 FontMutexLock();
467 FontCached **pcur = &first;
468 for (FontCached *cur = first; cur; cur = cur->next) {
469 if (cur->fid == fid_) {
470 cur->usage--;
471 if (cur->usage == 0) {
472 *pcur = cur->next;
473 cur->Release();
474 cur->next = 0;
475 delete cur;
477 break;
479 pcur = &cur->next;
481 FontMutexUnlock();
484 static GdkFont *LoadFontOrSet(const char *fontspec, int characterSet) {
485 if (IsDBCSCharacterSet(characterSet)) {
486 return gdk_fontset_load(fontspec);
487 } else {
488 return gdk_font_load(fontspec);
492 FontID FontCached::CreateNewFont(const char *fontName, int characterSet,
493 int size, bool bold, bool italic) {
494 char fontset[1024];
495 char fontspec[300];
496 char foundary[50];
497 char faceName[100];
498 char charset[50];
499 fontset[0] = '\0';
500 fontspec[0] = '\0';
501 foundary[0] = '\0';
502 faceName[0] = '\0';
503 charset[0] = '\0';
505 if (fontName[0] == '!') {
506 PangoFontDescription *pfd = pango_font_description_new();
507 if (pfd) {
508 pango_font_description_set_family(pfd, fontName+1);
509 pango_font_description_set_size(pfd, size * PANGO_SCALE);
510 pango_font_description_set_weight(pfd, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
511 pango_font_description_set_style(pfd, italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
512 return new FontHandle(pfd, characterSet);
516 GdkFont *newid = 0;
517 // If name of the font begins with a '-', assume, that it is
518 // a full fontspec.
519 if (fontName[0] == '-') {
520 if (strchr(fontName, ',') || IsDBCSCharacterSet(characterSet)) {
521 newid = gdk_fontset_load(fontName);
522 } else {
523 newid = gdk_font_load(fontName);
525 if (!newid) {
526 // Font not available so substitute a reasonable code font
527 // iso8859 appears to only allow western characters.
528 newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
529 characterSet);
531 return new FontHandle(newid);
534 // it's not a full fontspec, build one.
536 // This supports creating a FONT_SET
537 // in a method that allows us to also set size, slant and
538 // weight for the fontset. The expected input is multiple
539 // partial fontspecs seperated by comma
540 // eg. adobe-courier-iso10646-1,*-courier-iso10646-1,*-*-*-*
541 if (strchr(fontName, ',')) {
542 // build a fontspec and use gdk_fontset_load
543 int remaining = sizeof(fontset);
544 char fontNameCopy[1024];
545 strncpy(fontNameCopy, fontName, sizeof(fontNameCopy) - 1);
546 char *fn = fontNameCopy;
547 char *fp = strchr(fn, ',');
548 for (;;) {
549 const char *spec = "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
550 if (fontset[0] != '\0') {
551 // if this is not the first font in the list,
552 // append a comma seperator
553 spec = ",%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
556 if (fp)
557 *fp = '\0'; // nullify the comma
558 GenerateFontSpecStrings(fn, characterSet,
559 foundary, sizeof(foundary),
560 faceName, sizeof(faceName),
561 charset, sizeof(charset));
563 g_snprintf(fontspec,
564 sizeof(fontspec) - 1,
565 spec,
566 foundary, faceName,
567 bold ? "-bold" : "-medium",
568 italic ? "-i" : "-r",
569 size * 10,
570 charset);
572 // if this is the first font in the list, and
573 // we are doing italic, add an oblique font
574 // to the list
575 if (italic && fontset[0] == '\0') {
576 strncat(fontset, fontspec, remaining - 1);
577 remaining -= strlen(fontset);
579 g_snprintf(fontspec,
580 sizeof(fontspec) - 1,
581 ",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s",
582 foundary, faceName,
583 bold ? "-bold" : "-medium",
584 size * 10,
585 charset);
588 strncat(fontset, fontspec, remaining - 1);
589 remaining -= strlen(fontset);
591 if (!fp)
592 break;
594 fn = fp + 1;
595 fp = strchr(fn, ',');
598 newid = gdk_fontset_load(fontset);
599 if (newid)
600 return new FontHandle(newid);
602 // if fontset load failed, fall through, we'll use
603 // the last font entry and continue to try and
604 // get something that matches
607 // single fontspec support
609 GenerateFontSpecStrings(fontName, characterSet,
610 foundary, sizeof(foundary),
611 faceName, sizeof(faceName),
612 charset, sizeof(charset));
614 g_snprintf(fontspec,
615 sizeof(fontspec) - 1,
616 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
617 foundary, faceName,
618 bold ? "-bold" : "-medium",
619 italic ? "-i" : "-r",
620 size * 10,
621 charset);
622 newid = LoadFontOrSet(fontspec, characterSet);
623 if (!newid) {
624 // some fonts have oblique, not italic
625 g_snprintf(fontspec,
626 sizeof(fontspec) - 1,
627 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
628 foundary, faceName,
629 bold ? "-bold" : "-medium",
630 italic ? "-o" : "-r",
631 size * 10,
632 charset);
633 newid = LoadFontOrSet(fontspec, characterSet);
635 if (!newid) {
636 g_snprintf(fontspec,
637 sizeof(fontspec) - 1,
638 "-*-*-*-*-*-*-*-%0d-*-*-*-*-%s",
639 size * 10,
640 charset);
641 newid = gdk_font_load(fontspec);
643 if (!newid) {
644 // Font not available so substitute a reasonable code font
645 // iso8859 appears to only allow western characters.
646 newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
647 characterSet);
649 return new FontHandle(newid);
652 Font::Font() : fid(0) {}
654 Font::~Font() {}
656 void Font::Create(const char *faceName, int characterSet, int size,
657 bool bold, bool italic, int) {
658 Release();
659 fid = FontCached::FindOrCreate(faceName, characterSet, size, bold, italic);
662 void Font::Release() {
663 if (fid)
664 FontCached::ReleaseId(fid);
665 fid = 0;
668 // Required on OS X
669 #ifdef SCI_NAMESPACE
670 namespace Scintilla {
671 #endif
672 class SurfaceImpl : public Surface {
673 encodingType et;
674 GdkDrawable *drawable;
675 GdkGC *gc;
676 GdkPixmap *ppixmap;
677 int x;
678 int y;
679 bool inited;
680 bool createdGC;
681 PangoContext *pcontext;
682 PangoLayout *layout;
683 Converter conv;
684 int characterSet;
685 void SetConverter(int characterSet_);
686 public:
687 SurfaceImpl();
688 virtual ~SurfaceImpl();
690 void Init(WindowID wid);
691 void Init(SurfaceID sid, WindowID wid);
692 void InitPixMap(int width, int height, Surface *surface_, WindowID wid);
694 void Release();
695 bool Initialised();
696 void PenColour(ColourAllocated fore);
697 int LogPixelsY();
698 int DeviceHeightFont(int points);
699 void MoveTo(int x_, int y_);
700 void LineTo(int x_, int y_);
701 void Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back);
702 void RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back);
703 void FillRectangle(PRectangle rc, ColourAllocated back);
704 void FillRectangle(PRectangle rc, Surface &surfacePattern);
705 void RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back);
706 void AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
707 ColourAllocated outline, int alphaOutline, int flags);
708 void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back);
709 void Copy(PRectangle rc, Point from, Surface &surfaceSource);
711 void DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
712 void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
713 void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
714 void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
715 void MeasureWidths(Font &font_, const char *s, int len, int *positions);
716 int WidthText(Font &font_, const char *s, int len);
717 int WidthChar(Font &font_, char ch);
718 int Ascent(Font &font_);
719 int Descent(Font &font_);
720 int InternalLeading(Font &font_);
721 int ExternalLeading(Font &font_);
722 int Height(Font &font_);
723 int AverageCharWidth(Font &font_);
725 int SetPalette(Palette *pal, bool inBackGround);
726 void SetClip(PRectangle rc);
727 void FlushCachedState();
729 void SetUnicodeMode(bool unicodeMode_);
730 void SetDBCSMode(int codePage);
732 #ifdef SCI_NAMESPACE
734 #endif
736 const char *CharacterSetID(int characterSet) {
737 switch (characterSet) {
738 case SC_CHARSET_ANSI:
739 return "";
740 case SC_CHARSET_DEFAULT:
741 return "ISO-8859-1";
742 case SC_CHARSET_BALTIC:
743 return "ISO-8859-13";
744 case SC_CHARSET_CHINESEBIG5:
745 return "BIG-5";
746 case SC_CHARSET_EASTEUROPE:
747 return "ISO-8859-2";
748 case SC_CHARSET_GB2312:
749 return "GB2312";
750 case SC_CHARSET_GREEK:
751 return "ISO-8859-7";
752 case SC_CHARSET_HANGUL:
753 return "";
754 case SC_CHARSET_MAC:
755 return "MACINTOSH";
756 case SC_CHARSET_OEM:
757 return "ASCII";
758 case SC_CHARSET_RUSSIAN:
759 return "KOI8-R";
760 case SC_CHARSET_CYRILLIC:
761 return "CP1251";
762 case SC_CHARSET_SHIFTJIS:
763 return "SHIFT-JIS";
764 case SC_CHARSET_SYMBOL:
765 return "";
766 case SC_CHARSET_TURKISH:
767 return "ISO-8859-9";
768 case SC_CHARSET_JOHAB:
769 return "JOHAB";
770 case SC_CHARSET_HEBREW:
771 return "ISO-8859-8";
772 case SC_CHARSET_ARABIC:
773 return "ISO-8859-6";
774 case SC_CHARSET_VIETNAMESE:
775 return "";
776 case SC_CHARSET_THAI:
777 return "ISO-8859-11";
778 case SC_CHARSET_8859_15:
779 return "ISO-8859-15";
780 default:
781 return "";
785 void SurfaceImpl::SetConverter(int characterSet_) {
786 if (characterSet != characterSet_) {
787 characterSet = characterSet_;
788 conv.Open("UTF-8", CharacterSetID(characterSet), false);
792 SurfaceImpl::SurfaceImpl() : et(singleByte), drawable(0), gc(0), ppixmap(0),
793 x(0), y(0), inited(false), createdGC(false)
794 , pcontext(0), layout(0), characterSet(-1) {
797 SurfaceImpl::~SurfaceImpl() {
798 Release();
801 void SurfaceImpl::Release() {
802 et = singleByte;
803 drawable = 0;
804 if (createdGC) {
805 createdGC = false;
806 gdk_gc_unref(gc);
808 gc = 0;
809 if (ppixmap)
810 gdk_pixmap_unref(ppixmap);
811 ppixmap = 0;
812 if (layout)
813 g_object_unref(layout);
814 layout = 0;
815 if (pcontext)
816 g_object_unref(pcontext);
817 pcontext = 0;
818 conv.Close();
819 characterSet = -1;
820 x = 0;
821 y = 0;
822 inited = false;
823 createdGC = false;
826 bool SurfaceImpl::Initialised() {
827 return inited;
830 void SurfaceImpl::Init(WindowID wid) {
831 Release();
832 PLATFORM_ASSERT(wid);
833 pcontext = gtk_widget_create_pango_context(PWidget(wid));
834 PLATFORM_ASSERT(pcontext);
835 layout = pango_layout_new(pcontext);
836 PLATFORM_ASSERT(layout);
837 inited = true;
840 void SurfaceImpl::Init(SurfaceID sid, WindowID wid) {
841 PLATFORM_ASSERT(sid);
842 GdkDrawable *drawable_ = reinterpret_cast<GdkDrawable *>(sid);
843 Release();
844 PLATFORM_ASSERT(wid);
845 pcontext = gtk_widget_create_pango_context(PWidget(wid));
846 layout = pango_layout_new(pcontext);
847 drawable = drawable_;
848 gc = gdk_gc_new(drawable_);
849 // Ask for lines that do not paint the last pixel so is like Win32
850 gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
851 createdGC = true;
852 inited = true;
855 void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID wid) {
856 PLATFORM_ASSERT(surface_);
857 Release();
858 SurfaceImpl *surfImpl = static_cast<SurfaceImpl *>(surface_);
859 PLATFORM_ASSERT(surfImpl->drawable);
860 PLATFORM_ASSERT(wid);
861 pcontext = gtk_widget_create_pango_context(PWidget(wid));
862 PLATFORM_ASSERT(pcontext);
863 layout = pango_layout_new(pcontext);
864 PLATFORM_ASSERT(layout);
865 if (height > 0 && width > 0)
866 ppixmap = gdk_pixmap_new(surfImpl->drawable, width, height, -1);
867 drawable = ppixmap;
868 gc = gdk_gc_new(surfImpl->drawable);
869 // Ask for lines that do not paint the last pixel so is like Win32
870 gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
871 createdGC = true;
872 inited = true;
875 void SurfaceImpl::PenColour(ColourAllocated fore) {
876 if (gc) {
877 GdkColor co;
878 co.pixel = fore.AsLong();
879 gdk_gc_set_foreground(gc, &co);
883 int SurfaceImpl::LogPixelsY() {
884 return 72;
887 int SurfaceImpl::DeviceHeightFont(int points) {
888 int logPix = LogPixelsY();
889 return (points * logPix + logPix / 2) / 72;
892 void SurfaceImpl::MoveTo(int x_, int y_) {
893 x = x_;
894 y = y_;
897 void SurfaceImpl::LineTo(int x_, int y_) {
898 if (drawable && gc) {
899 gdk_draw_line(drawable, gc,
900 x, y,
901 x_, y_);
903 x = x_;
904 y = y_;
907 void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore,
908 ColourAllocated back) {
909 GdkPoint gpts[20];
910 if (npts < static_cast<int>((sizeof(gpts) / sizeof(gpts[0])))) {
911 for (int i = 0;i < npts;i++) {
912 gpts[i].x = pts[i].x;
913 gpts[i].y = pts[i].y;
915 PenColour(back);
916 gdk_draw_polygon(drawable, gc, 1, gpts, npts);
917 PenColour(fore);
918 gdk_draw_polygon(drawable, gc, 0, gpts, npts);
922 void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
923 if (gc && drawable) {
924 PenColour(back);
925 gdk_draw_rectangle(drawable, gc, 1,
926 rc.left + 1, rc.top + 1,
927 rc.right - rc.left - 2, rc.bottom - rc.top - 2);
929 PenColour(fore);
930 // The subtraction of 1 off the width and height here shouldn't be needed but
931 // otherwise a different rectangle is drawn than would be done if the fill parameter == 1
932 gdk_draw_rectangle(drawable, gc, 0,
933 rc.left, rc.top,
934 rc.right - rc.left - 1, rc.bottom - rc.top - 1);
938 void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) {
939 PenColour(back);
940 if (drawable && (rc.left < maxCoordinate)) { // Protect against out of range
941 gdk_draw_rectangle(drawable, gc, 1,
942 rc.left, rc.top,
943 rc.right - rc.left, rc.bottom - rc.top);
947 void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) {
948 if (static_cast<SurfaceImpl &>(surfacePattern).drawable) {
949 // Tile pattern over rectangle
950 // Currently assumes 8x8 pattern
951 int widthPat = 8;
952 int heightPat = 8;
953 for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) {
954 int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat;
955 for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) {
956 int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat;
957 gdk_draw_pixmap(drawable,
959 static_cast<SurfaceImpl &>(surfacePattern).drawable,
960 0, 0,
961 xTile, yTile,
962 widthx, heighty);
965 } else {
966 // Something is wrong so try to show anyway
967 // Shows up black because colour not allocated
968 FillRectangle(rc, ColourAllocated(0));
972 void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
973 if (((rc.right - rc.left) > 4) && ((rc.bottom - rc.top) > 4)) {
974 // Approximate a round rect with some cut off corners
975 Point pts[] = {
976 Point(rc.left + 2, rc.top),
977 Point(rc.right - 2, rc.top),
978 Point(rc.right, rc.top + 2),
979 Point(rc.right, rc.bottom - 2),
980 Point(rc.right - 2, rc.bottom),
981 Point(rc.left + 2, rc.bottom),
982 Point(rc.left, rc.bottom - 2),
983 Point(rc.left, rc.top + 2),
985 Polygon(pts, sizeof(pts) / sizeof(pts[0]), fore, back);
986 } else {
987 RectangleDraw(rc, fore, back);
991 // Plot a point into a guint32 buffer symetrically to all 4 qudrants
992 static void AllFour(guint32 *pixels, int stride, int width, int height, int x, int y, guint32 val) {
993 pixels[y*stride+x] = val;
994 pixels[y*stride+width-1-x] = val;
995 pixels[(height-1-y)*stride+x] = val;
996 pixels[(height-1-y)*stride+width-1-x] = val;
999 static unsigned int GetRValue(unsigned int co) {
1000 return (co >> 16) & 0xff;
1003 static unsigned int GetGValue(unsigned int co) {
1004 return (co >> 8) & 0xff;
1007 static unsigned int GetBValue(unsigned int co) {
1008 return co & 0xff;
1011 static guint32 u32FromRGBA(guint8 r, guint8 g, guint8 b, guint8 a) {
1012 union {
1013 guint8 pixVal[4];
1014 guint32 val;
1015 } converter;
1016 converter.pixVal[0] = r;
1017 converter.pixVal[1] = g;
1018 converter.pixVal[2] = b;
1019 converter.pixVal[3] = a;
1020 return converter.val;
1023 void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
1024 ColourAllocated outline, int alphaOutline, int flags) {
1025 if (gc && drawable && rc.Width() > 0) {
1026 int width = rc.Width();
1027 int height = rc.Height();
1028 // Ensure not distorted too much by corners when small
1029 cornerSize = Platform::Minimum(cornerSize, (Platform::Minimum(width, height) / 2) - 2);
1030 // Make a 32 bit deep pixbuf with alpha
1031 GdkPixbuf *pixalpha = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
1033 guint32 valEmpty = u32FromRGBA(0,0,0,0);
1034 guint32 valFill = u32FromRGBA(GetRValue(fill.AsLong()),
1035 GetGValue(fill.AsLong()), GetBValue(fill.AsLong()), alphaFill);
1036 guint32 valOutline = u32FromRGBA(GetRValue(outline.AsLong()),
1037 GetGValue(outline.AsLong()), GetBValue(outline.AsLong()), alphaOutline);
1038 guint32 *pixels = reinterpret_cast<guint32 *>(gdk_pixbuf_get_pixels(pixalpha));
1039 int stride = gdk_pixbuf_get_rowstride(pixalpha) / 4;
1040 for (int yr=0; yr<height; yr++) {
1041 for (int xr=0; xr<width; xr++) {
1042 if ((xr==0) || (xr==width-1) || (yr == 0) || (yr == height-1)) {
1043 pixels[yr*stride+xr] = valOutline;
1044 } else {
1045 pixels[yr*stride+xr] = valFill;
1049 for (int c=0;c<cornerSize; c++) {
1050 for (int xr=0;xr<c+1; xr++) {
1051 AllFour(pixels, stride, width, height, xr, c-xr, valEmpty);
1054 for (int xr=1;xr<cornerSize; xr++) {
1055 AllFour(pixels, stride, width, height, xr, cornerSize-xr, valOutline);
1058 // Draw with alpha
1059 gdk_draw_pixbuf(drawable, gc, pixalpha,
1060 0,0, rc.left,rc.top, width,height, GDK_RGB_DITHER_NORMAL, 0, 0);
1062 g_object_unref(pixalpha);
1066 void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
1067 PenColour(back);
1068 gdk_draw_arc(drawable, gc, 1,
1069 rc.left + 1, rc.top + 1,
1070 rc.right - rc.left - 2, rc.bottom - rc.top - 2,
1071 0, 32767);
1073 // The subtraction of 1 here is similar to the case for RectangleDraw
1074 PenColour(fore);
1075 gdk_draw_arc(drawable, gc, 0,
1076 rc.left, rc.top,
1077 rc.right - rc.left - 1, rc.bottom - rc.top - 1,
1078 0, 32767);
1081 void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
1082 if (static_cast<SurfaceImpl &>(surfaceSource).drawable) {
1083 gdk_draw_pixmap(drawable,
1085 static_cast<SurfaceImpl &>(surfaceSource).drawable,
1086 from.x, from.y,
1087 rc.left, rc.top,
1088 rc.right - rc.left, rc.bottom - rc.top);
1092 static size_t UTF8Len(char ch) {
1093 unsigned char uch = static_cast<unsigned char>(ch);
1094 if (uch < 0x80)
1095 return 1;
1096 else if (uch < (0x80 + 0x40 + 0x20))
1097 return 2;
1098 else
1099 return 3;
1102 char *UTF8FromLatin1(const char *s, int &len) {
1103 char *utfForm = new char[len*2+1];
1104 size_t lenU = 0;
1105 for (int i=0;i<len;i++) {
1106 unsigned int uch = static_cast<unsigned char>(s[i]);
1107 if (uch < 0x80) {
1108 utfForm[lenU++] = uch;
1109 } else {
1110 utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
1111 utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1114 utfForm[lenU] = '\0';
1115 len = lenU;
1116 return utfForm;
1119 static char *UTF8FromIconv(const Converter &conv, const char *s, int &len) {
1120 if (conv) {
1121 char *utfForm = new char[len*3+1];
1122 char *pin = const_cast<char *>(s);
1123 size_t inLeft = len;
1124 char *pout = utfForm;
1125 size_t outLeft = len*3+1;
1126 size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1127 if (conversions != ((size_t)(-1))) {
1128 *pout = '\0';
1129 len = pout - utfForm;
1130 return utfForm;
1132 delete []utfForm;
1134 return 0;
1137 // Work out how many bytes are in a character by trying to convert using iconv,
1138 // returning the first length that succeeds.
1139 static size_t MultiByteLenFromIconv(const Converter &conv, const char *s, size_t len) {
1140 for (size_t lenMB=1; (lenMB<4) && (lenMB <= len); lenMB++) {
1141 char wcForm[2];
1142 char *pin = const_cast<char *>(s);
1143 size_t inLeft = lenMB;
1144 char *pout = wcForm;
1145 size_t outLeft = 2;
1146 size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1147 if (conversions != ((size_t)(-1))) {
1148 return lenMB;
1151 return 1;
1154 static char *UTF8FromGdkWChar(GdkWChar *wctext, int wclen) {
1155 char *utfForm = new char[wclen*3+1]; // Maximum of 3 UTF-8 bytes per character
1156 size_t lenU = 0;
1157 for (int i = 0; i < wclen && wctext[i]; i++) {
1158 unsigned int uch = wctext[i];
1159 if (uch < 0x80) {
1160 utfForm[lenU++] = static_cast<char>(uch);
1161 } else if (uch < 0x800) {
1162 utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
1163 utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1164 } else {
1165 utfForm[lenU++] = static_cast<char>(0xE0 | (uch >> 12));
1166 utfForm[lenU++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
1167 utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1170 utfForm[lenU] = '\0';
1171 return utfForm;
1174 static char *UTF8FromDBCS(const char *s, int &len) {
1175 GdkWChar *wctext = new GdkWChar[len + 1];
1176 GdkWChar *wcp = wctext;
1177 int wclen = gdk_mbstowcs(wcp, s, len);
1178 if (wclen < 1) {
1179 // In the annoying case when non-locale chars in the line.
1180 // e.g. latin1 chars in Japanese locale.
1181 delete []wctext;
1182 return 0;
1185 char *utfForm = UTF8FromGdkWChar(wctext, wclen);
1186 delete []wctext;
1187 len = strlen(utfForm);
1188 return utfForm;
1191 static size_t UTF8CharLength(const char *s) {
1192 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
1193 unsigned char ch = *us;
1194 if (ch < 0x80) {
1195 return 1;
1196 } else if (ch < 0x80 + 0x40 + 0x20) {
1197 return 2;
1198 } else {
1199 return 3;
1203 // On GTK+, wchar_t is 4 bytes
1205 const int maxLengthTextRun = 10000;
1207 void SurfaceImpl::DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1208 ColourAllocated fore) {
1209 PenColour(fore);
1210 if (gc && drawable) {
1211 int xText = rc.left;
1212 if (PFont(font_)->pfd) {
1213 char *utfForm = 0;
1214 bool useGFree = false;
1215 if (et == UTF8) {
1216 pango_layout_set_text(layout, s, len);
1217 } else {
1218 if (!utfForm) {
1219 SetConverter(PFont(font_)->characterSet);
1220 utfForm = UTF8FromIconv(conv, s, len);
1222 if (!utfForm) { // iconv failed so try DBCS if DBCS mode
1223 if (et == dbcs) {
1224 // Convert to utf8
1225 utfForm = UTF8FromDBCS(s, len);
1228 if (!utfForm) { // iconv and DBCS failed so treat as Latin1
1229 utfForm = UTF8FromLatin1(s, len);
1231 pango_layout_set_text(layout, utfForm, len);
1233 pango_layout_set_font_description(layout, PFont(font_)->pfd);
1234 #ifdef PANGO_VERSION
1235 PangoLayoutLine *pll = pango_layout_get_line_readonly(layout,0);
1236 #else
1237 PangoLayoutLine *pll = pango_layout_get_line(layout,0);
1238 #endif
1239 gdk_draw_layout_line(drawable, gc, xText, ybase, pll);
1240 if (useGFree) {
1241 g_free(utfForm);
1242 } else {
1243 delete []utfForm;
1245 return;
1247 // Draw text as a series of segments to avoid limitations in X servers
1248 const int segmentLength = 1000;
1249 bool draw8bit = true;
1250 if (et != singleByte) {
1251 GdkWChar wctext[maxLengthTextRun];
1252 if (len >= maxLengthTextRun)
1253 len = maxLengthTextRun-1;
1254 int wclen;
1255 if (et == UTF8) {
1256 wclen = UTF16FromUTF8(s, len,
1257 static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
1258 } else { // dbcs, so convert using current locale
1259 char sMeasure[maxLengthTextRun];
1260 memcpy(sMeasure, s, len);
1261 sMeasure[len] = '\0';
1262 wclen = gdk_mbstowcs(
1263 wctext, sMeasure, maxLengthTextRun - 1);
1265 if (wclen > 0) {
1266 draw8bit = false;
1267 wctext[wclen] = L'\0';
1268 GdkWChar *wcp = wctext;
1269 while ((wclen > 0) && (xText < maxCoordinate)) {
1270 int lenDraw = Platform::Minimum(wclen, segmentLength);
1271 gdk_draw_text_wc(drawable, PFont(font_)->pfont, gc,
1272 xText, ybase, wcp, lenDraw);
1273 wclen -= lenDraw;
1274 if (wclen > 0) { // Avoid next calculation if possible as may be expensive
1275 xText += gdk_text_width_wc(PFont(font_)->pfont,
1276 wcp, lenDraw);
1278 wcp += lenDraw;
1282 if (draw8bit) {
1283 while ((len > 0) && (xText < maxCoordinate)) {
1284 int lenDraw = Platform::Minimum(len, segmentLength);
1285 gdk_draw_text(drawable, PFont(font_)->pfont, gc,
1286 xText, ybase, s, lenDraw);
1287 len -= lenDraw;
1288 if (len > 0) { // Avoid next calculation if possible as may be expensive
1289 xText += gdk_text_width(PFont(font_)->pfont, s, lenDraw);
1291 s += lenDraw;
1297 void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1298 ColourAllocated fore, ColourAllocated back) {
1299 FillRectangle(rc, back);
1300 DrawTextBase(rc, font_, ybase, s, len, fore);
1303 // On GTK+, exactly same as DrawTextNoClip
1304 void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1305 ColourAllocated fore, ColourAllocated back) {
1306 FillRectangle(rc, back);
1307 DrawTextBase(rc, font_, ybase, s, len, fore);
1310 void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1311 ColourAllocated fore) {
1312 // Avoid drawing spaces in transparent mode
1313 for (int i=0;i<len;i++) {
1314 if (s[i] != ' ') {
1315 DrawTextBase(rc, font_, ybase, s, len, fore);
1316 return;
1321 class ClusterIterator {
1322 PangoLayoutIter *iter;
1323 PangoRectangle pos;
1324 int lenPositions;
1325 public:
1326 bool finished;
1327 int positionStart;
1328 int position;
1329 int distance;
1330 int curIndex;
1331 ClusterIterator(PangoLayout *layout, int len) : lenPositions(len), finished(false),
1332 positionStart(0), position(0), distance(0), curIndex(0) {
1333 iter = pango_layout_get_iter(layout);
1334 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1336 ~ClusterIterator() {
1337 pango_layout_iter_free(iter);
1340 void Next() {
1341 positionStart = position;
1342 if (pango_layout_iter_next_cluster(iter)) {
1343 pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1344 position = PANGO_PIXELS(pos.x);
1345 curIndex = pango_layout_iter_get_index(iter);
1346 } else {
1347 finished = true;
1348 position = PANGO_PIXELS(pos.x + pos.width);
1349 curIndex = lenPositions;
1351 distance = position - positionStart;
1355 void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) {
1356 if (font_.GetID()) {
1357 int totalWidth = 0;
1358 const int lenPositions = len;
1359 if (PFont(font_)->pfd) {
1360 if (len == 1) {
1361 int width = PFont(font_)->CharWidth(*s, et);
1362 if (width) {
1363 positions[0] = width;
1364 return;
1367 pango_layout_set_font_description(layout, PFont(font_)->pfd);
1368 if (et == UTF8) {
1369 // Simple and direct as UTF-8 is native Pango encoding
1370 int i = 0;
1371 pango_layout_set_text(layout, s, len);
1372 ClusterIterator iti(layout, lenPositions);
1373 while (!iti.finished) {
1374 iti.Next();
1375 int places = iti.curIndex - i;
1376 while (i < iti.curIndex) {
1377 // Evenly distribute space among bytes of this cluster.
1378 // Would be better to find number of characters and then
1379 // divide evenly between characters with each byte of a character
1380 // being at the same position.
1381 positions[i] = iti.position - (iti.curIndex - 1 - i) * iti.distance / places;
1382 i++;
1385 PLATFORM_ASSERT(i == lenPositions);
1386 } else {
1387 int positionsCalculated = 0;
1388 if (et == dbcs) {
1389 SetConverter(PFont(font_)->characterSet);
1390 char *utfForm = UTF8FromIconv(conv, s, len);
1391 if (utfForm) {
1392 // Convert to UTF-8 so can ask Pango for widths, then
1393 // Loop through UTF-8 and DBCS forms, taking account of different
1394 // character byte lengths.
1395 Converter convMeasure("UCS-2", CharacterSetID(characterSet), false);
1396 pango_layout_set_text(layout, utfForm, strlen(utfForm));
1397 int i = 0;
1398 int clusterStart = 0;
1399 ClusterIterator iti(layout, strlen(utfForm));
1400 while (!iti.finished) {
1401 iti.Next();
1402 int clusterEnd = iti.curIndex;
1403 int places = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
1404 int place = 1;
1405 while (clusterStart < clusterEnd) {
1406 size_t lenChar = MultiByteLenFromIconv(convMeasure, s+i, len-i);
1407 while (lenChar--) {
1408 positions[i++] = iti.position - (places - place) * iti.distance / places;
1409 positionsCalculated++;
1411 clusterStart += UTF8CharLength(utfForm+clusterStart);
1412 place++;
1415 delete []utfForm;
1416 PLATFORM_ASSERT(i == lenPositions);
1419 if (positionsCalculated < 1 ) {
1420 // Either Latin1 or DBCS conversion failed so treat as Latin1.
1421 bool useGFree = false;
1422 SetConverter(PFont(font_)->characterSet);
1423 char *utfForm = UTF8FromIconv(conv, s, len);
1424 if (!utfForm) {
1425 utfForm = UTF8FromLatin1(s, len);
1427 pango_layout_set_text(layout, utfForm, len);
1428 int i = 0;
1429 int clusterStart = 0;
1430 // Each Latin1 input character may take 1 or 2 bytes in UTF-8
1431 // and groups of up to 3 may be represented as ligatures.
1432 ClusterIterator iti(layout, strlen(utfForm));
1433 while (!iti.finished) {
1434 iti.Next();
1435 int clusterEnd = iti.curIndex;
1436 int ligatureLength = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
1437 PLATFORM_ASSERT(ligatureLength > 0 && ligatureLength <= 3);
1438 for (int charInLig=0; charInLig<ligatureLength; charInLig++) {
1439 positions[i++] = iti.position - (ligatureLength - 1 - charInLig) * iti.distance / ligatureLength;
1441 clusterStart = clusterEnd;
1443 if (useGFree) {
1444 g_free(utfForm);
1445 } else {
1446 delete []utfForm;
1448 PLATFORM_ASSERT(i == lenPositions);
1451 if (len == 1) {
1452 PFont(font_)->SetCharWidth(*s, positions[0], et);
1454 return;
1456 GdkFont *gf = PFont(font_)->pfont;
1457 bool measure8bit = true;
1458 if (et != singleByte) {
1459 GdkWChar wctext[maxLengthTextRun];
1460 if (len >= maxLengthTextRun)
1461 len = maxLengthTextRun-1;
1462 int wclen;
1463 if (et == UTF8) {
1464 wclen = UTF16FromUTF8(s, len,
1465 static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
1466 } else { // dbcsMode, so convert using current locale
1467 char sDraw[maxLengthTextRun];
1468 memcpy(sDraw, s, len);
1469 sDraw[len] = '\0';
1470 wclen = gdk_mbstowcs(
1471 wctext, sDraw, maxLengthTextRun - 1);
1473 if (wclen > 0) {
1474 measure8bit = false;
1475 wctext[wclen] = L'\0';
1476 // Map widths back to utf-8 or DBCS input string
1477 int i = 0;
1478 for (int iU = 0; iU < wclen; iU++) {
1479 int width = gdk_char_width_wc(gf, wctext[iU]);
1480 totalWidth += width;
1481 int lenChar;
1482 if (et == UTF8) {
1483 lenChar = UTF8Len(s[i]);
1484 } else {
1485 lenChar = mblen(s+i, MB_CUR_MAX);
1486 if (lenChar < 0)
1487 lenChar = 1;
1489 while (lenChar--) {
1490 positions[i++] = totalWidth;
1493 while (i < len) { // In case of problems with lengths
1494 positions[i++] = totalWidth;
1498 if (measure8bit) {
1499 // Either Latin1 or conversion failed so treat as Latin1.
1500 for (int i = 0; i < len; i++) {
1501 int width = gdk_char_width(gf, s[i]);
1502 totalWidth += width;
1503 positions[i] = totalWidth;
1506 } else {
1507 // No font so return an ascending range of values
1508 for (int i = 0; i < len; i++) {
1509 positions[i] = i + 1;
1514 int SurfaceImpl::WidthText(Font &font_, const char *s, int len) {
1515 if (font_.GetID()) {
1516 if (PFont(font_)->pfd) {
1517 char *utfForm = 0;
1518 pango_layout_set_font_description(layout, PFont(font_)->pfd);
1519 PangoRectangle pos;
1520 bool useGFree = false;
1521 if (et == UTF8) {
1522 pango_layout_set_text(layout, s, len);
1523 } else {
1524 if (et == dbcs) {
1525 // Convert to utf8
1526 utfForm = UTF8FromDBCS(s, len);
1528 if (!utfForm) { // DBCS failed so treat as iconv
1529 SetConverter(PFont(font_)->characterSet);
1530 utfForm = UTF8FromIconv(conv, s, len);
1532 if (!utfForm) { // g_locale_to_utf8 failed so treat as Latin1
1533 utfForm = UTF8FromLatin1(s, len);
1535 pango_layout_set_text(layout, utfForm, len);
1537 #ifdef PANGO_VERSION
1538 PangoLayoutLine *pangoLine = pango_layout_get_line_readonly(layout,0);
1539 #else
1540 PangoLayoutLine *pangoLine = pango_layout_get_line(layout,0);
1541 #endif
1542 pango_layout_line_get_extents(pangoLine, NULL, &pos);
1543 if (useGFree) {
1544 g_free(utfForm);
1545 } else {
1546 delete []utfForm;
1548 return PANGO_PIXELS(pos.width);
1550 if (et == UTF8) {
1551 GdkWChar wctext[maxLengthTextRun];
1552 size_t wclen = UTF16FromUTF8(s, len, static_cast<wchar_t *>(static_cast<void *>(wctext)),
1553 sizeof(wctext) / sizeof(GdkWChar) - 1);
1554 wctext[wclen] = L'\0';
1555 return gdk_text_width_wc(PFont(font_)->pfont, wctext, wclen);
1556 } else {
1557 return gdk_text_width(PFont(font_)->pfont, s, len);
1559 } else {
1560 return 1;
1564 int SurfaceImpl::WidthChar(Font &font_, char ch) {
1565 if (font_.GetID()) {
1566 if (PFont(font_)->pfd) {
1567 return WidthText(font_, &ch, 1);
1569 return gdk_char_width(PFont(font_)->pfont, ch);
1570 } else {
1571 return 1;
1575 // Three possible strategies for determining ascent and descent of font:
1576 // 1) Call gdk_string_extents with string containing all letters, numbers and punctuation.
1577 // 2) Use the ascent and descent fields of GdkFont.
1578 // 3) Call gdk_string_extents with string as 1 but also including accented capitals.
1579 // Smallest values given by 1 and largest by 3 with 2 in between.
1580 // Techniques 1 and 2 sometimes chop off extreme portions of ascenders and
1581 // descenders but are mostly OK except for accented characters like Ã… which are
1582 // rarely used in code.
1584 // This string contains a good range of characters to test for size.
1585 //const char largeSizeString[] = "ÂÃÅÄ `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1586 // "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1587 #ifndef FAST_WAY
1588 const char sizeString[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1589 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1590 #endif
1592 int SurfaceImpl::Ascent(Font &font_) {
1593 if (!(font_.GetID()))
1594 return 1;
1595 #ifdef FAST_WAY
1596 FontMutexLock();
1597 int ascent = PFont(font_)->ascent;
1598 if ((ascent == 0) && (PFont(font_)->pfd)) {
1599 PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
1600 PFont(font_)->pfd, pango_context_get_language(pcontext));
1601 PFont(font_)->ascent =
1602 PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
1603 pango_font_metrics_unref(metrics);
1604 ascent = PFont(font_)->ascent;
1606 if ((ascent == 0) && (PFont(font_)->pfont)) {
1607 ascent = PFont(font_)->pfont->ascent;
1609 if (ascent == 0) {
1610 ascent = 1;
1612 FontMutexUnlock();
1613 return ascent;
1614 #else
1616 gint lbearing;
1617 gint rbearing;
1618 gint width;
1619 gint ascent;
1620 gint descent;
1622 gdk_string_extents(PFont(font_)->pfont, sizeString,
1623 &lbearing, &rbearing, &width, &ascent, &descent);
1624 return ascent;
1625 #endif
1628 int SurfaceImpl::Descent(Font &font_) {
1629 if (!(font_.GetID()))
1630 return 1;
1631 #ifdef FAST_WAY
1633 if (PFont(font_)->pfd) {
1634 PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
1635 PFont(font_)->pfd, pango_context_get_language(pcontext));
1636 int descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
1637 pango_font_metrics_unref(metrics);
1638 return descent;
1640 return PFont(font_)->pfont->descent;
1641 #else
1643 gint lbearing;
1644 gint rbearing;
1645 gint width;
1646 gint ascent;
1647 gint descent;
1649 gdk_string_extents(PFont(font_)->pfont, sizeString,
1650 &lbearing, &rbearing, &width, &ascent, &descent);
1651 return descent;
1652 #endif
1655 int SurfaceImpl::InternalLeading(Font &) {
1656 return 0;
1659 int SurfaceImpl::ExternalLeading(Font &) {
1660 return 0;
1663 int SurfaceImpl::Height(Font &font_) {
1664 return Ascent(font_) + Descent(font_);
1667 int SurfaceImpl::AverageCharWidth(Font &font_) {
1668 return WidthChar(font_, 'n');
1671 int SurfaceImpl::SetPalette(Palette *, bool) {
1672 // Handled in palette allocation for GTK so this does nothing
1673 return 0;
1676 void SurfaceImpl::SetClip(PRectangle rc) {
1677 GdkRectangle area = {rc.left, rc.top,
1678 rc.right - rc.left, rc.bottom - rc.top};
1679 gdk_gc_set_clip_rectangle(gc, &area);
1682 void SurfaceImpl::FlushCachedState() {}
1684 void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) {
1685 if (unicodeMode_)
1686 et = UTF8;
1689 void SurfaceImpl::SetDBCSMode(int codePage) {
1690 if (codePage && (codePage != SC_CP_UTF8))
1691 et = dbcs;
1694 Surface *Surface::Allocate() {
1695 return new SurfaceImpl;
1698 Window::~Window() {}
1700 void Window::Destroy() {
1701 if (wid)
1702 gtk_widget_destroy(GTK_WIDGET(wid));
1703 wid = 0;
1706 bool Window::HasFocus() {
1707 return GTK_WIDGET_HAS_FOCUS(wid);
1710 PRectangle Window::GetPosition() {
1711 // Before any size allocated pretend its 1000 wide so not scrolled
1712 PRectangle rc(0, 0, 1000, 1000);
1713 if (wid) {
1714 rc.left = PWidget(wid)->allocation.x;
1715 rc.top = PWidget(wid)->allocation.y;
1716 if (PWidget(wid)->allocation.width > 20) {
1717 rc.right = rc.left + PWidget(wid)->allocation.width;
1718 rc.bottom = rc.top + PWidget(wid)->allocation.height;
1721 return rc;
1724 void Window::SetPosition(PRectangle rc) {
1725 GtkAllocation alloc;
1726 alloc.x = rc.left;
1727 alloc.y = rc.top;
1728 alloc.width = rc.Width();
1729 alloc.height = rc.Height();
1730 gtk_widget_size_allocate(PWidget(wid), &alloc);
1733 void Window::SetPositionRelative(PRectangle rc, Window relativeTo) {
1734 int ox = 0;
1735 int oy = 0;
1736 gdk_window_get_origin(PWidget(relativeTo.wid)->window, &ox, &oy);
1737 ox += rc.left;
1738 if (ox < 0)
1739 ox = 0;
1740 oy += rc.top;
1741 if (oy < 0)
1742 oy = 0;
1744 /* do some corrections to fit into screen */
1745 int sizex = rc.right - rc.left;
1746 int sizey = rc.bottom - rc.top;
1747 int screenWidth = gdk_screen_width();
1748 int screenHeight = gdk_screen_height();
1749 if (sizex > screenWidth)
1750 ox = 0; /* the best we can do */
1751 else if (ox + sizex > screenWidth)
1752 ox = screenWidth - sizex;
1753 if (oy + sizey > screenHeight)
1754 oy = screenHeight - sizey;
1756 gtk_window_move(GTK_WINDOW(PWidget(wid)), ox, oy);
1758 gtk_widget_set_usize(PWidget(wid), sizex, sizey);
1761 PRectangle Window::GetClientPosition() {
1762 // On GTK+, the client position is the window position
1763 return GetPosition();
1766 void Window::Show(bool show) {
1767 if (show)
1768 gtk_widget_show(PWidget(wid));
1771 void Window::InvalidateAll() {
1772 if (wid) {
1773 gtk_widget_queue_draw(PWidget(wid));
1777 void Window::InvalidateRectangle(PRectangle rc) {
1778 if (wid) {
1779 gtk_widget_queue_draw_area(PWidget(wid),
1780 rc.left, rc.top,
1781 rc.right - rc.left, rc.bottom - rc.top);
1785 void Window::SetFont(Font &) {
1786 // Can not be done generically but only needed for ListBox
1789 void Window::SetCursor(Cursor curs) {
1790 // We don't set the cursor to same value numerous times under gtk because
1791 // it stores the cursor in the window once it's set
1792 if (curs == cursorLast)
1793 return;
1795 cursorLast = curs;
1796 GdkCursor *gdkCurs;
1797 switch (curs) {
1798 case cursorText:
1799 gdkCurs = gdk_cursor_new(GDK_XTERM);
1800 break;
1801 case cursorArrow:
1802 gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
1803 break;
1804 case cursorUp:
1805 gdkCurs = gdk_cursor_new(GDK_CENTER_PTR);
1806 break;
1807 case cursorWait:
1808 gdkCurs = gdk_cursor_new(GDK_WATCH);
1809 break;
1810 case cursorHand:
1811 gdkCurs = gdk_cursor_new(GDK_HAND2);
1812 break;
1813 case cursorReverseArrow:
1814 gdkCurs = gdk_cursor_new(GDK_RIGHT_PTR);
1815 break;
1816 default:
1817 gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
1818 cursorLast = cursorArrow;
1819 break;
1822 if (PWidget(wid)->window)
1823 gdk_window_set_cursor(PWidget(wid)->window, gdkCurs);
1824 gdk_cursor_destroy(gdkCurs);
1827 void Window::SetTitle(const char *s) {
1828 gtk_window_set_title(GTK_WINDOW(wid), s);
1831 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
1832 gdk window coordinates */
1833 PRectangle Window::GetMonitorRect(Point pt) {
1834 gint x_offset, y_offset;
1835 pt = pt;
1837 gdk_window_get_origin(PWidget(wid)->window, &x_offset, &y_offset);
1839 #if GTK_CHECK_VERSION(2,2,0)
1840 // GTK+ 2.2+
1842 GdkScreen* screen;
1843 gint monitor_num;
1844 GdkRectangle rect;
1846 screen = gtk_widget_get_screen(PWidget(wid));
1847 monitor_num = gdk_screen_get_monitor_at_point(screen, pt.x + x_offset, pt.y + y_offset);
1848 gdk_screen_get_monitor_geometry(screen, monitor_num, &rect);
1849 rect.x -= x_offset;
1850 rect.y -= y_offset;
1851 return PRectangle(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
1853 #else
1854 return PRectangle(-x_offset, -y_offset, (-x_offset) + gdk_screen_width(),
1855 (-y_offset) + gdk_screen_height());
1856 #endif
1859 struct ListImage {
1860 const char *xpm_data;
1861 GdkPixbuf *pixbuf;
1864 static void list_image_free(gpointer, gpointer value, gpointer) {
1865 ListImage *list_image = (ListImage *) value;
1866 if (list_image->pixbuf)
1867 gdk_pixbuf_unref (list_image->pixbuf);
1868 g_free(list_image);
1871 ListBox::ListBox() {
1874 ListBox::~ListBox() {
1877 enum {
1878 PIXBUF_COLUMN,
1879 TEXT_COLUMN,
1880 N_COLUMNS
1883 class ListBoxX : public ListBox {
1884 WindowID list;
1885 WindowID scroller;
1886 void *pixhash;
1887 GtkCellRenderer* pixbuf_renderer;
1888 XPMSet xset;
1889 int desiredVisibleRows;
1890 unsigned int maxItemCharacters;
1891 unsigned int aveCharWidth;
1892 public:
1893 CallBackAction doubleClickAction;
1894 void *doubleClickActionData;
1896 ListBoxX() : list(0), pixhash(NULL), pixbuf_renderer(0),
1897 desiredVisibleRows(5), maxItemCharacters(0),
1898 aveCharWidth(1), doubleClickAction(NULL), doubleClickActionData(NULL) {
1900 virtual ~ListBoxX() {
1901 if (pixhash) {
1902 g_hash_table_foreach((GHashTable *) pixhash, list_image_free, NULL);
1903 g_hash_table_destroy((GHashTable *) pixhash);
1906 virtual void SetFont(Font &font);
1907 virtual void Create(Window &parent, int ctrlID, Point location_, int lineHeight_, bool unicodeMode_);
1908 virtual void SetAverageCharWidth(int width);
1909 virtual void SetVisibleRows(int rows);
1910 virtual int GetVisibleRows() const;
1911 virtual PRectangle GetDesiredRect();
1912 virtual int CaretFromEdge();
1913 virtual void Clear();
1914 virtual void Append(char *s, int type = -1);
1915 virtual int Length();
1916 virtual void Select(int n);
1917 virtual int GetSelection();
1918 virtual int Find(const char *prefix);
1919 virtual void GetValue(int n, char *value, int len);
1920 virtual void RegisterImage(int type, const char *xpm_data);
1921 virtual void ClearRegisteredImages();
1922 virtual void SetDoubleClickAction(CallBackAction action, void *data) {
1923 doubleClickAction = action;
1924 doubleClickActionData = data;
1926 virtual void SetList(const char *listText, char separator, char typesep);
1929 ListBox *ListBox::Allocate() {
1930 ListBoxX *lb = new ListBoxX();
1931 return lb;
1934 static gboolean ButtonPress(GtkWidget *, GdkEventButton* ev, gpointer p) {
1935 try {
1936 ListBoxX* lb = reinterpret_cast<ListBoxX*>(p);
1937 if (ev->type == GDK_2BUTTON_PRESS && lb->doubleClickAction != NULL) {
1938 lb->doubleClickAction(lb->doubleClickActionData);
1939 return TRUE;
1942 } catch (...) {
1943 // No pointer back to Scintilla to save status
1945 return FALSE;
1948 /* Change the active color to the selected color so the listbox uses the color
1949 scheme that it would use if it had the focus. */
1950 static void StyleSet(GtkWidget *w, GtkStyle*, void*) {
1951 GtkStyle* style;
1953 g_return_if_fail(w != NULL);
1955 /* Copy the selected color to active. Note that the modify calls will cause
1956 recursive calls to this function after the value is updated and w->style to
1957 be set to a new object */
1958 style = gtk_widget_get_style(w);
1959 if (style == NULL)
1960 return;
1961 if (!gdk_color_equal(&style->base[GTK_STATE_SELECTED], &style->base[GTK_STATE_ACTIVE]))
1962 gtk_widget_modify_base(w, GTK_STATE_ACTIVE, &style->base[GTK_STATE_SELECTED]);
1964 style = gtk_widget_get_style(w);
1965 if (style == NULL)
1966 return;
1967 if (!gdk_color_equal(&style->text[GTK_STATE_SELECTED], &style->text[GTK_STATE_ACTIVE]))
1968 gtk_widget_modify_text(w, GTK_STATE_ACTIVE, &style->text[GTK_STATE_SELECTED]);
1971 void ListBoxX::Create(Window &, int, Point, int, bool) {
1972 wid = gtk_window_new(GTK_WINDOW_POPUP);
1974 GtkWidget *frame = gtk_frame_new(NULL);
1975 gtk_widget_show(frame);
1976 gtk_container_add(GTK_CONTAINER(GetID()), frame);
1977 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1978 gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
1980 scroller = gtk_scrolled_window_new(NULL, NULL);
1981 gtk_container_set_border_width(GTK_CONTAINER(scroller), 0);
1982 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1983 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1984 gtk_container_add(GTK_CONTAINER(frame), PWidget(scroller));
1985 gtk_widget_show(PWidget(scroller));
1987 /* Tree and its model */
1988 GtkListStore *store =
1989 gtk_list_store_new(N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING);
1991 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1992 g_signal_connect(G_OBJECT(list), "style-set", G_CALLBACK(StyleSet), NULL);
1994 GtkTreeSelection *selection =
1995 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
1996 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1997 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
1998 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list), FALSE);
2000 /* Columns */
2001 GtkTreeViewColumn *column = gtk_tree_view_column_new();
2002 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2003 gtk_tree_view_column_set_title(column, "Autocomplete");
2005 pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
2006 gtk_cell_renderer_set_fixed_size(pixbuf_renderer, 0, -1);
2007 gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE);
2008 gtk_tree_view_column_add_attribute(column, pixbuf_renderer,
2009 "pixbuf", PIXBUF_COLUMN);
2011 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
2012 gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), 1);
2013 gtk_tree_view_column_pack_start(column, renderer, TRUE);
2014 gtk_tree_view_column_add_attribute(column, renderer,
2015 "text", TEXT_COLUMN);
2017 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
2018 if (g_object_class_find_property(G_OBJECT_GET_CLASS(list), "fixed-height-mode"))
2019 g_object_set(G_OBJECT(list), "fixed-height-mode", TRUE, NULL);
2021 GtkWidget *wid = PWidget(list); // No code inside the G_OBJECT macro
2022 gtk_container_add(GTK_CONTAINER(PWidget(scroller)), wid);
2023 gtk_widget_show(wid);
2024 g_signal_connect(G_OBJECT(wid), "button_press_event",
2025 G_CALLBACK(ButtonPress), this);
2026 gtk_widget_realize(PWidget(wid));
2029 void ListBoxX::SetFont(Font &scint_font) {
2030 // Only do for Pango font as there have been crashes for GDK fonts
2031 if (Created() && PFont(scint_font)->pfd) {
2032 // Current font is Pango font
2033 gtk_widget_modify_font(PWidget(list), PFont(scint_font)->pfd);
2037 void ListBoxX::SetAverageCharWidth(int width) {
2038 aveCharWidth = width;
2041 void ListBoxX::SetVisibleRows(int rows) {
2042 desiredVisibleRows = rows;
2045 int ListBoxX::GetVisibleRows() const {
2046 return desiredVisibleRows;
2049 PRectangle ListBoxX::GetDesiredRect() {
2050 // Before any size allocated pretend its 100 wide so not scrolled
2051 PRectangle rc(0, 0, 100, 100);
2052 if (wid) {
2053 int rows = Length();
2054 if ((rows == 0) || (rows > desiredVisibleRows))
2055 rows = desiredVisibleRows;
2057 GtkRequisition req;
2058 int height;
2060 // First calculate height of the clist for our desired visible
2061 // row count otherwise it tries to expand to the total # of rows
2062 // Get cell height
2063 int row_width=0;
2064 int row_height=0;
2065 GtkTreeViewColumn * column =
2066 gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
2067 gtk_tree_view_column_cell_get_size(column, NULL,
2068 NULL, NULL, &row_width, &row_height);
2069 int ythickness = PWidget(list)->style->ythickness;
2070 height = (rows * row_height
2071 + 2 * (ythickness
2072 + GTK_CONTAINER(PWidget(list))->border_width + 1));
2073 gtk_widget_set_usize(GTK_WIDGET(PWidget(list)), -1, height);
2075 // Get the size of the scroller because we set usize on the window
2076 gtk_widget_size_request(GTK_WIDGET(scroller), &req);
2077 rc.right = req.width;
2078 rc.bottom = req.height;
2080 gtk_widget_set_usize(GTK_WIDGET(list), -1, -1);
2081 int width = maxItemCharacters;
2082 if (width < 12)
2083 width = 12;
2084 rc.right = width * (aveCharWidth + aveCharWidth / 3);
2085 if (Length() > rows)
2086 rc.right = rc.right + 16;
2088 return rc;
2091 int ListBoxX::CaretFromEdge() {
2092 gint renderer_width, renderer_height;
2093 gtk_cell_renderer_get_fixed_size(pixbuf_renderer, &renderer_width,
2094 &renderer_height);
2095 return 4 + renderer_width;
2098 void ListBoxX::Clear() {
2099 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2100 gtk_list_store_clear(GTK_LIST_STORE(model));
2101 maxItemCharacters = 0;
2104 static void init_pixmap(ListImage *list_image) {
2105 const char *textForm = list_image->xpm_data;
2106 const char * const * xpm_lineform = reinterpret_cast<const char * const *>(textForm);
2107 const char **xpm_lineformfromtext = 0;
2108 // The XPM data can be either in atext form as will be read from a file
2109 // or in a line form (array of char *) as will be used for images defined in code.
2110 // Test for text form and convert to line form
2111 if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
2112 // Test done is two parts to avoid possibility of overstepping the memory
2113 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
2114 xpm_lineformfromtext = XPM::LinesFormFromTextForm(textForm);
2115 xpm_lineform = xpm_lineformfromtext;
2118 // Drop any existing pixmap/bitmap as data may have changed
2119 if (list_image->pixbuf)
2120 gdk_pixbuf_unref(list_image->pixbuf);
2121 list_image->pixbuf =
2122 gdk_pixbuf_new_from_xpm_data((const gchar**)xpm_lineform);
2123 delete []xpm_lineformfromtext;
2126 #define SPACING 5
2128 void ListBoxX::Append(char *s, int type) {
2129 ListImage *list_image = NULL;
2130 if ((type >= 0) && pixhash) {
2131 list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash
2132 , (gconstpointer) GINT_TO_POINTER(type));
2134 GtkTreeIter iter;
2135 GtkListStore *store =
2136 GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
2137 gtk_list_store_append(GTK_LIST_STORE(store), &iter);
2138 if (list_image) {
2139 if (NULL == list_image->pixbuf)
2140 init_pixmap(list_image);
2141 if (list_image->pixbuf) {
2142 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2143 PIXBUF_COLUMN, list_image->pixbuf,
2144 TEXT_COLUMN, s, -1);
2146 gint pixbuf_width = gdk_pixbuf_get_width(list_image->pixbuf);
2147 gint renderer_height, renderer_width;
2148 gtk_cell_renderer_get_fixed_size(pixbuf_renderer,
2149 &renderer_width, &renderer_height);
2150 if (pixbuf_width > renderer_width)
2151 gtk_cell_renderer_set_fixed_size(pixbuf_renderer,
2152 pixbuf_width, -1);
2153 } else {
2154 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2155 TEXT_COLUMN, s, -1);
2157 } else {
2158 gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2159 TEXT_COLUMN, s, -1);
2161 size_t len = strlen(s);
2162 if (maxItemCharacters < len)
2163 maxItemCharacters = len;
2166 int ListBoxX::Length() {
2167 if (wid)
2168 return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
2169 (GTK_TREE_VIEW(list)), NULL);
2170 return 0;
2173 void ListBoxX::Select(int n) {
2174 GtkTreeIter iter;
2175 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2176 GtkTreeSelection *selection =
2177 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2179 if (n < 0) {
2180 gtk_tree_selection_unselect_all(selection);
2181 return;
2184 bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
2185 if (valid) {
2186 gtk_tree_selection_select_iter(selection, &iter);
2188 // Move the scrollbar to show the selection.
2189 int total = Length();
2190 GtkAdjustment *adj =
2191 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list));
2192 gfloat value = ((gfloat)n / total) * (adj->upper - adj->lower)
2193 + adj->lower - adj->page_size / 2;
2195 // Get cell height
2196 int row_width;
2197 int row_height;
2198 GtkTreeViewColumn * column =
2199 gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
2200 gtk_tree_view_column_cell_get_size(column, NULL, NULL,
2201 NULL, &row_width, &row_height);
2203 int rows = Length();
2204 if ((rows == 0) || (rows > desiredVisibleRows))
2205 rows = desiredVisibleRows;
2206 if (rows & 0x1) {
2207 // Odd rows to display -- We are now in the middle.
2208 // Align it so that we don't chop off rows.
2209 value += (gfloat)row_height / 2.0;
2211 // Clamp it.
2212 value = (value < 0)? 0 : value;
2213 value = (value > (adj->upper - adj->page_size))?
2214 (adj->upper - adj->page_size) : value;
2216 // Set it.
2217 gtk_adjustment_set_value(adj, value);
2218 } else {
2219 gtk_tree_selection_unselect_all(selection);
2223 int ListBoxX::GetSelection() {
2224 GtkTreeIter iter;
2225 GtkTreeModel *model;
2226 GtkTreeSelection *selection;
2227 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2228 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
2229 GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
2230 int *indices = gtk_tree_path_get_indices(path);
2231 // Don't free indices.
2232 if (indices)
2233 return indices[0];
2235 return -1;
2238 int ListBoxX::Find(const char *prefix) {
2239 GtkTreeIter iter;
2240 GtkTreeModel *model =
2241 gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2242 bool valid = gtk_tree_model_get_iter_first(model, &iter) != FALSE;
2243 int i = 0;
2244 while(valid) {
2245 gchar *s;
2246 gtk_tree_model_get(model, &iter, TEXT_COLUMN, &s, -1);
2247 if (s && (0 == strncmp(prefix, s, strlen(prefix)))) {
2248 return i;
2250 valid = gtk_tree_model_iter_next(model, &iter) != FALSE;
2251 i++;
2253 return -1;
2256 void ListBoxX::GetValue(int n, char *value, int len) {
2257 char *text = NULL;
2258 GtkTreeIter iter;
2259 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2260 bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
2261 if (valid) {
2262 gtk_tree_model_get(model, &iter, TEXT_COLUMN, &text, -1);
2264 if (text && len > 0) {
2265 strncpy(value, text, len);
2266 value[len - 1] = '\0';
2267 } else {
2268 value[0] = '\0';
2272 // g_return_if_fail causes unnecessary compiler warning in release compile.
2273 #ifdef _MSC_VER
2274 #pragma warning(disable: 4127)
2275 #endif
2277 void ListBoxX::RegisterImage(int type, const char *xpm_data) {
2278 g_return_if_fail(xpm_data);
2280 // Saved and use the saved copy so caller's copy can disappear.
2281 xset.Add(type, xpm_data);
2282 XPM *pxpm = xset.Get(type);
2283 xpm_data = reinterpret_cast<const char *>(pxpm->InLinesForm());
2285 if (!pixhash) {
2286 pixhash = g_hash_table_new(g_direct_hash, g_direct_equal);
2288 ListImage *list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash,
2289 (gconstpointer) GINT_TO_POINTER(type));
2290 if (list_image) {
2291 // Drop icon already registered
2292 if (list_image->pixbuf)
2293 gdk_pixbuf_unref(list_image->pixbuf);
2294 list_image->pixbuf = NULL;
2295 list_image->xpm_data = xpm_data;
2296 } else {
2297 list_image = g_new0(ListImage, 1);
2298 list_image->xpm_data = xpm_data;
2299 g_hash_table_insert((GHashTable *) pixhash, GINT_TO_POINTER(type),
2300 (gpointer) list_image);
2304 void ListBoxX::ClearRegisteredImages() {
2305 xset.Clear();
2308 void ListBoxX::SetList(const char *listText, char separator, char typesep) {
2309 Clear();
2310 int count = strlen(listText) + 1;
2311 char *words = new char[count];
2312 if (words) {
2313 memcpy(words, listText, count);
2314 char *startword = words;
2315 char *numword = NULL;
2316 int i = 0;
2317 for (; words[i]; i++) {
2318 if (words[i] == separator) {
2319 words[i] = '\0';
2320 if (numword)
2321 *numword = '\0';
2322 Append(startword, numword?atoi(numword + 1):-1);
2323 startword = words + i + 1;
2324 numword = NULL;
2325 } else if (words[i] == typesep) {
2326 numword = words + i;
2329 if (startword) {
2330 if (numword)
2331 *numword = '\0';
2332 Append(startword, numword?atoi(numword + 1):-1);
2334 delete []words;
2338 Menu::Menu() : mid(0) {}
2340 void Menu::CreatePopUp() {
2341 Destroy();
2342 mid = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
2345 void Menu::Destroy() {
2346 if (mid)
2347 g_object_unref(G_OBJECT(mid));
2348 mid = 0;
2351 void Menu::Show(Point pt, Window &) {
2352 int screenHeight = gdk_screen_height();
2353 int screenWidth = gdk_screen_width();
2354 GtkItemFactory *factory = reinterpret_cast<GtkItemFactory *>(mid);
2355 GtkWidget *widget = gtk_item_factory_get_widget(factory, "<main>");
2356 gtk_widget_show_all(widget);
2357 GtkRequisition requisition;
2358 gtk_widget_size_request(widget, &requisition);
2359 if ((pt.x + requisition.width) > screenWidth) {
2360 pt.x = screenWidth - requisition.width;
2362 if ((pt.y + requisition.height) > screenHeight) {
2363 pt.y = screenHeight - requisition.height;
2365 gtk_item_factory_popup(factory, pt.x - 4, pt.y - 4, 3,
2366 gtk_get_current_event_time());
2369 ElapsedTime::ElapsedTime() {
2370 GTimeVal curTime;
2371 g_get_current_time(&curTime);
2372 bigBit = curTime.tv_sec;
2373 littleBit = curTime.tv_usec;
2376 class DynamicLibraryImpl : public DynamicLibrary {
2377 protected:
2378 GModule* m;
2379 public:
2380 DynamicLibraryImpl(const char *modulePath) {
2381 m = g_module_open(modulePath, G_MODULE_BIND_LAZY);
2384 virtual ~DynamicLibraryImpl() {
2385 if (m != NULL)
2386 g_module_close(m);
2389 // Use g_module_symbol to get a pointer to the relevant function.
2390 virtual Function FindFunction(const char *name) {
2391 if (m != NULL) {
2392 gpointer fn_address = NULL;
2393 gboolean status = g_module_symbol(m, name, &fn_address);
2394 if (status)
2395 return static_cast<Function>(fn_address);
2396 else
2397 return NULL;
2398 } else
2399 return NULL;
2402 virtual bool IsValid() {
2403 return m != NULL;
2407 DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {
2408 return static_cast<DynamicLibrary *>( new DynamicLibraryImpl(modulePath) );
2411 double ElapsedTime::Duration(bool reset) {
2412 GTimeVal curTime;
2413 g_get_current_time(&curTime);
2414 long endBigBit = curTime.tv_sec;
2415 long endLittleBit = curTime.tv_usec;
2416 double result = 1000000.0 * (endBigBit - bigBit);
2417 result += endLittleBit - littleBit;
2418 result /= 1000000.0;
2419 if (reset) {
2420 bigBit = endBigBit;
2421 littleBit = endLittleBit;
2423 return result;
2426 ColourDesired Platform::Chrome() {
2427 return ColourDesired(0xe0, 0xe0, 0xe0);
2430 ColourDesired Platform::ChromeHighlight() {
2431 return ColourDesired(0xff, 0xff, 0xff);
2434 const char *Platform::DefaultFont() {
2435 #ifdef G_OS_WIN32
2436 return "Lucida Console";
2437 #else
2438 return "!Sans";
2439 #endif
2442 int Platform::DefaultFontSize() {
2443 #ifdef G_OS_WIN32
2444 return 10;
2445 #else
2446 return 12;
2447 #endif
2450 unsigned int Platform::DoubleClickTime() {
2451 return 500; // Half a second
2454 bool Platform::MouseButtonBounce() {
2455 return true;
2458 void Platform::DebugDisplay(const char *s) {
2459 fprintf(stderr, "%s", s);
2462 bool Platform::IsKeyDown(int) {
2463 // TODO: discover state of keys in GTK+/X
2464 return false;
2467 long Platform::SendScintilla(
2468 WindowID w, unsigned int msg, unsigned long wParam, long lParam) {
2469 return scintilla_send_message(SCINTILLA(w), msg, wParam, lParam);
2472 long Platform::SendScintillaPointer(
2473 WindowID w, unsigned int msg, unsigned long wParam, void *lParam) {
2474 return scintilla_send_message(SCINTILLA(w), msg, wParam,
2475 reinterpret_cast<sptr_t>(lParam));
2478 bool Platform::IsDBCSLeadByte(int codePage, char ch) {
2479 // Byte ranges found in Wikipedia articles with relevant search strings in each case
2480 unsigned char uch = static_cast<unsigned char>(ch);
2481 switch (codePage) {
2482 case 932:
2483 // Shift_jis
2484 return ((uch >= 0x81) && (uch <= 0x9F)) ||
2485 ((uch >= 0xE0) && (uch <= 0xEF));
2486 case 936:
2487 // GBK
2488 return (uch >= 0x81) && (uch <= 0xFE);
2489 case 950:
2490 // Big5
2491 return (uch >= 0x81) && (uch <= 0xFE);
2492 // Korean EUC-KR may be code page 949.
2494 return false;
2497 int Platform::DBCSCharLength(int codePage, const char *s) {
2498 if (codePage == 932 || codePage == 936 || codePage == 950) {
2499 return IsDBCSLeadByte(codePage, s[0]) ? 2 : 1;
2500 } else {
2501 int bytes = mblen(s, MB_CUR_MAX);
2502 if (bytes >= 1)
2503 return bytes;
2504 else
2505 return 1;
2509 int Platform::DBCSCharMaxLength() {
2510 return MB_CUR_MAX;
2511 //return 2;
2514 // These are utility functions not really tied to a platform
2516 int Platform::Minimum(int a, int b) {
2517 if (a < b)
2518 return a;
2519 else
2520 return b;
2523 int Platform::Maximum(int a, int b) {
2524 if (a > b)
2525 return a;
2526 else
2527 return b;
2530 //#define TRACE
2532 #ifdef TRACE
2533 void Platform::DebugPrintf(const char *format, ...) {
2534 char buffer[2000];
2535 va_list pArguments;
2536 va_start(pArguments, format);
2537 vsprintf(buffer, format, pArguments);
2538 va_end(pArguments);
2539 Platform::DebugDisplay(buffer);
2541 #else
2542 void Platform::DebugPrintf(const char *, ...) {}
2544 #endif
2546 // Not supported for GTK+
2547 static bool assertionPopUps = true;
2549 bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {
2550 bool ret = assertionPopUps;
2551 assertionPopUps = assertionPopUps_;
2552 return ret;
2555 void Platform::Assert(const char *c, const char *file, int line) {
2556 char buffer[2000];
2557 sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line);
2558 strcat(buffer, "\r\n");
2559 Platform::DebugDisplay(buffer);
2560 abort();
2563 int Platform::Clamp(int val, int minVal, int maxVal) {
2564 if (val > maxVal)
2565 val = maxVal;
2566 if (val < minVal)
2567 val = minVal;
2568 return val;
2571 void Platform_Initialise() {
2572 FontMutexAllocate();
2575 void Platform_Finalise() {
2576 FontMutexFree();