/** \file gtkdraw-cairo.c * Basic drawing functions */ /* XTrkCad - Model Railroad CAD * Copyright (C) 2005 Dave Bullis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #ifdef HAVE_MALLOC_H #include #endif #include #include #include #include #include // Trace low level drawing actions int iDrawLog = 0; #include "gtkint.h" #include "gdk/gdkkeysyms.h" #define gtkAddHelpString( a, b ) wlibAddHelpString( a, b ) #define CENTERMARK_LENGTH (6) static long drawVerbose = 0; // Hack to do TempRedraw or MainRedraw // For Windows only wBool_t wDrawDoTempDraw = TRUE; struct wDrawBitMap_t { int w; int h; int x; int y; const unsigned char * bits; GdkPixmap * pixmap; GdkBitmap * mask; }; //struct wDraw_t { //WOBJ_COMMON //void * context; //wDrawActionCallBack_p action; //wDrawRedrawCallBack_p redraw; //GdkPixmap * pixmap; //GdkPixmap * pixmapBackup; //double dpi; //GdkGC * gc; //wDrawWidth lineWidth; //wDrawOpts opts; //wWinPix_t maxW; //wWinPix_t maxH; //unsigned long lastColor; //wBool_t lastColorInverted; //const char * helpStr; //wWinPix_t lastX; //wWinPix_t lastY; //wBool_t delayUpdate; //}; struct wDraw_t psPrint_d; /***************************************************************************** * * MACROS * */ #define LBORDER (22) #define RBORDER (6) #define TBORDER (6) #define BBORDER (20) #define INMAPX(D,X) (X) #define INMAPY(D,Y) (((D)->h-1) - (Y)) #define OUTMAPX(D,X) (X) #define OUTMAPY(D,Y) (((D)->h-1) - (Y)) /******************************************************************************* * * Basic Drawing Functions * *******************************************************************************/ static cairo_t* gtkDrawCreateCairoCursorContext( wControl_p ct, cairo_surface_t * surf, wDrawWidth width, wDrawLineType_e lineType, wDrawColor color, wDrawOpts opts ) { cairo_t* cairo; cairo = cairo_create(surf); width = width ? abs(width) : 1; cairo_set_line_width(cairo, width); cairo_set_line_cap(cairo, CAIRO_LINE_CAP_BUTT); cairo_set_line_join(cairo, CAIRO_LINE_JOIN_MITER); switch(lineType) { case wDrawLineSolid: { cairo_set_dash(cairo, 0, 0, 0); break; } case wDrawLineDash: { double dashes[] = { 5, 3 }; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0); break; } case wDrawLineDot: { double dashes[] = { 1, 2 }; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0); break; } case wDrawLineDashDot: { double dashes[] = { 5, 2, 1, 2 }; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0); break; } case wDrawLineDashDotDot: { double dashes[] = { 5, 2, 1, 2, 1, 2 }; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0); break; } case wDrawLineCenter: { double dashes[] = { 8, 3, 5, 3}; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0.0); break; } case wDrawLinePhantom: { double dashes[] = { 8, 3, 5, 3, 5, 3}; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0.0); break; } } GdkColor * gcolor; cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); gcolor = wlibGetColor(color, TRUE); if (ct->type == B_DRAW) { wDraw_p bd = (wDraw_p)ct; bd->lastColor = color; } cairo_set_source_rgba(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0, 1.0); return cairo; } wBool_t wDrawSetTempMode( wDraw_p bd, wBool_t bTemp ) { wBool_t ret = bd->bTempMode; bd->bTempMode = bTemp; if ( ret == FALSE && bTemp == TRUE ) { // Main to Temp drawing wDrawClearTemp( bd ); } return ret; } static cairo_t* gtkDrawCreateCairoContext( wDraw_p bd, GdkDrawable * win, wDrawWidth width, wDrawLineType_e lineType, wDrawColor color, wDrawOpts opts ) { cairo_t* cairo; if (win) cairo = gdk_cairo_create(win); else { if (opts & wDrawOptTemp) { if ( ! bd->bTempMode ) printf( "Temp draw in Main Mode. Contact Developers. See %s:%d\n", "gtkdraw-cario.c", __LINE__+1 ); /* Temp Draw In Main Mode: You are seeing this message because there is a wDraw*() call on tempD but you are not in the context of TempRedraw() Typically this happens when Cmd() is processing a C_DOWN or C_MOVE action and it writes directly to tempD Instead it sould set some state which allows c_redraw to do the actual drawing If you set a break point on the printf you'll see the offending wDraw*() call in the traceback It should be sufficient to remove that draw code or move it to C_REDRAW This is not fatal but the draw will be ineffective because the next TempRedraw() will erase the temp surface before the expose event can copy (or bitblt) it */ cairo = cairo_create(bd->temp_surface); } else { if ( bd->bTempMode ) printf( "Main draw in Temp Mode. Contact Developers. See %s:%d\n", "gtkdraw-cario.c", __LINE__+1 ); /* Main Draw In Temp Mode: You are seeing this message because there is a wDraw*() call on mainD but you are in the context of TempRedraw() Typically this happens when C_REDRAW action calls wDraw*() on mainD, in which case it should be writing to tempD. Or the wDraw*() call should be removed if it is redundant. If you set a break point on the printf you'll see the offending wDraw*() call in the traceback This is not fatal but could result in garbage being left on the screen if the command is cancelled. */ cairo = gdk_cairo_create(bd->pixmap); } } width = width ? abs(width) : 1; if ( color == wDrawColorWhite ) width += 1; // Remove ghosts cairo_set_line_width(cairo, width); cairo_set_line_cap(cairo, CAIRO_LINE_CAP_BUTT); cairo_set_line_join(cairo, CAIRO_LINE_JOIN_MITER); switch(lineType) { case wDrawLineSolid: { cairo_set_dash(cairo, 0, 0, 0); break; } case wDrawLineDash: { double dashes[] = { 5, 3 }; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0); break; } case wDrawLineDot: { double dashes[] = { 1, 2 }; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0); break; } case wDrawLineDashDot: { double dashes[] = { 5, 2, 1, 2 }; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0); break; } case wDrawLineDashDotDot: { double dashes[] = { 5, 2, 1, 2, 1, 2 }; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0); break; } case wDrawLineCenter: { double dashes[] = { 8, 3, 5, 3}; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0.0); break; } case wDrawLinePhantom: { double dashes[] = { 8, 3, 5, 3, 5, 3}; static int len_dashes = sizeof(dashes) / sizeof(dashes[0]); cairo_set_dash(cairo, dashes, len_dashes, 0.0); break; } } GdkColor * gcolor; cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); gcolor = wlibGetColor(color, TRUE); bd->lastColor = color; cairo_set_source_rgb(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0); return cairo; } static cairo_t* gtkDrawDestroyCairoContext(cairo_t *cairo) { cairo_destroy(cairo); return NULL; } #ifdef CURSOR_SURFACE cairo_t* CreateCursorSurface(wControl_p ct, wSurface_p surface, wWinPix_t width, wWinPix_t height, wDrawColor color, wDrawOpts opts) { cairo_t * cairo = NULL; if ((opts&wDrawOptCursor) || (opts&wDrawOptCursorRmv)) { if (surface!=NULL || surface->width != width || surface->height != height) { if (surface->surface) cairo_surface_destroy(surface->surface); surface->surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width,height ); surface->width = width; surface->height = height; } cairo = gtkDrawCreateCairoCursorContext(ct,surface->surface,0,wDrawLineSolid, color, opts); cairo_save(cairo); cairo_set_source_rgba(cairo, 0.0, 0.0, 0.0, 0.0); cairo_paint(cairo); cairo_restore(cairo); surface->show = TRUE; cairo_set_operator(cairo,CAIRO_OPERATOR_SOURCE); } return cairo; } #endif void wDrawDelayUpdate( wDraw_p bd, wBool_t delay ) { GdkRectangle update_rect; if ( (!delay) && bd->delayUpdate ) { update_rect.x = 0; update_rect.y = 0; update_rect.width = bd->w; update_rect.height = bd->h; gtk_widget_draw( bd->widget, &update_rect ); } bd->delayUpdate = delay; } void wDrawLine( wDraw_p bd, wDrawPix_t x0, wDrawPix_t y0, wDrawPix_t x1, wDrawPix_t y1, wDrawWidth width, wDrawLineType_e lineType, wDrawColor color, wDrawOpts opts ) { GdkGC * gc; GdkRectangle update_rect; if ( bd == &psPrint_d ) { psPrintLine( x0, y0, x1, y1, width, lineType, color, opts ); return; } x0 = INMAPX(bd,x0); y0 = INMAPY(bd,y0); x1 = INMAPX(bd,x1); y1 = INMAPY(bd,y1); cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, width, lineType, color, opts); cairo_move_to(cairo, x0 + 0.5, y0 + 0.5); cairo_line_to(cairo, x1 + 0.5, y1 + 0.5); cairo_stroke(cairo); gtkDrawDestroyCairoContext(cairo); if (bd->widget) gtk_widget_queue_draw(GTK_WIDGET(bd->widget)); //,x0,y0+1,x1,y1+1); } /** * Draw an arc around a specified center * * \param bd IN ? * \param x0, y0 IN center of arc * \param r IN radius * \param angle0, angle1 IN start and end angle * \param drawCenter draw marking for center * \param width line width * \param lineType * \param color color * \param opts ? */ void wDrawArc( wDraw_p bd, wDrawPix_t x0, wDrawPix_t y0, wDrawPix_t r, wAngle_t angle0, wAngle_t angle1, int drawCenter, wDrawWidth width, wDrawLineType_e lineType, wDrawColor color, wDrawOpts opts ) { int x, y, w, h; if ( bd == &psPrint_d ) { psPrintArc( x0, y0, r, angle0, angle1, drawCenter, width, lineType, color, opts ); return; } if (r < 6.0/75.0) return; x = INMAPX(bd,x0-r); y = INMAPY(bd,y0+r); w = 2*r; h = 2*r; // now create the new arc cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, width, lineType, color, opts); cairo_new_path(cairo); // its center point marker if(drawCenter) { // draw a small crosshair to mark the center of the curve cairo_move_to(cairo, INMAPX(bd, x0 - (CENTERMARK_LENGTH / 2)), INMAPY(bd, y0 )); cairo_line_to(cairo, INMAPX(bd, x0 + (CENTERMARK_LENGTH / 2)), INMAPY(bd, y0 )); cairo_move_to(cairo, INMAPX(bd, x0), INMAPY(bd, y0 - (CENTERMARK_LENGTH / 2 ))); cairo_line_to(cairo, INMAPX(bd, x0) , INMAPY(bd, y0 + (CENTERMARK_LENGTH / 2))); cairo_new_sub_path( cairo ); } // draw the curve itself cairo_arc_negative(cairo, INMAPX(bd, x0), INMAPY(bd, y0), r, (angle0 - 90 + angle1) * (M_PI / 180.0), (angle0 - 90) * (M_PI / 180.0)); cairo_stroke(cairo); gtkDrawDestroyCairoContext(cairo); if (bd->widget && !bd->delayUpdate) gtk_widget_queue_draw_area(bd->widget,x,y,w,h); } void wDrawPoint( wDraw_p bd, wDrawPix_t x0, wDrawPix_t y0, wDrawColor color, wDrawOpts opts ) { GdkRectangle update_rect; if ( bd == &psPrint_d ) { /*psPrintArc( x0, y0, r, angle0, angle1, drawCenter, width, lineType, color, opts );*/ return; } cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opts); cairo_new_path(cairo); cairo_arc(cairo, INMAPX(bd, x0), INMAPY(bd, y0), 0.75, 0, 2 * M_PI); cairo_stroke(cairo); gtkDrawDestroyCairoContext(cairo); if (bd->widget && !bd->delayUpdate) gtk_widget_queue_draw_area(bd->widget,INMAPX(bd,x0-0.75),INMAPY(bd,y0+0.75),2,2); } /******************************************************************************* * * Strings * ******************************************************************************/ void wDrawString( wDraw_p bd, wDrawPix_t x, wDrawPix_t y, wAngle_t a, const char * s, wFont_p fp, wFontSize_t fs, wDrawColor color, wDrawOpts opts ) { PangoLayout *layout; GdkRectangle update_rect; wDrawPix_t w; wDrawPix_t h; wDrawPix_t ascent; wDrawPix_t descent; wDrawPix_t baseline; double angle = -M_PI * a / 180.0; if ( bd == &psPrint_d ) { psPrintString( x, y, a, (char *) s, fp, fs, color, opts ); return; } x = INMAPX(bd,x); y = INMAPY(bd,y); /* draw text */ cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opts); cairo_save( cairo ); cairo_identity_matrix(cairo); layout = wlibFontCreatePangoLayout(bd->widget, cairo, fp, fs, s, &w, &h, &ascent, &descent, &baseline); /* cairo does not support the old method of text removal by overwrite; * if color is White, then overwrite old text with a White rectangle */ GdkColor* const gcolor = wlibGetColor(color, TRUE); cairo_set_source_rgb(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0); cairo_translate( cairo, x, y ); cairo_rotate( cairo, angle ); cairo_translate( cairo, 0, -baseline); cairo_move_to(cairo, 0, 0); pango_cairo_update_layout(cairo, layout); pango_cairo_show_layout(cairo, layout); wlibFontDestroyPangoLayout(layout); cairo_restore( cairo ); gtkDrawDestroyCairoContext(cairo); if (bd->delayUpdate || bd->widget == NULL) return; /* recalculate the area to be updated * for simplicity sake I added plain text height ascent and descent, * mathematically correct would be to use the trigonometrical functions as well */ update_rect.x = (gint) x - 2; update_rect.y = (gint) y - (gint) (baseline + descent) - 2; update_rect.width = (gint) (w * cos( angle ) + h * sin(angle))+2; update_rect.height = (gint) (h * sin( angle ) + w * cos(angle))+2; gtk_widget_draw(bd->widget, &update_rect); if (bd->widget && !bd->delayUpdate) gtk_widget_queue_draw_area(bd->widget, update_rect.x, update_rect.y, update_rect.width, update_rect.height); } void wDrawGetTextSize( wDrawPix_t *w, wDrawPix_t *h, wDrawPix_t *d, wDrawPix_t *a, wDraw_p bd, const char * s, wFont_p fp, wFontSize_t fs ) { wDrawPix_t textWidth; wDrawPix_t textHeight; wDrawPix_t ascent; wDrawPix_t descent; wDrawPix_t baseline; *w = 0; *h = 0; /* draw text */ cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, wDrawColorBlack, bd->bTempMode?wDrawOptTemp:0 ); cairo_identity_matrix(cairo); wlibFontDestroyPangoLayout( wlibFontCreatePangoLayout(bd->widget, cairo, fp, fs, s, &textWidth, &textHeight, &ascent, &descent, &baseline) ); *w = textWidth; *h = textHeight; *a = ascent; //*d = textHeight-ascent; *d = descent; if (debugWindow >= 3) fprintf(stderr, "text metrics: w=%0.1f, h=%0.1f, d=%0.1f\n", *w, *h, *d); gtkDrawDestroyCairoContext(cairo); } /******************************************************************************* * * Basic Drawing Functions * *******************************************************************************/ static void wlibDrawFilled( cairo_t * cairo, wDrawColor color, wDrawOpts opt ) { if ( (opt & wDrawOptTransparent) != 0 ) { if ( (opt & wDrawOptTemp) == 0 ) { cairo_set_source_rgb(cairo, 0,0,0); cairo_set_operator(cairo, CAIRO_OPERATOR_DIFFERENCE); cairo_fill_preserve(cairo); } GdkColor * gcolor = wlibGetColor(color, TRUE); cairo_set_source_rgba(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0, 1.0); cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); cairo_stroke_preserve(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(cairo, gcolor->red / 65535.0, gcolor->green / 65535.0, gcolor->blue / 65535.0, 0.3); } cairo_fill(cairo); } void wDrawFilledRectangle( wDraw_p bd, wDrawPix_t x, wDrawPix_t y, wDrawPix_t w, wDrawPix_t h, wDrawColor color, wDrawOpts opt ) { GdkRectangle update_rect; if ( bd == &psPrint_d ) { psPrintFillRectangle( x, y, w, h, color, opt ); return; } x = INMAPX(bd,x); y = INMAPY(bd,y)-h; cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opt); cairo_move_to(cairo, x, y); cairo_rel_line_to(cairo, w, 0); cairo_rel_line_to(cairo, 0, h); cairo_rel_line_to(cairo, -w, 0); cairo_rel_line_to(cairo, 0, -h); wlibDrawFilled( cairo, color, opt ); gtkDrawDestroyCairoContext(cairo); if (bd->widget && !bd->delayUpdate) gtk_widget_queue_draw_area(GTK_WIDGET(bd->widget),x,y,w,h); } void wDrawPolygon( wDraw_p bd, wDrawPix_t p[][2], wPolyLine_e type[], int cnt, wDrawColor color, wDrawWidth dw, wDrawLineType_e lt, wDrawOpts opt, int fill, int open ) { static int maxCnt = 0; static GdkPoint *points; int i; if ( bd == &psPrint_d ) { psPrintFillPolygon( p, type, cnt, color, opt, fill, open ); return; } if (cnt > maxCnt) { if (points == NULL) points = (GdkPoint*)malloc( cnt*sizeof *points ); else points = (GdkPoint*)realloc( points, cnt*sizeof *points ); if (points == NULL) abort(); maxCnt = cnt; } wDrawPix_t min_x,max_x,min_y,max_y; min_x = max_x = INMAPX(bd,p[0][0]); min_y = max_y = INMAPY(bd,p[0][1]); for (i=0; i max_x) max_x = points[i].x; if (points[i].y > max_y) max_y = points[i].y; points[i].y = INMAPY(bd,p[i][1]); } cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, fill?0:dw, fill?wDrawLineSolid:lt, color, opt); for(i = 0; i < cnt; ++i) { int j = i-1; int k = i+1; if (j < 0) j = cnt-1; if (k > cnt-1) k = 0; GdkPoint mid0, mid1, mid3, mid4; // save is static because of an apparent compiler bug on Linux // This happens with RelWithDebInfo target // If the first segment is a line then save should = points[0] // However it becomes mid0 instead which causes the last corner to be misplaced. static GdkPoint save; double len0, len1; double d0x = (points[i].x-points[j].x); double d0y = (points[i].y-points[j].y); double d1x = (points[k].x-points[i].x); double d1y = (points[k].y-points[i].y); len0 = (d0x*d0x+d0y*d0y); len1 = (d1x*d1x+d1y*d1y); mid0.x = (d0x/2)+points[j].x; mid0.y = (d0y/2)+points[j].y; mid1.x = (d1x/2)+points[i].x; mid1.y = (d1y/2)+points[i].y; if (type && (type[i] == wPolyLineRound) && (len1>0) && (len0>0)) { double ratio = sqrt(len0/len1); if (len0 < len1) { mid1.x = ((d1x*ratio)/2)+points[i].x; mid1.y = ((d1y*ratio)/2)+points[i].y; } else { mid0.x = points[i].x-(d0x/(2*ratio)); mid0.y = points[i].y-(d0y/(2*ratio)); } } mid3.x = (points[i].x-mid0.x)/2+mid0.x; mid3.y = (points[i].y-mid0.y)/2+mid0.y; mid4.x = (mid1.x-points[i].x)/2+points[i].x; mid4.y = (mid1.y-points[i].y)/2+points[i].y; points[i].x = round(points[i].x)+0.5; points[i].y = round(points[i].y)+0.5; mid0.x = round(mid0.x)+0.5; mid0.y = round(mid0.y)+0.5; mid1.x = round(mid1.x)+0.5; mid1.y = round(mid1.y)+0.5; mid3.x = round(mid3.x)+0.5; mid3.y = round(mid3.y)+0.5; mid4.x = round(mid4.x)+0.5; mid4.y = round(mid4.y)+0.5; if(i==0) { if (!type || type[i] == wPolyLineStraight || open) { cairo_move_to(cairo, points[i].x, points[i].y); save = points[0]; } else { cairo_move_to(cairo, mid0.x, mid0.y); if (type[i] == 1) cairo_curve_to(cairo, points[i].x, points[i].y, points[i].x, points[i].y, mid1.x, mid1.y); else cairo_curve_to(cairo, mid3.x, mid3.y, mid4.x, mid4.y, mid1.x, mid1.y); save = mid0; } } else if (!type || type[i] == wPolyLineStraight || (open && (i==cnt-1))) { cairo_line_to(cairo, points[i].x, points[i].y); } else { cairo_line_to(cairo, mid0.x, mid0.y); if (type[i] == wPolyLineSmooth) cairo_curve_to(cairo, points[i].x, points[i].y, points[i].x, points[i].y, mid1.x, mid1.y); else cairo_curve_to(cairo, mid3.x, mid3.y, mid4.x, mid4.y, mid1.x, mid1.y); } if ((i==cnt-1) && !open) { cairo_line_to(cairo, save.x, save.y); } } if (fill && !open) { wlibDrawFilled( cairo, color, opt ); } else { cairo_stroke(cairo); } gtkDrawDestroyCairoContext(cairo); if (bd->widget && !bd->delayUpdate) gtk_widget_queue_draw_area(GTK_WIDGET(bd->widget),min_x,min_y,max_x-min_y,max_y-min_y); } void wDrawFilledCircle( wDraw_p bd, wDrawPix_t x0, wDrawPix_t y0, wDrawPix_t r, wDrawColor color, wDrawOpts opt ) { int x, y, w, h; if ( bd == &psPrint_d ) { psPrintFillCircle( x0, y0, r, color, opt ); return; } x = INMAPX(bd,x0-r); y = INMAPY(bd,y0+r); w = 2*r; h = 2*r; cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opt); cairo_arc(cairo, INMAPX(bd, x0), INMAPY(bd, y0), r, 0, 2 * M_PI); wlibDrawFilled( cairo, color, opt ); gtkDrawDestroyCairoContext(cairo); if (bd->widget) gtk_widget_queue_draw_area(GTK_WIDGET(bd->widget),x,y,w,h); } void wDrawClearTemp(wDraw_p bd) { //Wipe out temp space with 0 alpha (transparent) static long cDCT = 0; if ( iDrawLog ) printf( "wDrawClearTemp %ld\n", cDCT++ ); cairo_t* cairo = cairo_create(bd->temp_surface); cairo_set_source_rgba(cairo, 0.0, 0.0, 0.0, 0.0); cairo_set_operator (cairo, CAIRO_OPERATOR_SOURCE); cairo_move_to(cairo, 0, 0); cairo_rel_line_to(cairo, bd->w, 0); cairo_rel_line_to(cairo, 0, bd->h); cairo_rel_line_to(cairo, -bd->w, 0); cairo_fill(cairo); cairo_destroy(cairo); if (bd->widget && !bd->delayUpdate) gtk_widget_queue_draw(bd->widget); } void wDrawClear( wDraw_p bd ) { cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, wDrawColorWhite, 0); cairo_move_to(cairo, 0, 0); cairo_rel_line_to(cairo, bd->w, 0); cairo_rel_line_to(cairo, 0, bd->h); cairo_rel_line_to(cairo, -bd->w, 0); cairo_fill(cairo); if (bd->widget) gtk_widget_queue_draw(bd->widget); gtkDrawDestroyCairoContext(cairo); wDrawClearTemp(bd); } void * wDrawGetContext( wDraw_p bd ) { return bd->context; } /******************************************************************************* * * Bit Maps * *******************************************************************************/ wDrawBitMap_p wDrawBitMapCreate( wDraw_p bd, int w, int h, int x, int y, const unsigned char * fbits ) { wDrawBitMap_p bm; bm = (wDrawBitMap_p)malloc( sizeof *bm ); bm->w = w; bm->h = h; /*bm->pixmap = gtkMakeIcon( NULL, fbits, w, h, wDrawColorBlack, &bm->mask );*/ bm->bits = fbits; bm->x = x; bm->y = y; return bm; } void wDrawBitMap( wDraw_p bd, wDrawBitMap_p bm, wDrawPix_t x, wDrawPix_t y, wDrawColor color, wDrawOpts opts ) { int i, j, wb; wDrawPix_t xx, yy; wControl_p b; wWin_p win; GdkDrawable * gdk_drawable, * cairo_surface; GtkWidget * widget = bd->widget; static long cDBM = 0; if ( iDrawLog ) printf( "wDrawBitMap %ld\n", cDBM++ ); x = INMAPX( bd, x-bm->x ); y = INMAPY( bd, y-bm->y )-bm->h; wb = (bm->w+7)/8; cairo_t* cairo; #ifdef CURSOR_SURFACE if (opts&wDrawOptCursorRmv) color = wDrawColorWhite; //Wipeout existing cursor draw (simplistic first) if ((opts&wDrawOptCursor) || (opts&wDrawOptCursorRmv) || (opts&wDrawOptCursorQuit)) { cairo = CreateCursorSurface((wControl_p)bd,&bd->cursor_surface, bd->w, bd->h, color, opts); if ((opts&wDrawOptCursorRmv) || (opts&wDrawOptCursorQuit)) { bd->cursor_surface.show = FALSE; } else bd->cursor_surface.show = TRUE; widget = bd->widget; } else { cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opts); widget = bd->widget; } GtkWidget * new_widget = widget; GdkGC * gc = NULL; GdkWindow * gdk_window = NULL; win = bd->parent; #endif cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, color, opts); for ( i=0; iw; i++ ) for ( j=0; jh; j++ ) if ( bm->bits[ j*wb+(i>>3) ] & (1<<(i&07)) ) { xx = x+i; yy = y+j; #ifdef CURSOR_SURFACE if ( 0 <= xx && xx < bd->w && 0 <= yy && yy < bd->h ) { b = (wControl_p)bd; } else if ( (opts&wDrawOptNoClip) != 0 ) { xx += bd->realX; yy += bd->realY; b = wlibGetControlFromPos( bd->parent, xx, yy ); if ( b) { xx -= b->realX; yy -= b->realY; new_widget = b->widget; } else { new_widget = bd->parent->widget; } } else { continue; } if (new_widget != widget) { if (cairo) cairo_destroy(cairo); cairo = NULL; if (widget && (widget != bd->parent->widget)) gtk_widget_queue_draw(GTK_WIDGET(widget)); if ( (opts&wDrawOptCursor) || (opts&wDrawOptCursorRmv) || (opts&wDrawOptCursorQuit)) { if (!b) b = (wControl_p)(bd->parent->widget); cairo = CreateCursorSurface(b,&b->cursor_surface, b->w, b->h, color, opts); widget = b->widget; gc = NULL; if ((opts&wDrawOptCursorRmv) || (opts&wDrawOptCursorQuit)) b->cursor_surface.show = FALSE; else b->cursor_surface.show = TRUE; } else { continue; } widget = new_widget; } if ((opts&wDrawOptCursorQuit) || (opts&wDrawOptCursorQuit) ) continue; #endif cairo_rectangle(cairo, xx, yy, 1, 1); cairo_fill(cairo); } cairo_destroy(cairo); if (widget && !bd->delayUpdate) gtk_widget_queue_draw_area(GTK_WIDGET(widget), x, y, bm->w, bm->h); } /******************************************************************************* * * Event Handlers * *******************************************************************************/ void wDrawSaveImage( wDraw_p bd ) { cairo_t * cr; if ( bd->pixmapBackup ) { gdk_pixmap_unref( bd->pixmapBackup ); } bd->pixmapBackup = gdk_pixmap_new( bd->widget->window, bd->w, bd->h, -1 ); cr = gdk_cairo_create(bd->pixmapBackup); gdk_cairo_set_source_pixmap(cr, bd->pixmap, 0, 0); cairo_paint(cr); cairo_destroy(cr); cr = NULL; } void wDrawRestoreImage( wDraw_p bd ) { GdkRectangle update_rect; if ( bd->pixmapBackup ) { cairo_t * cr; cr = gdk_cairo_create(bd->pixmap); gdk_cairo_set_source_pixmap(cr, bd->pixmapBackup, 0, 0); cairo_paint(cr); cairo_destroy(cr); cr = NULL; if ( bd->delayUpdate || bd->widget == NULL ) return; update_rect.x = 0; update_rect.y = 0; update_rect.width = bd->w; update_rect.height = bd->h; gtk_widget_draw( bd->widget, &update_rect ); } } void wDrawSetSize( wDraw_p bd, wWinPix_t w, wWinPix_t h , void * redraw) { wBool_t repaint; if (bd == NULL) { fprintf(stderr,"resizeDraw: no client data\n"); return; } /* Negative values crashes the program */ if (w < 0 || h < 0) return; repaint = (w != bd->w || h != bd->h); bd->w = w; bd->h = h; gtk_widget_set_size_request( bd->widget, w, h ); if (repaint) { if (bd->pixmap) gdk_pixmap_unref( bd->pixmap ); bd->pixmap = gdk_pixmap_new( bd->widget->window, w, h, -1 ); if (bd->temp_surface) cairo_surface_destroy( bd->temp_surface); bd->temp_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, w,h ); wDrawClear( bd ); if (!redraw) bd->redraw( bd, bd->context, w, h ); } /*wRedraw( bd )*/; } void wDrawGetSize( wDraw_p bd, wWinPix_t *w, wWinPix_t *h ) { if (bd->widget) wlibControlGetSize( (wControl_p)bd ); *w = bd->w-2; *h = bd->h-2; } /** * Return the resolution of a device in dpi * * \param d IN the device * \return the resolution in dpi */ double wDrawGetDPI( wDraw_p d ) { //if (d == &psPrint_d) //return 1440.0; //else return d->dpi; } double wDrawGetMaxRadius( wDraw_p d ) { if (d == &psPrint_d) return 10e9; else return 32767.0; } void wDrawClip( wDraw_p d, wDrawPix_t x, wDrawPix_t y, wDrawPix_t w, wDrawPix_t h ) { GdkRectangle rect; rect.width = (wWinPix_t)w; rect.height = (wWinPix_t)h; rect.x = (wWinPix_t)INMAPX( d, x ); rect.y = (wWinPix_t)INMAPY( d, y ) - rect.height; gdk_gc_set_clip_rectangle( d->gc, &rect ); } static gint draw_expose_event( GtkWidget *widget, GdkEventExpose *event, wDraw_p bd) { static long cDEE = 0; if ( iDrawLog ) printf( "draw_expose_event %ld %dx%d+%dx%d %ldx%ld+%ldx%ld\n", cDEE++, event->area.x, event->area.y, event->area.width, event->area.height, 0L, bd->w, 0L, bd->h ); cairo_t* cairo = gdk_cairo_create (widget->window); gdk_cairo_set_source_pixmap(cairo,bd->pixmap,0,0); cairo_rectangle(cairo,event->area.x, event->area.y, event->area.width, event->area.height); cairo_set_operator(cairo,CAIRO_OPERATOR_SOURCE); cairo_fill(cairo); cairo_set_source_surface(cairo,bd->temp_surface,0,0); cairo_rectangle(cairo,event->area.x, event->area.y, event->area.width, event->area.height); cairo_set_operator(cairo,CAIRO_OPERATOR_OVER); cairo_fill(cairo); #ifdef CURSOR_SURFACE if (bd->cursor_surface.surface && bd->cursor_surface.show) { cairo_set_source_surface(cairo,bd->cursor_surface.surface,0,0); cairo_set_operator(cairo,CAIRO_OPERATOR_OVER); cairo_rectangle(cairo,event->area.x, event->area.y, event->area.width, event->area.height); cairo_fill(cairo); } #endif cairo_destroy(cairo); return TRUE; } static gint draw_configure_event( GtkWidget *widget, GdkEventConfigure *event, wDraw_p bd) { return TRUE; } static const char * actionNames[] = { "None", "Move", "LDown", "LDrag", "LUp", "RDown", "RDrag", "RUp", "Text", "ExtKey", "WUp", "WDown", "DblL", "ModK", "ScrU", "ScrD", "ScrL", "ScrR" }; /** * Handler for scroll events, ie mouse wheel activity */ static int scrollTimer; static int timer_busy_count; static wAction_t lastAction; static int ScrollTimerPop(wDraw_p bd) { if (timer_busy_count>1) { timer_busy_count = 0; scrollTimer = 0; } else { timer_busy_count++; return TRUE; } if (drawVerbose >= 2) printf( "%s-Pop\n", actionNames[lastAction] ); bd->action( bd, bd->context, lastAction, (wDrawPix_t)0, (wDrawPix_t)0 ); return FALSE; } static gint draw_scroll_event( GtkWidget *widget, GdkEventScroll *event, wDraw_p bd) { wAction_t action; static int oldEventX = 0; static int oldEventY = 0; static int newEventX = 0; static int newEventY = 0; if (event->state & (GDK_SHIFT_MASK|GDK_BUTTON2_MASK|GDK_MOD1_MASK)) { newEventX = OUTMAPX(bd, event->x); newEventY = OUTMAPY(bd, event->y); oldEventX = OUTMAPX(bd, event->x_root); oldEventY = OUTMAPX(bd, event->y_root); switch( event->direction ) { case GDK_SCROLL_UP: if (event->state & GDK_CONTROL_MASK) action = wActionScrollRight; else action = wActionScrollUp; break; case GDK_SCROLL_DOWN: if (event->state & GDK_CONTROL_MASK) action = wActionScrollLeft; else action = wActionScrollDown; break; case GDK_SCROLL_LEFT: action = wActionScrollLeft; break; case GDK_SCROLL_RIGHT: action = wActionScrollRight; break; default: return TRUE; break; } if (drawVerbose >= 2) printf( "%sNew[%dx%d]Delta[%dx%d]\n", actionNames[action], newEventX, newEventY, oldEventX, oldEventY ); if (scrollTimer) { // Already have a timer lastAction = action; return TRUE; } else { lastAction = action; timer_busy_count = 0; scrollTimer = g_timeout_add(25,(GSourceFunc)ScrollTimerPop,bd); // 25ms delay return TRUE; } } else { switch( event->direction ) { case GDK_SCROLL_UP: action = wActionWheelUp; break; case GDK_SCROLL_DOWN: action = wActionWheelDown; break; case GDK_SCROLL_LEFT: return TRUE; break; case GDK_SCROLL_RIGHT: return TRUE; break; default: break; } } if (action != 0) { if (drawVerbose >= 2) printf( "%s[%ldx%ld]\n", actionNames[action], bd->lastX, bd->lastY ); bd->action( bd, bd->context, action, (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY); } return TRUE; } static gint draw_leave_event( GtkWidget *widget, GdkEvent * event ) { wlibHelpHideBalloon(); return TRUE; } /** * Handler for mouse button clicks. */ static gint draw_button_event( GtkWidget *widget, GdkEventButton *event, wDraw_p bd ) { wAction_t action = 0; if (bd->action == NULL) return TRUE; bd->lastX = OUTMAPX(bd, event->x); bd->lastY = OUTMAPY(bd, event->y); switch ( event->button ) { case 1: /* left mouse button */ case 2: /* middle mouse button */ action = event->type==GDK_BUTTON_PRESS?wActionLDown:wActionLUp; if (event->type==GDK_2BUTTON_PRESS) action = wActionLDownDouble; /*bd->action( bd, bd->context, event->type==GDK_BUTTON_PRESS?wActionLDown:wActionLUp, (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY );*/ break; case 3: /* right mouse button */ action = event->type==GDK_BUTTON_PRESS?wActionRDown:wActionRUp; /*bd->action( bd, bd->context, event->type==GDK_BUTTON_PRESS?wActionRDown:wActionRUp, (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY );*/ break; } if (action != 0) { if (drawVerbose >= 2) printf( "%s[%ldx%ld]\n", actionNames[action], bd->lastX, bd->lastY ); bd->action( bd, bd->context, action, (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY ); } if (!(bd->option & BD_NOFOCUS)) gtk_widget_grab_focus( bd->widget ); return TRUE; } static gint draw_motion_event( GtkWidget *widget, GdkEventMotion *event, wDraw_p bd ) { int x, y; GdkModifierType state; wAction_t action; if (bd->action == NULL) return TRUE; if (event->is_hint) { gdk_window_get_pointer (event->window, &x, &y, &state); } else { x = event->x; y = event->y; state = event->state; } if (state & GDK_BUTTON1_MASK) { action = wActionLDrag; } else if (state & GDK_BUTTON2_MASK) { action = wActionLDrag; } else if (state & GDK_BUTTON3_MASK) { action = wActionRDrag; } else { action = wActionMove; } bd->lastX = OUTMAPX(bd, x); bd->lastY = OUTMAPY(bd, y); if (drawVerbose >= 2) printf( "%lx: %s[%ldx%ld] %s\n", (long)bd, actionNames[action], bd->lastX, bd->lastY, event->is_hint?"":"<>" ); bd->action( bd, bd->context, action, (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY ); if (!(bd->option & BD_NOFOCUS)) gtk_widget_grab_focus( bd->widget ); return TRUE; } static gint draw_char_release_event( GtkWidget * widget, GdkEventKey *event, wDraw_p bd ) { GdkModifierType modifiers; guint key = event->keyval; wModKey_e modKey = wModKey_None; switch (key) { case GDK_KEY_Alt_L: modKey = wModKey_Alt; break; case GDK_KEY_Alt_R: modKey = wModKey_Alt; break; case GDK_KEY_Shift_L: modKey = wModKey_Shift; break; case GDK_KEY_Shift_R: modKey = wModKey_Shift; break; case GDK_KEY_Control_L: modKey = wModKey_Ctrl; break; case GDK_KEY_Control_R: modKey = wModKey_Ctrl; break; default: ; } if (modKey!= wModKey_None && (bd->option & BD_MODKEYS)) { bd->action(bd, bd->context, wActionModKey+((int)modKey<<8), (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY ); if (!(bd->option & BD_NOFOCUS)) gtk_widget_grab_focus( bd->widget ); return TRUE; } else { return FALSE; } return FALSE; } static gint draw_char_event( GtkWidget * widget, GdkEventKey *event, wDraw_p bd ) { GdkModifierType modifiers; guint key = event->keyval; wAccelKey_e extKey = wAccelKey_None; wModKey_e modKey = wModKey_None; switch (key) { case GDK_KEY_Escape: key = 0x1B; break; case GDK_KEY_Return: modifiers = gtk_accelerator_get_default_mod_mask(); if (((event->state & modifiers)==GDK_CONTROL_MASK) || ((event->state & modifiers)==GDK_MOD1_MASK)) extKey = wAccelKey_LineFeed; //If Return plus Control or Alt send in LineFeed key = 0x0D; break; case GDK_KEY_Linefeed: key = 0x0A; break; case GDK_KEY_Tab: key = 0x09; break; case GDK_KEY_BackSpace: key = 0x08; break; case GDK_KEY_Delete: extKey = wAccelKey_Del; break; case GDK_KEY_Insert: extKey = wAccelKey_Ins; break; case GDK_KEY_Home: extKey = wAccelKey_Home; break; case GDK_KEY_End: extKey = wAccelKey_End; break; case GDK_KEY_Page_Up: extKey = wAccelKey_Pgup; break; case GDK_KEY_Page_Down: extKey = wAccelKey_Pgdn; break; case GDK_KEY_Up: extKey = wAccelKey_Up; break; case GDK_KEY_Down: extKey = wAccelKey_Down; break; case GDK_KEY_Right: extKey = wAccelKey_Right; break; case GDK_KEY_Left: extKey = wAccelKey_Left; break; case GDK_KEY_F1: extKey = wAccelKey_F1; break; case GDK_KEY_F2: extKey = wAccelKey_F2; break; case GDK_KEY_F3: extKey = wAccelKey_F3; break; case GDK_KEY_F4: extKey = wAccelKey_F4; break; case GDK_KEY_F5: extKey = wAccelKey_F5; break; case GDK_KEY_F6: extKey = wAccelKey_F6; break; case GDK_KEY_F7: extKey = wAccelKey_F7; break; case GDK_KEY_F8: extKey = wAccelKey_F8; break; case GDK_KEY_F9: extKey = wAccelKey_F9; break; case GDK_KEY_F10: extKey = wAccelKey_F10; break; case GDK_KEY_F11: extKey = wAccelKey_F11; break; case GDK_KEY_F12: extKey = wAccelKey_F12; break; case GDK_KEY_Alt_L: modKey = wModKey_Alt; break; case GDK_KEY_Alt_R: modKey = wModKey_Alt; break; case GDK_KEY_Shift_L: modKey = wModKey_Shift; break; case GDK_KEY_Shift_R: modKey = wModKey_Shift; break; case GDK_KEY_Control_L: modKey = wModKey_Ctrl; break; case GDK_KEY_Control_R: modKey = wModKey_Ctrl; break; default: ; } if (extKey != wAccelKey_None) { if ( wlibFindAccelKey( event ) == NULL ) { bd->action( bd, bd->context, wActionExtKey + ((int)extKey<<8), (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY ); } if (!(bd->option & BD_NOFOCUS)) gtk_widget_grab_focus( bd->widget ); return TRUE; } else if ((key >=wAccelKey_Up) && (key<=wAccelKey_Left) && bd->action) { bd->action( bd, bd->context, wActionText+(key<<8), (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY ); if (!(bd->option & BD_NOFOCUS)) gtk_widget_grab_focus( bd->widget ); return TRUE; } else if (key <= 0xFF && (event->state&(GDK_CONTROL_MASK|GDK_MOD1_MASK)) == 0 && bd->action) { bd->action( bd, bd->context, wActionText+(key<<8), (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY ); if (!(bd->option & BD_NOFOCUS)) gtk_widget_grab_focus( bd->widget ); return TRUE; } else if (modKey!= wModKey_None && (bd->option & BD_MODKEYS)) { bd->action(bd, bd->context, wActionModKey+((int)modKey<<8), (wDrawPix_t)bd->lastX, (wDrawPix_t)bd->lastY ); if (!(bd->option & BD_NOFOCUS)) gtk_widget_grab_focus( bd->widget ); return TRUE; } else { return FALSE; } } /******************************************************************************* * * Create * *******************************************************************************/ int XW = 0; int XH = 0; int xw, xh, cw, ch; wDraw_p wDrawCreate( wWin_p parent, wWinPix_t x, wWinPix_t y, const char * helpStr, long option, wWinPix_t width, wWinPix_t height, void * context, wDrawRedrawCallBack_p redraw, wDrawActionCallBack_p action ) { wDraw_p bd; bd = (wDraw_p)wlibAlloc( parent, B_DRAW, x, y, NULL, sizeof *bd, NULL ); bd->option = option; bd->context = context; bd->redraw = redraw; bd->action = action; bd->bTempMode = FALSE; wlibComputePos( (wControl_p)bd ); bd->widget = gtk_drawing_area_new(); gtk_drawing_area_size( GTK_DRAWING_AREA(bd->widget), width, height ); gtk_widget_set_size_request( GTK_WIDGET(bd->widget), width, height ); gtk_signal_connect (GTK_OBJECT (bd->widget), "expose_event", (GtkSignalFunc) draw_expose_event, bd); gtk_signal_connect (GTK_OBJECT(bd->widget),"configure_event", (GtkSignalFunc) draw_configure_event, bd); gtk_signal_connect (GTK_OBJECT (bd->widget), "motion_notify_event", (GtkSignalFunc) draw_motion_event, bd); gtk_signal_connect (GTK_OBJECT (bd->widget), "button_press_event", (GtkSignalFunc) draw_button_event, bd); gtk_signal_connect (GTK_OBJECT (bd->widget), "button_release_event", (GtkSignalFunc) draw_button_event, bd); gtk_signal_connect (GTK_OBJECT (bd->widget), "scroll_event", (GtkSignalFunc) draw_scroll_event, bd); gtk_signal_connect_after (GTK_OBJECT (bd->widget), "key_press_event", (GtkSignalFunc) draw_char_event, bd); gtk_signal_connect_after (GTK_OBJECT (bd->widget), "key_release_event", (GtkSignalFunc) draw_char_release_event, bd); gtk_signal_connect (GTK_OBJECT (bd->widget), "leave_notify_event", (GtkSignalFunc) draw_leave_event, bd); gtk_widget_set_can_focus(bd->widget,!(option & BD_NOFOCUS)); //if (!(option & BD_NOFOCUS)) // GTK_WIDGET_SET_FLAGS(GTK_WIDGET(bd->widget), GTK_CAN_FOCUS); gtk_widget_set_events (bd->widget, GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK ); bd->lastColor = -1; double dpi; wPrefGetFloat(PREFSECTION, DPISET, &dpi, 96.0); bd->dpi = dpi; bd->maxW = bd->w = width; bd->maxH = bd->h = height; gtk_fixed_put( GTK_FIXED(parent->widget), bd->widget, bd->realX, bd->realY ); wlibControlGetSize( (wControl_p)bd ); gtk_widget_realize( bd->widget ); bd->pixmap = gdk_pixmap_new( bd->widget->window, width, height, -1 ); bd->temp_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width,height ); wDrawClear(bd); bd->gc = gdk_gc_new( parent->gtkwin->window ); gdk_gc_copy( bd->gc, parent->gtkwin->style->base_gc[GTK_STATE_NORMAL] ); { GdkCursor * cursor; cursor = gdk_cursor_new ( GDK_TCROSS ); gdk_window_set_cursor ( bd->widget->window, cursor); gdk_cursor_destroy (cursor); } #ifdef LATER if (labelStr) bd->labelW = gtkAddLabel( (wControl_p)bd, labelStr ); #endif gtk_widget_show( bd->widget ); wlibAddButton( (wControl_p)bd ); gtkAddHelpString( bd->widget, helpStr ); return bd; } /******************************************************************************* * * BitMaps * *******************************************************************************/ wDraw_p wBitMapCreate( wWinPix_t w, wWinPix_t h, int arg ) { wDraw_p bd; bd = (wDraw_p)wlibAlloc( gtkMainW, B_DRAW, 0, 0, NULL, sizeof *bd, NULL ); bd->lastColor = -1; double dpi; wPrefGetFloat(PREFSECTION, DPISET, &dpi, 96.0); bd->dpi = dpi; bd->maxW = bd->w = w; bd->maxH = bd->h = h; bd->pixmap = gdk_pixmap_new( gtkMainW->widget->window, w, h, -1 ); bd->widget = gtk_pixmap_new(bd->pixmap, NULL); if ( bd->pixmap == NULL ) { wNoticeEx( NT_ERROR, "CreateBitMap: pixmap_new failed", "Ok", NULL ); return FALSE; } bd->gc = gdk_gc_new( gtkMainW->gtkwin->window ); if ( bd->gc == NULL ) { wNoticeEx( NT_ERROR, "CreateBitMap: gc_new failed", "Ok", NULL ); return FALSE; } gdk_gc_copy( bd->gc, gtkMainW->gtkwin->style->base_gc[GTK_STATE_NORMAL] ); wDrawClear( bd ); return bd; } wBool_t wBitMapDelete( wDraw_p d ) { gdk_pixmap_unref( d->pixmap ); d->pixmap = NULL; return TRUE; } /******************************************************************************* * * Background * ******************************************************************************/ int wDrawSetBackground( wDraw_p bd, char * path, char ** error) { GError *err = NULL; if (bd->background) { g_object_unref(bd->background); } if (path) { bd->background = gdk_pixbuf_new_from_file (path, &err); if (!bd->background) { *error = err->message; return -1; } } else { bd->background = NULL; return 1; } return 0; } /** * Use a loaded background in another context. * * \param from context with background * \param to context to get a reference to the existing background */ void wDrawCloneBackground(wDraw_p from, wDraw_p to) { if (from->background) { to->background = from->background; } else { to->background = NULL; } } /** * Draw background to screen. The background will be sized and rotated before being shown. The bitmap * is scaled so that the width is equal to size. The height is changed proportionally. * * \param bd drawing context * \param pos_x, pos_y bitmap position * \param size desired width after scaling * \param angle * \param screen visibility of bitmap in percent */ void wDrawShowBackground( wDraw_p bd, wWinPix_t pos_x, wWinPix_t pos_y, wWinPix_t size, wAngle_t angle, int screen) { if (bd->background) { cairo_t* cairo = gtkDrawCreateCairoContext(bd, NULL, 0, wDrawLineSolid, wDrawColorWhite, bd->bTempMode?wDrawOptTemp:0 ); cairo_save(cairo); int pixels_width = gdk_pixbuf_get_width(bd->background); int pixels_height = gdk_pixbuf_get_height(bd->background); double scale; double posx,posy,width,sized; posx = (double)pos_x; posy = (double)pos_y; if (size == 0) { scale = 1.0; } else { sized = (double)size; width = (double)pixels_width; scale = sized/width; } cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); double rad = M_PI*(angle/180); posy = (double)bd->h-((pixels_height*fabs(cos(rad))+pixels_width*fabs(sin(rad)))*scale)-posy; //width = (double)(pixels_width*scale); //height = (double)(pixels_height*scale); cairo_translate(cairo,posx,posy); cairo_scale(cairo, scale, scale); cairo_translate(cairo, fabs(pixels_width/2.0*cos(rad))+fabs(pixels_height/2.0*sin(rad)), fabs(pixels_width/2.0*sin(rad))+fabs(pixels_height/2.0*cos(rad))); cairo_rotate(cairo, M_PI*(angle/180.0)); // We need to clip around the image, or cairo will paint garbage data cairo_rectangle(cairo, -pixels_width/2.0, -pixels_height/2.0, pixels_width, pixels_height); cairo_clip(cairo); gdk_cairo_set_source_pixbuf(cairo, bd->background, -pixels_width/2.0, -pixels_height/2.0); cairo_pattern_t *mask = cairo_pattern_create_rgba (1.0,1.0,1.0,(100.0-screen)/100.0); cairo_mask(cairo,mask); cairo_pattern_destroy(mask); cairo_restore(cairo); gtkDrawDestroyCairoContext(cairo); gtk_widget_queue_draw(bd->widget); } }