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.
16
#include <gdk/gdkkeysyms.h>
20
#include "Scintilla.h"
21
#include "ScintillaWidget.h"
22
#include "UniConversion.h"
25
/* GLIB must be compiled with thread support, otherwise we
26
will bail on trying to use locks, and that could lead to
27
problems for someone. `glib-config --libs gthread` needs
28
to be used to get the glib libraries for linking, otherwise
29
g_thread_init will fail */
30
#define USE_LOCK defined(G_THREADS_ENABLED) && !defined(G_THREADS_IMPL_NONE)
31
/* Use fast way of getting char data on win32 to work around problems
32
with gdk_string_extents. */
35
#include "Converter.h"
37
#if GTK_CHECK_VERSION(2,20,0)
38
#define IS_WIDGET_FOCUSSED(w) (gtk_widget_has_focus(GTK_WIDGET(w)))
40
#define IS_WIDGET_FOCUSSED(w) (GTK_WIDGET_HAS_FOCUS(w))
43
#if GTK_CHECK_VERSION(2,22,0)
48
#define DISABLE_GDK_FONT 1
52
// Ignore unreferenced local functions in GTK+ headers
53
#pragma warning(disable: 4505)
57
using namespace Scintilla;
60
enum encodingType { singleByte, UTF8, dbcs};
71
static GMutex *fontMutex = NULL;
73
static void InitializeGLIBThreads() {
74
if (!g_thread_supported()) {
80
static void FontMutexAllocate() {
83
InitializeGLIBThreads();
84
fontMutex = g_mutex_new();
89
static void FontMutexFree() {
92
g_mutex_free(fontMutex);
98
static void FontMutexLock() {
100
g_mutex_lock(fontMutex);
104
static void FontMutexUnlock() {
107
g_mutex_unlock(fontMutex);
112
// On GTK+ 1.x holds a GdkFont* but on GTK+ 2.x can hold a GdkFont* or a
113
// PangoFontDescription*.
120
PangoFontDescription *pfd;
122
FontHandle(GdkFont *pfont_) {
130
FontHandle(PangoFontDescription *pfd_, int characterSet_) {
135
characterSet = characterSet_;
139
#ifndef DISABLE_GDK_FONT
141
gdk_font_unref(pfont);
145
pango_font_description_free(pfd);
148
void ResetWidths(encodingType et_) {
150
for (int i=0; i<=127; i++) {
154
int CharWidth(unsigned char ch, encodingType et_) {
157
if ((ch <= 127) && (et == et_)) {
163
void SetCharWidth(unsigned char ch, int w, encodingType et_) {
175
// X has a 16 bit coordinate space, so stop drawing here to avoid wrapping
176
static const int maxCoordinate = 32000;
178
static FontHandle *PFont(Font &f) {
179
return reinterpret_cast<FontHandle *>(f.GetID());
182
static GtkWidget *PWidget(WindowID wid) {
183
return reinterpret_cast<GtkWidget *>(wid);
186
static GtkWidget *PWidget(Window &w) {
187
return PWidget(w.GetID());
190
Point Point::FromLong(long lpoint) {
192
Platform::LowShortFromLong(lpoint),
193
Platform::HighShortFromLong(lpoint));
198
allowRealization = false;
199
allocatedPalette = 0;
202
entries = new ColourPair[size];
205
Palette::~Palette() {
211
void Palette::Release() {
213
delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
214
allocatedPalette = 0;
218
entries = new ColourPair[size];
221
// This method either adds a colour to the list of wanted colours (want==true)
222
// or retrieves the allocated colour back to the ColourPair.
223
// This is one method to make it easier to keep the code for wanting and retrieving in sync.
224
void Palette::WantFind(ColourPair &cp, bool want) {
226
for (int i=0; i < used; i++) {
227
if (entries[i].desired == cp.desired)
232
int sizeNew = size * 2;
233
ColourPair *entriesNew = new ColourPair[sizeNew];
234
for (int j=0; j<size; j++) {
235
entriesNew[j] = entries[j];
238
entries = entriesNew;
242
entries[used].desired = cp.desired;
243
entries[used].allocated.Set(cp.desired.AsLong());
246
for (int i=0; i < used; i++) {
247
if (entries[i].desired == cp.desired) {
248
cp.allocated = entries[i].allocated;
252
cp.allocated.Set(cp.desired.AsLong());
256
void Palette::Allocate(Window &w) {
257
if (allocatedPalette) {
258
gdk_colormap_free_colors(gtk_widget_get_colormap(PWidget(w)),
259
reinterpret_cast<GdkColor *>(allocatedPalette),
261
delete [](reinterpret_cast<GdkColor *>(allocatedPalette));
262
allocatedPalette = 0;
265
GdkColor *paletteNew = new GdkColor[used];
266
allocatedPalette = paletteNew;
267
gboolean *successPalette = new gboolean[used];
271
for (iPal = 0; iPal < used; iPal++) {
272
paletteNew[iPal].red = entries[iPal].desired.GetRed() * (65535 / 255);
273
paletteNew[iPal].green = entries[iPal].desired.GetGreen() * (65535 / 255);
274
paletteNew[iPal].blue = entries[iPal].desired.GetBlue() * (65535 / 255);
275
paletteNew[iPal].pixel = entries[iPal].desired.AsLong();
277
gdk_colormap_alloc_colors(gtk_widget_get_colormap(PWidget(w)),
278
paletteNew, allocatedLen, FALSE, TRUE,
280
for (iPal = 0; iPal < used; iPal++) {
281
entries[iPal].allocated.Set(paletteNew[iPal].pixel);
284
delete []successPalette;
287
#ifndef DISABLE_GDK_FONT
289
static const char *CharacterSetName(int characterSet) {
290
switch (characterSet) {
291
case SC_CHARSET_ANSI:
293
case SC_CHARSET_DEFAULT:
295
case SC_CHARSET_BALTIC:
297
case SC_CHARSET_CHINESEBIG5:
299
case SC_CHARSET_EASTEUROPE:
301
case SC_CHARSET_GB2312:
302
return "gb2312.1980-*";
303
case SC_CHARSET_GREEK:
305
case SC_CHARSET_HANGUL:
306
return "ksc5601.1987-*";
311
case SC_CHARSET_RUSSIAN:
313
case SC_CHARSET_CYRILLIC:
315
case SC_CHARSET_SHIFTJIS:
316
return "jisx0208.1983-*";
317
case SC_CHARSET_SYMBOL:
319
case SC_CHARSET_TURKISH:
321
case SC_CHARSET_JOHAB:
323
case SC_CHARSET_HEBREW:
325
case SC_CHARSET_ARABIC:
327
case SC_CHARSET_VIETNAMESE:
329
case SC_CHARSET_THAI:
331
case SC_CHARSET_8859_15:
338
static bool IsDBCSCharacterSet(int characterSet) {
339
switch (characterSet) {
340
case SC_CHARSET_GB2312:
341
case SC_CHARSET_HANGUL:
342
case SC_CHARSET_SHIFTJIS:
343
case SC_CHARSET_CHINESEBIG5:
350
static void GenerateFontSpecStrings(const char *fontName, int characterSet,
351
char *foundary, int foundary_len,
352
char *faceName, int faceName_len,
353
char *charset, int charset_len) {
354
// supported font strings include:
355
// foundary-fontface-isoxxx-x
359
if (strchr(fontName, '-')) {
361
char *d1 = NULL, *d2 = NULL, *d3 = NULL;
362
strncpy(tmp, fontName, sizeof(tmp) - 1);
363
tmp[sizeof(tmp) - 1] = '\0';
364
d1 = strchr(tmp, '-');
365
// we know the first dash exists
366
d2 = strchr(d1 + 1, '-');
368
d3 = strchr(d2 + 1, '-');
370
// foundary-fontface-isoxxx-x
374
strncpy(faceName, tmp, foundary_len - 1);
375
strncpy(charset, d2 + 1, charset_len - 1);
379
strcpy(foundary, "-*-");
380
strncpy(faceName, tmp, faceName_len - 1);
381
strncpy(charset, d1 + 1, charset_len - 1);
386
strncpy(faceName, tmp, faceName_len - 1);
387
strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
390
strncpy(foundary, "-*-", foundary_len);
391
strncpy(faceName, fontName, faceName_len - 1);
392
strncpy(charset, CharacterSetName(characterSet), charset_len - 1);
398
static void SetLogFont(LOGFONT &lf, const char *faceName, int characterSet, int size, bool bold, bool italic) {
399
memset(&lf, 0, sizeof(lf));
403
lf.characterSet = characterSet;
404
strncpy(lf.faceName, faceName, sizeof(lf.faceName) - 1);
408
* Create a hash from the parameters for a font to allow easy checking for identity.
409
* If one font is the same as another, its hash will be the same, but if the hash is the
410
* same then they may still be different.
412
static int HashFont(const char *faceName, int characterSet, int size, bool bold, bool italic) {
415
(characterSet << 10) ^
416
(bold ? 0x10000000 : 0) ^
417
(italic ? 0x20000000 : 0) ^
421
class FontCached : Font {
426
FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
428
bool SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
429
virtual void Release();
430
static FontID CreateNewFont(const char *fontName, int characterSet,
431
int size, bool bold, bool italic);
432
static FontCached *first;
434
static FontID FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_);
435
static void ReleaseId(FontID fid_);
438
FontCached *FontCached::first = 0;
440
FontCached::FontCached(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) :
441
next(0), usage(0), hash(0) {
442
::SetLogFont(lf, faceName_, characterSet_, size_, bold_, italic_);
443
hash = HashFont(faceName_, characterSet_, size_, bold_, italic_);
444
fid = CreateNewFont(faceName_, characterSet_, size_, bold_, italic_);
448
bool FontCached::SameAs(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
452
lf.italic == italic_ &&
453
lf.characterSet == characterSet_ &&
454
0 == strcmp(lf.faceName, faceName_);
457
void FontCached::Release() {
463
FontID FontCached::FindOrCreate(const char *faceName_, int characterSet_, int size_, bool bold_, bool italic_) {
466
int hashFind = HashFont(faceName_, characterSet_, size_, bold_, italic_);
467
for (FontCached *cur = first; cur; cur = cur->next) {
468
if ((cur->hash == hashFind) &&
469
cur->SameAs(faceName_, characterSet_, size_, bold_, italic_)) {
475
FontCached *fc = new FontCached(faceName_, characterSet_, size_, bold_, italic_);
486
void FontCached::ReleaseId(FontID fid_) {
488
FontCached **pcur = &first;
489
for (FontCached *cur = first; cur; cur = cur->next) {
490
if (cur->fid == fid_) {
492
if (cur->usage == 0) {
505
#ifndef DISABLE_GDK_FONT
506
static GdkFont *LoadFontOrSet(const char *fontspec, int characterSet) {
507
if (IsDBCSCharacterSet(characterSet)) {
508
return gdk_fontset_load(fontspec);
510
return gdk_font_load(fontspec);
515
FontID FontCached::CreateNewFont(const char *fontName, int characterSet,
516
int size, bool bold, bool italic) {
517
if (fontName[0] == '!') {
518
PangoFontDescription *pfd = pango_font_description_new();
520
pango_font_description_set_family(pfd, fontName+1);
521
pango_font_description_set_size(pfd, size * PANGO_SCALE);
522
pango_font_description_set_weight(pfd, bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
523
pango_font_description_set_style(pfd, italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
524
return new FontHandle(pfd, characterSet);
528
#ifndef DISABLE_GDK_FONT
541
// If name of the font begins with a '-', assume, that it is
543
if (fontName[0] == '-') {
544
if (strchr(fontName, ',') || IsDBCSCharacterSet(characterSet)) {
545
newid = gdk_fontset_load(fontName);
547
newid = gdk_font_load(fontName);
550
// Font not available so substitute a reasonable code font
551
// iso8859 appears to only allow western characters.
552
newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
555
return new FontHandle(newid);
558
// it's not a full fontspec, build one.
560
// This supports creating a FONT_SET
561
// in a method that allows us to also set size, slant and
562
// weight for the fontset. The expected input is multiple
563
// partial fontspecs seperated by comma
564
// eg. adobe-courier-iso10646-1,*-courier-iso10646-1,*-*-*-*
565
if (strchr(fontName, ',')) {
566
// build a fontspec and use gdk_fontset_load
567
int remaining = sizeof(fontset);
568
char fontNameCopy[1024];
569
strncpy(fontNameCopy, fontName, sizeof(fontNameCopy) - 1);
570
char *fn = fontNameCopy;
571
char *fp = strchr(fn, ',');
573
const char *spec = "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
574
if (fontset[0] != '\0') {
575
// if this is not the first font in the list,
576
// append a comma seperator
577
spec = ",%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
581
*fp = '\0'; // nullify the comma
582
GenerateFontSpecStrings(fn, characterSet,
583
foundary, sizeof(foundary),
584
faceName, sizeof(faceName),
585
charset, sizeof(charset));
588
sizeof(fontspec) - 1,
591
bold ? "-bold" : "-medium",
592
italic ? "-i" : "-r",
596
// if this is the first font in the list, and
597
// we are doing italic, add an oblique font
599
if (italic && fontset[0] == '\0') {
600
strncat(fontset, fontspec, remaining - 1);
601
remaining -= strlen(fontset);
604
sizeof(fontspec) - 1,
605
",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s",
607
bold ? "-bold" : "-medium",
612
strncat(fontset, fontspec, remaining - 1);
613
remaining -= strlen(fontset);
619
fp = strchr(fn, ',');
622
newid = gdk_fontset_load(fontset);
624
return new FontHandle(newid);
625
// if fontset load failed, fall through, we'll use
626
// the last font entry and continue to try and
627
// get something that matches
630
// single fontspec support
632
GenerateFontSpecStrings(fontName, characterSet,
633
foundary, sizeof(foundary),
634
faceName, sizeof(faceName),
635
charset, sizeof(charset));
638
sizeof(fontspec) - 1,
639
"%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
641
bold ? "-bold" : "-medium",
642
italic ? "-i" : "-r",
645
newid = LoadFontOrSet(fontspec, characterSet);
647
// some fonts have oblique, not italic
649
sizeof(fontspec) - 1,
650
"%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
652
bold ? "-bold" : "-medium",
653
italic ? "-o" : "-r",
656
newid = LoadFontOrSet(fontspec, characterSet);
660
sizeof(fontspec) - 1,
661
"-*-*-*-*-*-*-*-%0d-*-*-*-*-%s",
664
newid = gdk_font_load(fontspec);
667
// Font not available so substitute a reasonable code font
668
// iso8859 appears to only allow western characters.
669
newid = LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
672
return new FontHandle(newid);
674
return new FontHandle(0);
678
Font::Font() : fid(0) {}
682
void Font::Create(const char *faceName, int characterSet, int size,
683
bool bold, bool italic, int) {
685
fid = FontCached::FindOrCreate(faceName, characterSet, size, bold, italic);
688
void Font::Release() {
690
FontCached::ReleaseId(fid);
696
namespace Scintilla {
698
class SurfaceImpl : public Surface {
702
cairo_surface_t *psurf;
704
GdkDrawable *drawable;
712
PangoContext *pcontext;
716
void SetConverter(int characterSet_);
719
virtual ~SurfaceImpl();
721
void Init(WindowID wid);
722
void Init(SurfaceID sid, WindowID wid);
723
void InitPixMap(int width, int height, Surface *surface_, WindowID wid);
727
void PenColour(ColourAllocated fore);
729
int DeviceHeightFont(int points);
730
void MoveTo(int x_, int y_);
731
void LineTo(int x_, int y_);
732
void Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back);
733
void RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back);
734
void FillRectangle(PRectangle rc, ColourAllocated back);
735
void FillRectangle(PRectangle rc, Surface &surfacePattern);
736
void RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back);
737
void AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
738
ColourAllocated outline, int alphaOutline, int flags);
739
void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back);
740
void Copy(PRectangle rc, Point from, Surface &surfaceSource);
742
void DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
743
void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
744
void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back);
745
void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore);
746
void MeasureWidths(Font &font_, const char *s, int len, int *positions);
747
int WidthText(Font &font_, const char *s, int len);
748
int WidthChar(Font &font_, char ch);
749
int Ascent(Font &font_);
750
int Descent(Font &font_);
751
int InternalLeading(Font &font_);
752
int ExternalLeading(Font &font_);
753
int Height(Font &font_);
754
int AverageCharWidth(Font &font_);
756
int SetPalette(Palette *pal, bool inBackGround);
757
void SetClip(PRectangle rc);
758
void FlushCachedState();
760
void SetUnicodeMode(bool unicodeMode_);
761
void SetDBCSMode(int codePage);
767
const char *CharacterSetID(int characterSet) {
768
switch (characterSet) {
769
case SC_CHARSET_ANSI:
771
case SC_CHARSET_DEFAULT:
773
case SC_CHARSET_BALTIC:
774
return "ISO-8859-13";
775
case SC_CHARSET_CHINESEBIG5:
777
case SC_CHARSET_EASTEUROPE:
779
case SC_CHARSET_GB2312:
781
case SC_CHARSET_GREEK:
783
case SC_CHARSET_HANGUL:
789
case SC_CHARSET_RUSSIAN:
791
case SC_CHARSET_CYRILLIC:
793
case SC_CHARSET_SHIFTJIS:
795
case SC_CHARSET_SYMBOL:
797
case SC_CHARSET_TURKISH:
799
case SC_CHARSET_JOHAB:
801
case SC_CHARSET_HEBREW:
803
case SC_CHARSET_ARABIC:
805
case SC_CHARSET_VIETNAMESE:
807
case SC_CHARSET_THAI:
808
return "ISO-8859-11";
809
case SC_CHARSET_8859_15:
810
return "ISO-8859-15";
816
void SurfaceImpl::SetConverter(int characterSet_) {
817
if (characterSet != characterSet_) {
818
characterSet = characterSet_;
819
conv.Open("UTF-8", CharacterSetID(characterSet), false);
823
SurfaceImpl::SurfaceImpl() : et(singleByte),
832
x(0), y(0), inited(false), createdGC(false)
833
, pcontext(0), layout(0), characterSet(-1) {
836
SurfaceImpl::~SurfaceImpl() {
840
void SurfaceImpl::Release() {
848
cairo_destroy(context);
856
cairo_surface_destroy(psurf);
861
g_object_unref(ppixmap);
865
g_object_unref(layout);
868
g_object_unref(pcontext);
878
bool SurfaceImpl::Initialised() {
882
void SurfaceImpl::Init(WindowID wid) {
884
PLATFORM_ASSERT(wid);
886
GdkDrawable *drawable_ = GDK_DRAWABLE(PWidget(wid)->window);
888
context = gdk_cairo_create(drawable_);
889
PLATFORM_ASSERT(context);
891
// Shouldn't happen with valid window but may when calls made before
892
// window completely allocated and mapped.
893
psurf = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 1, 1);
894
context = cairo_create(psurf);
898
pcontext = gtk_widget_create_pango_context(PWidget(wid));
899
PLATFORM_ASSERT(pcontext);
900
layout = pango_layout_new(pcontext);
901
PLATFORM_ASSERT(layout);
905
void SurfaceImpl::Init(SurfaceID sid, WindowID wid) {
906
PLATFORM_ASSERT(sid);
907
GdkDrawable *drawable_ = reinterpret_cast<GdkDrawable *>(sid);
909
PLATFORM_ASSERT(wid);
911
context = gdk_cairo_create(drawable_);
913
gc = gdk_gc_new(drawable_);
914
drawable = drawable_;
916
pcontext = gtk_widget_create_pango_context(PWidget(wid));
917
layout = pango_layout_new(pcontext);
919
cairo_set_line_width(context, 1);
921
// Ask for lines that do not paint the last pixel so is like Win32
922
gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
928
void SurfaceImpl::InitPixMap(int width, int height, Surface *surface_, WindowID wid) {
929
PLATFORM_ASSERT(surface_);
931
SurfaceImpl *surfImpl = static_cast<SurfaceImpl *>(surface_);
932
PLATFORM_ASSERT(wid);
934
context = cairo_reference(surfImpl->context);
936
PLATFORM_ASSERT(surfImpl->drawable);
937
gc = gdk_gc_new(surfImpl->drawable);
939
pcontext = gtk_widget_create_pango_context(PWidget(wid));
940
PLATFORM_ASSERT(pcontext);
941
layout = pango_layout_new(pcontext);
942
PLATFORM_ASSERT(layout);
944
if (height > 0 && width > 0)
945
psurf = gdk_window_create_similar_surface(
946
gtk_widget_get_window(PWidget(wid)),
947
CAIRO_CONTENT_COLOR_ALPHA, width, height);
949
if (height > 0 && width > 0)
950
ppixmap = gdk_pixmap_new(surfImpl->drawable, width, height, -1);
954
cairo_destroy(context);
955
context = cairo_create(psurf);
956
cairo_rectangle(context, 0, 0, width, height);
957
cairo_set_source_rgb(context, 1.0, 0, 0);
959
// This produces sharp drawing more similar to GDK:
960
//cairo_set_antialias(context, CAIRO_ANTIALIAS_NONE);
963
cairo_set_line_width(context, 1);
965
// Ask for lines that do not paint the last pixel so is like Win32
966
gdk_gc_set_line_attributes(gc, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
972
void SurfaceImpl::PenColour(ColourAllocated fore) {
975
ColourDesired cdFore(fore.AsLong());
976
cairo_set_source_rgb(context,
977
cdFore.GetBlue() / 255.0,
978
cdFore.GetGreen() / 255.0,
979
cdFore.GetRed() / 255.0);
984
co.pixel = fore.AsLong();
985
gdk_gc_set_foreground(gc, &co);
990
int SurfaceImpl::LogPixelsY() {
994
int SurfaceImpl::DeviceHeightFont(int points) {
995
int logPix = LogPixelsY();
996
return (points * logPix + logPix / 2) / 72;
999
void SurfaceImpl::MoveTo(int x_, int y_) {
1005
static int Delta(int difference) {
1008
else if (difference > 0)
1015
void SurfaceImpl::LineTo(int x_, int y_) {
1017
// cairo_line_to draws the end position, unlike Win32 or GDK with GDK_CAP_NOT_LAST.
1018
// For simple cases, move back one pixel from end.
1021
int xDelta = Delta(xDiff);
1023
int yDelta = Delta(yDiff);
1024
if ((xDiff == 0) || (yDiff == 0)) {
1025
// Horizontal or vertical lines can be more precisely drawn as a filled rectangle
1026
int xEnd = x_ - xDelta;
1027
int left = Platform::Minimum(x, xEnd);
1028
int width = abs(x - xEnd) + 1;
1029
int yEnd = y_ - yDelta;
1030
int top = Platform::Minimum(y, yEnd);
1031
int height = abs(y - yEnd) + 1;
1032
cairo_rectangle(context, left, top, width, height);
1033
cairo_fill(context);
1034
} else if ((abs(xDiff) == abs(yDiff))) {
1036
cairo_move_to(context, x + 0.5, y + 0.5);
1037
cairo_line_to(context, x_ + 0.5 - xDelta, y_ + 0.5 - yDelta);
1039
// Line has a different slope so difficult to avoid last pixel
1040
cairo_move_to(context, x + 0.5, y + 0.5);
1041
cairo_line_to(context, x_ + 0.5, y_ + 0.5);
1043
cairo_stroke(context);
1046
if (drawable && gc) {
1047
gdk_draw_line(drawable, gc,
1056
void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore,
1057
ColourAllocated back) {
1060
cairo_move_to(context, pts[0].x + 0.5, pts[0].y + 0.5);
1061
for (int i = 1;i < npts;i++) {
1062
cairo_line_to(context, pts[i].x + 0.5, pts[i].y + 0.5);
1064
cairo_close_path(context);
1065
cairo_fill_preserve(context);
1067
cairo_stroke(context);
1070
if (npts < static_cast<int>((sizeof(gpts) / sizeof(gpts[0])))) {
1071
for (int i = 0;i < npts;i++) {
1072
gpts[i].x = pts[i].x;
1073
gpts[i].y = pts[i].y;
1076
gdk_draw_polygon(drawable, gc, 1, gpts, npts);
1078
gdk_draw_polygon(drawable, gc, 0, gpts, npts);
1083
void SurfaceImpl::RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
1087
if (gc && drawable) {
1090
cairo_rectangle(context, rc.left + 0.5, rc.top + 0.5,
1091
rc.right - rc.left - 1, rc.bottom - rc.top - 1);
1093
cairo_fill_preserve(context);
1095
cairo_stroke(context);
1098
gdk_draw_rectangle(drawable, gc, 1,
1099
rc.left + 1, rc.top + 1,
1100
rc.right - rc.left - 2, rc.bottom - rc.top - 2);
1102
// The subtraction of 1 off the width and height here shouldn't be needed but
1103
// otherwise a different rectangle is drawn than would be done if the fill parameter == 1
1104
gdk_draw_rectangle(drawable, gc, 0,
1106
rc.right - rc.left - 1, rc.bottom - rc.top - 1);
1111
void SurfaceImpl::FillRectangle(PRectangle rc, ColourAllocated back) {
1114
if (context && (rc.left < maxCoordinate)) { // Protect against out of range
1115
cairo_rectangle(context, rc.left, rc.top,
1116
rc.right - rc.left, rc.bottom - rc.top);
1117
cairo_fill(context);
1120
if (drawable && (rc.left < maxCoordinate)) { // Protect against out of range
1121
gdk_draw_rectangle(drawable, gc, 1,
1123
rc.right - rc.left, rc.bottom - rc.top);
1128
void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) {
1129
SurfaceImpl &surfi = static_cast<SurfaceImpl &>(surfacePattern);
1131
bool canDraw = surfi.psurf;
1133
bool canDraw = surfi.drawable;
1136
// Tile pattern over rectangle
1137
// Currently assumes 8x8 pattern
1140
for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) {
1141
int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat;
1142
for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) {
1143
int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat;
1145
cairo_set_source_surface(context, surfi.psurf, xTile, yTile);
1146
cairo_rectangle(context, xTile, yTile, widthx, heighty);
1147
cairo_fill(context);
1149
gdk_draw_drawable(drawable,
1151
static_cast<SurfaceImpl &>(surfacePattern).drawable,
1159
// Something is wrong so try to show anyway
1160
// Shows up black because colour not allocated
1161
FillRectangle(rc, ColourAllocated(0));
1165
void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
1166
if (((rc.right - rc.left) > 4) && ((rc.bottom - rc.top) > 4)) {
1167
// Approximate a round rect with some cut off corners
1169
Point(rc.left + 2, rc.top),
1170
Point(rc.right - 2, rc.top),
1171
Point(rc.right, rc.top + 2),
1172
Point(rc.right, rc.bottom - 2),
1173
Point(rc.right - 2, rc.bottom),
1174
Point(rc.left + 2, rc.bottom),
1175
Point(rc.left, rc.bottom - 2),
1176
Point(rc.left, rc.top + 2),
1178
Polygon(pts, sizeof(pts) / sizeof(pts[0]), fore, back);
1180
RectangleDraw(rc, fore, back);
1186
static void PathRoundRectangle(cairo_t *context, double left, double top, double width, double height, int radius) {
1187
double degrees = M_PI / 180.0;
1189
cairo_new_sub_path(context);
1190
cairo_arc(context, left + width - radius, top + radius, radius, -90 * degrees, 0 * degrees);
1191
cairo_arc(context, left + width - radius, top + height - radius, radius, 0 * degrees, 90 * degrees);
1192
cairo_arc(context, left + radius, top + height - radius, radius, 90 * degrees, 180 * degrees);
1193
cairo_arc(context, left + radius, top + radius, radius, 180 * degrees, 270 * degrees);
1194
cairo_close_path(context);
1199
// Plot a point into a guint32 buffer symetrically to all 4 qudrants
1200
static void AllFour(guint32 *pixels, int stride, int width, int height, int x, int y, guint32 val) {
1201
pixels[y*stride+x] = val;
1202
pixels[y*stride+width-1-x] = val;
1203
pixels[(height-1-y)*stride+x] = val;
1204
pixels[(height-1-y)*stride+width-1-x] = val;
1207
static guint32 u32FromRGBA(guint8 r, guint8 g, guint8 b, guint8 a) {
1212
converter.pixVal[0] = r;
1213
converter.pixVal[1] = g;
1214
converter.pixVal[2] = b;
1215
converter.pixVal[3] = a;
1216
return converter.val;
1221
static unsigned int GetRValue(unsigned int co) {
1222
return (co >> 16) & 0xff;
1225
static unsigned int GetGValue(unsigned int co) {
1226
return (co >> 8) & 0xff;
1229
static unsigned int GetBValue(unsigned int co) {
1233
void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill,
1234
ColourAllocated outline, int alphaOutline, int flags) {
1236
if (context && rc.Width() > 0) {
1237
cairo_set_source_rgba(context,
1238
GetRValue(fill.AsLong()) / 255.0,
1239
GetGValue(fill.AsLong()) / 255.0,
1240
GetBValue(fill.AsLong()) / 255.0,
1242
PathRoundRectangle(context, rc.left + 1.0, rc.top+1.0, rc.right - rc.left - 2.0, rc.bottom - rc.top - 2.0, cornerSize);
1243
cairo_fill(context);
1245
cairo_set_source_rgba(context,
1246
GetRValue(outline.AsLong()) / 255.0,
1247
GetGValue(outline.AsLong()) / 255.0,
1248
GetBValue(outline.AsLong()) / 255.0,
1249
alphaOutline / 255.0);
1250
PathRoundRectangle(context, rc.left +0.5, rc.top+0.5, rc.right - rc.left - 1, rc.bottom - rc.top - 1, cornerSize);
1251
cairo_stroke(context);
1254
if (gc && drawable && rc.Width() > 0) {
1255
int width = rc.Width();
1256
int height = rc.Height();
1257
// Ensure not distorted too much by corners when small
1258
cornerSize = Platform::Minimum(cornerSize, (Platform::Minimum(width, height) / 2) - 2);
1259
// Make a 32 bit deep pixbuf with alpha
1260
GdkPixbuf *pixalpha = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
1262
guint32 valEmpty = u32FromRGBA(0,0,0,0);
1263
guint32 valFill = u32FromRGBA(GetRValue(fill.AsLong()),
1264
GetGValue(fill.AsLong()), GetBValue(fill.AsLong()), alphaFill);
1265
guint32 valOutline = u32FromRGBA(GetRValue(outline.AsLong()),
1266
GetGValue(outline.AsLong()), GetBValue(outline.AsLong()), alphaOutline);
1267
guint32 *pixels = reinterpret_cast<guint32 *>(gdk_pixbuf_get_pixels(pixalpha));
1268
int stride = gdk_pixbuf_get_rowstride(pixalpha) / 4;
1269
for (int yr=0; yr<height; yr++) {
1270
for (int xr=0; xr<width; xr++) {
1271
if ((xr==0) || (xr==width-1) || (yr == 0) || (yr == height-1)) {
1272
pixels[yr*stride+xr] = valOutline;
1274
pixels[yr*stride+xr] = valFill;
1278
for (int c=0;c<cornerSize; c++) {
1279
for (int xr=0;xr<c+1; xr++) {
1280
AllFour(pixels, stride, width, height, xr, c-xr, valEmpty);
1283
for (int xr=1;xr<cornerSize; xr++) {
1284
AllFour(pixels, stride, width, height, xr, cornerSize-xr, valOutline);
1288
gdk_draw_pixbuf(drawable, gc, pixalpha,
1289
0,0, rc.left,rc.top, width,height, GDK_RGB_DITHER_NORMAL, 0, 0);
1291
g_object_unref(pixalpha);
1296
void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
1299
cairo_arc(context, (rc.left + rc.right) / 2 + 0.5, (rc.top + rc.bottom) / 2 + 0.5,
1300
Platform::Minimum(rc.Width(), rc.Height()) / 2, 0, 2*M_PI);
1301
cairo_fill_preserve(context);
1303
cairo_stroke(context);
1305
gdk_draw_arc(drawable, gc, 1,
1306
rc.left + 1, rc.top + 1,
1307
rc.right - rc.left - 2, rc.bottom - rc.top - 2,
1310
// The subtraction of 1 here is similar to the case for RectangleDraw
1312
gdk_draw_arc(drawable, gc, 0,
1314
rc.right - rc.left - 1, rc.bottom - rc.top - 1,
1319
void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
1320
SurfaceImpl &surfi = static_cast<SurfaceImpl &>(surfaceSource);
1322
bool canDraw = surfi.psurf;
1324
bool canDraw = surfi.drawable;
1328
cairo_set_source_surface(context, surfi.psurf,
1329
rc.left - from.x, rc.top - from.y);
1330
cairo_rectangle(context, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top);
1331
cairo_fill(context);
1333
gdk_draw_drawable(drawable,
1335
static_cast<SurfaceImpl &>(surfaceSource).drawable,
1338
rc.right - rc.left, rc.bottom - rc.top);
1343
#ifndef DISABLE_GDK_FONT
1344
static size_t UTF8Len(char ch) {
1345
unsigned char uch = static_cast<unsigned char>(ch);
1348
else if (uch < (0x80 + 0x40 + 0x20))
1355
char *UTF8FromLatin1(const char *s, int &len) {
1356
char *utfForm = new char[len*2+1];
1358
for (int i=0;i<len;i++) {
1359
unsigned int uch = static_cast<unsigned char>(s[i]);
1361
utfForm[lenU++] = uch;
1363
utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
1364
utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1367
utfForm[lenU] = '\0';
1372
static char *UTF8FromIconv(const Converter &conv, const char *s, int &len) {
1374
char *utfForm = new char[len*3+1];
1375
char *pin = const_cast<char *>(s);
1376
size_t inLeft = len;
1377
char *pout = utfForm;
1378
size_t outLeft = len*3+1;
1379
size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1380
if (conversions != ((size_t)(-1))) {
1382
len = pout - utfForm;
1390
// Work out how many bytes are in a character by trying to convert using iconv,
1391
// returning the first length that succeeds.
1392
static size_t MultiByteLenFromIconv(const Converter &conv, const char *s, size_t len) {
1393
for (size_t lenMB=1; (lenMB<4) && (lenMB <= len); lenMB++) {
1395
char *pin = const_cast<char *>(s);
1396
size_t inLeft = lenMB;
1397
char *pout = wcForm;
1399
size_t conversions = conv.Convert(&pin, &inLeft, &pout, &outLeft);
1400
if (conversions != ((size_t)(-1))) {
1407
#ifndef DISABLE_GDK_FONT
1408
static char *UTF8FromGdkWChar(GdkWChar *wctext, int wclen) {
1409
char *utfForm = new char[wclen*3+1]; // Maximum of 3 UTF-8 bytes per character
1411
for (int i = 0; i < wclen && wctext[i]; i++) {
1412
unsigned int uch = wctext[i];
1414
utfForm[lenU++] = static_cast<char>(uch);
1415
} else if (uch < 0x800) {
1416
utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
1417
utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1419
utfForm[lenU++] = static_cast<char>(0xE0 | (uch >> 12));
1420
utfForm[lenU++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
1421
utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
1424
utfForm[lenU] = '\0';
1429
static char *UTF8FromDBCS(const char *s, int &len) {
1430
#ifndef DISABLE_GDK_FONT
1431
GdkWChar *wctext = new GdkWChar[len + 1];
1432
GdkWChar *wcp = wctext;
1433
int wclen = gdk_mbstowcs(wcp, s, len);
1435
// In the annoying case when non-locale chars in the line.
1436
// e.g. latin1 chars in Japanese locale.
1441
char *utfForm = UTF8FromGdkWChar(wctext, wclen);
1443
len = strlen(utfForm);
1450
static size_t UTF8CharLength(const char *s) {
1451
const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
1452
unsigned char ch = *us;
1455
} else if (ch < 0x80 + 0x40 + 0x20) {
1462
// On GTK+, wchar_t is 4 bytes
1464
const int maxLengthTextRun = 10000;
1466
void SurfaceImpl::DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1467
ColourAllocated fore) {
1472
if (gc && drawable) {
1474
int xText = rc.left;
1475
if (PFont(font_)->pfd) {
1477
bool useGFree = false;
1479
pango_layout_set_text(layout, s, len);
1482
SetConverter(PFont(font_)->characterSet);
1483
utfForm = UTF8FromIconv(conv, s, len);
1485
if (!utfForm) { // iconv failed so try DBCS if DBCS mode
1488
utfForm = UTF8FromDBCS(s, len);
1491
if (!utfForm) { // iconv and DBCS failed so treat as Latin1
1492
utfForm = UTF8FromLatin1(s, len);
1494
pango_layout_set_text(layout, utfForm, len);
1496
pango_layout_set_font_description(layout, PFont(font_)->pfd);
1498
pango_cairo_update_layout(context, layout);
1500
#ifdef PANGO_VERSION
1501
PangoLayoutLine *pll = pango_layout_get_line_readonly(layout,0);
1503
PangoLayoutLine *pll = pango_layout_get_line(layout,0);
1506
cairo_move_to(context, xText, ybase);
1507
pango_cairo_show_layout_line(context, pll);
1509
gdk_draw_layout_line(drawable, gc, xText, ybase, pll);
1518
#ifndef DISABLE_GDK_FONT
1519
// Draw text as a series of segments to avoid limitations in X servers
1520
const int segmentLength = 1000;
1521
bool draw8bit = true;
1522
if (et != singleByte) {
1523
GdkWChar wctext[maxLengthTextRun];
1524
if (len >= maxLengthTextRun)
1525
len = maxLengthTextRun-1;
1528
wclen = UTF16FromUTF8(s, len,
1529
static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
1530
} else { // dbcs, so convert using current locale
1531
char sMeasure[maxLengthTextRun];
1532
memcpy(sMeasure, s, len);
1533
sMeasure[len] = '\0';
1534
wclen = gdk_mbstowcs(
1535
wctext, sMeasure, maxLengthTextRun - 1);
1539
wctext[wclen] = L'\0';
1540
GdkWChar *wcp = wctext;
1541
while ((wclen > 0) && (xText < maxCoordinate)) {
1542
int lenDraw = Platform::Minimum(wclen, segmentLength);
1543
gdk_draw_text_wc(drawable, PFont(font_)->pfont, gc,
1544
xText, ybase, wcp, lenDraw);
1546
if (wclen > 0) { // Avoid next calculation if possible as may be expensive
1547
xText += gdk_text_width_wc(PFont(font_)->pfont,
1555
while ((len > 0) && (xText < maxCoordinate)) {
1556
int lenDraw = Platform::Minimum(len, segmentLength);
1557
gdk_draw_text(drawable, PFont(font_)->pfont, gc,
1558
xText, ybase, s, lenDraw);
1560
if (len > 0) { // Avoid next calculation if possible as may be expensive
1561
xText += gdk_text_width(PFont(font_)->pfont, s, lenDraw);
1570
void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1571
ColourAllocated fore, ColourAllocated back) {
1572
FillRectangle(rc, back);
1573
DrawTextBase(rc, font_, ybase, s, len, fore);
1576
// On GTK+, exactly same as DrawTextNoClip
1577
void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1578
ColourAllocated fore, ColourAllocated back) {
1579
FillRectangle(rc, back);
1580
DrawTextBase(rc, font_, ybase, s, len, fore);
1583
void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len,
1584
ColourAllocated fore) {
1585
// Avoid drawing spaces in transparent mode
1586
for (int i=0;i<len;i++) {
1588
DrawTextBase(rc, font_, ybase, s, len, fore);
1594
class ClusterIterator {
1595
PangoLayoutIter *iter;
1604
ClusterIterator(PangoLayout *layout, int len) : lenPositions(len), finished(false),
1605
positionStart(0), position(0), distance(0), curIndex(0) {
1606
iter = pango_layout_get_iter(layout);
1607
pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1609
~ClusterIterator() {
1610
pango_layout_iter_free(iter);
1614
positionStart = position;
1615
if (pango_layout_iter_next_cluster(iter)) {
1616
pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
1617
position = PANGO_PIXELS(pos.x);
1618
curIndex = pango_layout_iter_get_index(iter);
1621
position = PANGO_PIXELS(pos.x + pos.width);
1622
curIndex = lenPositions;
1624
distance = position - positionStart;
1628
void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) {
1629
if (font_.GetID()) {
1630
const int lenPositions = len;
1631
if (PFont(font_)->pfd) {
1633
int width = PFont(font_)->CharWidth(*s, et);
1635
positions[0] = width;
1639
pango_layout_set_font_description(layout, PFont(font_)->pfd);
1641
// Simple and direct as UTF-8 is native Pango encoding
1643
pango_layout_set_text(layout, s, len);
1644
ClusterIterator iti(layout, lenPositions);
1645
while (!iti.finished) {
1647
int places = iti.curIndex - i;
1648
while (i < iti.curIndex) {
1649
// Evenly distribute space among bytes of this cluster.
1650
// Would be better to find number of characters and then
1651
// divide evenly between characters with each byte of a character
1652
// being at the same position.
1653
positions[i] = iti.position - (iti.curIndex - 1 - i) * iti.distance / places;
1657
PLATFORM_ASSERT(i == lenPositions);
1659
int positionsCalculated = 0;
1661
SetConverter(PFont(font_)->characterSet);
1662
char *utfForm = UTF8FromIconv(conv, s, len);
1664
// Convert to UTF-8 so can ask Pango for widths, then
1665
// Loop through UTF-8 and DBCS forms, taking account of different
1666
// character byte lengths.
1667
Converter convMeasure("UCS-2", CharacterSetID(characterSet), false);
1668
pango_layout_set_text(layout, utfForm, strlen(utfForm));
1670
int clusterStart = 0;
1671
ClusterIterator iti(layout, strlen(utfForm));
1672
while (!iti.finished) {
1674
int clusterEnd = iti.curIndex;
1675
int places = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
1677
while (clusterStart < clusterEnd) {
1678
size_t lenChar = MultiByteLenFromIconv(convMeasure, s+i, len-i);
1680
positions[i++] = iti.position - (places - place) * iti.distance / places;
1681
positionsCalculated++;
1683
clusterStart += UTF8CharLength(utfForm+clusterStart);
1688
PLATFORM_ASSERT(i == lenPositions);
1691
if (positionsCalculated < 1 ) {
1692
// Either Latin1 or DBCS conversion failed so treat as Latin1.
1693
bool useGFree = false;
1694
SetConverter(PFont(font_)->characterSet);
1695
char *utfForm = UTF8FromIconv(conv, s, len);
1697
utfForm = UTF8FromLatin1(s, len);
1699
pango_layout_set_text(layout, utfForm, len);
1701
int clusterStart = 0;
1702
// Each Latin1 input character may take 1 or 2 bytes in UTF-8
1703
// and groups of up to 3 may be represented as ligatures.
1704
ClusterIterator iti(layout, strlen(utfForm));
1705
while (!iti.finished) {
1707
int clusterEnd = iti.curIndex;
1708
int ligatureLength = g_utf8_strlen(utfForm + clusterStart, clusterEnd - clusterStart);
1709
PLATFORM_ASSERT(ligatureLength > 0 && ligatureLength <= 3);
1710
for (int charInLig=0; charInLig<ligatureLength; charInLig++) {
1711
positions[i++] = iti.position - (ligatureLength - 1 - charInLig) * iti.distance / ligatureLength;
1713
clusterStart = clusterEnd;
1720
PLATFORM_ASSERT(i == lenPositions);
1724
PFont(font_)->SetCharWidth(*s, positions[0], et);
1728
#ifndef DISABLE_GDK_FONT
1730
GdkFont *gf = PFont(font_)->pfont;
1731
bool measure8bit = true;
1732
if (et != singleByte) {
1733
GdkWChar wctext[maxLengthTextRun];
1734
if (len >= maxLengthTextRun)
1735
len = maxLengthTextRun-1;
1738
wclen = UTF16FromUTF8(s, len,
1739
static_cast<wchar_t *>(static_cast<void *>(wctext)), maxLengthTextRun - 1);
1740
} else { // dbcsMode, so convert using current locale
1741
char sDraw[maxLengthTextRun];
1742
memcpy(sDraw, s, len);
1744
wclen = gdk_mbstowcs(
1745
wctext, sDraw, maxLengthTextRun - 1);
1748
measure8bit = false;
1749
wctext[wclen] = L'\0';
1750
// Map widths back to utf-8 or DBCS input string
1752
for (int iU = 0; iU < wclen; iU++) {
1753
int width = gdk_char_width_wc(gf, wctext[iU]);
1754
totalWidth += width;
1757
lenChar = UTF8Len(s[i]);
1759
lenChar = mblen(s+i, MB_CUR_MAX);
1764
positions[i++] = totalWidth;
1767
while (i < len) { // In case of problems with lengths
1768
positions[i++] = totalWidth;
1773
// Either Latin1 or conversion failed so treat as Latin1.
1774
for (int i = 0; i < len; i++) {
1775
int width = gdk_char_width(gf, s[i]);
1776
totalWidth += width;
1777
positions[i] = totalWidth;
1782
// No font so return an ascending range of values
1783
for (int i = 0; i < len; i++) {
1784
positions[i] = i + 1;
1789
int SurfaceImpl::WidthText(Font &font_, const char *s, int len) {
1790
if (font_.GetID()) {
1791
if (PFont(font_)->pfd) {
1793
pango_layout_set_font_description(layout, PFont(font_)->pfd);
1795
bool useGFree = false;
1797
pango_layout_set_text(layout, s, len);
1801
utfForm = UTF8FromDBCS(s, len);
1803
if (!utfForm) { // DBCS failed so treat as iconv
1804
SetConverter(PFont(font_)->characterSet);
1805
utfForm = UTF8FromIconv(conv, s, len);
1807
if (!utfForm) { // g_locale_to_utf8 failed so treat as Latin1
1808
utfForm = UTF8FromLatin1(s, len);
1810
pango_layout_set_text(layout, utfForm, len);
1812
#ifdef PANGO_VERSION
1813
PangoLayoutLine *pangoLine = pango_layout_get_line_readonly(layout,0);
1815
PangoLayoutLine *pangoLine = pango_layout_get_line(layout,0);
1817
pango_layout_line_get_extents(pangoLine, NULL, &pos);
1823
return PANGO_PIXELS(pos.width);
1825
#ifndef DISABLE_GDK_FONT
1827
GdkWChar wctext[maxLengthTextRun];
1828
size_t wclen = UTF16FromUTF8(s, len, static_cast<wchar_t *>(static_cast<void *>(wctext)),
1829
sizeof(wctext) / sizeof(GdkWChar) - 1);
1830
wctext[wclen] = L'\0';
1831
return gdk_text_width_wc(PFont(font_)->pfont, wctext, wclen);
1833
return gdk_text_width(PFont(font_)->pfont, s, len);
1843
int SurfaceImpl::WidthChar(Font &font_, char ch) {
1844
if (font_.GetID()) {
1845
if (PFont(font_)->pfd) {
1846
return WidthText(font_, &ch, 1);
1848
#ifndef DISABLE_GDK_FONT
1849
return gdk_char_width(PFont(font_)->pfont, ch);
1858
// Three possible strategies for determining ascent and descent of font:
1859
// 1) Call gdk_string_extents with string containing all letters, numbers and punctuation.
1860
// 2) Use the ascent and descent fields of GdkFont.
1861
// 3) Call gdk_string_extents with string as 1 but also including accented capitals.
1862
// Smallest values given by 1 and largest by 3 with 2 in between.
1863
// Techniques 1 and 2 sometimes chop off extreme portions of ascenders and
1864
// descenders but are mostly OK except for accented characters like � which are
1865
// rarely used in code.
1867
// This string contains a good range of characters to test for size.
1868
//const char largeSizeString[] = "���� `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1869
// "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1871
const char sizeString[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1872
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1875
int SurfaceImpl::Ascent(Font &font_) {
1876
if (!(font_.GetID()))
1880
int ascent = PFont(font_)->ascent;
1881
if ((ascent == 0) && (PFont(font_)->pfd)) {
1882
PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
1883
PFont(font_)->pfd, pango_context_get_language(pcontext));
1884
PFont(font_)->ascent =
1885
PANGO_PIXELS(pango_font_metrics_get_ascent(metrics));
1886
pango_font_metrics_unref(metrics);
1887
ascent = PFont(font_)->ascent;
1889
#ifndef DISABLE_GDK_FONT
1890
if ((ascent == 0) && (PFont(font_)->pfont)) {
1891
ascent = PFont(font_)->pfont->ascent;
1907
gdk_string_extents(PFont(font_)->pfont, sizeString,
1908
&lbearing, &rbearing, &width, &ascent, &descent);
1913
int SurfaceImpl::Descent(Font &font_) {
1914
if (!(font_.GetID()))
1918
if (PFont(font_)->pfd) {
1919
PangoFontMetrics *metrics = pango_context_get_metrics(pcontext,
1920
PFont(font_)->pfd, pango_context_get_language(pcontext));
1921
int descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
1922
pango_font_metrics_unref(metrics);
1925
#ifndef DISABLE_GDK_FONT
1926
return PFont(font_)->pfont->descent;
1938
gdk_string_extents(PFont(font_)->pfont, sizeString,
1939
&lbearing, &rbearing, &width, &ascent, &descent);
1944
int SurfaceImpl::InternalLeading(Font &) {
1948
int SurfaceImpl::ExternalLeading(Font &) {
1952
int SurfaceImpl::Height(Font &font_) {
1953
return Ascent(font_) + Descent(font_);
1956
int SurfaceImpl::AverageCharWidth(Font &font_) {
1957
return WidthChar(font_, 'n');
1960
int SurfaceImpl::SetPalette(Palette *, bool) {
1961
// Handled in palette allocation for GTK so this does nothing
1965
void SurfaceImpl::SetClip(PRectangle rc) {
1967
cairo_rectangle(context, rc.left, rc.top, rc.right, rc.bottom);
1968
cairo_clip(context);
1970
GdkRectangle area = {rc.left, rc.top,
1971
rc.right - rc.left, rc.bottom - rc.top};
1972
gdk_gc_set_clip_rectangle(gc, &area);
1976
void SurfaceImpl::FlushCachedState() {}
1978
void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) {
1983
void SurfaceImpl::SetDBCSMode(int codePage) {
1984
if (codePage && (codePage != SC_CP_UTF8))
1988
Surface *Surface::Allocate() {
1989
return new SurfaceImpl;
1992
Window::~Window() {}
1994
void Window::Destroy() {
1996
gtk_widget_destroy(GTK_WIDGET(wid));
2000
bool Window::HasFocus() {
2001
return IS_WIDGET_FOCUSSED(wid);
2004
PRectangle Window::GetPosition() {
2005
// Before any size allocated pretend its 1000 wide so not scrolled
2006
PRectangle rc(0, 0, 1000, 1000);
2008
rc.left = PWidget(wid)->allocation.x;
2009
rc.top = PWidget(wid)->allocation.y;
2010
if (PWidget(wid)->allocation.width > 20) {
2011
rc.right = rc.left + PWidget(wid)->allocation.width;
2012
rc.bottom = rc.top + PWidget(wid)->allocation.height;
2018
void Window::SetPosition(PRectangle rc) {
2019
GtkAllocation alloc;
2022
alloc.width = rc.Width();
2023
alloc.height = rc.Height();
2024
gtk_widget_size_allocate(PWidget(wid), &alloc);
2027
void Window::SetPositionRelative(PRectangle rc, Window relativeTo) {
2030
gdk_window_get_origin(PWidget(relativeTo.wid)->window, &ox, &oy);
2038
/* do some corrections to fit into screen */
2039
int sizex = rc.right - rc.left;
2040
int sizey = rc.bottom - rc.top;
2041
int screenWidth = gdk_screen_width();
2042
int screenHeight = gdk_screen_height();
2043
if (sizex > screenWidth)
2044
ox = 0; /* the best we can do */
2045
else if (ox + sizex > screenWidth)
2046
ox = screenWidth - sizex;
2047
if (oy + sizey > screenHeight)
2048
oy = screenHeight - sizey;
2050
gtk_window_move(GTK_WINDOW(PWidget(wid)), ox, oy);
2052
gtk_widget_set_size_request(PWidget(wid), sizex, sizey);
2055
PRectangle Window::GetClientPosition() {
2056
// On GTK+, the client position is the window position
2057
return GetPosition();
2060
void Window::Show(bool show) {
2062
gtk_widget_show(PWidget(wid));
2065
void Window::InvalidateAll() {
2067
gtk_widget_queue_draw(PWidget(wid));
2071
void Window::InvalidateRectangle(PRectangle rc) {
2073
gtk_widget_queue_draw_area(PWidget(wid),
2075
rc.right - rc.left, rc.bottom - rc.top);
2079
void Window::SetFont(Font &) {
2080
// Can not be done generically but only needed for ListBox
2083
void Window::SetCursor(Cursor curs) {
2084
// We don't set the cursor to same value numerous times under gtk because
2085
// it stores the cursor in the window once it's set
2086
if (curs == cursorLast)
2093
gdkCurs = gdk_cursor_new(GDK_XTERM);
2096
gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
2099
gdkCurs = gdk_cursor_new(GDK_CENTER_PTR);
2102
gdkCurs = gdk_cursor_new(GDK_WATCH);
2105
gdkCurs = gdk_cursor_new(GDK_HAND2);
2107
case cursorReverseArrow:
2108
gdkCurs = gdk_cursor_new(GDK_RIGHT_PTR);
2111
gdkCurs = gdk_cursor_new(GDK_LEFT_PTR);
2112
cursorLast = cursorArrow;
2116
if (PWidget(wid)->window)
2117
gdk_window_set_cursor(PWidget(wid)->window, gdkCurs);
2118
gdk_cursor_unref(gdkCurs);
2121
void Window::SetTitle(const char *s) {
2122
gtk_window_set_title(GTK_WINDOW(wid), s);
2125
/* Returns rectangle of monitor pt is on, both rect and pt are in Window's
2126
gdk window coordinates */
2127
PRectangle Window::GetMonitorRect(Point pt) {
2128
gint x_offset, y_offset;
2130
gdk_window_get_origin(PWidget(wid)->window, &x_offset, &y_offset);
2132
#if GTK_CHECK_VERSION(2,2,0)
2139
screen = gtk_widget_get_screen(PWidget(wid));
2140
monitor_num = gdk_screen_get_monitor_at_point(screen, pt.x + x_offset, pt.y + y_offset);
2141
gdk_screen_get_monitor_geometry(screen, monitor_num, &rect);
2144
return PRectangle(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
2148
return PRectangle(-x_offset, -y_offset, (-x_offset) + gdk_screen_width(),
2149
(-y_offset) + gdk_screen_height());
2154
const char *xpm_data;
2158
static void list_image_free(gpointer, gpointer value, gpointer) {
2159
ListImage *list_image = (ListImage *) value;
2160
if (list_image->pixbuf)
2161
g_object_unref (list_image->pixbuf);
2165
ListBox::ListBox() {
2168
ListBox::~ListBox() {
2177
class ListBoxX : public ListBox {
2181
GtkCellRenderer* pixbuf_renderer;
2183
int desiredVisibleRows;
2184
unsigned int maxItemCharacters;
2185
unsigned int aveCharWidth;
2187
CallBackAction doubleClickAction;
2188
void *doubleClickActionData;
2190
ListBoxX() : list(0), scroller(0), pixhash(NULL), pixbuf_renderer(0),
2191
desiredVisibleRows(5), maxItemCharacters(0),
2192
aveCharWidth(1), doubleClickAction(NULL), doubleClickActionData(NULL) {
2194
virtual ~ListBoxX() {
2196
g_hash_table_foreach((GHashTable *) pixhash, list_image_free, NULL);
2197
g_hash_table_destroy((GHashTable *) pixhash);
2200
virtual void SetFont(Font &font);
2201
virtual void Create(Window &parent, int ctrlID, Point location_, int lineHeight_, bool unicodeMode_);
2202
virtual void SetAverageCharWidth(int width);
2203
virtual void SetVisibleRows(int rows);
2204
virtual int GetVisibleRows() const;
2205
virtual PRectangle GetDesiredRect();
2206
virtual int CaretFromEdge();
2207
virtual void Clear();
2208
virtual void Append(char *s, int type = -1);
2209
virtual int Length();
2210
virtual void Select(int n);
2211
virtual int GetSelection();
2212
virtual int Find(const char *prefix);
2213
virtual void GetValue(int n, char *value, int len);
2214
virtual void RegisterImage(int type, const char *xpm_data);
2215
virtual void ClearRegisteredImages();
2216
virtual void SetDoubleClickAction(CallBackAction action, void *data) {
2217
doubleClickAction = action;
2218
doubleClickActionData = data;
2220
virtual void SetList(const char *listText, char separator, char typesep);
2223
ListBox *ListBox::Allocate() {
2224
ListBoxX *lb = new ListBoxX();
2228
static gboolean ButtonPress(GtkWidget *, GdkEventButton* ev, gpointer p) {
2230
ListBoxX* lb = reinterpret_cast<ListBoxX*>(p);
2231
if (ev->type == GDK_2BUTTON_PRESS && lb->doubleClickAction != NULL) {
2232
lb->doubleClickAction(lb->doubleClickActionData);
2237
// No pointer back to Scintilla to save status
2242
/* Change the active color to the selected color so the listbox uses the color
2243
scheme that it would use if it had the focus. */
2244
static void StyleSet(GtkWidget *w, GtkStyle*, void*) {
2247
g_return_if_fail(w != NULL);
2249
/* Copy the selected color to active. Note that the modify calls will cause
2250
recursive calls to this function after the value is updated and w->style to
2251
be set to a new object */
2252
style = gtk_widget_get_style(w);
2255
if (!gdk_color_equal(&style->base[GTK_STATE_SELECTED], &style->base[GTK_STATE_ACTIVE]))
2256
gtk_widget_modify_base(w, GTK_STATE_ACTIVE, &style->base[GTK_STATE_SELECTED]);
2258
style = gtk_widget_get_style(w);
2261
if (!gdk_color_equal(&style->text[GTK_STATE_SELECTED], &style->text[GTK_STATE_ACTIVE]))
2262
gtk_widget_modify_text(w, GTK_STATE_ACTIVE, &style->text[GTK_STATE_SELECTED]);
2265
void ListBoxX::Create(Window &, int, Point, int, bool) {
2266
wid = gtk_window_new(GTK_WINDOW_POPUP);
2268
GtkWidget *frame = gtk_frame_new(NULL);
2269
gtk_widget_show(frame);
2270
gtk_container_add(GTK_CONTAINER(GetID()), frame);
2271
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
2272
gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
2274
scroller = gtk_scrolled_window_new(NULL, NULL);
2275
gtk_container_set_border_width(GTK_CONTAINER(scroller), 0);
2276
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
2277
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2278
gtk_container_add(GTK_CONTAINER(frame), PWidget(scroller));
2279
gtk_widget_show(PWidget(scroller));
2281
/* Tree and its model */
2282
GtkListStore *store =
2283
gtk_list_store_new(N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING);
2285
list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2286
g_signal_connect(G_OBJECT(list), "style-set", G_CALLBACK(StyleSet), NULL);
2288
GtkTreeSelection *selection =
2289
gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2290
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
2291
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
2292
gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list), FALSE);
2295
GtkTreeViewColumn *column = gtk_tree_view_column_new();
2296
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2297
gtk_tree_view_column_set_title(column, "Autocomplete");
2299
pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
2300
gtk_cell_renderer_set_fixed_size(pixbuf_renderer, 0, -1);
2301
gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE);
2302
gtk_tree_view_column_add_attribute(column, pixbuf_renderer,
2303
"pixbuf", PIXBUF_COLUMN);
2305
GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
2306
gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), 1);
2307
gtk_tree_view_column_pack_start(column, renderer, TRUE);
2308
gtk_tree_view_column_add_attribute(column, renderer,
2309
"text", TEXT_COLUMN);
2311
gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
2312
if (g_object_class_find_property(G_OBJECT_GET_CLASS(list), "fixed-height-mode"))
2313
g_object_set(G_OBJECT(list), "fixed-height-mode", TRUE, NULL);
2315
GtkWidget *wid = PWidget(list); // No code inside the G_OBJECT macro
2316
gtk_container_add(GTK_CONTAINER(PWidget(scroller)), wid);
2317
gtk_widget_show(wid);
2318
g_signal_connect(G_OBJECT(wid), "button_press_event",
2319
G_CALLBACK(ButtonPress), this);
2320
gtk_widget_realize(PWidget(wid));
2323
void ListBoxX::SetFont(Font &scint_font) {
2324
// Only do for Pango font as there have been crashes for GDK fonts
2325
if (Created() && PFont(scint_font)->pfd) {
2326
// Current font is Pango font
2327
gtk_widget_modify_font(PWidget(list), PFont(scint_font)->pfd);
2331
void ListBoxX::SetAverageCharWidth(int width) {
2332
aveCharWidth = width;
2335
void ListBoxX::SetVisibleRows(int rows) {
2336
desiredVisibleRows = rows;
2339
int ListBoxX::GetVisibleRows() const {
2340
return desiredVisibleRows;
2343
PRectangle ListBoxX::GetDesiredRect() {
2344
// Before any size allocated pretend its 100 wide so not scrolled
2345
PRectangle rc(0, 0, 100, 100);
2347
int rows = Length();
2348
if ((rows == 0) || (rows > desiredVisibleRows))
2349
rows = desiredVisibleRows;
2354
// First calculate height of the clist for our desired visible
2355
// row count otherwise it tries to expand to the total # of rows
2359
GtkTreeViewColumn * column =
2360
gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
2361
gtk_tree_view_column_cell_get_size(column, NULL,
2362
NULL, NULL, &row_width, &row_height);
2363
int ythickness = PWidget(list)->style->ythickness;
2364
height = (rows * row_height
2366
+ GTK_CONTAINER(PWidget(list))->border_width + 1));
2367
gtk_widget_set_size_request(GTK_WIDGET(PWidget(list)), -1, height);
2369
// Get the size of the scroller because we set usize on the window
2370
gtk_widget_size_request(GTK_WIDGET(scroller), &req);
2371
rc.right = req.width;
2372
rc.bottom = req.height;
2374
gtk_widget_set_size_request(GTK_WIDGET(list), -1, -1);
2375
int width = maxItemCharacters;
2378
rc.right = width * (aveCharWidth + aveCharWidth / 3);
2379
if (Length() > rows)
2380
rc.right = rc.right + 16;
2385
int ListBoxX::CaretFromEdge() {
2386
gint renderer_width, renderer_height;
2387
gtk_cell_renderer_get_fixed_size(pixbuf_renderer, &renderer_width,
2389
return 4 + renderer_width;
2392
void ListBoxX::Clear() {
2393
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2394
gtk_list_store_clear(GTK_LIST_STORE(model));
2395
maxItemCharacters = 0;
2398
static void init_pixmap(ListImage *list_image) {
2399
const char *textForm = list_image->xpm_data;
2400
const char * const * xpm_lineform = reinterpret_cast<const char * const *>(textForm);
2401
const char **xpm_lineformfromtext = 0;
2402
// The XPM data can be either in atext form as will be read from a file
2403
// or in a line form (array of char *) as will be used for images defined in code.
2404
// Test for text form and convert to line form
2405
if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
2406
// Test done is two parts to avoid possibility of overstepping the memory
2407
// if memcmp implemented strangely. Must be 4 bytes at least at destination.
2408
xpm_lineformfromtext = XPM::LinesFormFromTextForm(textForm);
2409
xpm_lineform = xpm_lineformfromtext;
2412
// Drop any existing pixmap/bitmap as data may have changed
2413
if (list_image->pixbuf)
2414
g_object_unref(list_image->pixbuf);
2415
list_image->pixbuf =
2416
gdk_pixbuf_new_from_xpm_data((const gchar**)xpm_lineform);
2417
delete []xpm_lineformfromtext;
2422
void ListBoxX::Append(char *s, int type) {
2423
ListImage *list_image = NULL;
2424
if ((type >= 0) && pixhash) {
2425
list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash
2426
, (gconstpointer) GINT_TO_POINTER(type));
2429
GtkListStore *store =
2430
GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
2431
gtk_list_store_append(GTK_LIST_STORE(store), &iter);
2433
if (NULL == list_image->pixbuf)
2434
init_pixmap(list_image);
2435
if (list_image->pixbuf) {
2436
gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2437
PIXBUF_COLUMN, list_image->pixbuf,
2438
TEXT_COLUMN, s, -1);
2440
gint pixbuf_width = gdk_pixbuf_get_width(list_image->pixbuf);
2441
gint renderer_height, renderer_width;
2442
gtk_cell_renderer_get_fixed_size(pixbuf_renderer,
2443
&renderer_width, &renderer_height);
2444
if (pixbuf_width > renderer_width)
2445
gtk_cell_renderer_set_fixed_size(pixbuf_renderer,
2448
gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2449
TEXT_COLUMN, s, -1);
2452
gtk_list_store_set(GTK_LIST_STORE(store), &iter,
2453
TEXT_COLUMN, s, -1);
2455
size_t len = strlen(s);
2456
if (maxItemCharacters < len)
2457
maxItemCharacters = len;
2460
int ListBoxX::Length() {
2462
return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
2463
(GTK_TREE_VIEW(list)), NULL);
2467
void ListBoxX::Select(int n) {
2469
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2470
GtkTreeSelection *selection =
2471
gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2474
gtk_tree_selection_unselect_all(selection);
2478
bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
2480
gtk_tree_selection_select_iter(selection, &iter);
2482
// Move the scrollbar to show the selection.
2483
int total = Length();
2484
GtkAdjustment *adj =
2485
gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list));
2486
gfloat value = ((gfloat)n / total) * (adj->upper - adj->lower)
2487
+ adj->lower - adj->page_size / 2;
2492
GtkTreeViewColumn * column =
2493
gtk_tree_view_get_column(GTK_TREE_VIEW(list), 0);
2494
gtk_tree_view_column_cell_get_size(column, NULL, NULL,
2495
NULL, &row_width, &row_height);
2497
int rows = Length();
2498
if ((rows == 0) || (rows > desiredVisibleRows))
2499
rows = desiredVisibleRows;
2501
// Odd rows to display -- We are now in the middle.
2502
// Align it so that we don't chop off rows.
2503
value += (gfloat)row_height / 2.0;
2506
value = (value < 0)? 0 : value;
2507
value = (value > (adj->upper - adj->page_size))?
2508
(adj->upper - adj->page_size) : value;
2511
gtk_adjustment_set_value(adj, value);
2513
gtk_tree_selection_unselect_all(selection);
2517
int ListBoxX::GetSelection() {
2519
GtkTreeModel *model;
2520
GtkTreeSelection *selection;
2521
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
2522
if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
2523
GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
2524
int *indices = gtk_tree_path_get_indices(path);
2525
// Don't free indices.
2532
int ListBoxX::Find(const char *prefix) {
2534
GtkTreeModel *model =
2535
gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2536
bool valid = gtk_tree_model_get_iter_first(model, &iter) != FALSE;
2540
gtk_tree_model_get(model, &iter, TEXT_COLUMN, &s, -1);
2541
if (s && (0 == strncmp(prefix, s, strlen(prefix)))) {
2546
valid = gtk_tree_model_iter_next(model, &iter) != FALSE;
2552
void ListBoxX::GetValue(int n, char *value, int len) {
2555
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
2556
bool valid = gtk_tree_model_iter_nth_child(model, &iter, NULL, n) != FALSE;
2558
gtk_tree_model_get(model, &iter, TEXT_COLUMN, &text, -1);
2560
if (text && len > 0) {
2561
strncpy(value, text, len);
2562
value[len - 1] = '\0';
2569
// g_return_if_fail causes unnecessary compiler warning in release compile.
2571
#pragma warning(disable: 4127)
2574
void ListBoxX::RegisterImage(int type, const char *xpm_data) {
2575
g_return_if_fail(xpm_data);
2577
// Saved and use the saved copy so caller's copy can disappear.
2578
xset.Add(type, xpm_data);
2579
XPM *pxpm = xset.Get(type);
2580
xpm_data = reinterpret_cast<const char *>(pxpm->InLinesForm());
2583
pixhash = g_hash_table_new(g_direct_hash, g_direct_equal);
2585
ListImage *list_image = (ListImage *) g_hash_table_lookup((GHashTable *) pixhash,
2586
(gconstpointer) GINT_TO_POINTER(type));
2588
// Drop icon already registered
2589
if (list_image->pixbuf)
2590
g_object_unref(list_image->pixbuf);
2591
list_image->pixbuf = NULL;
2592
list_image->xpm_data = xpm_data;
2594
list_image = g_new0(ListImage, 1);
2595
list_image->xpm_data = xpm_data;
2596
g_hash_table_insert((GHashTable *) pixhash, GINT_TO_POINTER(type),
2597
(gpointer) list_image);
2601
void ListBoxX::ClearRegisteredImages() {
2605
void ListBoxX::SetList(const char *listText, char separator, char typesep) {
2607
int count = strlen(listText) + 1;
2608
char *words = new char[count];
2610
memcpy(words, listText, count);
2611
char *startword = words;
2612
char *numword = NULL;
2614
for (; words[i]; i++) {
2615
if (words[i] == separator) {
2619
Append(startword, numword?atoi(numword + 1):-1);
2620
startword = words + i + 1;
2622
} else if (words[i] == typesep) {
2623
numword = words + i;
2629
Append(startword, numword?atoi(numword + 1):-1);
2635
Menu::Menu() : mid(0) {}
2637
void Menu::CreatePopUp() {
2639
mid = gtk_menu_new();
2640
#if GLIB_CHECK_VERSION(2,10,0)
2641
g_object_ref_sink(G_OBJECT(mid));
2643
g_object_ref(G_OBJECT(mid));
2644
gtk_object_sink(GTK_OBJECT(G_OBJECT(mid)));
2648
void Menu::Destroy() {
2650
g_object_unref(G_OBJECT(mid));
2654
static void MenuPositionFunc(GtkMenu *, gint *x, gint *y, gboolean *, gpointer userData) {
2655
sptr_t intFromPointer = reinterpret_cast<sptr_t>(userData);
2656
*x = intFromPointer & 0xffff;
2657
*y = intFromPointer >> 16;
2660
void Menu::Show(Point pt, Window &) {
2661
int screenHeight = gdk_screen_height();
2662
int screenWidth = gdk_screen_width();
2663
GtkMenu *widget = reinterpret_cast<GtkMenu *>(mid);
2664
gtk_widget_show_all(GTK_WIDGET(widget));
2665
GtkRequisition requisition;
2666
gtk_widget_size_request(GTK_WIDGET(widget), &requisition);
2667
if ((pt.x + requisition.width) > screenWidth) {
2668
pt.x = screenWidth - requisition.width;
2670
if ((pt.y + requisition.height) > screenHeight) {
2671
pt.y = screenHeight - requisition.height;
2673
gtk_menu_popup(widget, NULL, NULL, MenuPositionFunc,
2674
reinterpret_cast<void *>((pt.y << 16) | pt.x), 0,
2675
gtk_get_current_event_time());
2678
ElapsedTime::ElapsedTime() {
2680
g_get_current_time(&curTime);
2681
bigBit = curTime.tv_sec;
2682
littleBit = curTime.tv_usec;
2685
class DynamicLibraryImpl : public DynamicLibrary {
2689
DynamicLibraryImpl(const char *modulePath) {
2690
m = g_module_open(modulePath, G_MODULE_BIND_LAZY);
2693
virtual ~DynamicLibraryImpl() {
2698
// Use g_module_symbol to get a pointer to the relevant function.
2699
virtual Function FindFunction(const char *name) {
2701
gpointer fn_address = NULL;
2702
gboolean status = g_module_symbol(m, name, &fn_address);
2704
return static_cast<Function>(fn_address);
2711
virtual bool IsValid() {
2716
DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {
2717
return static_cast<DynamicLibrary *>( new DynamicLibraryImpl(modulePath) );
2720
double ElapsedTime::Duration(bool reset) {
2722
g_get_current_time(&curTime);
2723
long endBigBit = curTime.tv_sec;
2724
long endLittleBit = curTime.tv_usec;
2725
double result = 1000000.0 * (endBigBit - bigBit);
2726
result += endLittleBit - littleBit;
2727
result /= 1000000.0;
2730
littleBit = endLittleBit;
2735
ColourDesired Platform::Chrome() {
2736
return ColourDesired(0xe0, 0xe0, 0xe0);
2739
ColourDesired Platform::ChromeHighlight() {
2740
return ColourDesired(0xff, 0xff, 0xff);
2743
const char *Platform::DefaultFont() {
2745
return "Lucida Console";
2751
int Platform::DefaultFontSize() {
2759
unsigned int Platform::DoubleClickTime() {
2760
return 500; // Half a second
2763
bool Platform::MouseButtonBounce() {
2767
void Platform::DebugDisplay(const char *s) {
2768
fprintf(stderr, "%s", s);
2771
bool Platform::IsKeyDown(int) {
2772
// TODO: discover state of keys in GTK+/X
2776
long Platform::SendScintilla(
2777
WindowID w, unsigned int msg, unsigned long wParam, long lParam) {
2778
return scintilla_send_message(SCINTILLA(w), msg, wParam, lParam);
2781
long Platform::SendScintillaPointer(
2782
WindowID w, unsigned int msg, unsigned long wParam, void *lParam) {
2783
return scintilla_send_message(SCINTILLA(w), msg, wParam,
2784
reinterpret_cast<sptr_t>(lParam));
2787
bool Platform::IsDBCSLeadByte(int codePage, char ch) {
2788
// Byte ranges found in Wikipedia articles with relevant search strings in each case
2789
unsigned char uch = static_cast<unsigned char>(ch);
2793
return ((uch >= 0x81) && (uch <= 0x9F)) ||
2794
((uch >= 0xE0) && (uch <= 0xEF));
2797
return (uch >= 0x81) && (uch <= 0xFE);
2800
return (uch >= 0x81) && (uch <= 0xFE);
2801
// Korean EUC-KR may be code page 949.
2806
int Platform::DBCSCharLength(int codePage, const char *s) {
2807
if (codePage == 932 || codePage == 936 || codePage == 950) {
2808
return IsDBCSLeadByte(codePage, s[0]) ? 2 : 1;
2810
int bytes = mblen(s, MB_CUR_MAX);
2818
int Platform::DBCSCharMaxLength() {
2823
// These are utility functions not really tied to a platform
2825
int Platform::Minimum(int a, int b) {
2832
int Platform::Maximum(int a, int b) {
2842
void Platform::DebugPrintf(const char *format, ...) {
2845
va_start(pArguments, format);
2846
vsprintf(buffer, format, pArguments);
2848
Platform::DebugDisplay(buffer);
2851
void Platform::DebugPrintf(const char *, ...) {}
2855
// Not supported for GTK+
2856
static bool assertionPopUps = true;
2858
bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {
2859
bool ret = assertionPopUps;
2860
assertionPopUps = assertionPopUps_;
2864
void Platform::Assert(const char *c, const char *file, int line) {
2866
sprintf(buffer, "Assertion [%s] failed at %s %d", c, file, line);
2867
strcat(buffer, "\r\n");
2868
Platform::DebugDisplay(buffer);
2872
int Platform::Clamp(int val, int minVal, int maxVal) {
2880
void Platform_Initialise() {
2881
FontMutexAllocate();
2884
void Platform_Finalise() {