1 // Scintilla source code edit control
2 // PlatGTK.cxx - implementation of platform facilities on GTK+/Linux
3 // Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
4 // The License.txt file describes the conditions under which this software may be distributed.
15 #include <gdk/gdkkeysyms.h>
19 #include "Scintilla.h"
20 #include "ScintillaWidget.h"
21 #include "UniConversion.h"
24 /* GLIB must be compiled with thread support, otherwise we
25 will bail on trying to use locks, and that could lead to
26 problems for someone. `glib-config --libs gthread` needs
27 to be used to get the glib libraries for linking, otherwise
28 g_thread_init will fail */
29 #define USE_LOCK defined(G_THREADS_ENABLED) && !defined(G_THREADS_IMPL_NONE)
30 /* Use fast way of getting char data on win32 to work around problems
31 with gdk_string_extents. */
34 #include "Converter.h"
37 // Ignore unreferenced local functions in GTK+ headers
38 #pragma warning(disable: 4505)
42 using namespace Scintilla
;
45 enum encodingType
{ singleByte
, UTF8
, dbcs
};
56 static GMutex
*fontMutex
= NULL
;
58 static void InitializeGLIBThreads() {
59 if (!g_thread_supported()) {
65 static void FontMutexAllocate() {
68 InitializeGLIBThreads();
69 fontMutex
= g_mutex_new();
74 static void FontMutexFree() {
77 g_mutex_free(fontMutex
);
83 static void FontMutexLock() {
85 g_mutex_lock(fontMutex
);
89 static void FontMutexUnlock() {
92 g_mutex_unlock(fontMutex
);
97 // On GTK+ 1.x holds a GdkFont* but on GTK+ 2.x can hold a GdkFont* or a
98 // PangoFontDescription*.
105 PangoFontDescription
*pfd
;
107 FontHandle(GdkFont
*pfont_
) {
115 FontHandle(PangoFontDescription
*pfd_
, int characterSet_
) {
120 characterSet
= characterSet_
;
125 gdk_font_unref(pfont
);
128 pango_font_description_free(pfd
);
131 void ResetWidths(encodingType et_
) {
133 for (int i
=0; i
<=127; i
++) {
137 int CharWidth(unsigned char ch
, encodingType et_
) {
140 if ((ch
<= 127) && (et
== et_
)) {
146 void SetCharWidth(unsigned char ch
, int w
, encodingType et_
) {
158 // X has a 16 bit coordinate space, so stop drawing here to avoid wrapping
159 static const int maxCoordinate
= 32000;
161 static FontHandle
*PFont(Font
&f
) {
162 return reinterpret_cast<FontHandle
*>(f
.GetID());
165 static GtkWidget
*PWidget(WindowID wid
) {
166 return reinterpret_cast<GtkWidget
*>(wid
);
169 static GtkWidget
*PWidget(Window
&w
) {
170 return PWidget(w
.GetID());
173 Point
Point::FromLong(long lpoint
) {
175 Platform::LowShortFromLong(lpoint
),
176 Platform::HighShortFromLong(lpoint
));
181 allowRealization
= false;
182 allocatedPalette
= 0;
185 entries
= new ColourPair
[size
];
188 Palette::~Palette() {
194 void Palette::Release() {
196 delete [](reinterpret_cast<GdkColor
*>(allocatedPalette
));
197 allocatedPalette
= 0;
201 entries
= new ColourPair
[size
];
204 // This method either adds a colour to the list of wanted colours (want==true)
205 // or retrieves the allocated colour back to the ColourPair.
206 // This is one method to make it easier to keep the code for wanting and retrieving in sync.
207 void Palette::WantFind(ColourPair
&cp
, bool want
) {
209 for (int i
=0; i
< used
; i
++) {
210 if (entries
[i
].desired
== cp
.desired
)
215 int sizeNew
= size
* 2;
216 ColourPair
*entriesNew
= new ColourPair
[sizeNew
];
217 for (int j
=0; j
<size
; j
++) {
218 entriesNew
[j
] = entries
[j
];
221 entries
= entriesNew
;
225 entries
[used
].desired
= cp
.desired
;
226 entries
[used
].allocated
.Set(cp
.desired
.AsLong());
229 for (int i
=0; i
< used
; i
++) {
230 if (entries
[i
].desired
== cp
.desired
) {
231 cp
.allocated
= entries
[i
].allocated
;
235 cp
.allocated
.Set(cp
.desired
.AsLong());
239 void Palette::Allocate(Window
&w
) {
240 if (allocatedPalette
) {
241 gdk_colormap_free_colors(gtk_widget_get_colormap(PWidget(w
)),
242 reinterpret_cast<GdkColor
*>(allocatedPalette
),
244 delete [](reinterpret_cast<GdkColor
*>(allocatedPalette
));
245 allocatedPalette
= 0;
248 GdkColor
*paletteNew
= new GdkColor
[used
];
249 allocatedPalette
= paletteNew
;
250 gboolean
*successPalette
= new gboolean
[used
];
254 for (iPal
= 0; iPal
< used
; iPal
++) {
255 paletteNew
[iPal
].red
= entries
[iPal
].desired
.GetRed() * (65535 / 255);
256 paletteNew
[iPal
].green
= entries
[iPal
].desired
.GetGreen() * (65535 / 255);
257 paletteNew
[iPal
].blue
= entries
[iPal
].desired
.GetBlue() * (65535 / 255);
258 paletteNew
[iPal
].pixel
= entries
[iPal
].desired
.AsLong();
260 gdk_colormap_alloc_colors(gtk_widget_get_colormap(PWidget(w
)),
261 paletteNew
, allocatedLen
, FALSE
, TRUE
,
263 for (iPal
= 0; iPal
< used
; iPal
++) {
264 entries
[iPal
].allocated
.Set(paletteNew
[iPal
].pixel
);
267 delete []successPalette
;
270 static const char *CharacterSetName(int characterSet
) {
271 switch (characterSet
) {
272 case SC_CHARSET_ANSI
:
274 case SC_CHARSET_DEFAULT
:
276 case SC_CHARSET_BALTIC
:
278 case SC_CHARSET_CHINESEBIG5
:
280 case SC_CHARSET_EASTEUROPE
:
282 case SC_CHARSET_GB2312
:
283 return "gb2312.1980-*";
284 case SC_CHARSET_GREEK
:
286 case SC_CHARSET_HANGUL
:
287 return "ksc5601.1987-*";
292 case SC_CHARSET_RUSSIAN
:
294 case SC_CHARSET_CYRILLIC
:
296 case SC_CHARSET_SHIFTJIS
:
297 return "jisx0208.1983-*";
298 case SC_CHARSET_SYMBOL
:
300 case SC_CHARSET_TURKISH
:
302 case SC_CHARSET_JOHAB
:
304 case SC_CHARSET_HEBREW
:
306 case SC_CHARSET_ARABIC
:
308 case SC_CHARSET_VIETNAMESE
:
310 case SC_CHARSET_THAI
:
312 case SC_CHARSET_8859_15
:
319 static bool IsDBCSCharacterSet(int characterSet
) {
320 switch (characterSet
) {
321 case SC_CHARSET_GB2312
:
322 case SC_CHARSET_HANGUL
:
323 case SC_CHARSET_SHIFTJIS
:
324 case SC_CHARSET_CHINESEBIG5
:
331 static void GenerateFontSpecStrings(const char *fontName
, int characterSet
,
332 char *foundary
, int foundary_len
,
333 char *faceName
, int faceName_len
,
334 char *charset
, int charset_len
) {
335 // supported font strings include:
336 // foundary-fontface-isoxxx-x
340 if (strchr(fontName
, '-')) {
342 char *d1
= NULL
, *d2
= NULL
, *d3
= NULL
;
343 strncpy(tmp
, fontName
, sizeof(tmp
) - 1);
344 tmp
[sizeof(tmp
) - 1] = '\0';
345 d1
= strchr(tmp
, '-');
346 // we know the first dash exists
347 d2
= strchr(d1
+ 1, '-');
349 d3
= strchr(d2
+ 1, '-');
351 // foundary-fontface-isoxxx-x
355 strncpy(faceName
, tmp
, foundary_len
- 1);
356 strncpy(charset
, d2
+ 1, charset_len
- 1);
360 strcpy(foundary
, "-*-");
361 strncpy(faceName
, tmp
, faceName_len
- 1);
362 strncpy(charset
, d1
+ 1, charset_len
- 1);
367 strncpy(faceName
, tmp
, faceName_len
- 1);
368 strncpy(charset
, CharacterSetName(characterSet
), charset_len
- 1);
371 strncpy(foundary
, "-*-", foundary_len
);
372 strncpy(faceName
, fontName
, faceName_len
- 1);
373 strncpy(charset
, CharacterSetName(characterSet
), charset_len
- 1);
377 static void SetLogFont(LOGFONT
&lf
, const char *faceName
, int characterSet
, int size
, bool bold
, bool italic
) {
378 memset(&lf
, 0, sizeof(lf
));
382 lf
.characterSet
= characterSet
;
383 strncpy(lf
.faceName
, faceName
, sizeof(lf
.faceName
) - 1);
387 * Create a hash from the parameters for a font to allow easy checking for identity.
388 * If one font is the same as another, its hash will be the same, but if the hash is the
389 * same then they may still be different.
391 static int HashFont(const char *faceName
, int characterSet
, int size
, bool bold
, bool italic
) {
394 (characterSet
<< 10) ^
395 (bold
? 0x10000000 : 0) ^
396 (italic
? 0x20000000 : 0) ^
400 class FontCached
: Font
{
405 FontCached(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
);
407 bool SameAs(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
);
408 virtual void Release();
409 static FontID
CreateNewFont(const char *fontName
, int characterSet
,
410 int size
, bool bold
, bool italic
);
411 static FontCached
*first
;
413 static FontID
FindOrCreate(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
);
414 static void ReleaseId(FontID fid_
);
417 FontCached
*FontCached::first
= 0;
419 FontCached::FontCached(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
) :
420 next(0), usage(0), hash(0) {
421 ::SetLogFont(lf
, faceName_
, characterSet_
, size_
, bold_
, italic_
);
422 hash
= HashFont(faceName_
, characterSet_
, size_
, bold_
, italic_
);
423 fid
= CreateNewFont(faceName_
, characterSet_
, size_
, bold_
, italic_
);
427 bool FontCached::SameAs(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
) {
431 lf
.italic
== italic_
&&
432 lf
.characterSet
== characterSet_
&&
433 0 == strcmp(lf
.faceName
, faceName_
);
436 void FontCached::Release() {
442 FontID
FontCached::FindOrCreate(const char *faceName_
, int characterSet_
, int size_
, bool bold_
, bool italic_
) {
445 int hashFind
= HashFont(faceName_
, characterSet_
, size_
, bold_
, italic_
);
446 for (FontCached
*cur
= first
; cur
; cur
= cur
->next
) {
447 if ((cur
->hash
== hashFind
) &&
448 cur
->SameAs(faceName_
, characterSet_
, size_
, bold_
, italic_
)) {
454 FontCached
*fc
= new FontCached(faceName_
, characterSet_
, size_
, bold_
, italic_
);
465 void FontCached::ReleaseId(FontID fid_
) {
467 FontCached
**pcur
= &first
;
468 for (FontCached
*cur
= first
; cur
; cur
= cur
->next
) {
469 if (cur
->fid
== fid_
) {
471 if (cur
->usage
== 0) {
484 static GdkFont
*LoadFontOrSet(const char *fontspec
, int characterSet
) {
485 if (IsDBCSCharacterSet(characterSet
)) {
486 return gdk_fontset_load(fontspec
);
488 return gdk_font_load(fontspec
);
492 FontID
FontCached::CreateNewFont(const char *fontName
, int characterSet
,
493 int size
, bool bold
, bool italic
) {
505 if (fontName
[0] == '!') {
506 PangoFontDescription
*pfd
= pango_font_description_new();
508 pango_font_description_set_family(pfd
, fontName
+1);
509 pango_font_description_set_size(pfd
, size
* PANGO_SCALE
);
510 pango_font_description_set_weight(pfd
, bold
? PANGO_WEIGHT_BOLD
: PANGO_WEIGHT_NORMAL
);
511 pango_font_description_set_style(pfd
, italic
? PANGO_STYLE_ITALIC
: PANGO_STYLE_NORMAL
);
512 return new FontHandle(pfd
, characterSet
);
517 // If name of the font begins with a '-', assume, that it is
519 if (fontName
[0] == '-') {
520 if (strchr(fontName
, ',') || IsDBCSCharacterSet(characterSet
)) {
521 newid
= gdk_fontset_load(fontName
);
523 newid
= gdk_font_load(fontName
);
526 // Font not available so substitute a reasonable code font
527 // iso8859 appears to only allow western characters.
528 newid
= LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
531 return new FontHandle(newid
);
534 // it's not a full fontspec, build one.
536 // This supports creating a FONT_SET
537 // in a method that allows us to also set size, slant and
538 // weight for the fontset. The expected input is multiple
539 // partial fontspecs seperated by comma
540 // eg. adobe-courier-iso10646-1,*-courier-iso10646-1,*-*-*-*
541 if (strchr(fontName
, ',')) {
542 // build a fontspec and use gdk_fontset_load
543 int remaining
= sizeof(fontset
);
544 char fontNameCopy
[1024];
545 strncpy(fontNameCopy
, fontName
, sizeof(fontNameCopy
) - 1);
546 char *fn
= fontNameCopy
;
547 char *fp
= strchr(fn
, ',');
549 const char *spec
= "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
550 if (fontset
[0] != '\0') {
551 // if this is not the first font in the list,
552 // append a comma seperator
553 spec
= ",%s%s%s%s-*-*-*-%0d-*-*-*-*-%s";
557 *fp
= '\0'; // nullify the comma
558 GenerateFontSpecStrings(fn
, characterSet
,
559 foundary
, sizeof(foundary
),
560 faceName
, sizeof(faceName
),
561 charset
, sizeof(charset
));
564 sizeof(fontspec
) - 1,
567 bold
? "-bold" : "-medium",
568 italic
? "-i" : "-r",
572 // if this is the first font in the list, and
573 // we are doing italic, add an oblique font
575 if (italic
&& fontset
[0] == '\0') {
576 strncat(fontset
, fontspec
, remaining
- 1);
577 remaining
-= strlen(fontset
);
580 sizeof(fontspec
) - 1,
581 ",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s",
583 bold
? "-bold" : "-medium",
588 strncat(fontset
, fontspec
, remaining
- 1);
589 remaining
-= strlen(fontset
);
595 fp
= strchr(fn
, ',');
598 newid
= gdk_fontset_load(fontset
);
600 return new FontHandle(newid
);
602 // if fontset load failed, fall through, we'll use
603 // the last font entry and continue to try and
604 // get something that matches
607 // single fontspec support
609 GenerateFontSpecStrings(fontName
, characterSet
,
610 foundary
, sizeof(foundary
),
611 faceName
, sizeof(faceName
),
612 charset
, sizeof(charset
));
615 sizeof(fontspec
) - 1,
616 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
618 bold
? "-bold" : "-medium",
619 italic
? "-i" : "-r",
622 newid
= LoadFontOrSet(fontspec
, characterSet
);
624 // some fonts have oblique, not italic
626 sizeof(fontspec
) - 1,
627 "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s",
629 bold
? "-bold" : "-medium",
630 italic
? "-o" : "-r",
633 newid
= LoadFontOrSet(fontspec
, characterSet
);
637 sizeof(fontspec
) - 1,
638 "-*-*-*-*-*-*-*-%0d-*-*-*-*-%s",
641 newid
= gdk_font_load(fontspec
);
644 // Font not available so substitute a reasonable code font
645 // iso8859 appears to only allow western characters.
646 newid
= LoadFontOrSet("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*",
649 return new FontHandle(newid
);
652 Font::Font() : fid(0) {}
656 void Font::Create(const char *faceName
, int characterSet
, int size
,
657 bool bold
, bool italic
, int) {
659 fid
= FontCached::FindOrCreate(faceName
, characterSet
, size
, bold
, italic
);
662 void Font::Release() {
664 FontCached::ReleaseId(fid
);
670 namespace Scintilla
{
672 class SurfaceImpl
: public Surface
{
674 GdkDrawable
*drawable
;
681 PangoContext
*pcontext
;
685 void SetConverter(int characterSet_
);
688 virtual ~SurfaceImpl();
690 void Init(WindowID wid
);
691 void Init(SurfaceID sid
, WindowID wid
);
692 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
696 void PenColour(ColourAllocated fore
);
698 int DeviceHeightFont(int points
);
699 void MoveTo(int x_
, int y_
);
700 void LineTo(int x_
, int y_
);
701 void Polygon(Point
*pts
, int npts
, ColourAllocated fore
, ColourAllocated back
);
702 void RectangleDraw(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
);
703 void FillRectangle(PRectangle rc
, ColourAllocated back
);
704 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
705 void RoundedRectangle(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
);
706 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourAllocated fill
, int alphaFill
,
707 ColourAllocated outline
, int alphaOutline
, int flags
);
708 void Ellipse(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
);
709 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
711 void DrawTextBase(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
);
712 void DrawTextNoClip(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
, ColourAllocated back
);
713 void DrawTextClipped(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
, ColourAllocated back
);
714 void DrawTextTransparent(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
, ColourAllocated fore
);
715 void MeasureWidths(Font
&font_
, const char *s
, int len
, int *positions
);
716 int WidthText(Font
&font_
, const char *s
, int len
);
717 int WidthChar(Font
&font_
, char ch
);
718 int Ascent(Font
&font_
);
719 int Descent(Font
&font_
);
720 int InternalLeading(Font
&font_
);
721 int ExternalLeading(Font
&font_
);
722 int Height(Font
&font_
);
723 int AverageCharWidth(Font
&font_
);
725 int SetPalette(Palette
*pal
, bool inBackGround
);
726 void SetClip(PRectangle rc
);
727 void FlushCachedState();
729 void SetUnicodeMode(bool unicodeMode_
);
730 void SetDBCSMode(int codePage
);
736 const char *CharacterSetID(int characterSet
) {
737 switch (characterSet
) {
738 case SC_CHARSET_ANSI
:
740 case SC_CHARSET_DEFAULT
:
742 case SC_CHARSET_BALTIC
:
743 return "ISO-8859-13";
744 case SC_CHARSET_CHINESEBIG5
:
746 case SC_CHARSET_EASTEUROPE
:
748 case SC_CHARSET_GB2312
:
750 case SC_CHARSET_GREEK
:
752 case SC_CHARSET_HANGUL
:
758 case SC_CHARSET_RUSSIAN
:
760 case SC_CHARSET_CYRILLIC
:
762 case SC_CHARSET_SHIFTJIS
:
764 case SC_CHARSET_SYMBOL
:
766 case SC_CHARSET_TURKISH
:
768 case SC_CHARSET_JOHAB
:
770 case SC_CHARSET_HEBREW
:
772 case SC_CHARSET_ARABIC
:
774 case SC_CHARSET_VIETNAMESE
:
776 case SC_CHARSET_THAI
:
777 return "ISO-8859-11";
778 case SC_CHARSET_8859_15
:
779 return "ISO-8859-15";
785 void SurfaceImpl::SetConverter(int characterSet_
) {
786 if (characterSet
!= characterSet_
) {
787 characterSet
= characterSet_
;
788 conv
.Open("UTF-8", CharacterSetID(characterSet
), false);
792 SurfaceImpl::SurfaceImpl() : et(singleByte
), drawable(0), gc(0), ppixmap(0),
793 x(0), y(0), inited(false), createdGC(false)
794 , pcontext(0), layout(0), characterSet(-1) {
797 SurfaceImpl::~SurfaceImpl() {
801 void SurfaceImpl::Release() {
810 gdk_pixmap_unref(ppixmap
);
813 g_object_unref(layout
);
816 g_object_unref(pcontext
);
826 bool SurfaceImpl::Initialised() {
830 void SurfaceImpl::Init(WindowID wid
) {
832 PLATFORM_ASSERT(wid
);
833 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
834 PLATFORM_ASSERT(pcontext
);
835 layout
= pango_layout_new(pcontext
);
836 PLATFORM_ASSERT(layout
);
840 void SurfaceImpl::Init(SurfaceID sid
, WindowID wid
) {
841 PLATFORM_ASSERT(sid
);
842 GdkDrawable
*drawable_
= reinterpret_cast<GdkDrawable
*>(sid
);
844 PLATFORM_ASSERT(wid
);
845 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
846 layout
= pango_layout_new(pcontext
);
847 drawable
= drawable_
;
848 gc
= gdk_gc_new(drawable_
);
849 // Ask for lines that do not paint the last pixel so is like Win32
850 gdk_gc_set_line_attributes(gc
, 0, GDK_LINE_SOLID
, GDK_CAP_NOT_LAST
, GDK_JOIN_MITER
);
855 void SurfaceImpl::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
) {
856 PLATFORM_ASSERT(surface_
);
858 SurfaceImpl
*surfImpl
= static_cast<SurfaceImpl
*>(surface_
);
859 PLATFORM_ASSERT(surfImpl
->drawable
);
860 PLATFORM_ASSERT(wid
);
861 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
862 PLATFORM_ASSERT(pcontext
);
863 layout
= pango_layout_new(pcontext
);
864 PLATFORM_ASSERT(layout
);
865 if (height
> 0 && width
> 0)
866 ppixmap
= gdk_pixmap_new(surfImpl
->drawable
, width
, height
, -1);
868 gc
= gdk_gc_new(surfImpl
->drawable
);
869 // Ask for lines that do not paint the last pixel so is like Win32
870 gdk_gc_set_line_attributes(gc
, 0, GDK_LINE_SOLID
, GDK_CAP_NOT_LAST
, GDK_JOIN_MITER
);
875 void SurfaceImpl::PenColour(ColourAllocated fore
) {
878 co
.pixel
= fore
.AsLong();
879 gdk_gc_set_foreground(gc
, &co
);
883 int SurfaceImpl::LogPixelsY() {
887 int SurfaceImpl::DeviceHeightFont(int points
) {
888 int logPix
= LogPixelsY();
889 return (points
* logPix
+ logPix
/ 2) / 72;
892 void SurfaceImpl::MoveTo(int x_
, int y_
) {
897 void SurfaceImpl::LineTo(int x_
, int y_
) {
898 if (drawable
&& gc
) {
899 gdk_draw_line(drawable
, gc
,
907 void SurfaceImpl::Polygon(Point
*pts
, int npts
, ColourAllocated fore
,
908 ColourAllocated back
) {
910 if (npts
< static_cast<int>((sizeof(gpts
) / sizeof(gpts
[0])))) {
911 for (int i
= 0;i
< npts
;i
++) {
912 gpts
[i
].x
= pts
[i
].x
;
913 gpts
[i
].y
= pts
[i
].y
;
916 gdk_draw_polygon(drawable
, gc
, 1, gpts
, npts
);
918 gdk_draw_polygon(drawable
, gc
, 0, gpts
, npts
);
922 void SurfaceImpl::RectangleDraw(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
) {
923 if (gc
&& drawable
) {
925 gdk_draw_rectangle(drawable
, gc
, 1,
926 rc
.left
+ 1, rc
.top
+ 1,
927 rc
.right
- rc
.left
- 2, rc
.bottom
- rc
.top
- 2);
930 // The subtraction of 1 off the width and height here shouldn't be needed but
931 // otherwise a different rectangle is drawn than would be done if the fill parameter == 1
932 gdk_draw_rectangle(drawable
, gc
, 0,
934 rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1);
938 void SurfaceImpl::FillRectangle(PRectangle rc
, ColourAllocated back
) {
940 if (drawable
&& (rc
.left
< maxCoordinate
)) { // Protect against out of range
941 gdk_draw_rectangle(drawable
, gc
, 1,
943 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
947 void SurfaceImpl::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
948 if (static_cast<SurfaceImpl
&>(surfacePattern
).drawable
) {
949 // Tile pattern over rectangle
950 // Currently assumes 8x8 pattern
953 for (int xTile
= rc
.left
; xTile
< rc
.right
; xTile
+= widthPat
) {
954 int widthx
= (xTile
+ widthPat
> rc
.right
) ? rc
.right
- xTile
: widthPat
;
955 for (int yTile
= rc
.top
; yTile
< rc
.bottom
; yTile
+= heightPat
) {
956 int heighty
= (yTile
+ heightPat
> rc
.bottom
) ? rc
.bottom
- yTile
: heightPat
;
957 gdk_draw_pixmap(drawable
,
959 static_cast<SurfaceImpl
&>(surfacePattern
).drawable
,
966 // Something is wrong so try to show anyway
967 // Shows up black because colour not allocated
968 FillRectangle(rc
, ColourAllocated(0));
972 void SurfaceImpl::RoundedRectangle(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
) {
973 if (((rc
.right
- rc
.left
) > 4) && ((rc
.bottom
- rc
.top
) > 4)) {
974 // Approximate a round rect with some cut off corners
976 Point(rc
.left
+ 2, rc
.top
),
977 Point(rc
.right
- 2, rc
.top
),
978 Point(rc
.right
, rc
.top
+ 2),
979 Point(rc
.right
, rc
.bottom
- 2),
980 Point(rc
.right
- 2, rc
.bottom
),
981 Point(rc
.left
+ 2, rc
.bottom
),
982 Point(rc
.left
, rc
.bottom
- 2),
983 Point(rc
.left
, rc
.top
+ 2),
985 Polygon(pts
, sizeof(pts
) / sizeof(pts
[0]), fore
, back
);
987 RectangleDraw(rc
, fore
, back
);
991 // Plot a point into a guint32 buffer symetrically to all 4 qudrants
992 static void AllFour(guint32
*pixels
, int stride
, int width
, int height
, int x
, int y
, guint32 val
) {
993 pixels
[y
*stride
+x
] = val
;
994 pixels
[y
*stride
+width
-1-x
] = val
;
995 pixels
[(height
-1-y
)*stride
+x
] = val
;
996 pixels
[(height
-1-y
)*stride
+width
-1-x
] = val
;
999 static unsigned int GetRValue(unsigned int co
) {
1000 return (co
>> 16) & 0xff;
1003 static unsigned int GetGValue(unsigned int co
) {
1004 return (co
>> 8) & 0xff;
1007 static unsigned int GetBValue(unsigned int co
) {
1011 static guint32
u32FromRGBA(guint8 r
, guint8 g
, guint8 b
, guint8 a
) {
1016 converter
.pixVal
[0] = r
;
1017 converter
.pixVal
[1] = g
;
1018 converter
.pixVal
[2] = b
;
1019 converter
.pixVal
[3] = a
;
1020 return converter
.val
;
1023 void SurfaceImpl::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourAllocated fill
, int alphaFill
,
1024 ColourAllocated outline
, int alphaOutline
, int flags
) {
1025 if (gc
&& drawable
&& rc
.Width() > 0) {
1026 int width
= rc
.Width();
1027 int height
= rc
.Height();
1028 // Ensure not distorted too much by corners when small
1029 cornerSize
= Platform::Minimum(cornerSize
, (Platform::Minimum(width
, height
) / 2) - 2);
1030 // Make a 32 bit deep pixbuf with alpha
1031 GdkPixbuf
*pixalpha
= gdk_pixbuf_new(GDK_COLORSPACE_RGB
, TRUE
, 8, width
, height
);
1033 guint32 valEmpty
= u32FromRGBA(0,0,0,0);
1034 guint32 valFill
= u32FromRGBA(GetRValue(fill
.AsLong()),
1035 GetGValue(fill
.AsLong()), GetBValue(fill
.AsLong()), alphaFill
);
1036 guint32 valOutline
= u32FromRGBA(GetRValue(outline
.AsLong()),
1037 GetGValue(outline
.AsLong()), GetBValue(outline
.AsLong()), alphaOutline
);
1038 guint32
*pixels
= reinterpret_cast<guint32
*>(gdk_pixbuf_get_pixels(pixalpha
));
1039 int stride
= gdk_pixbuf_get_rowstride(pixalpha
) / 4;
1040 for (int yr
=0; yr
<height
; yr
++) {
1041 for (int xr
=0; xr
<width
; xr
++) {
1042 if ((xr
==0) || (xr
==width
-1) || (yr
== 0) || (yr
== height
-1)) {
1043 pixels
[yr
*stride
+xr
] = valOutline
;
1045 pixels
[yr
*stride
+xr
] = valFill
;
1049 for (int c
=0;c
<cornerSize
; c
++) {
1050 for (int xr
=0;xr
<c
+1; xr
++) {
1051 AllFour(pixels
, stride
, width
, height
, xr
, c
-xr
, valEmpty
);
1054 for (int xr
=1;xr
<cornerSize
; xr
++) {
1055 AllFour(pixels
, stride
, width
, height
, xr
, cornerSize
-xr
, valOutline
);
1059 gdk_draw_pixbuf(drawable
, gc
, pixalpha
,
1060 0,0, rc
.left
,rc
.top
, width
,height
, GDK_RGB_DITHER_NORMAL
, 0, 0);
1062 g_object_unref(pixalpha
);
1066 void SurfaceImpl::Ellipse(PRectangle rc
, ColourAllocated fore
, ColourAllocated back
) {
1068 gdk_draw_arc(drawable
, gc
, 1,
1069 rc
.left
+ 1, rc
.top
+ 1,
1070 rc
.right
- rc
.left
- 2, rc
.bottom
- rc
.top
- 2,
1073 // The subtraction of 1 here is similar to the case for RectangleDraw
1075 gdk_draw_arc(drawable
, gc
, 0,
1077 rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1,
1081 void SurfaceImpl::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
1082 if (static_cast<SurfaceImpl
&>(surfaceSource
).drawable
) {
1083 gdk_draw_pixmap(drawable
,
1085 static_cast<SurfaceImpl
&>(surfaceSource
).drawable
,
1088 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
1092 static size_t UTF8Len(char ch
) {
1093 unsigned char uch
= static_cast<unsigned char>(ch
);
1096 else if (uch
< (0x80 + 0x40 + 0x20))
1102 char *UTF8FromLatin1(const char *s
, int &len
) {
1103 char *utfForm
= new char[len
*2+1];
1105 for (int i
=0;i
<len
;i
++) {
1106 unsigned int uch
= static_cast<unsigned char>(s
[i
]);
1108 utfForm
[lenU
++] = uch
;
1110 utfForm
[lenU
++] = static_cast<char>(0xC0 | (uch
>> 6));
1111 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
1114 utfForm
[lenU
] = '\0';
1119 static char *UTF8FromIconv(const Converter
&conv
, const char *s
, int &len
) {
1121 char *utfForm
= new char[len
*3+1];
1122 char *pin
= const_cast<char *>(s
);
1123 size_t inLeft
= len
;
1124 char *pout
= utfForm
;
1125 size_t outLeft
= len
*3+1;
1126 size_t conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
1127 if (conversions
!= ((size_t)(-1))) {
1129 len
= pout
- utfForm
;
1137 // Work out how many bytes are in a character by trying to convert using iconv,
1138 // returning the first length that succeeds.
1139 static size_t MultiByteLenFromIconv(const Converter
&conv
, const char *s
, size_t len
) {
1140 for (size_t lenMB
=1; (lenMB
<4) && (lenMB
<= len
); lenMB
++) {
1142 char *pin
= const_cast<char *>(s
);
1143 size_t inLeft
= lenMB
;
1144 char *pout
= wcForm
;
1146 size_t conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
1147 if (conversions
!= ((size_t)(-1))) {
1154 static char *UTF8FromGdkWChar(GdkWChar
*wctext
, int wclen
) {
1155 char *utfForm
= new char[wclen
*3+1]; // Maximum of 3 UTF-8 bytes per character
1157 for (int i
= 0; i
< wclen
&& wctext
[i
]; i
++) {
1158 unsigned int uch
= wctext
[i
];
1160 utfForm
[lenU
++] = static_cast<char>(uch
);
1161 } else if (uch
< 0x800) {
1162 utfForm
[lenU
++] = static_cast<char>(0xC0 | (uch
>> 6));
1163 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
1165 utfForm
[lenU
++] = static_cast<char>(0xE0 | (uch
>> 12));
1166 utfForm
[lenU
++] = static_cast<char>(0x80 | ((uch
>> 6) & 0x3f));
1167 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
1170 utfForm
[lenU
] = '\0';
1174 static char *UTF8FromDBCS(const char *s
, int &len
) {
1175 GdkWChar
*wctext
= new GdkWChar
[len
+ 1];
1176 GdkWChar
*wcp
= wctext
;
1177 int wclen
= gdk_mbstowcs(wcp
, s
, len
);
1179 // In the annoying case when non-locale chars in the line.
1180 // e.g. latin1 chars in Japanese locale.
1185 char *utfForm
= UTF8FromGdkWChar(wctext
, wclen
);
1187 len
= strlen(utfForm
);
1191 static size_t UTF8CharLength(const char *s
) {
1192 const unsigned char *us
= reinterpret_cast<const unsigned char *>(s
);
1193 unsigned char ch
= *us
;
1196 } else if (ch
< 0x80 + 0x40 + 0x20) {
1203 // On GTK+, wchar_t is 4 bytes
1205 const int maxLengthTextRun
= 10000;
1207 void SurfaceImpl::DrawTextBase(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1208 ColourAllocated fore
) {
1210 if (gc
&& drawable
) {
1211 int xText
= rc
.left
;
1212 if (PFont(font_
)->pfd
) {
1214 bool useGFree
= false;
1216 pango_layout_set_text(layout
, s
, len
);
1219 SetConverter(PFont(font_
)->characterSet
);
1220 utfForm
= UTF8FromIconv(conv
, s
, len
);
1222 if (!utfForm
) { // iconv failed so try DBCS if DBCS mode
1225 utfForm
= UTF8FromDBCS(s
, len
);
1228 if (!utfForm
) { // iconv and DBCS failed so treat as Latin1
1229 utfForm
= UTF8FromLatin1(s
, len
);
1231 pango_layout_set_text(layout
, utfForm
, len
);
1233 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
1234 #ifdef PANGO_VERSION
1235 PangoLayoutLine
*pll
= pango_layout_get_line_readonly(layout
,0);
1237 PangoLayoutLine
*pll
= pango_layout_get_line(layout
,0);
1239 gdk_draw_layout_line(drawable
, gc
, xText
, ybase
, pll
);
1247 // Draw text as a series of segments to avoid limitations in X servers
1248 const int segmentLength
= 1000;
1249 bool draw8bit
= true;
1250 if (et
!= singleByte
) {
1251 GdkWChar wctext
[maxLengthTextRun
];
1252 if (len
>= maxLengthTextRun
)
1253 len
= maxLengthTextRun
-1;
1256 wclen
= UTF16FromUTF8(s
, len
,
1257 static_cast<wchar_t *>(static_cast<void *>(wctext
)), maxLengthTextRun
- 1);
1258 } else { // dbcs, so convert using current locale
1259 char sMeasure
[maxLengthTextRun
];
1260 memcpy(sMeasure
, s
, len
);
1261 sMeasure
[len
] = '\0';
1262 wclen
= gdk_mbstowcs(
1263 wctext
, sMeasure
, maxLengthTextRun
- 1);
1267 wctext
[wclen
] = L
'\0';
1268 GdkWChar
*wcp
= wctext
;
1269 while ((wclen
> 0) && (xText
< maxCoordinate
)) {
1270 int lenDraw
= Platform::Minimum(wclen
, segmentLength
);
1271 gdk_draw_text_wc(drawable
, PFont(font_
)->pfont
, gc
,
1272 xText
, ybase
, wcp
, lenDraw
);
1274 if (wclen
> 0) { // Avoid next calculation if possible as may be expensive
1275 xText
+= gdk_text_width_wc(PFont(font_
)->pfont
,
1283 while ((len
> 0) && (xText
< maxCoordinate
)) {
1284 int lenDraw
= Platform::Minimum(len
, segmentLength
);
1285 gdk_draw_text(drawable
, PFont(font_
)->pfont
, gc
,
1286 xText
, ybase
, s
, lenDraw
);
1288 if (len
> 0) { // Avoid next calculation if possible as may be expensive
1289 xText
+= gdk_text_width(PFont(font_
)->pfont
, s
, lenDraw
);
1297 void SurfaceImpl::DrawTextNoClip(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1298 ColourAllocated fore
, ColourAllocated back
) {
1299 FillRectangle(rc
, back
);
1300 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
1303 // On GTK+, exactly same as DrawTextNoClip
1304 void SurfaceImpl::DrawTextClipped(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1305 ColourAllocated fore
, ColourAllocated back
) {
1306 FillRectangle(rc
, back
);
1307 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
1310 void SurfaceImpl::DrawTextTransparent(PRectangle rc
, Font
&font_
, int ybase
, const char *s
, int len
,
1311 ColourAllocated fore
) {
1312 // Avoid drawing spaces in transparent mode
1313 for (int i
=0;i
<len
;i
++) {
1315 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
1321 class ClusterIterator
{
1322 PangoLayoutIter
*iter
;
1331 ClusterIterator(PangoLayout
*layout
, int len
) : lenPositions(len
), finished(false),
1332 positionStart(0), position(0), distance(0), curIndex(0) {
1333 iter
= pango_layout_get_iter(layout
);
1334 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
1336 ~ClusterIterator() {
1337 pango_layout_iter_free(iter
);
1341 positionStart
= position
;
1342 if (pango_layout_iter_next_cluster(iter
)) {
1343 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
1344 position
= PANGO_PIXELS(pos
.x
);
1345 curIndex
= pango_layout_iter_get_index(iter
);
1348 position
= PANGO_PIXELS(pos
.x
+ pos
.width
);
1349 curIndex
= lenPositions
;
1351 distance
= position
- positionStart
;
1355 void SurfaceImpl::MeasureWidths(Font
&font_
, const char *s
, int len
, int *positions
) {
1356 if (font_
.GetID()) {
1358 const int lenPositions
= len
;
1359 if (PFont(font_
)->pfd
) {
1361 int width
= PFont(font_
)->CharWidth(*s
, et
);
1363 positions
[0] = width
;
1367 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
1369 // Simple and direct as UTF-8 is native Pango encoding
1371 pango_layout_set_text(layout
, s
, len
);
1372 ClusterIterator
iti(layout
, lenPositions
);
1373 while (!iti
.finished
) {
1375 int places
= iti
.curIndex
- i
;
1376 while (i
< iti
.curIndex
) {
1377 // Evenly distribute space among bytes of this cluster.
1378 // Would be better to find number of characters and then
1379 // divide evenly between characters with each byte of a character
1380 // being at the same position.
1381 positions
[i
] = iti
.position
- (iti
.curIndex
- 1 - i
) * iti
.distance
/ places
;
1385 PLATFORM_ASSERT(i
== lenPositions
);
1387 int positionsCalculated
= 0;
1389 SetConverter(PFont(font_
)->characterSet
);
1390 char *utfForm
= UTF8FromIconv(conv
, s
, len
);
1392 // Convert to UTF-8 so can ask Pango for widths, then
1393 // Loop through UTF-8 and DBCS forms, taking account of different
1394 // character byte lengths.
1395 Converter
convMeasure("UCS-2", CharacterSetID(characterSet
), false);
1396 pango_layout_set_text(layout
, utfForm
, strlen(utfForm
));
1398 int clusterStart
= 0;
1399 ClusterIterator
iti(layout
, strlen(utfForm
));
1400 while (!iti
.finished
) {
1402 int clusterEnd
= iti
.curIndex
;
1403 int places
= g_utf8_strlen(utfForm
+ clusterStart
, clusterEnd
- clusterStart
);
1405 while (clusterStart
< clusterEnd
) {
1406 size_t lenChar
= MultiByteLenFromIconv(convMeasure
, s
+i
, len
-i
);
1408 positions
[i
++] = iti
.position
- (places
- place
) * iti
.distance
/ places
;
1409 positionsCalculated
++;
1411 clusterStart
+= UTF8CharLength(utfForm
+clusterStart
);
1416 PLATFORM_ASSERT(i
== lenPositions
);
1419 if (positionsCalculated
< 1 ) {
1420 // Either Latin1 or DBCS conversion failed so treat as Latin1.
1421 bool useGFree
= false;
1422 SetConverter(PFont(font_
)->characterSet
);
1423 char *utfForm
= UTF8FromIconv(conv
, s
, len
);
1425 utfForm
= UTF8FromLatin1(s
, len
);
1427 pango_layout_set_text(layout
, utfForm
, len
);
1429 int clusterStart
= 0;
1430 // Each Latin1 input character may take 1 or 2 bytes in UTF-8
1431 // and groups of up to 3 may be represented as ligatures.
1432 ClusterIterator
iti(layout
, strlen(utfForm
));
1433 while (!iti
.finished
) {
1435 int clusterEnd
= iti
.curIndex
;
1436 int ligatureLength
= g_utf8_strlen(utfForm
+ clusterStart
, clusterEnd
- clusterStart
);
1437 PLATFORM_ASSERT(ligatureLength
> 0 && ligatureLength
<= 3);
1438 for (int charInLig
=0; charInLig
<ligatureLength
; charInLig
++) {
1439 positions
[i
++] = iti
.position
- (ligatureLength
- 1 - charInLig
) * iti
.distance
/ ligatureLength
;
1441 clusterStart
= clusterEnd
;
1448 PLATFORM_ASSERT(i
== lenPositions
);
1452 PFont(font_
)->SetCharWidth(*s
, positions
[0], et
);
1456 GdkFont
*gf
= PFont(font_
)->pfont
;
1457 bool measure8bit
= true;
1458 if (et
!= singleByte
) {
1459 GdkWChar wctext
[maxLengthTextRun
];
1460 if (len
>= maxLengthTextRun
)
1461 len
= maxLengthTextRun
-1;
1464 wclen
= UTF16FromUTF8(s
, len
,
1465 static_cast<wchar_t *>(static_cast<void *>(wctext
)), maxLengthTextRun
- 1);
1466 } else { // dbcsMode, so convert using current locale
1467 char sDraw
[maxLengthTextRun
];
1468 memcpy(sDraw
, s
, len
);
1470 wclen
= gdk_mbstowcs(
1471 wctext
, sDraw
, maxLengthTextRun
- 1);
1474 measure8bit
= false;
1475 wctext
[wclen
] = L
'\0';
1476 // Map widths back to utf-8 or DBCS input string
1478 for (int iU
= 0; iU
< wclen
; iU
++) {
1479 int width
= gdk_char_width_wc(gf
, wctext
[iU
]);
1480 totalWidth
+= width
;
1483 lenChar
= UTF8Len(s
[i
]);
1485 lenChar
= mblen(s
+i
, MB_CUR_MAX
);
1490 positions
[i
++] = totalWidth
;
1493 while (i
< len
) { // In case of problems with lengths
1494 positions
[i
++] = totalWidth
;
1499 // Either Latin1 or conversion failed so treat as Latin1.
1500 for (int i
= 0; i
< len
; i
++) {
1501 int width
= gdk_char_width(gf
, s
[i
]);
1502 totalWidth
+= width
;
1503 positions
[i
] = totalWidth
;
1507 // No font so return an ascending range of values
1508 for (int i
= 0; i
< len
; i
++) {
1509 positions
[i
] = i
+ 1;
1514 int SurfaceImpl::WidthText(Font
&font_
, const char *s
, int len
) {
1515 if (font_
.GetID()) {
1516 if (PFont(font_
)->pfd
) {
1518 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
1520 bool useGFree
= false;
1522 pango_layout_set_text(layout
, s
, len
);
1526 utfForm
= UTF8FromDBCS(s
, len
);
1528 if (!utfForm
) { // DBCS failed so treat as iconv
1529 SetConverter(PFont(font_
)->characterSet
);
1530 utfForm
= UTF8FromIconv(conv
, s
, len
);
1532 if (!utfForm
) { // g_locale_to_utf8 failed so treat as Latin1
1533 utfForm
= UTF8FromLatin1(s
, len
);
1535 pango_layout_set_text(layout
, utfForm
, len
);
1537 #ifdef PANGO_VERSION
1538 PangoLayoutLine
*pangoLine
= pango_layout_get_line_readonly(layout
,0);
1540 PangoLayoutLine
*pangoLine
= pango_layout_get_line(layout
,0);
1542 pango_layout_line_get_extents(pangoLine
, NULL
, &pos
);
1548 return PANGO_PIXELS(pos
.width
);
1551 GdkWChar wctext
[maxLengthTextRun
];
1552 size_t wclen
= UTF16FromUTF8(s
, len
, static_cast<wchar_t *>(static_cast<void *>(wctext
)),
1553 sizeof(wctext
) / sizeof(GdkWChar
) - 1);
1554 wctext
[wclen
] = L
'\0';
1555 return gdk_text_width_wc(PFont(font_
)->pfont
, wctext
, wclen
);
1557 return gdk_text_width(PFont(font_
)->pfont
, s
, len
);
1564 int SurfaceImpl::WidthChar(Font
&font_
, char ch
) {
1565 if (font_
.GetID()) {
1566 if (PFont(font_
)->pfd
) {
1567 return WidthText(font_
, &ch
, 1);
1569 return gdk_char_width(PFont(font_
)->pfont
, ch
);
1575 // Three possible strategies for determining ascent and descent of font:
1576 // 1) Call gdk_string_extents with string containing all letters, numbers and punctuation.
1577 // 2) Use the ascent and descent fields of GdkFont.
1578 // 3) Call gdk_string_extents with string as 1 but also including accented capitals.
1579 // Smallest values given by 1 and largest by 3 with 2 in between.
1580 // Techniques 1 and 2 sometimes chop off extreme portions of ascenders and
1581 // descenders but are mostly OK except for accented characters like Ã… which are
1582 // rarely used in code.
1584 // This string contains a good range of characters to test for size.
1585 //const char largeSizeString[] = "ÂÃÅÄ `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1586 // "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1588 const char sizeString
[] = "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890"
1589 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1592 int SurfaceImpl::Ascent(Font
&font_
) {
1593 if (!(font_
.GetID()))
1597 int ascent
= PFont(font_
)->ascent
;
1598 if ((ascent
== 0) && (PFont(font_
)->pfd
)) {
1599 PangoFontMetrics
*metrics
= pango_context_get_metrics(pcontext
,
1600 PFont(font_
)->pfd
, pango_context_get_language(pcontext
));
1601 PFont(font_
)->ascent
=
1602 PANGO_PIXELS(pango_font_metrics_get_ascent(metrics
));
1603 pango_font_metrics_unref(metrics
);
1604 ascent
= PFont(font_
)->ascent
;
1606 if ((ascent
== 0) && (PFont(font_
)->pfont
)) {
1607 ascent
= PFont(font_
)->pfont
->ascent
;
1622 gdk_string_extents(PFont(font_
)->pfont
, sizeString
,
1623 &lbearing
, &rbearing
, &width
, &ascent
, &descent
);
1628 int SurfaceImpl::Descent(Font
&font_
) {
1629 if (!(font_
.GetID()))
1633 if (PFont(font_
)->pfd
) {
1634 PangoFontMetrics
*metrics
= pango_context_get_metrics(pcontext
,
1635 PFont(font_
)->pfd
, pango_context_get_language(pcontext
));
1636 int descent
= PANGO_PIXELS(pango_font_metrics_get_descent(metrics
));
1637 pango_font_metrics_unref(metrics
);
1640 return PFont(font_
)->pfont
->descent
;
1649 gdk_string_extents(PFont(font_
)->pfont
, sizeString
,
1650 &lbearing
, &rbearing
, &width
, &ascent
, &descent
);
1655 int SurfaceImpl::InternalLeading(Font
&) {
1659 int SurfaceImpl::ExternalLeading(Font
&) {
1663 int SurfaceImpl::Height(Font
&font_
) {
1664 return Ascent(font_
) + Descent(font_
);
1667 int SurfaceImpl::AverageCharWidth(Font
&font_
) {
1668 return WidthChar(font_
, 'n');
1671 int SurfaceImpl::SetPalette(Palette
*, bool) {
1672 // Handled in palette allocation for GTK so this does nothing
1676 void SurfaceImpl::SetClip(PRectangle rc
) {
1677 GdkRectangle area
= {rc
.left
, rc
.top
,
1678 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
};
1679 gdk_gc_set_clip_rectangle(gc
, &area
);
1682 void SurfaceImpl::FlushCachedState() {}
1684 void SurfaceImpl::SetUnicodeMode(bool unicodeMode_
) {
1689 void SurfaceImpl::SetDBCSMode(int codePage
) {
1690 if (codePage
&& (codePage
!= SC_CP_UTF8
))
1694 Surface
*Surface::Allocate() {
1695 return new SurfaceImpl
;
1698 Window::~Window() {}
1700 void Window::Destroy() {
1702 gtk_widget_destroy(GTK_WIDGET(wid
));
1706 bool Window::HasFocus() {
1707 return GTK_WIDGET_HAS_FOCUS(wid
);
1710 PRectangle
Window::GetPosition() {
1711 // Before any size allocated pretend its 1000 wide so not scrolled
1712 PRectangle
rc(0, 0, 1000, 1000);
1714 rc
.left
= PWidget(wid
)->allocation
.x
;
1715 rc
.top
= PWidget(wid
)->allocation
.y
;
1716 if (PWidget(wid
)->allocation
.width
> 20) {
1717 rc
.right
= rc
.left
+ PWidget(wid
)->allocation
.width
;
1718 rc
.bottom
= rc
.top
+ PWidget(wid
)->allocation
.height
;
1724 void Window::SetPosition(PRectangle rc
) {
1725 GtkAllocation alloc
;
1728 alloc
.width
= rc
.Width();
1729 alloc
.height
= rc
.Height();
1730 gtk_widget_size_allocate(PWidget(wid
), &alloc
);
1733 void Window::SetPositionRelative(PRectangle rc
, Window relativeTo
) {
1736 gdk_window_get_origin(PWidget(relativeTo
.wid
)->window
, &ox
, &oy
);
1744 /* do some corrections to fit into screen */
1745 int sizex
= rc
.right
- rc
.left
;
1746 int sizey
= rc
.bottom
- rc
.top
;
1747 int screenWidth
= gdk_screen_width();
1748 int screenHeight
= gdk_screen_height();
1749 if (sizex
> screenWidth
)
1750 ox
= 0; /* the best we can do */
1751 else if (ox
+ sizex
> screenWidth
)
1752 ox
= screenWidth
- sizex
;
1753 if (oy
+ sizey
> screenHeight
)
1754 oy
= screenHeight
- sizey
;
1756 gtk_window_move(GTK_WINDOW(PWidget(wid
)), ox
, oy
);
1758 gtk_widget_set_usize(PWidget(wid
), sizex
, sizey
);
1761 PRectangle
Window::GetClientPosition() {
1762 // On GTK+, the client position is the window position
1763 return GetPosition();
1766 void Window::Show(bool show
) {
1768 gtk_widget_show(PWidget(wid
));
1771 void Window::InvalidateAll() {
1773 gtk_widget_queue_draw(PWidget(wid
));
1777 void Window::InvalidateRectangle(PRectangle rc
) {
1779 gtk_widget_queue_draw_area(PWidget(wid
),
1781 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
1785 void Window::SetFont(Font
&) {
1786 // Can not be done generically but only needed for ListBox
1789 void Window::SetCursor(Cursor curs
) {
1790 // We don't set the cursor to same value numerous times under gtk because
1791 // it stores the cursor in the window once it's set
1792 if (curs
== cursorLast
)
1799 gdkCurs
= gdk_cursor_new(GDK_XTERM
);
1802 gdkCurs
= gdk_cursor_new(GDK_LEFT_PTR
);
1805 gdkCurs
= gdk_cursor_new(GDK_CENTER_PTR
);
1808 gdkCurs
= gdk_cursor_new(GDK_WATCH
);
1811 gdkCurs
= gdk_cursor_new(GDK_HAND2
);
1813 case cursorReverseArrow
:
1814 gdkCurs
= gdk_cursor_new(GDK_RIGHT_PTR
);
1817 gdkCurs
= gdk_cursor_new(GDK_LEFT_PTR
);
1818 cursorLast
= cursorArrow
;
1822 if (PWidget(wid
)->window
)
1823 gdk_window_set_cursor(PWidget(wid
)->window
, gdkCurs
);
1824 gdk_cursor_destroy(gdkCurs
);
1827 void Window::SetTitle(const char *s
) {
1828 gtk_window_set_title(GTK_WINDOW(wid
), s
);
1831 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
1832 gdk window coordinates */
1833 PRectangle
Window::GetMonitorRect(Point pt
) {
1834 gint x_offset
, y_offset
;
1837 gdk_window_get_origin(PWidget(wid
)->window
, &x_offset
, &y_offset
);
1839 #if GTK_CHECK_VERSION(2,2,0)
1846 screen
= gtk_widget_get_screen(PWidget(wid
));
1847 monitor_num
= gdk_screen_get_monitor_at_point(screen
, pt
.x
+ x_offset
, pt
.y
+ y_offset
);
1848 gdk_screen_get_monitor_geometry(screen
, monitor_num
, &rect
);
1851 return PRectangle(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
1854 return PRectangle(-x_offset
, -y_offset
, (-x_offset
) + gdk_screen_width(),
1855 (-y_offset
) + gdk_screen_height());
1860 const char *xpm_data
;
1864 static void list_image_free(gpointer
, gpointer value
, gpointer
) {
1865 ListImage
*list_image
= (ListImage
*) value
;
1866 if (list_image
->pixbuf
)
1867 gdk_pixbuf_unref (list_image
->pixbuf
);
1871 ListBox::ListBox() {
1874 ListBox::~ListBox() {
1883 class ListBoxX
: public ListBox
{
1887 GtkCellRenderer
* pixbuf_renderer
;
1889 int desiredVisibleRows
;
1890 unsigned int maxItemCharacters
;
1891 unsigned int aveCharWidth
;
1893 CallBackAction doubleClickAction
;
1894 void *doubleClickActionData
;
1896 ListBoxX() : list(0), pixhash(NULL
), pixbuf_renderer(0),
1897 desiredVisibleRows(5), maxItemCharacters(0),
1898 aveCharWidth(1), doubleClickAction(NULL
), doubleClickActionData(NULL
) {
1900 virtual ~ListBoxX() {
1902 g_hash_table_foreach((GHashTable
*) pixhash
, list_image_free
, NULL
);
1903 g_hash_table_destroy((GHashTable
*) pixhash
);
1906 virtual void SetFont(Font
&font
);
1907 virtual void Create(Window
&parent
, int ctrlID
, Point location_
, int lineHeight_
, bool unicodeMode_
);
1908 virtual void SetAverageCharWidth(int width
);
1909 virtual void SetVisibleRows(int rows
);
1910 virtual int GetVisibleRows() const;
1911 virtual PRectangle
GetDesiredRect();
1912 virtual int CaretFromEdge();
1913 virtual void Clear();
1914 virtual void Append(char *s
, int type
= -1);
1915 virtual int Length();
1916 virtual void Select(int n
);
1917 virtual int GetSelection();
1918 virtual int Find(const char *prefix
);
1919 virtual void GetValue(int n
, char *value
, int len
);
1920 virtual void RegisterImage(int type
, const char *xpm_data
);
1921 virtual void ClearRegisteredImages();
1922 virtual void SetDoubleClickAction(CallBackAction action
, void *data
) {
1923 doubleClickAction
= action
;
1924 doubleClickActionData
= data
;
1926 virtual void SetList(const char *listText
, char separator
, char typesep
);
1929 ListBox
*ListBox::Allocate() {
1930 ListBoxX
*lb
= new ListBoxX();
1934 static gboolean
ButtonPress(GtkWidget
*, GdkEventButton
* ev
, gpointer p
) {
1936 ListBoxX
* lb
= reinterpret_cast<ListBoxX
*>(p
);
1937 if (ev
->type
== GDK_2BUTTON_PRESS
&& lb
->doubleClickAction
!= NULL
) {
1938 lb
->doubleClickAction(lb
->doubleClickActionData
);
1943 // No pointer back to Scintilla to save status
1948 /* Change the active color to the selected color so the listbox uses the color
1949 scheme that it would use if it had the focus. */
1950 static void StyleSet(GtkWidget
*w
, GtkStyle
*, void*) {
1953 g_return_if_fail(w
!= NULL
);
1955 /* Copy the selected color to active. Note that the modify calls will cause
1956 recursive calls to this function after the value is updated and w->style to
1957 be set to a new object */
1958 style
= gtk_widget_get_style(w
);
1961 if (!gdk_color_equal(&style
->base
[GTK_STATE_SELECTED
], &style
->base
[GTK_STATE_ACTIVE
]))
1962 gtk_widget_modify_base(w
, GTK_STATE_ACTIVE
, &style
->base
[GTK_STATE_SELECTED
]);
1964 style
= gtk_widget_get_style(w
);
1967 if (!gdk_color_equal(&style
->text
[GTK_STATE_SELECTED
], &style
->text
[GTK_STATE_ACTIVE
]))
1968 gtk_widget_modify_text(w
, GTK_STATE_ACTIVE
, &style
->text
[GTK_STATE_SELECTED
]);
1971 void ListBoxX::Create(Window
&, int, Point
, int, bool) {
1972 wid
= gtk_window_new(GTK_WINDOW_POPUP
);
1974 GtkWidget
*frame
= gtk_frame_new(NULL
);
1975 gtk_widget_show(frame
);
1976 gtk_container_add(GTK_CONTAINER(GetID()), frame
);
1977 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1978 gtk_container_set_border_width(GTK_CONTAINER(frame
), 0);
1980 scroller
= gtk_scrolled_window_new(NULL
, NULL
);
1981 gtk_container_set_border_width(GTK_CONTAINER(scroller
), 0);
1982 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller
),
1983 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
1984 gtk_container_add(GTK_CONTAINER(frame
), PWidget(scroller
));
1985 gtk_widget_show(PWidget(scroller
));
1987 /* Tree and its model */
1988 GtkListStore
*store
=
1989 gtk_list_store_new(N_COLUMNS
, GDK_TYPE_PIXBUF
, G_TYPE_STRING
);
1991 list
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
1992 g_signal_connect(G_OBJECT(list
), "style-set", G_CALLBACK(StyleSet
), NULL
);
1994 GtkTreeSelection
*selection
=
1995 gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
1996 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
1997 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list
), FALSE
);
1998 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list
), FALSE
);
2001 GtkTreeViewColumn
*column
= gtk_tree_view_column_new();
2002 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
2003 gtk_tree_view_column_set_title(column
, "Autocomplete");
2005 pixbuf_renderer
= gtk_cell_renderer_pixbuf_new();
2006 gtk_cell_renderer_set_fixed_size(pixbuf_renderer
, 0, -1);
2007 gtk_tree_view_column_pack_start(column
, pixbuf_renderer
, FALSE
);
2008 gtk_tree_view_column_add_attribute(column
, pixbuf_renderer
,
2009 "pixbuf", PIXBUF_COLUMN
);
2011 GtkCellRenderer
* renderer
= gtk_cell_renderer_text_new();
2012 gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer
), 1);
2013 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
2014 gtk_tree_view_column_add_attribute(column
, renderer
,
2015 "text", TEXT_COLUMN
);
2017 gtk_tree_view_append_column(GTK_TREE_VIEW(list
), column
);
2018 if (g_object_class_find_property(G_OBJECT_GET_CLASS(list
), "fixed-height-mode"))
2019 g_object_set(G_OBJECT(list
), "fixed-height-mode", TRUE
, NULL
);
2021 GtkWidget
*wid
= PWidget(list
); // No code inside the G_OBJECT macro
2022 gtk_container_add(GTK_CONTAINER(PWidget(scroller
)), wid
);
2023 gtk_widget_show(wid
);
2024 g_signal_connect(G_OBJECT(wid
), "button_press_event",
2025 G_CALLBACK(ButtonPress
), this);
2026 gtk_widget_realize(PWidget(wid
));
2029 void ListBoxX::SetFont(Font
&scint_font
) {
2030 // Only do for Pango font as there have been crashes for GDK fonts
2031 if (Created() && PFont(scint_font
)->pfd
) {
2032 // Current font is Pango font
2033 gtk_widget_modify_font(PWidget(list
), PFont(scint_font
)->pfd
);
2037 void ListBoxX::SetAverageCharWidth(int width
) {
2038 aveCharWidth
= width
;
2041 void ListBoxX::SetVisibleRows(int rows
) {
2042 desiredVisibleRows
= rows
;
2045 int ListBoxX::GetVisibleRows() const {
2046 return desiredVisibleRows
;
2049 PRectangle
ListBoxX::GetDesiredRect() {
2050 // Before any size allocated pretend its 100 wide so not scrolled
2051 PRectangle
rc(0, 0, 100, 100);
2053 int rows
= Length();
2054 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2055 rows
= desiredVisibleRows
;
2060 // First calculate height of the clist for our desired visible
2061 // row count otherwise it tries to expand to the total # of rows
2065 GtkTreeViewColumn
* column
=
2066 gtk_tree_view_get_column(GTK_TREE_VIEW(list
), 0);
2067 gtk_tree_view_column_cell_get_size(column
, NULL
,
2068 NULL
, NULL
, &row_width
, &row_height
);
2069 int ythickness
= PWidget(list
)->style
->ythickness
;
2070 height
= (rows
* row_height
2072 + GTK_CONTAINER(PWidget(list
))->border_width
+ 1));
2073 gtk_widget_set_usize(GTK_WIDGET(PWidget(list
)), -1, height
);
2075 // Get the size of the scroller because we set usize on the window
2076 gtk_widget_size_request(GTK_WIDGET(scroller
), &req
);
2077 rc
.right
= req
.width
;
2078 rc
.bottom
= req
.height
;
2080 gtk_widget_set_usize(GTK_WIDGET(list
), -1, -1);
2081 int width
= maxItemCharacters
;
2084 rc
.right
= width
* (aveCharWidth
+ aveCharWidth
/ 3);
2085 if (Length() > rows
)
2086 rc
.right
= rc
.right
+ 16;
2091 int ListBoxX::CaretFromEdge() {
2092 gint renderer_width
, renderer_height
;
2093 gtk_cell_renderer_get_fixed_size(pixbuf_renderer
, &renderer_width
,
2095 return 4 + renderer_width
;
2098 void ListBoxX::Clear() {
2099 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2100 gtk_list_store_clear(GTK_LIST_STORE(model
));
2101 maxItemCharacters
= 0;
2104 static void init_pixmap(ListImage
*list_image
) {
2105 const char *textForm
= list_image
->xpm_data
;
2106 const char * const * xpm_lineform
= reinterpret_cast<const char * const *>(textForm
);
2107 const char **xpm_lineformfromtext
= 0;
2108 // The XPM data can be either in atext form as will be read from a file
2109 // or in a line form (array of char *) as will be used for images defined in code.
2110 // Test for text form and convert to line form
2111 if ((0 == memcmp(textForm
, "/* X", 4)) && (0 == memcmp(textForm
, "/* XPM */", 9))) {
2112 // Test done is two parts to avoid possibility of overstepping the memory
2113 // if memcmp implemented strangely. Must be 4 bytes at least at destination.
2114 xpm_lineformfromtext
= XPM::LinesFormFromTextForm(textForm
);
2115 xpm_lineform
= xpm_lineformfromtext
;
2118 // Drop any existing pixmap/bitmap as data may have changed
2119 if (list_image
->pixbuf
)
2120 gdk_pixbuf_unref(list_image
->pixbuf
);
2121 list_image
->pixbuf
=
2122 gdk_pixbuf_new_from_xpm_data((const gchar
**)xpm_lineform
);
2123 delete []xpm_lineformfromtext
;
2128 void ListBoxX::Append(char *s
, int type
) {
2129 ListImage
*list_image
= NULL
;
2130 if ((type
>= 0) && pixhash
) {
2131 list_image
= (ListImage
*) g_hash_table_lookup((GHashTable
*) pixhash
2132 , (gconstpointer
) GINT_TO_POINTER(type
));
2135 GtkListStore
*store
=
2136 GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list
)));
2137 gtk_list_store_append(GTK_LIST_STORE(store
), &iter
);
2139 if (NULL
== list_image
->pixbuf
)
2140 init_pixmap(list_image
);
2141 if (list_image
->pixbuf
) {
2142 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
2143 PIXBUF_COLUMN
, list_image
->pixbuf
,
2144 TEXT_COLUMN
, s
, -1);
2146 gint pixbuf_width
= gdk_pixbuf_get_width(list_image
->pixbuf
);
2147 gint renderer_height
, renderer_width
;
2148 gtk_cell_renderer_get_fixed_size(pixbuf_renderer
,
2149 &renderer_width
, &renderer_height
);
2150 if (pixbuf_width
> renderer_width
)
2151 gtk_cell_renderer_set_fixed_size(pixbuf_renderer
,
2154 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
2155 TEXT_COLUMN
, s
, -1);
2158 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
2159 TEXT_COLUMN
, s
, -1);
2161 size_t len
= strlen(s
);
2162 if (maxItemCharacters
< len
)
2163 maxItemCharacters
= len
;
2166 int ListBoxX::Length() {
2168 return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
2169 (GTK_TREE_VIEW(list
)), NULL
);
2173 void ListBoxX::Select(int n
) {
2175 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2176 GtkTreeSelection
*selection
=
2177 gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
2180 gtk_tree_selection_unselect_all(selection
);
2184 bool valid
= gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, n
) != FALSE
;
2186 gtk_tree_selection_select_iter(selection
, &iter
);
2188 // Move the scrollbar to show the selection.
2189 int total
= Length();
2190 GtkAdjustment
*adj
=
2191 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list
));
2192 gfloat value
= ((gfloat
)n
/ total
) * (adj
->upper
- adj
->lower
)
2193 + adj
->lower
- adj
->page_size
/ 2;
2198 GtkTreeViewColumn
* column
=
2199 gtk_tree_view_get_column(GTK_TREE_VIEW(list
), 0);
2200 gtk_tree_view_column_cell_get_size(column
, NULL
, NULL
,
2201 NULL
, &row_width
, &row_height
);
2203 int rows
= Length();
2204 if ((rows
== 0) || (rows
> desiredVisibleRows
))
2205 rows
= desiredVisibleRows
;
2207 // Odd rows to display -- We are now in the middle.
2208 // Align it so that we don't chop off rows.
2209 value
+= (gfloat
)row_height
/ 2.0;
2212 value
= (value
< 0)? 0 : value
;
2213 value
= (value
> (adj
->upper
- adj
->page_size
))?
2214 (adj
->upper
- adj
->page_size
) : value
;
2217 gtk_adjustment_set_value(adj
, value
);
2219 gtk_tree_selection_unselect_all(selection
);
2223 int ListBoxX::GetSelection() {
2225 GtkTreeModel
*model
;
2226 GtkTreeSelection
*selection
;
2227 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
2228 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
)) {
2229 GtkTreePath
*path
= gtk_tree_model_get_path(model
, &iter
);
2230 int *indices
= gtk_tree_path_get_indices(path
);
2231 // Don't free indices.
2238 int ListBoxX::Find(const char *prefix
) {
2240 GtkTreeModel
*model
=
2241 gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2242 bool valid
= gtk_tree_model_get_iter_first(model
, &iter
) != FALSE
;
2246 gtk_tree_model_get(model
, &iter
, TEXT_COLUMN
, &s
, -1);
2247 if (s
&& (0 == strncmp(prefix
, s
, strlen(prefix
)))) {
2252 valid
= gtk_tree_model_iter_next(model
, &iter
) != FALSE
;
2258 void ListBoxX::GetValue(int n
, char *value
, int len
) {
2261 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
2262 bool valid
= gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, n
) != FALSE
;
2264 gtk_tree_model_get(model
, &iter
, TEXT_COLUMN
, &text
, -1);
2266 if (text
&& len
> 0) {
2267 strncpy(value
, text
, len
);
2268 value
[len
- 1] = '\0';
2275 // g_return_if_fail causes unnecessary compiler warning in release compile.
2277 #pragma warning(disable: 4127)
2280 void ListBoxX::RegisterImage(int type
, const char *xpm_data
) {
2281 g_return_if_fail(xpm_data
);
2283 // Saved and use the saved copy so caller's copy can disappear.
2284 xset
.Add(type
, xpm_data
);
2285 XPM
*pxpm
= xset
.Get(type
);
2286 xpm_data
= reinterpret_cast<const char *>(pxpm
->InLinesForm());
2289 pixhash
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
2291 ListImage
*list_image
= (ListImage
*) g_hash_table_lookup((GHashTable
*) pixhash
,
2292 (gconstpointer
) GINT_TO_POINTER(type
));
2294 // Drop icon already registered
2295 if (list_image
->pixbuf
)
2296 gdk_pixbuf_unref(list_image
->pixbuf
);
2297 list_image
->pixbuf
= NULL
;
2298 list_image
->xpm_data
= xpm_data
;
2300 list_image
= g_new0(ListImage
, 1);
2301 list_image
->xpm_data
= xpm_data
;
2302 g_hash_table_insert((GHashTable
*) pixhash
, GINT_TO_POINTER(type
),
2303 (gpointer
) list_image
);
2307 void ListBoxX::ClearRegisteredImages() {
2311 void ListBoxX::SetList(const char *listText
, char separator
, char typesep
) {
2313 int count
= strlen(listText
) + 1;
2314 char *words
= new char[count
];
2316 memcpy(words
, listText
, count
);
2317 char *startword
= words
;
2318 char *numword
= NULL
;
2320 for (; words
[i
]; i
++) {
2321 if (words
[i
] == separator
) {
2325 Append(startword
, numword
?atoi(numword
+ 1):-1);
2326 startword
= words
+ i
+ 1;
2328 } else if (words
[i
] == typesep
) {
2329 numword
= words
+ i
;
2335 Append(startword
, numword
?atoi(numword
+ 1):-1);
2341 Menu::Menu() : mid(0) {}
2343 void Menu::CreatePopUp() {
2345 mid
= gtk_item_factory_new(GTK_TYPE_MENU
, "<main>", NULL
);
2348 void Menu::Destroy() {
2350 g_object_unref(G_OBJECT(mid
));
2354 void Menu::Show(Point pt
, Window
&) {
2355 int screenHeight
= gdk_screen_height();
2356 int screenWidth
= gdk_screen_width();
2357 GtkItemFactory
*factory
= reinterpret_cast<GtkItemFactory
*>(mid
);
2358 GtkWidget
*widget
= gtk_item_factory_get_widget(factory
, "<main>");
2359 gtk_widget_show_all(widget
);
2360 GtkRequisition requisition
;
2361 gtk_widget_size_request(widget
, &requisition
);
2362 if ((pt
.x
+ requisition
.width
) > screenWidth
) {
2363 pt
.x
= screenWidth
- requisition
.width
;
2365 if ((pt
.y
+ requisition
.height
) > screenHeight
) {
2366 pt
.y
= screenHeight
- requisition
.height
;
2368 gtk_item_factory_popup(factory
, pt
.x
- 4, pt
.y
- 4, 3,
2369 gtk_get_current_event_time());
2372 ElapsedTime::ElapsedTime() {
2374 g_get_current_time(&curTime
);
2375 bigBit
= curTime
.tv_sec
;
2376 littleBit
= curTime
.tv_usec
;
2379 class DynamicLibraryImpl
: public DynamicLibrary
{
2383 DynamicLibraryImpl(const char *modulePath
) {
2384 m
= g_module_open(modulePath
, G_MODULE_BIND_LAZY
);
2387 virtual ~DynamicLibraryImpl() {
2392 // Use g_module_symbol to get a pointer to the relevant function.
2393 virtual Function
FindFunction(const char *name
) {
2395 gpointer fn_address
= NULL
;
2396 gboolean status
= g_module_symbol(m
, name
, &fn_address
);
2398 return static_cast<Function
>(fn_address
);
2405 virtual bool IsValid() {
2410 DynamicLibrary
*DynamicLibrary::Load(const char *modulePath
) {
2411 return static_cast<DynamicLibrary
*>( new DynamicLibraryImpl(modulePath
) );
2414 double ElapsedTime::Duration(bool reset
) {
2416 g_get_current_time(&curTime
);
2417 long endBigBit
= curTime
.tv_sec
;
2418 long endLittleBit
= curTime
.tv_usec
;
2419 double result
= 1000000.0 * (endBigBit
- bigBit
);
2420 result
+= endLittleBit
- littleBit
;
2421 result
/= 1000000.0;
2424 littleBit
= endLittleBit
;
2429 ColourDesired
Platform::Chrome() {
2430 return ColourDesired(0xe0, 0xe0, 0xe0);
2433 ColourDesired
Platform::ChromeHighlight() {
2434 return ColourDesired(0xff, 0xff, 0xff);
2437 const char *Platform::DefaultFont() {
2439 return "Lucida Console";
2445 int Platform::DefaultFontSize() {
2453 unsigned int Platform::DoubleClickTime() {
2454 return 500; // Half a second
2457 bool Platform::MouseButtonBounce() {
2461 void Platform::DebugDisplay(const char *s
) {
2462 fprintf(stderr
, "%s", s
);
2465 bool Platform::IsKeyDown(int) {
2466 // TODO: discover state of keys in GTK+/X
2470 long Platform::SendScintilla(
2471 WindowID w
, unsigned int msg
, unsigned long wParam
, long lParam
) {
2472 return scintilla_send_message(SCINTILLA(w
), msg
, wParam
, lParam
);
2475 long Platform::SendScintillaPointer(
2476 WindowID w
, unsigned int msg
, unsigned long wParam
, void *lParam
) {
2477 return scintilla_send_message(SCINTILLA(w
), msg
, wParam
,
2478 reinterpret_cast<sptr_t
>(lParam
));
2481 bool Platform::IsDBCSLeadByte(int codePage
, char ch
) {
2482 // Byte ranges found in Wikipedia articles with relevant search strings in each case
2483 unsigned char uch
= static_cast<unsigned char>(ch
);
2487 return ((uch
>= 0x81) && (uch
<= 0x9F)) ||
2488 ((uch
>= 0xE0) && (uch
<= 0xEF));
2491 return (uch
>= 0x81) && (uch
<= 0xFE);
2494 return (uch
>= 0x81) && (uch
<= 0xFE);
2495 // Korean EUC-KR may be code page 949.
2500 int Platform::DBCSCharLength(int codePage
, const char *s
) {
2501 if (codePage
== 932 || codePage
== 936 || codePage
== 950) {
2502 return IsDBCSLeadByte(codePage
, s
[0]) ? 2 : 1;
2504 int bytes
= mblen(s
, MB_CUR_MAX
);
2512 int Platform::DBCSCharMaxLength() {
2517 // These are utility functions not really tied to a platform
2519 int Platform::Minimum(int a
, int b
) {
2526 int Platform::Maximum(int a
, int b
) {
2536 void Platform::DebugPrintf(const char *format
, ...) {
2539 va_start(pArguments
, format
);
2540 vsprintf(buffer
, format
, pArguments
);
2542 Platform::DebugDisplay(buffer
);
2545 void Platform::DebugPrintf(const char *, ...) {}
2549 // Not supported for GTK+
2550 static bool assertionPopUps
= true;
2552 bool Platform::ShowAssertionPopUps(bool assertionPopUps_
) {
2553 bool ret
= assertionPopUps
;
2554 assertionPopUps
= assertionPopUps_
;
2558 void Platform::Assert(const char *c
, const char *file
, int line
) {
2560 sprintf(buffer
, "Assertion [%s] failed at %s %d", c
, file
, line
);
2561 strcat(buffer
, "\r\n");
2562 Platform::DebugDisplay(buffer
);
2566 int Platform::Clamp(int val
, int minVal
, int maxVal
) {
2574 void Platform_Initialise() {
2575 FontMutexAllocate();
2578 void Platform_Finalise() {