diff options
Diffstat (limited to 'gtk/PlatGTK.cxx')
-rw-r--r-- | gtk/PlatGTK.cxx | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/gtk/PlatGTK.cxx b/gtk/PlatGTK.cxx new file mode 100644 index 000000000..e074aed17 --- /dev/null +++ b/gtk/PlatGTK.cxx @@ -0,0 +1,751 @@ +// Scintilla source code edit control +// PlatGTK.cxx - implementation of platform facilities on GTK+/Linux +// Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <string.h> +#include <stdio.h> + +#include <gtk/gtk.h> + +#include "Platform.h" + +#include "Scintilla.h" + +#define LOWORD(x) (x & 0xffff) +#define HIWORD(x) (x >> 16) + +Point Point::FromLong(long lpoint) { + return Point(LOWORD(lpoint), HIWORD(lpoint)); +} + +static GdkColor ColourfromRGB(unsigned int red, unsigned int green, unsigned int blue) { + GdkColor co; + co.red = red * (65535 / 255); + co.green = green * (65535 / 255); + co.blue = blue * (65535 / 255); + // the pixel value indicates the index in the colourmap of the colour. + // it is simply a combination of the RGB values we set earlier + co.pixel = (gulong)(red * 65536 + green * 256 + blue); + return co; +} + +Colour::Colour(long lcol) { + unsigned int red = lcol & 0xff; + unsigned int green = (lcol >> 8) & 0xff; + unsigned int blue = lcol >> 16; + co = ColourfromRGB(red, green, blue); +} + +Colour::Colour(unsigned int red, unsigned int green, unsigned int blue) { + co = ColourfromRGB(red, green, blue); +} + +bool Colour::operator==(const Colour &other) const { + return + co.red == other.co.red && + co.green == other.co.green && + co.blue == other.co.blue; +} + +unsigned int Colour::GetRed() { + return co.red; +} + +unsigned int Colour::GetGreen() { + return co.green; +} + +unsigned int Colour::GetBlue() { + return co.blue; +} + +long Colour::AsLong() const { + unsigned int red = co.red * 255 / 65535; + unsigned int green = co.green * 255 / 65535; + unsigned int blue = co.blue * 255 / 65535; + return (red + green*256 + blue*65536); +} + +Palette::Palette() { + used = 0; + allowRealization = false; + allocatedPalette = 0; + allocatedLen = 0; +} + +Palette::~Palette() { + Release(); +} + +void Palette::Release() { + used = 0; + delete []allocatedPalette; + allocatedPalette = 0; + allocatedLen = 0; +} + +// This method either adds a colour to the list of wanted colours (want==true) +// or retrieves the allocated colour back to the ColourPair. +// This is one method to make it easier to keep the code for wanting and retrieving in sync. +void Palette::WantFind(ColourPair &cp, bool want) { + if (want) { + for (int i=0; i < used; i++) { + if (entries[i].desired == cp.desired) + return; + } + + if (used < numEntries) { + entries[used].desired = cp.desired; + entries[used].allocated = cp.desired; + used++; + } + } else { + for (int i=0; i < used; i++) { + if (entries[i].desired == cp.desired) { + cp.allocated = entries[i].allocated; + return; + } + } + cp.allocated = cp.desired; + } +} + +void Palette::Allocate(Window &w) { + if (allocatedPalette) { + gdk_colormap_free_colors(gtk_widget_get_colormap(w.GetID()), + allocatedPalette, allocatedLen); + delete []allocatedPalette; + allocatedPalette = 0; + allocatedLen = 0; + } + allocatedPalette = new GdkColor[used]; + gboolean *successPalette = new gboolean[used]; + if (allocatedPalette) { + allocatedLen = used; + int iPal = 0; + for (iPal = 0; iPal < used; iPal++) { + allocatedPalette[iPal] = entries[iPal].desired.co; + } + gdk_colormap_alloc_colors(gtk_widget_get_colormap(w.GetID()), + allocatedPalette, allocatedLen, FALSE, TRUE, + successPalette); + for (iPal = 0; iPal < used; iPal++) { + entries[iPal].allocated.co = allocatedPalette[iPal]; + } + } + delete []successPalette; +} + +Font::Font() : id(0) {} + + +Font::~Font() {} + + +void Font::Create(const char *faceName, int size, bool bold, bool italic) { + Release(); + 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, "-*-*-*-*-*-*"); + 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-*"); + } +} + +void Font::Release() { + if (id) + gdk_font_unref(id); + id = 0; +} + +Surface::Surface() : drawable(0), gc(0), ppixmap(0), +x(0), y(0), inited(false), createdGC(false) {} + + +Surface::~Surface() { + Release(); +} + +void Surface::Release() { + drawable = 0; + if (createdGC) { + createdGC = false; + gdk_gc_unref(gc); + } + gc = 0; + if (ppixmap) + gdk_pixmap_unref(ppixmap); + ppixmap = 0; + x = 0; + y = 0; + inited = false; + createdGC = false; +} + +bool Surface::Initialised() { + return inited; +} + +void Surface::Init() { + Release(); + inited = true; +} + +void Surface::Init(GdkDrawable *drawable_) { + Release(); + drawable = drawable_; + gc = gdk_gc_new(drawable_); + createdGC = true; + inited = true; +} + +void Surface::InitPixMap(int width, int height, Surface *surface_) { + Release(); + if (height > 0 && width > 0) + ppixmap = gdk_pixmap_new(surface_->drawable, width, height, -1); + drawable = ppixmap; + gc = gdk_gc_new(surface_->drawable); + createdGC = true; + inited = true; +} + +void Surface::PenColour(Colour fore) { + if (gc) + gdk_gc_set_foreground(gc, &fore.co); +} + +int Surface::LogPixelsY() { + return 72; +} + +void Surface::MoveTo(int x_, int y_) { + x = x_; + y = y_; +} + +void Surface::LineTo(int x_, int y_) { + gdk_draw_line(drawable, gc, + x, y, + x_, y_); + x = x_; + y = y_; +} + +void Surface::Polygon(Point *pts, int npts, Colour fore, + Colour back) { + // Nasty casts works because Point is exactly same as GdkPoint + // Oh no it doesn't... + GdkPoint gpts[20]; + if (npts < (sizeof(gpts)/sizeof(gpts[0]))) { + for (int i=0;i<npts;i++) { + gpts[i].x = pts[i].x; + gpts[i].y = pts[i].y; + } + PenColour(back); + //gdk_draw_polygon(drawable, gc, 1, + // reinterpret_cast<GdkPoint *>(pts), npts); + gdk_draw_polygon(drawable, gc, 1, gpts, npts); + PenColour(fore); + gdk_draw_polygon(drawable, gc, 0, gpts, npts); + } +} + +void Surface::RectangleDraw(PRectangle rc, Colour fore, Colour back) { + if (gc && drawable) { + PenColour(back); + gdk_draw_rectangle(drawable, gc, 1, + rc.left, rc.top, + rc.right - rc.left + 1, rc.bottom - rc.top + 1); + PenColour(fore); + gdk_draw_rectangle(drawable, gc, 0, + rc.left, rc.top, + rc.right - rc.left + 1, rc.bottom - rc.top + 1); + } +} + +void Surface::FillRectangle(PRectangle rc, Colour back) { + // GTK+ rectangles include their lower and right edges + rc.bottom--; + rc.right--; + PenColour(back); + if (drawable) { + gdk_draw_rectangle(drawable, gc, 1, + rc.left, rc.top, + rc.right - rc.left + 1, rc.bottom - rc.top + 1); + } +} + +void Surface::FillRectangle(PRectangle rc, Surface &surfacePattern) { + if (surfacePattern.drawable) { + // Tile pattern over rectangle + // Currently assumes 8x8 pattern + int widthPat = 8; + int heightPat = 8; + for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) { + int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat; + for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) { + int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat; + gdk_draw_pixmap(drawable, + gc, + surfacePattern.drawable, + 0, 0, + xTile, yTile, + widthx, heighty); + } + } + } else { + // Something is wrong so try to show anyway + // Shows up black because colour not allocated + FillRectangle(rc, Colour(0xff, 0, 0)); + } +} + +void Surface::RoundedRectangle(PRectangle rc, Colour fore, Colour back) { + 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), + }; + Polygon(pts, sizeof(pts) / sizeof(pts[0]), fore, back); + } else { + RectangleDraw(rc, fore, back); + } +} + +void Surface::Ellipse(PRectangle rc, Colour fore, Colour back) { + PenColour(back); + gdk_draw_arc(drawable, gc, 1, + rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top, + 0, 32767); + PenColour(fore); + gdk_draw_arc(drawable, gc, 0, + rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top, + 0, 32767); +} + +void Surface::Copy(PRectangle rc, Point from, Surface &surfaceSource) { + if (surfaceSource.drawable) { + gdk_draw_pixmap(drawable, + gc, + surfaceSource.drawable, + from.x, from.y, + rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top); + } +} + +void Surface::DrawText(PRectangle rc, Font &font_, int ybase, const char *s, int len, + Colour fore, Colour back) { + FillRectangle(rc, back); + PenColour(fore); + if (gc && drawable) + gdk_draw_text(drawable, font_.id, gc, rc.left, ybase, s, len); +} + +// On GTK+, exactly same as DrawText +void Surface::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, + Colour fore, Colour back) { + FillRectangle(rc, back); + PenColour(fore); + if (gc && drawable) + gdk_draw_text(drawable, font_.id, gc, rc.left, ybase, s, len); +} + +void Surface::MeasureWidths(Font &font_, const char *s, int len, int *positions) { + int totalWidth = 0; + for (int i=0;i<len;i++) { + int width = gdk_char_width(font_.id, s[i]); + totalWidth += width; + positions[i] = totalWidth; + } +} + +int Surface::WidthText(Font &font_, const char *s, int len) { + if (font_.id) + return gdk_text_width(font_.id, s, len); + else + return 1; +} + +int Surface::WidthChar(Font &font_, char ch) { + return gdk_char_width(font_.id, ch); +} + +// Three possible strategies for determining ascent and descent of font: +// 1) Call gdk_string_extents with string containing all letters, numbers and punctuation. +// 2) Use the ascent and descent fields of GdkFont. +// 3) Call gdk_string_extents with string as 1 but also including accented capitals. +// Smallest values given by 1 and largest by 3 with 2 in between. +// Techniques 1 and 2 sometimes chop off extreme portions of ascenders and +// descenders but are mostly OK except for accented characters like Å which are +// rarely used in code. + +// This string contains a good range of characters to test for size. +const char largeSizeString[]= "ÂÃÅÄ `~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const char sizeString[]= "`~!@#$%^&*()-_=+\\|[]{};:\"\'<,>.?/1234567890" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +int Surface::Ascent(Font &font_) { +#ifdef FAST_WAY + return font_.id->ascent; +#else + gint lbearing; + gint rbearing; + gint width; + gint ascent; + gint descent; + + gdk_string_extents(font_.id, sizeString, + &lbearing, &rbearing, &width, &ascent, &descent); + return ascent; +#endif +} + +int Surface::Descent(Font &font_) { +#ifdef FAST_WAY + return font_.id->descent; +#else + gint lbearing; + gint rbearing; + gint width; + gint ascent; + gint descent; + + gdk_string_extents(font_.id, sizeString, + &lbearing, &rbearing, &width, &ascent, &descent); + return descent; +#endif +} + +int Surface::InternalLeading(Font &) { + return 0; +} + +int Surface::ExternalLeading(Font &) { + return 0; +} + +int Surface::Height(Font &font_) { + return Ascent(font_) + Descent(font_); +} + +int Surface::AverageCharWidth(Font &font_) { + return gdk_char_width(font_.id, 'n'); +} + +int Surface::SetPalette(Palette *, bool) { + // Handled in palette allocation for GTK so this does nothing + return 0; +} + +void Surface::SetClip(PRectangle rc) { + GdkRectangle area = {rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top}; + gdk_gc_set_clip_rectangle(gc, &area); +} + +Window::~Window() { +} + +void Window::Destroy() { + if (id) + gtk_widget_destroy(GTK_WIDGET(id)); + id = 0; +} + +bool Window::HasFocus() { + return GTK_WIDGET_HAS_FOCUS(id); +} + +PRectangle Window::GetPosition() { + // Before any size allocated pretend its 100 wide so not scrolled + PRectangle rc(0, 0, 100, 100); + if (id) { + rc.left = id->allocation.x; + rc.top = id->allocation.y; + if (id->allocation.width > 20) { + rc.right = rc.left + id->allocation.width; + rc.bottom = rc.top + id->allocation.height; + } + } + return rc; +} + +void Window::SetPosition(PRectangle rc) { + //gtk_widget_set_uposition(id, rc.left, rc.top); + GtkAllocation alloc; + alloc.x = rc.left; + alloc.y = rc.top; + alloc.width = rc.Width(); + alloc.height = rc.Height(); + gtk_widget_size_allocate(id, &alloc); +} + +void Window::SetPositionRelative(PRectangle rc, Window relativeTo) { + int ox = 0; + int oy = 0; + gdk_window_get_origin(relativeTo.id->window, &ox, &oy); + + gtk_widget_set_uposition(id, rc.left + ox, rc.top + oy); + GtkAllocation alloc; + alloc.x = rc.left + ox; + alloc.y = rc.top + oy; + alloc.width = rc.right - rc.left; + alloc.height = rc.bottom - rc.top; + gtk_widget_size_allocate(id, &alloc); +} + +PRectangle Window::GetClientPosition() { + // On GTK+, the client position is the window position + return GetPosition(); +} + +void Window::Show(bool show) { + if (show) + gtk_widget_show(id); +} + +void Window::InvalidateAll() { + if (id) { + gtk_widget_queue_draw(id); + } +} + +void Window::InvalidateRectangle(PRectangle rc) { + if (id) { + gtk_widget_queue_draw_area(id, + rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top); + } +} + +void Window::SetFont(Font &) { + // TODO +} + +void Window::SetCursor(Cursor curs) { + switch (curs) { + case cursorText: + gdk_window_set_cursor(id->window, gdk_cursor_new(GDK_XTERM)); + break; + case cursorArrow: + gdk_window_set_cursor(id->window, gdk_cursor_new(GDK_ARROW)); + break; + case cursorUp: + gdk_window_set_cursor(id->window, gdk_cursor_new(GDK_CENTER_PTR)); + break; + case cursorWait: + gdk_window_set_cursor(id->window, gdk_cursor_new(GDK_WATCH)); + break; + case cursorReverseArrow: + gdk_window_set_cursor(id->window, gdk_cursor_new(GDK_TOP_LEFT_ARROW)); + break; + default: + gdk_window_set_cursor(id->window, gdk_cursor_new(GDK_ARROW)); + break; + } +} + +void Window::SetTitle(const char *s) { + gtk_window_set_title(GTK_WINDOW(id), s); +} + +ListBox::ListBox() : list(0), current(0) {} + +ListBox::~ListBox() {} + +static void SelectionAC(GtkWidget *, gint row, gint, + GdkEventButton *, gpointer p) { + int *pi = reinterpret_cast<int *>(p); + *pi = row; +} + +void ListBox::Create(Window &, int) { + id = gtk_window_new(GTK_WINDOW_POPUP); + scroller = gtk_scrolled_window_new(NULL, NULL); + gtk_container_set_border_width(GTK_CONTAINER(scroller), 1); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + list = gtk_clist_new(1); + gtk_clist_set_column_auto_resize(GTK_CLIST(list), 0, TRUE); + gtk_container_add(GTK_CONTAINER(scroller), list); + + gtk_container_add(GTK_CONTAINER(GetID()), scroller); + + gtk_widget_show(list); + gtk_widget_show(scroller); + + gtk_clist_set_selection_mode(GTK_CLIST(list), GTK_SELECTION_BROWSE); + gtk_signal_connect(GTK_OBJECT(list), "select_row", + GTK_SIGNAL_FUNC(SelectionAC), ¤t); + gtk_clist_set_shadow_type(GTK_CLIST(list), GTK_SHADOW_OUT); + + gtk_widget_realize(id); +} + +void ListBox::Clear() { + gtk_clist_clear(GTK_CLIST(list)); +} + +void ListBox::Append(char *s) { + char *szs[] = { s, 0}; + gtk_clist_append(GTK_CLIST(list), szs); +} + +int ListBox::Length() { + return GTK_CLIST(list)->rows; +} + +void ListBox::Select(int n) { + gtk_clist_select_row(GTK_CLIST(list), n, 0); + gtk_clist_moveto(GTK_CLIST(list), n, 0, 0.5, 0.5); +} + +int ListBox::GetSelection() { + return current; +} + +int ListBox::Find(const char *prefix) { + int count = Length(); + for (int i = 0; i < count; i++) { + char *s = 0; + gtk_clist_get_text(GTK_CLIST(list), i, 0, &s); + if (s && (0 == strncmp(prefix, s, strlen(prefix)))) { + return i; + } + } + return - 1; +} + +void ListBox::GetValue(int n, char *value, int len) { + char *text = 0; + gtk_clist_get_text(GTK_CLIST(list), n, 0, &text); + if (text && len > 0) { + strncpy(value, text, len); + value[len - 1] = '\0'; + } else { + value[0] = '\0'; + } +} + +void ListBox::Sort() { + gtk_clist_sort(GTK_CLIST(list)); +} + +Menu::Menu() : id(0) {} + + +void Menu::CreatePopUp() { + Destroy(); + id = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL); +} + +void Menu::Destroy() { + if (id) + gtk_object_unref(GTK_OBJECT(id)); + id = 0; +} + +void Menu::Show(Point pt, Window &) { + gtk_item_factory_popup(id, pt.x - 4, pt.y, 3, 0); +} + +Colour Platform::Chrome() { + return Colour(0xe0, 0xe0, 0xe0); +} + +Colour Platform::ChromeHighlight() { + return Colour(0xff, 0xff, 0xff); +} + +const char *Platform::DefaultFont() { + return "lucidatypewriter"; +} + +int Platform::DefaultFontSize() { + return 12; +} + +unsigned int Platform::DoubleClickTime() { + return 500; // Half a second +} + +void Platform::DebugDisplay(const char *s) { + printf("%s", s); +} + +bool Platform::IsKeyDown(int) { + // TODO: discover state of keys in GTK+/X + return false; +} + +long Platform::SendScintilla( + WindowID w, unsigned int msg, unsigned long wParam, long lParam) { + return scintilla_send_message(SCINTILLA(w), msg, wParam, lParam); +} + +// These are utility functions not really tied to a platform + +int Platform::Minimum(int a, int b) { + if (a < b) + return a; + else + return b; +} + +int Platform::Maximum(int a, int b) { + if (a > b) + return a; + else + return b; +} + +//#define TRACE + +void Platform::DebugPrintf(const char *format, ...) { +#ifdef TRACE + char buffer[2000]; + va_list pArguments; + va_start(pArguments, format); + vsprintf(buffer, format, pArguments); + va_end(pArguments); + Platform::DebugDisplay(buffer); +#endif +} + +int Platform::Clamp(int val, int minVal, int maxVal) { + if (val > maxVal) + val = maxVal; + if (val < minVal) + val = minVal; + return val; +} |