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 #if GTK_MAJOR_VERSION >= 2
36 #include "Converter.h"
40 // Ignore unreferenced local functions in GTK+ headers
41 #pragma warning(disable: 4505)
45 using namespace Scintilla
;
48 enum encodingType
{ singleByte
, UTF8
, dbcs
};
59 static GMutex
*fontMutex
= NULL
;
61 static void InitializeGLIBThreads() {
62 if (!g_thread_supported()) {
68 static void FontMutexAllocate() {
71 InitializeGLIBThreads();
72 fontMutex
= g_mutex_new();
77 static void FontMutexFree() {
80 g_mutex_free(fontMutex
);
86 static void FontMutexLock() {
88 g_mutex_lock(fontMutex
);
92 static void FontMutexUnlock() {
95 g_mutex_unlock(fontMutex
);
100 // On GTK+ 1.x holds a GdkFont* but on GTK+ 2.x can hold a GdkFont* or a
101 // PangoFontDescription*.
109 PangoFontDescription
*pfd
;
112 FontHandle(GdkFont
*pfont_
) {
123 FontHandle(PangoFontDescription
*pfd_
, int characterSet_
) {
128 characterSet
= characterSet_
;
134 gdk_font_unref(pfont
);
138 pango_font_description_free(pfd
);
142 void ResetWidths(encodingType et_
) {
144 for (int i
=0; i
<=127; i
++) {
148 int CharWidth(unsigned char ch
, encodingType et_
) {
151 if ((ch
<= 127) && (et
== et_
)) {
157 void SetCharWidth(unsigned char ch
, int w
, encodingType et_
) {
169 // X has a 16 bit coordinate space, so stop drawing here to avoid wrapping
170 static const int maxCoordinate
= 32000;
172 static FontHandle
*PFont(Font
&f
) {
173 return reinterpret_cast<FontHandle
*>(f
.GetID());
176 static GtkWidget
*PWidget(WindowID wid
) {
177 return reinterpret_cast<GtkWidget
*>(wid
);
180 static GtkWidget
*PWidget(Window
&w
) {
181 return PWidget(w
.GetID());
184 Point
Point::FromLong(long lpoint
) {
186 Platform::LowShortFromLong(lpoint
),
187 Platform::HighShortFromLong(lpoint
));
192 allowRealization
= false;
193 allocatedPalette
= 0;
196 entries
= new ColourPair
[size
];
199 Palette::~Palette() {
205 void Palette::Release() {
207 delete [](reinterpret_cast<GdkColor
*>(allocatedPalette
));
208 allocatedPalette
= 0;
212 entries
= new ColourPair
[size
];
215 // This method either adds a colour to the list of wanted colours (want==true)
216 // or retrieves the allocated colour back to the ColourPair.
217 // This is one method to make it easier to keep the code for wanting and retrieving in sync.
218 void Palette::WantFind(ColourPair
&cp
, bool want
) {
220 for (int i
=0; i
< used
; i
++) {
221 if (entries
[i
].desired
== cp
.desired
)
226 int sizeNew
= size
* 2;
227 ColourPair
*entriesNew
= new ColourPair
[sizeNew
];
228 for (int j
=0; j
<size
; j
++) {
229 entriesNew
[j
] = entries
[j
];
232 entries
= entriesNew
;
236 entries
[used
].desired
= cp
.desired
;
237 entries
[used
].allocated
.Set(cp
.desired
.AsLong());
240 for (int i
=0; i
< used
; i
++) {
241 if (entries
[i
].desired
== cp
.desired
) {
242 cp
.allocated
= entries
[i
].allocated
;
246 cp
.allocated
.Set(cp
.desired
.AsLong());
250 void Palette::Allocate(Window
&w
) {
251 if (allocatedPalette
) {
252 gdk_colormap_free_colors(gtk_widget_get_colormap(PWidget(w
)),
253 reinterpret_cast<GdkColor
*>(allocatedPalette
),
255 delete [](reinterpret_cast<GdkColor
*>(allocatedPalette
));
256 allocatedPalette
= 0;
259 GdkColor
*paletteNew
= new GdkColor
[used
];
260 allocatedPalette
= paletteNew
;
261 gboolean
*successPalette
= new gboolean
[used
];
265 for (iPal
= 0; iPal
< used
; iPal
++) {
266 paletteNew
[iPal
].red
= entries
[iPal
].desired
.GetRed() * (65535 / 255);
267 paletteNew
[iPal
].green
= entries
[iPal
].desired
.GetGreen() * (65535 / 255);
268 paletteNew
[iPal
].blue
= entries
[iPal
].desired
.GetBlue() * (65535 / 255);
269 paletteNew
[iPal
].pixel
= entries
[iPal
].desired
.AsLong();
271 gdk_colormap_alloc_colors(gtk_widget_get_colormap(PWidget(w
)),
272 paletteNew
, allocatedLen
, FALSE
, TRUE
,
274 for (iPal
= 0; iPal
< used
; iPal
++) {
275 entries
[iPal
].allocated
.Set(paletteNew
[iPal
].pixel
);
278 delete []successPalette
;
281 static const char *CharacterSetName(int characterSet
) {
282 switch (characterSet
) {
283 case SC_CHARSET_ANSI
:
285 case SC_CHARSET_DEFAULT
:
287 case SC_CHARSET_BALTIC
:
289 case SC_CHARSET_CHINESEBIG5
:
291 case SC_CHARSET_EASTEUROPE
:
293 case SC_CHARSET_GB2312
:
294 return "gb2312.1980-*";
295 case SC_CHARSET_GREEK
:
297 case SC_CHARSET_HANGUL
:
298 return "ksc5601.1987-*";
303 case SC_CHARSET_RUSSIAN
:
305 case SC_CHARSET_CYRILLIC
:
307 case SC_CHARSET_SHIFTJIS
:
308 return "jisx0208.1983-*";
309 case SC_CHARSET_SYMBOL
:
311 case SC_CHARSET_TURKISH
:
313 case SC_CHARSET_JOHAB
:
315 case SC_CHARSET_HEBREW
:
317 case SC_CHARSET_ARABIC
:
319 case SC_CHARSET_VIETNAMESE
:
321 case SC_CHARSET_THAI
:
323 case SC_CHARSET_8859_15
:
330 static bool IsDBCSCharacterSet(int characterSet
) {
331 switch (characterSet
) {
332 case SC_CHARSET_GB2312
:
333 case SC_CHARSET_HANGUL
:
334 case SC_CHARSET_SHIFTJIS
:
335 case SC_CHARSET_CHINESEBIG5
:
342 static void GenerateFontSpecStrings(const char *fontName
, int characterSet
,
343 char *foundary
, int foundary_len
,
344 char *faceName
, int faceName_len
,
345 char *charset
, int charset_len
) {
346 // supported font strings include:
347 // foundary-fontface-isoxxx-x
351 if (strchr(fontName
, '-')) {
353 char *d1
= NULL
, *d2
= NULL
, *d3
= NULL
;
354 strncpy(tmp
, fontName
, sizeof(tmp
) - 1);
355 tmp
[sizeof(tmp
) - 1] = '\0';
356 d1
= strchr(tmp
, '-');
357 // we know the first dash exists
358 d2
= strchr(d1
+ 1, '-');
360 d3
= strchr(d2
+ 1, '-');
362 // foundary-fontface-isoxxx-x
366 strncpy(faceName
, tmp
, foundary_len
- 1);
367 strncpy(charset
, d2
+ 1, charset_len
- 1);
371 strcpy(foundary
, "-*-");
372 strncpy(faceName
, tmp
, faceName_len
- 1);
373 strncpy(charset
, d1
+ 1, charset_len
- 1);
378 strncpy(faceName
, tmp
, faceName_len
- 1);
379 strncpy(charset
, CharacterSetName(characterSet
), charset_len
- 1);
382 strncpy(foundary
, "-*-", foundary_len
);
383 strncpy(faceName
, fontName
, faceName_len
- 1);
384 strncpy(charset
, CharacterSetName(characterSet
), charset_len
- 1);
388 static void SetLogFont(LOGFONT
&lf
, const char *faceName
, int characterSet
, int size
, bool bold
, bool italic
) {
389 memset(&lf
, 0, sizeof(lf
));
393 lf
.characterSet
= characterSet
;
394 strncpy(lf
.faceName
, faceName
, sizeof(lf
.faceName
) - 1);
398 * Create a hash from the parameters for a font to allow easy checking for identity.
399 * If one font is the same as another, its hash will be the same, but if the hash is the
400 * same then they may still be different.
402 static int HashFont(const char *faceName
, int characterSet
, int size
, bool bold
, bool italic
) {
405 (characterSet
<< 10) ^
406 (bold
? 0x10000000 : 0) ^
407 (italic
? 0x20000000 : 0) ^
411 class FontCached
: Font
{
416 FontCached(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
);
418 bool SameAs(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
);
419 virtual void Release();
420 static FontID
CreateNewFont(const char *fontName
, int characterSet
,
421 int size
, bool bold
, bool italic
);
422 static FontCached
*first
;
424 static FontID
FindOrCreate(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
);
425 static void ReleaseId(FontID fid_
);
428 FontCached
*FontCached::first
= 0;
430 FontCached::FontCached(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
) :
431 next(0), usage(0), hash(0) {
432 ::SetLogFont(lf
, faceName_
, characterSet_
, size_
, bold_
, italic_
);
433 hash
= HashFont(faceName_
, characterSet_
, size_
, bold_
, italic_
);
434 fid
= CreateNewFont(faceName_
, characterSet_
, size_
, bold_
, italic_
);
438 bool FontCached::SameAs(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
) {
442 lf
.italic
== italic_
&&
443 lf
.characterSet
== characterSet_
&&
444 0 == strcmp(lf
.faceName
, faceName_
);
447 void FontCached::Release() {
453 FontID
FontCached::FindOrCreate(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
) {
456 int hashFind
= HashFont(faceName_
, characterSet_
, size_
, bold_
, italic_
);
457 for (FontCached
*cur
= first
; cur
; cur
= cur
->next
) {
458 if ((cur
->hash
== hashFind
) &&
459 cur
->SameAs(faceName_
, characterSet_
, size_
, bold_
, italic_
)) {
465 FontCached
*fc
= new FontCached(faceName_
, characterSet_
, size_
, bold_
, italic_
);
476 void FontCached::ReleaseId(FontID fid_
) {
478 FontCached
**pcur
= &first
;
479 for (FontCached
*cur
= first
; cur
; cur
= cur
->next
) {
480 if (cur
->fid
== fid_
) {
482 if (cur
->usage
== 0) {
495 static GdkFont
*LoadFontOrSet(const char *fontspec
, int characterSet
) {
496 if (IsDBCSCharacterSet(characterSet
)) {
497 return gdk_fontset_load(fontspec
);
499 return gdk_font_load(fontspec
);
503 FontID
FontCached::CreateNewFont(const char *fontName
, int characterSet
,
504 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
);
530 // If name of the font begins with a '-', assume, that it is
532 if (fontName
[0] == '-') {
533 if (strchr(fontName
, ',') || IsDBCSCharacterSet(characterSet
)) {
534 newid
= gdk_fontset_load(fontName
);
536 newid
= gdk_font_load(fontName
);
539 // Font not available so substitute a reasonable code font
540 // iso8859 appears to only allow western characters.
541 newid
= LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
544 return new FontHandle(newid
);
547 // it's not a full fontspec, build one.
549 // This supports creating a FONT_SET
550 // in a method that allows us to also set size, slant and
551 // weight for the fontset. The expected input is multiple
552 // partial fontspecs seperated by comma
553 // eg. adobe-courier-iso10646-1,*-courier-iso10646-1,*-*-*-*
554 if (strchr(fontName
, ',')) {
555 // build a fontspec and use gdk_fontset_load
556 int remaining
= sizeof(fontset
);
557 char fontNameCopy
[1024];
558 strncpy(fontNameCopy
, fontName
, sizeof(fontNameCopy
) - 1);
559 char *fn
= fontNameCopy
;
560 char *fp
= strchr(fn
, ',');
562 const char *spec
= "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
563 if (fontset
[0] != '\0') {
564 // if this is not the first font in the list,
565 // append a comma seperator
566 spec
= ",%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
570 *fp
= '\0'; // nullify the comma
571 GenerateFontSpecStrings(fn
, characterSet
,
572 foundary
, sizeof(foundary
),
573 faceName
, sizeof(faceName
),
574 charset
, sizeof(charset
));
577 sizeof(fontspec
) - 1,
580 bold
? "-bold" : "-medium",
581 italic
? "-i" : "-r",
585 // if this is the first font in the list, and
586 // we are doing italic, add an oblique font
588 if (italic
&& fontset
[0] == '\0') {
589 strncat(fontset
, fontspec
, remaining
- 1);
590 remaining
-= strlen(fontset
);
593 sizeof(fontspec
) - 1,
594 ",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s",
596 bold
? "-bold" : "-medium",
601 strncat(fontset
, fontspec
, remaining
- 1);
602 remaining
-= strlen(fontset
);
608 fp
= strchr(fn
, ',');
611 newid
= gdk_fontset_load(fontset
);
613 return new FontHandle(newid
);
615 // if fontset load failed, fall through, we'll use
616 // the last font entry and continue to try and
617 // get something that matches
620 // single fontspec support
622 GenerateFontSpecStrings(fontName
, characterSet
,
623 foundary
, sizeof(foundary
),
624 faceName
, sizeof(faceName
),
625 charset
, sizeof(charset
));
628 sizeof(fontspec
) - 1,
629 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
631 bold
? "-bold" : "-medium",
632 italic
? "-i" : "-r",
635 newid
= LoadFontOrSet(fontspec
, characterSet
);
637 // some fonts have oblique, not italic
639 sizeof(fontspec
) - 1,
640 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
642 bold
? "-bold" : "-medium",
643 italic
? "-o" : "-r",
646 newid
= LoadFontOrSet(fontspec
, characterSet
);
650 sizeof(fontspec
) - 1,
651 "-*-*-*-*-*-*-*-%0d-*-*-*-*-%s",
654 newid
= gdk_font_load(fontspec
);
657 // Font not available so substitute a reasonable code font
658 // iso8859 appears to only allow western characters.
659 newid
= LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
662 return new FontHandle(newid
);
665 Font::Font() : fid(0) {}
669 void Font::Create(const char *faceName
, int characterSet
, int size
,
670 bool bold
, bool italic
, int) {
672 fid
= FontCached::FindOrCreate(faceName
, characterSet
, size
, bold
, italic
);
675 void Font::Release() {
677 FontCached::ReleaseId(fid
);
683 class Scintilla::SurfaceImpl
: public Surface
685 class SurfaceImpl
: public Surface
689 GdkDrawable
*drawable
;
697 PangoContext
*pcontext
;
701 void SetConverter(int characterSet_
);
705 virtual ~SurfaceImpl();
707 void Init(WindowID wid
);
708 void Init(SurfaceID sid
, WindowID wid
);
709 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
713 void PenColour(ColourAllocated fore
);
715 int DeviceHeightFont(int points
);
716 void MoveTo(int x_
, int y_
);
717 void LineTo(int x_
, int y_
);
718 void Polygon(Point
*pts
, int npts
, ColourAllocated fore
, ColourAllocated back
);
719 void RectangleDraw(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
);
720 void FillRectangle(PRectangle rc
, ColourAllocated back
);
721 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
722 void RoundedRectangle(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
);
723 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourAllocated fill
, int alphaFill
,
724 ColourAllocated outline
, int alphaOutline
, int flags
);
725 void Ellipse(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
);
726 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
728 void DrawTextBase(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
);
729 void DrawTextNoClip(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
, ColourAllocated back
);
730 void DrawTextClipped(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
, ColourAllocated back
);
731 void DrawTextTransparent(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
);
732 void MeasureWidths(Font
&font_
, const char *s
, int len
, int *positions
);
733 int WidthText(Font
&font_
, const char *s
, int len
);
734 int WidthChar(Font
&font_
, char ch
);
735 int Ascent(Font
&font_
);
736 int Descent(Font
&font_
);
737 int InternalLeading(Font
&font_
);
738 int ExternalLeading(Font
&font_
);
739 int Height(Font
&font_
);
740 int AverageCharWidth(Font
&font_
);
742 int SetPalette(Palette
*pal
, bool inBackGround
);
743 void SetClip(PRectangle rc
);
744 void FlushCachedState();
746 void SetUnicodeMode(bool unicodeMode_
);
747 void SetDBCSMode(int codePage
);
750 const char *CharacterSetID(int characterSet
) {
751 switch (characterSet
) {
752 case SC_CHARSET_ANSI
:
754 case SC_CHARSET_DEFAULT
:
756 case SC_CHARSET_BALTIC
:
757 return "ISO-8859-13";
758 case SC_CHARSET_CHINESEBIG5
:
760 case SC_CHARSET_EASTEUROPE
:
762 case SC_CHARSET_GB2312
:
764 case SC_CHARSET_GREEK
:
766 case SC_CHARSET_HANGUL
:
772 case SC_CHARSET_RUSSIAN
:
774 case SC_CHARSET_CYRILLIC
:
776 case SC_CHARSET_SHIFTJIS
:
778 case SC_CHARSET_SYMBOL
:
780 case SC_CHARSET_TURKISH
:
782 case SC_CHARSET_JOHAB
:
784 case SC_CHARSET_HEBREW
:
786 case SC_CHARSET_ARABIC
:
788 case SC_CHARSET_VIETNAMESE
:
790 case SC_CHARSET_THAI
:
791 return "ISO-8859-11";
792 case SC_CHARSET_8859_15
:
793 return "ISO-8859-15";
801 void SurfaceImpl::SetConverter(int characterSet_
) {
802 if (characterSet
!= characterSet_
) {
803 characterSet
= characterSet_
;
804 conv
.Open("UTF-8", CharacterSetID(characterSet
), false);
809 SurfaceImpl::SurfaceImpl() : et(singleByte
), drawable(0), gc(0), ppixmap(0),
810 x(0), y(0), inited(false), createdGC(false)
812 , pcontext(0), layout(0), characterSet(-1)
817 SurfaceImpl::~SurfaceImpl() {
821 void SurfaceImpl::Release() {
830 gdk_pixmap_unref(ppixmap
);
834 g_object_unref(layout
);
837 g_object_unref(pcontext
);
848 bool SurfaceImpl::Initialised() {
852 // The WindowID argument is only used for Pango builds
859 void SurfaceImpl::Init(WindowID WID_NAME
) {
862 PLATFORM_ASSERT(wid
);
863 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
864 PLATFORM_ASSERT(pcontext
);
865 layout
= pango_layout_new(pcontext
);
866 PLATFORM_ASSERT(layout
);
871 void SurfaceImpl::Init(SurfaceID sid
, WindowID WID_NAME
) {
872 PLATFORM_ASSERT(sid
);
873 GdkDrawable
*drawable_
= reinterpret_cast<GdkDrawable
*>(sid
);
876 PLATFORM_ASSERT(wid
);
877 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
878 layout
= pango_layout_new(pcontext
);
880 drawable
= drawable_
;
881 gc
= gdk_gc_new(drawable_
);
882 // Ask for lines that do not paint the last pixel so is like Win32
883 gdk_gc_set_line_attributes(gc
, 0, GDK_LINE_SOLID
, GDK_CAP_NOT_LAST
, GDK_JOIN_MITER
);
888 void SurfaceImpl::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID WID_NAME
) {
889 PLATFORM_ASSERT(surface_
);
891 SurfaceImpl
*surfImpl
= static_cast<SurfaceImpl
*>(surface_
);
892 PLATFORM_ASSERT(surfImpl
->drawable
);
894 PLATFORM_ASSERT(wid
);
895 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
896 PLATFORM_ASSERT(pcontext
);
897 layout
= pango_layout_new(pcontext
);
898 PLATFORM_ASSERT(layout
);
900 if (height
> 0 && width
> 0)
901 ppixmap
= gdk_pixmap_new(surfImpl
->drawable
, width
, height
, -1);
903 gc
= gdk_gc_new(surfImpl
->drawable
);
904 // Ask for lines that do not paint the last pixel so is like Win32
905 gdk_gc_set_line_attributes(gc
, 0, GDK_LINE_SOLID
, GDK_CAP_NOT_LAST
, GDK_JOIN_MITER
);
910 void SurfaceImpl::PenColour(ColourAllocated fore
) {
913 co
.pixel
= fore
.AsLong();
914 gdk_gc_set_foreground(gc
, &co
);
918 int SurfaceImpl::LogPixelsY() {
922 int SurfaceImpl::DeviceHeightFont(int points
) {
923 int logPix
= LogPixelsY();
924 return (points
* logPix
+ logPix
/ 2) / 72;
927 void SurfaceImpl::MoveTo(int x_
, int y_
) {
932 void SurfaceImpl::LineTo(int x_
, int y_
) {
933 if (drawable
&& gc
) {
934 gdk_draw_line(drawable
, gc
,
942 void SurfaceImpl::Polygon(Point
*pts
, int npts
, ColourAllocated fore
,
943 ColourAllocated back
) {
945 if (npts
< static_cast<int>((sizeof(gpts
) / sizeof(gpts
[0])))) {
946 for (int i
= 0;i
< npts
;i
++) {
947 gpts
[i
].x
= pts
[i
].x
;
948 gpts
[i
].y
= pts
[i
].y
;
951 gdk_draw_polygon(drawable
, gc
, 1, gpts
, npts
);
953 gdk_draw_polygon(drawable
, gc
, 0, gpts
, npts
);
957 void SurfaceImpl::RectangleDraw(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
) {
958 if (gc
&& drawable
) {
960 gdk_draw_rectangle(drawable
, gc
, 1,
961 rc
.left
+ 1, rc
.top
+ 1,
962 rc
.right
- rc
.left
- 2, rc
.bottom
- rc
.top
- 2);
965 // The subtraction of 1 off the width and height here shouldn't be needed but
966 // otherwise a different rectangle is drawn than would be done if the fill parameter == 1
967 gdk_draw_rectangle(drawable
, gc
, 0,
969 rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1);
973 void SurfaceImpl::FillRectangle(PRectangle rc
, ColourAllocated back
) {
975 if (drawable
&& (rc
.left
< maxCoordinate
)) { // Protect against out of range
976 gdk_draw_rectangle(drawable
, gc
, 1,
978 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
982 void SurfaceImpl::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
983 if (static_cast<SurfaceImpl
&>(surfacePattern
).drawable
) {
984 // Tile pattern over rectangle
985 // Currently assumes 8x8 pattern
988 for (int xTile
= rc
.left
; xTile
< rc
.right
; xTile
+= widthPat
) {
989 int widthx
= (xTile
+ widthPat
> rc
.right
) ? rc
.right
- xTile
: widthPat
;
990 for (int yTile
= rc
.top
; yTile
< rc
.bottom
; yTile
+= heightPat
) {
991 int heighty
= (yTile
+ heightPat
> rc
.bottom
) ? rc
.bottom
- yTile
: heightPat
;
992 gdk_draw_pixmap(drawable
,
994 static_cast<SurfaceImpl
&>(surfacePattern
).drawable
,
1001 // Something is wrong so try to show anyway
1002 // Shows up black because colour not allocated
1003 FillRectangle(rc
, ColourAllocated(0));
1007 void SurfaceImpl::RoundedRectangle(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
) {
1008 if (((rc
.right
- rc
.left
) > 4) && ((rc
.bottom
- rc
.top
) > 4)) {
1009 // Approximate a round rect with some cut off corners
1011 Point(rc
.left
+ 2, rc
.top
),
1012 Point(rc
.right
- 2, rc
.top
),
1013 Point(rc
.right
, rc
.top
+ 2),
1014 Point(rc
.right
, rc
.bottom
- 2),
1015 Point(rc
.right
- 2, rc
.bottom
),
1016 Point(rc
.left
+ 2, rc
.bottom
),
1017 Point(rc
.left
, rc
.bottom
- 2),
1018 Point(rc
.left
, rc
.top
+ 2),
1020 Polygon(pts
, sizeof(pts
) / sizeof(pts
[0]), fore
, back
);
1022 RectangleDraw(rc
, fore
, back
);
1026 #if GTK_MAJOR_VERSION >= 2
1028 // Plot a point into a guint32 buffer symetrically to all 4 qudrants
1029 static void AllFour(guint32
*pixels
, int stride
, int width
, int height
, int x
, int y
, guint32 val
) {
1030 pixels
[y
*stride
+x
] = val
;
1031 pixels
[y
*stride
+width
-1-x
] = val
;
1032 pixels
[(height
-1-y
)*stride
+x
] = val
;
1033 pixels
[(height
-1-y
)*stride
+width
-1-x
] = val
;
1036 static unsigned int GetRValue(unsigned int co
) {
1037 return (co
>> 16) & 0xff;
1040 static unsigned int GetGValue(unsigned int co
) {
1041 return (co
>> 8) & 0xff;
1044 static unsigned int GetBValue(unsigned int co
) {
1050 #if GTK_MAJOR_VERSION < 2
1051 void SurfaceImpl::AlphaRectangle(PRectangle rc
, int , ColourAllocated
, int , ColourAllocated outline
, int , int ) {
1052 if (gc
&& drawable
) {
1053 // Can't use GdkPixbuf on GTK+ 1.x, so draw an outline rather than use alpha.
1055 gdk_draw_rectangle(drawable
, gc
, 0,
1057 rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1);
1062 static guint32
u32FromRGBA(guint8 r
, guint8 g
, guint8 b
, guint8 a
) {
1067 converter
.pixVal
[0] = r
;
1068 converter
.pixVal
[1] = g
;
1069 converter
.pixVal
[2] = b
;
1070 converter
.pixVal
[3] = a
;
1071 return converter
.val
;
1074 void SurfaceImpl::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourAllocated fill
, int alphaFill
,
1075 ColourAllocated outline
, int alphaOutline
, int flags
) {
1076 if (gc
&& drawable
&& rc
.Width() > 0) {
1077 int width
= rc
.Width();
1078 int height
= rc
.Height();
1079 // Ensure not distorted too much by corners when small
1080 cornerSize
= Platform::Minimum(cornerSize
, (Platform::Minimum(width
, height
) / 2) - 2);
1081 // Make a 32 bit deep pixbuf with alpha
1082 GdkPixbuf
*pixalpha
= gdk_pixbuf_new(GDK_COLORSPACE_RGB
, TRUE
, 8, width
, height
);
1084 guint32 valEmpty
= u32FromRGBA(0,0,0,0);
1085 guint32 valFill
= u32FromRGBA(GetRValue(fill
.AsLong()),
1086 GetGValue(fill
.AsLong()), GetBValue(fill
.AsLong()), alphaFill
);
1087 guint32 valOutline
= u32FromRGBA(GetRValue(outline
.AsLong()),
1088 GetGValue(outline
.AsLong()), GetBValue(outline
.AsLong()), alphaOutline
);
1089 guint32
*pixels
= reinterpret_cast<guint32
*>(gdk_pixbuf_get_pixels(pixalpha
));
1090 int stride
= gdk_pixbuf_get_rowstride(pixalpha
) / 4;
1091 for (int yr
=0; yr
<height
; yr
++) {
1092 for (int xr
=0; xr
<width
; xr
++) {
1093 if ((xr
==0) || (xr
==width
-1) || (yr
== 0) || (yr
== height
-1)) {
1094 pixels
[yr
*stride
+xr
] = valOutline
;
1096 pixels
[yr
*stride
+xr
] = valFill
;
1100 for (int c
=0;c
<cornerSize
; c
++) {
1101 for (int xr
=0;xr
<c
+1; xr
++) {
1102 AllFour(pixels
, stride
, width
, height
, xr
, c
-xr
, valEmpty
);
1105 for (int xr
=1;xr
<cornerSize
; xr
++) {
1106 AllFour(pixels
, stride
, width
, height
, xr
, cornerSize
-xr
, valOutline
);
1110 gdk_draw_pixbuf(drawable
, gc
, pixalpha
,
1111 0,0, rc
.left
,rc
.top
, width
,height
, GDK_RGB_DITHER_NORMAL
, 0, 0);
1113 g_object_unref(pixalpha
);
1119 void SurfaceImpl::Ellipse(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
) {
1121 gdk_draw_arc(drawable
, gc
, 1,
1122 rc
.left
+ 1, rc
.top
+ 1,
1123 rc
.right
- rc
.left
- 2, rc
.bottom
- rc
.top
- 2,
1126 // The subtraction of 1 here is similar to the case for RectangleDraw
1128 gdk_draw_arc(drawable
, gc
, 0,
1130 rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1,
1134 void SurfaceImpl::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
1135 if (static_cast<SurfaceImpl
&>(surfaceSource
).drawable
) {
1136 gdk_draw_pixmap(drawable
,
1138 static_cast<SurfaceImpl
&>(surfaceSource
).drawable
,
1141 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
1145 static size_t UTF8Len(char ch
) {
1146 unsigned char uch
= static_cast<unsigned char>(ch
);
1149 else if (uch
< (0x80 + 0x40 + 0x20))
1155 char *UTF8FromLatin1(const char *s
, int &len
) {
1156 char *utfForm
= new char[len
*2+1];
1158 for (int i
=0;i
<len
;i
++) {
1159 unsigned int uch
= static_cast<unsigned char>(s
[i
]);
1161 utfForm
[lenU
++] = uch
;
1163 utfForm
[lenU
++] = static_cast<char>(0xC0 | (uch
>> 6));
1164 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
1167 utfForm
[lenU
] = '\0';
1173 static char *UTF8FromIconv(const Converter
&conv
, const char *s
, int &len
) {
1175 char *utfForm
= new char[len
*3+1];
1176 char *pin
= const_cast<char *>(s
);
1177 size_t inLeft
= len
;
1178 char *pout
= utfForm
;
1179 size_t outLeft
= len
*3+1;
1180 size_t conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
1181 if (conversions
!= ((size_t)(-1))) {
1183 len
= pout
- utfForm
;
1191 // Work out how many bytes are in a character by trying to convert using iconv,
1192 // returning the first length that succeeds.
1193 static size_t MultiByteLenFromIconv(const Converter
&conv
, const char *s
, size_t len
) {
1194 for (size_t lenMB
=1; (lenMB
<4) && (lenMB
<= len
); lenMB
++) {
1196 char *pin
= const_cast<char *>(s
);
1197 size_t inLeft
= lenMB
;
1198 char *pout
= wcForm
;
1200 size_t conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
1201 if (conversions
!= ((size_t)(-1))) {
1208 static char *UTF8FromGdkWChar(GdkWChar
*wctext
, int wclen
) {
1209 char *utfForm
= new char[wclen
*3+1]; // Maximum of 3 UTF-8 bytes per character
1211 for (int i
= 0; i
< wclen
&& wctext
[i
]; i
++) {
1212 unsigned int uch
= wctext
[i
];
1214 utfForm
[lenU
++] = static_cast<char>(uch
);
1215 } else if (uch
< 0x800) {
1216 utfForm
[lenU
++] = static_cast<char>(0xC0 | (uch
>> 6));
1217 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
1219 utfForm
[lenU
++] = static_cast<char>(0xE0 | (uch
>> 12));
1220 utfForm
[lenU
++] = static_cast<char>(0x80 | ((uch
>> 6) & 0x3f));
1221 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
1224 utfForm
[lenU
] = '\0';
1228 static char *UTF8FromDBCS(const char *s
, int &len
) {
1229 GdkWChar
*wctext
= new GdkWChar
[len
+ 1];
1230 GdkWChar
*wcp
= wctext
;
1231 int wclen
= gdk_mbstowcs(wcp
, s
, len
);
1233 // In the annoying case when non-locale chars in the line.
1234 // e.g. latin1 chars in Japanese locale.
1239 char *utfForm
= UTF8FromGdkWChar(wctext
, wclen
);
1241 len
= strlen(utfForm
);
1245 static size_t UTF8CharLength(const char *s
) {
1246 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1247 unsigned char ch
= *us
;
1250 } else if (ch
< 0x80 + 0x40 + 0x20) {
1259 // On GTK+, wchar_t is 4 bytes
1261 const int maxLengthTextRun
= 10000;
1263 void SurfaceImpl::DrawTextBase(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1264 ColourAllocated fore
) {
1266 if (gc
&& drawable
) {
1267 int xText
= rc
.left
;
1269 if (PFont(font_
)->pfd
) {
1271 bool useGFree
= false;
1273 pango_layout_set_text(layout
, s
, len
);
1276 SetConverter(PFont(font_
)->characterSet
);
1277 utfForm
= UTF8FromIconv(conv
, s
, len
);
1279 if (!utfForm
) { // iconv failed so try DBCS if DBCS mode
1282 utfForm
= UTF8FromDBCS(s
, len
);
1285 if (!utfForm
) { // iconv and DBCS failed so treat as Latin1
1286 utfForm
= UTF8FromLatin1(s
, len
);
1288 pango_layout_set_text(layout
, utfForm
, len
);
1290 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
1291 #ifdef PANGO_VERSION
1292 PangoLayoutLine
*pll
= pango_layout_get_line_readonly(layout
,0);
1294 PangoLayoutLine
*pll
= pango_layout_get_line(layout
,0);
1296 gdk_draw_layout_line(drawable
, gc
, xText
, ybase
, pll
);
1305 // Draw text as a series of segments to avoid limitations in X servers
1306 const int segmentLength
= 1000;
1307 bool draw8bit
= true;
1308 if (et
!= singleByte
) {
1309 GdkWChar wctext
[maxLengthTextRun
];
1310 if (len
>= maxLengthTextRun
)
1311 len
= maxLengthTextRun
-1;
1314 wclen
= UTF16FromUTF8(s
, len
,
1315 static_cast<wchar_t *>(static_cast<void *>(wctext
)), maxLengthTextRun
- 1);
1316 } else { // dbcs, so convert using current locale
1317 char sMeasure
[maxLengthTextRun
];
1318 memcpy(sMeasure
, s
, len
);
1319 sMeasure
[len
] = '\0';
1320 wclen
= gdk_mbstowcs(
1321 wctext
, sMeasure
, maxLengthTextRun
- 1);
1325 wctext
[wclen
] = L
'\0';
1326 GdkWChar
*wcp
= wctext
;
1327 while ((wclen
> 0) && (xText
< maxCoordinate
)) {
1328 int lenDraw
= Platform::Minimum(wclen
, segmentLength
);
1329 gdk_draw_text_wc(drawable
, PFont(font_
)->pfont
, gc
,
1330 xText
, ybase
, wcp
, lenDraw
);
1332 if (wclen
> 0) { // Avoid next calculation if possible as may be expensive
1333 xText
+= gdk_text_width_wc(PFont(font_
)->pfont
,
1341 while ((len
> 0) && (xText
< maxCoordinate
)) {
1342 int lenDraw
= Platform::Minimum(len
, segmentLength
);
1343 gdk_draw_text(drawable
, PFont(font_
)->pfont
, gc
,
1344 xText
, ybase
, s
, lenDraw
);
1346 if (len
> 0) { // Avoid next calculation if possible as may be expensive
1347 xText
+= gdk_text_width(PFont(font_
)->pfont
, s
, lenDraw
);
1355 void SurfaceImpl::DrawTextNoClip(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1356 ColourAllocated fore
, ColourAllocated back
) {
1357 FillRectangle(rc
, back
);
1358 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
1361 // On GTK+, exactly same as DrawTextNoClip
1362 void SurfaceImpl::DrawTextClipped(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1363 ColourAllocated fore
, ColourAllocated back
) {
1364 FillRectangle(rc
, back
);
1365 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
1368 void SurfaceImpl::DrawTextTransparent(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1369 ColourAllocated fore
) {
1370 // Avoid drawing spaces in transparent mode
1371 for (int i
=0;i
<len
;i
++) {
1373 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
1381 class ClusterIterator
{
1382 PangoLayoutIter
*iter
;
1391 ClusterIterator(PangoLayout
*layout
, int len
) : lenPositions(len
), finished(false),
1392 positionStart(0), position(0), distance(0) {
1393 iter
= pango_layout_get_iter(layout
);
1394 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
1396 ~ClusterIterator() {
1397 pango_layout_iter_free(iter
);
1401 positionStart
= position
;
1402 if (pango_layout_iter_next_cluster(iter
)) {
1403 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
1404 position
= PANGO_PIXELS(pos
.x
);
1405 curIndex
= pango_layout_iter_get_index(iter
);
1408 position
= PANGO_PIXELS(pos
.x
+ pos
.width
);
1409 curIndex
= lenPositions
;
1411 distance
= position
- positionStart
;
1417 void SurfaceImpl::MeasureWidths(Font
&font_
, const char *s
, int len
, int *positions
) {
1418 if (font_
.GetID()) {
1421 const int lenPositions
= len
;
1422 if (PFont(font_
)->pfd
) {
1424 int width
= PFont(font_
)->CharWidth(*s
, et
);
1426 positions
[0] = width
;
1430 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
1432 // Simple and direct as UTF-8 is native Pango encoding
1434 pango_layout_set_text(layout
, s
, len
);
1435 ClusterIterator
iti(layout
, lenPositions
);
1436 while (!iti
.finished
) {
1438 int places
= iti
.curIndex
- i
;
1439 while (i
< iti
.curIndex
) {
1440 // Evenly distribute space among bytes of this cluster.
1441 // Would be better to find number of characters and then
1442 // divide evenly between characters with each byte of a character
1443 // being at the same position.
1444 positions
[i
] = iti
.position
- (iti
.curIndex
- 1 - i
) * iti
.distance
/ places
;
1448 PLATFORM_ASSERT(i
== lenPositions
);
1450 int positionsCalculated
= 0;
1452 SetConverter(PFont(font_
)->characterSet
);
1453 char *utfForm
= UTF8FromIconv(conv
, s
, len
);
1455 // Convert to UTF-8 so can ask Pango for widths, then
1456 // Loop through UTF-8 and DBCS forms, taking account of different
1457 // character byte lengths.
1458 Converter
convMeasure("UCS-2", CharacterSetID(characterSet
), false);
1459 pango_layout_set_text(layout
, utfForm
, strlen(utfForm
));
1461 int clusterStart
= 0;
1462 ClusterIterator
iti(layout
, strlen(utfForm
));
1463 while (!iti
.finished
) {
1465 int clusterEnd
= iti
.curIndex
;
1466 int places
= g_utf8_strlen(utfForm
+ clusterStart
, clusterEnd
- clusterStart
);
1468 while (clusterStart
< clusterEnd
) {
1469 size_t lenChar
= MultiByteLenFromIconv(convMeasure
, s
+i
, len
-i
);
1471 positions
[i
++] = iti
.position
- (places
- place
) * iti
.distance
/ places
;
1472 positionsCalculated
++;
1474 clusterStart
+= UTF8CharLength(utfForm
+clusterStart
);
1479 PLATFORM_ASSERT(i
== lenPositions
);
1482 if (positionsCalculated
< 1 ) {
1483 // Either Latin1 or DBCS conversion failed so treat as Latin1.
1484 bool useGFree
= false;
1485 SetConverter(PFont(font_
)->characterSet
);
1486 char *utfForm
= UTF8FromIconv(conv
, s
, len
);
1488 utfForm
= UTF8FromLatin1(s
, len
);
1490 pango_layout_set_text(layout
, utfForm
, len
);
1492 int clusterStart
= 0;
1493 // Each Latin1 input character may take 1 or 2 bytes in UTF-8
1494 // and groups of up to 3 may be represented as ligatures.
1495 ClusterIterator
iti(layout
, strlen(utfForm
));
1496 while (!iti
.finished
) {
1498 int clusterEnd
= iti
.curIndex
;
1499 int ligatureLength
= g_utf8_strlen(utfForm
+ clusterStart
, clusterEnd
- clusterStart
);
1500 PLATFORM_ASSERT(ligatureLength
> 0 && ligatureLength
<= 3);
1501 for (int charInLig
=0; charInLig
<ligatureLength
; charInLig
++) {
1502 positions
[i
++] = iti
.position
- (ligatureLength
- 1 - charInLig
) * iti
.distance
/ ligatureLength
;
1504 clusterStart
= clusterEnd
;
1511 PLATFORM_ASSERT(i
== lenPositions
);
1515 PFont(font_
)->SetCharWidth(*s
, positions
[0], et
);
1520 GdkFont
*gf
= PFont(font_
)->pfont
;
1521 bool measure8bit
= true;
1522 if (et
!= singleByte
) {
1523 GdkWChar wctext
[maxLengthTextRun
];
1524 if (len
>= maxLengthTextRun
)
1525 len
= maxLengthTextRun
-1;
1528 wclen
= UTF16FromUTF8(s
, len
,
1529 static_cast<wchar_t *>(static_cast<void *>(wctext
)), maxLengthTextRun
- 1);
1530 } else { // dbcsMode, so convert using current locale
1531 char sDraw
[maxLengthTextRun
];
1532 memcpy(sDraw
, s
, len
);
1534 wclen
= gdk_mbstowcs(
1535 wctext
, sDraw
, maxLengthTextRun
- 1);
1538 measure8bit
= false;
1539 wctext
[wclen
] = L
'\0';
1540 // Map widths back to utf-8 or DBCS input string
1542 for (int iU
= 0; iU
< wclen
; iU
++) {
1543 int width
= gdk_char_width_wc(gf
, wctext
[iU
]);
1544 totalWidth
+= width
;
1547 lenChar
= UTF8Len(s
[i
]);
1549 lenChar
= mblen(s
+i
, MB_CUR_MAX
);
1554 positions
[i
++] = totalWidth
;
1557 while (i
< len
) { // In case of problems with lengths
1558 positions
[i
++] = totalWidth
;
1563 // Either Latin1 or conversion failed so treat as Latin1.
1564 for (int i
= 0; i
< len
; i
++) {
1565 int width
= gdk_char_width(gf
, s
[i
]);
1566 totalWidth
+= width
;
1567 positions
[i
] = totalWidth
;
1571 // No font so return an ascending range of values
1572 for (int i
= 0; i
< len
; i
++) {
1573 positions
[i
] = i
+ 1;
1578 int SurfaceImpl::WidthText(Font
&font_
, const char *s
, int len
) {
1579 if (font_
.GetID()) {
1581 if (PFont(font_
)->pfd
) {
1583 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
1585 bool useGFree
= false;
1587 pango_layout_set_text(layout
, s
, len
);
1591 utfForm
= UTF8FromDBCS(s
, len
);
1593 if (!utfForm
) { // DBCS failed so treat as iconv
1594 SetConverter(PFont(font_
)->characterSet
);
1595 utfForm
= UTF8FromIconv(conv
, s
, len
);
1597 if (!utfForm
) { // g_locale_to_utf8 failed so treat as Latin1
1598 utfForm
= UTF8FromLatin1(s
, len
);
1600 pango_layout_set_text(layout
, utfForm
, len
);
1602 #ifdef PANGO_VERSION
1603 PangoLayoutLine
*pangoLine
= pango_layout_get_line_readonly(layout
,0);
1605 PangoLayoutLine
*pangoLine
= pango_layout_get_line(layout
,0);
1607 pango_layout_line_get_extents(pangoLine
, NULL
, &pos
);
1613 return PANGO_PIXELS(pos
.width
);
1617 GdkWChar wctext
[maxLengthTextRun
];
1618 size_t wclen
= UTF16FromUTF8(s
, len
, static_cast<wchar_t *>(static_cast<void *>(wctext
)),
1619 sizeof(wctext
) / sizeof(GdkWChar
) - 1);
1620 wctext
[wclen
] = L
'\0';
1621 return gdk_text_width_wc(PFont(font_
)->pfont
, wctext
, wclen
);
1623 return gdk_text_width(PFont(font_
)->pfont
, s
, len
);
1630 int SurfaceImpl::WidthChar(Font
&font_
, char ch
) {
1631 if (font_
.GetID()) {
1633 if (PFont(font_
)->pfd
) {
1634 return WidthText(font_
, &ch
, 1);
1637 return gdk_char_width(PFont(font_
)->pfont
, ch
);
1643 // Three possible strategies for determining ascent and descent of font:
1644 // 1) Call gdk_string_extents with string containing all letters, numbers and punctuation.
1645 // 2) Use the ascent and descent fields of GdkFont.
1646 // 3) Call gdk_string_extents with string as 1 but also including accented capitals.
1647 // Smallest values given by 1 and largest by 3 with 2 in between.
1648 // Techniques 1 and 2 sometimes chop off extreme portions of ascenders and
1649 // descenders but are mostly OK except for accented characters like Ã… which are
1650 // rarely used in code.
1652 // This string contains a good range of characters to test for size.
1653 //const char largeSizeString[] = "ÂÃÅÄ `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1654 // "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1656 const char sizeString
[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1657 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1660 int SurfaceImpl::Ascent(Font
&font_
) {
1661 if (!(font_
.GetID()))
1665 int ascent
= PFont(font_
)->ascent
;
1667 if ((ascent
== 0) && (PFont(font_
)->pfd
)) {
1668 PangoFontMetrics
*metrics
= pango_context_get_metrics(pcontext
,
1669 PFont(font_
)->pfd
, pango_context_get_language(pcontext
));
1670 PFont(font_
)->ascent
=
1671 PANGO_PIXELS(pango_font_metrics_get_ascent(metrics
));
1672 pango_font_metrics_unref(metrics
);
1673 ascent
= PFont(font_
)->ascent
;
1676 if ((ascent
== 0) && (PFont(font_
)->pfont
)) {
1677 ascent
= PFont(font_
)->pfont
->ascent
;
1692 gdk_string_extents(PFont(font_
)->pfont
, sizeString
,
1693 &lbearing
, &rbearing
, &width
, &ascent
, &descent
);
1698 int SurfaceImpl::Descent(Font
&font_
) {
1699 if (!(font_
.GetID()))
1704 if (PFont(font_
)->pfd
) {
1705 PangoFontMetrics
*metrics
= pango_context_get_metrics(pcontext
,
1706 PFont(font_
)->pfd
, pango_context_get_language(pcontext
));
1707 int descent
= PANGO_PIXELS(pango_font_metrics_get_descent(metrics
));
1708 pango_font_metrics_unref(metrics
);
1712 return PFont(font_
)->pfont
->descent
;
1721 gdk_string_extents(PFont(font_
)->pfont
, sizeString
,
1722 &lbearing
, &rbearing
, &width
, &ascent
, &descent
);
1727 int SurfaceImpl::InternalLeading(Font
&) {
1731 int SurfaceImpl::ExternalLeading(Font
&) {
1735 int SurfaceImpl::Height(Font
&font_
) {
1736 return Ascent(font_
) + Descent(font_
);
1739 int SurfaceImpl::AverageCharWidth(Font
&font_
) {
1740 return WidthChar(font_
, 'n');
1743 int SurfaceImpl::SetPalette(Palette
*, bool) {
1744 // Handled in palette allocation for GTK so this does nothing
1748 void SurfaceImpl::SetClip(PRectangle rc
) {
1749 GdkRectangle area
= {rc
.left
, rc
.top
,
1750 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
};
1751 gdk_gc_set_clip_rectangle(gc
, &area
);
1754 void SurfaceImpl::FlushCachedState() {}
1756 void SurfaceImpl::SetUnicodeMode(bool unicodeMode_
) {
1761 void SurfaceImpl::SetDBCSMode(int codePage
) {
1762 if (codePage
&& (codePage
!= SC_CP_UTF8
))
1766 Surface
*Surface::Allocate() {
1767 return new SurfaceImpl
;
1770 Window::~Window() {}
1772 void Window::Destroy() {
1774 gtk_widget_destroy(GTK_WIDGET(wid
));
1778 bool Window::HasFocus() {
1779 return GTK_WIDGET_HAS_FOCUS(wid
);
1782 PRectangle
Window::GetPosition() {
1783 // Before any size allocated pretend its 1000 wide so not scrolled
1784 PRectangle
rc(0, 0, 1000, 1000);
1786 rc
.left
= PWidget(wid
)->allocation
.x
;
1787 rc
.top
= PWidget(wid
)->allocation
.y
;
1788 if (PWidget(wid
)->allocation
.width
> 20) {
1789 rc
.right
= rc
.left
+ PWidget(wid
)->allocation
.width
;
1790 rc
.bottom
= rc
.top
+ PWidget(wid
)->allocation
.height
;
1796 void Window::SetPosition(PRectangle rc
) {
1798 GtkAllocation alloc
;
1801 alloc
.width
= rc
.Width();
1802 alloc
.height
= rc
.Height();
1803 gtk_widget_size_allocate(PWidget(wid
), &alloc
);
1806 gtk_widget_set_uposition(wid
, rc
.left
, rc
.top
);
1807 gtk_widget_set_usize(wid
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
1811 void Window::SetPositionRelative(PRectangle rc
, Window relativeTo
) {
1814 gdk_window_get_origin(PWidget(relativeTo
.wid
)->window
, &ox
, &oy
);
1822 /* do some corrections to fit into screen */
1823 int sizex
= rc
.right
- rc
.left
;
1824 int sizey
= rc
.bottom
- rc
.top
;
1825 int screenWidth
= gdk_screen_width();
1826 int screenHeight
= gdk_screen_height();
1827 if (sizex
> screenWidth
)
1828 ox
= 0; /* the best we can do */
1829 else if (ox
+ sizex
> screenWidth
)
1830 ox
= screenWidth
- sizex
;
1831 if (oy
+ sizey
> screenHeight
)
1832 oy
= screenHeight
- sizey
;
1834 #if GTK_MAJOR_VERSION >= 2
1835 gtk_window_move(GTK_WINDOW(PWidget(wid
)), ox
, oy
);
1837 gtk_widget_set_uposition(PWidget(wid
), ox
, oy
);
1842 GtkAllocation alloc
;
1843 alloc
.x
= rc
.left
+ ox
;
1844 alloc
.y
= rc
.top
+ oy
;
1845 alloc
.width
= rc
.right
- rc
.left
;
1846 alloc
.height
= rc
.bottom
- rc
.top
;
1847 gtk_widget_size_allocate(wid
, &alloc
);
1849 gtk_widget_set_usize(PWidget(wid
), sizex
, sizey
);
1852 PRectangle
Window::GetClientPosition() {
1853 // On GTK+, the client position is the window position
1854 return GetPosition();
1857 void Window::Show(bool show
) {
1859 gtk_widget_show(PWidget(wid
));
1862 void Window::InvalidateAll() {
1864 gtk_widget_queue_draw(PWidget(wid
));
1868 void Window::InvalidateRectangle(PRectangle rc
) {
1870 gtk_widget_queue_draw_area(PWidget(wid
),
1872 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
1876 void Window::SetFont(Font
&) {
1877 // Can not be done generically but only needed for ListBox
1880 void Window::SetCursor(Cursor curs
) {
1881 // We don't set the cursor to same value numerous times under gtk because
1882 // it stores the cursor in the window once it's set
1883 if (curs
== cursorLast
)
1890 gdkCurs
= gdk_cursor_new(GDK_XTERM
);
1893 gdkCurs
= gdk_cursor_new(GDK_LEFT_PTR
);
1896 gdkCurs
= gdk_cursor_new(GDK_CENTER_PTR
);
1899 gdkCurs
= gdk_cursor_new(GDK_WATCH
);
1902 gdkCurs
= gdk_cursor_new(GDK_HAND2
);
1904 case cursorReverseArrow
:
1905 gdkCurs
= gdk_cursor_new(GDK_RIGHT_PTR
);
1908 gdkCurs
= gdk_cursor_new(GDK_LEFT_PTR
);
1909 cursorLast
= cursorArrow
;
1913 if (PWidget(wid
)->window
)
1914 gdk_window_set_cursor(PWidget(wid
)->window
, gdkCurs
);
1915 gdk_cursor_destroy(gdkCurs
);
1918 void Window::SetTitle(const char *s
) {
1919 gtk_window_set_title(GTK_WINDOW(wid
), s
);
1922 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
1923 gdk window coordinates */
1924 PRectangle
Window::GetMonitorRect(Point pt
) {
1925 gint x_offset
, y_offset
;
1928 gdk_window_get_origin(PWidget(wid
)->window
, &x_offset
, &y_offset
);
1931 #if GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2)
1937 screen
= gtk_widget_get_screen(PWidget(wid
));
1938 monitor_num
= gdk_screen_get_monitor_at_point(screen
, pt
.x
+ x_offset
, pt
.y
+ y_offset
);
1939 gdk_screen_get_monitor_geometry(screen
, monitor_num
, &rect
);
1942 return PRectangle(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
1945 return PRectangle(-x_offset
, -y_offset
, (-x_offset
) + gdk_screen_width(),
1946 (-y_offset
) + gdk_screen_height());
1951 const char *xpm_data
;
1952 #if GTK_MAJOR_VERSION < 2
1960 static void list_image_free(gpointer
, gpointer value
, gpointer
) {
1961 ListImage
*list_image
= (ListImage
*) value
;
1962 #if GTK_MAJOR_VERSION < 2
1963 if (list_image
->pixmap
)
1964 gdk_pixmap_unref(list_image
->pixmap
);
1965 if (list_image
->bitmap
)
1966 gdk_bitmap_unref(list_image
->bitmap
);
1968 if (list_image
->pixbuf
)
1969 gdk_pixbuf_unref (list_image
->pixbuf
);
1974 ListBox::ListBox() {
1977 ListBox::~ListBox() {
1980 #if GTK_MAJOR_VERSION >= 2
1988 class ListBoxX
: public ListBox
{
1991 #if GTK_MAJOR_VERSION < 2
1995 #if GTK_MAJOR_VERSION >= 2
1996 GtkCellRenderer
* pixbuf_renderer
;
1999 int desiredVisibleRows
;
2000 unsigned int maxItemCharacters
;
2001 unsigned int aveCharWidth
;
2003 CallBackAction doubleClickAction
;
2004 void *doubleClickActionData
;
2006 ListBoxX() : list(0), pixhash(NULL
),
2007 desiredVisibleRows(5), maxItemCharacters(0),
2008 aveCharWidth(1), doubleClickAction(NULL
), doubleClickActionData(NULL
) {
2009 #if GTK_MAJOR_VERSION < 2
2012 #if GTK_MAJOR_VERSION >= 2
2013 pixbuf_renderer
= 0;
2016 virtual ~ListBoxX() {
2018 g_hash_table_foreach((GHashTable
*) pixhash
, list_image_free
, NULL
);
2019 g_hash_table_destroy((GHashTable
*) pixhash
);
2022 virtual void SetFont(Font
&font
);
2023 virtual void Create(Window
&parent
, int ctrlID
, Point location_
, int lineHeight_
, bool unicodeMode_
);
2024 virtual void SetAverageCharWidth(int width
);
2025 virtual void SetVisibleRows(int rows
);
2026 virtual int GetVisibleRows() const;
2027 virtual PRectangle
GetDesiredRect();
2028 virtual int CaretFromEdge();
2029 virtual void Clear();
2030 virtual void Append(char *s
, int type
= -1);
2031 virtual int Length();
2032 virtual void Select(int n
);
2033 virtual int GetSelection();
2034 virtual int Find(const char *prefix
);
2035 virtual void GetValue(int n
, char *value
, int len
);
2036 virtual void RegisterImage(int type
, const char *xpm_data
);
2037 virtual void ClearRegisteredImages();
2038 virtual void SetDoubleClickAction(CallBackAction action
, void *data
) {
2039 doubleClickAction
= action
;
2040 doubleClickActionData
= data
;
2042 virtual void SetList(const char *listText
, char separator
, char typesep
);
2045 ListBox
*ListBox::Allocate() {
2046 ListBoxX
*lb
= new ListBoxX();
2050 #if GTK_MAJOR_VERSION < 2
2051 static void UnselectionAC(GtkWidget
*, gint
, gint
,
2052 GdkEventButton
*, gpointer p
) {
2053 int *pi
= reinterpret_cast<int *>(p
);
2056 static void SelectionAC(GtkWidget
*, gint row
, gint
,
2057 GdkEventButton
*, gpointer p
) {
2058 int *pi
= reinterpret_cast<int *>(p
);
2063 static gboolean
ButtonPress(GtkWidget
*, GdkEventButton
* ev
, gpointer p
) {
2065 ListBoxX
* lb
= reinterpret_cast<ListBoxX
*>(p
);
2066 if (ev
->type
== GDK_2BUTTON_PRESS
&& lb
->doubleClickAction
!= NULL
) {
2067 lb
->doubleClickAction(lb
->doubleClickActionData
);
2072 // No pointer back to Scintilla to save status
2077 #if GTK_MAJOR_VERSION >= 2
2078 /* Change the active color to the selected color so the listbox uses the color
2079 scheme that it would use if it had the focus. */
2080 static void StyleSet(GtkWidget
*w
, GtkStyle
*, void*) {
2083 g_return_if_fail(w
!= NULL
);
2085 /* Copy the selected color to active. Note that the modify calls will cause
2086 recursive calls to this function after the value is updated and w->style to
2087 be set to a new object */
2088 style
= gtk_widget_get_style(w
);
2091 if (!gdk_color_equal(&style
->base
[GTK_STATE_SELECTED
], &style
->base
[GTK_STATE_ACTIVE
]))
2092 gtk_widget_modify_base(w
, GTK_STATE_ACTIVE
, &style
->base
[GTK_STATE_SELECTED
]);
2094 style
= gtk_widget_get_style(w
);
2097 if (!gdk_color_equal(&style
->text
[GTK_STATE_SELECTED
], &style
->text
[GTK_STATE_ACTIVE
]))
2098 gtk_widget_modify_text(w
, GTK_STATE_ACTIVE
, &style
->text
[GTK_STATE_SELECTED
]);
2102 void ListBoxX::Create(Window
&, int, Point
, int, bool) {
2103 wid
= gtk_window_new(GTK_WINDOW_POPUP
);
2105 GtkWidget
*frame
= gtk_frame_new(NULL
);
2106 gtk_widget_show(frame
);
2107 gtk_container_add(GTK_CONTAINER(GetID()), frame
);
2108 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
2109 gtk_container_set_border_width(GTK_CONTAINER(frame
), 0);
2111 scroller
= gtk_scrolled_window_new(NULL
, NULL
);
2112 gtk_container_set_border_width(GTK_CONTAINER(scroller
), 0);
2113 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller
),
2114 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
2115 gtk_container_add(GTK_CONTAINER(frame
), PWidget(scroller
));
2116 gtk_widget_show(PWidget(scroller
));
2118 #if GTK_MAJOR_VERSION < 2
2119 list
= gtk_clist_new(1);
2120 GtkWidget
*wid
= PWidget(list
); // No code inside the GTK_OBJECT macro
2121 gtk_widget_show(wid
);
2122 gtk_container_add(GTK_CONTAINER(PWidget(scroller
)), wid
);
2123 gtk_clist_set_column_auto_resize(GTK_CLIST(wid
), 0, TRUE
);
2124 gtk_clist_set_selection_mode(GTK_CLIST(wid
), GTK_SELECTION_BROWSE
);
2125 gtk_signal_connect(GTK_OBJECT(wid
), "unselect_row",
2126 GTK_SIGNAL_FUNC(UnselectionAC
), ¤t
);
2127 gtk_signal_connect(GTK_OBJECT(wid
), "select_row",
2128 GTK_SIGNAL_FUNC(SelectionAC
), ¤t
);
2129 gtk_signal_connect(GTK_OBJECT(wid
), "button_press_event",
2130 GTK_SIGNAL_FUNC(ButtonPress
), this);
2131 gtk_clist_set_shadow_type(GTK_CLIST(wid
), GTK_SHADOW_NONE
);
2133 /* Tree and its model */
2134 GtkListStore
*store
=
2135 gtk_list_store_new(N_COLUMNS
, GDK_TYPE_PIXBUF
, G_TYPE_STRING
);
2137 list
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
2138 g_signal_connect(G_OBJECT(list
), "style-set", G_CALLBACK(StyleSet
), NULL
);
2140 GtkTreeSelection
*selection
=
2141 gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
2142 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
2143 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list
), FALSE
);
2144 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list
), FALSE
);
2147 GtkTreeViewColumn
*column
= gtk_tree_view_column_new();
2148 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
2149 gtk_tree_view_column_set_title(column
, "Autocomplete");
2151 pixbuf_renderer
= gtk_cell_renderer_pixbuf_new();
2152 gtk_cell_renderer_set_fixed_size(pixbuf_renderer
, 0, -1);
2153 gtk_tree_view_column_pack_start(column
, pixbuf_renderer
, FALSE
);
2154 gtk_tree_view_column_add_attribute(column
, pixbuf_renderer
,
2155 "pixbuf", PIXBUF_COLUMN
);
2157 GtkCellRenderer
* renderer
= gtk_cell_renderer_text_new();
2158 gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer
), 1);
2159 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
2160 gtk_tree_view_column_add_attribute(column
, renderer
,
2161 "text", TEXT_COLUMN
);
2163 gtk_tree_view_append_column(GTK_TREE_VIEW(list
), column
);
2164 if (g_object_class_find_property(G_OBJECT_GET_CLASS(list
), "fixed-height-mode"))
2165 g_object_set(G_OBJECT(list
), "fixed-height-mode", TRUE
, NULL
);
2167 GtkWidget
*wid
= PWidget(list
); // No code inside the G_OBJECT macro
2168 gtk_container_add(GTK_CONTAINER(PWidget(scroller
)), wid
);
2169 gtk_widget_show(wid
);
2170 g_signal_connect(G_OBJECT(wid
), "button_press_event",
2171 G_CALLBACK(ButtonPress
), this);
2173 gtk_widget_realize(PWidget(wid
));
2176 void ListBoxX::SetFont(Font
&scint_font
) {
2177 #if GTK_MAJOR_VERSION < 2
2178 GtkStyle
*style
= gtk_widget_get_style(GTK_WIDGET(PWidget(list
)));
2179 if (!gdk_font_equal(style
->font
, PFont(scint_font
)->pfont
)) {
2180 style
= gtk_style_copy(style
);
2181 gdk_font_unref(style
->font
);
2182 style
->font
= PFont(scint_font
)->pfont
;
2183 gdk_font_ref(style
->font
);
2184 gtk_widget_set_style(GTK_WIDGET(PWidget(list
)), style
);
2185 gtk_style_unref(style
);
2188 // Only do for Pango font as there have been crashes for GDK fonts
2189 if (Created() && PFont(scint_font
)->pfd
) {
2190 // Current font is Pango font
2191 gtk_widget_modify_font(PWidget(list
), PFont(scint_font
)->pfd
);
2196 void ListBoxX::SetAverageCharWidth(int width
) {
2197 aveCharWidth
= width
;
2200 void ListBoxX::SetVisibleRows(int rows
) {
2201 desiredVisibleRows
= rows
;
2204 int ListBoxX::GetVisibleRows() const {
2205 return desiredVisibleRows
;
2208 PRectangle
ListBoxX::GetDesiredRect() {
2209 // Before any size allocated pretend its 100 wide so not scrolled
2210 PRectangle
rc(0, 0, 100, 100);
2212 int rows
= Length();
2213 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2214 rows
= desiredVisibleRows
;
2219 // First calculate height of the clist for our desired visible
2220 // row count otherwise it tries to expand to the total # of rows
2221 #if GTK_MAJOR_VERSION < 2
2222 int ythickness
= PWidget(list
)->style
->klass
->ythickness
;
2223 height
= (rows
* GTK_CLIST(list
)->row_height
2226 + GTK_CONTAINER(PWidget(list
))->border_width
));
2231 GtkTreeViewColumn
* column
=
2232 gtk_tree_view_get_column(GTK_TREE_VIEW(list
), 0);
2233 gtk_tree_view_column_cell_get_size(column
, NULL
,
2234 NULL
, NULL
, &row_width
, &row_height
);
2235 int ythickness
= PWidget(list
)->style
->ythickness
;
2236 height
= (rows
* row_height
2238 + GTK_CONTAINER(PWidget(list
))->border_width
+ 1));
2240 gtk_widget_set_usize(GTK_WIDGET(PWidget(list
)), -1, height
);
2242 // Get the size of the scroller because we set usize on the window
2243 gtk_widget_size_request(GTK_WIDGET(scroller
), &req
);
2244 rc
.right
= req
.width
;
2245 rc
.bottom
= req
.height
;
2247 gtk_widget_set_usize(GTK_WIDGET(list
), -1, -1);
2248 int width
= maxItemCharacters
;
2251 rc
.right
= width
* (aveCharWidth
+ aveCharWidth
/ 3);
2252 if (Length() > rows
)
2253 rc
.right
= rc
.right
+ 16;
2258 int ListBoxX::CaretFromEdge() {
2259 #if GTK_MAJOR_VERSION >= 2
2260 gint renderer_width
, renderer_height
;
2261 gtk_cell_renderer_get_fixed_size(pixbuf_renderer
, &renderer_width
,
2263 return 4 + renderer_width
;
2265 return 4 + xset
.GetWidth();
2268 void ListBoxX::Clear() {
2269 #if GTK_MAJOR_VERSION < 2
2270 gtk_clist_clear(GTK_CLIST(list
));
2272 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2273 gtk_list_store_clear(GTK_LIST_STORE(model
));
2275 maxItemCharacters
= 0;
2278 #if GTK_MAJOR_VERSION < 2
2279 static void init_pixmap(ListImage
*list_image
, GtkWidget
*window
) {
2281 static void init_pixmap(ListImage
*list_image
) {
2283 const char *textForm
= list_image
->xpm_data
;
2284 const char * const * xpm_lineform
= reinterpret_cast<const char * const *>(textForm
);
2285 const char **xpm_lineformfromtext
= 0;
2286 // The XPM data can be either in atext form as will be read from a file
2287 // or in a line form (array of char *) as will be used for images defined in code.
2288 // Test for text form and convert to line form
2289 if ((0 == memcmp(textForm
, "/* X", 4)) && (0 == memcmp(textForm
, "/* XPM */", 9))) {
2290 // Test done is two parts to avoid possibility of overstepping the memory
2291 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
2292 xpm_lineformfromtext
= XPM::LinesFormFromTextForm(textForm
);
2293 xpm_lineform
= xpm_lineformfromtext
;
2296 // Drop any existing pixmap/bitmap as data may have changed
2297 #if GTK_MAJOR_VERSION < 2
2298 if (list_image
->pixmap
)
2299 gdk_pixmap_unref(list_image
->pixmap
);
2300 list_image
->pixmap
= NULL
;
2301 if (list_image
->bitmap
)
2302 gdk_bitmap_unref(list_image
->bitmap
);
2303 list_image
->bitmap
= NULL
;
2305 list_image
->pixmap
= gdk_pixmap_colormap_create_from_xpm_d(NULL
2306 , gtk_widget_get_colormap(window
), &(list_image
->bitmap
), NULL
2307 , (gchar
**) xpm_lineform
);
2308 if (NULL
== list_image
->pixmap
) {
2309 if (list_image
->bitmap
)
2310 gdk_bitmap_unref(list_image
->bitmap
);
2311 list_image
->bitmap
= NULL
;
2314 if (list_image
->pixbuf
)
2315 gdk_pixbuf_unref(list_image
->pixbuf
);
2316 list_image
->pixbuf
=
2317 gdk_pixbuf_new_from_xpm_data((const gchar
**)xpm_lineform
);
2319 delete []xpm_lineformfromtext
;
2324 void ListBoxX::Append(char *s
, int type
) {
2325 ListImage
*list_image
= NULL
;
2326 if ((type
>= 0) && pixhash
) {
2327 list_image
= (ListImage
*) g_hash_table_lookup((GHashTable
*) pixhash
2328 , (gconstpointer
) GINT_TO_POINTER(type
));
2330 #if GTK_MAJOR_VERSION < 2
2331 char * szs
[] = { s
, NULL
};
2332 int rownum
= gtk_clist_append(GTK_CLIST(list
), szs
);
2334 if (NULL
== list_image
->pixmap
)
2335 init_pixmap(list_image
, (GtkWidget
*) list
);
2336 gtk_clist_set_pixtext(GTK_CLIST(list
), rownum
, 0, s
, SPACING
2337 , list_image
->pixmap
, list_image
->bitmap
);
2341 GtkListStore
*store
=
2342 GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list
)));
2343 gtk_list_store_append(GTK_LIST_STORE(store
), &iter
);
2345 if (NULL
== list_image
->pixbuf
)
2346 init_pixmap(list_image
);
2347 if (list_image
->pixbuf
) {
2348 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
2349 PIXBUF_COLUMN
, list_image
->pixbuf
,
2350 TEXT_COLUMN
, s
, -1);
2352 gint pixbuf_width
= gdk_pixbuf_get_width(list_image
->pixbuf
);
2353 gint renderer_height
, renderer_width
;
2354 gtk_cell_renderer_get_fixed_size(pixbuf_renderer
,
2355 &renderer_width
, &renderer_height
);
2356 if (pixbuf_width
> renderer_width
)
2357 gtk_cell_renderer_set_fixed_size(pixbuf_renderer
,
2360 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
2361 TEXT_COLUMN
, s
, -1);
2364 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
2365 TEXT_COLUMN
, s
, -1);
2368 size_t len
= strlen(s
);
2369 if (maxItemCharacters
< len
)
2370 maxItemCharacters
= len
;
2373 int ListBoxX::Length() {
2375 #if GTK_MAJOR_VERSION < 2
2376 return GTK_CLIST(list
)->rows
;
2378 return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
2379 (GTK_TREE_VIEW(list
)), NULL
);
2384 void ListBoxX::Select(int n
) {
2385 #if GTK_MAJOR_VERSION < 2
2387 gtk_clist_unselect_row(GTK_CLIST(list
), current
, 0);
2389 gtk_clist_select_row(GTK_CLIST(list
), n
, 0);
2390 gtk_clist_moveto(GTK_CLIST(list
), n
, 0, 0.5, 0.5);
2394 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2395 GtkTreeSelection
*selection
=
2396 gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
2399 gtk_tree_selection_unselect_all(selection
);
2403 bool valid
= gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, n
) != FALSE
;
2405 gtk_tree_selection_select_iter(selection
, &iter
);
2407 // Move the scrollbar to show the selection.
2408 int total
= Length();
2409 GtkAdjustment
*adj
=
2410 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list
));
2411 gfloat value
= ((gfloat
)n
/ total
) * (adj
->upper
- adj
->lower
)
2412 + adj
->lower
- adj
->page_size
/ 2;
2417 GtkTreeViewColumn
* column
=
2418 gtk_tree_view_get_column(GTK_TREE_VIEW(list
), 0);
2419 gtk_tree_view_column_cell_get_size(column
, NULL
, NULL
,
2420 NULL
, &row_width
, &row_height
);
2422 int rows
= Length();
2423 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2424 rows
= desiredVisibleRows
;
2426 // Odd rows to display -- We are now in the middle.
2427 // Align it so that we don't chop off rows.
2428 value
+= (gfloat
)row_height
/ 2.0;
2431 value
= (value
< 0)? 0 : value
;
2432 value
= (value
> (adj
->upper
- adj
->page_size
))?
2433 (adj
->upper
- adj
->page_size
) : value
;
2436 gtk_adjustment_set_value(adj
, value
);
2438 gtk_tree_selection_unselect_all(selection
);
2443 int ListBoxX::GetSelection() {
2444 #if GTK_MAJOR_VERSION < 2
2448 GtkTreeModel
*model
;
2449 GtkTreeSelection
*selection
;
2450 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
2451 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
)) {
2452 GtkTreePath
*path
= gtk_tree_model_get_path(model
, &iter
);
2453 int *indices
= gtk_tree_path_get_indices(path
);
2454 // Don't free indices.
2462 int ListBoxX::Find(const char *prefix
) {
2463 #if GTK_MAJOR_VERSION < 2
2464 int count
= Length();
2465 for (int i
= 0; i
< count
; i
++) {
2467 gtk_clist_get_text(GTK_CLIST(list
), i
, 0, &s
);
2468 if (s
&& (0 == strncmp(prefix
, s
, strlen(prefix
)))) {
2474 GtkTreeModel
*model
=
2475 gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2476 bool valid
= gtk_tree_model_get_iter_first(model
, &iter
) != FALSE
;
2480 gtk_tree_model_get(model
, &iter
, TEXT_COLUMN
, &s
, -1);
2481 if (s
&& (0 == strncmp(prefix
, s
, strlen(prefix
)))) {
2484 valid
= gtk_tree_model_iter_next(model
, &iter
) != FALSE
;
2491 void ListBoxX::GetValue(int n
, char *value
, int len
) {
2493 #if GTK_MAJOR_VERSION < 2
2494 GtkCellType type
= gtk_clist_get_cell_type(GTK_CLIST(list
), n
, 0);
2497 gtk_clist_get_text(GTK_CLIST(list
), n
, 0, &text
);
2499 case GTK_CELL_PIXTEXT
:
2500 gtk_clist_get_pixtext(GTK_CLIST(list
), n
, 0, &text
, NULL
, NULL
, NULL
);
2507 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2508 bool valid
= gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, n
) != FALSE
;
2510 gtk_tree_model_get(model
, &iter
, TEXT_COLUMN
, &text
, -1);
2513 if (text
&& len
> 0) {
2514 strncpy(value
, text
, len
);
2515 value
[len
- 1] = '\0';
2521 // g_return_if_fail causes unnecessary compiler warning in release compile.
2523 #pragma warning(disable: 4127)
2526 void ListBoxX::RegisterImage(int type
, const char *xpm_data
) {
2527 g_return_if_fail(xpm_data
);
2529 // Saved and use the saved copy so caller's copy can disappear.
2530 xset
.Add(type
, xpm_data
);
2531 XPM
*pxpm
= xset
.Get(type
);
2532 xpm_data
= reinterpret_cast<const char *>(pxpm
->InLinesForm());
2535 pixhash
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
2537 ListImage
*list_image
= (ListImage
*) g_hash_table_lookup((GHashTable
*) pixhash
,
2538 (gconstpointer
) GINT_TO_POINTER(type
));
2540 // Drop icon already registered
2541 #if GTK_MAJOR_VERSION < 2
2542 if (list_image
->pixmap
)
2543 gdk_pixmap_unref(list_image
->pixmap
);
2544 list_image
->pixmap
= 0;
2545 if (list_image
->bitmap
)
2546 gdk_bitmap_unref(list_image
->bitmap
);
2547 list_image
->bitmap
= 0;
2549 if (list_image
->pixbuf
)
2550 gdk_pixbuf_unref(list_image
->pixbuf
);
2551 list_image
->pixbuf
= NULL
;
2553 list_image
->xpm_data
= xpm_data
;
2555 list_image
= g_new0(ListImage
, 1);
2556 list_image
->xpm_data
= xpm_data
;
2557 g_hash_table_insert((GHashTable
*) pixhash
, GINT_TO_POINTER(type
),
2558 (gpointer
) list_image
);
2562 void ListBoxX::ClearRegisteredImages() {
2566 void ListBoxX::SetList(const char *listText
, char separator
, char typesep
) {
2568 int count
= strlen(listText
) + 1;
2569 char *words
= new char[count
];
2571 memcpy(words
, listText
, count
);
2572 char *startword
= words
;
2573 char *numword
= NULL
;
2575 for (; words
[i
]; i
++) {
2576 if (words
[i
] == separator
) {
2580 Append(startword
, numword
?atoi(numword
+ 1):-1);
2581 startword
= words
+ i
+ 1;
2583 } else if (words
[i
] == typesep
) {
2584 numword
= words
+ i
;
2590 Append(startword
, numword
?atoi(numword
+ 1):-1);
2596 Menu::Menu() : mid(0) {}
2598 void Menu::CreatePopUp() {
2600 mid
= gtk_item_factory_new(GTK_TYPE_MENU
, "<main>", NULL
);
2603 void Menu::Destroy() {
2605 #if GTK_MAJOR_VERSION < 2
2606 gtk_object_unref(GTK_OBJECT(mid
));
2608 g_object_unref(G_OBJECT(mid
));
2613 void Menu::Show(Point pt
, Window
&) {
2614 int screenHeight
= gdk_screen_height();
2615 int screenWidth
= gdk_screen_width();
2616 GtkItemFactory
*factory
= reinterpret_cast<GtkItemFactory
*>(mid
);
2617 GtkWidget
*widget
= gtk_item_factory_get_widget(factory
, "<main>");
2618 gtk_widget_show_all(widget
);
2619 GtkRequisition requisition
;
2620 gtk_widget_size_request(widget
, &requisition
);
2621 if ((pt
.x
+ requisition
.width
) > screenWidth
) {
2622 pt
.x
= screenWidth
- requisition
.width
;
2624 if ((pt
.y
+ requisition
.height
) > screenHeight
) {
2625 pt
.y
= screenHeight
- requisition
.height
;
2627 #if GTK_MAJOR_VERSION >= 2
2628 gtk_item_factory_popup(factory
, pt
.x
- 4, pt
.y
- 4, 3,
2629 gtk_get_current_event_time());
2631 gtk_item_factory_popup(factory
, pt
.x
- 4, pt
.y
- 4, 3, 0);
2635 ElapsedTime::ElapsedTime() {
2637 g_get_current_time(&curTime
);
2638 bigBit
= curTime
.tv_sec
;
2639 littleBit
= curTime
.tv_usec
;
2642 class DynamicLibraryImpl
: public DynamicLibrary
{
2646 DynamicLibraryImpl(const char *modulePath
) {
2647 m
= g_module_open(modulePath
, G_MODULE_BIND_LAZY
);
2650 virtual ~DynamicLibraryImpl() {
2655 // Use g_module_symbol to get a pointer to the relevant function.
2656 virtual Function
FindFunction(const char *name
) {
2658 gpointer fn_address
= NULL
;
2659 gboolean status
= g_module_symbol(m
, name
, &fn_address
);
2661 return static_cast<Function
>(fn_address
);
2668 virtual bool IsValid() {
2673 DynamicLibrary
*DynamicLibrary::Load(const char *modulePath
) {
2674 return static_cast<DynamicLibrary
*>( new DynamicLibraryImpl(modulePath
) );
2677 double ElapsedTime::Duration(bool reset
) {
2679 g_get_current_time(&curTime
);
2680 long endBigBit
= curTime
.tv_sec
;
2681 long endLittleBit
= curTime
.tv_usec
;
2682 double result
= 1000000.0 * (endBigBit
- bigBit
);
2683 result
+= endLittleBit
- littleBit
;
2684 result
/= 1000000.0;
2687 littleBit
= endLittleBit
;
2692 ColourDesired
Platform::Chrome() {
2693 return ColourDesired(0xe0, 0xe0, 0xe0);
2696 ColourDesired
Platform::ChromeHighlight() {
2697 return ColourDesired(0xff, 0xff, 0xff);
2700 const char *Platform::DefaultFont() {
2702 return "Lucida Console";
2707 return "lucidatypewriter";
2712 int Platform::DefaultFontSize() {
2720 unsigned int Platform::DoubleClickTime() {
2721 return 500; // Half a second
2724 bool Platform::MouseButtonBounce() {
2728 void Platform::DebugDisplay(const char *s
) {
2729 fprintf(stderr
, "%s", s
);
2732 bool Platform::IsKeyDown(int) {
2733 // TODO: discover state of keys in GTK+/X
2737 long Platform::SendScintilla(
2738 WindowID w
, unsigned int msg
, unsigned long wParam
, long lParam
) {
2739 return scintilla_send_message(SCINTILLA(w
), msg
, wParam
, lParam
);
2742 long Platform::SendScintillaPointer(
2743 WindowID w
, unsigned int msg
, unsigned long wParam
, void *lParam
) {
2744 return scintilla_send_message(SCINTILLA(w
), msg
, wParam
,
2745 reinterpret_cast<sptr_t
>(lParam
));
2748 bool Platform::IsDBCSLeadByte(int codePage
, char ch
) {
2749 // Byte ranges found in Wikipedia articles with relevant search strings in each case
2750 unsigned char uch
= static_cast<unsigned char>(ch
);
2754 return ((uch
>= 0x81) && (uch
<= 0x9F)) ||
2755 ((uch
>= 0xE0) && (uch
<= 0xEF));
2758 return (uch
>= 0x81) && (uch
<= 0xFE);
2761 return (uch
>= 0x81) && (uch
<= 0xFE);
2762 // Korean EUC-KR may be code page 949.
2767 int Platform::DBCSCharLength(int codePage
, const char *s
) {
2768 if (codePage
== 932 || codePage
== 936 || codePage
== 950) {
2769 return IsDBCSLeadByte(codePage
, s
[0]) ? 2 : 1;
2771 int bytes
= mblen(s
, MB_CUR_MAX
);
2779 int Platform::DBCSCharMaxLength() {
2784 // These are utility functions not really tied to a platform
2786 int Platform::Minimum(int a
, int b
) {
2793 int Platform::Maximum(int a
, int b
) {
2803 void Platform::DebugPrintf(const char *format
, ...) {
2806 va_start(pArguments
, format
);
2807 vsprintf(buffer
, format
, pArguments
);
2809 Platform::DebugDisplay(buffer
);
2812 void Platform::DebugPrintf(const char *, ...) {}
2816 // Not supported for GTK+
2817 static bool assertionPopUps
= true;
2819 bool Platform::ShowAssertionPopUps(bool assertionPopUps_
) {
2820 bool ret
= assertionPopUps
;
2821 assertionPopUps
= assertionPopUps_
;
2825 void Platform::Assert(const char *c
, const char *file
, int line
) {
2827 sprintf(buffer
, "Assertion [%s] failed at %s %d", c
, file
, line
);
2828 strcat(buffer
, "\r\n");
2829 Platform::DebugDisplay(buffer
);
2833 int Platform::Clamp(int val
, int minVal
, int maxVal
) {
2841 void Platform_Initialise() {
2842 FontMutexAllocate();
2845 void Platform_Finalise() {