diff options
-rw-r--r-- | gtk/PlatGTK.cxx | 360 |
1 files changed, 294 insertions, 66 deletions
diff --git a/gtk/PlatGTK.cxx b/gtk/PlatGTK.cxx index 32445cc4e..f763afafb 100644 --- a/gtk/PlatGTK.cxx +++ b/gtk/PlatGTK.cxx @@ -7,6 +7,7 @@ #include <stdio.h> #include <stdlib.h> +#include <gdk/gdk.h> #include <gtk/gtk.h> #include <gdk/gdkkeysyms.h> @@ -14,11 +15,14 @@ #include "Scintilla.h" #include "ScintillaWidget.h" +#include "UniConversion.h" /* Use fast way of getting char data on win32 to work around problems with gdk_string_extents. */ -#ifdef G_OS_WIN32 #define FAST_WAY + +#ifdef G_OS_WIN32 +#define snprintf _snprintf #endif #ifdef _MSC_VER @@ -29,7 +33,7 @@ // X has a 16 bit coordinate space, so stop drawing here to avoid wrapping static const int maxCoordinate = 32000; -static GdkFont *PFont(Font &f) { +static GdkFont *PFont(Font &f) { return reinterpret_cast<GdkFont *>(f.GetID()); } @@ -72,7 +76,7 @@ void Palette::WantFind(ColourPair &cp, bool want) { if (want) { for (int i = 0; i < used; i++) { if (entries[i].desired == cp.desired) - return ; + return; } if (used < numEntries) { @@ -84,7 +88,7 @@ void Palette::WantFind(ColourPair &cp, bool want) { for (int i = 0; i < used; i++) { if (entries[i].desired == cp.desired) { cp.allocated = entries[i].allocated; - return ; + return; } } cp.allocated.Set(cp.desired.AsLong()); @@ -94,8 +98,8 @@ void Palette::WantFind(ColourPair &cp, bool want) { void Palette::Allocate(Window &w) { if (allocatedPalette) { gdk_colormap_free_colors(gtk_widget_get_colormap(PWidget(w)), - reinterpret_cast<GdkColor *>(allocatedPalette), - allocatedLen); + reinterpret_cast<GdkColor *>(allocatedPalette), + allocatedLen); delete [](reinterpret_cast<GdkColor *>(allocatedPalette)); allocatedPalette = 0; allocatedLen = 0; @@ -171,39 +175,194 @@ static const char *CharacterSetName(int characterSet) { } } -void Font::Create(const char *faceName, int characterSet, +void GenerateFontSpecStrings(const char *fontName, int characterSet, + char *foundary, int foundary_len, + char *faceName, int faceName_len, + char *charset, int charset_len) { + // supported font strings include: + // foundary-fontface-isoxxx-x + // fontface-isoxxx-x + // foundary-fontface + // fontface + if (strchr(fontName, '-')) { + char tmp[300]; + char *d1 = NULL, *d2 = NULL, *d3 = NULL; + strncpy(tmp, fontName, sizeof(tmp) - 1); + d1 = strchr(tmp, '-'); + // we know the first dash exists + d2 = strchr(d1 + 1, '-'); + if (d2) + d3 = strchr(d2 + 1, '-'); + if (d3) { + // foundary-fontface-isoxxx-x + *d2 = '\0'; + foundary[0] = '-'; + foundary[1] = '\0'; + strncpy(faceName, tmp, foundary_len - 1); + strncpy(charset, d2 + 1, charset_len - 1); + } else if (d2) { + // fontface-isoxxx-x + *d1 = '\0'; + strcpy(foundary, "-*-"); + strncpy(faceName, tmp, faceName_len - 1); + strncpy(charset, d1 + 1, charset_len - 1); + } else { + // foundary-fontface + foundary[0] = '-'; + foundary[1] = '\0'; + strncpy(faceName, tmp, faceName_len - 1); + strncpy(charset, CharacterSetName(characterSet), charset_len - 1); + } + } else { + strncpy(foundary, "-*-", foundary_len); + strncpy(faceName, fontName, faceName_len - 1); + strncpy(charset, CharacterSetName(characterSet), charset_len - 1); + } +} + +void Font::Create(const char *fontName, int characterSet, int size, bool bold, bool italic) { Release(); + char fontset[1024]; + char fontspec[300]; + char foundary[50]; + char faceName[100]; + char charset[50]; + fontset[0] = '\0'; + fontspec[0] = '\0'; + foundary[0] = '\0'; + faceName[0] = '\0'; + charset[0] = '\0'; + // If name of the font begins with a '-', assume, that it is // a full fontspec. - if (faceName[0] == '-') { - id = gdk_font_load(faceName); + if (fontName[0] == '-') { + if (strchr(fontName, ',')) { + id = gdk_fontset_load(fontName); + } else { + id = gdk_font_load(fontName); + } + if (!id) { + // Font not available so substitute a reasonable code font + // iso8859 appears to only allow western characters. + id = gdk_font_load("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*"); + } + return; + } + + // it's not a full fontspec, build one. + + // This supports creating a FONT_SET + // in a method that allows us to also set size, slant and + // weight for the fontset. The expected input is multiple + // partial fontspecs seperated by comma + // eg. adobe-courier-iso10646-1,*-courier-iso10646-1,*-*-*-* + if (strchr(fontName, ',')) { + // build a fontspec and use gdk_fontset_load + int remaining = sizeof(fontset); + char fontNameCopy[1024]; + strncpy(fontNameCopy, fontName, sizeof(fontNameCopy) - 1); + char *fn = fontNameCopy; + char *fp = strchr(fn, ','); + for (;;) { + const char *spec = "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s"; + if (fontset[0] != '\0') { + // if this is not the first font in the list, + // append a comma seperator + spec = ",%s%s%s%s-*-*-*-%0d-*-*-*-*-%s"; + } + + if (fp) + *fp = '\0'; // nullify the comma + GenerateFontSpecStrings(fn, characterSet, + foundary, sizeof(foundary), + faceName, sizeof(faceName), + charset, sizeof(charset)); + + snprintf(fontspec, + sizeof(fontspec) - 1, + spec, + foundary, faceName, + bold ? "-bold" : "-medium", + italic ? "-i" : "-r", + size * 10, + charset); + + // if this is the first font in the list, and + // we are doing italic, add an oblique font + // to the list + if (italic && fontset[0] == '\0') { + strncat(fontset, fontspec, remaining - 1); + remaining -= strlen(fontset); + + snprintf(fontspec, + sizeof(fontspec) - 1, + ",%s%s%s-o-*-*-*-%0d-*-*-*-*-%s", + foundary, faceName, + bold ? "-bold" : "-medium", + size * 10, + charset); + } + + strncat(fontset, fontspec, remaining - 1); + remaining -= strlen(fontset); + + if (!fp) + break; + + fn = fp + 1; + fp = strchr(fn, ','); + } + + id = gdk_fontset_load(fontset); if (id) - return ; + return; + + // if fontset load failed, fall through, we'll use + // the last font entry and continue to try and + // get something that matches } - char fontspec[300]; - fontspec[0] = '\0'; - strcat(fontspec, "-*-"); - strcat(fontspec, faceName); - if (bold) - strcat(fontspec, "-bold"); - else - strcat(fontspec, "-medium"); - if (italic) - strcat(fontspec, "-i"); - else - strcat(fontspec, "-r"); - strcat(fontspec, "-*-*-*"); - char sizePts[100]; - sprintf(sizePts, "-%0d", size * 10); - strcat(fontspec, sizePts); - strcat(fontspec, "-*-*-*-*-"); - strcat(fontspec, CharacterSetName(characterSet)); + + // single fontspec support + + GenerateFontSpecStrings(fontName, characterSet, + foundary, sizeof(foundary), + faceName, sizeof(faceName), + charset, sizeof(charset)); + + snprintf(fontspec, + sizeof(fontspec) - 1, + "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s", + foundary, faceName, + bold ? "-bold" : "-medium", + italic ? "-i" : "-r", + size * 10, + charset); id = gdk_font_load(fontspec); if (!id) { + // some fonts have oblique, not italic + snprintf(fontspec, + sizeof(fontspec) - 1, + "%s%s%s%s-*-*-*-%0d-*-*-*-*-%s", + foundary, faceName, + bold ? "-bold" : "-medium", + italic ? "-o" : "-r", + size * 10, + charset); + id = gdk_font_load(fontspec); + } + if (!id) { + snprintf(fontspec, + sizeof(fontspec) - 1, + "-*-*-*-*-*-*-*-%0d-*-*-*-*-%s", + size * 10, + charset); + id = gdk_font_load(fontspec); + } + if (!id) { // Font not available so substitute a reasonable code font // iso8859 appears to only allow western characters. - id = gdk_font_load("*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*"); + id = gdk_font_load("-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-*"); } } @@ -342,15 +501,17 @@ void SurfaceImpl::MoveTo(int x_, int y_) { } void SurfaceImpl::LineTo(int x_, int y_) { - gdk_draw_line(drawable, gc, - x, y, - x_, y_); + if (drawable && gc) { + gdk_draw_line(drawable, gc, + x, y, + x_, y_); + } x = x_; y = y_; } void SurfaceImpl::Polygon(Point *pts, int npts, ColourAllocated fore, - ColourAllocated back) { + ColourAllocated back) { GdkPoint gpts[20]; if (npts < static_cast<int>((sizeof(gpts) / sizeof(gpts[0])))) { for (int i = 0;i < npts;i++) { @@ -418,15 +579,15 @@ void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAl if (((rc.right - rc.left) > 4) && ((rc.bottom - rc.top) > 4)) { // Approximate a round rect with some cut off corners Point pts[] = { - Point(rc.left + 2, rc.top), - Point(rc.right - 2, rc.top), - Point(rc.right, rc.top + 2), - Point(rc.right, rc.bottom - 2), - Point(rc.right - 2, rc.bottom), - Point(rc.left + 2, rc.bottom), - Point(rc.left, rc.bottom - 2), - Point(rc.left, rc.top + 2), - }; + Point(rc.left + 2, rc.top), + Point(rc.right - 2, rc.top), + Point(rc.right, rc.top + 2), + Point(rc.right, rc.bottom - 2), + Point(rc.right - 2, rc.bottom), + Point(rc.left + 2, rc.bottom), + Point(rc.left, rc.bottom - 2), + Point(rc.left, rc.top + 2), + }; Polygon(pts, sizeof(pts) / sizeof(pts[0]), fore, back); } else { RectangleDraw(rc, fore, back); @@ -459,30 +620,52 @@ void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) { } } +#define MAX_US_LEN 5000 + void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, - ColourAllocated fore, ColourAllocated back) { + ColourAllocated fore, ColourAllocated back) { FillRectangle(rc, back); PenColour(fore); if (gc && drawable) { // Draw text as a series of segments to avoid limitations in X servers - // TODO: make this DBCS and UTF-8 safe by not splitting multibyte characters const int segmentLength = 1000; int x = rc.left; - while ((len > 0) && (x < maxCoordinate)) { - int lenDraw = Platform::Minimum(len, segmentLength); - gdk_draw_text(drawable, PFont(font_), gc, x, ybase, s, lenDraw); - len -= lenDraw; - if (len > 0) { - x += gdk_text_width(PFont(font_), s, lenDraw); + if (unicodeMode) { + GdkWChar wctext[MAX_US_LEN]; + GdkWChar *wcp = (GdkWChar *) & wctext; + size_t wclen = UCS2FromUTF8(s, len, (wchar_t *)wctext, + sizeof(wctext) / sizeof(GdkWChar) - 1); + wctext[wclen] = L'\0'; + int lenDraw; + while ((wclen > 0) && (x < maxCoordinate)) { + lenDraw = Platform::Minimum(wclen, segmentLength); + gdk_draw_text_wc(drawable, PFont(font_), gc, + x, ybase, wcp, lenDraw); + wclen -= lenDraw; + if (wclen > 0) { + x += gdk_text_width_wc(PFont(font_), + wcp, lenDraw); + } + wcp += lenDraw; + } + } else { + while ((len > 0) && (x < maxCoordinate)) { + int lenDraw = Platform::Minimum(len, segmentLength); + gdk_draw_text(drawable, PFont(font_), gc, + x, ybase, s, lenDraw); + len -= lenDraw; + if (len > 0) { + x += gdk_text_width(PFont(font_), s, lenDraw); + } + s += lenDraw; } - s += lenDraw; } } } // On GTK+, exactly same as DrawTextNoClip void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, - ColourAllocated fore, ColourAllocated back) { + ColourAllocated fore, ColourAllocated back) { DrawTextNoClip(rc, font_, ybase, s, len, fore, back); } @@ -490,10 +673,47 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi if (font_.GetID()) { int totalWidth = 0; GdkFont *gf = PFont(font_); - for (int i = 0; i < len; i++) { - int width = gdk_char_width(gf, s[i]); - totalWidth += width; - positions[i] = totalWidth; + if (unicodeMode) { + GdkWChar wctext[MAX_US_LEN]; + size_t wclen = UCS2FromUTF8(s, len, (wchar_t *)wctext, sizeof(wctext) / sizeof(GdkWChar) - 1); + wctext[wclen] = L'\0'; + int poses[MAX_US_LEN]; + size_t i; + for (i = 0; i < wclen; i++) { + int width = gdk_char_width_wc(gf, wctext[i]); + totalWidth += width; + poses[i] = totalWidth; + } + // map widths back to utf-8 input string + size_t ui = 0; + i = 0; + const unsigned char *us = reinterpret_cast<const unsigned char *>(s); + unsigned char uch; + while (ui < wclen) { + uch = us[i]; + positions[i++] = poses[ui]; + if (uch >= 0x80) { + if (uch < (0x80 + 0x40 + 0x20)) { + positions[i++] = poses[ui]; + } else { + positions[i++] = poses[ui]; + positions[i++] = poses[ui]; + } + } + ui++; + } + int lastPos = 0; + if (i > 0) + lastPos = positions[i - 1]; + while (i < static_cast<size_t>(len)) { + positions[i++] = lastPos; + } + } else { + for (int i = 0; i < len; i++) { + int width = gdk_char_width(gf, s[i]); + totalWidth += width; + positions[i] = totalWidth; + } } } else { for (int i = 0; i < len; i++) { @@ -503,10 +723,18 @@ void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positi } int SurfaceImpl::WidthText(Font &font_, const char *s, int len) { - if (font_.GetID()) - return gdk_text_width(PFont(font_), s, len); - else + if (font_.GetID()) { + if (unicodeMode) { + GdkWChar wctext[MAX_US_LEN]; + size_t wclen = UCS2FromUTF8(s, len, (wchar_t *)wctext, sizeof(wctext) / sizeof(GdkWChar) - 1); + wctext[wclen] = L'\0'; + int width = gdk_text_width_wc(PFont(font_), wctext, wclen); + return width; + } else + return gdk_text_width(PFont(font_), s, len); + } else { return 1; + } } int SurfaceImpl::WidthChar(Font &font_, char ch) { @@ -544,7 +772,7 @@ int SurfaceImpl::Ascent(Font &font_) { gint descent; gdk_string_extents(PFont(font_), sizeString, - &lbearing, &rbearing, &width, &ascent, &descent); + &lbearing, &rbearing, &width, &ascent, &descent); return ascent; #endif } @@ -562,7 +790,7 @@ int SurfaceImpl::Descent(Font &font_) { gint descent; gdk_string_extents(PFont(font_), sizeString, - &lbearing, &rbearing, &width, &ascent, &descent); + &lbearing, &rbearing, &width, &ascent, &descent); return descent; #endif } @@ -600,7 +828,7 @@ void SurfaceImpl::SetClip(PRectangle rc) { void SurfaceImpl::FlushCachedState() {} void SurfaceImpl::SetUnicodeMode(bool unicodeMode_) { - unicodeMode=unicodeMode_; + unicodeMode = unicodeMode_; } Surface *Surface::Allocate() { @@ -733,7 +961,7 @@ void Window::SetTitle(const char *s) { } ListBox::ListBox() : list(0), current(0), desiredVisibleRows(5), maxItemCharacters(0), - doubleClickAction(NULL), doubleClickActionData(NULL) {} +doubleClickAction(NULL), doubleClickActionData(NULL) {} ListBox::~ListBox() {} @@ -851,7 +1079,6 @@ PRectangle ListBox::GetDesiredRect() { rc.right = rc.right + 16; } return rc; - } void ListBox::Clear() { @@ -981,6 +1208,7 @@ int Platform::DefaultFontSize() { #ifdef G_OS_WIN32 return 10; #else + return 12; #endif } @@ -1005,8 +1233,8 @@ long Platform::SendScintilla( long Platform::SendScintillaPointer( WindowID w, unsigned int msg, unsigned long wParam, void *lParam) { - return scintilla_send_message(SCINTILLA(w), msg, wParam, - reinterpret_cast<sptr_t>(lParam)); + return scintilla_send_message(SCINTILLA(w), msg, wParam, + reinterpret_cast<sptr_t>(lParam)); } bool Platform::IsDBCSLeadByte(int /*codePage*/, char /*ch*/) { |