Fixed UTF-8 file save bug Update to circle start handler Started two
[dasher.git] / Src / Gtk2 / Canvas.cpp
blob793544248874110b0b09c85af9b682217574df6c
1 #include "../Common/Common.h"
3 #include "Canvas.h"
4 #include "DasherControl.h"
6 #include "../DasherCore/DasherTypes.h"
8 using namespace Dasher;
10 extern "C" gint canvas_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data);
12 CCanvas::CCanvas(GtkWidget *pCanvas, CPangoCache *pPangoCache)
13 : CDasherScreen(pCanvas->allocation.width, pCanvas->allocation.height) {
15 #if WITH_CAIRO
16 cairo_colours = 0;
17 #else
18 colours = 0;
19 #endif
21 m_pCanvas = pCanvas;
22 m_pPangoCache = pPangoCache;
24 m_iWidth = m_pCanvas->allocation.width;
25 m_iHeight = m_pCanvas->allocation.height;
27 // Construct the buffer pixmaps
28 // FIXME - only allocate without cairo
31 m_pDummyBuffer = gdk_pixmap_new(pCanvas->window, m_iWidth, m_iHeight, -1);
33 m_pDisplayBuffer = gdk_pixmap_new(pCanvas->window, m_iWidth, m_iHeight, -1);
34 m_pDecorationBuffer = gdk_pixmap_new(pCanvas->window, m_iWidth, m_iHeight, -1);
35 m_pOnscreenBuffer = gdk_pixmap_new(pCanvas->window, m_iWidth, m_iHeight, -1);
37 // Set the display buffer to be current
39 m_pOffscreenBuffer = m_pDisplayBuffer;
42 #if WITH_CAIRO
43 // The lines between origin and pointer is draw here
44 decoration_cr = gdk_cairo_create(m_pDecorationBuffer);
45 cr = decoration_cr;
46 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
47 cairo_set_line_width(cr, 1.0);
49 // Base stuff are drawn here
50 display_cr = gdk_cairo_create(m_pDisplayBuffer);
51 cr = display_cr;
52 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
53 cairo_set_line_width(cr, 1.0);
54 #endif
56 m_pPangoInk = new PangoRectangle;
58 lSignalHandler = g_signal_connect(m_pCanvas, "expose_event", G_CALLBACK(canvas_expose_event), this);
60 /* gtk_widget_add_events(m_pCanvas, GDK_EXPOSURE_MASK);
61 gtk_widget_add_events(m_pCanvas, GDK_BUTTON_PRESS_MASK);
62 gtk_widget_add_events(m_pCanvas, GDK_BUTTON_RELEASE_MASK);
64 gtk_widget_add_events(m_pCanvas, GDK_ALL_EVENTS_MASK);
67 CCanvas::~CCanvas() {
68 // Free the buffer pixmaps
70 #if WITH_CAIRO
71 cr = NULL;
72 cairo_destroy(display_cr);
73 cairo_destroy(decoration_cr);
74 #endif
76 g_object_unref(m_pDummyBuffer);
77 g_object_unref(m_pDisplayBuffer);
78 g_object_unref(m_pDecorationBuffer);
79 g_object_unref(m_pOnscreenBuffer);
81 g_signal_handler_disconnect(m_pCanvas, lSignalHandler);
83 delete m_pPangoInk;
86 void CCanvas::Blank() {
87 // FIXME - this is replicated throughout this file - do something
88 // about that
89 #if WITH_CAIRO
90 #else
91 GdkGC *graphics_context;
92 GdkColormap *colormap;
94 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
95 colormap = gdk_colormap_get_system();
96 #endif
98 BEGIN_DRAWING;
99 SET_COLOR(0);
101 #if WITH_CAIRO
102 cairo_paint(cr);
103 #else
104 gdk_draw_rectangle(m_pOffscreenBuffer, graphics_context, TRUE, 0, 0, m_iWidth, m_iHeight);
105 #endif
107 END_DRAWING;
110 void CCanvas::Display() {
112 // FIXME - Some of this stuff is probably not needed
114 GdkRectangle update_rect;
116 GdkGC *graphics_context;
117 #if WITH_CAIRO
118 #else
119 GdkColormap *colormap;
120 #endif
122 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
124 #if WITH_CAIRO
125 #else
126 colormap = gdk_colormap_get_system();
127 #endif
129 BEGIN_DRAWING;
130 SET_COLOR(0);
132 // Copy the offscreen buffer into the onscreen buffer
134 gdk_draw_drawable(m_pOnscreenBuffer, m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)], m_pOffscreenBuffer, 0, 0, 0, 0, m_iWidth, m_iHeight);
136 // Blank the offscreen buffer (?)
138 gdk_draw_rectangle(m_pOffscreenBuffer, graphics_context, TRUE, 0, 0, m_iWidth, m_iHeight);
140 // Invalidate the full canvas to force it to be redrawn on-screen
142 update_rect.x = 0;
143 update_rect.y = 0;
144 update_rect.width = m_iWidth;
145 update_rect.height = m_iHeight;
147 gdk_window_invalidate_rect(m_pCanvas->window, &update_rect, FALSE);
149 // Restore original graphics context (?)
151 END_DRAWING;
153 gtk_main_iteration_do(0);
156 void CCanvas::DrawRectangle(int x1, int y1, int x2, int y2, int Color, int iOutlineColour, Opts::ColorSchemes ColorScheme, bool bDrawOutline, bool bFill, int iThickness) {
158 #if WITH_CAIRO
159 #else
160 GdkGC *graphics_context;
161 GdkColormap *colormap;
163 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
164 colormap = gdk_colormap_get_system();
165 #endif
167 BEGIN_DRAWING;
169 int iLeft;
170 int iTop;
171 int iWidth;
172 int iHeight;
174 if( x2 > x1 ) {
175 iLeft = x1;
176 iWidth = x2 - x1;
178 else {
179 iLeft = x2;
180 iWidth = x1 - x2;
183 if( y2 > y1 ) {
184 iTop = y1;
185 iHeight = y2 - y1;
187 else {
188 iTop = y2;
189 iHeight = y1 - y2;
192 if(bFill) {
193 SET_COLOR(Color);
194 #if WITH_CAIRO
195 cairo_set_line_width(cr, iThickness);
196 cairo_rectangle(cr, x1, y1, x2-x1+1.0, y2-y1+1.0);
197 cairo_fill(cr);
198 #else
199 gdk_draw_rectangle(m_pOffscreenBuffer, graphics_context, TRUE, iLeft, iTop, iWidth, iHeight);
200 #endif
203 if(bDrawOutline) {
204 if( iOutlineColour == -1 )
205 SET_COLOR(3);
206 else
207 SET_COLOR(iOutlineColour);
209 #if WITH_CAIRO
210 cairo_set_line_width(cr, iThickness);
211 cairo_rectangle(cr, x1+.5, y1+.5, x2-x1, y2-y1);
212 cairo_stroke(cr);
213 #else
214 gdk_gc_set_line_attributes(graphics_context, iThickness, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
215 gdk_draw_rectangle(m_pOffscreenBuffer, graphics_context, FALSE, iLeft, iTop, iWidth, iHeight);
216 #endif
218 END_DRAWING;
221 void CCanvas::DrawCircle(screenint iCX, screenint iCY, screenint iR, int iColour, int iFillColour, int iThickness, bool bFill) {
222 #if WITH_CAIRO
223 #else
224 if(iThickness == 1) // This is to make it work propely on Windows
225 iThickness = 0;
227 GdkGC *graphics_context;
228 GdkColormap *colormap;
230 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
231 colormap = gdk_colormap_get_system();
232 #endif
234 BEGIN_DRAWING;
236 if(bFill) {
237 SET_COLOR(iFillColour);
238 #if WITH_CAIRO
239 cairo_arc(cr, iCX, iCY, iR, 0, 2*M_PI);
240 cairo_fill(cr);
241 #else
242 gdk_draw_arc(m_pOffscreenBuffer, graphics_context, true, iCX - iR, iCY - iR, 2*iR, 2*iR, 0, 23040);
243 #endif
246 SET_COLOR(iColour);
247 #if WITH_CAIRO
248 cairo_set_line_width(cr, iThickness);
249 cairo_arc(cr, iCX, iCY, iR, 0, 2*M_PI);
250 cairo_stroke(cr);
251 #else
252 gdk_gc_set_line_attributes(graphics_context, iThickness, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
253 gdk_draw_arc(m_pOffscreenBuffer, graphics_context, false, iCX - iR, iCY - iR, 2*iR, 2*iR, 0, 23040);
254 #endif
256 END_DRAWING;
259 void CCanvas::Polygon(Dasher::CDasherScreen::point *Points, int Number, int Colour, int iWidth) {
261 if(iWidth == 1) // This is to make it work propely on Windows
262 iWidth = 0;
264 #if WITH_CAIRO
265 #else
266 GdkGC *graphics_context;
267 GdkColormap *colormap;
269 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
270 colormap = gdk_colormap_get_system();
271 #endif
273 BEGIN_DRAWING;
274 SET_COLOR(Colour);
276 #if WITH_CAIRO
277 cairo_move_to(cr, Points[0].x, Points[0].y);
278 for (int i=1; i < Number; i++)
279 cairo_line_to(cr, Points[i].x, Points[i].y);
280 cairo_close_path(cr);
281 cairo_fill(cr);
282 #else
283 GdkPoint *gdk_points;
284 gdk_points = (GdkPoint *) g_malloc(Number * sizeof(GdkPoint));
286 for(int i = 0; i < Number; i++) {
287 gdk_points[i].x = Points[i].x;
288 gdk_points[i].y = Points[i].y;
291 gdk_gc_set_line_attributes(graphics_context, iWidth, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
292 gdk_draw_polygon(m_pOffscreenBuffer, graphics_context, TRUE, gdk_points, Number);
293 g_free(gdk_points);
294 #endif
296 END_DRAWING;
299 void CCanvas::Polyline(Dasher::CDasherScreen::point *Points, int Number, int iWidth, int Colour) {
301 // FIXME - combine this with polygon?
303 #if WITH_CAIRO
304 #else
305 if(iWidth == 1) // This is to make it work propely on Windows
306 iWidth = 0;
308 GdkGC *graphics_context;
309 GdkColormap *colormap;
311 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
312 colormap = gdk_colormap_get_system();
313 #endif
315 BEGIN_DRAWING;
316 SET_COLOR(Colour);
318 #if WITH_CAIRO
319 cairo_set_line_width(cr, iWidth);
320 cairo_move_to(cr, Points[0].x+.5, Points[0].y+.5);
321 for (int i=1; i < Number; i++)
322 cairo_line_to(cr, Points[i].x+.5, Points[i].y+.5);
323 cairo_stroke(cr);
324 #else
325 GdkPoint *gdk_points;
326 gdk_points = (GdkPoint *) g_malloc(Number * sizeof(GdkPoint));
328 gdk_gc_set_line_attributes(graphics_context, iWidth, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
330 for(int i = 0; i < Number; i++) {
331 gdk_points[i].x = Points[i].x;
332 gdk_points[i].y = Points[i].y;
335 gdk_draw_lines(m_pOffscreenBuffer, graphics_context, gdk_points, Number);
336 g_free(gdk_points);
337 #endif
339 END_DRAWING;
342 void CCanvas::DrawString(const std::string &String, int x1, int y1, int size) {
343 #if WITH_CAIRO
344 #else
345 GdkGC *graphics_context;
346 GdkColormap *colormap;
348 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
349 colormap = gdk_colormap_get_system();
350 #endif
352 BEGIN_DRAWING;
353 SET_COLOR(4);
355 #if WITH_CAIRO
356 PangoLayout *pLayout(m_pPangoCache->GetLayout(cr, String, size));
357 #else
358 PangoLayout *pLayout(m_pPangoCache->GetLayout(GTK_WIDGET(m_pCanvas), String, size));
359 #endif
361 pango_layout_get_pixel_extents(pLayout, m_pPangoInk, NULL);
363 #if WITH_CAIRO
364 cairo_translate(cr, x1, y1-(int)m_pPangoInk->height/2);
365 pango_cairo_show_layout(cr, pLayout);
366 #else
367 gdk_draw_layout(m_pOffscreenBuffer, graphics_context, x1, y1 - m_pPangoInk->height / 2, pLayout);
368 #endif
370 END_DRAWING;
373 void CCanvas::TextSize(const std::string &String, int *Width, int *Height, int size) {
375 #if WITH_CAIRO
376 PangoLayout *pLayout(m_pPangoCache->GetLayout(cr, String, size));
377 #else
378 PangoLayout *pLayout(m_pPangoCache->GetLayout(GTK_WIDGET(m_pCanvas), String, size));
379 #endif
380 pango_layout_get_pixel_extents(pLayout, m_pPangoInk, NULL);
382 *Width = m_pPangoInk->width;
383 *Height = m_pPangoInk->height;
386 void CCanvas::SendMarker(int iMarker) {
388 switch(iMarker) {
389 case 0: // Switch to display buffer
390 m_pOffscreenBuffer = m_pDisplayBuffer;
391 #if WITH_CAIRO
392 cr = display_cr;
393 #endif
394 break;
395 case 1: // Switch to decorations buffer
396 gdk_draw_drawable(m_pDecorationBuffer, m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)], m_pDisplayBuffer, 0, 0, 0, 0, m_iWidth, m_iHeight);
397 m_pOffscreenBuffer = m_pDecorationBuffer;
398 #if WITH_CAIRO
399 cr = decoration_cr;
400 #endif
401 break;
405 bool CCanvas::ExposeEvent(GtkWidget *pWidget, GdkEventExpose *pEvent) {
406 gdk_draw_drawable(m_pCanvas->window, m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)], m_pOnscreenBuffer, pEvent->area.x, pEvent->area.y, pEvent->area.x, pEvent->area.y, pEvent->area.width, pEvent->area.height);
407 return true;
410 void CCanvas::SetColourScheme(const Dasher::CCustomColours *Colours) {
411 int iNumColours(Colours->GetNumColours());
413 #if WITH_CAIRO
414 if (cairo_colours)
415 delete[] cairo_colours;
416 cairo_colours = new my_cairo_colour_t[iNumColours];
417 #else
418 if (colours)
419 delete[] colours;
420 colours = new GdkColor[iNumColours];
421 #endif
423 for(int i = 0; i < iNumColours; i++) {
424 #if WITH_CAIRO
425 cairo_colours[i].r = Colours->GetRed(i) / 255.0;
426 cairo_colours[i].g = Colours->GetGreen(i) / 255.0;
427 cairo_colours[i].b = Colours->GetBlue(i) / 255.0;
428 #else
429 colours[i].pixel=0;
430 colours[i].red=Colours->GetRed(i)*257;
431 colours[i].green=Colours->GetGreen(i)*257;
432 colours[i].blue=Colours->GetBlue(i)*257;
433 #endif
437 bool CCanvas::GetCanvasSize(GdkRectangle *pRectangle)
439 if ((pRectangle == NULL) || (m_pCanvas == NULL))
440 return false;
442 // Using gtk_window_get_frame_extents() only seems to return the position
443 // and size of the parent Dasher window. So we'll get the widgets position
444 // and use its size to determine the bounding rectangle.
445 int iX = 0;
446 int iY = 0;
448 gdk_window_get_position(m_pCanvas->window, &iX, &iY);
450 pRectangle->x = iX;
451 pRectangle->y = iY;
452 pRectangle->width = m_iWidth;
453 pRectangle->height = m_iHeight;
455 return true;
458 extern "C" gint canvas_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) {
459 return ((CCanvas*)data)->ExposeEvent(widget, event);