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)
44 enum encodingType
{ singleByte
, UTF8
, dbcs
};
55 static GMutex
*fontMutex
= NULL
;
57 static void InitializeGLIBThreads() {
58 if (!g_thread_supported()) {
64 static void FontMutexAllocate() {
67 InitializeGLIBThreads();
68 fontMutex
= g_mutex_new();
73 static void FontMutexFree() {
76 g_mutex_free(fontMutex
);
82 static void FontMutexLock() {
84 g_mutex_lock(fontMutex
);
88 static void FontMutexUnlock() {
91 g_mutex_unlock(fontMutex
);
96 // On GTK+ 1.x holds a GdkFont* but on GTK+ 2.x can hold a GdkFont* or a
97 // PangoFontDescription*.
105 PangoFontDescription
*pfd
;
108 FontHandle(GdkFont
*pfont_
) {
119 FontHandle(PangoFontDescription
*pfd_
, int characterSet_
) {
124 characterSet
= characterSet_
;
130 gdk_font_unref(pfont
);
134 pango_font_description_free(pfd
);
138 void ResetWidths(encodingType et_
) {
140 for (int i
=0; i
<=127; i
++) {
144 int CharWidth(unsigned char ch
, encodingType et_
) {
147 if ((ch
<= 127) && (et
== et_
)) {
153 void SetCharWidth(unsigned char ch
, int w
, encodingType et_
) {
165 // X has a 16 bit coordinate space, so stop drawing here to avoid wrapping
166 static const int maxCoordinate
= 32000;
168 static FontHandle
*PFont(Font
&f
) {
169 return reinterpret_cast<FontHandle
*>(f
.GetID());
172 static GtkWidget
*PWidget(WindowID id
) {
173 return reinterpret_cast<GtkWidget
*>(id
);
176 static GtkWidget
*PWidget(Window
&w
) {
177 return PWidget(w
.GetID());
180 Point
Point::FromLong(long lpoint
) {
182 Platform::LowShortFromLong(lpoint
),
183 Platform::HighShortFromLong(lpoint
));
188 allowRealization
= false;
189 allocatedPalette
= 0;
192 entries
= new ColourPair
[size
];
195 Palette::~Palette() {
201 void Palette::Release() {
203 delete [](reinterpret_cast<GdkColor
*>(allocatedPalette
));
204 allocatedPalette
= 0;
208 entries
= new ColourPair
[size
];
211 // This method either adds a colour to the list of wanted colours (want==true)
212 // or retrieves the allocated colour back to the ColourPair.
213 // This is one method to make it easier to keep the code for wanting and retrieving in sync.
214 void Palette::WantFind(ColourPair
&cp
, bool want
) {
216 for (int i
=0; i
< used
; i
++) {
217 if (entries
[i
].desired
== cp
.desired
)
222 int sizeNew
= size
* 2;
223 ColourPair
*entriesNew
= new ColourPair
[sizeNew
];
224 for (int j
=0; j
<size
; j
++) {
225 entriesNew
[j
] = entries
[j
];
228 entries
= entriesNew
;
232 entries
[used
].desired
= cp
.desired
;
233 entries
[used
].allocated
.Set(cp
.desired
.AsLong());
236 for (int i
=0; i
< used
; i
++) {
237 if (entries
[i
].desired
== cp
.desired
) {
238 cp
.allocated
= entries
[i
].allocated
;
242 cp
.allocated
.Set(cp
.desired
.AsLong());
246 void Palette::Allocate(Window
&w
) {
247 if (allocatedPalette
) {
248 gdk_colormap_free_colors(gtk_widget_get_colormap(PWidget(w
)),
249 reinterpret_cast<GdkColor
*>(allocatedPalette
),
251 delete [](reinterpret_cast<GdkColor
*>(allocatedPalette
));
252 allocatedPalette
= 0;
255 GdkColor
*paletteNew
= new GdkColor
[used
];
256 allocatedPalette
= paletteNew
;
257 gboolean
*successPalette
= new gboolean
[used
];
261 for (iPal
= 0; iPal
< used
; iPal
++) {
262 paletteNew
[iPal
].red
= entries
[iPal
].desired
.GetRed() * (65535 / 255);
263 paletteNew
[iPal
].green
= entries
[iPal
].desired
.GetGreen() * (65535 / 255);
264 paletteNew
[iPal
].blue
= entries
[iPal
].desired
.GetBlue() * (65535 / 255);
265 paletteNew
[iPal
].pixel
= entries
[iPal
].desired
.AsLong();
267 gdk_colormap_alloc_colors(gtk_widget_get_colormap(PWidget(w
)),
268 paletteNew
, allocatedLen
, FALSE
, TRUE
,
270 for (iPal
= 0; iPal
< used
; iPal
++) {
271 entries
[iPal
].allocated
.Set(paletteNew
[iPal
].pixel
);
274 delete []successPalette
;
277 static const char *CharacterSetName(int characterSet
) {
278 switch (characterSet
) {
279 case SC_CHARSET_ANSI
:
281 case SC_CHARSET_DEFAULT
:
283 case SC_CHARSET_BALTIC
:
285 case SC_CHARSET_CHINESEBIG5
:
287 case SC_CHARSET_EASTEUROPE
:
289 case SC_CHARSET_GB2312
:
290 return "gb2312.1980-*";
291 case SC_CHARSET_GREEK
:
293 case SC_CHARSET_HANGUL
:
294 return "ksc5601.1987-*";
299 case SC_CHARSET_RUSSIAN
:
301 case SC_CHARSET_CYRILLIC
:
303 case SC_CHARSET_SHIFTJIS
:
304 return "jisx0208.1983-*";
305 case SC_CHARSET_SYMBOL
:
307 case SC_CHARSET_TURKISH
:
309 case SC_CHARSET_JOHAB
:
311 case SC_CHARSET_HEBREW
:
313 case SC_CHARSET_ARABIC
:
315 case SC_CHARSET_VIETNAMESE
:
317 case SC_CHARSET_THAI
:
319 case SC_CHARSET_8859_15
:
326 static bool IsDBCSCharacterSet(int characterSet
) {
327 switch (characterSet
) {
328 case SC_CHARSET_GB2312
:
329 case SC_CHARSET_HANGUL
:
330 case SC_CHARSET_SHIFTJIS
:
331 case SC_CHARSET_CHINESEBIG5
:
338 static void GenerateFontSpecStrings(const char *fontName
, int characterSet
,
339 char *foundary
, int foundary_len
,
340 char *faceName
, int faceName_len
,
341 char *charset
, int charset_len
) {
342 // supported font strings include:
343 // foundary-fontface-isoxxx-x
347 if (strchr(fontName
, '-')) {
349 char *d1
= NULL
, *d2
= NULL
, *d3
= NULL
;
350 strncpy(tmp
, fontName
, sizeof(tmp
) - 1);
351 d1
= strchr(tmp
, '-');
352 // we know the first dash exists
353 d2
= strchr(d1
+ 1, '-');
355 d3
= strchr(d2
+ 1, '-');
357 // foundary-fontface-isoxxx-x
361 strncpy(faceName
, tmp
, foundary_len
- 1);
362 strncpy(charset
, d2
+ 1, charset_len
- 1);
366 strcpy(foundary
, "-*-");
367 strncpy(faceName
, tmp
, faceName_len
- 1);
368 strncpy(charset
, d1
+ 1, charset_len
- 1);
373 strncpy(faceName
, tmp
, faceName_len
- 1);
374 strncpy(charset
, CharacterSetName(characterSet
), charset_len
- 1);
377 strncpy(foundary
, "-*-", foundary_len
);
378 strncpy(faceName
, fontName
, faceName_len
- 1);
379 strncpy(charset
, CharacterSetName(characterSet
), charset_len
- 1);
383 static void SetLogFont(LOGFONT
&lf
, const char *faceName
, int characterSet
, int size
, bool bold
, bool italic
) {
384 memset(&lf
, 0, sizeof(lf
));
388 lf
.characterSet
= characterSet
;
389 strncpy(lf
.faceName
, faceName
, sizeof(lf
.faceName
) - 1);
393 * Create a hash from the parameters for a font to allow easy checking for identity.
394 * If one font is the same as another, its hash will be the same, but if the hash is the
395 * same then they may still be different.
397 static int HashFont(const char *faceName
, int characterSet
, int size
, bool bold
, bool italic
) {
400 (characterSet
<< 10) ^
401 (bold
? 0x10000000 : 0) ^
402 (italic
? 0x20000000 : 0) ^
406 class FontCached
: Font
{
411 FontCached(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
);
413 bool SameAs(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
);
414 virtual void Release();
415 static FontID
CreateNewFont(const char *fontName
, int characterSet
,
416 int size
, bool bold
, bool italic
);
417 static FontCached
*first
;
419 static FontID
FindOrCreate(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
);
420 static void ReleaseId(FontID id_
);
423 FontCached
*FontCached::first
= 0;
425 FontCached::FontCached(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
) :
426 next(0), usage(0), hash(0) {
427 ::SetLogFont(lf
, faceName_
, characterSet_
, size_
, bold_
, italic_
);
428 hash
= HashFont(faceName_
, characterSet_
, size_
, bold_
, italic_
);
429 id
= CreateNewFont(faceName_
, characterSet_
, size_
, bold_
, italic_
);
433 bool FontCached::SameAs(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
) {
437 lf
.italic
== italic_
&&
438 lf
.characterSet
== characterSet_
&&
439 0 == strcmp(lf
.faceName
, faceName_
);
442 void FontCached::Release() {
448 FontID
FontCached::FindOrCreate(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
) {
451 int hashFind
= HashFont(faceName_
, characterSet_
, size_
, bold_
, italic_
);
452 for (FontCached
*cur
= first
; cur
; cur
= cur
->next
) {
453 if ((cur
->hash
== hashFind
) &&
454 cur
->SameAs(faceName_
, characterSet_
, size_
, bold_
, italic_
)) {
460 FontCached
*fc
= new FontCached(faceName_
, characterSet_
, size_
, bold_
, italic_
);
471 void FontCached::ReleaseId(FontID id_
) {
473 FontCached
**pcur
= &first
;
474 for (FontCached
*cur
= first
; cur
; cur
= cur
->next
) {
475 if (cur
->id
== id_
) {
477 if (cur
->usage
== 0) {
490 static GdkFont
*LoadFontOrSet(const char *fontspec
, int characterSet
) {
491 if (IsDBCSCharacterSet(characterSet
)) {
492 return gdk_fontset_load(fontspec
);
494 return gdk_font_load(fontspec
);
498 FontID
FontCached::CreateNewFont(const char *fontName
, int characterSet
,
499 int size
, bool bold
, bool italic
) {
512 if (fontName
[0] == '!') {
513 PangoFontDescription
*pfd
= pango_font_description_new();
515 pango_font_description_set_family(pfd
, fontName
+1);
516 pango_font_description_set_size(pfd
, size
* PANGO_SCALE
);
517 pango_font_description_set_weight(pfd
, bold
? PANGO_WEIGHT_BOLD
: PANGO_WEIGHT_NORMAL
);
518 pango_font_description_set_style(pfd
, italic
? PANGO_STYLE_ITALIC
: PANGO_STYLE_NORMAL
);
519 return new FontHandle(pfd
, characterSet
);
525 // If name of the font begins with a '-', assume, that it is
527 if (fontName
[0] == '-') {
528 if (strchr(fontName
, ',') || IsDBCSCharacterSet(characterSet
)) {
529 newid
= gdk_fontset_load(fontName
);
531 newid
= gdk_font_load(fontName
);
534 // Font not available so substitute a reasonable code font
535 // iso8859 appears to only allow western characters.
536 newid
= LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
539 return new FontHandle(newid
);
542 // it's not a full fontspec, build one.
544 // This supports creating a FONT_SET
545 // in a method that allows us to also set size, slant and
546 // weight for the fontset. The expected input is multiple
547 // partial fontspecs seperated by comma
548 // eg. adobe-courier-iso10646-1,*-courier-iso10646-1,*-*-*-*
549 if (strchr(fontName
, ',')) {
550 // build a fontspec and use gdk_fontset_load
551 int remaining
= sizeof(fontset
);
552 char fontNameCopy
[1024];
553 strncpy(fontNameCopy
, fontName
, sizeof(fontNameCopy
) - 1);
554 char *fn
= fontNameCopy
;
555 char *fp
= strchr(fn
, ',');
557 const char *spec
= "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
558 if (fontset
[0] != '\0') {
559 // if this is not the first font in the list,
560 // append a comma seperator
561 spec
= ",%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
565 *fp
= '\0'; // nullify the comma
566 GenerateFontSpecStrings(fn
, characterSet
,
567 foundary
, sizeof(foundary
),
568 faceName
, sizeof(faceName
),
569 charset
, sizeof(charset
));
572 sizeof(fontspec
) - 1,
575 bold
? "-bold" : "-medium",
576 italic
? "-i" : "-r",
580 // if this is the first font in the list, and
581 // we are doing italic, add an oblique font
583 if (italic
&& fontset
[0] == '\0') {
584 strncat(fontset
, fontspec
, remaining
- 1);
585 remaining
-= strlen(fontset
);
588 sizeof(fontspec
) - 1,
589 ",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s",
591 bold
? "-bold" : "-medium",
596 strncat(fontset
, fontspec
, remaining
- 1);
597 remaining
-= strlen(fontset
);
603 fp
= strchr(fn
, ',');
606 newid
= gdk_fontset_load(fontset
);
608 return new FontHandle(newid
);
610 // if fontset load failed, fall through, we'll use
611 // the last font entry and continue to try and
612 // get something that matches
615 // single fontspec support
617 GenerateFontSpecStrings(fontName
, characterSet
,
618 foundary
, sizeof(foundary
),
619 faceName
, sizeof(faceName
),
620 charset
, sizeof(charset
));
623 sizeof(fontspec
) - 1,
624 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
626 bold
? "-bold" : "-medium",
627 italic
? "-i" : "-r",
630 newid
= LoadFontOrSet(fontspec
, characterSet
);
632 // some fonts have oblique, not italic
634 sizeof(fontspec
) - 1,
635 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
637 bold
? "-bold" : "-medium",
638 italic
? "-o" : "-r",
641 newid
= LoadFontOrSet(fontspec
, characterSet
);
645 sizeof(fontspec
) - 1,
646 "-*-*-*-*-*-*-*-%0d-*-*-*-*-%s",
649 newid
= gdk_font_load(fontspec
);
652 // Font not available so substitute a reasonable code font
653 // iso8859 appears to only allow western characters.
654 newid
= LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
657 return new FontHandle(newid
);
660 Font::Font() : id(0) {}
664 void Font::Create(const char *faceName
, int characterSet
, int size
,
665 bool bold
, bool italic
, bool) {
667 id
= FontCached::FindOrCreate(faceName
, characterSet
, size
, bold
, italic
);
670 void Font::Release() {
672 FontCached::ReleaseId(id
);
676 class SurfaceImpl
: public Surface
{
678 GdkDrawable
*drawable
;
686 PangoContext
*pcontext
;
690 void SetConverter(int characterSet_
);
694 virtual ~SurfaceImpl();
696 void Init(WindowID wid
);
697 void Init(SurfaceID sid
, WindowID wid
);
698 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
702 void PenColour(ColourAllocated fore
);
704 int DeviceHeightFont(int points
);
705 void MoveTo(int x_
, int y_
);
706 void LineTo(int x_
, int y_
);
707 void Polygon(Point
*pts
, int npts
, ColourAllocated fore
, ColourAllocated back
);
708 void RectangleDraw(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
);
709 void FillRectangle(PRectangle rc
, ColourAllocated back
);
710 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
711 void RoundedRectangle(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
);
712 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourAllocated fill
, int alphaFill
,
713 ColourAllocated outline
, int alphaOutline
, int flags
);
714 void Ellipse(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
);
715 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
717 void DrawTextBase(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
);
718 void DrawTextNoClip(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
, ColourAllocated back
);
719 void DrawTextClipped(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
, ColourAllocated back
);
720 void DrawTextTransparent(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
);
721 void MeasureWidths(Font
&font_
, const char *s
, int len
, int *positions
);
722 int WidthText(Font
&font_
, const char *s
, int len
);
723 int WidthChar(Font
&font_
, char ch
);
724 int Ascent(Font
&font_
);
725 int Descent(Font
&font_
);
726 int InternalLeading(Font
&font_
);
727 int ExternalLeading(Font
&font_
);
728 int Height(Font
&font_
);
729 int AverageCharWidth(Font
&font_
);
731 int SetPalette(Palette
*pal
, bool inBackGround
);
732 void SetClip(PRectangle rc
);
733 void FlushCachedState();
735 void SetUnicodeMode(bool unicodeMode_
);
736 void SetDBCSMode(int codePage
);
739 const char *CharacterSetID(int characterSet
) {
740 switch (characterSet
) {
741 case SC_CHARSET_ANSI
:
743 case SC_CHARSET_DEFAULT
:
745 case SC_CHARSET_BALTIC
:
746 return "ISO-8859-13";
747 case SC_CHARSET_CHINESEBIG5
:
749 case SC_CHARSET_EASTEUROPE
:
751 case SC_CHARSET_GB2312
:
753 case SC_CHARSET_GREEK
:
755 case SC_CHARSET_HANGUL
:
761 case SC_CHARSET_RUSSIAN
:
763 case SC_CHARSET_CYRILLIC
:
765 case SC_CHARSET_SHIFTJIS
:
767 case SC_CHARSET_SYMBOL
:
769 case SC_CHARSET_TURKISH
:
771 case SC_CHARSET_JOHAB
:
773 case SC_CHARSET_HEBREW
:
775 case SC_CHARSET_ARABIC
:
777 case SC_CHARSET_VIETNAMESE
:
779 case SC_CHARSET_THAI
:
780 return "ISO-8859-11";
781 case SC_CHARSET_8859_15
:
782 return "ISO-8859-15";
790 void SurfaceImpl::SetConverter(int characterSet_
) {
791 if (characterSet
!= characterSet_
) {
792 characterSet
= characterSet_
;
793 conv
.Open("UTF-8", CharacterSetID(characterSet
), false);
798 SurfaceImpl::SurfaceImpl() : et(singleByte
), drawable(0), gc(0), ppixmap(0),
799 x(0), y(0), inited(false), createdGC(false)
801 , pcontext(0), layout(0), characterSet(-1)
806 SurfaceImpl::~SurfaceImpl() {
810 void SurfaceImpl::Release() {
819 gdk_pixmap_unref(ppixmap
);
823 g_object_unref(layout
);
826 g_object_unref(pcontext
);
837 bool SurfaceImpl::Initialised() {
841 // The WindowID argument is only used for Pango builds
848 void SurfaceImpl::Init(WindowID WID_NAME
) {
851 PLATFORM_ASSERT(wid
);
852 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
853 PLATFORM_ASSERT(pcontext
);
854 layout
= pango_layout_new(pcontext
);
855 PLATFORM_ASSERT(layout
);
860 void SurfaceImpl::Init(SurfaceID sid
, WindowID WID_NAME
) {
861 PLATFORM_ASSERT(sid
);
862 GdkDrawable
*drawable_
= reinterpret_cast<GdkDrawable
*>(sid
);
865 PLATFORM_ASSERT(wid
);
866 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
867 layout
= pango_layout_new(pcontext
);
869 drawable
= drawable_
;
870 gc
= gdk_gc_new(drawable_
);
871 // Ask for lines that do not paint the last pixel so is like Win32
872 gdk_gc_set_line_attributes(gc
, 0, GDK_LINE_SOLID
, GDK_CAP_NOT_LAST
, GDK_JOIN_MITER
);
877 void SurfaceImpl::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID WID_NAME
) {
878 PLATFORM_ASSERT(surface_
);
880 SurfaceImpl
*surfImpl
= static_cast<SurfaceImpl
*>(surface_
);
881 PLATFORM_ASSERT(surfImpl
->drawable
);
883 PLATFORM_ASSERT(wid
);
884 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
885 PLATFORM_ASSERT(pcontext
);
886 layout
= pango_layout_new(pcontext
);
887 PLATFORM_ASSERT(layout
);
889 if (height
> 0 && width
> 0)
890 ppixmap
= gdk_pixmap_new(surfImpl
->drawable
, width
, height
, -1);
892 gc
= gdk_gc_new(surfImpl
->drawable
);
893 // Ask for lines that do not paint the last pixel so is like Win32
894 gdk_gc_set_line_attributes(gc
, 0, GDK_LINE_SOLID
, GDK_CAP_NOT_LAST
, GDK_JOIN_MITER
);
899 void SurfaceImpl::PenColour(ColourAllocated fore
) {
902 co
.pixel
= fore
.AsLong();
903 gdk_gc_set_foreground(gc
, &co
);
907 int SurfaceImpl::LogPixelsY() {
911 int SurfaceImpl::DeviceHeightFont(int points
) {
912 int logPix
= LogPixelsY();
913 return (points
* logPix
+ logPix
/ 2) / 72;
916 void SurfaceImpl::MoveTo(int x_
, int y_
) {
921 void SurfaceImpl::LineTo(int x_
, int y_
) {
922 if (drawable
&& gc
) {
923 gdk_draw_line(drawable
, gc
,
931 void SurfaceImpl::Polygon(Point
*pts
, int npts
, ColourAllocated fore
,
932 ColourAllocated back
) {
934 if (npts
< static_cast<int>((sizeof(gpts
) / sizeof(gpts
[0])))) {
935 for (int i
= 0;i
< npts
;i
++) {
936 gpts
[i
].x
= pts
[i
].x
;
937 gpts
[i
].y
= pts
[i
].y
;
940 gdk_draw_polygon(drawable
, gc
, 1, gpts
, npts
);
942 gdk_draw_polygon(drawable
, gc
, 0, gpts
, npts
);
946 void SurfaceImpl::RectangleDraw(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
) {
947 if (gc
&& drawable
) {
949 gdk_draw_rectangle(drawable
, gc
, 1,
950 rc
.left
+ 1, rc
.top
+ 1,
951 rc
.right
- rc
.left
- 2, rc
.bottom
- rc
.top
- 2);
954 // The subtraction of 1 off the width and height here shouldn't be needed but
955 // otherwise a different rectangle is drawn than would be done if the fill parameter == 1
956 gdk_draw_rectangle(drawable
, gc
, 0,
958 rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1);
962 void SurfaceImpl::FillRectangle(PRectangle rc
, ColourAllocated back
) {
964 if (drawable
&& (rc
.left
< maxCoordinate
)) { // Protect against out of range
965 gdk_draw_rectangle(drawable
, gc
, 1,
967 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
971 void SurfaceImpl::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
972 if (static_cast<SurfaceImpl
&>(surfacePattern
).drawable
) {
973 // Tile pattern over rectangle
974 // Currently assumes 8x8 pattern
977 for (int xTile
= rc
.left
; xTile
< rc
.right
; xTile
+= widthPat
) {
978 int widthx
= (xTile
+ widthPat
> rc
.right
) ? rc
.right
- xTile
: widthPat
;
979 for (int yTile
= rc
.top
; yTile
< rc
.bottom
; yTile
+= heightPat
) {
980 int heighty
= (yTile
+ heightPat
> rc
.bottom
) ? rc
.bottom
- yTile
: heightPat
;
981 gdk_draw_pixmap(drawable
,
983 static_cast<SurfaceImpl
&>(surfacePattern
).drawable
,
990 // Something is wrong so try to show anyway
991 // Shows up black because colour not allocated
992 FillRectangle(rc
, ColourAllocated(0));
996 void SurfaceImpl::RoundedRectangle(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
) {
997 if (((rc
.right
- rc
.left
) > 4) && ((rc
.bottom
- rc
.top
) > 4)) {
998 // Approximate a round rect with some cut off corners
1000 Point(rc
.left
+ 2, rc
.top
),
1001 Point(rc
.right
- 2, rc
.top
),
1002 Point(rc
.right
, rc
.top
+ 2),
1003 Point(rc
.right
, rc
.bottom
- 2),
1004 Point(rc
.right
- 2, rc
.bottom
),
1005 Point(rc
.left
+ 2, rc
.bottom
),
1006 Point(rc
.left
, rc
.bottom
- 2),
1007 Point(rc
.left
, rc
.top
+ 2),
1009 Polygon(pts
, sizeof(pts
) / sizeof(pts
[0]), fore
, back
);
1011 RectangleDraw(rc
, fore
, back
);
1015 #if GTK_MAJOR_VERSION >= 2
1017 // Plot a point into a guint32 buffer symetrically to all 4 qudrants
1018 static void AllFour(guint32
*pixels
, int stride
, int width
, int height
, int x
, int y
, guint32 val
) {
1019 pixels
[y
*stride
+x
] = val
;
1020 pixels
[y
*stride
+width
-1-x
] = val
;
1021 pixels
[(height
-1-y
)*stride
+x
] = val
;
1022 pixels
[(height
-1-y
)*stride
+width
-1-x
] = val
;
1025 static unsigned int GetRValue(unsigned int co
) {
1026 return (co
>> 16) & 0xff;
1029 static unsigned int GetGValue(unsigned int co
) {
1030 return (co
>> 8) & 0xff;
1033 static unsigned int GetBValue(unsigned int co
) {
1039 #if GTK_MAJOR_VERSION < 2
1040 void SurfaceImpl::AlphaRectangle(PRectangle rc
, int , ColourAllocated
, int , ColourAllocated outline
, int , int ) {
1041 if (gc
&& drawable
) {
1042 // Can't use GdkPixbuf on GTK+ 1.x, so draw an outline rather than use alpha.
1044 gdk_draw_rectangle(drawable
, gc
, 0,
1046 rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1);
1050 void SurfaceImpl::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourAllocated fill
, int alphaFill
,
1051 ColourAllocated outline
, int alphaOutline
, int flags
) {
1052 if (gc
&& drawable
&& rc
.Width() > 0) {
1053 int width
= rc
.Width();
1054 int height
= rc
.Height();
1055 // Ensure not distorted too much by corners when small
1056 cornerSize
= Platform::Minimum(cornerSize
, (Platform::Minimum(width
, height
) / 2) - 2);
1057 // Make a 32 bit deep pixbuf with alpha
1058 GdkPixbuf
*pixalpha
= gdk_pixbuf_new(GDK_COLORSPACE_RGB
, TRUE
, 8, width
, height
);
1060 guint8 pixVal
[4] = {0};
1061 guint32 valEmpty
= *(reinterpret_cast<guint32
*>(pixVal
));
1062 pixVal
[0] = GetRValue(fill
.AsLong());
1063 pixVal
[1] = GetGValue(fill
.AsLong());
1064 pixVal
[2] = GetBValue(fill
.AsLong());
1065 pixVal
[3] = alphaFill
;
1066 guint32 valFill
= *(reinterpret_cast<guint32
*>(pixVal
));
1067 pixVal
[0] = GetRValue(outline
.AsLong());
1068 pixVal
[1] = GetGValue(outline
.AsLong());
1069 pixVal
[2] = GetBValue(outline
.AsLong());
1070 pixVal
[3] = alphaOutline
;
1071 guint32 valOutline
= *(reinterpret_cast<guint32
*>(pixVal
));
1072 guint32
*pixels
= reinterpret_cast<guint32
*>(gdk_pixbuf_get_pixels(pixalpha
));
1073 int stride
= gdk_pixbuf_get_rowstride(pixalpha
) / 4;
1074 for (int yr
=0; yr
<height
; yr
++) {
1075 for (int xr
=0; xr
<width
; xr
++) {
1076 if ((xr
==0) || (xr
==width
-1) || (yr
== 0) || (yr
== height
-1)) {
1077 pixels
[yr
*stride
+xr
] = valOutline
;
1079 pixels
[yr
*stride
+xr
] = valFill
;
1083 for (int c
=0;c
<cornerSize
; c
++) {
1084 for (int xr
=0;xr
<c
+1; xr
++) {
1085 AllFour(pixels
, stride
, width
, height
, xr
, c
-xr
, valEmpty
);
1088 for (int xr
=1;xr
<cornerSize
; xr
++) {
1089 AllFour(pixels
, stride
, width
, height
, xr
, cornerSize
-xr
, valOutline
);
1093 gdk_draw_pixbuf(drawable
, gc
, pixalpha
,
1094 0,0, rc
.left
,rc
.top
, width
,height
, GDK_RGB_DITHER_NORMAL
, 0, 0);
1096 g_object_unref(pixalpha
);
1101 void SurfaceImpl::Ellipse(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
) {
1103 gdk_draw_arc(drawable
, gc
, 1,
1104 rc
.left
+ 1, rc
.top
+ 1,
1105 rc
.right
- rc
.left
- 2, rc
.bottom
- rc
.top
- 2,
1108 // The subtraction of 1 here is similar to the case for RectangleDraw
1110 gdk_draw_arc(drawable
, gc
, 0,
1112 rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1,
1116 void SurfaceImpl::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
1117 if (static_cast<SurfaceImpl
&>(surfaceSource
).drawable
) {
1118 gdk_draw_pixmap(drawable
,
1120 static_cast<SurfaceImpl
&>(surfaceSource
).drawable
,
1123 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
1127 static size_t UTF8Len(char ch
) {
1128 unsigned char uch
= static_cast<unsigned char>(ch
);
1131 else if (uch
< (0x80 + 0x40 + 0x20))
1137 char *UTF8FromLatin1(const char *s
, int &len
) {
1138 char *utfForm
= new char[len
*2+1];
1140 for (int i
=0;i
<len
;i
++) {
1141 unsigned int uch
= static_cast<unsigned char>(s
[i
]);
1143 utfForm
[lenU
++] = uch
;
1145 utfForm
[lenU
++] = static_cast<char>(0xC0 | (uch
>> 6));
1146 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
1149 utfForm
[lenU
] = '\0';
1155 static char *UTF8FromIconv(const Converter
&conv
, const char *s
, int &len
) {
1157 char *utfForm
= new char[len
*3+1];
1158 char *pin
= const_cast<char *>(s
);
1159 size_t inLeft
= len
;
1160 char *pout
= utfForm
;
1161 size_t outLeft
= len
*3+1;
1162 size_t conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
1163 if (conversions
!= ((size_t)(-1))) {
1165 len
= pout
- utfForm
;
1173 // Work out how many bytes are in a character by trying to convert using iconv,
1174 // returning the first length that succeeds.
1175 static size_t MultiByteLenFromIconv(const Converter
&conv
, const char *s
, size_t len
) {
1176 for (size_t lenMB
=1; (lenMB
<4) && (lenMB
<= len
); lenMB
++) {
1178 char *pin
= const_cast<char *>(s
);
1179 size_t inLeft
= lenMB
;
1180 char *pout
= wcForm
;
1182 size_t conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
1183 if (conversions
!= ((size_t)(-1))) {
1190 static char *UTF8FromGdkWChar(GdkWChar
*wctext
, int wclen
) {
1191 char *utfForm
= new char[wclen
*3+1]; // Maximum of 3 UTF-8 bytes per character
1193 for (int i
= 0; i
< wclen
&& wctext
[i
]; i
++) {
1194 unsigned int uch
= wctext
[i
];
1196 utfForm
[lenU
++] = static_cast<char>(uch
);
1197 } else if (uch
< 0x800) {
1198 utfForm
[lenU
++] = static_cast<char>(0xC0 | (uch
>> 6));
1199 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
1201 utfForm
[lenU
++] = static_cast<char>(0xE0 | (uch
>> 12));
1202 utfForm
[lenU
++] = static_cast<char>(0x80 | ((uch
>> 6) & 0x3f));
1203 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
1206 utfForm
[lenU
] = '\0';
1210 static char *UTF8FromDBCS(const char *s
, int &len
) {
1211 GdkWChar
*wctext
= new GdkWChar
[len
+ 1];
1212 GdkWChar
*wcp
= wctext
;
1213 int wclen
= gdk_mbstowcs(wcp
, s
, len
);
1215 // In the annoying case when non-locale chars in the line.
1216 // e.g. latin1 chars in Japanese locale.
1221 char *utfForm
= UTF8FromGdkWChar(wctext
, wclen
);
1223 len
= strlen(utfForm
);
1227 static size_t UTF8CharLength(const char *s
) {
1228 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1229 unsigned char ch
= *us
;
1232 } else if (ch
< 0x80 + 0x40 + 0x20) {
1241 // On GTK+, wchar_t is 4 bytes
1243 const int maxLengthTextRun
= 10000;
1245 void SurfaceImpl::DrawTextBase(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1246 ColourAllocated fore
) {
1248 if (gc
&& drawable
) {
1249 int xText
= rc
.left
;
1251 if (PFont(font_
)->pfd
) {
1253 bool useGFree
= false;
1255 pango_layout_set_text(layout
, s
, len
);
1258 SetConverter(PFont(font_
)->characterSet
);
1259 utfForm
= UTF8FromIconv(conv
, s
, len
);
1261 if (!utfForm
) { // iconv failed so try DBCS if DBCS mode
1264 utfForm
= UTF8FromDBCS(s
, len
);
1267 if (!utfForm
) { // iconv and DBCS failed so treat as Latin1
1268 utfForm
= UTF8FromLatin1(s
, len
);
1270 pango_layout_set_text(layout
, utfForm
, len
);
1272 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
1273 PangoLayoutLine
*pll
= pango_layout_get_line(layout
,0);
1274 gdk_draw_layout_line(drawable
, gc
, xText
, ybase
, pll
);
1283 // Draw text as a series of segments to avoid limitations in X servers
1284 const int segmentLength
= 1000;
1285 bool draw8bit
= true;
1286 if (et
!= singleByte
) {
1287 GdkWChar wctext
[maxLengthTextRun
];
1288 if (len
>= maxLengthTextRun
)
1289 len
= maxLengthTextRun
-1;
1292 wclen
= UTF16FromUTF8(s
, len
,
1293 static_cast<wchar_t *>(static_cast<void *>(wctext
)), maxLengthTextRun
- 1);
1294 } else { // dbcs, so convert using current locale
1295 char sMeasure
[maxLengthTextRun
];
1296 memcpy(sMeasure
, s
, len
);
1297 sMeasure
[len
] = '\0';
1298 wclen
= gdk_mbstowcs(
1299 wctext
, sMeasure
, maxLengthTextRun
- 1);
1303 wctext
[wclen
] = L
'\0';
1304 GdkWChar
*wcp
= wctext
;
1305 while ((wclen
> 0) && (xText
< maxCoordinate
)) {
1306 int lenDraw
= Platform::Minimum(wclen
, segmentLength
);
1307 gdk_draw_text_wc(drawable
, PFont(font_
)->pfont
, gc
,
1308 xText
, ybase
, wcp
, lenDraw
);
1310 if (wclen
> 0) { // Avoid next calculation if possible as may be expensive
1311 xText
+= gdk_text_width_wc(PFont(font_
)->pfont
,
1319 while ((len
> 0) && (xText
< maxCoordinate
)) {
1320 int lenDraw
= Platform::Minimum(len
, segmentLength
);
1321 gdk_draw_text(drawable
, PFont(font_
)->pfont
, gc
,
1322 xText
, ybase
, s
, lenDraw
);
1324 if (len
> 0) { // Avoid next calculation if possible as may be expensive
1325 xText
+= gdk_text_width(PFont(font_
)->pfont
, s
, lenDraw
);
1333 void SurfaceImpl::DrawTextNoClip(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1334 ColourAllocated fore
, ColourAllocated back
) {
1335 FillRectangle(rc
, back
);
1336 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
1339 // On GTK+, exactly same as DrawTextNoClip
1340 void SurfaceImpl::DrawTextClipped(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1341 ColourAllocated fore
, ColourAllocated back
) {
1342 FillRectangle(rc
, back
);
1343 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
1346 void SurfaceImpl::DrawTextTransparent(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1347 ColourAllocated fore
) {
1348 // Avoid drawing spaces in transparent mode
1349 for (int i
=0;i
<len
;i
++) {
1351 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
1357 void SurfaceImpl::MeasureWidths(Font
&font_
, const char *s
, int len
, int *positions
) {
1358 if (font_
.GetID()) {
1361 const int lenPositions
= len
;
1362 if (PFont(font_
)->pfd
) {
1364 int width
= PFont(font_
)->CharWidth(*s
, et
);
1366 positions
[0] = width
;
1371 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
1373 // Simple and direct as UTF-8 is native Pango encoding
1374 pango_layout_set_text(layout
, s
, len
);
1375 PangoLayoutIter
*iter
= pango_layout_get_iter(layout
);
1376 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
1378 while (pango_layout_iter_next_cluster(iter
)) {
1379 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
1380 int position
= PANGO_PIXELS(pos
.x
);
1381 int curIndex
= pango_layout_iter_get_index(iter
);
1382 int places
= curIndex
- i
;
1383 int distance
= position
- positions
[i
-1];
1384 while (i
< curIndex
) {
1385 // Evenly distribute space among bytes of this cluster.
1386 // Would be better to find number of characters and then
1387 // divide evenly between characters with each byte of a character
1388 // being at the same position.
1389 positions
[i
] = position
- (curIndex
- 1 - i
) * distance
/ places
;
1393 while (i
< lenPositions
)
1394 positions
[i
++] = PANGO_PIXELS(pos
.x
+ pos
.width
);
1395 pango_layout_iter_free(iter
);
1396 PLATFORM_ASSERT(i
== lenPositions
);
1398 int positionsCalculated
= 0;
1400 SetConverter(PFont(font_
)->characterSet
);
1401 char *utfForm
= UTF8FromIconv(conv
, s
, len
);
1403 // Convert to UTF-8 so can ask Pango for widths, then
1404 // Loop through UTF-8 and DBCS forms, taking account of different
1405 // character byte lengths.
1406 Converter
convMeasure("UCS-2", CharacterSetID(characterSet
), false);
1407 pango_layout_set_text(layout
, utfForm
, strlen(utfForm
));
1410 PangoLayoutIter
*iter
= pango_layout_get_iter(layout
);
1411 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
1412 while (pango_layout_iter_next_cluster(iter
)) {
1413 pango_layout_iter_get_cluster_extents (iter
, NULL
, &pos
);
1414 int position
= PANGO_PIXELS(pos
.x
);
1415 int utfIndexNext
= pango_layout_iter_get_index(iter
);
1416 while (utfIndex
< utfIndexNext
) {
1417 size_t lenChar
= MultiByteLenFromIconv(convMeasure
, s
+i
, len
-i
);
1418 //size_t lenChar = mblen(s+i, MB_CUR_MAX);
1420 positions
[i
++] = position
;
1421 positionsCalculated
++;
1423 utfIndex
+= UTF8CharLength(utfForm
+utfIndex
);
1426 while (i
< lenPositions
)
1427 positions
[i
++] = PANGO_PIXELS(pos
.x
+ pos
.width
);
1428 pango_layout_iter_free(iter
);
1430 PLATFORM_ASSERT(i
== lenPositions
);
1433 if (positionsCalculated
< 1 ) {
1434 // Either Latin1 or DBCS conversion failed so treat as Latin1.
1435 bool useGFree
= false;
1436 SetConverter(PFont(font_
)->characterSet
);
1437 char *utfForm
= UTF8FromIconv(conv
, s
, len
);
1439 utfForm
= UTF8FromLatin1(s
, len
);
1441 pango_layout_set_text(layout
, utfForm
, len
);
1442 PangoLayoutIter
*iter
= pango_layout_get_iter(layout
);
1443 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
1445 int positionStart
= 0;
1446 int clusterStart
= 0;
1447 // Each Latin1 input character may take 1 or 2 bytes in UTF-8
1448 // and groups of up to 3 may be represented as ligatures.
1449 while (pango_layout_iter_next_cluster(iter
)) {
1450 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
1451 int position
= PANGO_PIXELS(pos
.x
);
1452 int distance
= position
- positionStart
;
1453 int clusterEnd
= pango_layout_iter_get_index(iter
);
1454 int ligatureLength
= g_utf8_strlen(utfForm
+ clusterStart
, clusterEnd
- clusterStart
);
1455 PLATFORM_ASSERT(ligatureLength
> 0 && ligatureLength
<= 3);
1456 for (int charInLig
=0; charInLig
<ligatureLength
; charInLig
++) {
1457 positions
[i
++] = position
- (ligatureLength
- 1 - charInLig
) * distance
/ ligatureLength
;
1459 positionStart
= position
;
1460 clusterStart
= clusterEnd
;
1462 while (i
< lenPositions
)
1463 positions
[i
++] = PANGO_PIXELS(pos
.x
+ pos
.width
);
1464 pango_layout_iter_free(iter
);
1470 PLATFORM_ASSERT(i
== lenPositions
);
1474 PFont(font_
)->SetCharWidth(*s
, positions
[0], et
);
1479 GdkFont
*gf
= PFont(font_
)->pfont
;
1480 bool measure8bit
= true;
1481 if (et
!= singleByte
) {
1482 GdkWChar wctext
[maxLengthTextRun
];
1483 if (len
>= maxLengthTextRun
)
1484 len
= maxLengthTextRun
-1;
1487 wclen
= UTF16FromUTF8(s
, len
,
1488 static_cast<wchar_t *>(static_cast<void *>(wctext
)), maxLengthTextRun
- 1);
1489 } else { // dbcsMode, so convert using current locale
1490 char sDraw
[maxLengthTextRun
];
1491 memcpy(sDraw
, s
, len
);
1493 wclen
= gdk_mbstowcs(
1494 wctext
, sDraw
, maxLengthTextRun
- 1);
1497 measure8bit
= false;
1498 wctext
[wclen
] = L
'\0';
1499 // Map widths back to utf-8 or DBCS input string
1501 for (int iU
= 0; iU
< wclen
; iU
++) {
1502 int width
= gdk_char_width_wc(gf
, wctext
[iU
]);
1503 totalWidth
+= width
;
1506 lenChar
= UTF8Len(s
[i
]);
1508 lenChar
= mblen(s
+i
, MB_CUR_MAX
);
1513 positions
[i
++] = totalWidth
;
1516 while (i
< len
) { // In case of problems with lengths
1517 positions
[i
++] = totalWidth
;
1522 // Either Latin1 or conversion failed so treat as Latin1.
1523 for (int i
= 0; i
< len
; i
++) {
1524 int width
= gdk_char_width(gf
, s
[i
]);
1525 totalWidth
+= width
;
1526 positions
[i
] = totalWidth
;
1530 // No font so return an ascending range of values
1531 for (int i
= 0; i
< len
; i
++) {
1532 positions
[i
] = i
+ 1;
1537 int SurfaceImpl::WidthText(Font
&font_
, const char *s
, int len
) {
1538 if (font_
.GetID()) {
1540 if (PFont(font_
)->pfd
) {
1542 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
1544 bool useGFree
= false;
1546 pango_layout_set_text(layout
, s
, len
);
1550 utfForm
= UTF8FromDBCS(s
, len
);
1552 if (!utfForm
) { // DBCS failed so treat as iconv
1553 SetConverter(PFont(font_
)->characterSet
);
1554 utfForm
= UTF8FromIconv(conv
, s
, len
);
1556 if (!utfForm
) { // g_locale_to_utf8 failed so treat as Latin1
1557 utfForm
= UTF8FromLatin1(s
, len
);
1559 pango_layout_set_text(layout
, utfForm
, len
);
1561 PangoLayoutLine
*pangoLine
= pango_layout_get_line(layout
, 0);
1562 pango_layout_line_get_extents(pangoLine
, NULL
, &pos
);
1568 return PANGO_PIXELS(pos
.width
);
1572 GdkWChar wctext
[maxLengthTextRun
];
1573 size_t wclen
= UTF16FromUTF8(s
, len
, static_cast<wchar_t *>(static_cast<void *>(wctext
)),
1574 sizeof(wctext
) / sizeof(GdkWChar
) - 1);
1575 wctext
[wclen
] = L
'\0';
1576 return gdk_text_width_wc(PFont(font_
)->pfont
, wctext
, wclen
);
1578 return gdk_text_width(PFont(font_
)->pfont
, s
, len
);
1585 int SurfaceImpl::WidthChar(Font
&font_
, char ch
) {
1586 if (font_
.GetID()) {
1588 if (PFont(font_
)->pfd
) {
1589 return WidthText(font_
, &ch
, 1);
1592 return gdk_char_width(PFont(font_
)->pfont
, ch
);
1598 // Three possible strategies for determining ascent and descent of font:
1599 // 1) Call gdk_string_extents with string containing all letters, numbers and punctuation.
1600 // 2) Use the ascent and descent fields of GdkFont.
1601 // 3) Call gdk_string_extents with string as 1 but also including accented capitals.
1602 // Smallest values given by 1 and largest by 3 with 2 in between.
1603 // Techniques 1 and 2 sometimes chop off extreme portions of ascenders and
1604 // descenders but are mostly OK except for accented characters like Ã… which are
1605 // rarely used in code.
1607 // This string contains a good range of characters to test for size.
1608 //const char largeSizeString[] = "ÂÃÅÄ `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1609 // "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1611 const char sizeString
[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1612 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1615 int SurfaceImpl::Ascent(Font
&font_
) {
1616 if (!(font_
.GetID()))
1620 int ascent
= PFont(font_
)->ascent
;
1622 if ((ascent
== 0) && (PFont(font_
)->pfd
)) {
1623 PangoFontMetrics
*metrics
= pango_context_get_metrics(pcontext
,
1624 PFont(font_
)->pfd
, pango_context_get_language(pcontext
));
1625 PFont(font_
)->ascent
=
1626 PANGO_PIXELS(pango_font_metrics_get_ascent(metrics
));
1627 pango_font_metrics_unref(metrics
);
1628 ascent
= PFont(font_
)->ascent
;
1631 if ((ascent
== 0) && (PFont(font_
)->pfont
)) {
1632 ascent
= PFont(font_
)->pfont
->ascent
;
1647 gdk_string_extents(PFont(font_
)->pfont
, sizeString
,
1648 &lbearing
, &rbearing
, &width
, &ascent
, &descent
);
1653 int SurfaceImpl::Descent(Font
&font_
) {
1654 if (!(font_
.GetID()))
1659 if (PFont(font_
)->pfd
) {
1660 PangoFontMetrics
*metrics
= pango_context_get_metrics(pcontext
,
1661 PFont(font_
)->pfd
, pango_context_get_language(pcontext
));
1662 int descent
= PANGO_PIXELS(pango_font_metrics_get_descent(metrics
));
1663 pango_font_metrics_unref(metrics
);
1667 return PFont(font_
)->pfont
->descent
;
1676 gdk_string_extents(PFont(font_
)->pfont
, sizeString
,
1677 &lbearing
, &rbearing
, &width
, &ascent
, &descent
);
1682 int SurfaceImpl::InternalLeading(Font
&) {
1686 int SurfaceImpl::ExternalLeading(Font
&) {
1690 int SurfaceImpl::Height(Font
&font_
) {
1691 return Ascent(font_
) + Descent(font_
);
1694 int SurfaceImpl::AverageCharWidth(Font
&font_
) {
1695 return WidthChar(font_
, 'n');
1698 int SurfaceImpl::SetPalette(Palette
*, bool) {
1699 // Handled in palette allocation for GTK so this does nothing
1703 void SurfaceImpl::SetClip(PRectangle rc
) {
1704 GdkRectangle area
= {rc
.left
, rc
.top
,
1705 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
};
1706 gdk_gc_set_clip_rectangle(gc
, &area
);
1709 void SurfaceImpl::FlushCachedState() {}
1711 void SurfaceImpl::SetUnicodeMode(bool unicodeMode_
) {
1716 void SurfaceImpl::SetDBCSMode(int codePage
) {
1717 if (codePage
&& (codePage
!= SC_CP_UTF8
))
1721 Surface
*Surface::Allocate() {
1722 return new SurfaceImpl
;
1725 Window::~Window() {}
1727 void Window::Destroy() {
1729 gtk_widget_destroy(GTK_WIDGET(id
));
1733 bool Window::HasFocus() {
1734 return GTK_WIDGET_HAS_FOCUS(id
);
1737 PRectangle
Window::GetPosition() {
1738 // Before any size allocated pretend its 1000 wide so not scrolled
1739 PRectangle
rc(0, 0, 1000, 1000);
1741 rc
.left
= PWidget(id
)->allocation
.x
;
1742 rc
.top
= PWidget(id
)->allocation
.y
;
1743 if (PWidget(id
)->allocation
.width
> 20) {
1744 rc
.right
= rc
.left
+ PWidget(id
)->allocation
.width
;
1745 rc
.bottom
= rc
.top
+ PWidget(id
)->allocation
.height
;
1751 void Window::SetPosition(PRectangle rc
) {
1753 //gtk_widget_set_uposition(id, rc.left, rc.top);
1754 GtkAllocation alloc
;
1757 alloc
.width
= rc
.Width();
1758 alloc
.height
= rc
.Height();
1759 gtk_widget_size_allocate(PWidget(id
), &alloc
);
1762 gtk_widget_set_uposition(id
, rc
.left
, rc
.top
);
1763 gtk_widget_set_usize(id
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
1767 void Window::SetPositionRelative(PRectangle rc
, Window relativeTo
) {
1770 gdk_window_get_origin(PWidget(relativeTo
.id
)->window
, &ox
, &oy
);
1778 /* do some corrections to fit into screen */
1779 int sizex
= rc
.right
- rc
.left
;
1780 int sizey
= rc
.bottom
- rc
.top
;
1781 int screenWidth
= gdk_screen_width();
1782 int screenHeight
= gdk_screen_height();
1783 if (sizex
> screenWidth
)
1784 ox
= 0; /* the best we can do */
1785 else if (ox
+ sizex
> screenWidth
)
1786 ox
= screenWidth
- sizex
;
1787 if (oy
+ sizey
> screenHeight
)
1788 oy
= screenHeight
- sizey
;
1790 gtk_widget_set_uposition(PWidget(id
), ox
, oy
);
1793 GtkAllocation alloc
;
1794 alloc
.x
= rc
.left
+ ox
;
1795 alloc
.y
= rc
.top
+ oy
;
1796 alloc
.width
= rc
.right
- rc
.left
;
1797 alloc
.height
= rc
.bottom
- rc
.top
;
1798 gtk_widget_size_allocate(id
, &alloc
);
1800 gtk_widget_set_usize(PWidget(id
), sizex
, sizey
);
1803 PRectangle
Window::GetClientPosition() {
1804 // On GTK+, the client position is the window position
1805 return GetPosition();
1808 void Window::Show(bool show
) {
1810 gtk_widget_show(PWidget(id
));
1813 void Window::InvalidateAll() {
1815 gtk_widget_queue_draw(PWidget(id
));
1819 void Window::InvalidateRectangle(PRectangle rc
) {
1821 gtk_widget_queue_draw_area(PWidget(id
),
1823 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
1827 void Window::SetFont(Font
&) {
1828 // Can not be done generically but only needed for ListBox
1831 void Window::SetCursor(Cursor curs
) {
1832 // We don't set the cursor to same value numerous times under gtk because
1833 // it stores the cursor in the window once it's set
1834 if (curs
== cursorLast
)
1841 gdkCurs
= gdk_cursor_new(GDK_XTERM
);
1844 gdkCurs
= gdk_cursor_new(GDK_LEFT_PTR
);
1847 gdkCurs
= gdk_cursor_new(GDK_CENTER_PTR
);
1850 gdkCurs
= gdk_cursor_new(GDK_WATCH
);
1853 gdkCurs
= gdk_cursor_new(GDK_HAND2
);
1855 case cursorReverseArrow
:
1856 gdkCurs
= gdk_cursor_new(GDK_RIGHT_PTR
);
1859 gdkCurs
= gdk_cursor_new(GDK_LEFT_PTR
);
1860 cursorLast
= cursorArrow
;
1864 if (PWidget(id
)->window
)
1865 gdk_window_set_cursor(PWidget(id
)->window
, gdkCurs
);
1866 gdk_cursor_destroy(gdkCurs
);
1869 void Window::SetTitle(const char *s
) {
1870 gtk_window_set_title(GTK_WINDOW(id
), s
);
1873 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
1874 gdk window coordinates */
1875 PRectangle
Window::GetMonitorRect(Point pt
) {
1876 gint x_offset
, y_offset
;
1879 gdk_window_get_origin(PWidget(id
)->window
, &x_offset
, &y_offset
);
1882 #if GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 2)
1888 screen
= gtk_widget_get_screen(PWidget(id
));
1889 monitor_num
= gdk_screen_get_monitor_at_point(screen
, pt
.x
+ x_offset
, pt
.y
+ y_offset
);
1890 gdk_screen_get_monitor_geometry(screen
, monitor_num
, &rect
);
1893 return PRectangle(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
1896 return PRectangle(-x_offset
, -y_offset
, (-x_offset
) + gdk_screen_width(),
1897 (-y_offset
) + gdk_screen_height());
1902 const char *xpm_data
;
1903 #if GTK_MAJOR_VERSION < 2
1911 static void list_image_free(gpointer
, gpointer value
, gpointer
) {
1912 ListImage
*list_image
= (ListImage
*) value
;
1913 #if GTK_MAJOR_VERSION < 2
1914 if (list_image
->pixmap
)
1915 gdk_pixmap_unref(list_image
->pixmap
);
1916 if (list_image
->bitmap
)
1917 gdk_bitmap_unref(list_image
->bitmap
);
1919 if (list_image
->pixbuf
)
1920 gdk_pixbuf_unref (list_image
->pixbuf
);
1925 ListBox::ListBox() {
1928 ListBox::~ListBox() {
1931 #if GTK_MAJOR_VERSION >= 2
1939 class ListBoxX
: public ListBox
{
1942 #if GTK_MAJOR_VERSION < 2
1949 int desiredVisibleRows
;
1950 unsigned int maxItemCharacters
;
1951 unsigned int aveCharWidth
;
1953 CallBackAction doubleClickAction
;
1954 void *doubleClickActionData
;
1956 ListBoxX() : list(0), pixhash(NULL
), desiredVisibleRows(5), maxItemCharacters(0),
1957 doubleClickAction(NULL
), doubleClickActionData(NULL
) {
1958 #if GTK_MAJOR_VERSION < 2
1962 virtual ~ListBoxX() {
1964 g_hash_table_foreach((GHashTable
*) pixhash
, list_image_free
, NULL
);
1965 g_hash_table_destroy((GHashTable
*) pixhash
);
1968 virtual void SetFont(Font
&font
);
1969 virtual void Create(Window
&parent
, int ctrlID
, Point location_
, int lineHeight_
, bool unicodeMode_
);
1970 virtual void SetAverageCharWidth(int width
);
1971 virtual void SetVisibleRows(int rows
);
1972 virtual int GetVisibleRows() const;
1973 virtual PRectangle
GetDesiredRect();
1974 virtual int CaretFromEdge();
1975 virtual void Clear();
1976 virtual void Append(char *s
, int type
= -1);
1977 virtual int Length();
1978 virtual void Select(int n
);
1979 virtual int GetSelection();
1980 virtual int Find(const char *prefix
);
1981 virtual void GetValue(int n
, char *value
, int len
);
1982 virtual void RegisterImage(int type
, const char *xpm_data
);
1983 virtual void ClearRegisteredImages();
1984 virtual void SetDoubleClickAction(CallBackAction action
, void *data
) {
1985 doubleClickAction
= action
;
1986 doubleClickActionData
= data
;
1988 virtual void SetList(const char *listText
, char separator
, char typesep
);
1991 ListBox
*ListBox::Allocate() {
1992 ListBoxX
*lb
= new ListBoxX();
1996 #if GTK_MAJOR_VERSION < 2
1997 static void UnselectionAC(GtkWidget
*, gint
, gint
,
1998 GdkEventButton
*, gpointer p
) {
1999 int *pi
= reinterpret_cast<int *>(p
);
2002 static void SelectionAC(GtkWidget
*, gint row
, gint
,
2003 GdkEventButton
*, gpointer p
) {
2004 int *pi
= reinterpret_cast<int *>(p
);
2009 static gboolean
ButtonPress(GtkWidget
*, GdkEventButton
* ev
, gpointer p
) {
2010 ListBoxX
* lb
= reinterpret_cast<ListBoxX
*>(p
);
2011 if (ev
->type
== GDK_2BUTTON_PRESS
&& lb
->doubleClickAction
!= NULL
) {
2012 lb
->doubleClickAction(lb
->doubleClickActionData
);
2019 #if GTK_MAJOR_VERSION >= 2
2020 /* Change the active color to the selected color so the listbox uses the color
2021 scheme that it would use if it had the focus. */
2022 static void StyleSet(GtkWidget
*w
, GtkStyle
*, void*) {
2025 g_return_if_fail(w
!= NULL
);
2027 /* Copy the selected color to active. Note that the modify calls will cause
2028 recursive calls to this function after the value is updated and w->style to
2029 be set to a new object */
2030 style
= gtk_widget_get_style(w
);
2033 if (!gdk_color_equal(&style
->base
[GTK_STATE_SELECTED
], &style
->base
[GTK_STATE_ACTIVE
]))
2034 gtk_widget_modify_base(w
, GTK_STATE_ACTIVE
, &style
->base
[GTK_STATE_SELECTED
]);
2036 style
= gtk_widget_get_style(w
);
2039 if (!gdk_color_equal(&style
->text
[GTK_STATE_SELECTED
], &style
->text
[GTK_STATE_ACTIVE
]))
2040 gtk_widget_modify_text(w
, GTK_STATE_ACTIVE
, &style
->text
[GTK_STATE_SELECTED
]);
2044 void ListBoxX::Create(Window
&, int, Point
, int, bool) {
2045 id
= gtk_window_new(GTK_WINDOW_POPUP
);
2047 GtkWidget
*frame
= gtk_frame_new(NULL
);
2048 gtk_widget_show(frame
);
2049 gtk_container_add(GTK_CONTAINER(GetID()), frame
);
2050 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
2051 gtk_container_set_border_width(GTK_CONTAINER(frame
), 0);
2053 scroller
= gtk_scrolled_window_new(NULL
, NULL
);
2054 gtk_container_set_border_width(GTK_CONTAINER(scroller
), 0);
2055 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller
),
2056 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
2057 gtk_container_add(GTK_CONTAINER(frame
), PWidget(scroller
));
2058 gtk_widget_show(PWidget(scroller
));
2060 #if GTK_MAJOR_VERSION < 2
2061 list
= gtk_clist_new(1);
2062 GtkWidget
*wid
= PWidget(list
); // No code inside the GTK_OBJECT macro
2063 gtk_widget_show(wid
);
2064 gtk_container_add(GTK_CONTAINER(PWidget(scroller
)), wid
);
2065 gtk_clist_set_column_auto_resize(GTK_CLIST(wid
), 0, TRUE
);
2066 gtk_clist_set_selection_mode(GTK_CLIST(wid
), GTK_SELECTION_BROWSE
);
2067 gtk_signal_connect(GTK_OBJECT(wid
), "unselect_row",
2068 GTK_SIGNAL_FUNC(UnselectionAC
), ¤t
);
2069 gtk_signal_connect(GTK_OBJECT(wid
), "select_row",
2070 GTK_SIGNAL_FUNC(SelectionAC
), ¤t
);
2071 gtk_signal_connect(GTK_OBJECT(wid
), "button_press_event",
2072 GTK_SIGNAL_FUNC(ButtonPress
), this);
2073 gtk_clist_set_shadow_type(GTK_CLIST(wid
), GTK_SHADOW_NONE
);
2075 /* Tree and its model */
2076 GtkListStore
*store
=
2077 gtk_list_store_new(N_COLUMNS
, GDK_TYPE_PIXBUF
, G_TYPE_STRING
);
2079 list
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
2080 g_signal_connect(G_OBJECT(list
), "style-set", G_CALLBACK(StyleSet
), NULL
);
2082 GtkTreeSelection
*selection
=
2083 gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
2084 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
2085 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list
), FALSE
);
2086 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list
), FALSE
);
2089 GtkTreeViewColumn
*column
= gtk_tree_view_column_new();
2090 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
2091 gtk_tree_view_column_set_title(column
, "Autocomplete");
2093 GtkCellRenderer
*renderer
= gtk_cell_renderer_pixbuf_new();
2094 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
2095 gtk_tree_view_column_add_attribute(column
, renderer
,
2096 "pixbuf", PIXBUF_COLUMN
);
2098 renderer
= gtk_cell_renderer_text_new();
2099 gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer
), 1);
2100 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
2101 gtk_tree_view_column_add_attribute(column
, renderer
,
2102 "text", TEXT_COLUMN
);
2104 gtk_tree_view_append_column(GTK_TREE_VIEW(list
), column
);
2105 if (g_object_class_find_property(G_OBJECT_GET_CLASS(list
), "fixed-height-mode"))
2106 g_object_set(G_OBJECT(list
), "fixed-height-mode", TRUE
, NULL
);
2108 GtkWidget
*wid
= PWidget(list
); // No code inside the G_OBJECT macro
2109 gtk_container_add(GTK_CONTAINER(PWidget(scroller
)), wid
);
2110 gtk_widget_show(wid
);
2111 g_signal_connect(G_OBJECT(wid
), "button_press_event",
2112 G_CALLBACK(ButtonPress
), this);
2114 gtk_widget_realize(PWidget(id
));
2117 void ListBoxX::SetFont(Font
&scint_font
) {
2118 #if GTK_MAJOR_VERSION < 2
2119 GtkStyle
*style
= gtk_widget_get_style(GTK_WIDGET(PWidget(list
)));
2120 if (!gdk_font_equal(style
->font
, PFont(scint_font
)->pfont
)) {
2121 style
= gtk_style_copy(style
);
2122 gdk_font_unref(style
->font
);
2123 style
->font
= PFont(scint_font
)->pfont
;
2124 gdk_font_ref(style
->font
);
2125 gtk_widget_set_style(GTK_WIDGET(PWidget(list
)), style
);
2126 gtk_style_unref(style
);
2129 // Only do for Pango font as there have been crashes for GDK fonts
2130 if (Created() && PFont(scint_font
)->pfd
) {
2131 // Current font is Pango font
2132 gtk_widget_modify_font(PWidget(list
), PFont(scint_font
)->pfd
);
2137 void ListBoxX::SetAverageCharWidth(int width
) {
2138 aveCharWidth
= width
;
2141 void ListBoxX::SetVisibleRows(int rows
) {
2142 desiredVisibleRows
= rows
;
2145 int ListBoxX::GetVisibleRows() const {
2146 return desiredVisibleRows
;
2149 PRectangle
ListBoxX::GetDesiredRect() {
2150 // Before any size allocated pretend its 100 wide so not scrolled
2151 PRectangle
rc(0, 0, 100, 100);
2153 int rows
= Length();
2154 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2155 rows
= desiredVisibleRows
;
2160 // First calculate height of the clist for our desired visible
2161 // row count otherwise it tries to expand to the total # of rows
2162 #if GTK_MAJOR_VERSION < 2
2163 int ythickness
= PWidget(list
)->style
->klass
->ythickness
;
2164 height
= (rows
* GTK_CLIST(list
)->row_height
2167 + GTK_CONTAINER(PWidget(list
))->border_width
));
2172 GtkTreeViewColumn
* column
=
2173 gtk_tree_view_get_column(GTK_TREE_VIEW(list
), 0);
2174 gtk_tree_view_column_cell_get_size(column
, NULL
,
2175 NULL
, NULL
, &row_width
, &row_height
);
2176 int ythickness
= PWidget(list
)->style
->ythickness
;
2177 height
= (rows
* row_height
2179 + GTK_CONTAINER(PWidget(list
))->border_width
+ 1));
2181 gtk_widget_set_usize(GTK_WIDGET(PWidget(list
)), -1, height
);
2183 // Get the size of the scroller because we set usize on the window
2184 gtk_widget_size_request(GTK_WIDGET(scroller
), &req
);
2185 rc
.right
= req
.width
;
2186 rc
.bottom
= req
.height
;
2188 gtk_widget_set_usize(GTK_WIDGET(list
), -1, -1);
2189 int width
= maxItemCharacters
;
2192 rc
.right
= width
* (aveCharWidth
+ aveCharWidth
/ 3);
2193 if (Length() > rows
)
2194 rc
.right
= rc
.right
+ 16;
2199 int ListBoxX::CaretFromEdge() {
2200 return 4 + xset
.GetWidth();
2203 void ListBoxX::Clear() {
2204 #if GTK_MAJOR_VERSION < 2
2205 gtk_clist_clear(GTK_CLIST(list
));
2207 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2208 gtk_list_store_clear(GTK_LIST_STORE(model
));
2210 maxItemCharacters
= 0;
2213 #if GTK_MAJOR_VERSION < 2
2214 static void init_pixmap(ListImage
*list_image
, GtkWidget
*window
) {
2216 static void init_pixmap(ListImage
*list_image
) {
2218 const char *textForm
= list_image
->xpm_data
;
2219 const char * const * xpm_lineform
= reinterpret_cast<const char * const *>(textForm
);
2220 const char **xpm_lineformfromtext
= 0;
2221 // The XPM data can be either in atext form as will be read from a file
2222 // or in a line form (array of char *) as will be used for images defined in code.
2223 // Test for text form and convert to line form
2224 if ((0 == memcmp(textForm
, "/* X", 4)) && (0 == memcmp(textForm
, "/* XPM */", 9))) {
2225 // Test done is two parts to avoid possibility of overstepping the memory
2226 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
2227 xpm_lineformfromtext
= XPM::LinesFormFromTextForm(textForm
);
2228 xpm_lineform
= xpm_lineformfromtext
;
2231 // Drop any existing pixmap/bitmap as data may have changed
2232 #if GTK_MAJOR_VERSION < 2
2233 if (list_image
->pixmap
)
2234 gdk_pixmap_unref(list_image
->pixmap
);
2235 list_image
->pixmap
= NULL
;
2236 if (list_image
->bitmap
)
2237 gdk_bitmap_unref(list_image
->bitmap
);
2238 list_image
->bitmap
= NULL
;
2240 list_image
->pixmap
= gdk_pixmap_colormap_create_from_xpm_d(NULL
2241 , gtk_widget_get_colormap(window
), &(list_image
->bitmap
), NULL
2242 , (gchar
**) xpm_lineform
);
2243 if (NULL
== list_image
->pixmap
) {
2244 if (list_image
->bitmap
)
2245 gdk_bitmap_unref(list_image
->bitmap
);
2246 list_image
->bitmap
= NULL
;
2249 if (list_image
->pixbuf
)
2250 gdk_pixbuf_unref(list_image
->pixbuf
);
2251 list_image
->pixbuf
=
2252 gdk_pixbuf_new_from_xpm_data((const gchar
**)xpm_lineform
);
2254 delete []xpm_lineformfromtext
;
2259 void ListBoxX::Append(char *s
, int type
) {
2260 ListImage
*list_image
= NULL
;
2261 if ((type
>= 0) && pixhash
) {
2262 list_image
= (ListImage
*) g_hash_table_lookup((GHashTable
*) pixhash
2263 , (gconstpointer
) GINT_TO_POINTER(type
));
2265 #if GTK_MAJOR_VERSION < 2
2266 char * szs
[] = { s
, NULL
};
2267 int rownum
= gtk_clist_append(GTK_CLIST(list
), szs
);
2269 if (NULL
== list_image
->pixmap
)
2270 init_pixmap(list_image
, (GtkWidget
*) list
);
2271 gtk_clist_set_pixtext(GTK_CLIST(list
), rownum
, 0, s
, SPACING
2272 , list_image
->pixmap
, list_image
->bitmap
);
2276 GtkListStore
*store
=
2277 GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list
)));
2278 gtk_list_store_append(GTK_LIST_STORE(store
), &iter
);
2280 if (NULL
== list_image
->pixbuf
)
2281 init_pixmap(list_image
);
2282 if (list_image
->pixbuf
) {
2283 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
2284 PIXBUF_COLUMN
, list_image
->pixbuf
,
2285 TEXT_COLUMN
, s
, -1);
2287 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
2288 TEXT_COLUMN
, s
, -1);
2291 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
2292 TEXT_COLUMN
, s
, -1);
2295 size_t len
= strlen(s
);
2296 if (maxItemCharacters
< len
)
2297 maxItemCharacters
= len
;
2300 int ListBoxX::Length() {
2302 #if GTK_MAJOR_VERSION < 2
2303 return GTK_CLIST(list
)->rows
;
2305 return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
2306 (GTK_TREE_VIEW(list
)), NULL
);
2311 void ListBoxX::Select(int n
) {
2312 #if GTK_MAJOR_VERSION < 2
2314 gtk_clist_unselect_row(GTK_CLIST(list
), current
, 0);
2316 gtk_clist_select_row(GTK_CLIST(list
), n
, 0);
2317 gtk_clist_moveto(GTK_CLIST(list
), n
, 0, 0.5, 0.5);
2321 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2322 GtkTreeSelection
*selection
=
2323 gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
2326 gtk_tree_selection_unselect_all(selection
);
2330 bool valid
= gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, n
) != FALSE
;
2332 gtk_tree_selection_select_iter(selection
, &iter
);
2334 // Move the scrollbar to show the selection.
2335 int total
= Length();
2336 GtkAdjustment
*adj
=
2337 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list
));
2338 gfloat value
= ((gfloat
)n
/ total
) * (adj
->upper
- adj
->lower
)
2339 + adj
->lower
- adj
->page_size
/ 2;
2344 GtkTreeViewColumn
* column
=
2345 gtk_tree_view_get_column(GTK_TREE_VIEW(list
), 0);
2346 gtk_tree_view_column_cell_get_size(column
, NULL
, NULL
,
2347 NULL
, &row_width
, &row_height
);
2349 int rows
= Length();
2350 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2351 rows
= desiredVisibleRows
;
2353 // Odd rows to display -- We are now in the middle.
2354 // Align it so that we don't chop off rows.
2355 value
+= (gfloat
)row_height
/ 2.0;
2358 value
= (value
< 0)? 0 : value
;
2359 value
= (value
> (adj
->upper
- adj
->page_size
))?
2360 (adj
->upper
- adj
->page_size
) : value
;
2363 gtk_adjustment_set_value(adj
, value
);
2365 gtk_tree_selection_unselect_all(selection
);
2370 int ListBoxX::GetSelection() {
2371 #if GTK_MAJOR_VERSION < 2
2375 GtkTreeModel
*model
;
2376 GtkTreeSelection
*selection
;
2377 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
2378 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
)) {
2379 GtkTreePath
*path
= gtk_tree_model_get_path(model
, &iter
);
2380 int *indices
= gtk_tree_path_get_indices(path
);
2381 // Don't free indices.
2389 int ListBoxX::Find(const char *prefix
) {
2390 #if GTK_MAJOR_VERSION < 2
2391 int count
= Length();
2392 for (int i
= 0; i
< count
; i
++) {
2394 gtk_clist_get_text(GTK_CLIST(list
), i
, 0, &s
);
2395 if (s
&& (0 == strncmp(prefix
, s
, strlen(prefix
)))) {
2401 GtkTreeModel
*model
=
2402 gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2403 bool valid
= gtk_tree_model_get_iter_first(model
, &iter
) != FALSE
;
2407 gtk_tree_model_get(model
, &iter
, TEXT_COLUMN
, &s
, -1);
2408 if (s
&& (0 == strncmp(prefix
, s
, strlen(prefix
)))) {
2411 valid
= gtk_tree_model_iter_next(model
, &iter
) != FALSE
;
2418 void ListBoxX::GetValue(int n
, char *value
, int len
) {
2420 #if GTK_MAJOR_VERSION < 2
2421 GtkCellType type
= gtk_clist_get_cell_type(GTK_CLIST(list
), n
, 0);
2424 gtk_clist_get_text(GTK_CLIST(list
), n
, 0, &text
);
2426 case GTK_CELL_PIXTEXT
:
2427 gtk_clist_get_pixtext(GTK_CLIST(list
), n
, 0, &text
, NULL
, NULL
, NULL
);
2434 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2435 bool valid
= gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, n
) != FALSE
;
2437 gtk_tree_model_get(model
, &iter
, TEXT_COLUMN
, &text
, -1);
2440 if (text
&& len
> 0) {
2441 strncpy(value
, text
, len
);
2442 value
[len
- 1] = '\0';
2448 // g_return_if_fail causes unnecessary compiler warning in release compile.
2450 #pragma warning(disable: 4127)
2453 void ListBoxX::RegisterImage(int type
, const char *xpm_data
) {
2454 g_return_if_fail(xpm_data
);
2456 // Saved and use the saved copy so caller's copy can disappear.
2457 xset
.Add(type
, xpm_data
);
2458 XPM
*pxpm
= xset
.Get(type
);
2459 xpm_data
= reinterpret_cast<const char *>(pxpm
->InLinesForm());
2462 pixhash
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
2464 ListImage
*list_image
= (ListImage
*) g_hash_table_lookup((GHashTable
*) pixhash
,
2465 (gconstpointer
) GINT_TO_POINTER(type
));
2467 // Drop icon already registered
2468 #if GTK_MAJOR_VERSION < 2
2469 if (list_image
->pixmap
)
2470 gdk_pixmap_unref(list_image
->pixmap
);
2471 list_image
->pixmap
= 0;
2472 if (list_image
->bitmap
)
2473 gdk_bitmap_unref(list_image
->bitmap
);
2474 list_image
->bitmap
= 0;
2476 if (list_image
->pixbuf
)
2477 gdk_pixbuf_unref(list_image
->pixbuf
);
2478 list_image
->pixbuf
= NULL
;
2480 list_image
->xpm_data
= xpm_data
;
2482 list_image
= g_new0(ListImage
, 1);
2483 list_image
->xpm_data
= xpm_data
;
2484 g_hash_table_insert((GHashTable
*) pixhash
, GINT_TO_POINTER(type
),
2485 (gpointer
) list_image
);
2489 void ListBoxX::ClearRegisteredImages() {
2493 void ListBoxX::SetList(const char *listText
, char separator
, char typesep
) {
2495 int count
= strlen(listText
) + 1;
2496 char *words
= new char[count
];
2498 memcpy(words
, listText
, count
);
2499 char *startword
= words
;
2500 char *numword
= NULL
;
2502 for (; words
[i
]; i
++) {
2503 if (words
[i
] == separator
) {
2507 Append(startword
, numword
?atoi(numword
+ 1):-1);
2508 startword
= words
+ i
+ 1;
2510 } else if (words
[i
] == typesep
) {
2511 numword
= words
+ i
;
2517 Append(startword
, numword
?atoi(numword
+ 1):-1);
2523 Menu::Menu() : id(0) {}
2525 void Menu::CreatePopUp() {
2527 id
= gtk_item_factory_new(GTK_TYPE_MENU
, "<main>", NULL
);
2530 void Menu::Destroy() {
2532 #if GTK_MAJOR_VERSION < 2
2533 gtk_object_unref(GTK_OBJECT(id
));
2535 g_object_unref(G_OBJECT(id
));
2540 void Menu::Show(Point pt
, Window
&) {
2541 int screenHeight
= gdk_screen_height();
2542 int screenWidth
= gdk_screen_width();
2543 GtkItemFactory
*factory
= reinterpret_cast<GtkItemFactory
*>(id
);
2544 GtkWidget
*widget
= gtk_item_factory_get_widget(factory
, "<main>");
2545 gtk_widget_show_all(widget
);
2546 GtkRequisition requisition
;
2547 gtk_widget_size_request(widget
, &requisition
);
2548 if ((pt
.x
+ requisition
.width
) > screenWidth
) {
2549 pt
.x
= screenWidth
- requisition
.width
;
2551 if ((pt
.y
+ requisition
.height
) > screenHeight
) {
2552 pt
.y
= screenHeight
- requisition
.height
;
2554 #if GTK_MAJOR_VERSION >= 2
2555 gtk_item_factory_popup(factory
, pt
.x
- 4, pt
.y
- 4, 3,
2556 gtk_get_current_event_time());
2558 gtk_item_factory_popup(factory
, pt
.x
- 4, pt
.y
- 4, 3, 0);
2562 ElapsedTime::ElapsedTime() {
2564 g_get_current_time(&curTime
);
2565 bigBit
= curTime
.tv_sec
;
2566 littleBit
= curTime
.tv_usec
;
2569 class DynamicLibraryImpl
: public DynamicLibrary
{
2573 DynamicLibraryImpl(const char *modulePath
) {
2574 m
= g_module_open(modulePath
, G_MODULE_BIND_LAZY
);
2577 virtual ~DynamicLibraryImpl() {
2582 // Use g_module_symbol to get a pointer to the relevant function.
2583 virtual Function
FindFunction(const char *name
) {
2585 gpointer fn_address
= NULL
;
2586 gboolean status
= g_module_symbol(m
, name
, &fn_address
);
2588 return static_cast<Function
>(fn_address
);
2595 virtual bool IsValid() {
2600 DynamicLibrary
*DynamicLibrary::Load(const char *modulePath
) {
2601 return static_cast<DynamicLibrary
*>( new DynamicLibraryImpl(modulePath
) );
2604 double ElapsedTime::Duration(bool reset
) {
2606 g_get_current_time(&curTime
);
2607 long endBigBit
= curTime
.tv_sec
;
2608 long endLittleBit
= curTime
.tv_usec
;
2609 double result
= 1000000.0 * (endBigBit
- bigBit
);
2610 result
+= endLittleBit
- littleBit
;
2611 result
/= 1000000.0;
2614 littleBit
= endLittleBit
;
2619 ColourDesired
Platform::Chrome() {
2620 return ColourDesired(0xe0, 0xe0, 0xe0);
2623 ColourDesired
Platform::ChromeHighlight() {
2624 return ColourDesired(0xff, 0xff, 0xff);
2627 const char *Platform::DefaultFont() {
2629 return "Lucida Console";
2634 return "lucidatypewriter";
2639 int Platform::DefaultFontSize() {
2647 unsigned int Platform::DoubleClickTime() {
2648 return 500; // Half a second
2651 bool Platform::MouseButtonBounce() {
2655 void Platform::DebugDisplay(const char *s
) {
2656 fprintf(stderr
, "%s", s
);
2659 bool Platform::IsKeyDown(int) {
2660 // TODO: discover state of keys in GTK+/X
2664 long Platform::SendScintilla(
2665 WindowID w
, unsigned int msg
, unsigned long wParam
, long lParam
) {
2666 return scintilla_send_message(SCINTILLA(w
), msg
, wParam
, lParam
);
2669 long Platform::SendScintillaPointer(
2670 WindowID w
, unsigned int msg
, unsigned long wParam
, void *lParam
) {
2671 return scintilla_send_message(SCINTILLA(w
), msg
, wParam
,
2672 reinterpret_cast<sptr_t
>(lParam
));
2675 bool Platform::IsDBCSLeadByte(int /* codePage */, char /* ch */) {
2679 int Platform::DBCSCharLength(int, const char *s
) {
2680 int bytes
= mblen(s
, MB_CUR_MAX
);
2687 int Platform::DBCSCharMaxLength() {
2692 // These are utility functions not really tied to a platform
2694 int Platform::Minimum(int a
, int b
) {
2701 int Platform::Maximum(int a
, int b
) {
2711 void Platform::DebugPrintf(const char *format
, ...) {
2714 va_start(pArguments
, format
);
2715 vsprintf(buffer
, format
, pArguments
);
2717 Platform::DebugDisplay(buffer
);
2720 void Platform::DebugPrintf(const char *, ...) {}
2724 // Not supported for GTK+
2725 static bool assertionPopUps
= true;
2727 bool Platform::ShowAssertionPopUps(bool assertionPopUps_
) {
2728 bool ret
= assertionPopUps
;
2729 assertionPopUps
= assertionPopUps_
;
2733 void Platform::Assert(const char *c
, const char *file
, int line
) {
2735 sprintf(buffer
, "Assertion [%s] failed at %s %d", c
, file
, line
);
2736 strcat(buffer
, "\r\n");
2737 Platform::DebugDisplay(buffer
);
2741 int Platform::Clamp(int val
, int minVal
, int maxVal
) {
2749 void Platform_Initialise() {
2750 FontMutexAllocate();
2753 void Platform_Finalise() {