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.
21 #include <gdk/gdkkeysyms.h>
25 #include "Scintilla.h"
26 #include "ScintillaWidget.h"
27 #include "StringCopy.h"
29 #include "UniConversion.h"
31 #if defined(__clang__)
32 // Clang 3.0 incorrectly displays sentinel warnings. Fixed by clang 3.1.
33 #pragma GCC diagnostic ignored "-Wsentinel"
36 #include "Converter.h"
38 static const double kPi
= 3.14159265358979323846;
40 // The Pango version guard for pango_units_from_double and pango_units_to_double
41 // is more complex than simply implementing these here.
43 static int pangoUnitsFromDouble(double d
) {
44 return static_cast<int>(d
* PANGO_SCALE
+ 0.5);
47 static double doubleFromPangoUnits(int pu
) {
48 return static_cast<double>(pu
) / PANGO_SCALE
;
51 static cairo_surface_t
*CreateSimilarSurface(GdkWindow
*window
, cairo_content_t content
, int width
, int height
) {
52 #if GTK_CHECK_VERSION(2,22,0)
53 return gdk_window_create_similar_surface(window
, content
, width
, height
);
55 cairo_surface_t
*window_surface
, *surface
;
57 g_return_val_if_fail(GDK_IS_WINDOW(window
), NULL
);
59 window_surface
= GDK_DRAWABLE_GET_CLASS(window
)->ref_cairo_surface(window
);
61 surface
= cairo_surface_create_similar(window_surface
, content
, width
, height
);
63 cairo_surface_destroy(window_surface
);
69 static GdkWindow
*WindowFromWidget(GtkWidget
*w
) {
70 return gtk_widget_get_window(w
);
74 // Ignore unreferenced local functions in GTK+ headers
75 #pragma warning(disable: 4505)
79 using namespace Scintilla
;
82 enum encodingType
{ singleByte
, UTF8
, dbcs
};
84 // Holds a PangoFontDescription*.
87 PangoFontDescription
*pfd
;
89 FontHandle() : pfd(0), characterSet(-1) {
91 FontHandle(PangoFontDescription
*pfd_
, int characterSet_
) {
93 characterSet
= characterSet_
;
97 pango_font_description_free(pfd
);
100 static FontHandle
*CreateNewFont(const FontParameters
&fp
);
103 FontHandle
*FontHandle::CreateNewFont(const FontParameters
&fp
) {
104 PangoFontDescription
*pfd
= pango_font_description_new();
106 pango_font_description_set_family(pfd
,
107 (fp
.faceName
[0] == '!') ? fp
.faceName
+1 : fp
.faceName
);
108 pango_font_description_set_size(pfd
, pangoUnitsFromDouble(fp
.size
));
109 pango_font_description_set_weight(pfd
, static_cast<PangoWeight
>(fp
.weight
));
110 pango_font_description_set_style(pfd
, fp
.italic
? PANGO_STYLE_ITALIC
: PANGO_STYLE_NORMAL
);
111 return new FontHandle(pfd
,fp
.characterSet
);
117 // X has a 16 bit coordinate space, so stop drawing here to avoid wrapping
118 static const int maxCoordinate
= 32000;
120 static FontHandle
*PFont(Font
&f
) {
121 return static_cast<FontHandle
*>(f
.GetID());
124 static GtkWidget
*PWidget(WindowID wid
) {
125 return static_cast<GtkWidget
*>(wid
);
128 Point
Point::FromLong(long lpoint
) {
130 Platform::LowShortFromLong(lpoint
),
131 Platform::HighShortFromLong(lpoint
));
134 Font::Font() : fid(0) {}
138 void Font::Create(const FontParameters
&fp
) {
140 fid
= FontHandle::CreateNewFont(fp
);
143 void Font::Release() {
145 delete static_cast<FontHandle
*>(fid
);
151 namespace Scintilla
{
154 // SurfaceID is a cairo_t*
155 class SurfaceImpl
: public Surface
{
158 cairo_surface_t
*psurf
;
163 PangoContext
*pcontext
;
167 void SetConverter(int characterSet_
);
170 virtual ~SurfaceImpl();
172 void Init(WindowID wid
);
173 void Init(SurfaceID sid
, WindowID wid
);
174 void InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
);
178 void PenColour(ColourDesired fore
);
180 int DeviceHeightFont(int points
);
181 void MoveTo(int x_
, int y_
);
182 void LineTo(int x_
, int y_
);
183 void Polygon(Point
*pts
, int npts
, ColourDesired fore
, ColourDesired back
);
184 void RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
185 void FillRectangle(PRectangle rc
, ColourDesired back
);
186 void FillRectangle(PRectangle rc
, Surface
&surfacePattern
);
187 void RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
188 void AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
189 ColourDesired outline
, int alphaOutline
, int flags
);
190 void DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
);
191 void Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
);
192 void Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
);
194 void DrawTextBase(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
195 void DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
196 void DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
, ColourDesired back
);
197 void DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
, ColourDesired fore
);
198 void MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
);
199 XYPOSITION
WidthText(Font
&font_
, const char *s
, int len
);
200 XYPOSITION
WidthChar(Font
&font_
, char ch
);
201 XYPOSITION
Ascent(Font
&font_
);
202 XYPOSITION
Descent(Font
&font_
);
203 XYPOSITION
InternalLeading(Font
&font_
);
204 XYPOSITION
ExternalLeading(Font
&font_
);
205 XYPOSITION
Height(Font
&font_
);
206 XYPOSITION
AverageCharWidth(Font
&font_
);
208 void SetClip(PRectangle rc
);
209 void FlushCachedState();
211 void SetUnicodeMode(bool unicodeMode_
);
212 void SetDBCSMode(int codePage
);
218 const char *CharacterSetID(int characterSet
) {
219 switch (characterSet
) {
220 case SC_CHARSET_ANSI
:
222 case SC_CHARSET_DEFAULT
:
224 case SC_CHARSET_BALTIC
:
225 return "ISO-8859-13";
226 case SC_CHARSET_CHINESEBIG5
:
228 case SC_CHARSET_EASTEUROPE
:
230 case SC_CHARSET_GB2312
:
232 case SC_CHARSET_GREEK
:
234 case SC_CHARSET_HANGUL
:
240 case SC_CHARSET_RUSSIAN
:
242 case SC_CHARSET_OEM866
:
244 case SC_CHARSET_CYRILLIC
:
246 case SC_CHARSET_SHIFTJIS
:
248 case SC_CHARSET_SYMBOL
:
250 case SC_CHARSET_TURKISH
:
252 case SC_CHARSET_JOHAB
:
254 case SC_CHARSET_HEBREW
:
256 case SC_CHARSET_ARABIC
:
258 case SC_CHARSET_VIETNAMESE
:
260 case SC_CHARSET_THAI
:
261 return "ISO-8859-11";
262 case SC_CHARSET_8859_15
:
263 return "ISO-8859-15";
269 void SurfaceImpl::SetConverter(int characterSet_
) {
270 if (characterSet
!= characterSet_
) {
271 characterSet
= characterSet_
;
272 conv
.Open("UTF-8", CharacterSetID(characterSet
), false);
276 SurfaceImpl::SurfaceImpl() : et(singleByte
),
279 x(0), y(0), inited(false), createdGC(false)
280 , pcontext(0), layout(0), characterSet(-1) {
283 SurfaceImpl::~SurfaceImpl() {
287 void SurfaceImpl::Release() {
291 cairo_destroy(context
);
295 cairo_surface_destroy(psurf
);
298 g_object_unref(layout
);
301 g_object_unref(pcontext
);
311 bool SurfaceImpl::Initialised() {
312 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 8, 0)
313 if (inited
&& context
) {
314 if (cairo_status(context
) == CAIRO_STATUS_SUCCESS
) {
315 // Even when status is success, the target surface may have been
316 // finished whch may cause an assertion to fail crashing the application.
317 // The cairo_surface_has_show_text_glyphs call checks the finished flag
318 // and when set, sets the status to CAIRO_STATUS_SURFACE_FINISHED
319 // which leads to warning messages instead of crashes.
320 // Performing the check in this method as it is called rarely and has no
321 // other side effects.
322 cairo_surface_t
*psurfContext
= cairo_get_target(context
);
324 cairo_surface_has_show_text_glyphs(psurfContext
);
327 return cairo_status(context
) == CAIRO_STATUS_SUCCESS
;
333 void SurfaceImpl::Init(WindowID wid
) {
335 PLATFORM_ASSERT(wid
);
336 // if we are only created from a window ID, we can't perform drawing
340 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
341 PLATFORM_ASSERT(pcontext
);
342 layout
= pango_layout_new(pcontext
);
343 PLATFORM_ASSERT(layout
);
347 void SurfaceImpl::Init(SurfaceID sid
, WindowID wid
) {
348 PLATFORM_ASSERT(sid
);
350 PLATFORM_ASSERT(wid
);
351 context
= cairo_reference(static_cast<cairo_t
*>(sid
));
352 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
353 // update the Pango context in case sid isn't the widget's surface
354 pango_cairo_update_context(context
, pcontext
);
355 layout
= pango_layout_new(pcontext
);
356 cairo_set_line_width(context
, 1);
361 void SurfaceImpl::InitPixMap(int width
, int height
, Surface
*surface_
, WindowID wid
) {
362 PLATFORM_ASSERT(surface_
);
364 SurfaceImpl
*surfImpl
= static_cast<SurfaceImpl
*>(surface_
);
365 PLATFORM_ASSERT(wid
);
366 context
= cairo_reference(surfImpl
->context
);
367 pcontext
= gtk_widget_create_pango_context(PWidget(wid
));
368 // update the Pango context in case surface_ isn't the widget's surface
369 pango_cairo_update_context(context
, pcontext
);
370 PLATFORM_ASSERT(pcontext
);
371 layout
= pango_layout_new(pcontext
);
372 PLATFORM_ASSERT(layout
);
373 if (height
> 0 && width
> 0)
374 psurf
= CreateSimilarSurface(
375 WindowFromWidget(PWidget(wid
)),
376 CAIRO_CONTENT_COLOR_ALPHA
, width
, height
);
377 cairo_destroy(context
);
378 context
= cairo_create(psurf
);
379 cairo_rectangle(context
, 0, 0, width
, height
);
380 cairo_set_source_rgb(context
, 1.0, 0, 0);
382 // This produces sharp drawing more similar to GDK:
383 //cairo_set_antialias(context, CAIRO_ANTIALIAS_NONE);
384 cairo_set_line_width(context
, 1);
390 void SurfaceImpl::PenColour(ColourDesired fore
) {
392 ColourDesired
cdFore(fore
.AsLong());
393 cairo_set_source_rgb(context
,
394 cdFore
.GetRed() / 255.0,
395 cdFore
.GetGreen() / 255.0,
396 cdFore
.GetBlue() / 255.0);
400 int SurfaceImpl::LogPixelsY() {
404 int SurfaceImpl::DeviceHeightFont(int points
) {
405 int logPix
= LogPixelsY();
406 return (points
* logPix
+ logPix
/ 2) / 72;
409 void SurfaceImpl::MoveTo(int x_
, int y_
) {
414 static int Delta(int difference
) {
417 else if (difference
> 0)
423 void SurfaceImpl::LineTo(int x_
, int y_
) {
424 // cairo_line_to draws the end position, unlike Win32 or GDK with GDK_CAP_NOT_LAST.
425 // For simple cases, move back one pixel from end.
428 int xDelta
= Delta(xDiff
);
430 int yDelta
= Delta(yDiff
);
431 if ((xDiff
== 0) || (yDiff
== 0)) {
432 // Horizontal or vertical lines can be more precisely drawn as a filled rectangle
433 int xEnd
= x_
- xDelta
;
434 int left
= Platform::Minimum(x
, xEnd
);
435 int width
= abs(x
- xEnd
) + 1;
436 int yEnd
= y_
- yDelta
;
437 int top
= Platform::Minimum(y
, yEnd
);
438 int height
= abs(y
- yEnd
) + 1;
439 cairo_rectangle(context
, left
, top
, width
, height
);
441 } else if ((abs(xDiff
) == abs(yDiff
))) {
443 cairo_move_to(context
, x
+ 0.5, y
+ 0.5);
444 cairo_line_to(context
, x_
+ 0.5 - xDelta
, y_
+ 0.5 - yDelta
);
446 // Line has a different slope so difficult to avoid last pixel
447 cairo_move_to(context
, x
+ 0.5, y
+ 0.5);
448 cairo_line_to(context
, x_
+ 0.5, y_
+ 0.5);
450 cairo_stroke(context
);
456 void SurfaceImpl::Polygon(Point
*pts
, int npts
, ColourDesired fore
,
457 ColourDesired back
) {
458 PLATFORM_ASSERT(context
);
460 cairo_move_to(context
, pts
[0].x
+ 0.5, pts
[0].y
+ 0.5);
461 for (int i
= 1; i
< npts
; i
++) {
462 cairo_line_to(context
, pts
[i
].x
+ 0.5, pts
[i
].y
+ 0.5);
464 cairo_close_path(context
);
465 cairo_fill_preserve(context
);
467 cairo_stroke(context
);
470 void SurfaceImpl::RectangleDraw(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
472 cairo_rectangle(context
, rc
.left
+ 0.5, rc
.top
+ 0.5,
473 rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1);
475 cairo_fill_preserve(context
);
477 cairo_stroke(context
);
481 void SurfaceImpl::FillRectangle(PRectangle rc
, ColourDesired back
) {
483 if (context
&& (rc
.left
< maxCoordinate
)) { // Protect against out of range
484 rc
.left
= lround(rc
.left
);
485 rc
.right
= lround(rc
.right
);
486 cairo_rectangle(context
, rc
.left
, rc
.top
,
487 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
492 void SurfaceImpl::FillRectangle(PRectangle rc
, Surface
&surfacePattern
) {
493 SurfaceImpl
&surfi
= static_cast<SurfaceImpl
&>(surfacePattern
);
494 bool canDraw
= surfi
.psurf
!= NULL
;
496 PLATFORM_ASSERT(context
);
497 // Tile pattern over rectangle
498 // Currently assumes 8x8 pattern
501 for (int xTile
= rc
.left
; xTile
< rc
.right
; xTile
+= widthPat
) {
502 int widthx
= (xTile
+ widthPat
> rc
.right
) ? rc
.right
- xTile
: widthPat
;
503 for (int yTile
= rc
.top
; yTile
< rc
.bottom
; yTile
+= heightPat
) {
504 int heighty
= (yTile
+ heightPat
> rc
.bottom
) ? rc
.bottom
- yTile
: heightPat
;
505 cairo_set_source_surface(context
, surfi
.psurf
, xTile
, yTile
);
506 cairo_rectangle(context
, xTile
, yTile
, widthx
, heighty
);
511 // Something is wrong so try to show anyway
512 // Shows up black because colour not allocated
513 FillRectangle(rc
, ColourDesired(0));
517 void SurfaceImpl::RoundedRectangle(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
518 if (((rc
.right
- rc
.left
) > 4) && ((rc
.bottom
- rc
.top
) > 4)) {
519 // Approximate a round rect with some cut off corners
521 Point(rc
.left
+ 2, rc
.top
),
522 Point(rc
.right
- 2, rc
.top
),
523 Point(rc
.right
, rc
.top
+ 2),
524 Point(rc
.right
, rc
.bottom
- 2),
525 Point(rc
.right
- 2, rc
.bottom
),
526 Point(rc
.left
+ 2, rc
.bottom
),
527 Point(rc
.left
, rc
.bottom
- 2),
528 Point(rc
.left
, rc
.top
+ 2),
530 Polygon(pts
, ELEMENTS(pts
), fore
, back
);
532 RectangleDraw(rc
, fore
, back
);
536 static void PathRoundRectangle(cairo_t
*context
, double left
, double top
, double width
, double height
, int radius
) {
537 double degrees
= kPi
/ 180.0;
539 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 2, 0)
540 cairo_new_sub_path(context
);
542 // First arc is in the top-right corner and starts from a point on the top line
543 cairo_move_to(context
, left
+ width
- radius
, top
);
545 cairo_arc(context
, left
+ width
- radius
, top
+ radius
, radius
, -90 * degrees
, 0 * degrees
);
546 cairo_arc(context
, left
+ width
- radius
, top
+ height
- radius
, radius
, 0 * degrees
, 90 * degrees
);
547 cairo_arc(context
, left
+ radius
, top
+ height
- radius
, radius
, 90 * degrees
, 180 * degrees
);
548 cairo_arc(context
, left
+ radius
, top
+ radius
, radius
, 180 * degrees
, 270 * degrees
);
549 cairo_close_path(context
);
552 void SurfaceImpl::AlphaRectangle(PRectangle rc
, int cornerSize
, ColourDesired fill
, int alphaFill
,
553 ColourDesired outline
, int alphaOutline
, int flags
) {
554 if (context
&& rc
.Width() > 0) {
555 ColourDesired
cdFill(fill
.AsLong());
556 cairo_set_source_rgba(context
,
557 cdFill
.GetRed() / 255.0,
558 cdFill
.GetGreen() / 255.0,
559 cdFill
.GetBlue() / 255.0,
562 PathRoundRectangle(context
, rc
.left
+ 1.0, rc
.top
+ 1.0, rc
.right
- rc
.left
- 2.0, rc
.bottom
- rc
.top
- 2.0, cornerSize
);
564 cairo_rectangle(context
, rc
.left
+ 1.0, rc
.top
+ 1.0, rc
.right
- rc
.left
- 2.0, rc
.bottom
- rc
.top
- 2.0);
567 ColourDesired
cdOutline(outline
.AsLong());
568 cairo_set_source_rgba(context
,
569 cdOutline
.GetRed() / 255.0,
570 cdOutline
.GetGreen() / 255.0,
571 cdOutline
.GetBlue() / 255.0,
572 alphaOutline
/ 255.0);
574 PathRoundRectangle(context
, rc
.left
+ 0.5, rc
.top
+ 0.5, rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1, cornerSize
);
576 cairo_rectangle(context
, rc
.left
+ 0.5, rc
.top
+ 0.5, rc
.right
- rc
.left
- 1, rc
.bottom
- rc
.top
- 1);
577 cairo_stroke(context
);
581 void SurfaceImpl::DrawRGBAImage(PRectangle rc
, int width
, int height
, const unsigned char *pixelsImage
) {
582 PLATFORM_ASSERT(context
);
583 if (rc
.Width() > width
)
584 rc
.left
+= (rc
.Width() - width
) / 2;
585 rc
.right
= rc
.left
+ width
;
586 if (rc
.Height() > height
)
587 rc
.top
+= (rc
.Height() - height
) / 2;
588 rc
.bottom
= rc
.top
+ height
;
590 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0)
591 int stride
= cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32
, width
);
593 int stride
= width
* 4;
595 int ucs
= stride
* height
;
596 std::vector
<unsigned char> image(ucs
);
597 for (int iy
=0; iy
<height
; iy
++) {
598 for (int ix
=0; ix
<width
; ix
++) {
599 unsigned char *pixel
= &image
[0] + iy
*stride
+ ix
* 4;
600 unsigned char alpha
= pixelsImage
[3];
601 pixel
[2] = (*pixelsImage
++) * alpha
/ 255;
602 pixel
[1] = (*pixelsImage
++) * alpha
/ 255;
603 pixel
[0] = (*pixelsImage
++) * alpha
/ 255;
604 pixel
[3] = *pixelsImage
++;
608 cairo_surface_t
*psurfImage
= cairo_image_surface_create_for_data(&image
[0], CAIRO_FORMAT_ARGB32
, width
, height
, stride
);
609 cairo_set_source_surface(context
, psurfImage
, rc
.left
, rc
.top
);
610 cairo_rectangle(context
, rc
.left
, rc
.top
, rc
.right
-rc
.left
, rc
.bottom
-rc
.top
);
613 cairo_surface_destroy(psurfImage
);
616 void SurfaceImpl::Ellipse(PRectangle rc
, ColourDesired fore
, ColourDesired back
) {
617 PLATFORM_ASSERT(context
);
619 cairo_arc(context
, (rc
.left
+ rc
.right
) / 2, (rc
.top
+ rc
.bottom
) / 2,
620 Platform::Minimum(rc
.Width(), rc
.Height()) / 2, 0, 2*kPi
);
621 cairo_fill_preserve(context
);
623 cairo_stroke(context
);
626 void SurfaceImpl::Copy(PRectangle rc
, Point from
, Surface
&surfaceSource
) {
627 SurfaceImpl
&surfi
= static_cast<SurfaceImpl
&>(surfaceSource
);
628 bool canDraw
= surfi
.psurf
!= NULL
;
630 PLATFORM_ASSERT(context
);
631 cairo_set_source_surface(context
, surfi
.psurf
,
632 rc
.left
- from
.x
, rc
.top
- from
.y
);
633 cairo_rectangle(context
, rc
.left
, rc
.top
, rc
.right
-rc
.left
, rc
.bottom
-rc
.top
);
638 std::string
UTF8FromLatin1(const char *s
, int len
) {
639 std::string
utfForm(len
*2 + 1, '\0');
641 for (int i
=0; i
<len
; i
++) {
642 unsigned int uch
= static_cast<unsigned char>(s
[i
]);
644 utfForm
[lenU
++] = uch
;
646 utfForm
[lenU
++] = static_cast<char>(0xC0 | (uch
>> 6));
647 utfForm
[lenU
++] = static_cast<char>(0x80 | (uch
& 0x3f));
650 utfForm
.resize(lenU
);
654 static std::string
UTF8FromIconv(const Converter
&conv
, const char *s
, int len
) {
656 std::string
utfForm(len
*3+1, '\0');
657 char *pin
= const_cast<char *>(s
);
659 char *putf
= &utfForm
[0];
661 size_t outLeft
= len
*3+1;
662 size_t conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
663 if (conversions
!= ((size_t)(-1))) {
665 utfForm
.resize(pout
- putf
);
669 return std::string();
672 // Work out how many bytes are in a character by trying to convert using iconv,
673 // returning the first length that succeeds.
674 static size_t MultiByteLenFromIconv(const Converter
&conv
, const char *s
, size_t len
) {
675 for (size_t lenMB
=1; (lenMB
<4) && (lenMB
<= len
); lenMB
++) {
677 char *pin
= const_cast<char *>(s
);
678 size_t inLeft
= lenMB
;
681 size_t conversions
= conv
.Convert(&pin
, &inLeft
, &pout
, &outLeft
);
682 if (conversions
!= ((size_t)(-1))) {
689 void SurfaceImpl::DrawTextBase(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
690 ColourDesired fore
) {
693 XYPOSITION xText
= rc
.left
;
694 if (PFont(font_
)->pfd
) {
697 pango_layout_set_text(layout
, s
, len
);
699 SetConverter(PFont(font_
)->characterSet
);
700 utfForm
= UTF8FromIconv(conv
, s
, len
);
701 if (utfForm
.empty()) { // iconv failed so treat as Latin1
702 utfForm
= UTF8FromLatin1(s
, len
);
704 pango_layout_set_text(layout
, utfForm
.c_str(), utfForm
.length());
706 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
707 pango_cairo_update_layout(context
, layout
);
709 PangoLayoutLine
*pll
= pango_layout_get_line_readonly(layout
,0);
711 PangoLayoutLine
*pll
= pango_layout_get_line(layout
,0);
713 cairo_move_to(context
, xText
, ybase
);
714 pango_cairo_show_layout_line(context
, pll
);
719 void SurfaceImpl::DrawTextNoClip(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
720 ColourDesired fore
, ColourDesired back
) {
721 FillRectangle(rc
, back
);
722 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
725 // On GTK+, exactly same as DrawTextNoClip
726 void SurfaceImpl::DrawTextClipped(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
727 ColourDesired fore
, ColourDesired back
) {
728 FillRectangle(rc
, back
);
729 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
732 void SurfaceImpl::DrawTextTransparent(PRectangle rc
, Font
&font_
, XYPOSITION ybase
, const char *s
, int len
,
733 ColourDesired fore
) {
734 // Avoid drawing spaces in transparent mode
735 for (int i
=0; i
<len
; i
++) {
737 DrawTextBase(rc
, font_
, ybase
, s
, len
, fore
);
743 class ClusterIterator
{
744 PangoLayoutIter
*iter
;
749 XYPOSITION positionStart
;
753 ClusterIterator(PangoLayout
*layout
, int len
) : lenPositions(len
), finished(false),
754 positionStart(0), position(0), distance(0), curIndex(0) {
755 iter
= pango_layout_get_iter(layout
);
756 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
759 pango_layout_iter_free(iter
);
763 positionStart
= position
;
764 if (pango_layout_iter_next_cluster(iter
)) {
765 pango_layout_iter_get_cluster_extents(iter
, NULL
, &pos
);
766 position
= doubleFromPangoUnits(pos
.x
);
767 curIndex
= pango_layout_iter_get_index(iter
);
770 position
= doubleFromPangoUnits(pos
.x
+ pos
.width
);
771 curIndex
= lenPositions
;
773 distance
= position
- positionStart
;
777 void SurfaceImpl::MeasureWidths(Font
&font_
, const char *s
, int len
, XYPOSITION
*positions
) {
779 const int lenPositions
= len
;
780 if (PFont(font_
)->pfd
) {
781 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
783 // Simple and direct as UTF-8 is native Pango encoding
785 pango_layout_set_text(layout
, s
, len
);
786 ClusterIterator
iti(layout
, lenPositions
);
787 while (!iti
.finished
) {
789 int places
= iti
.curIndex
- i
;
790 while (i
< iti
.curIndex
) {
791 // Evenly distribute space among bytes of this cluster.
792 // Would be better to find number of characters and then
793 // divide evenly between characters with each byte of a character
794 // being at the same position.
795 positions
[i
] = iti
.position
- (iti
.curIndex
- 1 - i
) * iti
.distance
/ places
;
799 PLATFORM_ASSERT(i
== lenPositions
);
801 int positionsCalculated
= 0;
803 SetConverter(PFont(font_
)->characterSet
);
804 std::string utfForm
= UTF8FromIconv(conv
, s
, len
);
805 if (!utfForm
.empty()) {
806 // Convert to UTF-8 so can ask Pango for widths, then
807 // Loop through UTF-8 and DBCS forms, taking account of different
808 // character byte lengths.
809 Converter
convMeasure("UCS-2", CharacterSetID(characterSet
), false);
810 pango_layout_set_text(layout
, utfForm
.c_str(), strlen(utfForm
.c_str()));
812 int clusterStart
= 0;
813 ClusterIterator
iti(layout
, strlen(utfForm
.c_str()));
814 while (!iti
.finished
) {
816 int clusterEnd
= iti
.curIndex
;
817 int places
= g_utf8_strlen(utfForm
.c_str() + clusterStart
, clusterEnd
- clusterStart
);
819 while (clusterStart
< clusterEnd
) {
820 size_t lenChar
= MultiByteLenFromIconv(convMeasure
, s
+i
, len
-i
);
822 positions
[i
++] = iti
.position
- (places
- place
) * iti
.distance
/ places
;
823 positionsCalculated
++;
825 clusterStart
+= UTF8CharLength(static_cast<unsigned char>(utfForm
.c_str()[clusterStart
]));
829 PLATFORM_ASSERT(i
== lenPositions
);
832 if (positionsCalculated
< 1 ) {
833 // Either 8-bit or DBCS conversion failed so treat as 8-bit.
834 SetConverter(PFont(font_
)->characterSet
);
835 const bool rtlCheck
= PFont(font_
)->characterSet
== SC_CHARSET_HEBREW
||
836 PFont(font_
)->characterSet
== SC_CHARSET_ARABIC
;
837 std::string utfForm
= UTF8FromIconv(conv
, s
, len
);
838 if (utfForm
.empty()) {
839 utfForm
= UTF8FromLatin1(s
, len
);
841 pango_layout_set_text(layout
, utfForm
.c_str(), utfForm
.length());
843 int clusterStart
= 0;
844 // Each 8-bit input character may take 1 or 2 bytes in UTF-8
845 // and groups of up to 3 may be represented as ligatures.
846 ClusterIterator
iti(layout
, utfForm
.length());
847 while (!iti
.finished
) {
849 int clusterEnd
= iti
.curIndex
;
850 int ligatureLength
= g_utf8_strlen(utfForm
.c_str() + clusterStart
, clusterEnd
- clusterStart
);
851 if (rtlCheck
&& ((clusterEnd
<= clusterStart
) || (ligatureLength
== 0) || (ligatureLength
> 3))) {
852 // Something has gone wrong: exit quickly but pretend all the characters are equally spaced:
854 pango_layout_get_size(layout
, &widthLayout
, NULL
);
855 XYPOSITION widthTotal
= doubleFromPangoUnits(widthLayout
);
856 for (int bytePos
=0; bytePos
<lenPositions
; bytePos
++) {
857 positions
[bytePos
] = widthTotal
/ lenPositions
* (bytePos
+ 1);
861 PLATFORM_ASSERT(ligatureLength
> 0 && ligatureLength
<= 3);
862 for (int charInLig
=0; charInLig
<ligatureLength
; charInLig
++) {
863 positions
[i
++] = iti
.position
- (ligatureLength
- 1 - charInLig
) * iti
.distance
/ ligatureLength
;
865 clusterStart
= clusterEnd
;
867 while (i
< lenPositions
) {
868 // If something failed, fill in rest of the positions
869 positions
[i
++] = clusterStart
;
871 PLATFORM_ASSERT(i
== lenPositions
);
876 // No font so return an ascending range of values
877 for (int i
= 0; i
< len
; i
++) {
878 positions
[i
] = i
+ 1;
883 XYPOSITION
SurfaceImpl::WidthText(Font
&font_
, const char *s
, int len
) {
885 if (PFont(font_
)->pfd
) {
887 pango_layout_set_font_description(layout
, PFont(font_
)->pfd
);
890 pango_layout_set_text(layout
, s
, len
);
892 SetConverter(PFont(font_
)->characterSet
);
893 utfForm
= UTF8FromIconv(conv
, s
, len
);
894 if (utfForm
.empty()) { // iconv failed so treat as Latin1
895 utfForm
= UTF8FromLatin1(s
, len
);
897 pango_layout_set_text(layout
, utfForm
.c_str(), utfForm
.length());
900 PangoLayoutLine
*pangoLine
= pango_layout_get_line_readonly(layout
,0);
902 PangoLayoutLine
*pangoLine
= pango_layout_get_line(layout
,0);
904 pango_layout_line_get_extents(pangoLine
, NULL
, &pos
);
905 return doubleFromPangoUnits(pos
.width
);
913 XYPOSITION
SurfaceImpl::WidthChar(Font
&font_
, char ch
) {
915 if (PFont(font_
)->pfd
) {
916 return WidthText(font_
, &ch
, 1);
924 // Ascent and descent determined by Pango font metrics.
926 XYPOSITION
SurfaceImpl::Ascent(Font
&font_
) {
927 if (!(font_
.GetID()))
930 if (PFont(font_
)->pfd
) {
931 PangoFontMetrics
*metrics
= pango_context_get_metrics(pcontext
,
932 PFont(font_
)->pfd
, pango_context_get_language(pcontext
));
934 doubleFromPangoUnits(pango_font_metrics_get_ascent(metrics
));
935 pango_font_metrics_unref(metrics
);
943 XYPOSITION
SurfaceImpl::Descent(Font
&font_
) {
944 if (!(font_
.GetID()))
946 if (PFont(font_
)->pfd
) {
947 PangoFontMetrics
*metrics
= pango_context_get_metrics(pcontext
,
948 PFont(font_
)->pfd
, pango_context_get_language(pcontext
));
949 int descent
= doubleFromPangoUnits(pango_font_metrics_get_descent(metrics
));
950 pango_font_metrics_unref(metrics
);
956 XYPOSITION
SurfaceImpl::InternalLeading(Font
&) {
960 XYPOSITION
SurfaceImpl::ExternalLeading(Font
&) {
964 XYPOSITION
SurfaceImpl::Height(Font
&font_
) {
965 return Ascent(font_
) + Descent(font_
);
968 XYPOSITION
SurfaceImpl::AverageCharWidth(Font
&font_
) {
969 return WidthChar(font_
, 'n');
972 void SurfaceImpl::SetClip(PRectangle rc
) {
973 PLATFORM_ASSERT(context
);
974 cairo_rectangle(context
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
978 void SurfaceImpl::FlushCachedState() {}
980 void SurfaceImpl::SetUnicodeMode(bool unicodeMode_
) {
985 void SurfaceImpl::SetDBCSMode(int codePage
) {
986 if (codePage
&& (codePage
!= SC_CP_UTF8
))
990 Surface
*Surface::Allocate(int) {
991 return new SurfaceImpl();
996 void Window::Destroy() {
998 ListBox
*listbox
= dynamic_cast<ListBox
*>(this);
1000 gtk_widget_hide(GTK_WIDGET(wid
));
1001 // clear up window content
1003 // resize the window to the smallest possible size for it to adapt
1004 // to future content
1005 gtk_window_resize(GTK_WINDOW(wid
), 1, 1);
1007 gtk_widget_destroy(GTK_WIDGET(wid
));
1013 bool Window::HasFocus() {
1014 return gtk_widget_has_focus(GTK_WIDGET(wid
));
1017 PRectangle
Window::GetPosition() {
1018 // Before any size allocated pretend its 1000 wide so not scrolled
1019 PRectangle
rc(0, 0, 1000, 1000);
1021 GtkAllocation allocation
;
1022 gtk_widget_get_allocation(PWidget(wid
), &allocation
);
1023 rc
.left
= allocation
.x
;
1024 rc
.top
= allocation
.y
;
1025 if (allocation
.width
> 20) {
1026 rc
.right
= rc
.left
+ allocation
.width
;
1027 rc
.bottom
= rc
.top
+ allocation
.height
;
1033 void Window::SetPosition(PRectangle rc
) {
1034 GtkAllocation alloc
;
1037 alloc
.width
= rc
.Width();
1038 alloc
.height
= rc
.Height();
1039 gtk_widget_size_allocate(PWidget(wid
), &alloc
);
1042 void Window::SetPositionRelative(PRectangle rc
, Window relativeTo
) {
1045 gdk_window_get_origin(WindowFromWidget(PWidget(relativeTo
.wid
)), &ox
, &oy
);
1053 /* do some corrections to fit into screen */
1054 int sizex
= rc
.right
- rc
.left
;
1055 int sizey
= rc
.bottom
- rc
.top
;
1056 int screenWidth
= gdk_screen_width();
1057 int screenHeight
= gdk_screen_height();
1058 if (sizex
> screenWidth
)
1059 ox
= 0; /* the best we can do */
1060 else if (ox
+ sizex
> screenWidth
)
1061 ox
= screenWidth
- sizex
;
1062 if (oy
+ sizey
> screenHeight
)
1063 oy
= screenHeight
- sizey
;
1065 gtk_window_move(GTK_WINDOW(PWidget(wid
)), ox
, oy
);
1067 gtk_window_resize(GTK_WINDOW(wid
), sizex
, sizey
);
1070 PRectangle
Window::GetClientPosition() {
1071 // On GTK+, the client position is the window position
1072 return GetPosition();
1075 void Window::Show(bool show
) {
1077 gtk_widget_show(PWidget(wid
));
1080 void Window::InvalidateAll() {
1082 gtk_widget_queue_draw(PWidget(wid
));
1086 void Window::InvalidateRectangle(PRectangle rc
) {
1088 gtk_widget_queue_draw_area(PWidget(wid
),
1090 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
1094 void Window::SetFont(Font
&) {
1095 // Can not be done generically but only needed for ListBox
1098 void Window::SetCursor(Cursor curs
) {
1099 // We don't set the cursor to same value numerous times under gtk because
1100 // it stores the cursor in the window once it's set
1101 if (curs
== cursorLast
)
1105 GdkDisplay
*pdisplay
= gtk_widget_get_display(PWidget(wid
));
1110 gdkCurs
= gdk_cursor_new_for_display(pdisplay
, GDK_XTERM
);
1113 gdkCurs
= gdk_cursor_new_for_display(pdisplay
, GDK_LEFT_PTR
);
1116 gdkCurs
= gdk_cursor_new_for_display(pdisplay
, GDK_CENTER_PTR
);
1119 gdkCurs
= gdk_cursor_new_for_display(pdisplay
, GDK_WATCH
);
1122 gdkCurs
= gdk_cursor_new_for_display(pdisplay
, GDK_HAND2
);
1124 case cursorReverseArrow
:
1125 gdkCurs
= gdk_cursor_new_for_display(pdisplay
, GDK_RIGHT_PTR
);
1128 gdkCurs
= gdk_cursor_new_for_display(pdisplay
, GDK_LEFT_PTR
);
1129 cursorLast
= cursorArrow
;
1133 if (WindowFromWidget(PWidget(wid
)))
1134 gdk_window_set_cursor(WindowFromWidget(PWidget(wid
)), gdkCurs
);
1135 #if GTK_CHECK_VERSION(3,0,0)
1136 g_object_unref(gdkCurs
);
1138 gdk_cursor_unref(gdkCurs
);
1142 void Window::SetTitle(const char *s
) {
1143 gtk_window_set_title(GTK_WINDOW(wid
), s
);
1146 /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
1147 gdk window coordinates */
1148 PRectangle
Window::GetMonitorRect(Point pt
) {
1149 gint x_offset
, y_offset
;
1151 gdk_window_get_origin(WindowFromWidget(PWidget(wid
)), &x_offset
, &y_offset
);
1157 screen
= gtk_widget_get_screen(PWidget(wid
));
1158 monitor_num
= gdk_screen_get_monitor_at_point(screen
, pt
.x
+ x_offset
, pt
.y
+ y_offset
);
1159 gdk_screen_get_monitor_geometry(screen
, monitor_num
, &rect
);
1162 return PRectangle(rect
.x
, rect
.y
, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
1165 typedef std::map
<int, RGBAImage
*> ImageMap
;
1168 const RGBAImage
*rgba_data
;
1172 static void list_image_free(gpointer
, gpointer value
, gpointer
) {
1173 ListImage
*list_image
= static_cast<ListImage
*>(value
);
1174 if (list_image
->pixbuf
)
1175 g_object_unref(list_image
->pixbuf
);
1179 ListBox::ListBox() {
1182 ListBox::~ListBox() {
1191 class ListBoxX
: public ListBox
{
1197 GtkCellRenderer
* pixbuf_renderer
;
1198 GtkCellRenderer
* renderer
;
1199 RGBAImageSet images
;
1200 int desiredVisibleRows
;
1201 unsigned int maxItemCharacters
;
1202 unsigned int aveCharWidth
;
1203 #if GTK_CHECK_VERSION(3,0,0)
1204 GtkCssProvider
*cssProvider
;
1207 CallBackAction doubleClickAction
;
1208 void *doubleClickActionData
;
1210 ListBoxX() : widCached(0), frame(0), list(0), scroller(0), pixhash(NULL
), pixbuf_renderer(0),
1212 desiredVisibleRows(5), maxItemCharacters(0),
1214 #if GTK_CHECK_VERSION(3,0,0)
1217 doubleClickAction(NULL
), doubleClickActionData(NULL
) {
1219 virtual ~ListBoxX() {
1221 g_hash_table_foreach((GHashTable
*) pixhash
, list_image_free
, NULL
);
1222 g_hash_table_destroy((GHashTable
*) pixhash
);
1225 gtk_widget_destroy(GTK_WIDGET(widCached
));
1226 wid
= widCached
= 0;
1228 #if GTK_CHECK_VERSION(3,0,0)
1230 g_object_unref(cssProvider
);
1235 virtual void SetFont(Font
&font
);
1236 virtual void Create(Window
&parent
, int ctrlID
, Point location_
, int lineHeight_
, bool unicodeMode_
, int technology_
);
1237 virtual void SetAverageCharWidth(int width
);
1238 virtual void SetVisibleRows(int rows
);
1239 virtual int GetVisibleRows() const;
1241 virtual PRectangle
GetDesiredRect();
1242 virtual int CaretFromEdge();
1243 virtual void Clear();
1244 virtual void Append(char *s
, int type
= -1);
1245 virtual int Length();
1246 virtual void Select(int n
);
1247 virtual int GetSelection();
1248 virtual int Find(const char *prefix
);
1249 virtual void GetValue(int n
, char *value
, int len
);
1250 void RegisterRGBA(int type
, RGBAImage
*image
);
1251 virtual void RegisterImage(int type
, const char *xpm_data
);
1252 virtual void RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
);
1253 virtual void ClearRegisteredImages();
1254 virtual void SetDoubleClickAction(CallBackAction action
, void *data
) {
1255 doubleClickAction
= action
;
1256 doubleClickActionData
= data
;
1258 virtual void SetList(const char *listText
, char separator
, char typesep
);
1261 ListBox
*ListBox::Allocate() {
1262 ListBoxX
*lb
= new ListBoxX();
1266 // SmallScroller, a GtkScrolledWindow that can shrink very small, as
1267 // gtk_widget_set_size_request() cannot shrink widgets on GTK3
1269 GtkScrolledWindow parent
;
1270 /* Workaround ABI issue with Windows GTK2 bundle and GCC > 3.
1271 See http://lists.geany.org/pipermail/devel/2015-April/thread.html#9379
1273 GtkScrolledWindow contains a bitfield, and GCC 3.4 and 4.8 don't agree
1274 on the size of the structure (regardless of -mms-bitfields):
1275 - GCC 3.4 has sizeof(GtkScrolledWindow)=88
1276 - GCC 4.8 has sizeof(GtkScrolledWindow)=84
1277 As Windows GTK2 bundle is built with GCC 3, it requires types derived
1278 from GtkScrolledWindow to be at least 88 bytes, which means we need to
1279 add some fake padding to fill in the extra 4 bytes.
1280 There is however no other issue with the layout difference as we never
1281 access any GtkScrolledWindow fields ourselves. */
1284 typedef GtkScrolledWindowClass SmallScrollerClass
;
1286 G_DEFINE_TYPE(SmallScroller
, small_scroller
, GTK_TYPE_SCROLLED_WINDOW
)
1288 #if GTK_CHECK_VERSION(3,0,0)
1289 static void small_scroller_get_preferred_height(GtkWidget
*widget
, gint
*min
, gint
*nat
) {
1290 GTK_WIDGET_CLASS(small_scroller_parent_class
)->get_preferred_height(widget
, min
, nat
);
1295 static void small_scroller_size_request(GtkWidget
*widget
, GtkRequisition
*req
) {
1296 GTK_WIDGET_CLASS(small_scroller_parent_class
)->size_request(widget
, req
);
1301 static void small_scroller_class_init(SmallScrollerClass
*klass
) {
1302 #if GTK_CHECK_VERSION(3,0,0)
1303 GTK_WIDGET_CLASS(klass
)->get_preferred_height
= small_scroller_get_preferred_height
;
1305 GTK_WIDGET_CLASS(klass
)->size_request
= small_scroller_size_request
;
1309 static void small_scroller_init(SmallScroller
*){}
1311 static gboolean
ButtonPress(GtkWidget
*, GdkEventButton
* ev
, gpointer p
) {
1313 ListBoxX
* lb
= static_cast<ListBoxX
*>(p
);
1314 if (ev
->type
== GDK_2BUTTON_PRESS
&& lb
->doubleClickAction
!= NULL
) {
1315 lb
->doubleClickAction(lb
->doubleClickActionData
);
1320 // No pointer back to Scintilla to save status
1325 /* Change the active color to the selected color so the listbox uses the color
1326 scheme that it would use if it had the focus. */
1327 static void StyleSet(GtkWidget
*w
, GtkStyle
*, void*) {
1329 g_return_if_fail(w
!= NULL
);
1331 /* Copy the selected color to active. Note that the modify calls will cause
1332 recursive calls to this function after the value is updated and w->style to
1333 be set to a new object */
1335 #if GTK_CHECK_VERSION(3,16,0)
1336 // On recent releases of GTK+, it does not appear necessary to set the list box colours.
1337 // This may be because of common themes and may be needed with other themes.
1338 // The *override* calls are deprecated now, so only call them for older versions of GTK+.
1339 #elif GTK_CHECK_VERSION(3,0,0)
1340 GtkStyleContext
*styleContext
= gtk_widget_get_style_context(w
);
1341 if (styleContext
== NULL
)
1344 GdkRGBA colourForeSelected
;
1345 gtk_style_context_get_color(styleContext
, GTK_STATE_FLAG_SELECTED
, &colourForeSelected
);
1346 GdkRGBA colourForeActive
;
1347 gtk_style_context_get_color(styleContext
, GTK_STATE_FLAG_ACTIVE
, &colourForeActive
);
1348 if (!gdk_rgba_equal(&colourForeSelected
, &colourForeActive
))
1349 gtk_widget_override_color(w
, GTK_STATE_FLAG_ACTIVE
, &colourForeSelected
);
1351 styleContext
= gtk_widget_get_style_context(w
);
1352 if (styleContext
== NULL
)
1355 GdkRGBA colourBaseSelected
;
1356 gtk_style_context_get_background_color(styleContext
, GTK_STATE_FLAG_SELECTED
, &colourBaseSelected
);
1357 GdkRGBA colourBaseActive
;
1358 gtk_style_context_get_background_color(styleContext
, GTK_STATE_FLAG_ACTIVE
, &colourBaseActive
);
1359 if (!gdk_rgba_equal(&colourBaseSelected
, &colourBaseActive
))
1360 gtk_widget_override_background_color(w
, GTK_STATE_FLAG_ACTIVE
, &colourBaseSelected
);
1362 GtkStyle
*style
= gtk_widget_get_style(w
);
1365 if (!gdk_color_equal(&style
->base
[GTK_STATE_SELECTED
], &style
->base
[GTK_STATE_ACTIVE
]))
1366 gtk_widget_modify_base(w
, GTK_STATE_ACTIVE
, &style
->base
[GTK_STATE_SELECTED
]);
1367 style
= gtk_widget_get_style(w
);
1370 if (!gdk_color_equal(&style
->text
[GTK_STATE_SELECTED
], &style
->text
[GTK_STATE_ACTIVE
]))
1371 gtk_widget_modify_text(w
, GTK_STATE_ACTIVE
, &style
->text
[GTK_STATE_SELECTED
]);
1375 void ListBoxX::Create(Window
&, int, Point
, int, bool, int) {
1376 if (widCached
!= 0) {
1381 #if GTK_CHECK_VERSION(3,0,0)
1383 cssProvider
= gtk_css_provider_new();
1387 wid
= widCached
= gtk_window_new(GTK_WINDOW_POPUP
);
1389 frame
= gtk_frame_new(NULL
);
1390 gtk_widget_show(PWidget(frame
));
1391 gtk_container_add(GTK_CONTAINER(GetID()), PWidget(frame
));
1392 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1393 gtk_container_set_border_width(GTK_CONTAINER(frame
), 0);
1395 scroller
= g_object_new(small_scroller_get_type(), NULL
);
1396 gtk_container_set_border_width(GTK_CONTAINER(scroller
), 0);
1397 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller
),
1398 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
1399 gtk_container_add(GTK_CONTAINER(frame
), PWidget(scroller
));
1400 gtk_widget_show(PWidget(scroller
));
1402 /* Tree and its model */
1403 GtkListStore
*store
=
1404 gtk_list_store_new(N_COLUMNS
, GDK_TYPE_PIXBUF
, G_TYPE_STRING
);
1406 list
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
1407 g_signal_connect(G_OBJECT(list
), "style-set", G_CALLBACK(StyleSet
), NULL
);
1409 #if GTK_CHECK_VERSION(3,0,0)
1410 GtkStyleContext
*styleContext
= gtk_widget_get_style_context(GTK_WIDGET(list
));
1412 gtk_style_context_add_provider(styleContext
, GTK_STYLE_PROVIDER(cssProvider
),
1413 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
1417 GtkTreeSelection
*selection
=
1418 gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
1419 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
1420 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list
), FALSE
);
1421 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(list
), FALSE
);
1424 GtkTreeViewColumn
*column
= gtk_tree_view_column_new();
1425 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
1426 gtk_tree_view_column_set_title(column
, "Autocomplete");
1428 pixbuf_renderer
= gtk_cell_renderer_pixbuf_new();
1429 gtk_cell_renderer_set_fixed_size(pixbuf_renderer
, 0, -1);
1430 gtk_tree_view_column_pack_start(column
, pixbuf_renderer
, FALSE
);
1431 gtk_tree_view_column_add_attribute(column
, pixbuf_renderer
,
1432 "pixbuf", PIXBUF_COLUMN
);
1434 renderer
= gtk_cell_renderer_text_new();
1435 gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer
), 1);
1436 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
1437 gtk_tree_view_column_add_attribute(column
, renderer
,
1438 "text", TEXT_COLUMN
);
1440 gtk_tree_view_append_column(GTK_TREE_VIEW(list
), column
);
1441 if (g_object_class_find_property(G_OBJECT_GET_CLASS(list
), "fixed-height-mode"))
1442 g_object_set(G_OBJECT(list
), "fixed-height-mode", TRUE
, NULL
);
1444 GtkWidget
*widget
= PWidget(list
); // No code inside the G_OBJECT macro
1445 gtk_container_add(GTK_CONTAINER(PWidget(scroller
)), widget
);
1446 gtk_widget_show(widget
);
1447 g_signal_connect(G_OBJECT(widget
), "button_press_event",
1448 G_CALLBACK(ButtonPress
), this);
1451 void ListBoxX::SetFont(Font
&scint_font
) {
1452 // Only do for Pango font as there have been crashes for GDK fonts
1453 if (Created() && PFont(scint_font
)->pfd
) {
1454 // Current font is Pango font
1455 #if GTK_CHECK_VERSION(3,0,0)
1457 PangoFontDescription
*pfd
= PFont(scint_font
)->pfd
;
1458 std::ostringstream ssFontSetting
;
1459 ssFontSetting
<< "GtkTreeView { ";
1460 ssFontSetting
<< "font-family: " << pango_font_description_get_family(pfd
) << "; ";
1461 ssFontSetting
<< "font-size:";
1462 ssFontSetting
<< static_cast<double>(pango_font_description_get_size(pfd
)) / PANGO_SCALE
;
1463 ssFontSetting
<< "px; ";
1464 ssFontSetting
<< "font-weight:"<< pango_font_description_get_weight(pfd
) << "; ";
1465 ssFontSetting
<< "}";
1466 gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(cssProvider
),
1467 ssFontSetting
.str().c_str(), -1, NULL
);
1470 gtk_widget_modify_font(PWidget(list
), PFont(scint_font
)->pfd
);
1472 gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer
), -1);
1473 gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer
), 1);
1477 void ListBoxX::SetAverageCharWidth(int width
) {
1478 aveCharWidth
= width
;
1481 void ListBoxX::SetVisibleRows(int rows
) {
1482 desiredVisibleRows
= rows
;
1485 int ListBoxX::GetVisibleRows() const {
1486 return desiredVisibleRows
;
1489 int ListBoxX::GetRowHeight()
1491 #if GTK_CHECK_VERSION(3,0,0)
1492 // This version sometimes reports erroneous results on GTK2, but the GTK2
1493 // version is inaccurate for GTK 3.14.
1495 GtkTreePath
*path
= gtk_tree_path_new_first();
1496 gtk_tree_view_get_background_area(GTK_TREE_VIEW(list
), path
, NULL
, &rect
);
1500 int vertical_separator
=0;
1501 int expander_size
=0;
1502 GtkTreeViewColumn
*column
= gtk_tree_view_get_column(GTK_TREE_VIEW(list
), 0);
1503 gtk_tree_view_column_cell_get_size(column
, NULL
, NULL
, NULL
, NULL
, &row_height
);
1504 gtk_widget_style_get(PWidget(list
),
1505 "vertical-separator", &vertical_separator
,
1506 "expander-size", &expander_size
, NULL
);
1507 row_height
+= vertical_separator
;
1508 row_height
= Platform::Maximum(row_height
, expander_size
);
1513 PRectangle
ListBoxX::GetDesiredRect() {
1514 // Before any size allocated pretend its 100 wide so not scrolled
1515 PRectangle
rc(0, 0, 100, 100);
1517 int rows
= Length();
1518 if ((rows
== 0) || (rows
> desiredVisibleRows
))
1519 rows
= desiredVisibleRows
;
1522 // This, apparently unnecessary call, ensures gtk_tree_view_column_cell_get_size
1523 // returns reasonable values.
1524 #if GTK_CHECK_VERSION(3,0,0)
1525 gtk_widget_get_preferred_size(GTK_WIDGET(frame
), NULL
, &req
);
1527 gtk_widget_size_request(GTK_WIDGET(frame
), &req
);
1531 // First calculate height of the clist for our desired visible
1532 // row count otherwise it tries to expand to the total # of rows
1534 int row_height
= GetRowHeight();
1535 #if GTK_CHECK_VERSION(3,0,0)
1536 GtkStyleContext
*styleContextFrame
= gtk_widget_get_style_context(PWidget(frame
));
1537 GtkBorder padding
, border
;
1538 gtk_style_context_get_padding(styleContextFrame
, GTK_STATE_FLAG_NORMAL
, &padding
);
1539 gtk_style_context_get_border(styleContextFrame
, GTK_STATE_FLAG_NORMAL
, &border
);
1540 height
= (rows
* row_height
1541 + padding
.top
+ padding
.bottom
1542 + border
.top
+ border
.bottom
1543 + 2 * gtk_container_get_border_width(GTK_CONTAINER(PWidget(list
))));
1545 height
= (rows
* row_height
1546 + 2 * (PWidget(frame
)->style
->ythickness
1547 + GTK_CONTAINER(PWidget(list
))->border_width
));
1551 int width
= maxItemCharacters
;
1554 rc
.right
= width
* (aveCharWidth
+ aveCharWidth
/ 3);
1555 // Add horizontal padding and borders
1556 int horizontal_separator
=0;
1557 gtk_widget_style_get(PWidget(list
),
1558 "horizontal-separator", &horizontal_separator
, NULL
);
1559 rc
.right
+= horizontal_separator
;
1560 #if GTK_CHECK_VERSION(3,0,0)
1561 rc
.right
+= (padding
.left
+ padding
.right
1562 + border
.left
+ border
.right
1563 + 2 * gtk_container_get_border_width(GTK_CONTAINER(PWidget(list
))));
1565 rc
.right
+= 2 * (PWidget(frame
)->style
->xthickness
1566 + GTK_CONTAINER(PWidget(list
))->border_width
);
1568 if (Length() > rows
) {
1569 // Add the width of the scrollbar
1570 GtkWidget
*vscrollbar
=
1571 gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(scroller
));
1572 #if GTK_CHECK_VERSION(3,0,0)
1573 gtk_widget_get_preferred_size(vscrollbar
, NULL
, &req
);
1575 gtk_widget_size_request(vscrollbar
, &req
);
1577 rc
.right
+= req
.width
;
1583 int ListBoxX::CaretFromEdge() {
1584 gint renderer_width
, renderer_height
;
1585 gtk_cell_renderer_get_fixed_size(pixbuf_renderer
, &renderer_width
,
1587 return 4 + renderer_width
;
1590 void ListBoxX::Clear() {
1591 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
1592 gtk_list_store_clear(GTK_LIST_STORE(model
));
1593 maxItemCharacters
= 0;
1596 static void init_pixmap(ListImage
*list_image
) {
1597 if (list_image
->rgba_data
) {
1598 // Drop any existing pixmap/bitmap as data may have changed
1599 if (list_image
->pixbuf
)
1600 g_object_unref(list_image
->pixbuf
);
1601 list_image
->pixbuf
=
1602 gdk_pixbuf_new_from_data(list_image
->rgba_data
->Pixels(),
1606 list_image
->rgba_data
->GetWidth(),
1607 list_image
->rgba_data
->GetHeight(),
1608 list_image
->rgba_data
->GetWidth() * 4,
1616 void ListBoxX::Append(char *s
, int type
) {
1617 ListImage
*list_image
= NULL
;
1618 if ((type
>= 0) && pixhash
) {
1619 list_image
= static_cast<ListImage
*>(g_hash_table_lookup((GHashTable
*) pixhash
1620 , (gconstpointer
) GINT_TO_POINTER(type
)));
1623 GtkListStore
*store
=
1624 GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list
)));
1625 gtk_list_store_append(GTK_LIST_STORE(store
), &iter
);
1627 if (NULL
== list_image
->pixbuf
)
1628 init_pixmap(list_image
);
1629 if (list_image
->pixbuf
) {
1630 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
1631 PIXBUF_COLUMN
, list_image
->pixbuf
,
1632 TEXT_COLUMN
, s
, -1);
1634 gint pixbuf_width
= gdk_pixbuf_get_width(list_image
->pixbuf
);
1635 gint renderer_height
, renderer_width
;
1636 gtk_cell_renderer_get_fixed_size(pixbuf_renderer
,
1637 &renderer_width
, &renderer_height
);
1638 if (pixbuf_width
> renderer_width
)
1639 gtk_cell_renderer_set_fixed_size(pixbuf_renderer
,
1642 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
1643 TEXT_COLUMN
, s
, -1);
1646 gtk_list_store_set(GTK_LIST_STORE(store
), &iter
,
1647 TEXT_COLUMN
, s
, -1);
1649 size_t len
= strlen(s
);
1650 if (maxItemCharacters
< len
)
1651 maxItemCharacters
= len
;
1654 int ListBoxX::Length() {
1656 return gtk_tree_model_iter_n_children(gtk_tree_view_get_model
1657 (GTK_TREE_VIEW(list
)), NULL
);
1661 void ListBoxX::Select(int n
) {
1663 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
1664 GtkTreeSelection
*selection
=
1665 gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
1668 gtk_tree_selection_unselect_all(selection
);
1672 bool valid
= gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, n
) != FALSE
;
1674 gtk_tree_selection_select_iter(selection
, &iter
);
1676 // Move the scrollbar to show the selection.
1677 int total
= Length();
1678 #if GTK_CHECK_VERSION(3,0,0)
1679 GtkAdjustment
*adj
=
1680 gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(list
));
1682 GtkAdjustment
*adj
=
1683 gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(list
));
1685 gfloat value
= ((gfloat
)n
/ total
) * (gtk_adjustment_get_upper(adj
) - gtk_adjustment_get_lower(adj
))
1686 + gtk_adjustment_get_lower(adj
) - gtk_adjustment_get_page_size(adj
) / 2;
1688 int row_height
= GetRowHeight();
1690 int rows
= Length();
1691 if ((rows
== 0) || (rows
> desiredVisibleRows
))
1692 rows
= desiredVisibleRows
;
1694 // Odd rows to display -- We are now in the middle.
1695 // Align it so that we don't chop off rows.
1696 value
+= (gfloat
)row_height
/ 2.0;
1699 value
= (value
< 0)? 0 : value
;
1700 value
= (value
> (gtk_adjustment_get_upper(adj
) - gtk_adjustment_get_page_size(adj
)))?
1701 (gtk_adjustment_get_upper(adj
) - gtk_adjustment_get_page_size(adj
)) : value
;
1704 gtk_adjustment_set_value(adj
, value
);
1706 gtk_tree_selection_unselect_all(selection
);
1710 int ListBoxX::GetSelection() {
1713 GtkTreeModel
*model
;
1714 GtkTreeSelection
*selection
;
1715 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
1716 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
)) {
1717 GtkTreePath
*path
= gtk_tree_model_get_path(model
, &iter
);
1718 int *indices
= gtk_tree_path_get_indices(path
);
1719 // Don't free indices.
1722 gtk_tree_path_free(path
);
1727 int ListBoxX::Find(const char *prefix
) {
1729 GtkTreeModel
*model
=
1730 gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
1731 bool valid
= gtk_tree_model_get_iter_first(model
, &iter
) != FALSE
;
1735 gtk_tree_model_get(model
, &iter
, TEXT_COLUMN
, &s
, -1);
1736 if (s
&& (0 == strncmp(prefix
, s
, strlen(prefix
)))) {
1741 valid
= gtk_tree_model_iter_next(model
, &iter
) != FALSE
;
1747 void ListBoxX::GetValue(int n
, char *value
, int len
) {
1750 GtkTreeModel
*model
= gtk_tree_view_get_model(GTK_TREE_VIEW(list
));
1751 bool valid
= gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, n
) != FALSE
;
1753 gtk_tree_model_get(model
, &iter
, TEXT_COLUMN
, &text
, -1);
1755 if (text
&& len
> 0) {
1756 g_strlcpy(value
, text
, len
);
1763 // g_return_if_fail causes unnecessary compiler warning in release compile.
1765 #pragma warning(disable: 4127)
1768 void ListBoxX::RegisterRGBA(int type
, RGBAImage
*image
) {
1769 images
.Add(type
, image
);
1772 pixhash
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
1774 ListImage
*list_image
= static_cast<ListImage
*>(g_hash_table_lookup((GHashTable
*) pixhash
,
1775 (gconstpointer
) GINT_TO_POINTER(type
)));
1777 // Drop icon already registered
1778 if (list_image
->pixbuf
)
1779 g_object_unref(list_image
->pixbuf
);
1780 list_image
->pixbuf
= NULL
;
1781 list_image
->rgba_data
= image
;
1783 list_image
= g_new0(ListImage
, 1);
1784 list_image
->rgba_data
= image
;
1785 g_hash_table_insert((GHashTable
*) pixhash
, GINT_TO_POINTER(type
),
1786 (gpointer
) list_image
);
1790 void ListBoxX::RegisterImage(int type
, const char *xpm_data
) {
1791 g_return_if_fail(xpm_data
);
1792 XPM
xpmImage(xpm_data
);
1793 RegisterRGBA(type
, new RGBAImage(xpmImage
));
1796 void ListBoxX::RegisterRGBAImage(int type
, int width
, int height
, const unsigned char *pixelsImage
) {
1797 RegisterRGBA(type
, new RGBAImage(width
, height
, 1.0, pixelsImage
));
1800 void ListBoxX::ClearRegisteredImages() {
1804 void ListBoxX::SetList(const char *listText
, char separator
, char typesep
) {
1806 int count
= strlen(listText
) + 1;
1807 std::vector
<char> words(listText
, listText
+count
);
1808 char *startword
= &words
[0];
1809 char *numword
= NULL
;
1811 for (; words
[i
]; i
++) {
1812 if (words
[i
] == separator
) {
1816 Append(startword
, numword
?atoi(numword
+ 1):-1);
1817 startword
= &words
[0] + i
+ 1;
1819 } else if (words
[i
] == typesep
) {
1820 numword
= &words
[0] + i
;
1826 Append(startword
, numword
?atoi(numword
+ 1):-1);
1830 Menu::Menu() : mid(0) {}
1832 void Menu::CreatePopUp() {
1834 mid
= gtk_menu_new();
1835 g_object_ref_sink(G_OBJECT(mid
));
1838 void Menu::Destroy() {
1840 g_object_unref(G_OBJECT(mid
));
1844 static void MenuPositionFunc(GtkMenu
*, gint
*x
, gint
*y
, gboolean
*, gpointer userData
) {
1845 sptr_t intFromPointer
= GPOINTER_TO_INT(userData
);
1846 *x
= intFromPointer
& 0xffff;
1847 *y
= intFromPointer
>> 16;
1850 void Menu::Show(Point pt
, Window
&) {
1851 int screenHeight
= gdk_screen_height();
1852 int screenWidth
= gdk_screen_width();
1853 GtkMenu
*widget
= static_cast<GtkMenu
*>(mid
);
1854 gtk_widget_show_all(GTK_WIDGET(widget
));
1855 GtkRequisition requisition
;
1856 #if GTK_CHECK_VERSION(3,0,0)
1857 gtk_widget_get_preferred_size(GTK_WIDGET(widget
), NULL
, &requisition
);
1859 gtk_widget_size_request(GTK_WIDGET(widget
), &requisition
);
1861 if ((pt
.x
+ requisition
.width
) > screenWidth
) {
1862 pt
.x
= screenWidth
- requisition
.width
;
1864 if ((pt
.y
+ requisition
.height
) > screenHeight
) {
1865 pt
.y
= screenHeight
- requisition
.height
;
1867 gtk_menu_popup(widget
, NULL
, NULL
, MenuPositionFunc
,
1868 GINT_TO_POINTER((static_cast<int>(pt
.y
) << 16) | static_cast<int>(pt
.x
)), 0,
1869 gtk_get_current_event_time());
1872 ElapsedTime::ElapsedTime() {
1874 g_get_current_time(&curTime
);
1875 bigBit
= curTime
.tv_sec
;
1876 littleBit
= curTime
.tv_usec
;
1879 class DynamicLibraryImpl
: public DynamicLibrary
{
1883 explicit DynamicLibraryImpl(const char *modulePath
) {
1884 m
= g_module_open(modulePath
, G_MODULE_BIND_LAZY
);
1887 virtual ~DynamicLibraryImpl() {
1892 // Use g_module_symbol to get a pointer to the relevant function.
1893 virtual Function
FindFunction(const char *name
) {
1895 gpointer fn_address
= NULL
;
1896 gboolean status
= g_module_symbol(m
, name
, &fn_address
);
1898 return static_cast<Function
>(fn_address
);
1906 virtual bool IsValid() {
1911 DynamicLibrary
*DynamicLibrary::Load(const char *modulePath
) {
1912 return static_cast<DynamicLibrary
*>( new DynamicLibraryImpl(modulePath
) );
1915 double ElapsedTime::Duration(bool reset
) {
1917 g_get_current_time(&curTime
);
1918 long endBigBit
= curTime
.tv_sec
;
1919 long endLittleBit
= curTime
.tv_usec
;
1920 double result
= 1000000.0 * (endBigBit
- bigBit
);
1921 result
+= endLittleBit
- littleBit
;
1922 result
/= 1000000.0;
1925 littleBit
= endLittleBit
;
1930 ColourDesired
Platform::Chrome() {
1931 return ColourDesired(0xe0, 0xe0, 0xe0);
1934 ColourDesired
Platform::ChromeHighlight() {
1935 return ColourDesired(0xff, 0xff, 0xff);
1938 const char *Platform::DefaultFont() {
1940 return "Lucida Console";
1946 int Platform::DefaultFontSize() {
1954 unsigned int Platform::DoubleClickTime() {
1955 return 500; // Half a second
1958 bool Platform::MouseButtonBounce() {
1962 void Platform::DebugDisplay(const char *s
) {
1963 fprintf(stderr
, "%s", s
);
1966 bool Platform::IsKeyDown(int) {
1967 // TODO: discover state of keys in GTK+/X
1971 long Platform::SendScintilla(
1972 WindowID w
, unsigned int msg
, unsigned long wParam
, long lParam
) {
1973 return scintilla_send_message(SCINTILLA(w
), msg
, wParam
, lParam
);
1976 long Platform::SendScintillaPointer(
1977 WindowID w
, unsigned int msg
, unsigned long wParam
, void *lParam
) {
1978 return scintilla_send_message(SCINTILLA(w
), msg
, wParam
,
1979 reinterpret_cast<sptr_t
>(lParam
));
1982 bool Platform::IsDBCSLeadByte(int codePage
, char ch
) {
1983 // Byte ranges found in Wikipedia articles with relevant search strings in each case
1984 unsigned char uch
= static_cast<unsigned char>(ch
);
1988 return ((uch
>= 0x81) && (uch
<= 0x9F)) ||
1989 ((uch
>= 0xE0) && (uch
<= 0xFC));
1990 // Lead bytes F0 to FC may be a Microsoft addition.
1993 return (uch
>= 0x81) && (uch
<= 0xFE);
1996 return (uch
>= 0x81) && (uch
<= 0xFE);
1997 // Korean EUC-KR may be code page 949.
2002 int Platform::DBCSCharLength(int codePage
, const char *s
) {
2003 if (codePage
== 932 || codePage
== 936 || codePage
== 950) {
2004 return IsDBCSLeadByte(codePage
, s
[0]) ? 2 : 1;
2006 int bytes
= mblen(s
, MB_CUR_MAX
);
2014 int Platform::DBCSCharMaxLength() {
2019 // These are utility functions not really tied to a platform
2021 int Platform::Minimum(int a
, int b
) {
2028 int Platform::Maximum(int a
, int b
) {
2038 void Platform::DebugPrintf(const char *format
, ...) {
2041 va_start(pArguments
, format
);
2042 vsprintf(buffer
, format
, pArguments
);
2044 Platform::DebugDisplay(buffer
);
2047 void Platform::DebugPrintf(const char *, ...) {}
2051 // Not supported for GTK+
2052 static bool assertionPopUps
= true;
2054 bool Platform::ShowAssertionPopUps(bool assertionPopUps_
) {
2055 bool ret
= assertionPopUps
;
2056 assertionPopUps
= assertionPopUps_
;
2060 void Platform::Assert(const char *c
, const char *file
, int line
) {
2062 g_snprintf(buffer
, sizeof(buffer
), "Assertion [%s] failed at %s %d\r\n", c
, file
, line
);
2063 Platform::DebugDisplay(buffer
);
2067 int Platform::Clamp(int val
, int minVal
, int maxVal
) {
2075 void Platform_Initialise() {
2078 void Platform_Finalise() {