aboutsummaryrefslogtreecommitdiffhomepage
path: root/gtk/PlatGTK.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/PlatGTK.cxx')
-rw-r--r--gtk/PlatGTK.cxx751
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), &current);
+ 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;
+}