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.
15
#include <gdk/gdkkeysyms.h>
19
#include "Scintilla.h"
20
#include "ScintillaWidget.h"
21
#include "UniConversion.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. */
34
#include "Converter.h"
37
// Ignore unreferenced local functions in GTK+ headers
38
#pragma warning(disable: 4505)
42
using namespace Scintilla;
45
enum encodingType { singleByte, UTF8, dbcs};
56
static GMutex *fontMutex = NULL;
58
static void InitializeGLIBThreads() {
59
if (!g_thread_supported()) {
65
static void FontMutexAllocate() {
68
InitializeGLIBThreads();
69
fontMutex = g_mutex_new();
74
static void FontMutexFree() {
77
g_mutex_free(fontMutex);
83
static void FontMutexLock() {
85
g_mutex_lock(fontMutex);
89
static void FontMutexUnlock() {
92
g_mutex_unlock(fontMutex);
97
// On GTK+ 1.x holds a GdkFont* but on GTK+ 2.x can hold a GdkFont* or a
98
// PangoFontDescription*.
105
PangoFontDescription *pfd;
107
FontHandle(GdkFont *pfont_) {
115
FontHandle(PangoFontDescription *pfd_, int characterSet_) {
120
characterSet = characterSet_;
125
gdk_font_unref(pfont);
128
pango_font_description_free(pfd);
131
void ResetWidths(encodingType et_) {
133
for (int i=0; i<=127; i++) {
137
int CharWidth(unsigned char ch, encodingType et_) {
140
if ((ch <= 127) && (et == et_)) {
146
void SetCharWidth(unsigned char ch, int w, encodingType et_) {
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) {
175
Platform::LowShortFromLong(lpoint),
176
Platform::HighShortFromLong(lpoint));
181
allowRealization = false;
182
allocatedPalette = 0;
185
entries = new ColourPair[size];
188
Palette::~Palette() {
194
void Palette::Release() {
196
delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
197
allocatedPalette = 0;
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) {
209
for (int i=0; i < used; i++) {
210
if (entries[i].desired == cp.desired)
215
int sizeNew = size * 2;
216
ColourPair *entriesNew = new ColourPair[sizeNew];
217
for (int j=0; j<size; j++) {
218
entriesNew[j] = entries[j];
221
entries = entriesNew;
225
entries[used].desired = cp.desired;
226
entries[used].allocated.Set(cp.desired.AsLong());
229
for (int i=0; i < used; i++) {
230
if (entries[i].desired == cp.desired) {
231
cp.allocated = entries[i].allocated;
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),
244
delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
245
allocatedPalette = 0;
248
GdkColor *paletteNew = new GdkColor[used];
249
allocatedPalette = paletteNew;
250
gboolean *successPalette = new gboolean[used];
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,
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:
274
case SC_CHARSET_DEFAULT:
276
case SC_CHARSET_BALTIC:
278
case SC_CHARSET_CHINESEBIG5:
280
case SC_CHARSET_EASTEUROPE:
282
case SC_CHARSET_GB2312:
283
return "gb2312.1980-*";
284
case SC_CHARSET_GREEK:
286
case SC_CHARSET_HANGUL:
287
return "ksc5601.1987-*";
292
case SC_CHARSET_RUSSIAN:
294
case SC_CHARSET_CYRILLIC:
296
case SC_CHARSET_SHIFTJIS:
297
return "jisx0208.1983-*";
298
case SC_CHARSET_SYMBOL:
300
case SC_CHARSET_TURKISH:
302
case SC_CHARSET_JOHAB:
304
case SC_CHARSET_HEBREW:
306
case SC_CHARSET_ARABIC:
308
case SC_CHARSET_VIETNAMESE:
310
case SC_CHARSET_THAI:
312
case SC_CHARSET_8859_15:
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:
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
340
if (strchr(fontName, '-')) {
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, '-');
349
d3 = strchr(d2 + 1, '-');
351
// foundary-fontface-isoxxx-x
355
strncpy(faceName, tmp, foundary_len - 1);
356
strncpy(charset, d2 + 1, charset_len - 1);
360
strcpy(foundary, "-*-");
361
strncpy(faceName, tmp, faceName_len - 1);
362
strncpy(charset, d1 + 1, charset_len - 1);
367
strncpy(faceName, tmp, faceName_len - 1);
368
strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
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));
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) {
394
(characterSet << 10) ^
395
(bold ? 0x10000000 : 0) ^
396
(italic ? 0x20000000 : 0) ^
400
class FontCached : Font {
405
FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
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;
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_);
427
bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
431
lf.italic == italic_ &&
432
lf.characterSet == characterSet_ &&
433
0 == strcmp(lf.faceName, faceName_);
436
void FontCached::Release() {
442
FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
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_)) {
454
FontCached *fc = new FontCached(faceName_, characterSet_, size_, bold_, italic_);
465
void FontCached::ReleaseId(FontID fid_) {
467
FontCached **pcur = &first;
468
for (FontCached *cur = first; cur; cur = cur->next) {
469
if (cur->fid == fid_) {
471
if (cur->usage == 0) {
484
static GdkFont *LoadFontOrSet(const char *fontspec, int characterSet) {
485
if (IsDBCSCharacterSet(characterSet)) {
486
return gdk_fontset_load(fontspec);
488
return gdk_font_load(fontspec);
492
FontID FontCached::CreateNewFont(const char *fontName, int characterSet,
493
int size, bool bold, bool italic) {
505
if (fontName[0] == '!') {
506
PangoFontDescription *pfd = pango_font_description_new();
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);
517
// If name of the font begins with a '-', assume, that it is
519
if (fontName[0] == '-') {
520
if (strchr(fontName, ',') || IsDBCSCharacterSet(characterSet)) {
521
newid = gdk_fontset_load(fontName);
523
newid = gdk_font_load(fontName);
526
// Font not available so substitute a reasonable code font
527
// iso8859 appears to only allow western characters.
528
newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
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, ',');
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";
557
*fp = '\0'; // nullify the comma
558
GenerateFontSpecStrings(fn, characterSet,
559
foundary, sizeof(foundary),
560
faceName, sizeof(faceName),
561
charset, sizeof(charset));
564
sizeof(fontspec) - 1,
567
bold ? "-bold" : "-medium",
568
italic ? "-i" : "-r",
572
// if this is the first font in the list, and
573
// we are doing italic, add an oblique font
575
if (italic && fontset[0] == '\0') {
576
strncat(fontset, fontspec, remaining - 1);
577
remaining -= strlen(fontset);
580
sizeof(fontspec) - 1,
581
",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s",
583
bold ? "-bold" : "-medium",
588
strncat(fontset, fontspec, remaining - 1);
589
remaining -= strlen(fontset);
595
fp = strchr(fn, ',');
598
newid = gdk_fontset_load(fontset);
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));
615
sizeof(fontspec) - 1,
616
"%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
618
bold ? "-bold" : "-medium",
619
italic ? "-i" : "-r",
622
newid = LoadFontOrSet(fontspec, characterSet);
624
// some fonts have oblique, not italic
626
sizeof(fontspec) - 1,
627
"%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
629
bold ? "-bold" : "-medium",
630
italic ? "-o" : "-r",
633
newid = LoadFontOrSet(fontspec, characterSet);
637
sizeof(fontspec) - 1,
638
"-*-*-*-*-*-*-*-%0d-*-*-*-*-%s",
641
newid = gdk_font_load(fontspec);
644
// Font not available so substitute a reasonable code font
645
// iso8859 appears to only allow western characters.
646
newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
649
return new FontHandle(newid);
652
Font::Font() : fid(0) {}
656
void Font::Create(const char *faceName, int characterSet, int size,
657
bool bold, bool italic, int) {
659
fid = FontCached::FindOrCreate(faceName, characterSet, size, bold, italic);
662
void Font::Release() {
664
FontCached::ReleaseId(fid);
670
namespace Scintilla {
672
class SurfaceImpl : public Surface {
674
GdkDrawable *drawable;
681
PangoContext *pcontext;
685
void SetConverter(int characterSet_);
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);
696
void PenColour(ColourAllocated fore);
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);
736
const char *CharacterSetID(int characterSet) {
737
switch (characterSet) {
738
case SC_CHARSET_ANSI:
740
case SC_CHARSET_DEFAULT:
742
case SC_CHARSET_BALTIC:
743
return "ISO-8859-13";
744
case SC_CHARSET_CHINESEBIG5:
746
case SC_CHARSET_EASTEUROPE:
748
case SC_CHARSET_GB2312:
750
case SC_CHARSET_GREEK:
752
case SC_CHARSET_HANGUL:
758
case SC_CHARSET_RUSSIAN:
760
case SC_CHARSET_CYRILLIC:
762
case SC_CHARSET_SHIFTJIS:
764
case SC_CHARSET_SYMBOL:
766
case SC_CHARSET_TURKISH:
768
case SC_CHARSET_JOHAB:
770
case SC_CHARSET_HEBREW:
772
case SC_CHARSET_ARABIC:
774
case SC_CHARSET_VIETNAMESE:
776
case SC_CHARSET_THAI:
777
return "ISO-8859-11";
778
case SC_CHARSET_8859_15:
779
return "ISO-8859-15";
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() {
801
void SurfaceImpl::Release() {
810
gdk_pixmap_unref(ppixmap);
813
g_object_unref(layout);
816
g_object_unref(pcontext);
826
bool SurfaceImpl::Initialised() {
830
void SurfaceImpl::Init(WindowID wid) {
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);
840
void SurfaceImpl::Init(SurfaceID sid, WindowID wid) {
841
PLATFORM_ASSERT(sid);
842
GdkDrawable *drawable_ = reinterpret_cast<GdkDrawable *>(sid);
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);
855
void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID wid) {
856
PLATFORM_ASSERT(surface_);
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);
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);
875
void SurfaceImpl::PenColour(ColourAllocated fore) {
878
co.pixel = fore.AsLong();
879
gdk_gc_set_foreground(gc, &co);
883
int SurfaceImpl::LogPixelsY() {
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_) {
897
void SurfaceImpl::LineTo(int x_, int y_) {
898
if (drawable && gc) {
899
gdk_draw_line(drawable, gc,
907
void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore,
908
ColourAllocated back) {
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;
916
gdk_draw_polygon(drawable, gc, 1, gpts, npts);
918
gdk_draw_polygon(drawable, gc, 0, gpts, npts);
922
void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
923
if (gc && drawable) {
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);
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,
934
rc.right - rc.left - 1, rc.bottom - rc.top - 1);
938
void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) {
940
if (drawable && (rc.left < maxCoordinate)) { // Protect against out of range
941
gdk_draw_rectangle(drawable, gc, 1,
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
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,
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
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);
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) {
1011
static guint32 u32FromRGBA(guint8 r, guint8 g, guint8 b, guint8 a) {
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;
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);
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) {
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,
1073
// The subtraction of 1 here is similar to the case for RectangleDraw
1075
gdk_draw_arc(drawable, gc, 0,
1077
rc.right - rc.left - 1, rc.bottom - rc.top - 1,
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,
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);
1096
else if (uch < (0x80 + 0x40 + 0x20))
1102
char *UTF8FromLatin1(const char *s, int &len) {
1103
char *utfForm = new char[len*2+1];
1105
for (int i=0;i<len;i++) {
1106
unsigned int uch = static_cast<unsigned char>(s[i]);
1108
utfForm[lenU++] = uch;
1110
utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
1111
utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1114
utfForm[lenU] = '\0';
1119
static char *UTF8FromIconv(const Converter &conv, const char *s, int &len) {
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))) {
1129
len = pout - utfForm;
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++) {
1142
char *pin = const_cast<char *>(s);
1143
size_t inLeft = lenMB;
1144
char *pout = wcForm;
1146
size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1147
if (conversions != ((size_t)(-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
1157
for (int i = 0; i < wclen && wctext[i]; i++) {
1158
unsigned int uch = wctext[i];
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));
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';
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);
1179
// In the annoying case when non-locale chars in the line.
1180
// e.g. latin1 chars in Japanese locale.
1185
char *utfForm = UTF8FromGdkWChar(wctext, wclen);
1187
len = strlen(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;
1196
} else if (ch < 0x80 + 0x40 + 0x20) {
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) {
1210
if (gc && drawable) {
1211
int xText = rc.left;
1212
if (PFont(font_)->pfd) {
1214
bool useGFree = false;
1216
pango_layout_set_text(layout, s, len);
1219
SetConverter(PFont(font_)->characterSet);
1220
utfForm = UTF8FromIconv(conv, s, len);
1222
if (!utfForm) { // iconv failed so try DBCS if DBCS mode
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);
1237
PangoLayoutLine *pll = pango_layout_get_line(layout,0);
1239
gdk_draw_layout_line(drawable, gc, xText, ybase, pll);
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;
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);
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);
1274
if (wclen > 0) { // Avoid next calculation if possible as may be expensive
1275
xText += gdk_text_width_wc(PFont(font_)->pfont,
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);
1288
if (len > 0) { // Avoid next calculation if possible as may be expensive
1289
xText += gdk_text_width(PFont(font_)->pfont, 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++) {
1315
DrawTextBase(rc, font_, ybase, s, len, fore);
1321
class ClusterIterator {
1322
PangoLayoutIter *iter;
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);
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);
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()) {
1358
const int lenPositions = len;
1359
if (PFont(font_)->pfd) {
1361
int width = PFont(font_)->CharWidth(*s, et);
1363
positions[0] = width;
1367
pango_layout_set_font_description(layout, PFont(font_)->pfd);
1369
// Simple and direct as UTF-8 is native Pango encoding
1371
pango_layout_set_text(layout, s, len);
1372
ClusterIterator iti(layout, lenPositions);
1373
while (!iti.finished) {
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;
1385
PLATFORM_ASSERT(i == lenPositions);
1387
int positionsCalculated = 0;
1389
SetConverter(PFont(font_)->characterSet);
1390
char *utfForm = UTF8FromIconv(conv, s, len);
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));
1398
int clusterStart = 0;
1399
ClusterIterator iti(layout, strlen(utfForm));
1400
while (!iti.finished) {
1402
int clusterEnd = iti.curIndex;
1403
int places = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
1405
while (clusterStart < clusterEnd) {
1406
size_t lenChar = MultiByteLenFromIconv(convMeasure, s+i, len-i);
1408
positions[i++] = iti.position - (places - place) * iti.distance / places;
1409
positionsCalculated++;
1411
clusterStart += UTF8CharLength(utfForm+clusterStart);
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);
1425
utfForm = UTF8FromLatin1(s, len);
1427
pango_layout_set_text(layout, utfForm, len);
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) {
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;
1448
PLATFORM_ASSERT(i == lenPositions);
1452
PFont(font_)->SetCharWidth(*s, positions[0], et);
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;
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);
1470
wclen = gdk_mbstowcs(
1471
wctext, sDraw, maxLengthTextRun - 1);
1474
measure8bit = false;
1475
wctext[wclen] = L'\0';
1476
// Map widths back to utf-8 or DBCS input string
1478
for (int iU = 0; iU < wclen; iU++) {
1479
int width = gdk_char_width_wc(gf, wctext[iU]);
1480
totalWidth += width;
1483
lenChar = UTF8Len(s[i]);
1485
lenChar = mblen(s+i, MB_CUR_MAX);
1490
positions[i++] = totalWidth;
1493
while (i < len) { // In case of problems with lengths
1494
positions[i++] = totalWidth;
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;
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) {
1518
pango_layout_set_font_description(layout, PFont(font_)->pfd);
1520
bool useGFree = false;
1522
pango_layout_set_text(layout, s, len);
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);
1540
PangoLayoutLine *pangoLine = pango_layout_get_line(layout,0);
1542
pango_layout_line_get_extents(pangoLine, NULL, &pos);
1548
return PANGO_PIXELS(pos.width);
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);
1557
return gdk_text_width(PFont(font_)->pfont, s, len);
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);
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";
1588
const char sizeString[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1589
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1592
int SurfaceImpl::Ascent(Font &font_) {
1593
if (!(font_.GetID()))
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;
1622
gdk_string_extents(PFont(font_)->pfont, sizeString,
1623
&lbearing, &rbearing, &width, &ascent, &descent);
1628
int SurfaceImpl::Descent(Font &font_) {
1629
if (!(font_.GetID()))
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);
1640
return PFont(font_)->pfont->descent;
1649
gdk_string_extents(PFont(font_)->pfont, sizeString,
1650
&lbearing, &rbearing, &width, &ascent, &descent);
1655
int SurfaceImpl::InternalLeading(Font &) {
1659
int SurfaceImpl::ExternalLeading(Font &) {
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
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_) {
1689
void SurfaceImpl::SetDBCSMode(int codePage) {
1690
if (codePage && (codePage != SC_CP_UTF8))
1694
Surface *Surface::Allocate() {
1695
return new SurfaceImpl;
1698
Window::~Window() {}
1700
void Window::Destroy() {
1702
gtk_widget_destroy(GTK_WIDGET(wid));
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);
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;
1724
void Window::SetPosition(PRectangle rc) {
1725
GtkAllocation alloc;
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) {
1736
gdk_window_get_origin(PWidget(relativeTo.wid)->window, &ox, &oy);
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) {
1768
gtk_widget_show(PWidget(wid));
1771
void Window::InvalidateAll() {
1773
gtk_widget_queue_draw(PWidget(wid));
1777
void Window::InvalidateRectangle(PRectangle rc) {
1779
gtk_widget_queue_draw_area(PWidget(wid),
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)
1799
gdkCurs = gdk_cursor_new(GDK_XTERM);
1802
gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
1805
gdkCurs = gdk_cursor_new(GDK_CENTER_PTR);
1808
gdkCurs = gdk_cursor_new(GDK_WATCH);
1811
gdkCurs = gdk_cursor_new(GDK_HAND2);
1813
case cursorReverseArrow:
1814
gdkCurs = gdk_cursor_new(GDK_RIGHT_PTR);
1817
gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
1818
cursorLast = cursorArrow;
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;
1837
gdk_window_get_origin(PWidget(wid)->window, &x_offset, &y_offset);
1839
#if GTK_CHECK_VERSION(2,2,0)
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);
1851
return PRectangle(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
1854
return PRectangle(-x_offset, -y_offset, (-x_offset) + gdk_screen_width(),
1855
(-y_offset) + gdk_screen_height());
1860
const char *xpm_data;
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);
1871
ListBox::ListBox() {
1874
ListBox::~ListBox() {
1883
class ListBoxX : public ListBox {
1887
GtkCellRenderer* pixbuf_renderer;
1889
int desiredVisibleRows;
1890
unsigned int maxItemCharacters;
1891
unsigned int aveCharWidth;
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() {
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();
1934
static gboolean ButtonPress(GtkWidget *, GdkEventButton* ev, gpointer p) {
1936
ListBoxX* lb = reinterpret_cast<ListBoxX*>(p);
1937
if (ev->type == GDK_2BUTTON_PRESS && lb->doubleClickAction != NULL) {
1938
lb->doubleClickAction(lb->doubleClickActionData);
1943
// No pointer back to Scintilla to save status
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*) {
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);
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);
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);
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);
2053
int rows = Length();
2054
if ((rows == 0) || (rows > desiredVisibleRows))
2055
rows = desiredVisibleRows;
2060
// First calculate height of the clist for our desired visible
2061
// row count otherwise it tries to expand to the total # of rows
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
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;
2084
rc.right = width * (aveCharWidth + aveCharWidth / 3);
2085
if (Length() > rows)
2086
rc.right = rc.right + 16;
2091
int ListBoxX::CaretFromEdge() {
2092
gint renderer_width, renderer_height;
2093
gtk_cell_renderer_get_fixed_size(pixbuf_renderer, &renderer_width,
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;
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));
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);
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,
2154
gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2155
TEXT_COLUMN, s, -1);
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() {
2168
return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
2169
(GTK_TREE_VIEW(list)), NULL);
2173
void ListBoxX::Select(int n) {
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));
2180
gtk_tree_selection_unselect_all(selection);
2184
bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
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;
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;
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;
2212
value = (value < 0)? 0 : value;
2213
value = (value > (adj->upper - adj->page_size))?
2214
(adj->upper - adj->page_size) : value;
2217
gtk_adjustment_set_value(adj, value);
2219
gtk_tree_selection_unselect_all(selection);
2223
int ListBoxX::GetSelection() {
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.
2238
int ListBoxX::Find(const char *prefix) {
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;
2246
gtk_tree_model_get(model, &iter, TEXT_COLUMN, &s, -1);
2247
if (s && (0 == strncmp(prefix, s, strlen(prefix)))) {
2252
valid = gtk_tree_model_iter_next(model, &iter) != FALSE;
2258
void ListBoxX::GetValue(int n, char *value, int len) {
2261
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2262
bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
2264
gtk_tree_model_get(model, &iter, TEXT_COLUMN, &text, -1);
2266
if (text && len > 0) {
2267
strncpy(value, text, len);
2268
value[len - 1] = '\0';
2275
// g_return_if_fail causes unnecessary compiler warning in release compile.
2277
#pragma warning(disable: 4127)
2280
void ListBoxX::RegisterImage(int type, const char *xpm_data) {
2281
g_return_if_fail(xpm_data);
2283
// Saved and use the saved copy so caller's copy can disappear.
2284
xset.Add(type, xpm_data);
2285
XPM *pxpm = xset.Get(type);
2286
xpm_data = reinterpret_cast<const char *>(pxpm->InLinesForm());
2289
pixhash = g_hash_table_new(g_direct_hash, g_direct_equal);
2291
ListImage *list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash,
2292
(gconstpointer) GINT_TO_POINTER(type));
2294
// Drop icon already registered
2295
if (list_image->pixbuf)
2296
gdk_pixbuf_unref(list_image->pixbuf);
2297
list_image->pixbuf = NULL;
2298
list_image->xpm_data = xpm_data;
2300
list_image = g_new0(ListImage, 1);
2301
list_image->xpm_data = xpm_data;
2302
g_hash_table_insert((GHashTable *) pixhash, GINT_TO_POINTER(type),
2303
(gpointer) list_image);
2307
void ListBoxX::ClearRegisteredImages() {
2311
void ListBoxX::SetList(const char *listText, char separator, char typesep) {
2313
int count = strlen(listText) + 1;
2314
char *words = new char[count];
2316
memcpy(words, listText, count);
2317
char *startword = words;
2318
char *numword = NULL;
2320
for (; words[i]; i++) {
2321
if (words[i] == separator) {
2325
Append(startword, numword?atoi(numword + 1):-1);
2326
startword = words + i + 1;
2328
} else if (words[i] == typesep) {
2329
numword = words + i;
2335
Append(startword, numword?atoi(numword + 1):-1);
2341
Menu::Menu() : mid(0) {}
2343
void Menu::CreatePopUp() {
2345
mid = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
2348
void Menu::Destroy() {
2350
g_object_unref(G_OBJECT(mid));
2354
void Menu::Show(Point pt, Window &) {
2355
int screenHeight = gdk_screen_height();
2356
int screenWidth = gdk_screen_width();
2357
GtkItemFactory *factory = reinterpret_cast<GtkItemFactory *>(mid);
2358
GtkWidget *widget = gtk_item_factory_get_widget(factory, "<main>");
2359
gtk_widget_show_all(widget);
2360
GtkRequisition requisition;
2361
gtk_widget_size_request(widget, &requisition);
2362
if ((pt.x + requisition.width) > screenWidth) {
2363
pt.x = screenWidth - requisition.width;
2365
if ((pt.y + requisition.height) > screenHeight) {
2366
pt.y = screenHeight - requisition.height;
2368
gtk_item_factory_popup(factory, pt.x - 4, pt.y - 4, 3,
2369
gtk_get_current_event_time());
2372
ElapsedTime::ElapsedTime() {
2374
g_get_current_time(&curTime);
2375
bigBit = curTime.tv_sec;
2376
littleBit = curTime.tv_usec;
2379
class DynamicLibraryImpl : public DynamicLibrary {
2383
DynamicLibraryImpl(const char *modulePath) {
2384
m = g_module_open(modulePath, G_MODULE_BIND_LAZY);
2387
virtual ~DynamicLibraryImpl() {
2392
// Use g_module_symbol to get a pointer to the relevant function.
2393
virtual Function FindFunction(const char *name) {
2395
gpointer fn_address = NULL;
2396
gboolean status = g_module_symbol(m, name, &fn_address);
2398
return static_cast<Function>(fn_address);
2405
virtual bool IsValid() {
2410
DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {
2411
return static_cast<DynamicLibrary *>( new DynamicLibraryImpl(modulePath) );
2414
double ElapsedTime::Duration(bool reset) {
2416
g_get_current_time(&curTime);
2417
long endBigBit = curTime.tv_sec;
2418
long endLittleBit = curTime.tv_usec;
2419
double result = 1000000.0 * (endBigBit - bigBit);
2420
result += endLittleBit - littleBit;
2421
result /= 1000000.0;
2424
littleBit = endLittleBit;
2429
ColourDesired Platform::Chrome() {
2430
return ColourDesired(0xe0, 0xe0, 0xe0);
2433
ColourDesired Platform::ChromeHighlight() {
2434
return ColourDesired(0xff, 0xff, 0xff);
2437
const char *Platform::DefaultFont() {
2439
return "Lucida Console";
2445
int Platform::DefaultFontSize() {
2453
unsigned int Platform::DoubleClickTime() {
2454
return 500; // Half a second
2457
bool Platform::MouseButtonBounce() {
2461
void Platform::DebugDisplay(const char *s) {
2462
fprintf(stderr, "%s", s);
2465
bool Platform::IsKeyDown(int) {
2466
// TODO: discover state of keys in GTK+/X
2470
long Platform::SendScintilla(
2471
WindowID w, unsigned int msg, unsigned long wParam, long lParam) {
2472
return scintilla_send_message(SCINTILLA(w), msg, wParam, lParam);
2475
long Platform::SendScintillaPointer(
2476
WindowID w, unsigned int msg, unsigned long wParam, void *lParam) {
2477
return scintilla_send_message(SCINTILLA(w), msg, wParam,
2478
reinterpret_cast<sptr_t>(lParam));
2481
bool Platform::IsDBCSLeadByte(int codePage, char ch) {
2482
// Byte ranges found in Wikipedia articles with relevant search strings in each case
2483
unsigned char uch = static_cast<unsigned char>(ch);
2487
return ((uch >= 0x81) && (uch <= 0x9F)) ||
2488
((uch >= 0xE0) && (uch <= 0xEF));
2491
return (uch >= 0x81) && (uch <= 0xFE);
2494
return (uch >= 0x81) && (uch <= 0xFE);
2495
// Korean EUC-KR may be code page 949.
2500
int Platform::DBCSCharLength(int codePage, const char *s) {
2501
if (codePage == 932 || codePage == 936 || codePage == 950) {
2502
return IsDBCSLeadByte(codePage, s[0]) ? 2 : 1;
2504
int bytes = mblen(s, MB_CUR_MAX);
2512
int Platform::DBCSCharMaxLength() {
2517
// These are utility functions not really tied to a platform
2519
int Platform::Minimum(int a, int b) {
2526
int Platform::Maximum(int a, int b) {
2536
void Platform::DebugPrintf(const char *format, ...) {
2539
va_start(pArguments, format);
2540
vsprintf(buffer, format, pArguments);
2542
Platform::DebugDisplay(buffer);
2545
void Platform::DebugPrintf(const char *, ...) {}
2549
// Not supported for GTK+
2550
static bool assertionPopUps = true;
2552
bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {
2553
bool ret = assertionPopUps;
2554
assertionPopUps = assertionPopUps_;
2558
void Platform::Assert(const char *c, const char *file, int line) {
2560
sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line);
2561
strcat(buffer, "\r\n");
2562
Platform::DebugDisplay(buffer);
2566
int Platform::Clamp(int val, int minVal, int maxVal) {
2574
void Platform_Initialise() {
2575
FontMutexAllocate();
2578
void Platform_Finalise() {